Skip to main content

Hook Events

Claude Code has 33 hook events. Each event fires at one point in a session, and each one gets its own input fields and accepts its own output fields. This page lists them in groups: tool, tool result, session, prompt and turn, agent and task, and other events. The fields that every event shares are on Input and Output.

Add or remove the event key in the hooks map to turn an event on or off.

Example​

A SessionStart hook that adds your open tickets as context.

.claude/settings.json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [{ "type": "command", "command": ".claude/hooks/tickets.sh" }]
}
]
}
}
.claude/hooks/tickets.sh
#!/bin/bash
# Plain stdout on SessionStart becomes context for Claude.
echo "Open tickets: $(gh issue list --limit 5 --json title -q '.[].title' | paste -sd ';' -)"

All Events​

EventGroupFires whenMatcherCan block
PreToolUseToolBefore a tool runs.Tool name✅
PermissionRequestToolA permission prompt would show.Tool name❌ (use a decision)
PermissionDeniedToolAuto mode denies a call.Tool name❌
PostToolUseTool resultA tool call succeeds.Tool name❌
PostToolUseFailureTool resultA tool call fails.Tool name❌
PostToolBatchTool resultAll tool calls of a batch are done.None✅
SessionStartSessionA session starts or resumes.Start source❌
SetupSession--init-only, or --init or --maintenance with -p.CLI flag❌
SessionEndSessionA session ends.End reason❌
InstructionsLoadedSessionA CLAUDE.md or rules file loads.Load reason❌
ConfigChangeSessionA settings or skill file changes.Config source✅
CwdChangedSessionA shell command changes the directory.None❌
FileChangedSessionA watched file changes on disk.File names❌
DirectoryAddedSessionA directory is added with /add-dir.Add source❌
UserPromptSubmitPromptYou submit a prompt.None✅
UserPromptExpansionPromptA typed command expands to a prompt.Command name✅
MessageDisplayPromptAssistant text streams to the screen.None❌
NotificationPromptClaude Code sends a notification.Notification type❌
StopPromptThe main agent finishes.None✅ (keeps working)
StopFailurePromptThe turn ends on an API error.Error type❌
SubagentStartAgentA subagent starts or resumes.Agent type❌
SubagentStopAgentA subagent finishes.Agent type✅ (keeps working)
TaskCreatedAgentTaskCreate makes a task.None✅
TaskCompletedAgentA task is marked done.None✅
TeammateIdleAgentA teammate is about to go idle.None✅ (keeps working)
PreCompactOtherBefore a compaction.Trigger✅
PostCompactOtherAfter a compaction.Trigger❌
PreModelSwitchOtherBefore a requested model switch.Target model✅
PostModelSwitchOtherAfter the model changes.Target model❌
ElicitationOtherAn MCP server asks you for input.Server name✅ (declines)
ElicitationResultOtherYou answered an MCP question.Server name✅
WorktreeCreateOtherA worktree is needed.None✅ (fails)
WorktreeRemoveOtherA hook-made worktree is cleaned up.None✅ (fails)

Tool Events​

These events fire before a tool runs, and when a permission is asked for or denied. They are the main place to approve, change, or block a tool call. MCP tools are named mcp__<server>__<tool>.

PreToolUse​

Fires after Claude builds the tool input and before the tool runs. It does not fire for @ file mentions. Exit 2 blocks the call, and stderr is the reason. Handlers: all five types.

FieldStatusDirectionWhat it does
tool_nameDocumentedInputThe tool name. Any tool except EndConversation.
tool_inputDocumentedInputThe tool arguments. The shape depends on the tool (see below). File paths are absolute.
tool_use_idDocumentedInputThe tool call ID.
permissionDecisionDocumentedOutput, in hookSpecificOutputallow, deny, ask, or defer. allow skips the prompt but not deny or ask rules. Order of strength: deny, then defer, then ask, then allow. defer works only in -p with one tool call.
permissionDecisionReasonDocumentedOutput, in hookSpecificOutputThe reason text. For ask it shows in the prompt. For deny it goes to Claude. Otherwise it goes to the debug log.
updatedInputDocumentedOutput, in hookSpecificOutputReplaces the whole tool input. Rules and background checks use the new input. Needed with allow for AskUserQuestion and ExitPlanMode. Ignored with defer.
additionalContextDocumentedOutput, in hookSpecificOutputText placed next to the tool result. Ignored with defer.
permissionDisplayUndocumentedOutput, in hookSpecificOutputWords for a permission card. For the desktop host only. An object with title (200 characters), description (2000), and displayName (64). Ignored, with a debug line, if the text cannot be shown.
Top-level decision and reasonDiffersOutputapprove maps to allow, and block maps to deny. Use permissionDecision instead.
ask labelDocumentedBehaviorThe prompt shows [settings], [plugin:<name>], or [skill]. An ask also forces a prompt in auto mode.

