# Plugins | Claude for Real Work

> Packaging the command, the skill, the agent and the hook you just wrote into one thing your team can install, and checking it works for them too.

Source: https://karakabakov.com/courses/claude-for-real-work/plugins/

---

[&larr; Claude for Real Work](https://karakabakov.com/courses/claude-for-real-work/)

# Plugins

Lesson 25 of 28 · 11 min · Updated August 21, 2026

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.

[&larr; Previous

## MCP](https://karakabakov.com/courses/claude-for-real-work/mcp/) [Next &rarr;

## Headless and CI](https://karakabakov.com/courses/claude-for-real-work/headless-and-ci/)

[&larr; All lessons](https://karakabakov.com/courses/claude-for-real-work/) [Next lesson &rarr;](https://karakabakov.com/courses/claude-for-real-work/headless-and-ci/)
