# Cross-Tool Compatibility Reference Claude Code and GitHub Copilot CLI share the plugin concept but diverge in specific, breaking ways. **Skills are the portable core. Manifests and agents are where they split.** Make Claude Code the source of truth — it is the stricter, more fully specified format. Treat "loads in Copilot CLI" as a tested checklist item per plugin, not an assumption. --- ## Divergence table | 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 mirror to `.github/plugin/`. | | Plugin manifest path | `.claude-plugin/plugin.json` (required; only `plugin.json` goes in this dir) | `plugin.json` at **plugin root** | Ship in both locations until verified — see ⚠️ below | | Skills | `skills//SKILL.md` | `skills//SKILL.md` | ✅ Identical | | Agents | `agents/.md` | `agents/.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 | Run Claude validator + manual 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 @` | install by plugin name; `@marketplace` suffix unconfirmed | ⚠️ Verify Copilot install string before documenting | | Local install (dev) | `claude --plugin-dir ./plugin` | `copilot plugin install ./plugin` | Tool-specific | | Component paths in plugin.json | Claude defaults to standard dirs; path overrides via marketplace entry only | `"skills": "skills/"`, `"agents": "agents/"`, etc. in plugin.json | Generate per-tool manifests rather than one shared file | > ⚠️ **UNVERIFIED — test before committing to a layout.** > Copilot docs confirm it reads the **marketplace** manifest from `.claude-plugin/`. They do NOT > confirm the same fallback for a plugin's `plugin.json`. Copilot docs show `plugin.json` at plugin > root; Claude requires it in `.claude-plugin/`. Until verified: ship `plugin.json` in BOTH > `plugin-name/plugin.json` and `plugin-name/.claude-plugin/plugin.json` (identical content), then > drop whichever proves redundant. --- ## `@` resolution `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 they are separate fields. --- ## Canonical cross-compatible repo layout ```text repo-root/ ├── .claude-plugin/ │ └── marketplace.json # both tools read here ├── .github/plugin/ │ └── marketplace.json # OPTIONAL: Copilot canonical path (mirror) ├── plugins/ │ └── startup-cto/ │ ├── plugin.json # Copilot root manifest ┐ ship both until │ ├── .claude-plugin/ # │ the note above 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 shipping native agents) │ ├── hooks/hooks.json # Claude │ ├── hooks.json # Copilot (if hooks used) │ └── README.md └── README.md ``` --- ## Open questions to resolve before generating layouts 1. **Does Copilot CLI load a plugin whose `plugin.json` lives only in `.claude-plugin/`?** Install a test plugin both ways. The answer decides whether to ship one manifest or two. 2. **What is Copilot's exact install-from-marketplace command?** Run `copilot plugin install --help`. The `update`/`uninstall` commands take a bare plugin name — the `@marketplace` form may not apply.