Tool Input Shapes​

ToolStatusKeys in tool_input
BashDocumentedcommand, description, timeout (milliseconds), run_in_background
PowerShellDocumentedThe same keys as Bash. Match with Bash|PowerShell.
WriteDocumentedfile_path, content
EditDocumentedfile_path, old_string, new_string, replace_all
ReadDocumentedfile_path, offset, limit
GlobDocumentedpattern, path
GrepDocumentedpattern, path, glob, output_mode, -i, multiline
WebFetchDocumentedurl, prompt, offset (2.1.290)
WebSearchDocumentedquery, allowed_domains, blocked_domains
AgentDocumentedprompt, description, subagent_type, model
AskUserQuestionDocumentedquestions, and answers (set only through updatedInput)
ExitPlanModeDocumentedClaude Code adds plan and planFilePath. The old allowedPrompts key is accepted and ignored.

The docs name Workflow as a tool you can match, but give no input shape for it.

PermissionRequest​

Fires when a permission prompt would show, or when a call that cannot prompt would be denied. Exit 2 is not honored here. Deny with the decision object instead. There is no event for a sandbox network request. Handlers: command, http, mcp_tool, and prompt. An agent hook is skipped.

FieldStatusDirectionWhat it does
tool_name, tool_inputDocumentedInputThe same as in PreToolUse. There is no tool_use_id.
permission_suggestionsDocumentedInputUpdates that Claude Code suggests. The same shape as updatedPermissions. A dialog may show other options.
decision.behaviorDocumentedOutput, in hookSpecificOutputallow or deny. Deny and ask rules still apply.
decision.updatedInputDocumentedOutputNew tool input. allow only. Checked again against deny and ask rules.
decision.updatedPermissionsDocumentedOutputPermission updates to apply. allow only.
decision.messageDocumentedOutputWhy it was denied. deny only. Goes to Claude.
decision.interruptDocumentedOutputStops Claude. deny only.

Permission Update Entries​

These entries are used in updatedPermissions and permission_suggestions.

EntryStatusWhat it doesFields
addRules, replaceRules, removeRulesDocumentedChange permission rules.rules (each {toolName, ruleContent?}), behavior (allow, deny, ask), destination
setModeDocumentedChange the permission mode.mode: default, auto, acceptEdits, dontAsk, bypassPermissions, plan. bypassPermissions works only if bypass was on at launch.
addDirectories, removeDirectoriesDocumentedChange working directories.directories
destinationDiffersWhere Claude Code keeps the change.session, localSettings, projectSettings, userSettings

PermissionDenied​

Fires only when auto mode denies a call, including a denial with no classifier verdict. It does not fire for manual denials, PreToolUse blocks, or deny rules. The exit code and stderr are ignored. All five handler types are allowed, but output from prompt and agent is dropped.

FieldStatusDirectionWhat it does
tool_name, tool_input, tool_use_id, mcp_serverDocumentedInputAs in PreToolUse.
reasonDocumentedInputWhy auto mode denied. A rule name in brackets, such as [Data Exfiltration].
retryDocumentedOutput, in hookSpecificOutputtrue tells the model it may retry. The denial stands. Ignored for denials with no verdict.

Tool Result Events​

These events fire after a tool runs. PostToolUse and PostToolUseFailure match on tool name. PostToolBatch has no matcher. All three accept the five handler types.

PostToolUse​

Fires after a tool call succeeds. Exit 2 shows stderr to Claude but does not undo the tool. It does not see writes made by Bash or by outside processes. Use FileChanged for those.

