---
title: "WordPress/agent-skills"
description: "Expert-level WordPress knowledge for AI coding assistants - blocks, themes, plugins, and best practices"
source: https://github.com/WordPress/agent-skills
ref: trunk
license: null
licenseName: "Other"
canonical: https://skillsdocs.com/WordPress/agent-skills
base: https://github.com/WordPress/agent-skills/blob/trunk/
provenance: authored
chapters: 19
inlined: 19
withheld: 0
words: 11300
updated: 2026-09-23T05:37:17Z
generator: "Skills Docs"
---

> **WordPress/agent-skills** — every Agent Skill in this repository, inlined verbatim.
>
> Canonical HTML: https://skillsdocs.com/WordPress/agent-skills
> Per-skill Markdown: https://skillsdocs.com/WordPress/agent-skills/<skill>.md
> Machine manifest: https://skillsdocs.com/WordPress/agent-skills/.well-known/agent-skills/index.json
> JSON: https://skillsdocs.com/api/v1/books/WordPress/agent-skills
> Install: `npx skills add WordPress/agent-skills`
> Upstream: https://github.com/WordPress/agent-skills @ `trunk`
> Licence: Other
>
> Content is mirrored from GitHub and © its authors, served unmodified. Takedown: https://github.com/DreambaseAI/skillsdocs/issues/new?labels=takedown&title=Takedown+request

# WordPress/agent-skills

Expert-level WordPress knowledge for AI coding assistants - blocks, themes, plugins, and best practices

- **Skills:** 19
- **Inlined:** 19 (licence detected)
- **Words:** 11,300
- **Reading time:** 52 min
- **Stars:** 2,162

## Table of contents

