Plugins
A plugin is a directory with a name on it holding commands, skills, agents and hooks, so
they can be installed somewhere else. You have written four things in the last five lessons:
a /failing command, an add-report skill, a report-reviewer agent, and a hook that
protects the seed file. They are all in .claude/ in one repository.
Those four things, moved into a directory with a name on it, are a plugin. That is the whole
idea, and the reason to care is that “everyone should set
this up” is an instruction nobody follows and claude plugin install is one command.
The shape
stockroom-kit/
├── .claude-plugin/
│ └── plugin.json
├── commands/
│ └── failing.md
├── skills/
│ └── add-report/
│ └── SKILL.md
├── agents/
│ └── report-reviewer.md
└── hooks/
├── hooks.json
└── protect-seed.sh
Every one of those files is a copy of something you already wrote, moved. The only new file is the manifest:
{
"$schema": "https://anthropic.com/claude-code/plugin.schema.json",
"name": "stockroom-kit",
"version": "0.1.0",
"description": "Stockroom conventions: a command, a skill, a reviewer, a guard rail.",
"author": { "name": "Your Name" }
}
name and version are the load-bearing ones. description is what somebody reads when
deciding whether to install it.
The hook config moves to hooks/hooks.json and its script path changes:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/protect-seed.sh"
}
]
}
]
}
}
${CLAUDE_PLUGIN_ROOT} instead of ${CLAUDE_PROJECT_DIR}, because the script now lives
with the plugin rather than with the project. Forgetting this is the most common way a
plugin works for its author and nobody else.
Check it before you share it
claude plugin validate ./stockroom-kit --strict
Validating plugin manifest: /tmp/stockroom-kit/.claude-plugin/plugin.json
✔ Validation passed
--strict fails on warnings, which includes a missing author and unrecognised fields. Put
it in CI if the plugin is a team’s.
claude plugin init <name> scaffolds a starting point if you would rather not create the
directories yourself.
Try it without installing it
claude --plugin-dir ./stockroom-kit
That loads it for one session, from disk, with no install and nothing to undo. It is how you iterate.
Now the thing that will confuse you for ten minutes if nobody says it:
/failing
Unknown command: /failing
Plugin components are namespaced by plugin name. /context shows what loaded:
| stockroom-kit:report-reviewer | Plugin | 36 |
| stockroom-kit:failing | Plugin (stockroom-kit) | ~20 |
| stockroom-kit:add-report | Plugin (stockroom-kit) | ~40 |
So it is /stockroom-kit:failing, and it prints exactly what it printed in lesson 20.
The namespace is deliberate: two plugins can both ship a /deploy and neither wins. It does
mean a command you use constantly gets longer to type, which is a real argument for keeping
the everyday things in .claude/ and putting the shared things in a plugin.
Handing it to people
claude plugin install <plugin> # from a configured marketplace
claude plugin list # what is installed
claude plugin details <name> # components and their token cost
claude plugin enable / disable <name>
A marketplace is a repository with a manifest listing plugins. For a team, that is usually
one internal repository that everyone adds once, after which claude plugin install works
for everything in it.
claude plugin details is worth running on anything you install before you rely on it. It
lists the components and what they cost in context, and a plugin with eleven skills is
eleven descriptions in every session for everyone who installed it.
What is worth packaging as a plugin?
Conventions with a shape. Stockroom’s five-step report procedure is the same for everyone who works on it. So is the seed file rule.
Things that must be consistent. A hook is the best argument for a plugin, because a hook that only some of the team has is a rule that only sometimes applies.
Onboarding. A new person installs one thing and has the commands, the reviewer and the guard rails on their first afternoon.
Not: your personal preferences. Those go in ~/.claude/. A plugin is a thing you are
asking other people to run.
Not: something with one component that only you use. A single command in
.claude/commands/ is one file and costs nothing to maintain. As a plugin it is a directory,
a manifest, a version number and a namespace prefix.
Your turn
Package the four things you have written.
mkdir -p stockroom-kit/.claude-plugin stockroom-kit/commands \
stockroom-kit/skills/add-report stockroom-kit/agents stockroom-kit/hooks
Copy in your failing.md, add-report/SKILL.md, report-reviewer.md and
protect-seed.sh, write hooks/hooks.json with ${CLAUDE_PLUGIN_ROOT}, and write the
manifest. Then:
claude plugin validate ./stockroom-kit --strict
Check 1: it prints
✔ Validation passed. If it complains about the author, add one; that is--strictdoing its job.
Then load it and run its command:
claude --plugin-dir ./stockroom-kit
/stockroom-kit:failing
Check 2: the same output you got from
/failingin lesson 20, and/contextlists all three components with astockroom-kit:prefix. Type/failingwithout the prefix first, watch it fail, and you will not forget the namespace again.
Last, the check that matters most for a hook: with the plugin loaded, ask it to edit
data/seed.json in a clone that has no .claude/hooks/ of its own.
Check 3: it is still blocked, and the reason still says to edit
scripts/seed.mjs. The rule travelled with the plugin. If it is not blocked, yourhooks.jsonstill says${CLAUDE_PROJECT_DIR}.
Recap
A plugin is a directory with a manifest and the components you already wrote: commands, skills, agents, hooks. Nothing new to learn except where the files go.
${CLAUDE_PLUGIN_ROOT} replaces ${CLAUDE_PROJECT_DIR} in hook paths. Components are
namespaced as plugin:name, so /failing becomes /stockroom-kit:failing.
claude plugin validate --strict before you share it, --plugin-dir to try it without
installing, claude plugin details before you trust somebody else’s.
Package conventions, hooks and onboarding. Leave personal preferences in ~/.claude/ and
one-off commands in .claude/.
Next: running all of this with nobody watching.