FieldStatusDirectionWhat it does
tool_name, tool_input, tool_use_id, mcp_serverDocumentedInputAs in PreToolUse.
tool_responseDocumentedInputThe structured tool output. The shape depends on the tool.
duration_msDocumentedInputRun time in milliseconds. Leaves out permission prompts and PreToolUse hooks. Optional.
decision, reasonDocumentedOutputblock adds the reason next to the result. Claude still sees the original output.
additionalContextDocumentedOutput, in hookSpecificOutputText next to the result. Not delivered for async hooks.
updatedToolOutputDocumentedOutput, in hookSpecificOutputReplaces what Claude sees. Must match the tool output shape. For built-in tools a bad shape is ignored. The tool already ran.
updatedMCPToolOutputDocumentedOutput, in hookSpecificOutputThe same, for MCP tools only. Prefer updatedToolOutput.
classifierContextDocumentedOutput, in hookSpecificOutputA note for the auto mode classifier. Needs 2.1.236. At most 2,000 characters for one call. Dropped for async hooks and read-only calls. Treated as unverified context.
tool_response.bashEditDiffDocumentedInputFiles that a Bash run changed. Needs 2.1.269, a public beta, and the setting bashEditDiffEnabled. Keys: changedFiles (200), files (5), moreFiles, unavailable, skipped, shared.
tool_response of AgentDocumentedInputstatus (completed or async_launched), agentId, content, resolvedModel, modelsUsed, totalTokens, totalDurationMs, totalToolUseCount, usage, and outputFile for a background launch.
tool_response of ExitPlanModeDocumentedInputplan and filePath.
SubagentHandback toolDocumentedMatcherThe tool that carries a subagent report in auto mode. Match it to read tool_input.message. Needs 2.1.271.

PostToolUseFailure​

Fires when a started tool fails, or an MCP tool returns an error. It does not fire for unknown tools, schema errors, or permission denials. Exit 2 shows stderr to Claude.

FieldStatusDirectionWhat it does
tool_name, tool_input, tool_use_id, mcp_serverDocumentedInputAs in PostToolUse.
errorDocumentedInputWhat went wrong. The same text as the tool result. Bash and PowerShell errors start with Exit code N.
is_interruptDocumentedInputtrue for an abort. Canceling a running tool does not fire this hook.
duration_msDocumentedInputRun time in milliseconds. Optional.
additionalContextDocumentedOutput, in hookSpecificOutputText next to the error. A top-level decision: "block" also works.

PostToolBatch​

Fires once after all tool calls of a batch resolve, before the next model request. Exit 2, decision: "block", or continue: false stops the loop.

FieldStatusDirectionWhat it does
tool_callsDocumentedInputOne entry per call, with tool_name, tool_input, tool_use_id, and tool_response.
tool_calls[].tool_responseDocumentedInputWhat the model gets in the tool_result. A serialized string or content blocks. It is not the PostToolUse object.
additionalContextDocumentedOutput, in hookSpecificOutputContext for the next request. Sent once.

Session Events​

These events fire when a session starts or ends, and when its settings, files, or folders change.

SessionStart​

Fires when a session starts or resumes. Interactive starts run it in the background, but the first reply waits for it. Exit 2 shows stderr to you only. Handlers: command and mcp_tool (skipped at launch).

FieldStatusDirectionWhat it does
sourceDocumentedInputHow the session began: startup, resume, clear, compact, fork. This is the matcher.
modelDocumentedInputThe active model ID. Can be missing, for example after /clear.
agent_typeDocumentedInputThe agent name from --agent. Optional.
session_titleDocumentedInputThe custom title. Present only when one exists.
seconds_since_last_response, context_tokens, prompt_cache_likely_expired, estimated_cache_write_usdDocumentedInputThe cost of a stale resume. Only for resume and fork with an earlier reply. Needs 2.1.251.
additionalContextDocumentedOutput, in hookSpecificOutputText before the first prompt. Plain stdout works too.
initialUserMessageDocumentedOutputThe first user message. Only in -p. From a plugin it works only if the plugin was installed before the start.
sessionTitleDocumentedOutputSets the title. Works for startup, resume, fork. Same as /rename.
watchPathsDocumentedOutputAbsolute paths for FileChanged to watch.
reloadSkillsDocumentedOutputScans the skill and command folders again. Use it after a hook installs skills.

