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 a tsconfig.json, covering hooks/, types/ and tests/, that the plugin's own tsconfig.json extends. It is not ours, so .gitignore keeps 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 reports types ./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 no author, 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

PathWhat
plugin.jsonThe 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/ →