## Why
Several small quality issues accumulated across plugin-author and
marketplace-author: the plugin-author description was ambiguous about
scope, the ADD flow asked an unnecessary clarifying question when local
path is the obvious default, VALIDATE had redundant wording already
captured inline, the keywords field was missing from the validation
checklist, and tests/README.md was a placeholder with nothing to test.
## Implementation Notes
- plugin-author: tightened description scope to "inside those
directories"; added `keywords` identical-in-both-manifests check to
VALIDATE checklist.
- marketplace-author: moved reserved-prefix constraint from gotchas
into the ADD checklist item; replaced the four-option prompt with a
default+escape-hatch ("I'll treat this as local path — is that right?");
folded the validate-from-root note inline and removed trailing padding;
deleted tests/README.md — no scripts/ directory exists, so the
placeholder was noise.
- README.md: removed the tests/README.md row from the file table.
183 lines
8.4 KiB
Markdown
183 lines
8.4 KiB
Markdown
---
|
|
name: plugin-author
|
|
description: >
|
|
Use when the user wants to create a new plugin scaffold ("create a plugin
|
|
for X", "new plugin called Y"), update plugin configuration ("change the
|
|
description", "add keyword", "bump version"), or release a plugin version
|
|
("release", "tag", "publish"). Manages both Claude Code
|
|
(.claude-plugin/plugin.json) and Copilot CLI (plugin.json) manifests in one
|
|
pass. Do not use when the request is about plugin content (skills, agents,
|
|
hooks, or MCP servers inside those directories). Do not use for
|
|
marketplace.json entries — use /marketplace-author for that.
|
|
allowed-tools: Bash Read Write Edit
|
|
metadata:
|
|
category: factory
|
|
source_keys:
|
|
- context7-websites-code-claude
|
|
- claude-code-plugins-docs
|
|
- claude-code-subagents-docs
|
|
- context7-github-en-copilot
|
|
- github-cli-plugin-reference
|
|
- github-plugins-creating
|
|
- github-plugins-finding-installing
|
|
---
|
|
|
|
## Gotchas
|
|
|
|
- Both manifests must carry identical `version` values — version parity is a hard invariant (ADR-0016). Never update version in one manifest without updating the other in the same edit pass.
|
|
- `author.email` is placed in the Copilot manifest by convention; `author.url` is placed in the CC manifest by convention. Both fields are supported by both platforms — do not add them to the other manifest without a deliberate reason.
|
|
- `claude plugin tag --push` is irreversible: it creates a git tag and pushes it to remote. Always present the HITL gate and wait for explicit confirmation before running it.
|
|
- `claude plugin tag --push` requires a clean working tree and will fail if there are uncommitted changes. Commit or stash all changes before running it.
|
|
- `name` in both manifests must be kebab-case and must not use reserved prefixes: `anthropic-*`, `claude-*`, `agent-skills`, `official-claude-plugins`.
|
|
- Copilot manifest lookup order: `.plugin/plugin.json` → `plugin.json` → `.github/plugin/plugin.json` → `.claude-plugin/plugin.json`. The canonical location for the Copilot manifest in this repo is `plugin.json` at the plugin root.
|
|
- `displayName` is a CC platform field — Copilot has no equivalent. Do not add it to the Copilot manifest.
|
|
- `skills`, `agents`, `hooks`, `mcpServers` are declared in the Copilot manifest by convention — Copilot requires explicit path declarations while CC auto-discovers content from the plugin root. Both platforms support these fields; omit them from the CC manifest by convention.
|
|
- Agent files in a plugin's `agents/` directory silently ignore `hooks`, `mcpServers`, and `permissionMode` frontmatter fields.
|
|
|
|
## Route
|
|
|
|
Determine which flow before touching the filesystem. Read both manifest files if the plugin directory exists.
|
|
|
|
- **Plugin directory does not exist** → follow **CREATE flow**
|
|
- **Plugin directory exists + version/release intent** ("release", "tag", "bump", "publish", "version") → follow **RELEASE flow**
|
|
- **Plugin directory exists + field change intent** ("update description", "add keyword", "change author") → follow **UPDATE flow**
|
|
- **Ambiguous** → ask: "Did you mean to create a new plugin, update its configuration, or release a version?"
|
|
|
|
Validate runs automatically before tagging (in RELEASE flow) and can be invoked explicitly at any time: `claude plugin validate plugins/<name>`.
|
|
|
|
## CREATE flow
|
|
|
|
### Prerequisites
|
|
|
|
Before touching the filesystem, confirm you have:
|
|
- [ ] Plugin name (kebab-case, e.g. `my-tools`)
|
|
- [ ] Repo root (absolute path or `.` for current directory)
|
|
|
|
If either is missing, stop and ask before proceeding.
|
|
|
|
### Step 1 — Scaffold
|
|
|
|
Run the scaffold script:
|
|
|
|
```bash
|
|
bash scripts/new-plugin.sh <name> <repo-root>
|
|
```
|
|
|
|
Examples:
|
|
```bash
|
|
bash scripts/new-plugin.sh my-tools /root/ai-development
|
|
bash scripts/new-plugin.sh data-tools .
|
|
```
|
|
|
|
The script creates under `<repo-root>/plugins/<name>/`:
|
|
- `plugin.json` — Copilot manifest with `FILL_IN_*` placeholders
|
|
- `.claude-plugin/plugin.json` — CC manifest with `FILL_IN_*` placeholders
|
|
- Empty skeleton directories: `skills/`, `agents/`, `hooks/`, `bin/`
|
|
|
|
Each file/dir is a no-op if it already exists.
|
|
|
|
### Step 2 — Fill in placeholders
|
|
|
|
Open both manifest files and replace every `FILL_IN_*` placeholder.
|
|
|
|
**Fields shared by both manifests** (must be identical in both):
|
|
- `name` — kebab-case plugin identifier (already set by script; verify it is correct)
|
|
- `description` — one or two sentences; what the plugin provides
|
|
- `version` — SemVer; defaults to `1.0.0`; must be identical in both manifests
|
|
- `author.name` — author display name
|
|
- `license` — SPDX identifier (default: `MIT`)
|
|
- `keywords` — search/discovery tags (default: `[]`)
|
|
|
|
**CC manifest fields** (`.claude-plugin/plugin.json` only):
|
|
- `displayName` — human-readable name shown in plugin manager; capitalised form of `name` (CC platform field — no Copilot equivalent)
|
|
- `author.url` — author URL (e.g. Gitea profile URL) (both platforms support this; placed here by convention)
|
|
|
|
**Copilot manifest fields** (`plugin.json` only):
|
|
- `author.email` — author email (both platforms support this; placed here by convention)
|
|
- `skills`, `agents`, `hooks`, `mcpServers` — paths; defaults are already set by the script (CC auto-discovers these; Copilot requires explicit declarations)
|
|
|
|
### Step 3 — Validate
|
|
|
|
Check:
|
|
- [ ] `name` identical in both manifests, kebab-case, no reserved prefixes
|
|
- [ ] `description` identical in both manifests, non-empty
|
|
- [ ] `version` identical in both manifests (version parity — ADR-0016)
|
|
- [ ] `author.name` identical in both manifests
|
|
- [ ] `license` identical in both manifests
|
|
- [ ] `keywords` identical in both manifests
|
|
- [ ] No `FILL_IN_*` placeholders remain
|
|
- [ ] `displayName` present in CC manifest only
|
|
- [ ] `author.url` in CC manifest, `author.email` in Copilot manifest
|
|
|
|
## UPDATE flow
|
|
|
|
### Step 1 — Read both manifests
|
|
|
|
Read `plugins/<name>/plugin.json` and `plugins/<name>/.claude-plugin/plugin.json`. Identify the current field values.
|
|
|
|
### Step 2 — Classify each change
|
|
|
|
For every field the user wants to change:
|
|
|
|
| Change type | What to update |
|
|
|---|---|
|
|
| Shared field (`name`, `description`, `version`, `author.name`, `license`, `keywords`) | Both manifests in the same edit pass |
|
|
| CC platform field (`displayName`) | `.claude-plugin/plugin.json` only — Copilot has no equivalent field |
|
|
| Copilot platform fields (`category`, `tags`, `extensions`) | `plugin.json` only — not in the CC manifest schema |
|
|
| CC scaffold convention (`author.url`) | `.claude-plugin/plugin.json` only — both platforms support this field; it is placed here by convention |
|
|
| Copilot scaffold convention (`author.email`, `skills`, `agents`, `hooks`, `mcpServers`) | `plugin.json` only by convention — CC also supports these fields; CC auto-discovers content from the plugin root rather than requiring explicit path declarations |
|
|
|
|
Never update a shared field in one manifest without updating the other in the same pass.
|
|
|
|
If the target field is not listed in the classification table, read `references/manifest-fields.md` for the full field list and platform support notes.
|
|
|
|
### Step 3 — Announce and apply
|
|
|
|
State which fields change and which files are affected. Then apply. For `version` changes not part of a release, bump both manifests in the same edit.
|
|
|
|
### Step 4 — Validate
|
|
|
|
Re-run the validation checklist from CREATE flow Step 3 on both files.
|
|
|
|
## RELEASE flow
|
|
|
|
### Step 1 — Confirm version
|
|
|
|
If the user has not stated the new SemVer version, ask: "What version are you releasing?" Do not proceed until you have the version.
|
|
|
|
### Step 2 — Bump version in both manifests
|
|
|
|
Update `version` in both `plugin.json` and `.claude-plugin/plugin.json` in the same edit pass. Confirm they are identical after the edit.
|
|
|
|
### Step 3 — Validate
|
|
|
|
Run:
|
|
|
|
```bash
|
|
claude plugin validate plugins/<name>
|
|
```
|
|
|
|
Use `--strict` to promote warnings to errors: `claude plugin validate --strict plugins/<name>`.
|
|
|
|
Stop and report errors if validation fails. Do not proceed to tagging until validation passes.
|
|
|
|
### Step 4 — HITL gate
|
|
|
|
State exactly:
|
|
|
|
> "I will run `claude plugin tag --push` for plugin `<name>`, which will create git tag `<name>--v<version>` and push it to remote. This is irreversible. Confirm?"
|
|
|
|
Do not call the tool until the user explicitly confirms in the conversation.
|
|
|
|
### Step 5 — Tag and release
|
|
|
|
To preview without tagging or pushing: `claude plugin tag --dry-run`.
|
|
|
|
After explicit confirmation, run from the repo root:
|
|
|
|
```bash
|
|
claude plugin tag --push
|
|
```
|
|
|
|
Report the created tag name and confirm the push completed.
|