Home / Extending: skills, MCP, subagents, hooks, plugins
Hooks: automating the lifecycle
A hook is an action the harness executes itself on a precise lifecycle event, never a preference entrusted to the model in the hope that it remembers.
A hook is an action the harness executes itself when a precise lifecycle event occurs, never a request the model interprets at the moment of acting. Writing to a memory file every time you commit, running the tests, remains a preference: the model may forget it after a long conversation or a compaction. A hook declared on the corresponding event runs on every occurrence, independently of what the model recalls at that moment.
The mechanism: an event, a command, a decision
A hook is declared in settings.json, or in a plugin's hooks.json file, tied to a lifecycle event and an action type: a shell command, an HTTP call, an MCP tool, a prompt, or an agent. When the event occurs, the harness runs that action and waits for a decision in return, before the question reaches the model.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "./verifier-avant-commande.sh" }
]
}
]
}
}
This declaration ties the PreToolUse event, restricted to calls of the Bash tool, to a script that runs before every proposed command. Each hook type carries its own default timeout, different depending on the event that triggers it: the next lesson details these timeouts event by event, along with the precise JSON fields a hook returns to block, allow, or enrich what the model sees.
Hook versus memory rule: what each actually guarantees
A memory rule depends on a model that rereads it and decides to apply it, within a context window that grows over the course of a session. A hook depends on the harness, which checks the event independently of what the conversation holds at that moment. The difference shows up most on a long session: an instruction repeated twenty times in a memory file can dilute within a loaded context, whereas a hook declared once continues to run identically on the thousandth call as on the first.
Translating an intended automatism into a hook rather than a memory note is therefore less a matter of style than a matter of mechanical reliability: one depends on a careful reading by the model, the other on an event the harness itself observes.
From trigger to decision
Memory rule versus declared hook
| Making a repeated automatism reliable | What triggers the action | Behaviour on a long session | Where the automatism is declared |
|---|---|---|---|
| Rule written in memory | A reading by the model, which must remember it at the right moment | Can dilute after a compaction or a loaded context | A CLAUDE.md file, interpreted by the model |
| Declared hook | A lifecycle event, observed by the harness itself | Runs identically on every occurrence of the event | settings.json or a plugin's hooks.json, executed by the harness |
A developer declares a PreToolUse hook in settings.json that blocks any call to the Bash tool containing the pattern rm followed by -rf. He then asks Claude Code to run a command containing this pattern, and sees the hook trigger.
Write in one sentence what this situation establishes, and in one sentence what it does not establish.
What this establishes: This situation establishes that the hook does intercept a Bash command matching the declared pattern, at the moment the tool is about to run.
What this does not establish: It does not establish that the same hook will intercept a destructive command phrased differently, for example with separate options or a dynamically built path, since only the tested pattern was observed.
The three most common miscalibrations
- Too broad This situation proves that the hook now blocks every destructive command, whatever its phrasing.
- Too narrow This situation proves nothing, since a hook can never replace the vigilance of a developer who rereads every command.
- Beside the point This situation shows that the hook ran faster than a manual review of the command would have.
- A hook fires because a lifecycle event occurs, never because the model decides to apply it at the moment of acting.
- A rule repeated in a memory file can dilute within a loaded context, a hook declared once runs identically on every occurrence of the event.
- A hook is declared in settings.json or in a plugin's hooks.json file, tied to an action type among shell command, HTTP call, MCP tool, prompt, or agent.
- Each hook type carries its own default timeout, detailed event by event in the next lesson.
Create a minimal .claude/settings.json file in a test repository, declare a PreToolUse hook there that prints a message before every Bash command so you can watch the mechanism trigger, then remove that hook once the demonstration is done.
Every datable claim in this lesson links here to the public text behind it. A source that does not open proves nothing.