1. [blueprint](https://skillsdocs.com/WordPress/agent-skills/blueprint.md) — Use when the deliverable is WordPress Playground Blueprint JSON or a Blueprint bundle, including creating, editing, reviewing, validating schema keys, choosing…
2. [wordpress-router](https://skillsdocs.com/WordPress/agent-skills/wordpress-router.md) — Use when the user asks about WordPress codebases (plugins, themes, block themes, Gutenberg blocks, WP core checkouts) and you need to quickly classify the repo…
3. [wp-abilities-api](https://skillsdocs.com/WordPress/agent-skills/wp-abilities-api.md) — Use when working with the WordPress Abilities API (wp_register_ability, wp_register_ability_category, /wp-json/wp-abilities/v1/*, @wordpress/abilities) includi…
4. [wp-abilities-audit](https://skillsdocs.com/WordPress/agent-skills/wp-abilities-audit.md) — Audit a WordPress plugin's REST surface and produce a standardized audit document proposing Abilities API registrations. Produces a markdown doc with a YAML sc…
5. [wp-abilities-verify](https://skillsdocs.com/WordPress/agent-skills/wp-abilities-verify.md) — Verify a WordPress plugin's Abilities API registrations: enumerate abilities, check that callback behavior matches each annotation's claim (the adversarial rea…
6. [wp-block-development](https://skillsdocs.com/WordPress/agent-skills/wp-block-development.md) — Use when developing WordPress (Gutenberg) blocks: block.json metadata, register_block_type(_from_metadata), attributes/serialization, supports, dynamic renderi…
7. [wp-block-themes](https://skillsdocs.com/WordPress/agent-skills/wp-block-themes.md) — Use when developing WordPress block themes: theme.json (global settings/styles), templates and template parts, patterns, style variations, and Site Editor trou…
8. [wp-env](https://skillsdocs.com/WordPress/agent-skills/wp-env.md) — Use when setting up, configuring, or troubleshooting local WordPress development environments with @wordpress/env (wp-env). Triggers on mentions of wp-env, loc…
9. [wp-interactivity-api](https://skillsdocs.com/WordPress/agent-skills/wp-interactivity-api.md) — Use when building or debugging WordPress Interactivity API features (data-wp-* directives, @wordpress/interactivity store/state/actions, block viewScriptModule…
10. [wp-patterns](https://skillsdocs.com/WordPress/agent-skills/wp-patterns.md) — Pattern: create or update WordPress block patterns (starter pages, templates, template parts, Query Loop layouts), review pattern registration, block markup, c…
11. [wp-performance](https://skillsdocs.com/WordPress/agent-skills/wp-performance.md) — Use when investigating or improving WordPress performance (backend-only agent): profiling and measurement (WP-CLI profile/doctor, Server-Timing, Query Monitor…
12. [wp-phpstan](https://skillsdocs.com/WordPress/agent-skills/wp-phpstan.md) — Use when configuring, running, or fixing PHPStan static analysis in WordPress projects (plugins/themes/sites): phpstan.neon setup, baselines, WordPress-specifi…
13. [wp-playground](https://skillsdocs.com/WordPress/agent-skills/wp-playground.md) — Use as the WordPress Playground routing wrapper for ambiguous Playground work, local CLI runs with @wp-playground/cli, playground.wordpress.net share links, br…
14. [wp-plugin-development](https://skillsdocs.com/WordPress/agent-skills/wp-plugin-development.md) — Use when developing WordPress plugins: architecture and hooks, activation/deactivation/uninstall, admin UI and Settings API, data storage, cron/tasks, security…
15. [wp-plugin-directory-guidelines](https://skillsdocs.com/WordPress/agent-skills/wp-plugin-directory-guidelines.md) — Use when reviewing WordPress plugins for GPL compliance, checking license headers or compatibility, evaluating upsell/freemium/trialware patterns, validating p…
16. [wp-project-triage](https://skillsdocs.com/WordPress/agent-skills/wp-project-triage.md) — Use when you need a deterministic inspection of a WordPress repository (plugin/theme/block theme/WP core/Gutenberg/full site) including tooling/tests/version h…
17. [wp-rest-api](https://skillsdocs.com/WordPress/agent-skills/wp-rest-api.md) — Use when building, extending, or debugging WordPress REST API endpoints/routes: register_rest_route, WP_REST_Controller/controller classes, schema/argument val…
18. [wp-wpcli-and-ops](https://skillsdocs.com/WordPress/agent-skills/wp-wpcli-and-ops.md) — Use when working with WP-CLI (wp) for WordPress operations: safe search-replace, db export/import, plugin/theme/user/content management, cron, cache flushing,…
19. [wpds](https://skillsdocs.com/WordPress/agent-skills/wpds.md) — Use when building UIs leveraging the WordPress Design System (WPDS) and its components, tokens, patterns, etc.


## Front matter

_The repository README, verbatim except that relative links are resolved against https://github.com/WordPress/agent-skills/blob/trunk/._

# Agent Skills for WordPress

**Teach AI coding assistants how to build WordPress the right way.**

Agent Skills are portable bundles of instructions, checklists, and scripts that help AI assistants (Claude, Copilot, Codex, Cursor, etc.) understand WordPress development patterns, avoid common mistakes, and follow best practices.

> **AI Authorship Disclosure:** These skills were generated using GPT-5.2 Codex (High Reasoning) from official Gutenberg and WordPress documentation, then reviewed and edited by WordPress contributors. We tested skills with AI assistants and iterated based on results. This is v1, and skills will improve as the community uses them and contributes fixes. See [docs/ai-authorship.md](https://github.com/WordPress/agent-skills/blob/trunk/docs/ai-authorship.md) for details. ([WordPress AI Guidelines](https://make.wordpress.org/ai/handbook/ai-guidelines/))

## Why Agent Skills?

AI coding assistants are powerful, but they often:
- Generate outdated WordPress patterns (pre-Gutenberg, pre-block themes)
- Miss critical security considerations in plugin development
- Skip proper block deprecations, causing "Invalid block" errors
- Ignore existing tooling in your repo

Agent Skills solve this by giving AI assistants **expert-level WordPress knowledge** in a format they can actually use.

## Available Skills

| Skill | What it teaches |
|-------|-----------------|
| **wordpress-router** | Classifies WordPress repos and routes to the right workflow |
| **wp-project-triage** | Detects project type, tooling, and versions automatically |
| **wp-block-development** | Gutenberg blocks: `block.json`, attributes, rendering, deprecations |
| **wp-block-themes** | Block themes: `theme.json`, templates, patterns, style variations |
| **wp-plugin-development** | Plugin architecture, hooks, settings API, security |
| **wp-rest-api** | REST API routes/endpoints, schema, auth, and response shaping |
| **wp-interactivity-api** | Frontend interactivity with `data-wp-*` directives and stores |
| **wp-abilities-api** | Capability-based permissions and REST API authentication |
| **wp-abilities-audit** | Audit a plugin's REST surface and propose Abilities API registrations |
| **wp-abilities-verify** | Verify a plugin's Abilities API registrations against their declared annotations |
| **wp-wpcli-and-ops** | WP-CLI commands, automation, multisite, search-replace |
| **wp-performance** | Profiling, caching, database optimization, Server-Timing |
| **wp-phpstan** | PHPStan static analysis for WordPress projects (config, baselines, WP-specific typing) |
| **wp-playground** | WordPress Playground routing, CLI runs, browser previews, and snapshots |
| **wpds** | WordPress Design System |
| **wp-plugin-directory-guidelines** | WordPress Plugin Directory Guidelines |
| **blueprint** | WordPress Playground Blueprints for declarative environment setup |
| **wp-env** | Local WordPress development with `@wordpress/env`: setup, configuration, WP-CLI, Xdebug, troubleshooting |

## How It Works

Each skill is a self-contained folder with instructions, references, and optional scripts:

```
skills/wp-block-development/
├── SKILL.md              # Main instructions (when to use, procedure, verification)
├── references/           # Deep-dive docs on specific topics
│   ├── block-json.md
│   ├── deprecations.md
│   └── ...
└── scripts/              # Deterministic helpers (detection, validation)
    └── list_blocks.mjs
```

When you ask your AI assistant to work on WordPress code, it reads these skills and follows the documented procedures rather than guessing.


## Global vs. Project Scope

Skills can be installed in two scopes:

**Global** — installed in your home directory (e.g. `~/.claude/skills/`, `~/.cursor/skills/`).
- Available across **all** your projects automatically.
- Best for individual developers who want WordPress knowledge in every repo.

**Project** — installed inside a repository (e.g. `.claude/skills/`, `.github/skills/`, `.cursor/skills/`).
- Available only within **that specific project**.
- Can be committed to version control so the entire team benefits.

You can use both at the same time. When a skill exists in both scopes, the project-level version is used.

## Quick Start

The fastest way to install a skill is with a single command:

```bash
npx skills add WordPress/agent-skills --skill wp-plugin-development
```

To see all available skills:

```bash
npx skills add WordPress/agent-skills --list
```

To install multiple skills at once:

```bash
npx skills add WordPress/agent-skills --skill wp-plugin-development wp-abilities-api wp-playground
```

#### Choosing a scope

`npx skills add` asks to choose the skill **project-scoped**, selecting the local scope on which the skills are installed in the local project; it can be stored at the repository (e.g. `.claude/skills/`, `.cursor/skills/`) — so the skills can be committed to version control and shared with your team.

Installing **globally** makes the skill available to your user (across **all** your projects). Adding the `-g` / `--global` flag, it will install your skill with global scope:

```bash
npx skills add WordPress/agent-skills --skill wp-plugin-development --global
```

### Install globally for Claude Code

```bash
# Clone agent-skills
git clone https://github.com/WordPress/agent-skills.git
cd agent-skills

# Build the distribution
node shared/scripts/skillpack-build.mjs --clean

# Install all skills globally (available across all projects)
node shared/scripts/skillpack-install.mjs --global

# Or install specific skills only
node shared/scripts/skillpack-install.mjs --global --skills=wp-playground,wp-block-development
```

This installs skills to `~/.claude/skills/` where Claude Code will automatically discover them.

### Install into your repo

```bash
# Clone agent-skills
git clone https://github.com/WordPress/agent-skills.git
cd agent-skills

# Build the distribution
node shared/scripts/skillpack-build.mjs --clean

# Install into your WordPress project
node shared/scripts/skillpack-install.mjs --dest=../your-wp-project --targets=codex,vscode,claude,cursor
```

This copies skills into:
- `.codex/skills/` for OpenAI Codex
- `.github/skills/` for VS Code / GitHub Copilot
- `.claude/skills/` for Claude Code (project-level)
- `.cursor/skills/` for Cursor (project-level)

Antigravity is opt-in for project-level installs. To also copy skills into `.agents/skills/`, include `antigravity` when building and installing:

```bash
node shared/scripts/skillpack-build.mjs --clean --targets=codex,vscode,claude,cursor,antigravity
node shared/scripts/skillpack-install.mjs --dest=../your-wp-project --targets=codex,vscode,claude,cursor,antigravity
```

### Install globally for Cursor

```bash
node shared/scripts/skillpack-install.mjs --targets=cursor-global
```

This installs skills to `~/.cursor/skills/` where Cursor will discover them.

### Install globally for Antigravity

```bash
node shared/scripts/skillpack-build.mjs --clean --targets=antigravity
node shared/scripts/skillpack-install.mjs --targets=antigravity-global
```

This installs skills to `~/.gemini/antigravity/skills/` where Antigravity will discover them.

### Available options

```bash
# List available skills
node shared/scripts/skillpack-install.mjs --list

# Dry run (preview without installing)
node shared/scripts/skillpack-install.mjs --global --dry-run

# Install specific skills to a project (e.g. Claude + Cursor)
node shared/scripts/skillpack-install.mjs --dest=../my-repo --targets=claude,cursor --skills=wp-wpcli-and-ops
```

### Manual installation

Copy any skill folder from `skills/` into your project's instructions directory for your AI assistant.

## Compatibility

- **WordPress 7.0+** (PHP 7.4.0+)
- Works with any AI assistant that supports project-level instructions

## Contributing

**We welcome contributions!** This project is a great way to share your WordPress expertise—you don't need to be a coding wizard. Most skills are written in Markdown, focusing on clear procedures and best practices.

See [CONTRIBUTING.md](https://github.com/WordPress/agent-skills/blob/trunk/CONTRIBUTING.md) for details on how to get started.

Quick commands:

```bash
# Scaffold a new skill
node shared/scripts/scaffold-skill.mjs <skill-name> "<description>"

# Validate skills
node eval/harness/run.mjs
```

## Documentation

- [Authoring Guide](https://github.com/WordPress/agent-skills/blob/trunk/docs/authoring-guide.md) - How to create and improve skills
- [Principles](https://github.com/WordPress/agent-skills/blob/trunk/docs/principles.md) - Design philosophy
- [Packaging](https://github.com/WordPress/agent-skills/blob/trunk/docs/packaging.md) - Build and distribution
- [Compatibility Policy](https://github.com/WordPress/agent-skills/blob/trunk/docs/compatibility-policy.md) - Version targeting

## License

GPL-2.0-or-later

---

<!-- chapter:begin slug=blueprint position=1 -->

## 1. blueprint

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/blueprint/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/blueprint/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/blueprint.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: blueprint
description: Use when the deliverable is WordPress Playground Blueprint JSON or a Blueprint bundle, including creating, editing, reviewing, validating schema keys, choosing steps/resources, and debugging Blueprint files. For only running or sharing a Playground environment, use wp-playground.
compatibility: "WordPress 7.0+, PHP 7.4.0+. Optionally Playground CLI or a browser"
---

# WordPress Playground Blueprints

## Overview

A Blueprint is a JSON file that declaratively configures a WordPress Playground instance — installing plugins/themes, setting options, running PHP/SQL, manipulating files, and more.

**Core principle:** Blueprints are trusted JSON-only declarations. No arbitrary JavaScript. They work on web, Node.js, and CLI.

## Quick Start Template

```json
{
  "$schema": "https://playground.wordpress.net/blueprint-schema.json",
  "landingPage": "/wp-admin/",
  "preferredVersions": { "php": "8.3", "wp": "latest" },
  "steps": [{ "step": "login" }]
}
```

## Top-Level Properties

All optional. Only documented keys are allowed — the schema rejects unknown properties.

| Property | Type | Notes |
|----------|------|-------|
| `$schema` | string | Always `"https://playground.wordpress.net/blueprint-schema.json"` |
| `landingPage` | string | Relative path, e.g. `/wp-admin/` |
| `description` | string | Deprecated optional top-level description. Prefer `meta.description` for new Blueprints |
| `meta` | object | `{ title, author, description?, categories? }` — title and author required |
| `preferredVersions` | object | `{ php, wp }` — both required when present |
| `features` | object | `{ networking?: boolean, intl?: boolean }` — **only** these two keys, nothing else. Networking defaults to `true` |
| `phpExtensionBundles` | any | Deprecated/no longer used; the schema leaves the value unconstrained and says to remove it from Blueprints |
| `extraLibraries` | array | `["wp-cli"]` — auto-included when any `wp-cli` step is present |
| `constants` | object | Shorthand for `defineWpConfigConsts`. Values: string/boolean/number |
| `plugins` | array | Shorthand for `installPlugin` steps. Strings = wp.org slugs |
| `siteOptions` | object | Shorthand for `setSiteOptions` |
| `login` | boolean or object | `true` = login as admin. Object = `{ username?, password? }` (both default to `"admin"`/`"password"`) |
| `steps` | array | Main execution pipeline. Runs after shorthands |

### preferredVersions Values

- **php:** Major.minor only: `"7.4"`, `"8.0"`, `"8.1"`, `"8.2"`, `"8.3"`, `"8.4"`, `"8.5"`, or `"latest"`. Patch versions like `"7.4.1"` are invalid. Check the schema for currently supported versions.
- **wp:** Recent major versions, `"latest"`, `"beta"`, `"nightly"`/`"trunk"`, or a URL to a custom zip. The schema also accepts `false` for PHP-only Playground; do not combine `wp: false` with WordPress-only fields such as `plugins`, `siteOptions`, `login`, or WordPress-only steps.

### Shorthands vs Steps

Shorthands (`login`, `plugins`, `siteOptions`, `constants`) are expanded and prepended to `steps` in an **unspecified order**. Use explicit steps when execution order matters.

## Resource References

Resources tell Playground where to find files. Used by `installPlugin`, `installTheme`, `writeFile`, `writeFiles`, `importWxr`, etc.

| Resource Type | Required Fields | Example |
|--------------|----------------|---------|
| `wordpress.org/plugins` | `slug` | `{ "resource": "wordpress.org/plugins", "slug": "woocommerce" }` |
| `wordpress.org/themes` | `slug` | `{ "resource": "wordpress.org/themes", "slug": "astra" }` |
| `url` | `url` | `{ "resource": "url", "url": "https://example.com/plugin.zip" }` |
| `git:directory` | `url`, `ref` | See below |
| `literal` | `name`, `contents` | `{ "resource": "literal", "name": "file.txt", "contents": "hello" }` |
| `literal:directory` | `name`, `files` | See below |
| `bundled` | `path` | References a file within a blueprint bundle (e.g. `{ "resource": "bundled", "path": "/plugin.zip" }`) |
| `zip` | `inner` | Wraps another resource in a ZIP — use when a step expects a zip but your source isn't one (e.g. wrapping a `url` resource pointing to a raw directory) |

### git:directory — Installing from GitHub

```json
{
  "resource": "git:directory",
  "url": "https://github.com/WordPress/gutenberg",
  "ref": "trunk",
  "refType": "branch",
  "path": "/"
}
```

- When using a branch or tag name for `ref`, you **must** set `refType` (`"branch"` | `"tag"` | `"commit"` | `"refname"`). Without it, only `"HEAD"` resolves reliably.
- `path` selects a subdirectory (defaults to repo root).

### literal:directory — Inline File Trees

```json
{
  "resource": "literal:directory",
  "name": "my-plugin",
  "files": {
    "plugin.php": "<?php /* Plugin Name: My Plugin */ ?>",
    "includes": {
      "helper.php": "<?php // helper code ?>"
    }
  }
}
```

- `files` uses nested objects for subdirectories — keys are filenames or directory names, values are **plain strings** (file content) or **objects** (subdirectories). Never use resource references as values.
- **Do NOT use path separators in keys** (e.g. `"includes/helper.php"` is wrong — use a nested `"includes": { "helper.php": "..." }` object).

## Steps Reference

Every step requires `"step": "<name>"`. Any step can optionally include `"progress": { "weight": 1, "caption": "Installing..." }` for UI feedback.

### Plugin & Theme Installation

```json
{
  "step": "installPlugin",
  "pluginData": { "resource": "wordpress.org/plugins", "slug": "gutenberg" },
  "options": { "activate": true, "targetFolderName": "gutenberg" },
  "ifAlreadyInstalled": "overwrite"
}
```

```json
{
  "step": "installTheme",
  "themeData": { "resource": "wordpress.org/themes", "slug": "twentytwentyfour" },
  "options": { "activate": true, "importStarterContent": true },
  "ifAlreadyInstalled": "overwrite"
}
```

- Use `pluginData` / `themeData` — **NOT** the deprecated `pluginZipFile` / `themeZipFile`.
- `pluginData` / `themeData` accept any FileReference or DirectoryReference — a zip URL, a `wordpress.org/plugins` slug, a `git:directory`, or a `literal:directory` (no `zip` wrapper needed).
- `options.activate` controls activation. No need for a separate `activatePlugin`/`activateTheme` step when using `installPlugin`/`installTheme`.
- `ifAlreadyInstalled`: `"overwrite"` | `"skip"` | `"error"`

### Activation (standalone)

Only needed for plugins/themes already on disk (e.g. after `writeFile`/`writeFiles`):

```json
{ "step": "activatePlugin", "pluginPath": "my-plugin/my-plugin.php" }
```
```json
{ "step": "activateTheme", "themeFolderName": "twentytwentyfour" }
```

### File Operations

```json
{ "step": "writeFile", "path": "/wordpress/wp-content/mu-plugins/custom.php", "data": "<?php // code" }
```

`data` accepts a plain string (as shown above) or a resource reference (e.g. `{ "resource": "url", "url": "https://..." }`).

```json
{
  "step": "writeFiles",
  "writeToPath": "/wordpress/wp-content/plugins/",
  "filesTree": {
    "resource": "literal:directory",
    "name": "my-plugin",
    "files": {
      "plugin.php": "<?php\n/*\nPlugin Name: My Plugin\n*/",
      "includes": {
        "helpers.php": "<?php // helpers"
      }
    }
  }
}
```

**`writeFiles` requires a DirectoryReference** (`literal:directory` or `git:directory`) as `filesTree` — not a plain object.

Other file operations: `mkdir`, `cp`, `mv`, `rm`, `rmdir`, `unzip`.

### Running Code

**runPHP:**
```json
{ "step": "runPHP", "code": "<?php require '/wordpress/wp-load.php'; update_option('key', 'value');" }
```
**GOTCHA:** You must `require '/wordpress/wp-load.php';` to use any WordPress functions.

**wp-cli:**
```json
{ "step": "wp-cli", "command": "wp post create --post_type=page --post_title='Hello' --post_status=publish" }
```
The step name is `wp-cli` (with hyphen), NOT `cli` or `wpcli`.

**runSql:**
```json
{ "step": "runSql", "sql": { "resource": "literal", "name": "q.sql", "contents": "UPDATE wp_options SET option_value='val' WHERE option_name='key';" } }
```

### Site Configuration

```json
{ "step": "setSiteOptions", "options": { "blogname": "My Site", "blogdescription": "A tagline" } }
```
```json
{ "step": "defineWpConfigConsts", "consts": { "WP_DEBUG": true } }
```
```json
{ "step": "setSiteLanguage", "language": "en_US" }
```
```json
{ "step": "defineSiteUrl", "siteUrl": "https://example.com" }
```

### Other Steps

| Step | Key Properties |
|------|---------------|
| `login` | `username?`, `password?` (default `"admin"` / `"password"`) |
| `enableMultisite` | (no required props) |
| `importWxr` | `file` (FileReference) |
| `importThemeStarterContent` | `themeSlug?` |
| `importWordPressFiles` | `wordPressFilesZip`, `pathInZip?` — imports a full WordPress directory from a zip |
| `request` | `request: { url, method?, headers?, body? }` |
| `updateUserMeta` | `userId`, `meta` |
| `runWpInstallationWizard` | `options?` — runs the WP install wizard with given options |
| `resetData` | (no props) |

## Common Patterns

### Inline mu-plugin (quick custom code)

```json
{
  "step": "writeFile",
  "path": "/wordpress/wp-content/mu-plugins/custom.php",
  "data": "<?php\n// mu-plugins load automatically — no activation needed, no require wp-load.php\nadd_filter('show_admin_bar', '__return_false');"
}
```

### Inline plugin with multiple files

```json
{
  "step": "writeFiles",
  "writeToPath": "/wordpress/wp-content/plugins/",
  "filesTree": {
    "resource": "literal:directory",
    "name": "my-plugin",
    "files": {
      "my-plugin.php": "<?php\n/*\nPlugin Name: My Plugin\n*/\nrequire __DIR__ . '/includes/main.php';",
      "includes": {
        "main.php": "<?php // main logic"
      }
    }
  }
}
```

Then activate it with a separate step:

```json
{ "step": "activatePlugin", "pluginPath": "my-plugin/my-plugin.php" }
```

### Plugin from a GitHub branch

```json
{
  "step": "installPlugin",
  "pluginData": {
    "resource": "git:directory",
    "url": "https://github.com/user/repo",
    "ref": "feature-branch",
    "refType": "branch",
    "path": "/"
  }
}
```

## Common Mistakes

| Mistake | Correct |
|---------|---------|
| `pluginZipFile` / `themeZipFile` | `pluginData` / `themeData` |
| `"step": "cli"` | `"step": "wp-cli"` |
| Flat object as `writeFiles.filesTree` | Must be a `literal:directory` or `git:directory` resource |
| Path separators in `files` keys | Use nested objects for subdirectories |
| `runPHP` without `wp-load.php` | Always `require '/wordpress/wp-load.php';` for WP functions |
| Invented top-level keys | Only documented keys work — schema rejects unknown properties |
| Inventing proxy URLs for GitHub | Use `git:directory` resource type |
| Omitting `refType` with branch/tag `ref` | Required — only `"HEAD"` works without it |
| Resource references in `literal:directory` `files` values | Values must be plain strings (content) or objects (subdirectories) — never resource refs |
| `features.debug` or other invented feature keys | `features` only supports `networking` and `intl` — use `constants: { "WP_DEBUG": true }` for debug mode |
| `require wp-load.php` in mu-plugin code | Only needed in `runPHP` steps — mu-plugins already run within WordPress |
| Schema URL with `.org` domain | Must be `playground.wordpress.net`, not `playground.wordpress.org` |

## Full Reference

This skill covers the most common steps and patterns. For the complete API, see:

- **Blueprint docs:** https://wordpress.github.io/wordpress-playground/blueprints
- **JSON schema:** https://playground.wordpress.net/blueprint-schema.json

Additional steps not covered above: `runPHPWithOptions` (run PHP with custom `ini` settings), `runWpInstallationWizard`, and resource types `vfs` and `bundled` (for advanced embedding scenarios).

## Blueprint Bundles

Bundles are self-contained packages that include a `blueprint.json` along with all the resources it references (plugins, themes, WXR files, etc.). Instead of hosting assets externally, bundle them alongside the blueprint.

### Bundle Structure

```
my-bundle/
├── blueprint.json          ← must be at the root
├── my-plugin.zip           ← zipped plugin directory
├── theme.zip
└── content/
    └── sample-content.wxr
```

Plugins and themes must be zipped before bundling — `installPlugin` expects a zip, not a raw directory. To create the zip from a plugin directory:

```bash
cd my-bundle
zip -r my-plugin.zip my-plugin/
```

### Referencing Bundled Resources

Use the `bundled` resource type to reference files within the bundle:

```json
{
  "step": "installPlugin",
  "pluginData": {
    "resource": "bundled",
    "path": "/my-plugin.zip"
  },
  "options": { "activate": true }
}
```

```json
{
  "step": "importWxr",
  "file": {
    "resource": "bundled",
    "path": "/content/sample-content.wxr"
  }
}
```

### Creating a Bundle Step by Step

1. Create the bundle directory and add `blueprint.json` at its root.
2. Write your plugin/theme source files in a subdirectory (e.g. `my-plugin/my-plugin.php`).
3. Zip the plugin directory: `zip -r my-plugin.zip my-plugin/`
4. Reference it in `blueprint.json` using `{ "resource": "bundled", "path": "/my-plugin.zip" }`.

Full example — a bundle that installs a custom plugin:

```
dashboard-widget-bundle/
├── blueprint.json
├── dashboard-widget.zip        ← zip of dashboard-widget/
└── dashboard-widget/           ← plugin source (kept for editing)
    └── dashboard-widget.php
```

```json
{
  "$schema": "https://playground.wordpress.net/blueprint-schema.json",
  "landingPage": "/wp-admin/",
  "preferredVersions": { "php": "8.3", "wp": "latest" },
  "steps": [
    { "step": "login" },
    {
      "step": "installPlugin",
      "pluginData": { "resource": "bundled", "path": "/dashboard-widget.zip" },
      "options": { "activate": true }
    }
  ]
}
```

### Distribution Formats

| Format | How to use |
|--------|-----------|
| ZIP file (remote) | Website: `https://playground.wordpress.net/?blueprint-url=https://example.com/bundle.zip` |
| ZIP file (local) | CLI: `npx @wp-playground/cli server --blueprint=./bundle.zip` |
| Local directory | CLI: `npx @wp-playground/cli server --blueprint=./my-bundle/ --blueprint-may-read-adjacent-files` |
| Git repository directory | Point `blueprint-url` at a repo directory containing `blueprint.json` |

**GOTCHA:** Local directory bundles always need `--blueprint-may-read-adjacent-files` for the CLI to read bundled resources. Without it, any `"resource": "bundled"` reference will fail with a "File not found" error. ZIP bundles don't need this flag — all files are self-contained inside the archive.

## Testing Blueprints

### Inline Blueprints (quick test, no bundles)

Minify the blueprint JSON (no extra whitespace), encode it once with `encodeURIComponent()`, prepend `https://playground.wordpress.net/#`, and open the URL in a browser:

```
https://playground.wordpress.net/#%7B%22%24schema%22%3A%22https%3A%2F%2Fplayground.wordpress.net%2Fblueprint-schema.json%22%2C%22preferredVersions%22%3A%7B%22php%22%3A%228.3%22%2C%22wp%22%3A%22latest%22%7D%2C%22steps%22%3A%5B%7B%22step%22%3A%22login%22%7D%5D%7D
```

Very large blueprints may exceed browser URL length limits; use the CLI or a hosted Blueprint URL instead. For share-link details, use `wp-playground/references/website.md`.

### Local CLI Testing

**Interactive server** (keeps running, opens in browser):
```bash
# Directory bundle — requires --blueprint-may-read-adjacent-files
npx @wp-playground/cli server --blueprint=./my-bundle/ --blueprint-may-read-adjacent-files

# ZIP bundle — self-contained, no extra flags needed
npx @wp-playground/cli server --blueprint=./bundle.zip
```

**Headless validation** (runs blueprint and exits):
```bash
npx @wp-playground/cli run-blueprint --blueprint=./my-bundle/ --blueprint-may-read-adjacent-files
```

### Testing with WordPress Playground

Use the `wp-playground` skill for local or browser testing. For CLI testing, follow `wp-playground/references/cli.md` with `--blueprint=<path-or-url>`; for directory bundles, pass `--blueprint-may-read-adjacent-files`.

<!-- chapter:end slug=blueprint -->

---

<!-- chapter:begin slug=wordpress-router position=2 -->

## 2. wordpress-router

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wordpress-router/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wordpress-router/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wordpress-router.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (1), referenced from this skill's directory:
  - `references/decision-tree.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wordpress-router/references/decision-tree.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wordpress-router
description: "Use when the user asks about WordPress codebases (plugins, themes, block themes, Gutenberg blocks, WP core checkouts) and you need to quickly classify the repo and route to the correct workflow/skill (blocks, theme.json, REST API, WP-CLI, performance, security, testing, release packaging)."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Filesystem-based agent with bash + node. Some workflows require WP-CLI."
---

# WordPress Router

## When to use

Use this skill at the start of most WordPress tasks to:

- identify what kind of WordPress codebase this is (plugin vs theme vs block theme vs WP core checkout vs full site),
- pick the right workflow and guardrails,
- delegate to the most relevant domain skill(s).

## Inputs required

- Repo root (current working directory).
- The user’s intent (what they want changed) and any constraints (WP version targets, WP.com specifics, release requirements).

## Procedure

1. Run the project triage script:
   - `node skills/wp-project-triage/scripts/detect_wp_project.mjs`
2. Read the triage output and classify:
   - primary project kind(s),
   - tooling available (PHP/Composer, Node, @wordpress/scripts),
   - tests present (PHPUnit, Playwright, wp-env),
   - any version hints.
3. Route to domain workflows based on user intent + repo kind:
   - For the decision tree, read: `skills/wordpress-router/references/decision-tree.md`.
4. Apply guardrails before making changes:
   - Confirm any version constraints if unclear.
   - Prefer the repo’s existing tooling and conventions for builds/tests.

## Verification

- Re-run the triage script if you create or restructure significant files.
- Run the repo’s lint/test/build commands that the triage output recommends (if available).

## Failure modes / debugging

- If triage reports `kind: unknown`, inspect:
  - root `composer.json`, `package.json`, `style.css`, `block.json`, `theme.json`, `wp-content/`.
- If the repo is huge, consider narrowing scanning scope or adding ignore rules to the triage script.

## Escalation

- If routing is ambiguous, ask one question:
  - “Is this intended to be a WordPress plugin, a theme (classic/block), or a full site repo?”

<!-- chapter:end slug=wordpress-router -->

---

<!-- chapter:begin slug=wp-abilities-api position=3 -->

## 3. wp-abilities-api

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-abilities-api/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-api/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-abilities-api.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (9), referenced from this skill's directory:
  - `references/delegate-helper-pattern.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-api/references/delegate-helper-pattern.md
  - `references/domain-vs-projection.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-api/references/domain-vs-projection.md
  - `references/error-code-vocabulary.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-api/references/error-code-vocabulary.md
  - `references/grouping-heuristic.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-api/references/grouping-heuristic.md
  - `references/input-schema-gotchas.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-api/references/input-schema-gotchas.md
  - `references/php-registration.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-api/references/php-registration.md
  - `references/plugin-family-patterns.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-api/references/plugin-family-patterns.md
  - `references/rest-api.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-api/references/rest-api.md
  - `references/shared-core-service.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-api/references/shared-core-service.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-abilities-api
description: "Use when working with the WordPress Abilities API (wp_register_ability, wp_register_ability_category, /wp-json/wp-abilities/v1/*, @wordpress/abilities) including defining abilities, categories, meta, REST exposure, and permissions checks for clients."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Filesystem-based agent with bash + node. Some workflows require WP-CLI."
---

# WP Abilities API

## When to use

Use this skill when the task involves:

- registering abilities or ability categories in PHP,
- exposing abilities to clients via REST (`wp-abilities/v1`),
- consuming abilities in JS (notably `@wordpress/abilities`),
- diagnosing “ability doesn’t show up” / “client can’t see ability” / “REST returns empty”.

## Inputs required

- Repo root (run `wp-project-triage` first if you haven’t).
- Target WordPress version(s) and whether this is WP core or a plugin/theme.
- Where the change should live (plugin vs theme vs mu-plugin).

## Procedure

Before deciding what to register, read `references/domain-vs-projection.md` — abilities live at the domain capability layer; MCP / Command Palette / REST exposure is a projection. Registration shape and exposure shape are different decisions, and conflating them forces re-registration every time a consumer's constraints change.

### 1) Confirm availability and version constraints

- If this is WP core work, check `signals.isWpCoreCheckout` and `versions.wordpress.core`.
- If the project targets WP < 6.9, you may need the Abilities API plugin/package rather than relying on core.

### 2) Find existing Abilities usage

Search for these in the repo:

- `wp_register_ability(`
- `wp_register_ability_category(`
- `wp_abilities_api_init`
- `wp_abilities_api_categories_init`
- `wp-abilities/v1`
- `@wordpress/abilities`

If none exist, decide whether you’re introducing Abilities API fresh (new registrations + client consumption) or only consuming.

### 3) Register categories (optional)

If you need a logical grouping, register an ability category early (see `references/php-registration.md`).

### 4) Register abilities (PHP)

For grouping decisions (how many abilities to register, and where to put filters vs. new ability names), read `references/grouping-heuristic.md` first — it keeps you from shipping one atomic ability per REST operation.

To avoid drift between the ability and the existing UI / REST code path, see `references/shared-core-service.md` — abilities, REST handlers, CLI commands, and UI controllers should be thin adapters over a shared service. The reference also covers the metric trap (REST handlers that emit usage telemetry) and the `AGENTS.md` rule for keeping registrations in sync when underlying code paths change.

For shared helper patterns when multiple execute callbacks delegate to existing REST controllers, see `references/plugin-family-patterns.md` (identify the shared-API-client vs zero-arg-controllers shape) and `references/delegate-helper-pattern.md` (one helper shape that works, and when not to use it).

For standardized `WP_Error` codes that let agents reason about retry vs. escalation, see `references/error-code-vocabulary.md`.

Implement the ability in PHP registration with:

- stable `id` (namespaced),
- `label`/`description`,
- `category`,
- `meta`:
  - add `readonly: true` when the ability is informational,
  - set `show_in_rest: true` for abilities you want visible to clients.

Use the documented init hooks for Abilities API registration so they load at the right time (see `references/php-registration.md`).

### 5) Confirm REST exposure

- Verify the REST endpoints exist and return expected results (see `references/rest-api.md`).
- If the client still can’t see the ability, confirm `meta.show_in_rest` is enabled and you’re querying the right endpoint.

### 6) Consume from JS (if needed)

- Prefer `@wordpress/abilities` APIs for client-side access and checks.
- Ensure build tooling includes the dependency and the project’s build pipeline bundles it.

## Verification

- `wp-project-triage` indicates `signals.usesAbilitiesApi: true` after your change (if applicable).
- REST check (in a WP environment): endpoints under `wp-abilities/v1` return your ability and category when expected.
- If the repo has tests, add/update coverage near:
  - PHP: ability registration and meta exposure
  - JS: ability consumption and UI gating

## Failure modes / debugging

- Ability never appears:
  - registration code not running (wrong hook / file not loaded),
  - missing `meta.show_in_rest`,
  - incorrect category/ID mismatch.
- REST shows ability but JS doesn’t:
  - wrong REST base/namespace,
  - JS dependency not bundled,
  - caching (object/page caches) masking changes.
- Execute callback returns unexpected errors or silently ignores input:
  - `input_schema` defaults aren't being applied, pagination key drift between the ability and the backing, or `empty()`-based ID validation — see `references/input-schema-gotchas.md`.

## Escalation

- If you’re uncertain about version support, confirm target WP core versions and whether Abilities API is expected from core or as a plugin.
- For canonical details, consult:
  - `references/rest-api.md`
  - `references/php-registration.md`

<!-- chapter:end slug=wp-abilities-api -->

---

<!-- chapter:begin slug=wp-abilities-audit position=4 -->

## 4. wp-abilities-audit

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-abilities-audit/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-audit/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-abilities-audit.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (3), referenced from this skill's directory:
  - `references/audit-schema.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-audit/references/audit-schema.md
  - `references/capability-gate-tracing.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-audit/references/capability-gate-tracing.md
  - `references/controller-enumeration.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-audit/references/controller-enumeration.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-abilities-audit
description: "Audit a WordPress plugin's REST surface and produce a standardized audit document proposing Abilities API registrations. Produces a markdown doc with a YAML schema and prose sections that humans and agents can both consume when planning a registration rollout. Works on any WP plugin."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Filesystem-based agent with bash + node. Requires access to the plugin checkout; some workflows benefit from WP-CLI but don't require it."
---

# WP Abilities Audit

Produce a standardized audit document for a WordPress plugin's REST surface,
proposing a set of Abilities API registrations grouped by semantic intent. The
audit doc is a planning artifact for implementers — humans, agents, or both —
that captures the controller inventory, capability gates, and proposed
ability shapes in a structured form. A reviewer reading the doc can scope the
work without re-deriving the survey.

This skill works on any plugin that exposes a REST surface. Plugin
classification (for purposes of the optional `plugin_family` annotation) is
the user's call; the workflow itself is plugin-agnostic.

## When to use

- The task is "register Abilities API abilities for a WP plugin" and no audit
  doc exists yet.
- Planning participation in a multi-plugin abilities rollout and need a
  shareable, standardized audit artifact.
- Pre-flight checking a plugin's agent-readiness before implementing abilities.
- A PM or non-implementer wants to scope the work before engineering picks it up.

## Inputs required

1. **Plugin checkout path** — working tree of the plugin to audit.
2. **Triage output** — run `wp-project-triage` first if not already done. The
   audit consumes `signals.usesAbilitiesApi`, `versions.wordpress`, and
   `project.kind` from the report.
3. **Auditor identity** — name and team or context, recorded in the audit's
   `auditor` field.
4. **Output path** — where the audit doc should land. Default explicit over
   implicit; ask if not provided rather than writing into the plugin worktree.

## Prerequisites

- `wp-project-triage` has run successfully and classified the plugin.
- The plugin has at least one REST controller. If enumeration finds zero
  controllers, the audit doesn't apply — see "Failure modes" below.

## Procedure

### 1. Enumerate REST controllers

Read `references/controller-enumeration.md` now — it covers the two observed
enumeration paths (glob for standard layouts, grep as the universal fallback)
and when to use each.

Record every controller class + file + REST base + routes in a "Controller
Inventory" table. The inventory is exhaustive even though only a subset
becomes proposed abilities.

### 2. For each controller, extract the backing fields

For every controller found, extract the fields the audit schema requires:
class, file, HTTP method, route, route-registration line number, callback
name, callback line number, permission callback, whether the callback takes
a `WP_REST_Request` argument or is zero-arg, and the return type.

Read `references/audit-schema.md` now for the exact field list and the shape
of `proposed_abilities` entries. Line-number fields may be `null` for
inherited callbacks — the schema allows this and pairs it with an optional
`inherited_from` field.

### 3. Confirm capability gate(s)

Trace each controller's `permission_callback` to its `current_user_can()` call
(or to the post-type capability machinery if the controller extends a
post-type-backed base).

Read `references/capability-gate-tracing.md` now — it documents the two
common mechanisms (direct `check_permission()` vs post-type-backed
`wc_rest_check_post_permissions()`) and how to represent each in the schema.
Note explicitly whether read and write gates differ: compound gates are
represented as a `{read, write}` object, not a single string.

### 4. Propose abilities using semantic-intent grouping

Do NOT atomize one ability per HTTP method. Apply the semantic-intent grouping
heuristic — it's the only grouping rule this skill uses.

Read `../wp-abilities-api/references/grouping-heuristic.md` now — do NOT
re-derive the rules here. Short version: one ability per real-world question
or state transition, with filter parameters in `input_schema` collapsing N
variants into 1.

**Apply the use-case sanity check before populating any candidate.** Per
`../wp-abilities-api/references/domain-vs-projection.md`'s use-case-contract
test: would a human or agent intentionally perform this behavior through a
supported plugin workflow? If yes, the candidate is a real ability —
proceed to fill in fields. If no, the route is internal transport plumbing
(cache invalidation, scheduler ticks, bookkeeping endpoints, debug
introspection) — keep it in the Controller Inventory section for
completeness, but do NOT promote it to `proposed_abilities`. The route may
be useful to inventory; the proposed ability must represent a real
user/operator question or action.

For each proposed ability that passes the sanity check, fill in every
field in the `proposed_abilities` schema: `name`, `intent`, `backing`,
`permission`, `return_type`, `effort` (S/M/L), `annotations`
(readonly/destructive/idempotent), `notes`, `risks`, `use_case_fit`,
`side_effects`, `seed_data_needs`.

The last three are the implementation-readiness facts the implementer
and the verify-mode tooling both need: which human/agent workflow this
ability serves (`use_case_fit`), what the backing path emits on every
call (`side_effects` — empty array is a fact, not a missing value), and
what representative data must exist in the test environment for the
ability to execute through the public boundary (`seed_data_needs`).

### 5. Surface gaps and deferred items

Three buckets:

- **`excluded_from_mvp`** — candidates intentionally deferred for risk reasons
  (real-money writes, irreversible state changes, or prerequisite design
  work). Each entry gets a one-sentence reason.
- **`surfaced_gaps`** — MVP candidates with no backing endpoint (ability with
  `backing: null`), plus high-value endpoints discovered during enumeration
  that aren't in the MVP list but would be easy future wins.
- **Risks per ability** — anything about a backing endpoint that the
  implementer must handle (no idempotency key, two-phase behavior,
  state-transition caveats, zero-arg endpoints registered with
  `permission_callback => '__return_true'` that must NOT copy that into the
  ability registration).

### 6. Write the audit doc

Write to the explicit output path collected in "Inputs required". The
document structure must match `references/audit-schema.md` exactly:

1. `Last updated: YYYY-MM-DD HH:MM` header.
2. YAML block with all required top-level metadata + `proposed_abilities`,
   `excluded_from_mvp`, `surfaced_gaps`.
3. "Controller Inventory" table.
4. "Notes and Surprises" prose section.

A copy-pasteable minimal example showing the full shape lives in
`references/audit-schema.md` under "Minimal valid example" — start there
when authoring a new audit.

### 7. (Optional) Designate a reference implementation ability

Set `reference_ability: true` on the first ability an implementer should
land — typically the smallest, safest, highest-leverage read. This gives
downstream workflows a deterministic starting point.

## Verification

- The audit conforms to `references/audit-schema.md` (all required top-level
  fields present, at least one entry in `proposed_abilities`, annotations
  complete on every ability).
- `capability_gate` is a string for single-cap plugins or a `{read, write}`
  object for post-type-backed plugins.
- Every ability with `backing: null` also appears in `surfaced_gaps`.
- The doc round-trips through the validator in `audit-schema.md` "Known
  limitations" without errors.

## Failure modes / debugging

- **Plugin has no REST controllers** — audit doesn't apply. Consider
  hooks/filters-based abilities (out of scope for this skill's current
  version) or skip abilities adoption for this plugin.
- **Plugin inherits controllers from another repo** (common for plugins
  extending core post-type-backed controllers like `WP_REST_Posts_Controller`,
  or extension plugins built on a parent's REST classes) — capture with
  `backing.inherited_from: "<parent FQCN>"`. Line-number fields may be
  `null` per the schema.
- **Compound capability gate (distinct read/write caps)** — use the
  structured `{read, write}` form documented in
  `references/capability-gate-tracing.md`. Don't smuggle a `/`-separated
  string into a field typed as a single cap.
- **Ambiguous grouping** — route to
  `../wp-abilities-api/references/grouping-heuristic.md`. Do not invent
  alternative grouping rules in the audit doc.
- **Zero-arg endpoints with `permission_callback => '__return_true'`** —
  legal at the REST layer, but the ability's own `permission_callback` must
  match the plugin's merchant gate. Never promote `'__return_true'` into an
  ability registration. Note this in the ability's `risks`.
- **Output path defaults to plugin worktree** — always ask the user for an
  explicit output directory (e.g. their vault `plans/`). Writing the audit
  into the plugin's own git history pollutes the worktree and buries the
  artifact.

## Escalation

- If the plugin uses an enumeration convention not covered by
  `references/controller-enumeration.md` (neither the standard glob nor the
  grep fallback produces a complete inventory), update that reference with
  the new convention and open a PR so future audits cover it deterministically.
- If capability tracing hits a mechanism not covered by
  `references/capability-gate-tracing.md`, extend that file rather than
  encoding the new case in the audit's "Notes and Surprises" only.

<!-- chapter:end slug=wp-abilities-audit -->

---

<!-- chapter:begin slug=wp-abilities-verify position=5 -->

## 5. wp-abilities-verify

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-abilities-verify/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-verify/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-abilities-verify.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (6), referenced from this skill's directory:
  - `references/annotation-correctness.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-verify/references/annotation-correctness.md
  - `references/audit-schema-validation.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-verify/references/audit-schema-validation.md
  - `references/permission-roundtrip.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-verify/references/permission-roundtrip.md
  - `references/runtime-harness.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-verify/references/runtime-harness.md
  - `references/schema-lints.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-verify/references/schema-lints.md
  - `references/static-enumeration.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-abilities-verify/references/static-enumeration.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-abilities-verify
description: "Verify a WordPress plugin's Abilities API registrations: enumerate abilities, check that callback behavior matches each annotation's claim (the adversarial readonly-but-writes detection), validate permissions and schemas, and validate audit documents produced by wp-abilities-audit."
compatibility: "Targets WordPress 7.0+ plugins (PHP 7.4.0+). Requires a runnable environment (wp-env, docker-based dev stack, or equivalent) for runtime mode; static mode runs entirely from the plugin checkout with no env. Filesystem-based agent with bash + node."
---

# WP Abilities Verify

Verify a WordPress plugin's Abilities API registrations. The
centerpiece is the **adversarial annotation correctness check**: a
`readonly: true` ability that actually writes (via `$wpdb->update`,
`update_option`, a non-GET delegate, etc.) is a security and UX
disaster because agents plan actions on the basis of the annotations
they introspect. This skill catches those lies by reading the callback
body and comparing what it does against what the annotation claims.

The skill also validates audit docs produced by `wp-abilities-audit`,
checks permission gates and schema hygiene, and optionally executes
each ability against a live environment.

## When to use

- After abilities have been registered in a plugin but before a PR
  lands.
- As a health-check on an already-shipped plugin (catch regressions
  where a refactor turned a readonly ability into a writing one).
- To validate an audit document before handing it to an implementer.

## Two modes

- **Static mode** — runs from the plugin checkout. No env. Enumerates
  via source inspection, runs the adversarial correctness check, runs
  schema and permission lints, and validates audit docs.
- **Runtime mode** — requires a running env. Does everything static
  does PLUS: `wp_get_abilities()` for authoritative enumeration,
  executes each ability with curated inputs, confirms permission
  roundtrip against real users, and runs a twin-invocation heuristic
  on `idempotent: true` abilities to flag candidates for review
  (return-value equality is a signal, not a verdict — core defines
  idempotent as "no additional effect on the environment").

Both modes produce the same structured report format.

A static-mode PASS means "no obvious-shape violations," not "verified
write-free." For high-stakes plugins, run runtime mode before landing
— it catches bootstrap-order, permission-roundtrip, and idempotency
issues that static can't. See `references/annotation-correctness.md`
for the static blind spots.

## Inputs required

1. **Plugin checkout path** — working tree to verify.
2. **Mode** — `static` or `runtime`. Default to static if unspecified.
3. **(Runtime only) Env-up command** — read the plugin's `AGENTS.md`.
   Common patterns: `npm run wp-env start`, `npx wp-env start`, or a
   composer-based bring-up. Plugin families with their own dev tooling
   will document their own command. Do NOT assume `npm run wp-env`
   works.
4. **(Optional) Audit doc path** — enables cross-checks between the
   audit and the registered abilities, and validates the audit itself.
5. **Report output path** — explicit path, typically the user's vault.

## Prerequisites

- `wp-project-triage` has been run on the plugin.
- The plugin has at least one registered ability in source. Zero hits
  on `wp_register_ability(` → return a clear "no abilities registered"
  report, not an empty PASS.

## Procedure

### 1. (If audit provided) Validate the audit doc

Read `references/audit-schema-validation.md`. Validate the audit
against the canonical schema owned by `wp-abilities-audit`. Surface
missing required fields, multiple `reference_ability: true`, and
`backing: null` entries that aren't paired with a `surfaced_gaps`
entry. `backing: null` alone is WARN (intentional gap output), not
FAIL.

### 2. Enumerate abilities statically

Read `references/static-enumeration.md`. Find each
`wp_register_ability(` call, extract the name, the annotation block,
and the execute-callback location. Use a multi-line tool (`rg
--multiline --pcre2`) — the canonical formatting splits the call
across lines. Record each ability's source-file + line + annotations +
callback byte range.

### 3. (Runtime only) Enumerate via REST + wp-cli

Read `references/runtime-harness.md`. Bring the env up using the
command from `AGENTS.md`, then enumerate via `wp_get_abilities()` over
wp-cli and cross-check against the static inventory. Source-only →
FAIL (registration not firing). Runtime-only → WARN (dynamic
registration path).

### 4. Annotation correctness (the adversarial core)

Read `references/annotation-correctness.md`. Read each callback body
and verify it matches the annotation claim:

- `readonly: true` → callback must not write to the database, the
  options table, post / user / term / comment data, the filesystem,
  cron, or via non-GET HTTP / REST delegates.
- `destructive: false` → callback must not delete, refund, void,
  cancel, or trash.
- `idempotent: true` → repeated calls with the same input have no
  additional effect on the environment (per the `idempotent`
  annotation's docblock in `class-wp-ability.php`). Static catches
  counter writes and per-call cron schedules; runtime adds a
  twin-invocation heuristic for visible state changes.

The reference lists common write patterns as a starting set, not a
checklist — plugin vocabularies vary, and the agent extends with verbs
specific to the plugin under verification.

False positives get suppressed via an inline `// verify-ignore:
<annotation> -- <reason>` comment.

### 5. Permission roundtrip

Read `references/permission-roundtrip.md`. Static: classify each
`permission_callback` against the six shapes (preferred Shape A
`current_user_can(...)`; FAIL on Shape B-bad `WP_REST_Request`
patterns or Shape E literal `true`). Runtime: anon and subscriber
denied; admin allowed (unless deliberately public). When an audit was
provided, cross-check the registered cap against the audit's declared
gate.

### 6. Schema lints

Read `references/schema-lints.md`. Six small principles applied to
each ability's `input_schema`: object schemas declare
`additionalProperties`; required fields have descriptions; enums
non-empty; no `$ref`; defaults are statically constant (including
`(object) array()`); reference abilities have no required inputs.

Cross-reference `../wp-abilities-api/references/input-schema-gotchas.md`
for the four runtime gotchas (defaults not injected on the
property-level path, pagination key drift, `empty()` on string IDs,
direct vs indirect invocation strictness).

### 7. Error-code vocabulary

Cross-reference `../wp-abilities-api/references/error-code-vocabulary.md`.
Inspect each callback's `WP_Error` returns; non-vocabulary codes →
WARN.

## Verification

The run produces a structured markdown report at the user-specified
path:

```
---
Last updated: <YYYY-MM-DD HH:MM>
---

# <Plugin> Abilities Verification — <Static|Runtime> Mode

## Status: <PASS|WARN|FAIL>

## Audit doc validation (if provided)

## Static inventory

## Annotation correctness
| Ability | Claim | Result | Evidence |
|---|---|---|---|

## Permission gates

## Schema lints

## Error-code vocabulary
```

Every ability is OK, WARN, or FAIL. A single FAIL → top-line FAIL;
WARNs without FAILs → WARN; otherwise PASS.

## Failure modes / debugging

- **Env not reachable (runtime)** — env-up failed or Docker isn't
  running. Re-run `wp-project-triage`, then fix the env. Don't fall
  back silently to static without noting it in the report.
- **No abilities in source** — return a clear "nothing to verify"
  report.
- **Audit schema mismatch** — point at
  `references/audit-schema-validation.md`; don't auto-fix the audit.
- **False positive on readonly-writes** — see the `// verify-ignore`
  mechanism in `references/annotation-correctness.md`. Document why
  each suppression is legitimate.
- **Runtime enumeration smaller than static** — registration hook
  isn't firing. Check init hook timing, activation state, autoloader
  order.

## Escalation

- Recurring legitimate pattern that trips the adversarial check across
  multiple plugins → propose adding it to the suppression guidance in
  `annotation-correctness.md`. Don't broaden the candidate-pattern
  list speculatively.
- Audit-schema validator rejects a legitimate audit → the canonical
  schema in `../wp-abilities-audit/references/audit-schema.md` has
  evolved. Update `references/audit-schema-validation.md` to match.

## Out of scope

Token-budget measurement is a separate verification axis — an
annotation-clean, schema-clean, runtime-passing ability set can still
be unshippable if its `tools/list` form burns through an agent's
context budget. That axis is tracked separately. Do not aggregate
manual or external measurement into this skill's PASS / FAIL verdict.

<!-- chapter:end slug=wp-abilities-verify -->

---

<!-- chapter:begin slug=wp-block-development position=6 -->

## 6. wp-block-development

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-block-development/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-development/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-block-development.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (11), referenced from this skill's directory:
  - `references/attributes-and-serialization.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-development/references/attributes-and-serialization.md
  - `references/block-json.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-development/references/block-json.md
  - `references/creating-new-blocks.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-development/references/creating-new-blocks.md
  - `references/debugging.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-development/references/debugging.md
  - `references/deprecations.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-development/references/deprecations.md
  - `references/dynamic-rendering.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-development/references/dynamic-rendering.md
  - `references/inner-blocks.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-development/references/inner-blocks.md
  - `references/registration.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-development/references/registration.md
  - `references/supports-and-wrappers.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-development/references/supports-and-wrappers.md
  - `references/tooling-and-testing.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-development/references/tooling-and-testing.md
  - `scripts/list_blocks.mjs` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-development/scripts/list_blocks.mjs

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-block-development
description: "Use when developing WordPress (Gutenberg) blocks: block.json metadata, register_block_type(_from_metadata), attributes/serialization, supports, dynamic rendering (render.php/render_callback), deprecations/migrations, viewScript vs viewScriptModule, and @wordpress/scripts/@wordpress/create-block build and test workflows."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Filesystem-based agent with bash + node. Some workflows require WP-CLI."
---

# WP Block Development

## When to use

Use this skill for block work such as:

- creating a new block, or updating an existing one
- changing `block.json` (scripts/styles/supports/attributes/render/viewScriptModule)
- fixing “block invalid / not saving / attributes not persisting”
- adding dynamic rendering (`render.php` / `render_callback`)
- block deprecations and migrations (`deprecated` versions)
- build tooling for blocks (`@wordpress/scripts`, `@wordpress/create-block`, `wp-env`)

## Inputs required

- Repo root and target (plugin vs theme vs full site).
- The block name/namespace and where it lives (path to `block.json` if known).
- Target WordPress version range (especially if using modules / `viewScriptModule`).

## Procedure

### 0) Triage and locate blocks

1. Run triage:
   - `node skills/wp-project-triage/scripts/detect_wp_project.mjs`
2. List blocks (deterministic scan):
   - `node skills/wp-block-development/scripts/list_blocks.mjs`
3. Identify the block root (directory containing `block.json`) you’re changing.

If this repo is a full site (`wp-content/` present), be explicit about *which* plugin/theme contains the block.

### 1) Create a new block (if needed)

If you are creating a new block, prefer scaffolding rather than hand-rolling structure:

- Use `@wordpress/create-block` to scaffold a modern block/plugin setup.
- If you need Interactivity API from day 1, use the interactive template.

Read:
- `references/creating-new-blocks.md`

After scaffolding:

1. Re-run the block list script and confirm the new block root.
2. Continue with the remaining steps (model choice, metadata, registration, serialization).

### 2) Ensure apiVersion 3 (WordPress 6.9+)

WordPress 6.9 enforces `apiVersion: 3` in the block.json schema. Blocks with apiVersion 2 or lower trigger console warnings when `SCRIPT_DEBUG` is enabled.

**Why this matters:**
- WordPress 7.0 will run the post editor in an iframe regardless of block apiVersion.
- apiVersion 3 ensures your block works correctly inside the iframed editor (style isolation, viewport units, media queries).

**Migration:** Changing from version 2 to 3 is usually as simple as updating the `apiVersion` field in `block.json`. However:
- Test in a local environment with the iframe editor enabled.
- Ensure any style handles are included in `block.json` (styles missing from the iframe won't apply).
- Third-party scripts attached to a specific `window` may have scoping issues.

Read:
- `references/block-json.md` (apiVersion and schema details)

### 3) Pick the right block model

- **Static block** (markup saved into post content): implement `save()`; keep attributes serialization stable.
- **Dynamic block** (server-rendered): use `render` in `block.json` (or `render_callback` in PHP) and keep `save()` minimal or `null`.
- **Interactive frontend behavior**:
  - Prefer `viewScriptModule` for modern module-based view scripts where supported.
  - If you're working primarily on `data-wp-*` directives or stores, also use `wp-interactivity-api`.

### 4) Update `block.json` safely

Make changes in the block’s `block.json`, then confirm registration matches metadata.

For field-by-field guidance, read:
- `references/block-json.md`

Common pitfalls:

- changing `name` breaks compatibility (treat it as stable API)
- changing saved markup without adding `deprecated` causes “Invalid block”
- adding attributes without defining source/serialization correctly causes “attribute not saving”

### 5) Register the block (server-side preferred)

Prefer PHP registration using metadata, especially when:

- you need dynamic rendering
- you need translations (`wp_set_script_translations`)
- you need conditional asset loading

Read and apply:
- `references/registration.md`

### 6) Implement edit/save/render patterns

Follow wrapper attribute best practices:

- Editor: `useBlockProps()`
- Static save: `useBlockProps.save()`
- Dynamic render (PHP): `get_block_wrapper_attributes()`

Read:
- `references/supports-and-wrappers.md`
- `references/dynamic-rendering.md` (if dynamic)

### 7) Inner blocks (block composition)

If your block is a “container” that nests other blocks, treat Inner Blocks as a first-class feature:

- Use `useInnerBlocksProps()` to integrate inner blocks with wrapper props.
- Keep migrations in mind if you change inner markup.

Read:
- `references/inner-blocks.md`

### 8) Attributes and serialization

Before changing attributes:

- confirm where the attribute value lives (comment delimiter vs HTML vs context)
- avoid the deprecated `meta` attribute source

Read:
- `references/attributes-and-serialization.md`

### 9) Migrations and deprecations (avoid "Invalid block")

If you change saved markup or attributes:

1. Add a `deprecated` entry (newest → oldest).
2. Provide `save` for old versions and an optional `migrate` to normalize attributes.

Read:
- `references/deprecations.md`

### 10) Tooling and verification commands

Prefer whatever the repo already uses:

- `@wordpress/scripts` (common) → run existing npm scripts
- `wp-env` (common) → use for local WP + E2E

Read:
- `references/tooling-and-testing.md`

## Verification

- Block appears in inserter and inserts successfully.
- Saving + reloading does not create “Invalid block”.
- Frontend output matches expectations (static: saved markup; dynamic: server output).
- Assets load where expected (editor vs frontend).
- Run the repo’s lint/build/tests that triage recommends.

## Failure modes / debugging

If something fails, start here:

- `references/debugging.md` (common failures + fastest checks)
- `references/attributes-and-serialization.md` (attributes not saving)
- `references/deprecations.md` (invalid block after change)

## Escalation

If you’re uncertain about upstream behavior/version support, consult canonical docs first:

- WordPress Developer Resources (Block Editor Handbook, Theme Handbook, Plugin Handbook)
- Gutenberg repo docs for bleeding-edge behaviors

<!-- chapter:end slug=wp-block-development -->

---

<!-- chapter:begin slug=wp-block-themes position=7 -->

## 7. wp-block-themes

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-block-themes/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-themes/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-block-themes.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (7), referenced from this skill's directory:
  - `references/creating-new-block-theme.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-themes/references/creating-new-block-theme.md
  - `references/debugging.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-themes/references/debugging.md
  - `references/patterns.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-themes/references/patterns.md
  - `references/style-variations.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-themes/references/style-variations.md
  - `references/templates-and-parts.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-themes/references/templates-and-parts.md
  - `references/theme-json.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-themes/references/theme-json.md
  - `scripts/detect_block_themes.mjs` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-block-themes/scripts/detect_block_themes.mjs

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-block-themes
description: "Use when developing WordPress block themes: theme.json (global settings/styles), templates and template parts, patterns, style variations, and Site Editor troubleshooting (style hierarchy, overrides, caching)."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Filesystem-based agent with bash + node. Some workflows require WP-CLI."
---

# WP Block Themes

## When to use

Use this skill for block theme work such as:

- editing `theme.json` (presets, settings, styles, per-block styles)
- adding or changing templates (`templates/*.html`) and template parts (`parts/*.html`)
- adding patterns (`patterns/*.php`) and controlling what appears in the inserter
- adding style variations (`styles/*.json`)
- debugging “styles not applying” / “editor doesn’t reflect theme.json”

## Inputs required

- Repo root and which theme is targeted (theme directory if multiple exist).
- Target WordPress version range (theme.json version and features vary by core version).
- Where the issue manifests: Site Editor, post editor, frontend, or all.

## Procedure

### 0) Triage and locate block theme roots

1. Run triage:
   - `node skills/wp-project-triage/scripts/detect_wp_project.mjs`
2. Detect theme roots + key folders:
   - `node skills/wp-block-themes/scripts/detect_block_themes.mjs`

If multiple themes exist, pick one and scope all changes to that theme root.

### 1) Create a new block theme (if needed)

If you are creating a new block theme from scratch (or converting a classic theme):

- Prefer starting from a known-good scaffold (or exporting from a WP environment) rather than guessing file layout.
- Be explicit about the minimum supported WordPress version because `theme.json` schema versions differ.

Read:
- `references/creating-new-block-theme.md`

After creating the theme root, re-run `detect_block_themes` and continue below.

### 2) Confirm theme type and override expectations

- Block theme indicators:
  - `theme.json` present
  - `templates/` and/or `parts/` present
- Remember the style hierarchy:
  - core defaults → theme.json → child theme → user customizations
  - user customizations can make theme.json edits appear “ignored”

Read:
- `references/debugging.md` (style hierarchy + fastest checks)

### 3) Make `theme.json` changes safely

Decide whether you are changing:

- **settings** (what the UI allows): presets, typography scale, colors, layout, spacing
- **styles** (how it looks by default): CSS-like rules for elements/blocks

Read:
- `references/theme-json.md`

### 4) Templates and template parts

- Templates live under `templates/` and are HTML.
- Template parts live under `parts/` and must not be nested in subdirectories.

Read:
- `references/templates-and-parts.md`

### 5) Patterns

Prefer filesystem patterns under `patterns/` when you want theme-owned patterns.

Read:
- `references/patterns.md`

### 6) Style variations

Style variations are JSON files under `styles/`. Note: once a user picks a style variation, that selection is stored in the DB, so changing the file may not “update what the user sees” automatically.

Read:
- `references/style-variations.md`

## Verification

- Site Editor reflects changes where expected (Styles UI, templates, patterns).
- Frontend renders with expected styles.
- If styles aren’t changing, confirm whether user customizations override theme defaults.
- Run the repo’s build/lint scripts if assets are involved (fonts, custom JS/CSS build).

## Failure modes / debugging

Start with:

- `references/debugging.md`

Common issues:

- wrong theme root (editing an inactive theme)
- user customizations override your defaults
- invalid `theme.json` shape/typos prevent application
- templates/parts in wrong folders (or nested parts)

## Escalation

If upstream behavior is unclear, consult canonical docs:

- Theme Handbook and Block Editor Handbook for `theme.json`, templates, patterns, and style variations.

<!-- chapter:end slug=wp-block-themes -->

---

<!-- chapter:begin slug=wp-env position=8 -->

## 8. wp-env

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-env/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-env/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-env.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-env
description: "Use when setting up, configuring, or troubleshooting local WordPress development environments with @wordpress/env (wp-env). Triggers on mentions of wp-env, local WordPress development, Docker-based WordPress, or requests to start/stop/configure a local WordPress instance."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Requires Docker and Node.js 18.12+."
---

# @wordpress/env (wp-env)

Zero-config, Docker-based local WordPress development environment for plugins, themes, and core.

## When to use

- User asks to set up a local WordPress development environment
- Project contains a `.wp-env.json` file
- User mentions `wp-env`, `@wordpress/env`, or Docker-based WordPress development
- User wants to run WP-CLI commands, PHPUnit tests, or debug with Xdebug locally
- Triage detects a plugin or theme project that needs a local WordPress instance

**wp-env or wp-playground?** For a quick local WordPress, prefer the **wp-playground** skill by default — it's faster, disposable, and needs no Docker. Use **wp-env** when the task actually requires it:

- the repo already contains a `.wp-env.json`
- a real MySQL database is needed (PHPUnit against the DB, `wp db` commands, data that survives restarts)
- arbitrary commands must run inside the environment (shell access, Composer, full WP-CLI via `wp-env run`)
- this is a WordPress core or Gutenberg checkout
- the user explicitly asks for wp-env or Docker

wp-env can also run without Docker by using Playground as its runtime (`npx @wordpress/env start --runtime=playground`, experimental). That is still wp-env, driven by the same `.wp-env.json`, so treat it as a Docker-free fallback for a project that already carries wp-env config, not as a third option. It swaps MySQL for SQLite and drops `wp-env run` and the separate tests environment, which are most of the reasons to pick wp-env in the first place. For a quick Docker-free WordPress with no wp-env config, use the **wp-playground** skill directly.

## Inputs required

1. **Docker status** -- verify Docker is installed and running: `docker info`
2. **Node.js version** -- must be >= 18.12.0: `node -v`
3. **Project type** -- plugin, theme, or full site (check for `.wp-env.json`, plugin headers, or `style.css` theme headers)
4. **Existing config** -- read `.wp-env.json` and `.wp-env.override.json` if present

## Procedure

### 1. Install wp-env

```sh
# Global (recommended)
npm -g install @wordpress/env

# Or project-local
npm i @wordpress/env --save-dev
# Then use: npx wp-env start
```

### 2. Start the environment

```sh
wp-env start
```

Default credentials:
- **URL:** http://localhost:8888/wp-admin/
- **Username:** `admin`
- **Password:** `password`

Common start options:
- `wp-env start --update` -- pull latest sources and reconfigure
- `wp-env start --xdebug` -- enable Xdebug (debug mode)
- `wp-env start --xdebug=profile,trace` -- multiple Xdebug modes
- `wp-env start --auto-port` -- find available ports when defaults are busy

### 3. Auto-detection (no config file)

When no `.wp-env.json` exists, wp-env scans the current directory:

| Detected type | How detected | Auto-config |
|--------------|-------------|-------------|
| Plugin | `Plugin Name:` header in a root `.php` file | `{ "plugins": ["."] }` |
| Theme | `Theme Name:` header in `style.css` | `{ "themes": ["."] }` |
| Core | `wp-includes/version.php` exists | `{ "core": "." }` |

### 4. Configure with `.wp-env.json`

Place at the project root. All fields are optional.

```json
{
  "core": null,
  "phpVersion": "8.1",
  "plugins": [
    ".",
    "https://downloads.wordpress.org/plugin/akismet.zip",
    "WordPress/classic-editor"
  ],
  "themes": [],
  "port": 8888,
  "multisite": false,
  "phpmyadmin": false,
  "config": {
    "WP_DEBUG": true,
    "SCRIPT_DEBUG": true
  },
  "mappings": {
    "wp-content/mu-plugins": "./mu-plugins"
  },
  "lifecycleScripts": {
    "afterStart": "wp-env run cli wp rewrite structure /%postname%/"
  }
}
```

#### Source string formats (for `core`, `plugins`, `themes`, `mappings`)

| Format | Example |
|--------|---------|
| Local path | `"."`, `"./path"`, `"../path"` |
| GitHub shorthand | `"WordPress/classic-editor"`, `"owner/repo#branch"` |
| ZIP URL | `"https://downloads.wordpress.org/plugin/akismet.zip"` |
| Git SSH | `"ssh://user@host/repo.git#ref"` |

**GOTCHA:** WordPress.org plugin/theme slugs (bare names like `"akismet"`) do NOT work. Use the full ZIP URL.

#### Local overrides with `.wp-env.override.json`

Create `.wp-env.override.json` next to `.wp-env.json` for personal settings (gitignored). Only `config` and `mappings` are **merged** -- all other fields (including `plugins` and `themes` arrays) **fully replace** the base.

### 5. Run commands in containers

```sh
# WP-CLI commands
wp-env run cli wp user list
wp-env run cli wp plugin list
wp-env run cli wp option update blogname "My Site"
wp-env run cli "wp rewrite structure /%postname%/"

# Run commands in a specific directory
wp-env run cli --env-cwd=wp-content/plugins/my-plugin composer install

# PHPUnit tests
wp-env run cli --env-cwd=wp-content/plugins/my-plugin vendor/bin/phpunit

# Pass flags with -- separator
wp-env run cli php -- --version

# MySQL access
wp-env run mysql mysql -- --user=root --password=password wordpress
```

Available containers: `mysql`, `wordpress`, `cli`, `composer`, `phpmyadmin`.

### 6. Manage the environment

```sh
wp-env stop                    # Stop and free ports
wp-env reset development       # Reset dev database (keeps test)
wp-env reset all               # Reset all databases
wp-env logs                    # Stream PHP/Docker logs
wp-env logs --no-watch         # Print logs and exit
wp-env status                  # Show URLs, ports, config
wp-env status --json           # Machine-readable status
wp-env cleanup                 # Remove containers/volumes (keep images)
wp-env destroy                 # Remove everything including images
```

### 7. Xdebug setup

```sh
wp-env start --xdebug          # Enable debug mode
wp-env start --xdebug=coverage # For code coverage
wp-env start                   # Disable Xdebug (restart without flag)
```

Modes: `debug`, `profile`, `trace`, `develop`, `coverage`.

IDE listens on port **9003**. VS Code `launch.json` needs:
```json
{
  "type": "php",
  "request": "launch",
  "name": "Listen for Xdebug",
  "port": 9003,
  "pathMappings": {
    "/var/www/html/wp-content/plugins/your-plugin": "${workspaceFolder}"
  }
}
```

### 8. Multisite

```json
{ "multisite": true, "plugins": ["."] }
```

## Verification

- [ ] `wp-env status` shows running containers with correct ports
- [ ] `http://localhost:8888/wp-admin/` loads the WordPress admin
- [ ] `wp-env run cli wp plugin list` shows expected plugins
- [ ] Plugin/theme under development appears in the WordPress admin
- [ ] Database resets work: `wp-env reset development`

## Failure modes / debugging

| Symptom | Cause | Fix |
|---------|-------|-----|
| "Cannot connect to Docker daemon" | Docker not running | Start Docker Desktop |
| "Port 8888 already in use" | Port conflict | Use `--auto-port` or set custom `port` in `.wp-env.json` |
| Plugin not appearing | Missing `Plugin Name:` header in main PHP file | Add standard plugin header comment |
| "Could not find a valid source" | Invalid source string in config | Use full ZIP URL for wp.org plugins, not bare slugs |
| Stale environment after source changes | Cached Docker volumes | `wp-env start --update` or `wp-env destroy && wp-env start` |
| White screen / PHP errors | Corrupted database | `wp-env reset all && wp-env start` |
| Override not taking effect | Wrong merge behavior | `plugins`/`themes` in override **replace** base arrays; only `config`/`mappings` merge |
| Tests environment not accessible | Wrong port | Test environment runs on port 8889 by default |
| Xdebug not connecting | IDE not listening or wrong port | Ensure IDE listens on port 9003 with correct `pathMappings` |
| npm global install permission error | Node installed to a system path | Use `nvm`, or install locally: `npm i -D @wordpress/env` and run via `npx wp-env` |

## Escalation

- Docker issues beyond wp-env scope (networking, disk space, WSL2 backend)
- Custom Docker Compose configurations that conflict with wp-env
- CI/CD pipeline integration requiring non-standard Docker setups
- WordPress Playground runtime (`--runtime=playground`) is experimental and has limited feature parity

## References

- [Official documentation](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/)
- [Getting started guide](https://developer.wordpress.org/block-editor/getting-started/devenv/get-started-with-wp-env/)
- [Source code](https://github.com/WordPress/gutenberg/tree/trunk/packages/env)

<!-- chapter:end slug=wp-env -->

---

<!-- chapter:begin slug=wp-interactivity-api position=9 -->

## 9. wp-interactivity-api

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-interactivity-api/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-interactivity-api/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-interactivity-api.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (3), referenced from this skill's directory:
  - `references/debugging.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-interactivity-api/references/debugging.md
  - `references/directives-quickref.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-interactivity-api/references/directives-quickref.md
  - `references/server-side-rendering.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-interactivity-api/references/server-side-rendering.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-interactivity-api
description: "Use when building or debugging WordPress Interactivity API features (data-wp-* directives, @wordpress/interactivity store/state/actions, block viewScriptModule integration, wp_interactivity_*()) including performance, hydration, and directive behavior."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Filesystem-based agent with bash + node. Some workflows require WP-CLI."
---

# WP Interactivity API

## When to use

Use this skill when the user mentions:

- Interactivity API, `@wordpress/interactivity`,
- `data-wp-interactive`, `data-wp-on--*`, `data-wp-bind--*`, `data-wp-context`,
- block `viewScriptModule` / module-based view scripts,
- hydration issues or “directives don’t fire”.

## Inputs required

- Repo root + triage output (`wp-project-triage`).
- Which block/theme/plugin surfaces are affected (frontend, editor, both).
- Any constraints: WP version, whether modules are supported in the build.

## Procedure

### 1) Detect existing usage + integration style

Search for:

- `data-wp-interactive`
- `@wordpress/interactivity`
- `viewScriptModule`

Decide:

- Is this a block providing interactivity via `block.json` view script module?
- Is this theme-level interactivity?
- Is this plugin-side “enhance existing markup” usage?

If you’re creating a new interactive block (not just debugging), prefer the official scaffold template:

- `@wordpress/create-block-interactive-template` (via `@wordpress/create-block`)

### 2) Identify the store(s)

Locate store definitions and confirm:

- state shape,
- actions (mutations),
- callbacks/event handlers used by `data-wp-on--*`.

### 3) Server-side rendering (best practice)

**Pre-render HTML on the server** before outputting to ensure:

- Correct initial state in the HTML before JavaScript loads (no layout shift).
- SEO benefits and faster perceived load time.
- Seamless hydration when the client-side JavaScript takes over.

#### Enable server directive processing

For components using `block.json`, add `supports.interactivity`:

```json
{
  "supports": {
    "interactivity": true
  }
}
```

For themes/plugins without `block.json`, use `wp_interactivity_process_directives()` to process directives.

#### Initialize state/context in PHP

Use `wp_interactivity_state()` to define initial global state:

```php
wp_interactivity_state( 'myPlugin', array(
  'items'    => array( 'Apple', 'Banana', 'Cherry' ),
  'hasItems' => true,
));
```

For local context, use `wp_interactivity_data_wp_context()`:

```php
<?php
$context = array( 'isOpen' => false );
?>
<div <?php echo wp_interactivity_data_wp_context( $context ); ?>>
  ...
</div>
```

#### Define derived state in PHP

When derived state affects initial HTML rendering, replicate the logic in PHP:

```php
wp_interactivity_state( 'myPlugin', array(
  'items'    => array( 'Apple', 'Banana' ),
  'hasItems' => function() {
    $state = wp_interactivity_state();
    return count( $state['items'] ) > 0;
  }
));
```

This ensures directives like `data-wp-bind--hidden="!state.hasItems"` render correctly on first load.

For detailed examples and patterns, see `references/server-side-rendering.md`.

### 4) Implement or change directives safely

When touching markup directives:

- keep directive usage minimal and scoped,
- prefer stable data attributes that map clearly to store state,
- ensure server-rendered markup + client hydration align.

**WordPress 6.9 changes:**

- **`data-wp-ignore` is deprecated** and will be removed in future versions. It broke context inheritance and caused issues with client-side navigation. Avoid using it.
- **Unique directive IDs**: Multiple directives of the same type can now exist on one element using the `---` separator (e.g., `data-wp-on--click---plugin-a="..."` and `data-wp-on--click---plugin-b="..."`).
- **New TypeScript types**: `AsyncAction<ReturnType>` and `TypeYield<T>` help with async action typing.

For quick directive reminders, see `references/directives-quickref.md`.

### 5) Build/tooling alignment

Verify the repo supports the required module build path:

- if it uses `@wordpress/scripts`, prefer its conventions.
- if it uses custom bundling, confirm module output is supported.

### 6) Debug common failure modes

If “nothing happens” on interaction:

- confirm the `viewScriptModule` is enqueued/loaded,
- confirm the DOM element has `data-wp-interactive`,
- confirm the store namespace matches the directive’s value,
- confirm there are no JS errors before hydration.

See `references/debugging.md`.

## Verification

- `wp-project-triage` indicates `signals.usesInteractivityApi: true` after your change (if applicable).
- Manual smoke test: directive triggers and state updates as expected.
- If tests exist: add/extend Playwright E2E around the interaction path.

## Failure modes / debugging

- Directives present but inert:
  - view script not loading, wrong module entrypoint, or missing `data-wp-interactive`.
- Hydration mismatch / flicker:
  - server markup differs from client expectations; simplify or align initial state.
  - derived state not defined in PHP: use `wp_interactivity_state()` with closures.
- Initial content missing or wrong:
  - `supports.interactivity` not set in `block.json` (for blocks).
  - `wp_interactivity_process_directives()` not called (for themes/plugins).
  - state/context not initialized in PHP before render.
- Layout shift on load:
  - derived state like `state.hasItems` missing on server, causing `hidden` attribute to be absent.
- Performance regressions:
  - overly broad interactive roots; scope interactivity to smaller subtrees.
- Client-side navigation issues (WordPress 6.9):
  - `getServerState()` and `getServerContext()` now reset between page transitions—ensure your code doesn't assume stale values persist.
  - Router regions now support `attachTo` for rendering overlays (modals, pop-ups) dynamically.

## Escalation

- If repo build constraints are unclear, ask: "Is this using `@wordpress/scripts` or a custom bundler (webpack/vite)?"
- Consult:
  - `references/server-side-rendering.md`
  - `references/directives-quickref.md`
  - `references/debugging.md`

<!-- chapter:end slug=wp-interactivity-api -->

---

<!-- chapter:begin slug=wp-patterns position=10 -->

## 10. wp-patterns

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-patterns/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-patterns/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-patterns.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (7), referenced from this skill's directory:
  - `references/anti-patterns.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-patterns/references/anti-patterns.md
  - `references/block-markup-reference.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-patterns/references/block-markup-reference.md
  - `references/design-with-tokens.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-patterns/references/design-with-tokens.md
  - `references/example-prompts.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-patterns/references/example-prompts.md
  - `references/pattern-categories-and-types.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-patterns/references/pattern-categories-and-types.md
  - `references/pattern-registration.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-patterns/references/pattern-registration.md
  - `references/visual-composition.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-patterns/references/visual-composition.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-patterns
description: "Pattern: create or update WordPress block patterns (starter pages, templates, template parts, Query Loop layouts), review pattern registration, block markup, categories, accessibility, or i18n/escaping, or improve pattern design quality. Route custom blocks to wp-block-development; route frontend interactivity to wp-interactivity-api."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Filesystem-based agent with bash + node. Some workflows require WP-CLI."
---

# WordPress Block Patterns

## Inputs required

- Repo root and target theme/plugin directory.
- Pattern type: section, starter page, template, template part, or manually registered plugin pattern.
- Theme/plugin slug, pattern slug, and text domain.
- Pattern title, categories, keywords, block types, template types, and inserter visibility.
- Target WordPress version if it differs from this repo's compatibility contract.
- Available `theme.json` presets for colors, typography, spacing, layout, and gradients.
- Asset paths for images/icons, including whether assets are decorative or informational.
- Verification environment: WordPress Playground, wp-env, local WordPress, or manual Code Editor check.
- If updating an existing pattern: current slug, current file path, and whether existing inserted content must remain compatible.
- For child themes: child theme slug, text domain, and asset root; do not reuse the parent namespace unless explicitly intended.

## Guardrails

1. **Block markup only** — express all visual design through block comment attributes and `preset` slugs. No inline `<style>` tags, no custom CSS classes, no arbitrary HTML outside of block wrappers. Read `references/design-with-tokens.md` for the core principle.

2. **No JavaScript** — patterns are static `block markup`. For interactivity, use blocks that natively support it (Navigation, Search, Query Loop).

3. **Registration-time PHP** — pattern files execute PHP once during registration, not at render. Read `references/pattern-registration.md` for safe output functions, i18n, and functions to avoid.

4. **Valid nesting** — read `references/block-markup-reference.md` for comment syntax and nesting rules.

5. **Native blocks for behavior** — use Query Loop, Search, Navigation, Social Icons, or an existing form block instead of custom PHP/HTML behavior. For newsletter, donation, payment, or map behavior, create a CTA/placeholder or use an existing block/plugin.

6. **Local assets** — use `get_theme_file_uri()` with `esc_url()`; no external placeholder URLs unless the user approves. Read `references/pattern-registration.md` and `references/anti-patterns.md` for examples.

## Procedure

### 0) Triage and locate the pattern target

1. Run triage when working in a repository:
   - `node skills/wp-project-triage/scripts/detect_wp_project.mjs`
2. For block themes, locate the target theme root:
   - `node skills/wp-block-themes/scripts/detect_block_themes.mjs`
3. Confirm the pattern belongs in a theme `patterns/` directory or needs manual plugin registration.
4. If multiple themes/plugins exist, scope all changes to the requested target.

If the user did not provide required inputs, infer only low-risk defaults. Ask before inventing a theme slug, text domain, asset path, custom post type, taxonomy, event date field, or theme-specific `preset`. If `theme.json` is missing or presets cannot be verified, use conservative core presets or ask before using theme-specific slugs.

**Done when:** target theme/plugin root, pattern type, and registration path are confirmed.

### 1) Design thinking

Make five deliberate design decisions — purpose, tone, spatial composition, typography hierarchy, and color strategy — before writing `block markup`.

Read `references/design-with-tokens.md` for the decision framework and `preset` mapping.

For pattern-type metadata (starter pages, template patterns, template parts, query loops, forms/CTAs, comparison/pricing, social/navigation/search, 404), read `references/pattern-categories-and-types.md` — including the Query Loop patterns section when using `core/query`.

When the request calls for a visually _distinctive_ composition, read `references/visual-composition.md`.

**Done when:** all five design decisions are made and recorded before markup.

### 2) Plan block structure

Sketch the nesting tree before writing markup. Example for a hero pattern:

```
Group (full-width, constrained layout, dark bg, vertical padding 80)
  Group (constrained inner, flex vertical, center align)
    Paragraph (uppercase label, small, letter-spacing, accent color)
    Heading (h2, xx-large, heading font, tight line-height)
    Paragraph (lead text, large, secondary color)
    Buttons (flex, center)
      Button (primary bg, base text)
      Button (outline style)
```

**Done when:** nesting tree is sketched and hierarchy is intentional before writing comment tags.

### 3) Write the pattern file

Assemble the PHP header and `block markup`. Read `references/pattern-registration.md` for header fields, PHP rules, manual registration, and file examples.

Use categories and template types from step 1. Read `references/pattern-categories-and-types.md` when header metadata was not decided in step 1.

**Block markup body:**
- Follow the nesting tree from step 2
- Use `preset` slugs for colors, font sizes, spacing
- Use placeholder text that reflects real content — not "Lorem ipsum"

**Done when:** PHP header and block markup file are written.

### 4) Design quality check

Review against the Design Quality Checklist in `references/anti-patterns.md`. Every item must pass.

**Done when:** every Design Quality Checklist item passes.

### 5) Technical validation

Review against the Technical Validation Checklist in `references/anti-patterns.md`. Every item must pass.

**Done when:** every Technical Validation Checklist item passes.

## Verification

Test the pattern in a real WordPress environment:

**Using WordPress Playground (recommended):**
```bash
npx @wp-playground/cli@latest server --auto-mount
```
Mount the theme directory and verify:
- Pattern appears in inserter under specified categories
- Pattern inserts without block validation errors
- Layout renders correctly at desktop and mobile widths
- Content is editable (text, images, buttons)
- If `templateLock` is used, locked elements resist editing

For template patterns, verify the Site Editor offers the pattern in the expected template replacement flow. If `Inserter: no` is used, confirm it is hidden from the general inserter but still available where intended.

**Manual check:**
- Paste block markup into the Code Editor view in WordPress
- Switch to Visual Editor — blocks should parse without "Attempt Block Recovery" prompts
- If recovery is needed, the markup has syntax errors

Run the repo's existing lint, build, or test commands if the pattern change touches assets, generated files, or registration code.

When updating an existing pattern, remember that inserted pattern content is copied into posts/templates. Changing the pattern file does not retroactively update already inserted content, and changing block names or saved markup can create recovery prompts for newly inserted content.

For PR or package review, confirm the diff is scoped to the intended pattern files, references, scripts, and eval scenarios. Do not mix unrelated repo updates into a pattern change.

## Failure modes / debugging

Start with `references/block-markup-reference.md`, `references/pattern-registration.md`, and `references/anti-patterns.md`.

Common failures:

- **Pattern missing from inserter**: check required `Title`, `Slug`, and `Categories` headers; confirm the file is under `patterns/*.php`; confirm `Inserter: no` is not hiding it.
- **Wrong pattern shown or overwritten**: check for slug collisions and ensure the slug is namespaced as `theme-slug/pattern-name` or `plugin-slug/pattern-name`.
- **Block recovery prompt appears**: validate block comment nesting, JSON syntax, and closing comments.
- **Strings are not translated or escaped**: replace raw text/PHP output with `esc_html_e()`, `esc_html__()`, `esc_attr_e()`, `esc_attr__()`, or `esc_url()` as appropriate.
- **Translations do not load**: verify the text domain matches the target theme/plugin.
- **Dynamic content is stale or unavailable**: remove query-dependent PHP (`get_posts()`, `the_title()`, `wp_get_current_user()`) and use blocks such as Query Loop instead.
- **Query Loop output is incomplete**: check for `core/post-template`, post title/excerpt/date/image blocks, pagination when needed, and `core/query-no-results` fallback.
- **Archive/search/category/author context is wrong**: use inherited query context instead of hardcoded runtime PHP.
- **CPT or event listings are wrong**: confirm post type slugs, taxonomy/date fields, and plugin-provided blocks before generating the pattern.
- **Styles do not match the theme**: confirm `preset` slugs exist in `theme.json`; avoid unsupported theme-specific slugs unless documented.
- **Accessibility issues**: fix skipped heading levels, empty alt text for informational images, low-contrast preset combinations, vague button/link text, social icon labels, search labels, and color-only emphasis.
- **Manual registration fails**: confirm the code runs on `init`, categories are registered before patterns, and pattern content remains static block markup.

## Escalation

Stop and ask for help or consult canonical docs when:

- Theme-specific `preset` slugs, text domains, asset paths, or pattern categories cannot be verified.
- Color contrast, image meaning, or content hierarchy needs human design/accessibility judgment.
- Behavior depends on a WordPress/Gutenberg version not covered by this repo's compatibility contract.

## Example prompts

Read `references/example-prompts.md`.

<!-- chapter:end slug=wp-patterns -->

---

<!-- chapter:begin slug=wp-performance position=11 -->

## 11. wp-performance

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-performance/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-performance/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-performance.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (11), referenced from this skill's directory:
  - `references/autoload-options.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-performance/references/autoload-options.md
  - `references/cron.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-performance/references/cron.md
  - `references/database.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-performance/references/database.md
  - `references/http-api.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-performance/references/http-api.md
  - `references/measurement.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-performance/references/measurement.md
  - `references/object-cache.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-performance/references/object-cache.md
  - `references/query-monitor-headless.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-performance/references/query-monitor-headless.md
  - `references/server-timing.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-performance/references/server-timing.md
  - `references/wp-cli-doctor.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-performance/references/wp-cli-doctor.md
  - `references/wp-cli-profile.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-performance/references/wp-cli-profile.md
  - `scripts/perf_inspect.mjs` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-performance/scripts/perf_inspect.mjs

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-performance
description: "Use when investigating or improving WordPress performance (backend-only agent): profiling and measurement (WP-CLI profile/doctor, Server-Timing, Query Monitor via REST headers), database/query optimization, autoloaded options, object caching, cron, HTTP API calls, and safe verification."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Backend-only agent; prefers WP-CLI (doctor/profile) when available."
---

# WP Performance (backend-only)

## When to use

Use this skill when:

- a WordPress site/page/endpoint is slow (frontend TTFB, admin, REST, WP-Cron)
- you need a profiling plan and tooling recommendations (WP-CLI profile/doctor, Query Monitor, Xdebug/XHProf, APMs)
- you’re optimizing DB queries, autoloaded options, object caching, cron tasks, or remote HTTP calls

This skill assumes the agent cannot use a browser UI. Prefer WP-CLI, logs, and HTTP requests.

## Inputs required

- Environment and safety: dev/staging/prod, any restrictions (no writes, no plugin installs).
- How to target the install:
  - WP root `--path=<path>`
  - (multisite/site targeting) `--url=<url>`
- The performance symptom and scope:
  - which URL/REST route/admin screen
  - when it happens (always vs sporadic; logged-in vs logged-out)

## Procedure

### 0) Guardrails: measure first, avoid risky ops

1. Confirm whether you may run write operations (plugin installs, config changes, cache flush).
2. Pick a reproducible target (URL or REST route) and capture a baseline:
   - TTFB/time with `curl` if possible
   - WP-CLI profiling if available

Read:
- `references/measurement.md`

### 1) Generate a backend-only performance report (deterministic)

Run:

- `node skills/wp-performance/scripts/perf_inspect.mjs --path=<path> [--url=<url>]`

This detects:

- WP-CLI availability and core version
- whether `wp doctor` / `wp profile` are available
- autoloaded options size (if possible)
- object-cache drop-in presence

### 2) Fast wins: run diagnostics before deep profiling

If you have WP-CLI access, prefer:

- `wp doctor check`

It catches common production foot-guns (autoload bloat, SAVEQUERIES/WP_DEBUG, plugin counts, updates).

Read:
- `references/wp-cli-doctor.md`

### 3) Deep profiling (no browser required)

Preferred order:

1. `wp profile stage` to see where time goes (bootstrap/main_query/template).
2. `wp profile hook` (optionally with `--url=`) to find slow hooks/callbacks.
3. `wp profile eval` for targeted code paths.

Read:
- `references/wp-cli-profile.md`

### 4) Query Monitor (backend-only usage)

Query Monitor is normally UI-driven, but it can be used headlessly via REST API response headers and `_envelope` responses:

- Authenticate (nonce or Application Password).
- Request REST responses and inspect headers (`x-qm-*`) and/or the `qm` property when using `?_envelope`.

Read:
- `references/query-monitor-headless.md`

### 5) Fix by category (choose the dominant bottleneck)

Use the profile output to pick *one* primary bottleneck category:

- **DB queries** → reduce query count, fix N+1 patterns, improve indexes, avoid expensive meta queries.
  - `references/database.md`
- **Autoloaded options** → identify the biggest autoloaded options and stop autoloading large blobs.
  - `references/autoload-options.md`
- **Object cache misses** → introduce caching or fix cache key/group usage; add persistent object cache where appropriate.
  - `references/object-cache.md`
- **Remote HTTP calls** → add timeouts, caching, batching; avoid calling remote APIs on every request.
  - `references/http-api.md`
- **Cron** → reduce due-now spikes, de-duplicate events, move heavy tasks out of request paths.
  - `references/cron.md`

### 6) Verify (repeat the same measurement)

- Re-run the same `wp profile` / `wp doctor` / REST request.
- Confirm the performance delta and that behavior is unchanged.
- If the fix is risky, ship behind a feature flag or staged rollout when possible.

## WordPress 6.9 performance improvements

Be aware of these 6.9 changes when profiling:

**On-demand CSS for classic themes:**
- Classic themes now get on-demand CSS loading (previously only block themes had this).
- Reduces CSS payload by 30-65% by only loading styles for blocks actually used on the page.
- If you're profiling a classic theme, this should already be helping.

**Block themes with no render-blocking resources:**
- Block themes that don't define custom stylesheets (like Twenty Twenty-Three/Four) can now load with zero render-blocking CSS.
- Styles come from global styles (theme.json) and separate block styles, all inlined.
- This significantly improves LCP (Largest Contentful Paint).

**Inline CSS limit increased:**
- The threshold for inlining small stylesheets has been raised, reducing render-blocking resources.

Reference: https://make.wordpress.org/core/2025/11/18/wordpress-6-9-frontend-performance-field-guide/

## Verification

- Baseline vs after numbers are captured (same environment, same URL/route).
- `wp doctor check` is clean (or improved) when applicable.
- No new PHP errors or warnings in logs.
- No cache flush is required for correctness (cache flush should be last resort).

## Failure modes / debugging

- “No change” after code changes:
  - you measured a different URL/site (`--url` mismatch), caches masked results, or opcode cache is stale
- Profiling data is noisy:
  - eliminate background tasks, test with warmed caches, run multiple samples
- `SAVEQUERIES`/Query Monitor causes overhead:
  - don’t run in production unless explicitly approved

## Escalation

- If this is production and you don’t have explicit approval, do not:
  - install plugins, enable `SAVEQUERIES`, run load tests, or flush caches during traffic
- If you need system-level profiling (APM, PHP profiler extensions), coordinate with ops/hosting.

<!-- chapter:end slug=wp-performance -->

---

<!-- chapter:begin slug=wp-phpstan position=12 -->

## 12. wp-phpstan

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-phpstan/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-phpstan/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-phpstan.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (4), referenced from this skill's directory:
  - `references/configuration.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-phpstan/references/configuration.md
  - `references/third-party-classes.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-phpstan/references/third-party-classes.md
  - `references/wordpress-annotations.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-phpstan/references/wordpress-annotations.md
  - `scripts/phpstan_inspect.mjs` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-phpstan/scripts/phpstan_inspect.mjs

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-phpstan
description: "Use when configuring, running, or fixing PHPStan static analysis in WordPress projects (plugins/themes/sites): phpstan.neon setup, baselines, WordPress-specific typing, and handling third-party plugin classes."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Requires Composer-based PHPStan."
---

# WP PHPStan

## When to use

Use this skill when working on PHPStan in a WordPress codebase, for example:

- setting up or updating `phpstan.neon` / `phpstan.neon.dist`
- generating or updating `phpstan-baseline.neon`
- fixing PHPStan errors via WordPress-friendly PHPDoc (REST requests, hooks, query results)
- handling third-party plugin/theme classes safely (stubs/autoload/targeted ignores)

## Inputs required

- `wp-project-triage` output (run first if you haven't)
- Whether adding/updating Composer dev dependencies is allowed (stubs).
- Whether changing the baseline is allowed for this task.

## Procedure

### 0) Discover PHPStan entrypoints (deterministic)
1. Inspect PHPStan setup (config, baseline, scripts):
   - `node skills/wp-phpstan/scripts/phpstan_inspect.mjs`

Prefer the repo’s existing `composer` script (e.g. `composer run phpstan`) when present.

### 1) Ensure WordPress core stubs are loaded

`szepeviktor/phpstan-wordpress` or `php-stubs/wordpress-stubs` are effectively required for most WordPress plugin/theme repos. Without it, expect a high volume of errors about unknown WordPress core functions.

- Confirm the package is installed (see `composer.dependencies` in the inspect report).
- Ensure the PHPStan config references the stubs (see `references/third-party-classes.md`).

### 2) Ensure a sane `phpstan.neon` for WordPress projects

- Keep `paths` focused on first-party code (plugin/theme directories).
- Exclude generated and vendored code (`vendor/`, `node_modules/`, build artifacts, tests unless explicitly analyzed).
- Keep `ignoreErrors` entries narrow and documented.

See:
- `references/configuration.md`

### 3) Fix errors with WordPress-specific typing (preferred)

Prefer correcting types over ignoring errors. Common WP patterns that need help:

- REST endpoints: type request parameters using `WP_REST_Request<...>`
- Hook callbacks: add accurate `@param` types for callback args
- Database results and iterables: use array shapes or object shapes for query results
- Action Scheduler: type `$args` array shapes for job callbacks

See:
- `references/wordpress-annotations.md`

### 4) Handle third-party plugin/theme classes (only when needed)

When integrating with plugins/themes not present in the analysis environment:

- First, confirm the dependency is real (installed/required).
- Prefer plugin-specific stubs already used in the repo (common examples: `php-stubs/woocommerce-stubs`, `php-stubs/acf-pro-stubs`).
- If PHPStan still cannot resolve classes, add targeted `ignoreErrors` patterns for the specific vendor prefix.

See:
- `references/third-party-classes.md`

### 5) Baseline management (use as a migration tool, not a trash bin)

- Generate a baseline once for legacy code, then reduce it over time.
- Do not “baseline” newly introduced errors.

See:
- `references/configuration.md`

## Verification

- Run PHPStan using the discovered command (`composer run ...` or `vendor/bin/phpstan analyse`).
- Confirm the baseline file (if used) is included and didn’t grow unexpectedly.
- Re-run after changing `ignoreErrors` to ensure patterns are not masking unrelated issues.

## Failure modes / debugging

- “Class not found”:
  - confirm autoloading/stubs, or add a narrow ignore pattern
- Huge error counts after enabling PHPStan:
  - reduce `paths`, add `excludePaths`, start at a lower level, then ratchet up
- Inconsistent types around hooks / REST params:
  - add explicit PHPDoc (see references) rather than runtime guards

## Escalation

- If a type depends on a third-party plugin API you can’t confirm, ask for the dependency version or source before inventing types.
- If fixing requires adding new Composer dependencies (stubs/extensions), confirm it with the user first.

<!-- chapter:end slug=wp-phpstan -->

---

<!-- chapter:begin slug=wp-playground position=13 -->

## 13. wp-playground

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-playground/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-playground/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-playground.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (6), referenced from this skill's directory:
  - `references/cli.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-playground/references/cli.md
  - `references/debugging.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-playground/references/debugging.md
  - `references/mcp.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-playground/references/mcp.md
  - `references/sites-api.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-playground/references/sites-api.md
  - `references/webmcp.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-playground/references/webmcp.md
  - `references/website.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-playground/references/website.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-playground
description: "Use as the WordPress Playground routing wrapper for ambiguous Playground work, local CLI runs with @wp-playground/cli, playground.wordpress.net share links, browser previews, WebMCP site tools, snapshots, mounts, version switching, and Xdebug. For Blueprint JSON authoring or review, use the blueprint skill directly."
compatibility: "Targets WordPress 7.0+, PHP 7.4.0+. Playground CLI requires Node.js 20.18+; runs WordPress in WebAssembly with SQLite."
---

# WordPress Playground

This is a thin routing wrapper. Use it to pick the right Playground workflow, then load only the focused reference or skill needed for the task.

## Procedure

1. Identify the user intent: Blueprint authoring/review, local CLI execution, browser-only website/share link workflow, Xdebug/stuck CLI run, or a mixed Playground request.
2. Route to the focused source below, loading more than one only when the request has multiple distinct parts.
3. For mixed requests, delegate Blueprint JSON work to `blueprint`, then return here for runtime, CLI, debugging, or sharing guidance.

- **Blueprint JSON, schema, steps, resources, bundles, or Blueprint review**: use the `blueprint` skill directly. Do not duplicate Blueprint schema details here.
- **Local CLI execution**: read `references/cli.md` for `@wp-playground/cli` server, `run-blueprint`, `build-snapshot`, mounts, version switching, and local validation.
- **Xdebug or stuck CLI runs**: read `references/debugging.md` for Xdebug, runtime logs, worker flags, and stuck CLI runs.
- **Browser-only Playground website workflows**: read `references/website.md` for Query API and Blueprint URL setup, share links, and browser limitations. It routes existing-site operations to separate WebMCP, Playground MCP, and Sites API references; load only the selected method. Unless the user requests a specific connection method, prefer available WebMCP tools for supported browser operations.

## Inputs required

- The intended workflow: Blueprint authoring, local CLI run, website/share link, snapshot, or debugging.
- Project or bundle path if local code must be mounted or packaged.
- Desired WordPress/PHP versions if compatibility matters.
- Port preference if a local server is needed.
- Whether browser-only sharing or local filesystem access is required.

## Guardrails

- Playground instances are disposable, SQLite-backed environments; never point them at production data.
- Keep Blueprint JSON guidance in `blueprint` so the schema and examples have one source of truth.
- For local CLI work, verify Node.js 20.18+ and `npm`/`npx` before running commands.
- Browser-only Playground cannot read local filesystem paths; use public URLs, hosted ZIP bundles, or inline Blueprint JSON.

## Verification

- For Blueprint content, validate against the published schema and follow the `blueprint` skill verification.
- For local CLI runs, verify the mounted plugin/theme or Blueprint side effects in the Playground instance.
- For share links, open the generated URL and confirm the expected landing page and installed assets load.

## Failure modes

- **Blueprint work routed here**: stop and use the `blueprint` skill for schema keys, steps, resources, bundles, validation, or Blueprint review.
- **Local filesystem needed in a browser-only workflow**: use `references/cli.md`; `playground.wordpress.net` cannot read local filesystem paths.
- **Shareable browser link requested from a local CLI workflow**: use `references/website.md`; local server URLs are not portable share links.
- **Debugging treated as a second-hop reference**: read `references/debugging.md` directly for Xdebug, logs, worker flags, and stuck CLI runs.

## Escalation

- If the task needs PHP extensions, native database access, persistence, or production-like infrastructure that Playground cannot provide, use a full WordPress stack such as wp-env, Docker, or the project-provided environment.

<!-- chapter:end slug=wp-playground -->

---

<!-- chapter:begin slug=wp-plugin-development position=14 -->

## 14. wp-plugin-development

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-plugin-development/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-plugin-development/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-plugin-development.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (7), referenced from this skill's directory:
  - `references/data-and-cron.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-plugin-development/references/data-and-cron.md
  - `references/debugging.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-plugin-development/references/debugging.md
  - `references/lifecycle.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-plugin-development/references/lifecycle.md
  - `references/security.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-plugin-development/references/security.md
  - `references/settings-api.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-plugin-development/references/settings-api.md
  - `references/structure.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-plugin-development/references/structure.md
  - `scripts/detect_plugins.mjs` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-plugin-development/scripts/detect_plugins.mjs

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-plugin-development
description: "Use when developing WordPress plugins: architecture and hooks, activation/deactivation/uninstall, admin UI and Settings API, data storage, cron/tasks, security (nonces/capabilities/sanitization/escaping), and release packaging."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Filesystem-based agent with bash + node. Some workflows require WP-CLI."
---

# WP Plugin Development

## When to use

Use this skill for plugin work such as:

- creating or refactoring plugin structure (bootstrap, includes, namespaces/classes)
- adding hooks/actions/filters
- activation/deactivation/uninstall behavior and migrations
- adding settings pages / options / admin UI (Settings API)
- security fixes (nonces, capabilities, sanitization/escaping, SQL safety)
- packaging a release (build artifacts, readme, assets)

## Inputs required

- Repo root + target plugin(s) (path to plugin main file if known).
- Where this plugin runs: single site vs multisite; WP.com conventions if applicable.
- Target WordPress + PHP versions (affects available APIs and placeholder support in `$wpdb->prepare()`).

## Procedure

### 0) Triage and locate plugin entrypoints

1. Run triage:
   - `node skills/wp-project-triage/scripts/detect_wp_project.mjs`
2. Detect plugin headers (deterministic scan):
   - `node skills/wp-plugin-development/scripts/detect_plugins.mjs`

If this is a full site repo, pick the specific plugin under `wp-content/plugins/` or `mu-plugins/` before changing code.

### 1) Follow a predictable architecture

Guidelines:

- Keep a single bootstrap (main plugin file with header).
- Avoid heavy side effects at file load time; load on hooks.
- Prefer a dedicated loader/class to register hooks.
- Keep admin-only code behind `is_admin()` (or admin hooks) to reduce frontend overhead.

See:
- `references/structure.md`

### 2) Hooks and lifecycle (activation/deactivation/uninstall)

Activation hooks are fragile; follow guardrails:

- register activation/deactivation hooks at top-level, not inside other hooks
- flush rewrite rules only when needed and only after registering CPTs/rules
- uninstall should be explicit and safe (`uninstall.php` or `register_uninstall_hook`)

See:
- `references/lifecycle.md`

### 3) Settings and admin UI (Settings API)

Prefer Settings API for options:

- `register_setting()`, `add_settings_section()`, `add_settings_field()`
- sanitize via `sanitize_callback`

See:
- `references/settings-api.md`

### 4) Security baseline (always)

Before shipping:

- Validate/sanitize input early; escape output late.
- Use nonces to prevent CSRF *and* capability checks for authorization.
- Avoid directly trusting `$_POST` / `$_GET`; use `wp_unslash()` and specific keys.
- Use `$wpdb->prepare()` for SQL; avoid building SQL with string concatenation.

See:
- `references/security.md`

### 5) Data storage, cron, migrations (if needed)

- Prefer options for small config; custom tables only if necessary.
- For cron tasks, ensure idempotency and provide manual run paths (WP-CLI or admin).
- For schema changes, write upgrade routines and store schema version.

See:
- `references/data-and-cron.md`

## Verification

- Plugin activates with no fatals/notices.
- Settings save and read correctly (capability + nonce enforced).
- Uninstall removes intended data (and nothing else).
- Run repo lint/tests (PHPUnit/PHPCS if present) and any JS build steps if the plugin ships assets.

## Failure modes / debugging

- Activation hook not firing:
  - hook registered incorrectly (not in main file scope), wrong main file path, or plugin is network-activated
- Settings not saving:
  - settings not registered, wrong option group, missing capability, nonce failure
- Security regressions:
  - nonce present but missing capability checks; or sanitized input not escaped on output

See:
- `references/debugging.md`

## Escalation

For canonical detail, consult the Plugin Handbook and security guidelines before inventing patterns.

<!-- chapter:end slug=wp-plugin-development -->

---

<!-- chapter:begin slug=wp-plugin-directory-guidelines position=15 -->

## 15. wp-plugin-directory-guidelines

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-plugin-directory-guidelines/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-plugin-directory-guidelines/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-plugin-directory-guidelines.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (3), referenced from this skill's directory:
  - `references/gpl-compliance.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-plugin-directory-guidelines/references/gpl-compliance.md
  - `references/guideline-review-checklist.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-plugin-directory-guidelines/references/guideline-review-checklist.md
  - `references/naming-rules.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-plugin-directory-guidelines/references/naming-rules.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-plugin-directory-guidelines
description: "Use when reviewing WordPress plugins for GPL compliance, checking license headers or compatibility, evaluating upsell/freemium/trialware patterns, validating plugin naming or trademark rules, checking plugin slugs, understanding why a plugin was rejected from WordPress.org, or answering any question about the 18 WordPress.org Plugin Directory guidelines — even if the user doesn't mention 'guidelines' explicitly."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+)."
---

## Overview

Authoritative reference for the 18 WordPress.org Plugin Directory guidelines. Covers GPL licensing, plugin naming/trademark rules, trialware restrictions, and all other submission requirements.

## When to use

Use this skill when you need to:
- Review a WordPress plugin for compliance with the WordPress.org Plugin Directory guidelines
- Check GPL license compatibility for a plugin or its bundled libraries
- Verify license headers in plugin files
- Identify common guideline violations before submission
- Answer questions about what is or is not allowed on WordPress.org
- Evaluate premium/upsell flows, license checks, or freemium positioning
- Review "teaser" or "preview" UI for trialware violations

## Inputs required

- Plugin source code (or specific files to review)
- Optional: plugin readme and plugin header metadata for naming and license checks

## Procedure

1. Check the plugin's license header against the **Valid License Headers** section below.
2. Walk through the **18 Guidelines** checklist, paying special attention to Guidelines 1, 4, 5, 7, 8, and 17.
3. Confirm trialware/freemium compliance using the checklist in [guideline-review-checklist.md](references/guideline-review-checklist.md) (Guideline 5 section).
4. For bundled third-party code, verify license compatibility against **GPL-Compatible Licenses (Quick)** below.
5. Flag matches from **Common GPL Violations (Quick)** below.
6. For edge cases, consult the detailed references and the [GNU GPL FAQ](https://www.gnu.org/licenses/gpl-faq.html).

## 18-Guideline Review Checklist

Use the detailed, per-guideline checklist in [guideline-review-checklist.md](references/guideline-review-checklist.md). Load this reference file only when a full guideline audit is requested.

## GPL Compliance (Guideline 1 in Detail)

Use [gpl-compliance.md](references/gpl-compliance.md) for full license tables, compatibility nuances, and examples. Keep this inline section as a quick decision aid.

### Verification (Licensing)

- Every licensing-related issue must cite **Guideline 1** and include the file path and exact license string.
- Confirm compatibility claims against **GPL-Compatible Licenses (Quick)** and escalate ambiguous licenses.

### Failure modes (Licensing)

- If a license is not clearly GPL-compatible, do not guess. Check the [GNU license list](https://www.gnu.org/licenses/license-list.html).
- For dual-license packages, verify both licenses and redistribution terms.

### Quick Reference: WordPress GPL Requirements

- WordPress is **GPLv2 or later**.
- Plugins distributed on WordPress.org must be 100% GPL-compatible (code and assets).
- Include a valid `License:` header and `License URI:` in the main plugin file.
- Do not add restrictions that conflict with GPL freedoms.

### Valid License Headers

## GPL Versions Summary

| Version | Year | Key Addition |
|---------|------|--------------|
| GPLv1 | 1989 | Base copyleft: share-alike for modifications |
| GPLv2 | 1991 | "Liberty or death" clause (Section 7), clearer distribution terms |
| GPLv3 | 2007 | Anti-tivoization, explicit patent grants, compatibility provisions |

WordPress uses **GPLv2 or later**, meaning plugins can use GPLv2, GPLv3, or "GPLv2 or later".

For full license texts, see:
- [GNU General Public License v1](https://www.gnu.org/licenses/gpl-1.0.html)
- [GNU General Public License v2](https://www.gnu.org/licenses/gpl-2.0.html)
- [GNU General Public License v3](https://www.gnu.org/licenses/gpl-3.0.html)

## License Compliance Checklist

When reviewing a plugin, verify:

- [ ] Main plugin file has a valid `License:` header (e.g., `GPL-2.0-or-later`, `GPL-2.0+`, `GPLv2 or later`)
- [ ] Main plugin file has a `License URI:` header pointing to the GPL text
- [ ] If bundled libraries exist, each has a GPL-compatible license
- [ ] No "split licensing" (e.g., code GPL but premium features proprietary)
- [ ] No additional restrictions beyond what GPL allows
- [ ] No clauses restricting commercial use, modification, or redistribution
- [ ] No obfuscated code (violates the spirit of source code availability)

## Valid License Headers for WordPress Plugins

```
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
```

```text
License: GPL-3.0-or-later
License URI: https://www.gnu.org/licenses/gpl-3.0.html
```

```text
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
```

### GPL-Compatible Licenses (Quick)

- Safe defaults: GPL-2.0-or-later, GPL-3.0-or-later.
- Commonly accepted permissive families: MIT/Expat, BSD, ISC, zlib, Boost.
- Conditional compatibility requires care: Apache-2.0 and MPL-2.0 (verify usage context).
- For full accepted and rejected identifiers, use [gpl-compliance.md](references/gpl-compliance.md).

### Common GPL Violations (Quick)

- Split licensing that restricts distributed code.
- Obfuscated or non-corresponding source distribution.
- Restrictive clauses (non-commercial, no-resale, forced backlink).
- Bundling GPL-incompatible libraries or assets.

## Plugin Naming Rules (Guideline 17)

Use [naming-rules.md](references/naming-rules.md) for full trademark lists, slug blocks, and naming examples. Keep this inline checklist for quick screening.

### Naming Checklist (Quick)

- Name is not a placeholder and has at least 5 alphanumeric characters.
- Header name and readme name match.
- Name is specific and function-related; avoid keyword stuffing.
- Trademark/project names appear only after connectors like `for`, `with`, `using`, `and`.
- No banned/discouraged terms or trademark portmanteaus.
- Slug is lowercase, hyphenated, <= 50 chars, and avoids blocked terms.

<!-- chapter:end slug=wp-plugin-directory-guidelines -->

---

<!-- chapter:begin slug=wp-project-triage position=16 -->

## 16. wp-project-triage

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-project-triage/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-project-triage/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-project-triage.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (2), referenced from this skill's directory:
  - `references/triage.schema.json` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-project-triage/references/triage.schema.json
  - `scripts/detect_wp_project.mjs` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-project-triage/scripts/detect_wp_project.mjs

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-project-triage
description: "Use when you need a deterministic inspection of a WordPress repository (plugin/theme/block theme/WP core/Gutenberg/full site) including tooling/tests/version hints, and a structured JSON report to guide workflows and guardrails."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Filesystem-based agent with bash + node. Some workflows require WP-CLI."
---

# WP Project Triage

## When to use

Use this skill to quickly understand what kind of WordPress repo you’re in and what commands/conventions to follow before making changes.

## Inputs required

- Repo root (current working directory).

## Procedure

1. Run the detector (prints JSON to stdout):
   - `node skills/wp-project-triage/scripts/detect_wp_project.mjs`
2. If you need the exact output contract, read:
   - `skills/wp-project-triage/references/triage.schema.json`
3. Use the report to select workflow guardrails:
   - project kind(s)
   - PHP/Node tooling present
   - tests present
   - version hints and sources
4. If the report is missing signals you need, update the detector rather than guessing.

## Verification

- The JSON should parse and include: `project.kind`, `signals`, and `tooling`.
- Re-run after changes that affect structure/tooling (adding `theme.json`, `block.json`, build config).

## Failure modes / debugging

- If it reports `unknown`, check whether the repo root is correct.
- If scanning is slow, add/extend ignore directories in the script.

<!-- chapter:end slug=wp-project-triage -->

---

<!-- chapter:begin slug=wp-rest-api position=17 -->

## 17. wp-rest-api

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-rest-api/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-rest-api/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-rest-api.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (6), referenced from this skill's directory:
  - `references/authentication.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-rest-api/references/authentication.md
  - `references/custom-content-types.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-rest-api/references/custom-content-types.md
  - `references/discovery-and-params.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-rest-api/references/discovery-and-params.md
  - `references/responses-and-fields.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-rest-api/references/responses-and-fields.md
  - `references/routes-and-endpoints.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-rest-api/references/routes-and-endpoints.md
  - `references/schema.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-rest-api/references/schema.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-rest-api
description: "Use when building, extending, or debugging WordPress REST API endpoints/routes: register_rest_route, WP_REST_Controller/controller classes, schema/argument validation, permission_callback/authentication, response shaping, register_rest_field/register_meta, or exposing CPTs/taxonomies via show_in_rest."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Filesystem-based agent with bash + node. Some workflows require WP-CLI."
---

# WP REST API

## When to use

Use this skill when you need to:

- create or update REST routes/endpoints
- debug 401/403/404 errors or permission/nonce issues
- add custom fields/meta to REST responses
- expose custom post types or taxonomies via REST
- implement schema + argument validation
- adjust response links/embedding/pagination

## Inputs required

- Repo root + target plugin/theme/mu-plugin (path to entrypoint).
- Desired namespace + version (e.g. `my-plugin/v1`) and routes.
- Authentication mode (cookie + nonce vs application passwords vs auth plugin).
- Target WordPress version constraints (if below 7.0, call out).

## Procedure

### 0) Triage and locate REST usage

1. Run triage:
   - `node skills/wp-project-triage/scripts/detect_wp_project.mjs`
2. Search for existing REST usage:
   - `register_rest_route`
   - `WP_REST_Controller`
   - `rest_api_init`
   - `show_in_rest`, `rest_base`, `rest_controller_class`

If this is a full site repo, pick the specific plugin/theme before changing code.

### 1) Choose the right approach

- **Expose CPT/taxonomy in `wp/v2`:**
  - Use `show_in_rest => true` + `rest_base` if needed.
  - Optionally provide `rest_controller_class`.
  - Read `references/custom-content-types.md`.
- **Custom endpoints:**
  - Use `register_rest_route()` on `rest_api_init`.
  - Prefer a controller class (`WP_REST_Controller` subclass) for anything non-trivial.
  - Read `references/routes-and-endpoints.md` and `references/schema.md`.

### 2) Register routes safely (namespaces, methods, permissions)

- Use a unique namespace `vendor/v1`; avoid `wp/*` unless core.
- Always provide `permission_callback` (use `__return_true` for public endpoints).
- Use `WP_REST_Server::READABLE/CREATABLE/EDITABLE/DELETABLE` constants.
- Return data via `rest_ensure_response()` or `WP_REST_Response`.
- Return errors via `WP_Error` with an explicit `status`.

Read `references/routes-and-endpoints.md`.

### 3) Validate/sanitize request args

- Define `args` with `type`, `default`, `required`, `validate_callback`, `sanitize_callback`.
- Prefer JSON Schema validation with `rest_validate_value_from_schema` then `rest_sanitize_value_from_schema`.
- Never read `$_GET`/`$_POST` directly inside endpoints; use `WP_REST_Request`.

Read `references/schema.md`.

### 4) Responses, fields, and links

- Do **not** remove core fields from default endpoints; add fields instead.
- Use `register_rest_field` for computed fields; `register_meta` with `show_in_rest` for meta.
- For `object`/`array` meta, define schema in `show_in_rest.schema`.
- If you need unfiltered post content (e.g., ToC plugins injecting HTML), request `?context=edit` to access `content.raw` (auth required). Pair with `_fields=content.raw` to keep responses small.
- Add related resource links via `WP_REST_Response::add_link()`.

Read `references/responses-and-fields.md`.

### 5) Authentication and authorization

- For wp-admin/JS: cookie auth + `X-WP-Nonce` (action `wp_rest`).
- For external clients: application passwords (basic auth) or an auth plugin.
- Use capability checks in `permission_callback` (authorization), not just “logged in”.

Read `references/authentication.md`.

### 6) Client-facing behavior (discovery, pagination, embeds)

- Ensure discovery works (`Link` header or `<link rel="https://api.w.org/">`).
- Support `_fields`, `_embed`, `_method`, `_envelope`, pagination headers.
- Remember `per_page` is capped at 100.

Read `references/discovery-and-params.md`.

## Verification

- `/wp-json/` index includes your namespace.
- `OPTIONS` on your route returns schema (when provided).
- Endpoint returns expected data; permission failures return 401/403 as appropriate.
- CPT/taxonomy routes appear under `wp/v2` when `show_in_rest` is true.
- Run repo lint/tests and any PHP/JS build steps.

## Failure modes / debugging

- 404: `rest_api_init` not firing, route typo, or permalinks off (use `?rest_route=`).
- 401/403: missing nonce/auth, or `permission_callback` too strict.
- `_doing_it_wrong` for missing `permission_callback`: add it (use `__return_true` if public).
- Invalid params: missing/incorrect `args` schema or validation callbacks.
- Fields missing: `show_in_rest` false, meta not registered, or CPT lacks `custom-fields` support.

## Escalation

If version support or behavior is unclear, consult the REST API Handbook and core docs before inventing patterns.

<!-- chapter:end slug=wp-rest-api -->

---

<!-- chapter:begin slug=wp-wpcli-and-ops position=18 -->

## 18. wp-wpcli-and-ops

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wp-wpcli-and-ops/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-wpcli-and-ops/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wp-wpcli-and-ops.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

Bundled files (8), referenced from this skill's directory:
  - `references/automation.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-wpcli-and-ops/references/automation.md
  - `references/cron-and-cache.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-wpcli-and-ops/references/cron-and-cache.md
  - `references/debugging.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-wpcli-and-ops/references/debugging.md
  - `references/multisite.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-wpcli-and-ops/references/multisite.md
  - `references/packages-and-updates.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-wpcli-and-ops/references/packages-and-updates.md
  - `references/safety.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-wpcli-and-ops/references/safety.md
  - `references/search-replace.md` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-wpcli-and-ops/references/search-replace.md
  - `scripts/wpcli_inspect.mjs` — https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wp-wpcli-and-ops/scripts/wpcli_inspect.mjs

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wp-wpcli-and-ops
description: "Use when working with WP-CLI (wp) for WordPress operations: safe search-replace, db export/import, plugin/theme/user/content management, cron, cache flushing, multisite, and scripting/automation with wp-cli.yml."
compatibility: "Targets WordPress 7.0+ (PHP 7.4.0+). Requires WP-CLI in the execution environment."
---

# WP-CLI and Ops

## When to use

Use this skill when the task involves WordPress operational work via WP-CLI, including:

- `wp search-replace` (URL changes, domain migrations, protocol switch)
- DB export/import, resets, and inspections (`wp db *`)
- plugin/theme install/activate/update, language packs
- cron event listing/running
- cache/rewrite flushing
- multisite operations (`wp site *`, `--url`, `--network`)
- building repeatable scripts (`wp-cli.yml`, shell scripts, CI jobs)

## Inputs required

- Where WP-CLI will run (local dev, staging, production) and whether it’s safe to run.
- How to target the correct site root:
  - `--path=<wordpress-root>` and (multisite) `--url=<site-url>`
- Whether this is multisite and whether commands should run network-wide.
- Any constraints (no downtime, no DB writes, maintenance window).

## Procedure

### 0) Guardrails: confirm environment and blast radius

WP-CLI commands can be destructive. Before running anything that writes:

1. Confirm environment (dev/staging/prod).
2. Confirm targeting (path/url) so you don’t hit the wrong site.
3. Make a backup when performing risky operations.

Read:
- `references/safety.md`

### 1) Inspect WP-CLI and site targeting (deterministic)

Run the inspector:

- `node skills/wp-wpcli-and-ops/scripts/wpcli_inspect.mjs --path=<path> [--url=<url>]`

If WP-CLI isn’t available, fall back to installing it via the project’s documented tooling (Composer, container, or system package), or ask for the expected execution environment.

### 2) Choose the right workflow

#### A) Safe URL/domain migration (`search-replace`)

Follow a safe sequence:

1. `wp db export` (backup)
2. `wp search-replace --dry-run` (review impact)
3. Run the real replace with appropriate flags
4. Flush caches/rewrite if needed

Read:
- `references/search-replace.md`

#### B) Plugin/theme operations

Use `wp plugin *` / `wp theme *` and confirm you’re acting on the intended site (and network) first.

Read:
- `references/packages-and-updates.md`

#### C) Cron and queues

Inspect cron state and run individual events for debugging rather than “run everything blindly”.

Read:
- `references/cron-and-cache.md`

#### D) Multisite operations

Multisite changes can affect many sites. Always decide whether you’re operating:

- on a single site (`--url=`), or
- network-wide (`--network` / iterating sites)

Read:
- `references/multisite.md`

### 3) Automation patterns (scripts + wp-cli.yml)

For repeatable ops, prefer:

- `wp-cli.yml` for defaults (path/url, PHP memory limits)
- shell scripts that log commands and stop on error
- CI jobs that run read-only checks by default

Read:
- `references/automation.md`

## Verification

- Re-run `wpcli_inspect` after changes that could affect targeting or config.
- Confirm intended side effects:
  - correct URLs updated
  - plugins/themes in expected state
  - cron/caches flushed where needed
- If there’s a health check endpoint or smoke test suite, run it after ops changes.

## Failure modes / debugging

- “Error: This does not seem to be a WordPress installation.”
  - wrong `--path`, wrong container, or missing `wp-config.php`
- Multisite commands affecting the wrong site
  - missing `--url` or wrong URL
- Search-replace causes unexpected serialization issues
  - wrong flags or changing serialized data unsafely

See:
- `references/debugging.md`

## Escalation

- If you cannot confirm environment safety, do not run write operations.
- If the repo uses containerized tooling (Docker/wp-env) but you can’t access it, ask for the intended command runner or CI job.

<!-- chapter:end slug=wp-wpcli-and-ops -->

---

<!-- chapter:begin slug=wpds position=19 -->

## 19. wpds

- **Source:** https://github.com/WordPress/agent-skills/blob/trunk/skills/wpds/SKILL.md
- **Raw:** https://raw.githubusercontent.com/WordPress/agent-skills/trunk/skills/wpds/SKILL.md
- **Markdown:** https://skillsdocs.com/WordPress/agent-skills/wpds.md
- **Licence:** Other — https://github.com/WordPress/agent-skills

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: wpds
description: "Use when building UIs leveraging the WordPress Design System (WPDS) and its components, tokens, patterns, etc."
compatibility: "Requires WPDS MCP server configured and running. Targets WordPress 7.0+ (PHP 7.4.0+)."
---

# WordPress Design System (WPDS)

## Prerequisites

This skill works best with the **WPDS MCP server** installed. The MCP provides access to WordPress Design System documentation and resources, such as components and DS token lists.

The following terms should be treated as synonyms:
- "WordPress" and "WP";
- "Design System" and "DS";
- "WordPress Design System" and "WPDS".

## When to use

Use this skill when the user mentions:

- building and/or reviewing any UI in a WordPress-related context (for example, Gutenberg, WooCommerce, WordPress.com, Jetpack, etc etc);
- WordPress Design System, WPDS, Design System;
- UI components, Design tokens, color primitives, spacing scales, typography variables and presets;
- Specific component packages such as @wordpress/components or @wordpress/ui;

## Rules

### Use the WPDS MCP server to access WPDS-related documentation

- Use the WPDS MCP server to retrieve the canonical, authoritative documentation:
  - reference site (`wpds://pages`)
  - list of available components (`wpds://components`) and specific component information (`wpds://components/:name`)
  - list of available tokens (`wpds://design-tokens`)
- DO NOT search the web for canonical documentation about the WordPress Design System. If asked by the user, push back and ask for confirmation, warning them that the MCP server is the best place to provide information

### Required documentation

Before working on any WPDS-related tasks, make sure you read relevant documentation on the reference site. This documentation should take the absolute precedence when evaluating the best course of action for any given tasks.

### Boundaries

- Skip non-UI related aspects of an answer (for example, fetching data from stores, or localizing strings of text).
- Focus on building UI that adheres as much as possible to the WPDS best practices, uses the most fitting WPDS components/tokens/patterns.

### Tech stack

- Unless you are told otherwise (or gathered specific information from the local context of the request), assume the following tech stack: TypeScript, React, CSS.

### Validation

- If the local context in which a task is running provide lint scripts, use them to validate the proposed code output when possible.

## Output

- As a recap at the end of your response, provide a clear and concise explanation of what the solution does, and add context to why each decision was made.
- Be explicit about the boundaries, ie. what was explicitly left out of the task because not relevant (eg non-ui related).
- Provide working code snippets

<!-- chapter:end slug=wpds -->
