A Claude Code plugin template that also ships as a Cursor plugin, a Codex plugin,
a pi package, and an opencode plugin — from one source tree. Write your
Agent Skills (SKILL.md), slash commands, subagents, and
MCP servers once; the repo is its own one-plugin marketplace, and a validator in CI
keeps the manifests in sync.
The repository root is the plugin: shared components (skills/, commands/,
agents/) sit at the top level, a small manifest per harness describes them, and a
generated one-entry catalog makes the repo installable as a marketplace.
One repository, one plugin — create a new repository from this template for each
plugin you build.
| Harness | Manifest | Loads | Install from |
|---|---|---|---|
| Claude Code | .claude-plugin/plugin.json |
skills, commands, agents, hooks, .mcp.json |
the repo as a marketplace |
| Cursor | .cursor-plugin/plugin.json |
skills, commands, agents, rules/*.mdc, mcp.json |
the repo |
| Codex | .codex-plugin/plugin.json |
skills, hooks, .mcp.json |
the same marketplace |
| pi | package.json ("pi" key) |
skills, commands (as prompt templates) | pi install git:… |
| opencode | .opencode-plugin/ adapter |
skills, commands, agents, .mcp.json |
opencode plugin github:… |
Exact install commands and per-harness caveats are in How one tree serves three harnesses, pi, and opencode below.
-
Click Use this template on GitHub (or
gh repo create my-plugin --template DanielHou315/multi-harness-plugin-template --private --clone). -
Initialise it — this renames the plugin in both manifests, writes a starter README over this one, regenerates the catalog, validates, and then removes itself:
scripts/init_plugin.sh doc-translator "Translate Markdown docs between languages. Use when asked to translate or localise documentation."Author name and email default to your
git config; seescripts/init_plugin.sh --helpfor--display-name,--author,--email,--category, and--keep. -
Replace the example components with real ones and commit.
Requires bash and jq.
├── .claude-plugin/
│ ├── plugin.json # Claude Code manifest
│ └── marketplace.json # one-entry catalog — GENERATED, do not edit
├── .cursor-plugin/plugin.json # Cursor manifest
├── .codex-plugin/plugin.json # Codex manifest
├── .opencode-plugin/ # opencode adapter (package.json + index.js)
├── marketplace.config.json # catalog-only fields (category)
├── package.json # pi package manifest ("pi" key)
├── skills/example-skill/SKILL.md
├── commands/example-command.md
├── agents/example-agent.md
├── scripts/
│ ├── init_plugin.sh # one-shot: template -> your plugin
│ ├── gen_catalog.sh # regenerates the catalog from plugin.json
│ └── validate_plugin.sh # the validator (CI + local)
├── docs/develop_plugin.md # full structure reference
├── AGENTS.md / CLAUDE.md # rules for coding agents working on the plugin
└── .github/workflows/validate.yml
| Claude Code | Cursor | Codex | |
|---|---|---|---|
| Manifest | .claude-plugin/plugin.json |
.cursor-plugin/plugin.json |
.codex-plugin/plugin.json |
| Installed via | .claude-plugin/marketplace.json ("source": "./") |
the repo itself | the same Claude catalog |
| Install | claude plugin marketplace add <owner>/<repo> then claude plugin install <name>@<name> |
add the repo in plugin settings | codex plugin marketplace add <owner>/<repo> then codex plugin add <name>@<name> |
| pi coding agent | |
|---|---|
| Manifest | package.json (the "pi" key) |
| Installed via | the repo itself as a pi package (git, npm, or local path) |
| Install | pi install git:github.com/<owner>/<repo> (append @<ref> to pin) |
| Loads | skills/ as skills; commands/*.md as prompt templates (/example-command) |
| Not loaded | agents/ (no subagents), rules/, .mcp.json (no built-in MCP) |
| opencode | |
|---|---|
| Manifest | .opencode-plugin/package.json (plugin id = name) + index.js adapter |
| Installed via | the repo as a git package (root package.json exports["./server"] → the adapter), or a local clone |
| Install | opencode plugin -g github:<owner>/<repo> (append #<ref> to pin) |
| Loads | skills/ (via skills.paths), commands/*.md as commands, agents/*.md as subagents, .mcp.json servers |
| Not loaded | rules/, hooks/; Claude-only frontmatter (allowed-tools, tools, model: sonnet) |
From a local clone instead: opencode plugin -g <dir>/.opencode-plugin (then
git pull there to update). Skills-only alternative, no adapter:
"skills": {"paths": ["<dir>/skills"]} in opencode.json.
scripts/gen_catalog.sh # after editing a plugin.json or marketplace.config.json
scripts/validate_plugin.sh --strict # before every commit; CI runs the same
claude plugin validate . --strict # optional, official Claude Code checker
claude --plugin-dir . # try the plugin without installing itSee docs/develop_plugin.md for manifest fields,
component frontmatter, optional config files (MCP, hooks, rules), and everything
the validator checks.
Direct pushes to main are not accepted. Fork the repository, make your change on
a branch in your fork, and open a pull request against main. CI must pass
(scripts/validate_plugin.sh --strict) before a PR can be merged.
Create a repository from this template and run scripts/init_plugin.sh. All three
harnesses read the same skills/, commands/, and agents/ directories; each
only needs its own small manifest (.claude-plugin/, .cursor-plugin/,
.codex-plugin/), which the init script fills in and the validator keeps in sync.
Codex installs from the same marketplace.json as Claude Code.
Yes. A plugin with only a skills/ directory is valid — delete the example command
and agent. The skills load in every harness above, including pi and opencode.
No. scripts/gen_catalog.sh generates a one-entry .claude-plugin/marketplace.json
pointing at the repo root ("source": "./"), so claude plugin marketplace add <owner>/<repo> (or codex plugin marketplace add) works on the plugin repo itself.
Declare it in .mcp.json (Claude Code, Codex, opencode) and the same servers in
mcp.json (Cursor). See docs/develop_plugin.md for
${CLAUDE_PLUGIN_ROOT} handling in each harness. pi has no built-in MCP support.
Run scripts/validate_plugin.sh --strict before committing; the included GitHub
Actions workflow runs it on every pull request. It checks name/version parity across
all manifests, frontmatter on every component, and that the catalog isn't stale.