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>
18 KiB
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.jsonper-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, abin/on PATH, and defaultsettings.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/andsecurity-reviewer/, notall-skills/andall-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'splugin.json; the Copilot docs only showplugin.jsonat the plugin root. Claude requires it in.claude-plugin/. Until you verify, the pragmatic move is to shipplugin.jsonin bothplugin-name/plugin.jsonandplugin-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)
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):
{
"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):
{
"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.
-
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. -
Version-pinning footgun. If
plugin.jsonsets"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 omitversionso the git commit SHA is used (every commit = new version). Don't setversionin bothplugin.jsonand the marketplace entry; theplugin.jsonvalue wins silently. -
Reserved marketplace names. Claude blocks a set of names (
anthropic-*,claude-*,agent-skills, and impersonators likeofficial-claude-plugins). Validate against these. -
commands/≠skills/in Claude. A flatfoo.mdis a legacy command; afoo/SKILL.mddirectory is a skill. Promote flat command files to skill directories during migration — don't treat them as interchangeable. -
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. -
Strict mode (Claude). A marketplace plugin entry defaults to
strict: true—plugin.jsonis authoritative and the entry can only supplement it. Setstrict: falseto make the marketplace entry the entire definition (it then declaresskills/agents/hooks/mcpServerspath arrays itself, and the plugin needs noplugin.json). Useful when the architect curates a plugin's exposed components differently from how the files are laid out. Don't mix the two — astrict:falseentry plus a component-declaringplugin.jsonis a conflict and fails to load. -
Plugin sources beyond relative paths (Claude). This repo uses
./plugins/xrelative sources (simplest for a monorepo). If you later split a plugin into its own repo, thesourcefield also supportsgithub(owner/repo+ref/sha),git-subdir(sparse clone of a monorepo path),url(any git host), andnpm(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. -
Copilot declares component paths in
plugin.json(Claude does it differently). Copilot'splugin.jsoncan 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 sameplugin.jsonmay 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)
---
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)
- Audit — walk the repo; classify every asset as skill / command / agent / hook / prompt / MCP.
- Detect cross-references — flag any
../or shared-file dependencies that break under caching (§4.1). - Recommend plugin boundaries — group by outcome; warn on 50-tiny-plugins sprawl.
- Prevent duplicate capabilities — diff skill descriptions/agents for overlap before splitting.
- Generate manifests — emit
plugin.json(both locations per §2 note) andmarketplace.json(.claude-plugin/, optionally mirror to.github/plugin/). - Validate naming — kebab-case, reserved names, unique plugin names,
@name↔ manifestname. - Produce a migration plan — concrete file-move list (old path → new path) as a checklist.
- Emit cross-tool deltas — for each plugin, note what's needed for Copilot (
.agent.md, rootplugin.json) vs Claude. - Generate release notes + install docs — per-plugin README with both
claudeandcopilotinstall commands.
Suggested bundled structure
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.
- Inventory. Classify every asset (skill/command/agent/hook/prompt/MCP). Record current path.
- Detect breakage. Find cross-references and shared files (§4.1). Decide duplicate vs symlink.
- Draw boundaries. Group by outcome into ~10–20 plugins. De-dupe overlapping capabilities.
- Move files.
skills/react.md(flat command) →plugins/system-architect/skills/react/SKILL.mdagents/startup-founder.md→plugins/startup-cto/agents/startup-founder.md(+startup-founder.agent.mdif shipping Copilot-native agents)
- Generate manifests. Per-plugin
plugin.json(both locations); rootmarketplace.json. - 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>thencopilot plugin list//skills list//agent
- 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:
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.mdfiles have valid YAML frontmatter. - Unique plugin names; kebab-case; no reserved names.
- Every
sourcepath resolves to an existing directory. - (If mirroring)
.claude-plugin/and.github/plugin/manifests are in sync. - Version consistency (no conflicting
versionin 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:
- Does Copilot CLI load a plugin whose
plugin.jsonlives only in.claude-plugin/? (Install the test plugin both ways.) The answer decides whether you ship one manifest or two. Copilot's docs putplugin.jsonat the plugin root; Claude requires.claude-plugin/. - What is Copilot's exact install-from-marketplace command? The how-to pages don't show the
literal string, and Copilot's
update/uninstalltake a bare plugin name — so it may not use Claude's<name>@<marketplace>form. Runcopilot plugin install --helpand confirm before putting the command in any README or the architect's generated docs.