feat(kyberforge): add plugin-author and marketplace-author skills

## 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
This commit is contained in:
2026-06-28 10:45:03 +00:00
parent 098fc7315e
commit 4d061bd199
18 changed files with 1159 additions and 13 deletions

View File

@@ -0,0 +1,234 @@
---
name: marketplace-author
description: >
Use when the user wants to add a plugin to the marketplace ("register my
plugin", "add to marketplace", "list plugin X"), remove an entry ("unlist
plugin X", "remove from marketplace"), or update an existing entry ("bump
the marketplace version", "update the description for Y"). Always updates
both .claude-plugin/marketplace.json and .github/plugin/marketplace.json in
the same pass. Out of scope: plugin scaffold and configuration — use
/plugin-author for that. Does not manage marketplace registration with
Claude Code or Copilot CLI.
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-marketplace
- github-plugins-finding-installing
---
## Gotchas
- Both marketplace files must be identical after every operation — never update one without the other in the same edit pass.
- `source` for local plugins is a relative path from the marketplace root, not the plugin directory name alone (e.g. `"./plugins/kyberforge"`, not `"kyberforge"`).
- `claude plugin validate .` must be run from the repo root, not from the plugin directory or the `.claude-plugin/` directory.
- The `{ "source": "github", ... }` object form is only for GitHub. For GitLab, Gitea, or any other git host, use `{ "source": "git", "url": "https://..." }` with a full URL.
- Removing an entry from the marketplace does NOT delete the plugin files — it only removes the catalog listing.
- `version` in a marketplace entry is optional for git-sourced plugins; Claude Code derives version from git tags automatically. Include it when the source is npm or when the user explicitly wants a pinned version visible in the catalog.
- `name` must be kebab-case. Reserved prefixes (`anthropic-*`, `claude-*`, `agent-skills`, `official-claude-plugins`) are rejected by the validator.
- The `plugins[]` array order is not semantically significant, but maintain it consistently — add new entries at the end.
- For Copilot CLI, the canonical marketplace.json location is `.github/plugin/marketplace.json`. Claude Code also reads `.claude-plugin/marketplace.json`. Both are equivalent; this repo maintains both files in sync.
Read `references/manifest-fields.md` for the full field reference and source type shapes before editing any file.
## Route
Determine which operation applies before touching any file:
- **Neither `.claude-plugin/marketplace.json` nor `.github/plugin/marketplace.json` exist** → follow **CREATE**
- **Only one file exists** → stop and note the mirror is missing; ask the user whether to create the missing mirror from the existing file, or whether this is an error. Do not proceed until both files are present or the user has explicitly directed you to create the missing one.
- **Both files exist + plugin name NOT in `plugins[]` + add/register/list intent** → follow **ADD**
- **Both files exist + plugin name IS in `plugins[]` + remove/unlist/delete intent** → follow **REMOVE**
- **Both files exist + plugin name IS in `plugins[]` + change/update/bump intent** → follow **UPDATE**
- **User asks to validate without any add/remove/update intent** → follow **VALIDATE**
- **Ambiguous** → ask: "Did you mean to add a new plugin entry, update an existing one, or remove one?"
---
## CREATE
Run this flow only when no marketplace.json exists anywhere in the repo.
### Prerequisites
Confirm you have:
- [ ] Marketplace name (kebab-case, e.g. `my-marketplace`)
- [ ] Owner name (and optionally email)
- [ ] Marketplace description (optional but recommended)
- [ ] At least one initial plugin entry (name, source, description)
If prerequisites are missing, ask before writing.
### Step 1 — Write `.claude-plugin/marketplace.json`
Create the file with the following structure (fill in the values from prerequisites):
```json
{
"name": "<marketplace-name>",
"owner": { "name": "<owner-name>", "email": "<owner-email>" },
"description": "<marketplace-description>",
"version": "0.1.0",
"plugins": [
{
"name": "<plugin-name>",
"description": "<plugin-description>",
"source": "<source>"
}
]
}
```
Omit `"email"` if not provided. Omit `"version"` from plugin entries unless the user specifies one (see Gotchas).
### Step 2 — Write `.github/plugin/marketplace.json`
Write identical content to `.github/plugin/marketplace.json`. These two files must always be identical.
### Step 3 — Validate
Follow the **VALIDATE** flow.
---
## ADD
Run this flow when a plugin name does not yet exist in `plugins[]` and the intent is to add it.
### Prerequisites
Confirm you have:
- [ ] Plugin name (kebab-case)
- [ ] Plugin description
- [ ] Source type and source value (see source type branching below)
- [ ] Version (optional; omit for git-sourced plugins)
### Source type branching
If the user has not specified a source type, ask:
> "Which source type does this plugin use?
> 1. **Local path** — plugin lives in this repo (e.g. `./plugins/<name>`)
> 2. **GitHub** — separate GitHub repo (e.g. `owner/repo`)
> 3. **Git URL** — any git host via full URL (e.g. `https://gitlab.com/org/repo.git`)
> 4. **npm** — distributed on npm (e.g. `@scope/package`)"
Source shapes per type:
**Local path:**
```json
"source": "./plugins/<name>"
```
**GitHub:**
```json
"source": { "source": "github", "repo": "owner/repo" }
```
Add `"ref": "<branch-or-tag>"` inside the object if the user specifies a branch or tag.
**Git URL:**
```json
"source": { "source": "git", "url": "https://..." }
```
Add `"ref": "<branch-or-tag>"` inside the object if specified.
**npm:**
```json
"source": { "source": "npm", "package": "@scope/pkg", "version": "1.0.0" }
```
`version` is required for npm source.
### Entry shape
The full entry added to `plugins[]`:
```json
{
"name": "<name>",
"description": "<description>",
"source": <source per type above>
}
```
Include `"version": "<version>"` at the entry level only when the source is npm or when the user explicitly requests a pinned version in the catalog.
### Step 1 — Read both files
Read `.claude-plugin/marketplace.json` and `.github/plugin/marketplace.json`. Verify they are identical. If they differ, stop and report the divergence — do not proceed until the user resolves it.
### Step 2 — Add the entry
Append the new entry to the `plugins[]` array in `.claude-plugin/marketplace.json`.
### Step 3 — Mirror
Apply the identical addition to `.github/plugin/marketplace.json` in the same edit pass.
### Step 4 — Validate
Follow the **VALIDATE** flow.
---
## REMOVE
Run this flow when an entry exists in `plugins[]` and the intent is to remove it.
### Step 1 — Confirm the target
Read `.claude-plugin/marketplace.json`. Identify the entry to remove. State the full entry as it currently appears.
### Step 2 — HITL gate
State clearly before proceeding:
> "I will remove the `<name>` entry from both `.claude-plugin/marketplace.json` and `.github/plugin/marketplace.json`. This does not delete the plugin files. Confirm?"
Do not proceed until the user confirms. If the user says "yes" or equivalent, continue to Step 3.
### Step 3 — Remove from both files
Remove the entry from `plugins[]` in `.claude-plugin/marketplace.json`.
Apply the identical removal to `.github/plugin/marketplace.json` in the same edit pass.
### Step 4 — Validate
Follow the **VALIDATE** flow.
---
## UPDATE
Run this flow when an entry exists in `plugins[]` and the intent is to change one or more fields.
### Step 1 — Read the current entry
Read `.claude-plugin/marketplace.json`. Show the current state of the target entry so the user can confirm the fields to change.
### Step 2 — Apply changes
State which fields will change and to what values, then edit `.claude-plugin/marketplace.json`.
Apply the identical change to `.github/plugin/marketplace.json` in the same edit pass.
### Step 3 — Validate
Follow the **VALIDATE** flow.
---
## VALIDATE
Run `claude plugin validate .` from the repo root:
```bash
cd <repo-root> && claude plugin validate .
```
Report the output. If validation fails, describe the specific error and what needs to be fixed. Do not attempt to auto-fix validation errors unless the fix is unambiguous (e.g. a trailing comma that violates JSON syntax); otherwise, describe the fix and ask the user to confirm.
Validation checks include: `marketplace.json` schema compliance, duplicate plugin names, source path traversal, and version mismatches.