Adds a new skill for creating, managing, and adopting plugins across Claude Code and GitHub Copilot CLI marketplaces. Includes three Bash scripts (inventory, gen_manifests, validate), three reference docs (cross-compat, claude-code, copilot-cli), a test harness with 18 passing tests, and an eval.yaml. Also adds the `marketplace` category to CATEGORIES.md and commits the plugin marketplace architecture research doc that informed the skill design. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
335 lines
18 KiB
Markdown
335 lines
18 KiB
Markdown
# Plugin Marketplace Architecture
|
||
|
||
Reference for refactoring this repo of skills/agents/hooks/prompts into a single Git-based
|
||
plugin marketplace installable by **Claude Code** and **GitHub Copilot CLI**, and for building
|
||
the `marketplace-architect` skill that automates the migration.
|
||
|
||
> **Provenance & staleness:** Verified against the official Claude Code plugin docs
|
||
> (`code.claude.com/docs`) and GitHub Copilot CLI plugin docs (`docs.github.com`) as of
|
||
> **June 2026**. Both ecosystems are moving fast; re-verify the divergence table before a
|
||
> big migration. One item below (Copilot reading `.claude-plugin/plugin.json` per-plugin) is
|
||
> **explicitly unverified** — see the flagged note. Don't treat that part as settled.
|
||
|
||
---
|
||
|
||
## 1. Core model (this part is correct and stable)
|
||
|
||
- The **Git repository is the marketplace.** No backend, registry API, database, SaaS, or MCP
|
||
server is required. A marketplace is just a manifest file that lists plugins and where to find them.
|
||
- A **plugin is the deployable unit.** Each plugin bundles one or more of: skills, agents, hooks,
|
||
prompts/commands (flat `.md`), MCP servers, and — Claude Code only — output styles, LSP servers,
|
||
background monitors, a `bin/` on PATH, and default `settings.json`. "Workflows" from the handoff
|
||
aren't a distinct file type; express them as a skill that orchestrates steps, or a command.
|
||
- **Organize by user outcome, not file type.** `startup-cto/` and `security-reviewer/`, not
|
||
`all-skills/` and `all-agents/`.
|
||
- **Aim for ~10–20 opinionated plugins**, not 50 tiny ones. (This is a usability judgment, not a
|
||
hard rule from either vendor — but it's sound. Many skills can live inside one plugin.) The
|
||
handoff's suggested set, as a starting shape: `startup-cto`, `system-architect`,
|
||
`security-reviewer`, `product-manager`, `growth-marketer`, `developer-relations`,
|
||
`technical-writer`, `research-analyst`.
|
||
|
||
Everything below is where the original handoff was either wrong or incomplete.
|
||
|
||
---
|
||
|
||
## 2. The two tools are convergent, NOT identical
|
||
|
||
This is the single most important correction. The handoff assumed "write once, run both."
|
||
In reality the formats overlap heavily but diverge in specific, breaking ways. **Skills are the
|
||
portable core; manifests and agents are where they split.**
|
||
|
||
**Recommended stance:** make **Claude Code the source of truth** (it's the stricter, more
|
||
fully-specified format) and treat "loads in Copilot CLI" as a **tested checklist item per plugin**,
|
||
not an assumption. Encode the Copilot deltas (manifest location, `.agent.md` naming) explicitly in
|
||
the architect skill rather than pretending the two are the same. This is more honest than "write
|
||
once, run both" and stops surprises at install time.
|
||
|
||
### Divergence table (ground truth)
|
||
|
||
| Concern | Claude Code | GitHub Copilot CLI | Portable choice |
|
||
|---|---|---|---|
|
||
| Marketplace manifest path | `.claude-plugin/marketplace.json` (required) | `.github/plugin/marketplace.json` (primary); **also reads `.claude-plugin/`** | Put it in `.claude-plugin/` — both read it. Optionally also `.github/plugin/`. |
|
||
| Plugin manifest path | `.claude-plugin/plugin.json` (required; **only** plugin.json goes in this dir) | `plugin.json` at **plugin root** | ⚠️ See flagged note — may need it in **both** locations |
|
||
| Skills | `skills/<name>/SKILL.md` | `skills/<name>/SKILL.md` | ✅ Identical — lean on these |
|
||
| Agents | `agents/<name>.md` | `agents/<name>.agent.md` (frontmatter incl. `tools:`) | Diverges — keep portable logic in skills; ship per-tool agent files only when needed |
|
||
| Hooks | `hooks/hooks.json` | `hooks.json` at plugin root | Diverges; declare paths in manifest to be safe |
|
||
| MCP servers | `.mcp.json` at plugin root | `.mcp.json` at plugin root | ✅ Same |
|
||
| Relative `source` | must start with `./` | `./x` and `x` both valid | Always use `./` — valid for both |
|
||
| Validate command | `claude plugin validate .` (or `/plugin validate .`) | none documented | Claude validator + custom JSON checks for Copilot |
|
||
| Install marketplace | `claude plugin marketplace add owner/repo` | `copilot plugin marketplace add owner/repo` | Same shape |
|
||
| Install plugin | `claude plugin install <name>@<marketplace-name>` | from a registered marketplace by plugin name; **`@marketplace` suffix not confirmed** — `update`/`uninstall` take a bare `<name>`, so don't assume Claude's `@marketplace` form | ⚠️ Verify the Copilot install string before documenting it |
|
||
| Local install (dev) | `claude --plugin-dir ./plugin` | `copilot plugin install ./plugin` | Tool-specific |
|
||
|
||
> ⚠️ **FLAGGED / UNVERIFIED — test this by hand before committing to a layout.**
|
||
> Copilot's docs explicitly confirm it falls back to reading the **marketplace** manifest from
|
||
> `.claude-plugin/`. They do **not** confirm the same fallback for an individual plugin's
|
||
> `plugin.json`; the Copilot docs only show `plugin.json` at the plugin root. Claude *requires*
|
||
> it in `.claude-plugin/`. Until you verify, the pragmatic move is to **ship `plugin.json` in
|
||
> both** `plugin-name/plugin.json` and `plugin-name/.claude-plugin/plugin.json` (identical
|
||
> content), then drop whichever proves redundant. The architect skill should generate both and
|
||
> note the duplication.
|
||
|
||
### `@<marketplace-name>` resolves to the manifest `name`, not the repo
|
||
|
||
`claude plugin install startup-cto@my-ai-marketplace` requires the marketplace manifest's
|
||
top-level `name` field to be exactly `my-ai-marketplace`. It is **not** the GitHub repo name.
|
||
Keep them aligned to avoid confusion, but know they're separate things.
|
||
|
||
---
|
||
|
||
## 3. Canonical repo layout (cross-compatible)
|
||
|
||
```text
|
||
repo-root/
|
||
├── .claude-plugin/
|
||
│ └── marketplace.json # both tools read here
|
||
├── .github/plugin/
|
||
│ └── marketplace.json # OPTIONAL: Copilot's canonical path (mirror of above)
|
||
├── plugins/
|
||
│ └── startup-cto/
|
||
│ ├── plugin.json # Copilot root manifest ┐ ship both until
|
||
│ ├── .claude-plugin/ # │ the §2 note is
|
||
│ │ └── plugin.json # Claude manifest ┘ verified
|
||
│ ├── skills/
|
||
│ │ ├── fundraising/SKILL.md
|
||
│ │ └── hiring/SKILL.md
|
||
│ ├── agents/
|
||
│ │ ├── startup-cto.md # Claude
|
||
│ │ └── startup-cto.agent.md # Copilot (only if you ship native agents)
|
||
│ ├── hooks/hooks.json # Claude
|
||
│ ├── hooks.json # Copilot (if hooks used)
|
||
│ ├── docs/
|
||
│ └── README.md
|
||
└── README.md
|
||
```
|
||
|
||
If maintaining two manifest copies is annoying, generate the mirrors from one source in CI
|
||
(see §7) rather than hand-editing both.
|
||
|
||
### Manifest shapes
|
||
|
||
`marketplace.json` (root):
|
||
|
||
```json
|
||
{
|
||
"name": "my-ai-marketplace",
|
||
"owner": { "name": "Your Name", "email": "you@example.com" },
|
||
"metadata": { "description": "Agents, skills and workflows", "version": "1.0.0" },
|
||
"plugins": [
|
||
{ "name": "startup-cto", "source": "./plugins/startup-cto", "description": "..." },
|
||
{ "name": "system-architect", "source": "./plugins/system-architect", "description": "..." }
|
||
]
|
||
}
|
||
```
|
||
|
||
`plugin.json` (keep minimal; add fields only when needed):
|
||
|
||
```json
|
||
{
|
||
"name": "startup-cto",
|
||
"version": "1.0.0",
|
||
"description": "Startup technical leadership toolkit",
|
||
"author": { "name": "Your Name" }
|
||
}
|
||
```
|
||
|
||
Plugin names must be **kebab-case** (lowercase, digits, hyphens). Claude.ai's marketplace sync
|
||
rejects anything else even though the local CLI may tolerate it.
|
||
|
||
---
|
||
|
||
## 4. Gotchas the original handoff omitted
|
||
|
||
These will cause real breakage during refactor. The architect skill must check for them.
|
||
|
||
1. **Plugins are copied to a cache on install.** A plugin **cannot** reference files outside its
|
||
own directory (e.g. `../shared-utils`) — those files aren't copied. If your current repo shares
|
||
helper files across skills/agents, that sharing breaks. Fix by **duplicating** the shared file
|
||
into each plugin or using **symlinks**. Audit for cross-references before moving anything.
|
||
|
||
2. **Version-pinning footgun.** If `plugin.json` sets `"version"` and you don't bump it on a new
|
||
release, existing users get **no update** — the cached copy is kept. Either bump every release,
|
||
or **omit `version`** so the git commit SHA is used (every commit = new version). Don't set
|
||
`version` in both `plugin.json` and the marketplace entry; the `plugin.json` value wins silently.
|
||
|
||
3. **Reserved marketplace names.** Claude blocks a set of names (`anthropic-*`, `claude-*`,
|
||
`agent-skills`, and impersonators like `official-claude-plugins`). Validate against these.
|
||
|
||
4. **`commands/` ≠ `skills/` in Claude.** A flat `foo.md` is a legacy *command*; a
|
||
`foo/SKILL.md` directory is a *skill*. Promote flat command files to skill directories during
|
||
migration — don't treat them as interchangeable.
|
||
|
||
5. **Use `${CLAUDE_PLUGIN_ROOT}`** in Claude hook/MCP configs to reference in-plugin files, since
|
||
the plugin runs from a cache path, not its repo location.
|
||
|
||
6. **Strict mode (Claude).** A marketplace plugin entry defaults to `strict: true` — `plugin.json`
|
||
is authoritative and the entry can only supplement it. Set `strict: false` to make the
|
||
marketplace entry the *entire* definition (it then declares `skills`/`agents`/`hooks`/`mcpServers`
|
||
path arrays itself, and the plugin needs no `plugin.json`). Useful when the architect curates a
|
||
plugin's exposed components differently from how the files are laid out. Don't mix the two — a
|
||
`strict:false` entry plus a component-declaring `plugin.json` is a conflict and fails to load.
|
||
|
||
7. **Plugin sources beyond relative paths (Claude).** This repo uses `./plugins/x` relative sources
|
||
(simplest for a monorepo). If you later split a plugin into its own repo, the `source` field also
|
||
supports `github` (`owner/repo` + `ref`/`sha`), `git-subdir` (sparse clone of a monorepo path),
|
||
`url` (any git host), and `npm` (published package). Copilot's docs only *show* relative-path
|
||
sources — other source types aren't documented there, so don't rely on them cross-tool. Stay on
|
||
relative paths unless you have a reason not to.
|
||
|
||
8. **Copilot declares component paths in `plugin.json` (Claude does it differently).** Copilot's
|
||
`plugin.json` can carry `"skills": "skills/"`, `"agents": "agents/"`, `"hooks": "hooks.json"`,
|
||
`"mcpServers": ".mcp.json"` fields that tell it where components live. Claude instead defaults to
|
||
the standard dirs and only takes path overrides via the *marketplace entry* (see strict mode,
|
||
#6). So the same `plugin.json` may need these path fields for Copilot but not for Claude — another
|
||
reason the architect should generate per-tool manifests rather than one shared file.
|
||
|
||
---
|
||
|
||
## 5. The `marketplace-architect` skill spec
|
||
|
||
Build this skill **on the corrected spec above** — not on the original handoff, which would bake
|
||
in the format errors. It's a Claude Code skill (`skills/marketplace-architect/SKILL.md`) following
|
||
standard skill conventions: a tight SKILL.md body (<500 lines) plus bundled `references/` and
|
||
`scripts/` loaded progressively.
|
||
|
||
### Frontmatter (description is the trigger — make it pushy)
|
||
|
||
```yaml
|
||
---
|
||
name: marketplace-architect
|
||
description: >
|
||
Audits a repository of Claude Code / Copilot CLI skills, agents, hooks, and prompts and
|
||
refactors it into an installable plugin marketplace. Use this whenever the user wants to
|
||
organize loose skills/agents into plugins, define plugin boundaries, generate plugin.json
|
||
or marketplace.json manifests, plan a migration to a plugin marketplace, validate plugin
|
||
naming or detect duplicate capabilities, or set up cross-tool (Claude Code + GitHub Copilot
|
||
CLI) distribution — even if they don't say the word "marketplace".
|
||
---
|
||
```
|
||
|
||
### Responsibilities (from the handoff, refined)
|
||
|
||
1. **Audit** — walk the repo; classify every asset as skill / command / agent / hook / prompt / MCP.
|
||
2. **Detect cross-references** — flag any `../` or shared-file dependencies that break under caching (§4.1).
|
||
3. **Recommend plugin boundaries** — group by outcome; warn on 50-tiny-plugins sprawl.
|
||
4. **Prevent duplicate capabilities** — diff skill descriptions/agents for overlap before splitting.
|
||
5. **Generate manifests** — emit `plugin.json` (both locations per §2 note) and `marketplace.json`
|
||
(`.claude-plugin/`, optionally mirror to `.github/plugin/`).
|
||
6. **Validate naming** — kebab-case, reserved names, unique plugin names, `@name` ↔ manifest `name`.
|
||
7. **Produce a migration plan** — concrete file-move list (old path → new path) as a checklist.
|
||
8. **Emit cross-tool deltas** — for each plugin, note what's needed for Copilot (`.agent.md`,
|
||
root `plugin.json`) vs Claude.
|
||
9. **Generate release notes + install docs** — per-plugin README with both `claude` and `copilot`
|
||
install commands.
|
||
|
||
### Suggested bundled structure
|
||
|
||
```text
|
||
skills/marketplace-architect/
|
||
├── SKILL.md
|
||
├── references/
|
||
│ ├── claude-code.md # Claude paths, validate, version rules, reserved names
|
||
│ ├── copilot-cli.md # Copilot paths, .agent.md, marketplace fallback
|
||
│ └── cross-compat.md # the §2 divergence table — the heart of the skill
|
||
└── scripts/
|
||
├── inventory.py # scan repo → classify assets → emit a table
|
||
├── gen_manifests.py # write plugin.json + marketplace.json (both layouts)
|
||
└── validate.py # wraps `claude plugin validate .` + JSON/naming checks
|
||
```
|
||
|
||
Put the §2 divergence table verbatim into `references/cross-compat.md` — that's the knowledge the
|
||
skill exists to apply. Keep SKILL.md to the workflow (audit → boundaries → generate → validate)
|
||
and point it at the reference files.
|
||
|
||
### Authoring notes
|
||
|
||
- Use **imperative** instructions ("Scan the repo", "Emit the manifest").
|
||
- Explain *why* a rule matters (e.g. the cache constraint) rather than bare MUSTs — the model
|
||
applies judgment better with rationale.
|
||
- After drafting, run 2–3 realistic test prompts (e.g. "turn this repo into a marketplace",
|
||
"which of these skills belong together?") and iterate.
|
||
|
||
---
|
||
|
||
## 6. Refactor playbook (corrected phases)
|
||
|
||
Run these *with* the architect skill once it exists; it automates 1–5.
|
||
|
||
1. **Inventory.** Classify every asset (skill/command/agent/hook/prompt/MCP). Record current path.
|
||
2. **Detect breakage.** Find cross-references and shared files (§4.1). Decide duplicate vs symlink.
|
||
3. **Draw boundaries.** Group by outcome into ~10–20 plugins. De-dupe overlapping capabilities.
|
||
4. **Move files.**
|
||
- `skills/react.md` (flat command) → `plugins/system-architect/skills/react/SKILL.md`
|
||
- `agents/startup-founder.md` → `plugins/startup-cto/agents/startup-founder.md`
|
||
(+ `startup-founder.agent.md` if shipping Copilot-native agents)
|
||
5. **Generate manifests.** Per-plugin `plugin.json` (both locations); root `marketplace.json`.
|
||
6. **Validate.** `claude plugin validate .` — fix every warning. Then test-install in both tools:
|
||
- `claude --plugin-dir ./plugins/<x>` then `/plugin install <x>@<marketplace>`
|
||
- `copilot plugin install ./plugins/<x>` then `copilot plugin list` / `/skills list` / `/agent`
|
||
7. **Document.** Per-plugin README with both install commands; root README listing all plugins.
|
||
|
||
---
|
||
|
||
## 7. Future / roadmap (preserved from the handoff)
|
||
|
||
Three post-migration features the original handoff called for. Not needed for the first cut, but
|
||
recorded here so the intent isn't lost.
|
||
|
||
### Marketplace website
|
||
|
||
Generate a docs site **directly from `marketplace.json`** — no separate content source. Iterate
|
||
over the `plugins` array to produce a browsable catalog (one page per plugin from its
|
||
`description`/`README.md`), publishable to something like `marketplace.example.com` via GitHub
|
||
Pages. Because the manifest is the single source of truth, the site never drifts from what's
|
||
installable.
|
||
|
||
### Plugin templates
|
||
|
||
Add a `templates/` directory so contributors never start from scratch:
|
||
|
||
```text
|
||
templates/
|
||
skill-plugin/ # plugin.json + skills/<name>/SKILL.md skeleton
|
||
agent-plugin/ # plugin.json + agents/ skeleton (both .md and .agent.md)
|
||
workflow-plugin/ # plugin.json + a multi-step orchestration skill
|
||
```
|
||
|
||
Each ships the dual-manifest layout from §3 so new plugins are cross-tool by default.
|
||
|
||
### CI validation (the cross-tool catch)
|
||
|
||
A GitHub Action can gate PRs, but remember: **`claude plugin validate` covers the Claude side
|
||
only.** There's no Copilot validator, so the Action needs custom JSON checks for the Copilot
|
||
layout. Minimum checks:
|
||
|
||
- Valid JSON in every `marketplace.json` / `plugin.json`.
|
||
- Each plugin's `SKILL.md` files have valid YAML frontmatter.
|
||
- Unique plugin names; kebab-case; no reserved names.
|
||
- Every `source` path resolves to an existing directory.
|
||
- (If mirroring) `.claude-plugin/` and `.github/plugin/` manifests are in sync.
|
||
- Version consistency (no conflicting `version` in plugin.json vs marketplace entry).
|
||
|
||
Fail the PR on any violation so the contributor flow stays self-service.
|
||
|
||
### Success criteria (the target end-state)
|
||
|
||
A new contributor should be able to, with **no manual registry edits, no backend, no MCP
|
||
dependency**: (1) fork the repo, (2) create a plugin folder, (3) add `plugin.json`, (4) add
|
||
skills/agents, (5) open a PR, (6) have the plugin appear in the marketplace automatically once
|
||
merged. If a step requires hand-editing a central list, the automation isn't done.
|
||
|
||
---
|
||
|
||
## Open questions to resolve first
|
||
|
||
Two facts couldn't be confirmed from the published docs. Settle both with a one-skill test plugin
|
||
before the architect generates layouts:
|
||
|
||
1. **Does Copilot CLI load a plugin whose `plugin.json` lives only in `.claude-plugin/`?** (Install
|
||
the test plugin both ways.) The answer decides whether you ship one manifest or two. Copilot's
|
||
docs put `plugin.json` at the plugin root; Claude requires `.claude-plugin/`.
|
||
2. **What is Copilot's exact install-from-marketplace command?** The how-to pages don't show the
|
||
literal string, and Copilot's `update`/`uninstall` take a bare plugin name — so it may *not* use
|
||
Claude's `<name>@<marketplace>` form. Run `copilot plugin install --help` and confirm before
|
||
putting the command in any README or the architect's generated docs.
|