18 18
19<div style={{maxWidth: "500px", margin: "0 auto"}}>19<div style={{maxWidth: "500px", margin: "0 auto"}}>
20 <Frame>20 <Frame>
21 <img src="https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=ce5f1225339bbccdfbb52e99205db912" alt="Hook lifecycle diagram showing the sequence of hooks from SessionStart through the agentic loop to SessionEnd, with WorktreeCreate and WorktreeRemove as standalone setup and teardown events" data-og-width="520" width="520" data-og-height="1020" height="1020" data-path="images/hooks-lifecycle.svg" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?w=280&fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=7c7143c65492c1beb6bc66f5d206ba15 280w, https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?w=560&fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=dafaebf8f789f94edbf6bd66853c69df 560w, https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?w=840&fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=2caa51d2d95596f1f80b92e3f5f534fa 840w, https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?w=1100&fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=614def559f34f9b0c1dec93739d96b64 1100w, https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?w=1650&fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=ca45b85fdd8b2da81c69d12c453230cb 1650w, https://mintcdn.com/claude-code/rsuu-ovdPNos9Dnn/images/hooks-lifecycle.svg?w=2500&fit=max&auto=format&n=rsuu-ovdPNos9Dnn&q=85&s=7fd92d6b9713493f59962c9f295c9d2f 2500w" />21 <img src="https://mintcdn.com/claude-code/lBsitdsGyD9caWJQ/images/hooks-lifecycle.svg?fit=max&auto=format&n=lBsitdsGyD9caWJQ&q=85&s=be3486ef2cf2563eb213b6cbbce93982" alt="Hook lifecycle diagram showing the sequence of hooks from SessionStart through the agentic loop (PreToolUse, PermissionRequest, PostToolUse, SubagentStart/Stop, TaskCompleted) to PostCompact and SessionEnd, with Elicitation and ElicitationResult nested inside MCP tool execution and WorktreeCreate, WorktreeRemove, Notification, ConfigChange, and InstructionsLoaded as standalone async events" width="520" height="1100" data-path="images/hooks-lifecycle.svg" />
22 </Frame>22 </Frame>
23</div>23</div>
24 24
25The table below summarizes when each event fires. The [Hook events](#hook-events) section documents the full input schema and decision control options for each one.25The table below summarizes when each event fires. The [Hook events](#hook-events) section documents the full input schema and decision control options for each one.
26 26
27| Event | When it fires |27| Event | When it fires |
28| :------------------- | :---------------------------------------------------------------------------------------------------------- |28| :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- |
29| `SessionStart` | When a session begins or resumes |29| `SessionStart` | When a session begins or resumes |
30| `UserPromptSubmit` | When you submit a prompt, before Claude processes it |30| `UserPromptSubmit` | When you submit a prompt, before Claude processes it |
31| `PreToolUse` | Before a tool call executes. Can block it |31| `PreToolUse` | Before a tool call executes. Can block it |
38| `Stop` | When Claude finishes responding |38| `Stop` | When Claude finishes responding |
39| `TeammateIdle` | When an [agent team](/en/agent-teams) teammate is about to go idle |39| `TeammateIdle` | When an [agent team](/en/agent-teams) teammate is about to go idle |
40| `TaskCompleted` | When a task is being marked as completed |40| `TaskCompleted` | When a task is being marked as completed |
41| `InstructionsLoaded` | When a CLAUDE.md or `.claude/rules/*.md` file is loaded into context. Fires at session start and when files are lazily loaded during a session |
41| `ConfigChange` | When a configuration file changes during a session |42| `ConfigChange` | When a configuration file changes during a session |
42| `WorktreeCreate` | When a worktree is being created via `--worktree` or `isolation: "worktree"`. Replaces default git behavior |43| `WorktreeCreate` | When a worktree is being created via `--worktree` or `isolation: "worktree"`. Replaces default git behavior |
43| `WorktreeRemove` | When a worktree is being removed, either at session exit or when a subagent finishes |44| `WorktreeRemove` | When a worktree is being removed, either at session exit or when a subagent finishes |
44| `PreCompact` | Before context compaction |45| `PreCompact` | Before context compaction |
46| `PostCompact` | After context compaction completes |
47| `Elicitation` | When an MCP server requests user input during a tool call |
48| `ElicitationResult` | After a user responds to an MCP elicitation, before the response is sent back to the server |
45| `SessionEnd` | When a session terminates |49| `SessionEnd` | When a session terminates |
46 50
47### How a hook resolves51### How a hook resolves
89Now suppose Claude Code decides to run `Bash "rm -rf /tmp/build"`. Here's what happens:93Now suppose Claude Code decides to run `Bash "rm -rf /tmp/build"`. Here's what happens:
90 94
91<Frame>95<Frame>
92 <img src="https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=5bb890134390ecd0581477cf41ef730b" alt="Hook resolution flow: PreToolUse event fires, matcher checks for Bash match, hook handler runs, result returns to Claude Code" data-og-width="780" width="780" data-og-height="290" height="290" data-path="images/hook-resolution.svg" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?w=280&fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=5dcaecd24c260b8a90365d74e2c1fcda 280w, https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?w=560&fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=c03d91c279f01d92e58ddd70fdbe66f2 560w, https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?w=840&fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=1be57a4819cbb949a5ea9d08a05c9ecd 840w, https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?w=1100&fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=0e9dd1807dc7a5c56011d0889b0d5208 1100w, https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?w=1650&fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=69496ac02e70fabfece087ba31a1dcfc 1650w, https://mintcdn.com/claude-code/TBPmHzr19mDCuhZi/images/hook-resolution.svg?w=2500&fit=max&auto=format&n=TBPmHzr19mDCuhZi&q=85&s=a012346cb46a33b86580348802055267 2500w" />96 <img src="https://mintcdn.com/claude-code/c5r9_6tjPMzFdDDT/images/hook-resolution.svg?fit=max&auto=format&n=c5r9_6tjPMzFdDDT&q=85&s=ad667ee6d86ab2276aa48a4e73e220df" alt="Hook resolution flow: PreToolUse event fires, matcher checks for Bash match, hook handler runs, result returns to Claude Code" width="780" height="290" data-path="images/hook-resolution.svg" />
93</Frame>97</Frame>
94 98
95<Steps>99<Steps>
162The `matcher` field is a regex string that filters when hooks fire. Use `"*"`, `""`, or omit `matcher` entirely to match all occurrences. Each event type matches on a different field:166The `matcher` field is a regex string that filters when hooks fire. Use `"*"`, `""`, or omit `matcher` entirely to match all occurrences. Each event type matches on a different field:
163 167
164| Event | What the matcher filters | Example matcher values |168| Event | What the matcher filters | Example matcher values |
165| :---------------------------------------------------------------------------------------------- | :------------------------ | :--------------------------------------------------------------------------------- |169| :-------------------------------------------------------------------------------------------------------------------- | :------------------------ | :--------------------------------------------------------------------------------- |
166| `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest` | tool name | `Bash`, `Edit\|Write`, `mcp__.*` |170| `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest` | tool name | `Bash`, `Edit\|Write`, `mcp__.*` |
167| `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact` |171| `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact` |
168| `SessionEnd` | why the session ended | `clear`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` |172| `SessionEnd` | why the session ended | `clear`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` |
171| `PreCompact` | what triggered compaction | `manual`, `auto` |175| `PreCompact` | what triggered compaction | `manual`, `auto` |
172| `SubagentStop` | agent type | same values as `SubagentStart` |176| `SubagentStop` | agent type | same values as `SubagentStart` |
173| `ConfigChange` | configuration source | `user_settings`, `project_settings`, `local_settings`, `policy_settings`, `skills` |177| `ConfigChange` | configuration source | `user_settings`, `project_settings`, `local_settings`, `policy_settings`, `skills` |
174| `UserPromptSubmit`, `Stop`, `TeammateIdle`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove` | no matcher support | always fires on every occurrence |178| `UserPromptSubmit`, `Stop`, `TeammateIdle`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove`, `InstructionsLoaded` | no matcher support | always fires on every occurrence |
175 179
176The matcher is a regex, so `Edit|Write` matches either tool and `Notebook.*` matches any tool starting with Notebook. The matcher runs against a field from the [JSON input](#hook-input-and-output) that Claude Code sends to your hook on stdin. For tool events, that field is `tool_name`. Each [hook event](#hook-events) section lists the full set of matcher values and the input schema for that event.180The matcher is a regex, so `Edit|Write` matches either tool and `Notebook.*` matches any tool starting with Notebook. The matcher runs against a field from the [JSON input](#hook-input-and-output) that Claude Code sends to your hook on stdin. For tool events, that field is `tool_name`. Each [hook event](#hook-events) section lists the full set of matcher values and the input schema for that event.
177 181
195}199}
196```200```
197 201
198`UserPromptSubmit`, `Stop`, `TeammateIdle`, `TaskCompleted`, `WorktreeCreate`, and `WorktreeRemove` don't support matchers and always fire on every occurrence. If you add a `matcher` field to these events, it is silently ignored.202`UserPromptSubmit`, `Stop`, `TeammateIdle`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove`, and `InstructionsLoaded` don't support matchers and always fire on every occurrence. If you add a `matcher` field to these events, it is silently ignored.
199 203
200#### Match MCP tools204#### Match MCP tools
201 205
309}313}
310```314```
311 315
312<Note>
313 HTTP hooks must be configured by editing settings JSON directly. The `/hooks` interactive menu only supports adding command hooks.
314</Note>
315
316#### Prompt and agent hook fields316#### Prompt and agent hook fields
317 317
318In addition to the [common fields](#common-fields), prompt and agent hooks accept these fields:318In addition to the [common fields](#common-fields), prompt and agent hooks accept these fields:
410 410
411### The `/hooks` menu411### The `/hooks` menu
412 412
413Type `/hooks` in Claude Code to open the interactive hooks manager, where you can view, add, and delete hooks without editing settings files directly. For a step-by-step walkthrough, see [Set up your first hook](/en/hooks-guide#set-up-your-first-hook) in the guide.413Type `/hooks` in Claude Code to open a read-only browser for your configured hooks. The menu shows every hook event with a count of configured hooks, lets you drill into matchers, and shows the full details of each hook handler. Use it to verify configuration, check which settings file a hook came from, or inspect a hook's command, prompt, or URL.
414
415The menu displays all four hook types: `command`, `prompt`, `agent`, and `http`. Each hook is labeled with a `[type]` prefix and a source indicating where it was defined:
414 416
415Each hook in the menu is labeled with a bracket prefix indicating its source:417* `User`: from `~/.claude/settings.json`
418* `Project`: from `.claude/settings.json`
419* `Local`: from `.claude/settings.local.json`
420* `Plugin`: from a plugin's `hooks/hooks.json`
421* `Session`: registered in memory for the current session
422* `Built-in`: registered internally by Claude Code
416 423
417* `[User]`: from `~/.claude/settings.json`424Selecting a hook opens a detail view showing its event, matcher, type, source file, and the full command, prompt, or URL. The menu is read-only: to add, modify, or remove hooks, edit the settings JSON directly or ask Claude to make the change.
418* `[Project]`: from `.claude/settings.json`
419* `[Local]`: from `.claude/settings.local.json`
420* `[Plugin]`: from a plugin's `hooks/hooks.json`, read-only
421 425
422### Disable or remove hooks426### Disable or remove hooks
423 427
424To remove a hook, delete its entry from the settings JSON file, or use the `/hooks` menu and select the hook to delete it.428To remove a hook, delete its entry from the settings JSON file.
425 429
426To temporarily disable all hooks without removing them, set `"disableAllHooks": true` in your settings file or use the toggle in the `/hooks` menu. There is no way to disable an individual hook while keeping it in the configuration.430To temporarily disable all hooks without removing them, set `"disableAllHooks": true` in your settings file. There is no way to disable an individual hook while keeping it in the configuration.
427 431
428The `disableAllHooks` setting respects the managed settings hierarchy. If an administrator has configured hooks through managed policy settings, `disableAllHooks` set in user, project, or local settings cannot disable those managed hooks. Only `disableAllHooks` set at the managed settings level can disable managed hooks.432The `disableAllHooks` setting respects the managed settings hierarchy. If an administrator has configured hooks through managed policy settings, `disableAllHooks` set in user, project, or local settings cannot disable those managed hooks. Only `disableAllHooks` set at the managed settings level can disable managed hooks.
429 433
430Direct edits to hooks in settings files don't take effect immediately. Claude Code captures a snapshot of hooks at startup and uses it throughout the session. This prevents malicious or accidental hook modifications from taking effect mid-session without your review. If hooks are modified externally, Claude Code warns you and requires review in the `/hooks` menu before changes apply.434Direct edits to hooks in settings files are normally picked up automatically by the file watcher.
431 435
432## Hook input and output436## Hook input and output
433 437
445| `permission_mode` | Current [permission mode](/en/permissions#permission-modes): `"default"`, `"plan"`, `"acceptEdits"`, `"dontAsk"`, or `"bypassPermissions"` |449| `permission_mode` | Current [permission mode](/en/permissions#permission-modes): `"default"`, `"plan"`, `"acceptEdits"`, `"dontAsk"`, or `"bypassPermissions"` |
446| `hook_event_name` | Name of the event that fired |450| `hook_event_name` | Name of the event that fired |
447 451
452When running with `--agent` or inside a subagent, two additional fields are included:
453
454| Field | Description |
455| :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
456| `agent_id` | Unique identifier for the subagent. Present only when the hook fires inside a subagent call. Use this to distinguish subagent hook calls from main-thread calls. |
457| `agent_type` | Agent name (for example, `"Explore"` or `"security-reviewer"`). Present when the session uses `--agent` or the hook fires inside a subagent. For subagents, the subagent's type takes precedence over the session's `--agent` value. |
458
448For example, a `PreToolUse` hook for a Bash command receives this on stdin:459For example, a `PreToolUse` hook for a Bash command receives this on stdin:
449 460
450```json theme={null}461```json theme={null}
509| `SessionStart` | No | Shows stderr to user only |520| `SessionStart` | No | Shows stderr to user only |
510| `SessionEnd` | No | Shows stderr to user only |521| `SessionEnd` | No | Shows stderr to user only |
511| `PreCompact` | No | Shows stderr to user only |522| `PreCompact` | No | Shows stderr to user only |
523| `PostCompact` | No | Shows stderr to user only |
524| `Elicitation` | Yes | Denies the elicitation |
525| `ElicitationResult` | Yes | Blocks the response (action becomes decline) |
512| `WorktreeCreate` | Yes | Any non-zero exit code causes worktree creation to fail |526| `WorktreeCreate` | Yes | Any non-zero exit code causes worktree creation to fail |
513| `WorktreeRemove` | No | Failures are logged in debug mode only |527| `WorktreeRemove` | No | Failures are logged in debug mode only |
528| `InstructionsLoaded` | No | Exit code is ignored |
514 529
515### HTTP response handling530### HTTP response handling
516 531
558Not every event supports blocking or controlling behavior through JSON. The events that do each use a different set of fields to express that decision. Use this table as a quick reference before writing a hook:573Not every event supports blocking or controlling behavior through JSON. The events that do each use a different set of fields to express that decision. Use this table as a quick reference before writing a hook:
559 574
560| Events | Decision pattern | Key fields |575| Events | Decision pattern | Key fields |
561| :---------------------------------------------------------------------------------- | :------------------- | :-------------------------------------------------------------------------- |576| :------------------------------------------------------------------------------------ | :----------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
562| UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop, SubagentStop, ConfigChange | Top-level `decision` | `decision: "block"`, `reason` |577| UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop, SubagentStop, ConfigChange | Top-level `decision` | `decision: "block"`, `reason` |
563| TeammateIdle, TaskCompleted | Exit code only | Exit code 2 blocks the action, stderr is fed back as feedback |578| TeammateIdle, TaskCompleted | Exit code or `continue: false` | Exit code 2 blocks the action with stderr feedback. JSON `{"continue": false, "stopReason": "..."}` also stops the teammate entirely, matching `Stop` hook behavior |
564| PreToolUse | `hookSpecificOutput` | `permissionDecision` (allow/deny/ask), `permissionDecisionReason` |579| PreToolUse | `hookSpecificOutput` | `permissionDecision` (allow/deny/ask), `permissionDecisionReason` |
565| PermissionRequest | `hookSpecificOutput` | `decision.behavior` (allow/deny) |580| PermissionRequest | `hookSpecificOutput` | `decision.behavior` (allow/deny) |
566| WorktreeCreate | stdout path | Hook prints absolute path to created worktree. Non-zero exit fails creation |581| WorktreeCreate | stdout path | Hook prints absolute path to created worktree. Non-zero exit fails creation |
567| WorktreeRemove, Notification, SessionEnd, PreCompact | None | No decision control. Used for side effects like logging or cleanup |582| Elicitation | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values for accept) |
583| ElicitationResult | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values override) |
584| WorktreeRemove, Notification, SessionEnd, PreCompact, PostCompact, InstructionsLoaded | None | No decision control. Used for side effects like logging or cleanup |
568 585
569Here are examples of each pattern in action:586Here are examples of each pattern in action:
570 587
623 640
624Runs when Claude Code starts a new session or resumes an existing session. Useful for loading development context like existing issues or recent changes to your codebase, or setting up environment variables. For static context that does not require a script, use [CLAUDE.md](/en/memory) instead.641Runs when Claude Code starts a new session or resumes an existing session. Useful for loading development context like existing issues or recent changes to your codebase, or setting up environment variables. For static context that does not require a script, use [CLAUDE.md](/en/memory) instead.
625 642
626SessionStart runs on every session, so keep these hooks fast.643SessionStart runs on every session, so keep these hooks fast. Only `type: "command"` hooks are supported.
627 644
628The matcher value corresponds to how the session was initiated:645The matcher value corresponds to how the session was initiated:
629 646
710 `CLAUDE_ENV_FILE` is available for SessionStart hooks. Other hook types do not have access to this variable.727 `CLAUDE_ENV_FILE` is available for SessionStart hooks. Other hook types do not have access to this variable.
711</Note>728</Note>
712 729
730### InstructionsLoaded
731
732Fires when a `CLAUDE.md` or `.claude/rules/*.md` file is loaded into context. This event fires at session start for eagerly-loaded files and again later when files are lazily loaded, for example when Claude accesses a subdirectory that contains a nested `CLAUDE.md` or when conditional rules with `paths:` frontmatter match. The hook does not support blocking or decision control. It runs asynchronously for observability purposes.
733
734InstructionsLoaded does not support matchers and fires on every load occurrence.
735
736#### InstructionsLoaded input
737
738In addition to the [common input fields](#common-input-fields), InstructionsLoaded hooks receive these fields:
739
740| Field | Description |
741| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
742| `file_path` | Absolute path to the instruction file that was loaded |
743| `memory_type` | Scope of the file: `"User"`, `"Project"`, `"Local"`, or `"Managed"` |
744| `load_reason` | Why the file was loaded: `"session_start"`, `"nested_traversal"`, `"path_glob_match"`, `"include"`, or `"compact"`. The `"compact"` value fires when instruction files are re-loaded after a compaction event |
745| `globs` | Path glob patterns from the file's `paths:` frontmatter, if any. Present only for `path_glob_match` loads |
746| `trigger_file_path` | Path to the file whose access triggered this load, for lazy loads |
747| `parent_file_path` | Path to the parent instruction file that included this one, for `include` loads |
748
749```json theme={null}
750{
751 "session_id": "abc123",
752 "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
753 "cwd": "/Users/my-project",
754 "permission_mode": "default",
755 "hook_event_name": "InstructionsLoaded",
756 "file_path": "/Users/my-project/CLAUDE.md",
757 "memory_type": "Project",
758 "load_reason": "session_start"
759}
760```
761
762#### InstructionsLoaded decision control
763
764InstructionsLoaded hooks have no decision control. They cannot block or modify instruction loading. Use this event for audit logging, compliance tracking, or observability.
765
713### UserPromptSubmit766### UserPromptSubmit
714 767
715Runs when the user submits a prompt, before Claude processes it. This allows you768Runs when the user submits a prompt, before Claude processes it. This allows you
874`PreToolUse` hooks can control whether a tool call proceeds. Unlike other hooks that use a top-level `decision` field, PreToolUse returns its decision inside a `hookSpecificOutput` object. This gives it richer control: three outcomes (allow, deny, or ask) plus the ability to modify tool input before execution.927`PreToolUse` hooks can control whether a tool call proceeds. Unlike other hooks that use a top-level `decision` field, PreToolUse returns its decision inside a `hookSpecificOutput` object. This gives it richer control: three outcomes (allow, deny, or ask) plus the ability to modify tool input before execution.
875 928
876| Field | Description |929| Field | Description |
877| :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- |930| :------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
878| `permissionDecision` | `"allow"` bypasses the permission system, `"deny"` prevents the tool call, `"ask"` prompts the user to confirm |931| `permissionDecision` | `"allow"` skips the permission prompt. `"deny"` prevents the tool call. `"ask"` prompts the user to confirm. [Deny and ask rules](/en/permissions#manage-permissions) still apply when a hook returns `"allow"` |
879| `permissionDecisionReason` | For `"allow"` and `"ask"`, shown to the user but not Claude. For `"deny"`, shown to Claude |932| `permissionDecisionReason` | For `"allow"` and `"ask"`, shown to the user but not Claude. For `"deny"`, shown to Claude |
880| `updatedInput` | Modifies the tool's input parameters before execution. Combine with `"allow"` to auto-approve, or `"ask"` to show the modified input to the user |933| `updatedInput` | Modifies the tool's input parameters before execution. Combine with `"allow"` to auto-approve, or `"ask"` to show the modified input to the user |
881| `additionalContext` | String added to Claude's context before the tool executes |934| `additionalContext` | String added to Claude's context before the tool executes |
882 935
936When a hook returns `"ask"`, the permission prompt displayed to the user includes a label identifying where the hook came from: for example, `[User]`, `[Project]`, `[Plugin]`, or `[Local]`. This helps users understand which configuration source is requesting confirmation.
937
883```json theme={null}938```json theme={null}
884{939{
885 "hookSpecificOutput": {940 "hookSpecificOutput": {
922 "description": "Remove node_modules directory"977 "description": "Remove node_modules directory"
923 },978 },
924 "permission_suggestions": [979 "permission_suggestions": [
925 { "type": "toolAlwaysAllow", "tool": "Bash" }980 {
981 "type": "addRules",
982 "rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
983 "behavior": "allow",
984 "destination": "localSettings"
985 }
926 ]986 ]
927}987}
928```988```
932`PermissionRequest` hooks can allow or deny permission requests. In addition to the [JSON output fields](#json-output) available to all hooks, your hook script can return a `decision` object with these event-specific fields:992`PermissionRequest` hooks can allow or deny permission requests. In addition to the [JSON output fields](#json-output) available to all hooks, your hook script can return a `decision` object with these event-specific fields:
933 993
934| Field | Description |994| Field | Description |
935| :------------------- | :------------------------------------------------------------------------------------------------------------- |995| :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
936| `behavior` | `"allow"` grants the permission, `"deny"` denies it |996| `behavior` | `"allow"` grants the permission, `"deny"` denies it |
937| `updatedInput` | For `"allow"` only: modifies the tool's input parameters before execution |997| `updatedInput` | For `"allow"` only: modifies the tool's input parameters before execution |
938| `updatedPermissions` | For `"allow"` only: applies permission rule updates, equivalent to the user selecting an "always allow" option |998| `updatedPermissions` | For `"allow"` only: array of [permission update entries](#permission-update-entries) to apply, such as adding an allow rule or changing the session permission mode |
939| `message` | For `"deny"` only: tells Claude why the permission was denied |999| `message` | For `"deny"` only: tells Claude why the permission was denied |
940| `interrupt` | For `"deny"` only: if `true`, stops Claude |1000| `interrupt` | For `"deny"` only: if `true`, stops Claude |
941 1001
953}1013}
954```1014```
955 1015
1016#### Permission update entries
1017
1018The `updatedPermissions` output field and the [`permission_suggestions` input field](#permissionrequest-input) both use the same array of entry objects. Each entry has a `type` that determines its other fields, and a `destination` that controls where the change is written.
1019
1020| `type` | Fields | Effect |
1021| :------------------ | :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1022| `addRules` | `rules`, `behavior`, `destination` | Adds permission rules. `rules` is an array of `{toolName, ruleContent?}` objects. Omit `ruleContent` to match the whole tool. `behavior` is `"allow"`, `"deny"`, or `"ask"` |
1023| `replaceRules` | `rules`, `behavior`, `destination` | Replaces all rules of the given `behavior` at the `destination` with the provided `rules` |
1024| `removeRules` | `rules`, `behavior`, `destination` | Removes matching rules of the given `behavior` |
1025| `setMode` | `mode`, `destination` | Changes the permission mode. Valid modes are `default`, `acceptEdits`, `dontAsk`, `bypassPermissions`, and `plan` |
1026| `addDirectories` | `directories`, `destination` | Adds working directories. `directories` is an array of path strings |
1027| `removeDirectories` | `directories`, `destination` | Removes working directories |
1028
1029The `destination` field on every entry determines whether the change stays in memory or persists to a settings file.
1030
1031| `destination` | Writes to |
1032| :---------------- | :---------------------------------------------- |
1033| `session` | in-memory only, discarded when the session ends |
1034| `localSettings` | `.claude/settings.local.json` |
1035| `projectSettings` | `.claude/settings.json` |
1036| `userSettings` | `~/.claude/settings.json` |
1037
1038A hook can echo one of the `permission_suggestions` it received as its own `updatedPermissions` output, which is equivalent to the user selecting that "always allow" option in the dialog.
1039
956### PostToolUse1040### PostToolUse
957 1041
958Runs immediately after a tool completes successfully.1042Runs immediately after a tool completes successfully.
1212 1296
1213Runs when an [agent team](/en/agent-teams) teammate is about to go idle after finishing its turn. Use this to enforce quality gates before a teammate stops working, such as requiring passing lint checks or verifying that output files exist.1297Runs when an [agent team](/en/agent-teams) teammate is about to go idle after finishing its turn. Use this to enforce quality gates before a teammate stops working, such as requiring passing lint checks or verifying that output files exist.
1214 1298
1215When a `TeammateIdle` hook exits with code 2, the teammate receives the stderr message as feedback and continues working instead of going idle. TeammateIdle hooks do not support matchers and fire on every occurrence.1299When a `TeammateIdle` hook exits with code 2, the teammate receives the stderr message as feedback and continues working instead of going idle. To stop the teammate entirely instead of re-running it, return JSON with `{"continue": false, "stopReason": "..."}`. TeammateIdle hooks do not support matchers and fire on every occurrence.
1216 1300
1217#### TeammateIdle input1301#### TeammateIdle input
1218 1302
1237 1321
1238#### TeammateIdle decision control1322#### TeammateIdle decision control
1239 1323
1240TeammateIdle hooks use exit codes only, not JSON decision control. This example checks that a build artifact exists before allowing a teammate to go idle:1324TeammateIdle hooks support two ways to control teammate behavior:
1325
1326* **Exit code 2**: the teammate receives the stderr message as feedback and continues working instead of going idle.
1327* **JSON `{"continue": false, "stopReason": "..."}`**: stops the teammate entirely, matching `Stop` hook behavior. The `stopReason` is shown to the user.
1328
1329This example checks that a build artifact exists before allowing a teammate to go idle:
1241 1330
1242```bash theme={null}1331```bash theme={null}
1243#!/bin/bash1332#!/bin/bash
1254 1343
1255Runs when a task is being marked as completed. This fires in two situations: when any agent explicitly marks a task as completed through the TaskUpdate tool, or when an [agent team](/en/agent-teams) teammate finishes its turn with in-progress tasks. Use this to enforce completion criteria like passing tests or lint checks before a task can close.1344Runs when a task is being marked as completed. This fires in two situations: when any agent explicitly marks a task as completed through the TaskUpdate tool, or when an [agent team](/en/agent-teams) teammate finishes its turn with in-progress tasks. Use this to enforce completion criteria like passing tests or lint checks before a task can close.
1256 1345
1257When a `TaskCompleted` hook exits with code 2, the task is not marked as completed and the stderr message is fed back to the model as feedback. TaskCompleted hooks do not support matchers and fire on every occurrence.1346When a `TaskCompleted` hook exits with code 2, the task is not marked as completed and the stderr message is fed back to the model as feedback. To stop the teammate entirely instead of re-running it, return JSON with `{"continue": false, "stopReason": "..."}`. TaskCompleted hooks do not support matchers and fire on every occurrence.
1258 1347
1259#### TaskCompleted input1348#### TaskCompleted input
1260 1349
1285 1374
1286#### TaskCompleted decision control1375#### TaskCompleted decision control
1287 1376
1288TaskCompleted hooks use exit codes only, not JSON decision control. This example runs tests and blocks task completion if they fail:1377TaskCompleted hooks support two ways to control task completion:
1378
1379* **Exit code 2**: the task is not marked as completed and the stderr message is fed back to the model as feedback.
1380* **JSON `{"continue": false, "stopReason": "..."}`**: stops the teammate entirely, matching `Stop` hook behavior. The `stopReason` is shown to the user.
1381
1382This example runs tests and blocks task completion if they fail:
1289 1383
1290```bash theme={null}1384```bash theme={null}
1291#!/bin/bash1385#!/bin/bash
1483}1577}
1484```1578```
1485 1579
1580### PostCompact
1581
1582Runs after Claude Code completes a compact operation. Use this event to react to the new compacted state, for example to log the generated summary or update external state.
1583
1584The same matcher values apply as for `PreCompact`:
1585
1586| Matcher | When it fires |
1587| :------- | :------------------------------------------------- |
1588| `manual` | After `/compact` |
1589| `auto` | After auto-compact when the context window is full |
1590
1591#### PostCompact input
1592
1593In addition to the [common input fields](#common-input-fields), PostCompact hooks receive `trigger` and `compact_summary`. The `compact_summary` field contains the conversation summary generated by the compact operation.
1594
1595```json theme={null}
1596{
1597 "session_id": "abc123",
1598 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1599 "cwd": "/Users/...",
1600 "permission_mode": "default",
1601 "hook_event_name": "PostCompact",
1602 "trigger": "manual",
1603 "compact_summary": "Summary of the compacted conversation..."
1604}
1605```
1606
1607PostCompact hooks have no decision control. They cannot affect the compaction result but can perform follow-up tasks.
1608
1486### SessionEnd1609### SessionEnd
1487 1610
1488Runs when a Claude Code session ends. Useful for cleanup tasks, logging session1611Runs when a Claude Code session ends. Useful for cleanup tasks, logging session
1515 1638
1516SessionEnd hooks have no decision control. They cannot block session termination but can perform cleanup tasks.1639SessionEnd hooks have no decision control. They cannot block session termination but can perform cleanup tasks.
1517 1640
1641SessionEnd hooks have a default timeout of 1.5 seconds. This applies to both session exit and `/clear`. If your hooks need more time, set the `CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS` environment variable to a higher value in milliseconds. Any per-hook `timeout` setting is also capped by this value.
1642
1643```bash theme={null}
1644CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude
1645```
1646
1647### Elicitation
1648
1649Runs when an MCP server requests user input mid-task. By default, Claude Code shows an interactive dialog for the user to respond. Hooks can intercept this request and respond programmatically, skipping the dialog entirely.
1650
1651The matcher field matches against the MCP server name.
1652
1653#### Elicitation input
1654
1655In addition to the [common input fields](#common-input-fields), Elicitation hooks receive `mcp_server_name`, `message`, and optional `mode`, `url`, `elicitation_id`, and `requested_schema` fields.
1656
1657For form-mode elicitation (the most common case):
1658
1659```json theme={null}
1660{
1661 "session_id": "abc123",
1662 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1663 "cwd": "/Users/...",
1664 "permission_mode": "default",
1665 "hook_event_name": "Elicitation",
1666 "mcp_server_name": "my-mcp-server",
1667 "message": "Please provide your credentials",
1668 "mode": "form",
1669 "requested_schema": {
1670 "type": "object",
1671 "properties": {
1672 "username": { "type": "string", "title": "Username" }
1673 }
1674 }
1675}
1676```
1677
1678For URL-mode elicitation (browser-based authentication):
1679
1680```json theme={null}
1681{
1682 "session_id": "abc123",
1683 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1684 "cwd": "/Users/...",
1685 "permission_mode": "default",
1686 "hook_event_name": "Elicitation",
1687 "mcp_server_name": "my-mcp-server",
1688 "message": "Please authenticate",
1689 "mode": "url",
1690 "url": "https://auth.example.com/login"
1691}
1692```
1693
1694#### Elicitation output
1695
1696To respond programmatically without showing the dialog, return a JSON object with `hookSpecificOutput`:
1697
1698```json theme={null}
1699{
1700 "hookSpecificOutput": {
1701 "hookEventName": "Elicitation",
1702 "action": "accept",
1703 "content": {
1704 "username": "alice"
1705 }
1706 }
1707}
1708```
1709
1710| Field | Values | Description |
1711| :-------- | :---------------------------- | :--------------------------------------------------------------- |
1712| `action` | `accept`, `decline`, `cancel` | Whether to accept, decline, or cancel the request |
1713| `content` | object | Form field values to submit. Only used when `action` is `accept` |
1714
1715Exit code 2 denies the elicitation and shows stderr to the user.
1716
1717### ElicitationResult
1718
1719Runs after a user responds to an MCP elicitation. Hooks can observe, modify, or block the response before it is sent back to the MCP server.
1720
1721The matcher field matches against the MCP server name.
1722
1723#### ElicitationResult input
1724
1725In addition to the [common input fields](#common-input-fields), ElicitationResult hooks receive `mcp_server_name`, `action`, and optional `mode`, `elicitation_id`, and `content` fields.
1726
1727```json theme={null}
1728{
1729 "session_id": "abc123",
1730 "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
1731 "cwd": "/Users/...",
1732 "permission_mode": "default",
1733 "hook_event_name": "ElicitationResult",
1734 "mcp_server_name": "my-mcp-server",
1735 "action": "accept",
1736 "content": { "username": "alice" },
1737 "mode": "form",
1738 "elicitation_id": "elicit-123"
1739}
1740```
1741
1742#### ElicitationResult output
1743
1744To override the user's response, return a JSON object with `hookSpecificOutput`:
1745
1746```json theme={null}
1747{
1748 "hookSpecificOutput": {
1749 "hookEventName": "ElicitationResult",
1750 "action": "decline",
1751 "content": {}
1752 }
1753}
1754```
1755
1756| Field | Values | Description |
1757| :-------- | :---------------------------- | :--------------------------------------------------------------------- |
1758| `action` | `accept`, `decline`, `cancel` | Overrides the user's action |
1759| `content` | object | Overrides form field values. Only meaningful when `action` is `accept` |
1760
1761Exit code 2 blocks the response, changing the effective action to `decline`.
1762
1518## Prompt-based hooks1763## Prompt-based hooks
1519 1764
1520In addition to command and HTTP hooks, Claude Code supports prompt-based hooks (`type: "prompt"`) that use an LLM to evaluate whether to allow or block an action, and agent hooks (`type: "agent"`) that spawn an agentic verifier with tool access. Not all events support every hook type.1765In addition to command and HTTP hooks, Claude Code supports prompt-based hooks (`type: "prompt"`) that use an LLM to evaluate whether to allow or block an action, and agent hooks (`type: "agent"`) that spawn an agentic verifier with tool access. Not all events support every hook type.
1533Events that only support `type: "command"` hooks:1778Events that only support `type: "command"` hooks:
1534 1779
1535* `ConfigChange`1780* `ConfigChange`
1781* `Elicitation`
1782* `ElicitationResult`
1783* `InstructionsLoaded`
1536* `Notification`1784* `Notification`
1785* `PostCompact`
1537* `PreCompact`1786* `PreCompact`
1538* `SessionEnd`1787* `SessionEnd`
1539* `SessionStart`1788* `SessionStart`
1704 1953
1705After the background process exits, if the hook produced a JSON response with a `systemMessage` or `additionalContext` field, that content is delivered to Claude as context on the next conversation turn.1954After the background process exits, if the hook produced a JSON response with a `systemMessage` or `additionalContext` field, that content is delivered to Claude as context on the next conversation turn.
1706 1955
1956Async hook completion notifications are suppressed by default. To see them, enable verbose mode with `Ctrl+O` or start Claude Code with `--verbose`.
1957
1707### Example: run tests after file changes1958### Example: run tests after file changes
1708 1959
1709This hook starts a test suite in the background whenever Claude writes a file, then reports the results back to Claude when the tests finish. Save this script to `.claude/hooks/run-tests-async.sh` in your project and make it executable with `chmod +x`:1960This hook starts a test suite in the background whenever Claude writes a file, then reports the results back to Claude when the tests finish. Save this script to `.claude/hooks/run-tests-async.sh` in your project and make it executable with `chmod +x`: