Hooks
Shell commands, prompts, or HTTP calls that fire on lifecycle events — deterministic automation, not left to the model's discretion.
Overview
A hook is something Claudin runs automatically at a defined point in the session — after a file edit, before a tool call, when a session starts — instead of relying on the model to remember to do it. Hooks live under a hooks key in a settings file, keyed by event name.
Config
| Source | Path | Git |
|---|---|---|
| Project | .claudin/settings.json | Commit |
| Project-local | .claudin/settings.local.json | Gitignore |
| User | ~/.claudin/settings.json | — |
| Managed | OS-specific absolute path, e.g. /etc/claude-code/managed-settings.json on Linux | Enterprise |
Precedence when the same event/matcher shows up in more than one source: user < project < local < the --settings flag < managed.
Full settings reference (permissions, model config, output styles, and more) lives on the Configuration page.
{
"hooks": {
"<EventName>": [
{ "matcher": "<pattern>", "hooks": [ { "type": "command", "command": "..." } ] }
]
}
}
/hooks opens a read-only browser of everything currently configured.
Events
27 event names, grouped by what they're for:
| Group | Events |
|---|---|
| Tool calls | PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied |
| Session lifecycle | SessionStart, SessionEnd, Setup, Stop, StopFailure, InstructionsLoaded |
| Subagents & tasks | SubagentStart, SubagentStop, TaskCreated, TaskCompleted, TeammateIdle |
| Context & input | UserPromptSubmit, PreCompact, PostCompact, Elicitation, ElicitationResult |
| Environment | ConfigChange, WorktreeCreate, WorktreeRemove, CwdChanged, FileChanged |
| Misc | Notification |
Matcher syntax
For tool events, matcher matches a tool name — "Write", "Write|Edit", "Bash", empty for every tool. Non-tool events use their own matcher values, e.g. PreCompact/PostCompact use "manual" or "auto". Each individual hook can also carry its own if field, using permission-rule syntax (like "Bash(git *)") to conditionally gate that one hook regardless of the matcher.
Action types
| Type | Fields | Notes |
|---|---|---|
command | command, if, shell, timeout, once, async, asyncRewake | Runs a shell command. asyncRewake runs it in the background and wakes the model if it exits with code 2. |
prompt | prompt (uses $ARGUMENTS), timeout, model | LLM-evaluated condition — works on tool events and lifecycle events like Stop. |
agent | prompt, timeout (default 60s), model (default Haiku) | Agentic verifier hook, runs with tools — works on tool events and lifecycle events like Stop. |
http | url, headers, allowedEnvVars | POSTs the hook input JSON to a URL. |
Input & output
Every hook gets a common base on stdin — session_id, transcript_path, cwd, hook_event_name, plus optional permission_mode/agent_id/agent_type — with extra fields layered on per event. Tool events (PreToolUse, PostToolUse, and friends) also carry tool_name, tool_input, and tool_response:
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/path/to/project",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": { "file_path": "/path/to/file.txt", "content": "..." },
"tool_response": { "success": true }
}
To block or steer, a hook writes JSON to stdout:
{
"decision": "block",
"reason": "Explanation shown to the model",
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "..."
}
}
Example
Run prettier after every write or edit:
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "jq -r '.tool_response.filePath // .tool_input.file_path' | { read -r f; prettier --write \"$f\"; } 2>/dev/null || true"
}]
}]
}
}
Hook chains
A separate, opt-in layer sits on top of ordinary hooks: instead of a matcher firing a command, a rules engine reacts to hook outcomes — a failed tool call, a completed task — with declarative remediation (spawn a fallback agent, notify the team, warm up remote capacity). Configured at .claudin/hook-chains.json, disabled by default. Most setups won't need this; reach for it only once plain hooks stop being enough.