Claude Code Hooks Reference
Everything you need on one page when writing a hook: the full event list with the values each
matcher accepts, the fields every hook gets on stdin, the settings.json and
statusLine shapes, and what exit codes mean. The last section documents the per-session
state file that AgentManager derives from these events, for anyone who wants to read it
from their own scripts. To generate a config instead of writing one, use the
hooks builder.
- Hook events
- What every hook receives on stdin
- settings.json schema
- statusLine schema and its stdin
- Exit codes
- AgentManager session state file
Hook events
Claude Code currently defines 33 hook events. The third column lists what the matcher field
filters on for that event, with example values. Events marked “—” ignore the matcher and always fire.
A matcher made of plain names separated by | is an exact match; anything containing regex
characters is treated as a JavaScript regular expression. An empty matcher matches everything.
| Event | Fires when | Matcher filters on |
|---|---|---|
| Session lifecycle | ||
SessionStart | A session starts or resumes | how it started startup|resume|clear|compact|fork |
Setup | Runs for --init / --maintenance setup | setup trigger init|maintenance |
UserPromptSubmit | You submit a prompt, before Claude reads it | — |
UserPromptExpansion | A slash command expands into a prompt | — |
Stop | The main agent finishes its response | — |
StopFailure | The turn ends because of an API error | error type rate_limit|overloaded|authentication_failed|billing_error|max_output_tokens|unknown |
Notification | Claude Code needs your attention (permission prompt, idle…) | notification type permission_prompt|idle_prompt|elicitation_dialog|agent_needs_input |
SessionEnd | The session terminates | why it ended clear|resume|logout|prompt_input_exit|other |
| Tools and permissions | ||
PreToolUse | Before a tool call runs (can block it) | tool name Bash|Edit|Write|mcp__.* |
PermissionRequest | A tool call needs a permission decision | tool name Bash|Edit|Write |
PermissionDenied | Auto mode denied a tool call | tool name Bash|Edit|Write |
PostToolUse | After a tool call succeeds | tool name Bash|Edit|Write|mcp__.* |
PostToolUseFailure | After a tool call fails | tool name Bash|Edit|Write |
PostToolBatch | A batch of parallel tool calls has all resolved | — |
MessageDisplay | An assistant message is displayed | — |
| Subagents and tasks | ||
SubagentStart | A subagent is spawned | agent type general-purpose|Explore|Plan |
SubagentStop | A subagent finishes | agent type general-purpose|Explore|Plan |
TaskCreated | A task is created | — |
TaskCompleted | A task is marked completed | — |
TeammateIdle | An agent-team teammate is about to go idle | — |
| Context, config, model | ||
PreCompact | Before the context is compacted | trigger manual|auto |
PostCompact | After compaction completes | trigger manual|auto |
InstructionsLoaded | A CLAUDE.md or rules file is loaded | load reason session_start|nested_traversal|path_glob_match|include|compact |
ConfigChange | A settings file changes during the session | config source user_settings|project_settings|local_settings|policy_settings|skills |
PreModelSwitch | Before a model switch (can block it) | target model .*opus.* |
PostModelSwitch | After the session model changes | target model .*opus.* |
Elicitation | An MCP server asks you for input | MCP server name my-server |
ElicitationResult | You answered an MCP elicitation | MCP server name my-server |
| Workspace | ||
CwdChanged | The working directory changes | — |
DirectoryAdded | A directory is added with /add-dir | how it was added slash_command|register_repo_root |
FileChanged | A watched file changes on disk | file names (literal, | separated) .envrc|.env |
WorktreeCreate | A git worktree is being created | — |
WorktreeRemove | A git worktree is being removed | — |
claude --version before adding recent events such as StopFailure
or PostCompact.
What every hook receives on stdin
Each hook command gets one JSON object on stdin. These fields are present for every event; each event
adds its own on top (for example tool_name and tool_input on the tool events,
notification_type on Notification, error_type on
StopFailure, reason on SessionEnd).
| Field | Meaning |
|---|---|
session_id | Stable ID for the Claude Code session. Use it as the key for anything you persist. |
hook_event_name | The event name from the table above. Branch on this when one script handles several events. |
cwd | Working directory of the session at the time the hook fired. |
transcript_path | Path to the session's JSONL transcript. |
prompt_id | ID of the user prompt the current turn belongs to. |
scratchpad_dir | Session-specific temp directory. |
permission_mode | Current permission mode (default, plan, acceptEdits, bypassPermissions…). |
effort | Current reasoning effort setting. |
agent_id, agent_type | Set when the hook fires inside a subagent. Absent for the main agent. |
settings.json schema
Hooks live under the top-level hooks key in any of ~/.claude/settings.json (user),
.claude/settings.json (project, committed) or .claude/settings.local.json (project,
ignored by git). Files merge; a hook defined in several files runs from each.
{
"hooks": {
"<EventName>": [
{
"matcher": "<optional filter>",
"hooks": [
{ "type": "command", "command": "<shell command>", "timeout": 600 }
]
}
]
}
}
| Key | Meaning |
|---|---|
matcher | Optional. Filters by the value in the events table. Omit it, or leave it empty, to match everything. |
hooks[].type | command runs a shell command. Other handler types exist (http, mcp_tool, prompt, agent); this page and the builder cover command. |
hooks[].command | Shell command, run with your user's environment. $HOME and other variables expand. |
hooks[].timeout | Seconds before the hook is killed. Default 600; 30 for UserPromptSubmit, PreModelSwitch and PostModelSwitch; 10 for MessageDisplay. All SessionEnd hooks share a 1.5-second budget. |
statusLine schema and its stdin
The status line is a separate top-level key with exactly one slot. Its command runs after every assistant response and receives data that hooks never see: context-window usage and rate-limit state.
{
"statusLine": {
"type": "command",
"command": "<shell command>",
"padding": 0,
"refreshInterval": 5,
"hideVimModeIndicator": false
}
}
| stdin field | Meaning |
|---|---|
session_id, version | Session ID and Claude Code version. |
model.id, model.display_name | The model currently in use. |
workspace.* | Current and project directories. |
cost.* | Cumulative cost and duration counters for the session. |
context_window.used_percentage | Share of the context window in use. Also remaining_percentage, total_input_tokens, context_window_size, current_usage. |
rate_limits.five_hour, rate_limits.seven_day | Each has used_percentage and resets_at (epoch seconds). |
Exit codes
| Exit code | Effect |
|---|---|
0 | Success. stdout is shown in verbose mode, or parsed as JSON on events that accept structured output. |
2 | Blocks the action on events that support blocking (PreToolUse, UserPromptSubmit, PermissionRequest, PreModelSwitch, Stop, SubagentStop…). stderr is fed back to Claude as the reason. |
| other | Non-blocking error. stderr is shown to you; the action continues. |
AgentManager session state file
AgentManager's hook reduces the events above to one JSON file per session at
~/.claude/agent-manager/sessions/<session_id>.json. The file is rewritten in place on
every event, so treat a parse failure as “read again”, not as corruption. The derivation rules are described in
Tracking Claude Code session status with hooks.
| Field | Type | Meaning |
|---|---|---|
session_id | string | Claude Code session ID. Same as the file name. |
state | string | One of waiting, done, processing, idle, error. |
waiting_kind | string | null | Why the session is waiting: approval (permission prompt), plan (plan approval), choice (a question to answer). Null unless state is waiting. |
error_reason | string | null | The StopFailure error type when state is error: rate_limit, overloaded, authentication_failed, billing_error, max_output_tokens, unknown. |
cwd | string | Working directory reported by the latest event. |
label | string | Display name shown in the app: the last path component of cwd. |
host_bundle_id, host_bundle_chain | string | null, array | Bundle ID of the terminal app hosting the session, and the process ancestry used to find it. |
iterm_session_id, tmux_pane_id | string | null | Identifiers used to focus the exact pane when you click the session. |
owner_pid, owner_started_at | int, string | PID and start time of the claude process. Both are compared to detect a dead session whose PID was reused. |
created_at, updated_at, state_since | ISO 8601 string | When the file was first written, last written, and when state last changed. |
active_subagent_ids, subagent_seen_ids | array of string | Subagents currently running, and every subagent seen this session. |
main_stopped, main_waiting | bool | Internal flags: the main agent has emitted Stop; the main agent is blocked on a prompt. |
agent_input_pending_ids | array of string | Subagents that raised agent_needs_input and have not been answered. |
A second file, ~/.claude/agent-manager/stats/<session_id>.json, is written by the
statusLine pass-through. It carries the context_window, rate_limits
and model objects from the status-line stdin unchanged, plus a marker when
PreCompact has fired. The status-line command you had before installing is preserved in
~/.claude/agent-manager/statusline-original.json and still runs.
Skip the script, keep the state
AgentManager installs the hook and the status-line pass-through for you, registers the events it needs, and shows every Claude Code session on a live board. Free to use, no account.