Two review passes on PR #91 (correctness + simplification) found the
prior fixup commit (d6fd9b6) left the deprecation notice it added
half-applied, cited a nonexistent ADR, and introduced/compounded a
few duplicate restatements of the same facts across apm-workflow's
skill files and apm-orchestrate's Hard rules.
- plugin-author/SKILL.md cited ADR-0016 (doesn't exist) instead of
ADR-0006 for the version-parity invariant.
- The deprecation banner on plugin-author/marketplace-author wasn't
reflected in their routing `description:` frontmatter, forge's
routing table, either kyberforge README skill table, or CONTEXT.md
— now propagated consistently; routing behavior is unchanged since
issue #90 (repo-wide apm conversion) hasn't landed yet.
- apm-orchestrate's `configure` operation only covered scaffolding a
new package, not editing an existing apm.yml (e.g. removing a
marketplace.packages[] entry, which has no dedicated `apm` CLI verb
and is just a manifest edit). Added `edit-config`, intentionally
ungated — a local file edit isn't the same class of irreversible
action as `apm publish`, and normal review already gates the commit.
- Deduplicated facts restated 3-4x across SKILL.md/references/ADR
boundaries (audit semantics, reserved name prefixes, marketplace-add
direction, registries precondition), and trimmed configure.md's
~55-line apm.yml schema dump — copied near-verbatim from the
research doc — down to a compact key list with a pointer, matching
install.md's existing terse cross-referencing style.
Deliberately left as-is: apm-orchestrate's parallel/sequential
fan-out logic stays inline rather than extracted to a shared
reference, since neither git-orchestrate nor gitea-orchestrate need
it yet — premature extraction for a single caller.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186ERbyACLRuRxPRnqwpa4m
192 lines
9.9 KiB
Markdown
192 lines
9.9 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. Deprecated
|
|
per ADR-0015 — superseded by apm-workflow; kept until issue #90 lands.
|
|
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
|
|
---
|
|
|
|
> **Deprecated:** Superseded by `apm-workflow` per ADR-0015 — deletion pending issue #90 (not yet changed). Use `apm-workflow configure` (apm.yml authoring, `apm plugin init` scaffolding) for CREATE/UPDATE and `apm-workflow compile` (`apm compile`/`apm pack`/`apm publish`) for RELEASE instead.
|
|
|
|
## Gotchas
|
|
|
|
- Both manifests must carry identical `version` values — version parity is a hard invariant (ADR-0006). Never update version in one manifest without updating the other in the same edit pass.
|
|
- Every UPDATE to non-version fields (description, keywords, author, etc.) requires a patch bump to `version` in both manifests in the same edit pass. Consumers cache plugin metadata and use the version to detect changes — skipping the bump makes the update invisible until a forced refresh.
|
|
- `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.
|
|
- `claude plugin validate --strict` auto-discovers every `.md` file directly under `agents/` and treats it as an agent definition requiring frontmatter — this is independent of the manifest. An explicit `agents` array listing only the real agent files does not suppress the scan; unlisted `.md` files in the same directory still fail validation (tested empirically — see ADR-0010). Any non-agent file (notes, provenance records, READMEs) must live outside `agents/` — e.g. at the plugin root — regardless of what the manifest declares.
|
|
|
|
## 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-0006)
|
|
- [ ] `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 — Bump version
|
|
|
|
If the change did not explicitly target the `version` field, apply a **patch bump** to `version` in both manifests in the same edit pass (e.g. `1.2.3` → `1.2.4`). Skip this step only when the user has explicitly set the version themselves in Step 3.
|
|
|
|
### Step 5 — 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.
|