Hooks and the Hook Chain
A hook is a function that Claude Code calls when an event happens.
You register it with on.
It can watch the event, change it, or answer it.
The events themselves are on the page of their namespace. See the Mod API Overview for the map.
Remove the on call to turn a hook off. disableAllHooks stops installed mods.
Official pages: React to Events, Mods Reference, Mods Overview.
Example
This mod refuses every Bash command that contains rm -rf.
export const register = (on) => {
on("tool.call", { tool: "Bash", command: /rm -rf/ }, async () => ({
deny: "No rm -rf here.",
}));
};
Entry Point and Registration
| Item | Status | What it does | How it works | Where it can be used |
|---|---|---|---|---|
register(on, options) | Documented: An official docs page names this item. | The entry point. The module exports it. on registers hooks. options holds the user values with defaults filled in. | Runs once when the module loads, and again after a reload. The engine drops its return value. The engine awaits a promise. $ does not exist yet. | The hooks module named in hooks/. |
on(pattern, hook) | Documented: An official docs page names this item. | Registers hook on the events that pattern selects. | pattern is an event name, a glob (*, classic.), or a negation (!tool.). A repeat registration throws. A plugin's registrations nest in order, first outermost. | Inside register only. |
on(pattern, matcher, hook) | Documented: An official docs page names this item. | The same, narrowed by a matcher. A matcher is a partial of the event input. | A leaf matches by === or by Reg. An array means any of. An object is a partial of an object. Example: { tool:. | Inside register only. |
on(... | Documented: An official docs page names this item. | Sets the handler that runs when the hook throws or overruns its budget. | A second . on one registration throws. So does one after register returned. An answer from the handler within the grace time stands as the hook result. | Inside register only, on the returned registration. |
Pattern * and negation | Documented: An official docs page names this item. | * selects every event. !x selects every event except x. | * and a negation do not select a telemetry event for an installed plugin. Hook it by name or with telemetry.. A glob hook types e as the union of the selected events. | Inside register only. |
Arguments of a Hook
A hook is async ($, e, next) => result.
Hooks on turn.step and process.spawn are async generators.
| Item | Status | What it does | How it works | Where it can be used |
|---|---|---|---|---|
$ | Documented: An official docs page names this item. | The Mod API object. Frozen. Holds the core nouns and every noun that the engine. steps added. | Write each call in full, as in $.. | Every hook and every closure a hook drew. |
e | Documented: An official docs page names this item. | The event input. Deeply frozen plain data. | To change it, pass a copy to next, as in next({ .... The engine refuses a change to a field marked pinned in the type. | Every hook. |
next(e) | Documented: An official docs page names this item. | The rest of the chain, as middleware. It runs the hooks beneath, then the engine. It resolves to the event result. | Called with no argument it rejects. A hook that returns without next ends the chain. Returning nothing is a failure. On a streaming event (turn., process.) it returns a Hook. | Every hook. |
next. | Documented: An official docs page names this item. | Continues the dispatch at a later tier (user, append, builtin, or core). The links between are skipped. | Managed hooks only. prepend may name user, append, builtin, or core. user skips each prepend peer below the hook, never one above it, and an outer next. to a deeper tier still wins (since 2.). append may name core. No user hook skips the organization. | Mods in prepend or append. |
next. | Documented: An official docs page names this item. | An Abort that aborts when the engine abandons the dispatch. | It aborts when the user interrupts, when a hook above settled first, or when this hook ran out of budget. Pass it to $. and other waits. | Every hook. |
next. | Documented: An official docs page names this item. | { plugin, tier } of who raised the dispatch. The engine is { plugin:. | The host sets it. A plugin cannot write it. Every hook of one dispatch sees the same origin. | Every hook. |
next. | Documented: An official docs page names this item. | A type predicate. It says whether pattern selects this dispatch event. | Narrows e for a hook on a glob or on *. Under a matcher the narrowing includes it. | Glob and * hooks. |
next. | Documented: An official docs page names this item. | The name of the dispatch event, as a value. | Use it in a glob hook to log or switch on. | Every hook. |
next. | Documented: An official docs page names this item. | A list of Trace: what settled beneath on the latest next() call. | One entry for each link beneath, nearest first. Empty before the hook calls next. Frozen data. | Every hook. |
next. | Documented: An official docs page names this item. | { ms, remaining: the time limit of the hook and what remains. | Read fresh at each access. It stands still while a next or $ call is in flight. In a . handler it is the grace time. At session. it reads the exit bound, which never stops. | Every hook. |
next. | Differs: The official docs and the program disagree. The row says how. The type file types kind as throw or timeout. The docs add re-entry and next.error.cause. | In a . handler only. Why the hook failed: kind, message, budget. | The docs add the kind re- (the event came from a call by that hook or by your mod's code outside its hooks) and next.. | . handlers. |
next. | Documented: An official docs page names this item. | In a . handler only. True when the hook called next. | In a handler next is replay-safe. When called is true, next(e) returns what the last call settled to and runs nothing again. | . handlers. |
Failure Behavior
The engine skips a hook that throws, overruns its budget, or returns a wrong shape.
The hooks beneath and the engine run in its place, or its last next result stands.
So a guard fails open unless its registration carries a .catch that refuses in its place.
Since 2.1.304, when a plugin that runs first skips one of your mod's hooks, the transcript shows a line naming the event and the plugin: its <event> hook was skipped. "<plugin>" runs first and sent the event on past it. (the program's text).
The type file gives this advice at each event where an answer can refuse.
| Event | Status | What happens when the hook fails | The advice |
|---|---|---|---|
tool. | Documented: An official docs page names this item. | The tool runs. | Add .. |
tool. | Documented: An official docs page names this item. | The verdict beneath stands. | Add a . that refuses. |
ui. | Documented: An official docs page names this item. | The engine skips the hook. | None. |
ui. | Documented: An official docs page names this item. | A Client born in that redraw that fails is heard too, and the engine draws nothing again for it. | To go on, ask with $.. |
ui. | Documented: An official docs page names this item. | The focus stays where it was. | next(e) lands the focus on e. and redraws. |
agent. | Documented: An official docs page names this item. | The offer passes through. | None. |
agent. | Documented: An official docs page names this item. | The subagent starts. | Add .. |
prompt. | Documented: An official docs page names this item. | The prompt goes through. | Add .. |
prompt. | Documented: An official docs page names this item. | The engine reads the file. | Add .. |
command. | Documented: An official docs page names this item. | The command runs. | Add .. |
command. | Documented: An official docs page names this item. | The description passes through. | None. |
config. | Documented: An official docs page names this item. | The engine writes the row. | Add .. |
config. | Documented: An official docs page names this item. | The row passes through. | None. |
skill. | Documented: An official docs page names this item. | The text passes through. | None. |
attribution. | Documented: An official docs page names this item. | The text passes through. | None. |
plugin. | Documented: An official docs page names this item. | The module joins. | Add .. |
turn. | Documented: An official docs page names this item. | The answer stays as it was. | None. |
engine. | Documented: An official docs page names this item. | It fails, and its plugin unloads, when it replaces another plugin's noun, in user also past its own stub, or adds a seated name. | None. |
Budgets
| Item | Status | What it does | How it works | Where it can be used |
|---|---|---|---|---|
| Hook time per event | Documented: An official docs page names this item. | 10 seconds of the hook's own code. | The clock stops during next and $ calls, but a $. counts. Hook is 10000. A streaming hook counts its own code only, never a yield or a read beneath. | Every hook. |
prompt. hook time | Documented: An official docs page names this item. | 50 milliseconds. | Hook has one ms value. The program sets the 50 millisecond value on the event (budget). It replaces the 10 second default. If a hook fails, the editor's own edit stands. | prompt. hooks. |
. grace | Documented: An official docs page names this item. | 1 second. | Hook is 1000. The handler gets a fresh budget. | . handlers. |
| Linger after abort | Documented: An official docs page names this item. | 5 seconds. | Hook is 5000. Past it the engine reports the hook as lingering. The dispatch continued without it. | Every hook. |
session. hooks together | Documented: An official docs page names this item. | The Session hook budget, 1.5 seconds by default. | The whole chain shares one short wall-clock bound that next. reads. | session. hooks. |