Setup​

Fires for --init-only, or for --init or --maintenance with -p. It does not fire on a normal start. --init-only also runs SessionStart with startup. Handler: command only.

FieldStatusDirectionWhat it does
Setup eventGatedEventSee the flags above.
triggerDocumentedInputinit or maintenance. It is also the matcher.
additionalContextDiffersOutputTreat it as not delivered. Not verified in 2.1.304.
CLAUDE_ENV_FILEDocumentedEnvironmentPersists environment variables, as for SessionStart.

SessionEnd​

Fires when a session ends. The budget is 1.5 seconds for all hooks together. Exit 2 shows stderr to you. JSON is dropped. Handlers: command, http, mcp_tool.

FieldStatusDirectionWhat it does
reasonDocumentedInputWhy it ended: clear, resume, logout, prompt_input_exit, other. This is the matcher.

InstructionsLoaded​

Fires when CLAUDE.md or a .claude/rules/*.md file loads. It runs async and has no decision. It does not fire for an AGENTS.md file read directly. Handlers: command, http, mcp_tool.

FieldStatusDirectionWhat it does
file_pathDocumentedInputThe absolute path of the file.
memory_typeDocumentedInputUser, Project, Local, or Managed.
load_reasonDocumentedInputsession_start, nested_traversal, path_glob_match, include, compact. This is the matcher.
globs, trigger_file_path, parent_file_pathDocumentedInputExtra data for lazy and include loads. globs is only for path_glob_match.

ConfigChange​

Fires when a settings, managed policy, or skill file changes. It does not fire for server-managed or MDM changes. Exit 2 or decision: "block" stops the change, except for policy_settings. Handlers: command, http, mcp_tool.

FieldStatusDirectionWhat it does
sourceDocumentedInputuser_settings, project_settings, local_settings, policy_settings, skills. This is the matcher.
file_pathDocumentedInputThe changed file. Optional.
decision, reasonDocumentedOutputblock keeps the old settings. Nobody sees reason. systemMessage and continue are dropped.

CwdChanged​

Fires when a shell command in the main conversation changes the directory. It has no matcher and cannot block. CLAUDE_ENV_FILE is cleared at the next event. Handlers: command, http, mcp_tool.

FieldStatusDirectionWhat it does
old_cwd, new_cwdDocumentedInputThe two directories.
watchPathsDocumentedOutput, in hookSpecificOutputReplaces the dynamic watch list. An empty array clears it. systemMessage shows as a short notice. continue is dropped.

FileChanged​

Fires when a watched file changes on disk. A file watcher drives it, not tool calls. The matcher names the files to watch, and Claude Code filters by base name. It cannot block. Handlers: command, http, mcp_tool.

FieldStatusDirectionWhat it does
file_pathDocumentedInputThe absolute path.
eventDocumentedInputchange, add, or unlink.
watchPathsDocumentedOutput, in hookSpecificOutputReplaces the dynamic watch list. The watcher starts only when something names a file.

DirectoryAdded​

Fires after /add-dir, or after the SDK register_repo_root request. It runs in the background with a 600 second timeout. It does not fire for --add-dir, /permissions, or a directory Claude Code already knows. Handlers: command, http, mcp_tool.

FieldStatusDirectionWhat it does
directoryDocumentedInputThe absolute path.
sourceDocumentedInputslash_command or register_repo_root. This is the matcher. systemMessage goes to Claude for slash_command and to the debug log otherwise.

Prompt and Turn Events​

These events fire around a turn: when you send a prompt, while text shows, when Claude Code sends a notification, and when Claude stops.

UserPromptSubmit​

Fires when you submit a prompt, before Claude reads it. It has no matcher. It also fires for scheduled tasks, background subagent reports, and messages from other sessions. The default timeout is 30 seconds. Exit 2 blocks the prompt. Plain stdout is added as context. Handlers: all five types.

FieldStatusDirectionWhat it does
promptDocumentedInputThe text. A typed /skill arrives as typed, not expanded. Pasted text arrives expanded.
sourceUndocumentedInputWho made the prompt: user, sdk, system, loop_wakeup, schedule_wakeup, poll_event. May be missing.
session_titleDocumentedInputThe custom title, only when one is set.
decision, reasonDocumentedOutputblock stops the prompt. You see reason. It is not added to context.
additionalContextDocumentedOutput, in hookSpecificOutputContext placed next to the prompt.
sessionTitleDocumentedOutputSets the session title.
suppressOriginalPromptDocumentedOutputLeaves the prompt text out of the block message. Otherwise the block message ends with Original prompt: and is written to the transcript.

UserPromptExpansion​

Fires when a typed command expands into a prompt. The matcher is command_name. It covers typing /skill, which skips PreToolUse. Exit 2 blocks. Plain stdout is context. Handlers: all five types.

FieldStatusDirectionWhat it does
expansion_typeDocumentedInputslash_command or mcp_prompt.
command_name, command_args, promptDocumentedInputThe command parts and the raw line.
command_sourceDocumentedInputWhere the command came from, such as plugin. Optional.
decision, reasonDocumentedOutputblock stops the expansion.
additionalContext, suppressOriginalPromptDocumentedOutput, in hookSpecificOutputAs in UserPromptSubmit.

MessageDisplay​

Fires while assistant text streams, once for each batch of finished lines. It has no matcher. The timeout is 10 seconds. It only changes what you see. The transcript and Claude keep the original. In -p mode it runs once with the full text. Handlers: command, http, mcp_tool.

FieldStatusDirectionWhat it does
turn_id, message_idDocumentedInputUUIDs of the turn and the message. message_id is stable for the whole message.
index, finalDocumentedInputThe position of the batch. final is true on the last one.
deltaDocumentedInputThe new lines. Whole lines, except in the final batch.
displayContentDocumentedOutput, in hookSpecificOutputText shown in place of delta. If the hook fails, the original shows. systemMessage and continue are dropped.

Notification​

Fires when Claude Code sends a notification. The matcher is the notification type. It runs even if desktop notifications are off. The exit code and stderr are ignored, but terminalSequence still works. Handlers: command, http, mcp_tool.

FieldStatusDirectionWhat it does
message, title, notification_typeDocumentedInputThe text, an optional title, and the type.
permission_promptDocumentedTypeA prompt waited about 6 seconds.
idle_promptDocumentedTypeThe session was idle for about 60 seconds. Both depend on typing and running agents.
auth_success, elicitation_dialog, elicitation_url_dialog, elicitation_complete, elicitation_responseDocumentedTypeAuth finished, and the steps of an MCP question. The two dialog types wait 6 seconds.
agent_needs_input, agent_completedDocumentedTypeBackground session events in agent view. agent_needs_input for a teammate setup question needs 2.1.248.
quota_auto_resume_fired, quota_auto_resume_stale, quota_auto_resume_disabledDocumentedTypeUsage limit wait events. Need 2.1.234.
auth_storage_failure, computer_use_enter, computer_use_exit, plugin_notification, push_notification, worker_permission_promptUndocumentedTypeOther values that the program passes as notificationType. plugin_notification comes from the mod call $.ui.notify. Whether each one reaches a hook is not verified.

Stop​

Fires when the main agent finishes. It does not fire on a user interrupt. It has no matcher. Exit 2 or decision: "block" keeps Claude working. The cap is 8 continuations in a row, set by CLAUDE_CODE_STOP_HOOK_BLOCK_CAP. Handlers: all five types.

FieldStatusDirectionWhat it does
stop_hook_activeDocumentedInputtrue when a stop hook already continued the turn. Check it to avoid loops.
last_assistant_messageDocumentedInputThe text of the final reply. Use it instead of the transcript.
background_tasksDocumentedInputTasks still running. Each has id, type, status, description, and some of command, agent_type, server, tool, name by type. Text is cut at 1000 characters.
session_cronsDocumentedInputScheduled wakeups. Each has id, schedule, recurring, prompt.
decision, reasonDocumentedOutputblock stops Claude from stopping. reason is required. It becomes the next instruction.
additionalContextDocumentedOutput, in hookSpecificOutputFeedback that keeps the turn going. Labeled Stop hook feedback, not an error.

StopFailure​

Fires instead of Stop when the turn ends on an API error. The matcher is error. Output and the exit code are ignored, but terminalSequence works. Handlers: command, http, mcp_tool.

FieldStatusDirectionWhat it does
errorDiffersInputThe error type. See the list on Configuration.
error_detailsDocumentedInputMore detail. Optional.
last_assistant_messageDocumentedInputThe rendered API error text. It is not Claude's own words, unlike in Stop.

Agent and Task Events​

These events tell your hooks when a subagent, a task, or a teammate starts, stops, or goes idle. Teammates belong to agent teams. See Subagent Runtime.

SubagentStart​

Fires when a subagent starts or resumes, and for each message of an in-process teammate. The matcher is the agent type. It cannot block. Exit 2 shows stderr in the subagent transcript only. Handlers: command, http, mcp_tool.

FieldStatusDirectionWhat it does
agent_id, agent_typeDocumentedInputThe subagent ID and type. A plugin agent type is my-plugin:reviewer. Anchor the matcher with ^ and $.
additionalContextDocumentedOutput, in hookSpecificOutputContext for the subagent before its first prompt. On a repeat run it is added only if the earlier copy is gone, to keep the cache.

SubagentStop​

Fires when a subagent finishes. The matcher is the agent type. Exit 2 or decision: "block" keeps it running, and reason is its next instruction. Handlers: all five types.

FieldStatusDirectionWhat it does
stop_hook_active, last_assistant_message, background_tasks, session_cronsDocumentedInputAs in Stop. The two arrays are scoped to the parent session.
agent_id, agent_typeDocumentedInputThe subagent ID and type. agent_type can be empty for internal agents, such as prompt suggestions and /btw. A named matcher does not match an empty type.
agent_transcript_pathDocumentedInputThe subagent transcript, in a nested subagents/ folder. transcript_path is the main one.
additionalContextDocumentedOutput, in hookSpecificOutputNon-error feedback that keeps the subagent running. Same loop guards as Stop.

TaskCreated​

Fires when TaskCreate makes a task. It has no matcher. It does not fire in a session without the Task tools. Exit 2 or decision: "block" deletes the task and gives the message to Claude. continue: false is ignored. Handlers: all five types.

FieldStatusDirectionWhat it does
task_id, task_subjectDocumentedInputThe task ID and title.
task_description, teammate_nameDocumentedInputOptional detail and the creator. May be absent.
team_nameDocumentedInputThe session team name. Deprecated, to be removed.
agent_idDocumentedInputThe creating subagent or teammate. Needs 2.1.290. May be absent.

TaskCompleted​

Fires when a task is marked done, or when a teammate ends its turn with open tasks. It has no matcher. Exit 2 keeps the task open, and stderr goes to the model. continue: false stops a teammate, and is ignored when TaskUpdate caused the event. Handlers: all five types.

FieldStatusDirectionWhat it does
task_id, task_subject, task_description, teammate_nameDocumentedInputAs in TaskCreated.
team_name, agent_idDocumentedInputThe deprecated team name, and the acting agent. agent_id needs 2.1.290.

TeammateIdle​

Fires when an agent team teammate is about to go idle. It has no matcher. Exit 2 gives stderr to the teammate, and it keeps working. continue: false stops it. Handlers: all five types.

FieldStatusDirectionWhat it does
teammate_name, team_nameDocumentedInputThe teammate and the deprecated team name.
agent_idDocumentedInputThe in-process teammate. Needs 2.1.290. May be absent.

Other Events​

These events cover compaction, model switches, MCP questions, and worktrees. None of them accepts prompt or agent handlers.

PreCompact and PostCompact​

PreCompact fires before a compaction. PostCompact fires after it. The matcher is trigger. For PreCompact, exit 2 or decision: "block" blocks it. Blocking a proactive auto compaction skips it. Blocking a recovery compaction shows the API error. PostCompact has no decision. Exit 2 shows stderr to you. Handlers: command, http, mcp_tool. For the compaction process, see Context and Compaction.

FieldStatusEventWhat it does
triggerDocumentedBoth, inputmanual or auto. auto includes idle compaction.
custom_instructionsDocumentedPreCompact, inputThe text after /compact. null for auto or when empty.
compact_summaryDocumentedPostCompact, inputThe summary that Claude Code made.

PreModelSwitch and PostModelSwitch​

Both need 2.1.251. The matcher is the canonical name of to_model. Handlers: command, http, mcp_tool. For how a model is chosen, see Models.

PreModelSwitch fires before a requested switch: /model, the picker, /config, fast mode, or set_model. It does not fire for automatic fallback or resume. The timeout is 30 seconds, and a timeout blocks. Exit 2 or decision: "block" cancels the switch.

PostModelSwitch fires after the model changes, also on fallback, opusplan, and resume. It cannot block. It does not fire for a one-turn fallback chain. Plain stdout is context. Exit 2 shows stderr as a notice.

FieldStatusDirectionWhat it does
from_model, to_model, requested_modelDocumentedInputThe model IDs and the name you asked for. requested_model is null for the default.
sourceDocumentedInputcommand, picker, or sdk. PostModelSwitch adds auto and resume.
context_tokens, prompt_cache_warm, cache_ttlDocumentedInputThe cost of sending the context again. cache_ttl is 5m or 1h.
estimated_cache_write_usd, pricingDocumentedInputThe estimate and its basis. pricing is configured, catalog, or default.
permissionDecision, permissionDecisionReasonDocumentedOutput, PreModelSwitchallow, deny, ask, and the reason. ask works only in interactive /model, else it is a refusal. No defer, updatedInput, or additionalContext. Strength order: deny, ask, allow.
additionalContextDocumentedOutput, PostModelSwitchText for the next request. If the hook is late by 5 seconds, it goes to the request after.

Elicitation and ElicitationResult​

An elicitation is a question that an MCP server asks you. The matcher for both events is the server name. Handlers: command, http, mcp_tool.

Elicitation fires when the server asks. A hook can answer, so no dialog shows. Exit 2 declines. reason, systemMessage, and continue are dropped. ElicitationResult fires after you answer, before the answer goes to the server. It also runs on decline and cancel.

FieldStatusDirectionWhat it does
mcp_server_name, messageDocumentedElicitation, inputThe server and the prompt text.
mode, url, elicitation_id, requested_schemaDocumentedElicitation, inputform or url mode, the URL, the ID, and the form schema. Optional.
action, contentDocumentedElicitation, outputaccept, decline, cancel, and the form values. Read only on exit 0. A hook answer skips ElicitationResult hooks. A decline beats an accept.
decision: "block"DocumentedElicitation, outputDeclines. Works from 2.1.284. Beats action.
action, content, mode, elicitation_idDocumentedElicitationResult, inputYour answer. action is accept, decline, or cancel.
action, contentDocumentedElicitationResult, outputChange or block the answer. content replaces the whole object. An output with no action is ignored. cancel still sends your values.

WorktreeCreate and WorktreeRemove​

WorktreeCreate replaces the default git worktree step. It fires for --worktree, EnterWorktree, isolation: "worktree", workflows, background sessions, and remote control. It has no matcher. Any non-zero exit fails it. .worktreeinclude is not processed.

WorktreeRemove cleans up a worktree that a WorktreeCreate hook made. It has no matcher. Exit 0 counts as removed. A non-zero exit fails the removal if the folder still exists. Claude Code keeps a subagent worktree. JSON is dropped.

Handlers: command, http, mcp_tool.

FieldStatusDirectionWhat it does
nameDocumentedWorktreeCreate, inputThe slug of the new worktree, such as bold-oak-a3f2.
Path on stdoutDocumentedWorktreeCreate, outputA command hook prints the path as its last non-empty line. ANSI codes are stripped. A relative path resolves against the hook folder. Absolute paths with . or .., and paths through a repo symlink, are refused.
worktreePathDocumentedWorktreeCreate, outputAn HTTP hook returns the path here. A command hook cannot return JSON, so it cannot use terminalSequence.
worktree_pathDocumentedWorktreeRemove, inputThe absolute path to remove.

Source: Hooks Reference, the HookInput types in the program type file, and the hook output schema in the program. Not verified: any input shape for Workflow, Skill, and MCP tools beyond tool_input being unknown; the output schema of each built-in tool for updatedToolOutput (the docs give only Bash: stdout, stderr, interrupted, isImage).