-
Notifications
You must be signed in to change notification settings - Fork 1
Plugin Format
An existing Claude Code plugin directory loads here unchanged. This page is a consolidation of what's currently spread across npm/src/plugins.ts's doc comments and CONTRIBUTING.md — the source of truth is the code; this is the map of it.
Implemented from the format's public shape — nothing here is copied from Anthropic's source, and none of it is their code.
An OMNIHARNESS.md skill and a plugin command look similar from the outside — both show up as a callable skill — but they do different things:
OMNIHARNESS.md skill |
Plugin command | |
|---|---|---|
| What it is | a shell command | a prompt |
| What invoking it does | runs the command | puts the body in front of the model as instructions, with $ARGUMENTS filled in |
| Needs the shell | yes | no — it runs nothing itself |
| Risk marking | high-risk | not high-risk on its own; whatever tools it then asks for are gated normally |
Skill.kind tells them apart. Absent means shell, so every existing OMNIHARNESS.md is untouched by any of this.
<plugin>/.claude-plugin/plugin.json { name, description, version, author }
<plugin>/commands/*.md frontmatter + a markdown prompt body
<plugin>/agents/*.md same shape, described as an agent
A directory holding .claude-plugin/marketplace.json is a marketplace: a collection whose plugins entries point at individual plugin directories by a relative source. A source that resolves outside the marketplace is ignored rather than followed.
--- description: Code review a pull request allowed-tools: Bash(gh pr view:*), Bash(gh pr diff:*), mcp__github__comment --- Review the PR.
Read by a small key/value reader, not a full YAML parser. These files come from the internet, and a parser that can evaluate arbitrary YAML is a poor thing to point at them. allowed-tools splits on commas that sit outside the parentheses of a Bash(...) entry — recorded, not yet enforced.
A command's body may use $ARGUMENTS (the whole invocation argument, trimmed) or positional 1ドル, 2ドル, etc. — one word each, matching the shell convention the format borrows. A token with nothing to fill it becomes empty, not left as literal 2ドル, so the model never sees an unfilled placeholder and mistakes it for instruction.
Searched in order, nearest first:
<workspace>/.claude/plugins
<workspace>/.omniharness/plugins
~/.claude/plugins
A workspace-level plugin wins a name collision with a user-level one. Discovery recurses up to two levels — cloning a plugin repository into .claude/plugins puts a marketplace one level below the search root, which is the ordinary case, not an edge one.
OMNIHARNESS_PLUGIN_PATH overrides the whole list, :-delimited like PATH; setting it empty disables discovery entirely. This exists because the default reaches into the user's home directory — without an override, what an agent can do depends on what happens to be installed on the machine it's running on, which is exactly the kind of thing that makes a test pass on one box and fail on every other one.
Commands are namespaced by plugin: code-review:review, not just review — so two plugins can each ship a command called review without colliding.
Cloning the actual anthropics/claude-code marketplace into .claude/plugins and loading it:
plugins discovered: 41
skills exposed: 63
Including code-review:code-review, whose prompt body starts "Provide a code review for the given pull request..." — read correctly, namespaced correctly, exposed to the model as a prompt-kind skill.
See Security Model for how MCP tool descriptions relate to this — a plugin's allowed-tools frontmatter is recorded but not yet enforced, so a plugin from an untrusted source is trusted the same way an MCP server is: don't run one you don't trust.