Chapter 2: .claude-plugin, the name on the door
Every plugin has a folder called .claude-plugin holding one file,
plugin.json, and Claude Code reads it before anything else. It is the
plugin's package.json: a name, a version, a sentence about what it does.
Here is all of it:
{
"name": "jevhooks",
"version": "0.1.0",
"description": "Jev judges Bash commands before they run and turn ends before they stop",
"types": "./types/index.d.ts"
}
The first three lines are what you would expect. The fourth is the
interesting one. A mod can keep state that outlives one hook call (the last
decision, here, so the band above the prompt can show it), and Claude Code
wants to know its shape in advance. types points at a TypeScript file
that declares it, so the engine can check every read and write of that
state against one contract. That file is chapter 4.
Aside: the folder that appears by itself. Once Claude Code has loaded the plugin, a
types/folder turns up in here too. Claude Code puts it there: the type declarations for its own API (claude-code,claude-code-tools,claude-code-mcp) and atsconfig.json, coveringhooks/,types/andtests/, that the plugin's owntsconfig.jsonextends. It is not ours, so.gitignorekeeps it out of the repository, and nothing here links to it.
Try it.
claude plugin validate plugin. The first thing it checks is this file, and it reportstypes ./types/index.d.ts declares state: jevhooks.last: the manifest pointed at the contract, and the contract was read. It also warns that there is noauthor, which is true.
For the people who maintain it
name is also the plugin's key in its state (jevhooks.last), in the
atom that hooks/register.tsx makes, and in the test that mounts the
band (plugin: 'jevhooks'). version matches the Cargo workspace's
0.1.0, by hand.
In this folder
| Path | What |
|---|---|
| plugin.json | The manifest: name, version, description, and where the state's types are. |
types/ | Laid down by Claude Code; gitignored. |
← Previous: Chapter 1, plugin/ · Up: plugin · Next: Chapter 3, hooks/ →