Skip to content

Plugin Format

shipking-ai edited this page Sep 3, 2026 · 1 revision

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.

Two kinds of skill

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.

On-disk layout

<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.

Frontmatter

---
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.

$ARGUMENTS and $1..$9

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.

Discovery

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.

Naming

Commands are namespaced by plugin: code-review:review, not just review — so two plugins can each ship a command called review without colliding.

Verified against the real thing

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.

Clone this wiki locally