← Claude for Real Work

Plugins

Lesson 25 of 28 · 11 min · Updated

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 --strict doing 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 /failing in lesson 20, and /context lists all three components with a stockroom-kit: prefix. Type /failing without 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, your hooks.json still 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.