## Why Plugin and marketplace management had no governed authoring path. Creating or updating a plugin required knowing the dual-manifest convention, version parity rules, and directory skeleton by memory — nothing enforced consistency or guided the process. `/plugin-author` closes that gap by owning the full plugin scaffold lifecycle: create, update, rename, and release. `/marketplace-author` handles the marketplace-facing side: register, deregister, and update plugin entries in `marketplace.json`. ADR-0016 codifies the version parity convention (identical `version` in both `plugin.json` and `.claude-plugin/plugin.json`) that `/plugin-author` now enforces. The two plugin.json files in this repo are backfilled to comply (keys also sorted to pass the pretty-format-json hook). CONTEXT.md gains glossary entries for "plugin scaffold" and "version parity" so future agents have shared vocabulary for these concepts. ## Implementation Notes `/plugin-author` ships a `scripts/new-plugin.sh` scaffold script that generates the directory skeleton and both manifests in one shot; the skill calls the script rather than generating files ad hoc so the scaffold is reviewable and repeatable. Version parity is an invariant, not a suggestion — the skill will fail loudly on create/update if the two versions would diverge. ADR: docs/adr/0016-plugin-version-parity.md
169 lines
6.7 KiB
Markdown
169 lines
6.7 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. Explicitly out of scope: plugin content (skills, agents, hooks, MCP
|
|
servers) and marketplace.json entries — use /marketplace-author for those.
|
|
allowed-tools: Bash Read Write Edit
|
|
metadata:
|
|
category: factory
|
|
source_keys:
|
|
- context7-websites-code-claude
|
|
- claude-code-plugins-docs
|
|
- 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` belongs in the Copilot manifest (`plugin.json`); `author.url` belongs in the CC manifest (`.claude-plugin/plugin.json`). Do not swap them.
|
|
- `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.
|
|
- `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 CC-only — do not add it to the Copilot manifest.
|
|
- `skills`, `agents`, `hooks`, `mcpServers` are Copilot-only fields — do not add them to the CC manifest.
|
|
|
|
## 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.
|
|
|
|
## 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-only fields** (`.claude-plugin/plugin.json` only):
|
|
- `displayName` — human-readable name shown in plugin manager; capitalised form of `name`
|
|
- `author.url` — author URL (e.g. Gitea profile URL)
|
|
|
|
**Copilot-only fields** (`plugin.json` only):
|
|
- `author.email` — author email
|
|
- `skills`, `agents`, `hooks`, `mcpServers` — paths; defaults are already set by the script
|
|
|
|
### 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
|
|
- [ ] 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-only (`displayName`, `author.url`) | `.claude-plugin/plugin.json` only |
|
|
| Copilot-only (`author.email`, `skills`, `agents`, `hooks`, `mcpServers`, `category`, `tags`, `extensions`) | `plugin.json` only |
|
|
|
|
Never update a shared field in one manifest without updating the other in the same pass.
|
|
|
|
### 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>
|
|
```
|
|
|
|
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
|
|
|
|
After explicit confirmation, run from the repo root:
|
|
|
|
```bash
|
|
claude plugin tag --push
|
|
```
|
|
|
|
Report the created tag name and confirm the push completed.
|