claude_hooks 1.2.1 โ†’ 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ff845df334960263a1d36b01aeba57393609c59acbc63424572c9d7fdca7999
4
- data.tar.gz: 70029c193102d9a63b6d6e5ff8361fcf42e506bff26435c907ec4a06010a571a
3
+ metadata.gz: ee18a384c04bcfff4fa734935277a938e14d2799646369e8db8e013c5a7b33ce
4
+ data.tar.gz: b784bc4cc4e52096b563c25a2c6d19175a38ac37e6ea72d110c32c3887566128
5
5
  SHA512:
6
- metadata.gz: 28a4548cafd93c7f5d5c9d1f577399d8e90503c2e4ae1fa279a9723234045848c0b3bdbb1c41df9f0808d375eb5d345a5210d9db46fe74ba3332fb2394920530
7
- data.tar.gz: e2555f3731aad5bacb7e572c9630fa9e39721b625684664bc2ea41d9b0579af146f697b10a6d99410368140ebb7cd37de53b2bd195e2a2c13a5100722502fcd2
6
+ metadata.gz: 25345ee635cc383661393b66bca8d82f476615e311fb07d3206630b4cc13b9972c62cb08efe2d0ba03ab053ce40f07bdac2dfa3b0d633377150474ba37743e5d
7
+ data.tar.gz: ed9511e85a7a74e9ea851e5ece032afcb6c860f48b52143da80fc999a81dce35374ff1df3fca1914d2c91963578c7f7561d94890bb6f39bf23e530599b2a107e
data/CHANGELOG.md CHANGED
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.0] - 2026-08-15
9
+
10
+ ### Added
11
+
12
+ - **`DirectoryAdded` hook event** (`ClaudeHooks::DirectoryAdded` / `ClaudeHooks::Output::DirectoryAdded`): non-blocking hook that runs after a working directory is added mid-session via `/add-dir` or the SDK `register_repo_root` control request. Exposes `directory` and `source` readers plus a `system_message!` builder.
13
+
8
14
  ## [1.2.1] - 2026-07-15
9
15
 
10
16
  ### Added
data/README.md CHANGED
@@ -289,6 +289,7 @@ The framework supports the following hook types:
289
289
  | **[PostCompact](docs/API/POST_COMPACT.md)** | `ClaudeHooks::PostCompact` | Runs after transcript compaction completes |
290
290
  | **[ConfigChange](docs/API/CONFIG_CHANGE.md)** | `ClaudeHooks::ConfigChange` | Runs when Claude Code configuration changes; can block it |
291
291
  | **[CwdChanged](docs/API/CWD_CHANGED.md)** | `ClaudeHooks::CwdChanged` | Runs when the working directory changes |
292
+ | **[DirectoryAdded](docs/API/DIRECTORY_ADDED.md)** | `ClaudeHooks::DirectoryAdded` | Runs when a working directory is added mid-session |
292
293
  | **[FileChanged](docs/API/FILE_CHANGED.md)** | `ClaudeHooks::FileChanged` | Runs when a watched file is created, modified, or deleted |
293
294
  | **[InstructionsLoaded](docs/API/INSTRUCTIONS_LOADED.md)** | `ClaudeHooks::InstructionsLoaded` | Runs when a CLAUDE.md instructions file is loaded |
294
295
  | **[Elicitation](docs/API/ELICITATION.md)** | `ClaudeHooks::Elicitation` | Runs when an MCP server requests user input |
@@ -417,6 +418,7 @@ The framework supports all existing hook types with their respective input field
417
418
  | **PostCompact** | `trigger`, `compact_summary` |
418
419
  | **ConfigChange** | `source`, `file_path` |
419
420
  | **CwdChanged** | `old_cwd`, `new_cwd` |
421
+ | **DirectoryAdded** | `directory`, `source` |
420
422
  | **FileChanged** | `file_path`, `event` |
421
423
  | **InstructionsLoaded** | `file_path`, `load_reason` |
422
424
  | **Elicitation** | `mcp_server_name`, `message`, `mode`, `url`, `elicitation_id`, `requested_schema` |
@@ -650,7 +652,7 @@ Claude Code hooks support multiple exit codes with different behaviors depending
650
652
  > - **Blocking via top-level `decision`** (behave like `PreToolUse`/`Stop`): `UserPromptExpansion`, `PostToolBatch`, `ConfigChange`.
651
653
  > - **Blocking via `exit 2` / `continue: false`** (no `decision` field): `TaskCreated`, `TaskCompleted`, `TeammateIdle`.
652
654
  > - **JSON-API special** (always `exit 0`, decision in `hookSpecificOutput`): `PermissionDenied`, `Elicitation`, `ElicitationResult`, `WorktreeCreate` (bare-path stdout).
653
- > - **Non-blocking / context-only** (exit code effectively ignored): `Setup`, `SubagentStart`, `PostToolUseFailure`, `StopFailure`, `PostCompact`, `CwdChanged`, `FileChanged`, `InstructionsLoaded`, `WorktreeRemove`, `MessageDisplay`.
655
+ > - **Non-blocking / context-only** (exit code effectively ignored): `Setup`, `SubagentStart`, `PostToolUseFailure`, `StopFailure`, `PostCompact`, `CwdChanged`, `DirectoryAdded`, `FileChanged`, `InstructionsLoaded`, `WorktreeRemove`, `MessageDisplay`.
654
656
 
655
657
 
656
658
  #### Manually outputing and exiting example with success
@@ -0,0 +1,34 @@
1
+ # DirectoryAdded API
2
+
3
+ Available when inheriting from `ClaudeHooks::DirectoryAdded`:
4
+
5
+ Runs after a working directory is added mid-session via `/add-dir` or the SDK `register_repo_root` control request. Non-blocking โ€” the directory is already added when the hook runs.
6
+
7
+ ## Input Helpers
8
+
9
+ [๐Ÿ“š Shared input helpers](COMMON.md#input-helpers)
10
+
11
+ | Method | Description |
12
+ |--------|-------------|
13
+ | `directory` | Absolute path of the directory that was added |
14
+ | `source` | How the directory was added: `'slash_command'` for `/add-dir` or `'register_repo_root'` for the SDK control request |
15
+
16
+ ## Hook State Helpers
17
+
18
+ [๐Ÿ“š Shared hook state methods](COMMON.md#hook-state-methods)
19
+
20
+ | Method | Description |
21
+ |--------|-------------|
22
+ | `system_message!` | Set a message delivered to Claude as context on the next turn (`slash_command`) or written to the debug log (`register_repo_root`) |
23
+
24
+ ## Output Helpers
25
+
26
+ [๐Ÿ“š Shared output helpers](COMMON.md#output-helpers)
27
+
28
+ | Method | Description |
29
+ |--------|-------------|
30
+ | `output.system_message` | The configured system message |
31
+
32
+ ## Hook Exit Codes
33
+
34
+ Non-blocking. Exit code is ignored; only `systemMessage` is consumed.
@@ -8,17 +8,17 @@
8
8
 
9
9
  For a quickstart guide with examples, see [Automate actions with hooks](https://code.claude.com/docs/en/hooks-guide).
10
10
 
11
- Hooks are user-defined shell commands, HTTP endpoints, or LLM prompts that execute automatically at specific points in Claude Codeโ€™s lifecycle. Use this reference to look up event schemas, configuration options, JSON input/output formats, and advanced features like async hooks, HTTP hooks, and MCP tool hooks. If youโ€™re setting up hooks for the first time, start with the [guide](https://code.claude.com/docs/en/hooks-guide) instead.
11
+ Hooks are user-defined shell commands, HTTP endpoints, or LLM prompts that execute automatically at specific points in Claude Codeโ€™s lifecycle. Hooks run wherever Claude Code runs: sessions in the terminal, IDE extensions, the [Desktop app](https://code.claude.com/docs/en/desktop-quickstart), and [Claude Code on the web](https://code.claude.com/docs/en/claude-code-on-the-web) all fire the same hook events. Use this reference to look up event schemas, configuration options, JSON input/output formats, and advanced features like async hooks, HTTP hooks, and MCP tool hooks.
12
12
 
13
13
  ## [โ€‹](https://code.claude.com/docs/en/hooks\#hook-lifecycle) Hook lifecycle
14
14
 
15
- Hooks fire at specific points during a Claude Code session. When an event fires and a matcher matches, Claude Code passes JSON context about the event to your hook handler. For command hooks, input arrives on stdin. For HTTP hooks, it arrives as the POST request body. Your handler can then inspect the input, take action, and optionally return a decision.Events fall into three cadences:
15
+ Claude Code runs hooks at specific points during a session. When an event fires and a matcher matches, Claude Code passes JSON context about the event to your hook handler. For command hooks, input arrives on stdin. For HTTP hooks, it arrives as the POST request body. Your handler can then inspect the input, take action, and optionally return a decision.Events fall into three cadences:
16
16
 
17
17
  - once per session: `SessionStart` and `SessionEnd`
18
18
  - once per turn: `UserPromptSubmit`, `Stop`, and `StopFailure`
19
- - on every tool call inside the agentic loop: `PreToolUse` and `PostToolUse`
19
+ - on every tool call inside the agentic loop: `PreToolUse` and `PostToolUse`, except [`EndConversation`](https://code.claude.com/docs/en/tools-reference#endconversation-tool-behavior) calls, which skip both
20
20
 
21
- ![Hook lifecycle diagram showing optional Setup feeding into SessionStart, then a per-turn loop containing UserPromptSubmit, UserPromptExpansion for slash commands, the nested agentic loop (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), and Stop or StopFailure, followed by TeammateIdle, PreCompact, PostCompact, and SessionEnd, with Elicitation and ElicitationResult nested inside MCP tool execution, PermissionDenied as a side branch from PermissionRequest for auto-mode denials, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, and FileChanged as standalone async events, and MessageDisplay as a display-only event that runs while assistant message text streams](https://mintcdn.com/claude-code/uLsR38F1U_5zPppm/images/hooks-lifecycle.svg?fit=max&auto=format&n=uLsR38F1U_5zPppm&q=85&s=fbdbd78ad9f474da7d344879341341f0)
21
+ ![Hook lifecycle diagram showing optional Setup feeding into SessionStart, then a per-turn loop containing UserPromptSubmit, UserPromptExpansion for slash commands, the nested agentic loop (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), and Stop or StopFailure, followed by TeammateIdle, PreCompact, PostCompact, and SessionEnd, with Elicitation and ElicitationResult nested inside MCP tool execution, PermissionDenied as a side branch from PermissionRequest for auto-mode denials, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged, and DirectoryAdded as standalone async events, and MessageDisplay as a display-only event that runs while assistant message text streams](https://mintcdn.com/claude-code/jhXrDR5TrSZ5hgXM/images/hooks-lifecycle.svg?fit=max&auto=format&n=jhXrDR5TrSZ5hgXM&q=85&s=3ca47113d5956460e6e4611b8dbc63b7)![Hook lifecycle diagram showing optional Setup feeding into SessionStart, then a per-turn loop containing UserPromptSubmit, UserPromptExpansion for slash commands, the nested agentic loop (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), and Stop or StopFailure, followed by TeammateIdle, PreCompact, PostCompact, and SessionEnd, with Elicitation and ElicitationResult nested inside MCP tool execution, PermissionDenied as a side branch from PermissionRequest for auto-mode denials, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged, and DirectoryAdded as standalone async events, and MessageDisplay as a display-only event that runs while assistant message text streams](https://mintcdn.com/claude-code/jhXrDR5TrSZ5hgXM/images/hooks-lifecycle-dark.svg?fit=max&auto=format&n=jhXrDR5TrSZ5hgXM&q=85&s=0ffe95014d33411538778a66e4173973)
22
22
 
23
23
  The table below summarizes when each event fires. The [Hook events](https://code.claude.com/docs/en/hooks#hook-events) section documents the full input schema and decision control options for each one.
24
24
 
@@ -29,7 +29,7 @@ The table below summarizes when each event fires. The [Hook events](https://code
29
29
  | `UserPromptSubmit` | When you submit a prompt, before Claude processes it |
30
30
  | `UserPromptExpansion` | When a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion |
31
31
  | `PreToolUse` | Before a tool call executes. Can block it |
32
- | `PermissionRequest` | When a permission dialog appears |
32
+ | `PermissionRequest` | When a tool call needs a permission decision |
33
33
  | `PermissionDenied` | When a tool call is denied by the auto mode classifier. Return `{retry: true}` to tell the model it may retry the denied tool call |
34
34
  | `PostToolUse` | After a tool call succeeds |
35
35
  | `PostToolUseFailure` | After a tool call fails |
@@ -46,9 +46,10 @@ The table below summarizes when each event fires. The [Hook events](https://code
46
46
  | `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 |
47
47
  | `ConfigChange` | When a configuration file changes during a session |
48
48
  | `CwdChanged` | When the working directory changes, for example when Claude executes a `cd` command. Useful for reactive environment management with tools like direnv |
49
+ | `DirectoryAdded` | When a working directory is added mid-session via `/add-dir` or the SDK `register_repo_root` control request |
49
50
  | `FileChanged` | When a watched file changes on disk. The `matcher` field specifies which filenames to watch |
50
- | `WorktreeCreate` | When a worktree is being created via `--worktree` or `isolation: "worktree"`. Replaces default git behavior |
51
- | `WorktreeRemove` | When a worktree is being removed, either at session exit or when a subagent finishes |
51
+ | `WorktreeCreate` | When a worktree is being created via `--worktree`, `isolation: "worktree"`, or for a background session. Replaces default git behavior |
52
+ | `WorktreeRemove` | When a worktree is being removed at session exit, when a subagent finishes, or when you delete a background session |
52
53
  | `PreCompact` | Before context compaction |
53
54
  | `PostCompact` | After context compaction completes |
54
55
  | `Elicitation` | When an MCP server requests user input during a tool call |
@@ -57,7 +58,14 @@ The table below summarizes when each event fires. The [Hook events](https://code
57
58
 
58
59
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#how-a-hook-resolves) How a hook resolves
59
60
 
60
- To see how these pieces fit together, consider this `PreToolUse` hook that blocks destructive shell commands. The `matcher` narrows to Bash tool calls and the `if` condition narrows further to Bash subcommands matching `rm *`, so `block-rm.sh` only spawns when both filters match:
61
+ To see how these pieces fit together, consider this `PreToolUse` hook that blocks destructive shell commands.
62
+
63
+ - macOS/Linux
64
+
65
+ - Windows (PowerShell)
66
+
67
+
68
+ The `matcher` narrows to Bash tool calls and the `if` condition narrows further to Bash subcommands matching `rm *`, so `block-rm.sh` only spawns when both filters match:
61
69
 
62
70
  ```
63
71
  {
@@ -79,7 +87,7 @@ To see how these pieces fit together, consider this `PreToolUse` hook that block
79
87
  }
80
88
  ```
81
89
 
82
- The script reads the JSON input from stdin, extracts the command, and returns a `permissionDecision` of `"deny"` if it contains `rm -rf`:
90
+ The script reads the JSON input from stdin, extracts the command, and returns a `permissionDecision` of `"deny"` if it contains `rm -rf`. Save it to `.claude/hooks/block-rm.sh` in your project and make it executable with `chmod +x .claude/hooks/block-rm.sh` so Claude Code can run it:
83
91
 
84
92
  ```
85
93
  #!/bin/bash
@@ -99,13 +107,73 @@ else
99
107
  fi
100
108
  ```
101
109
 
102
- Now suppose Claude Code decides to run `Bash "rm -rf /tmp/build"`. Hereโ€™s what happens:
110
+ This script, like the other Bash examples on this page that parse JSON input, uses `jq`, so install `jq` and make sure it is on your `PATH` before trying them.
103
111
 
104
- ![Diagram of hook resolution: PreToolUse fires, the matcher checks for a Bash match, then the if condition checks for a Bash(rm *) match. If both match, the hook command runs and returns permissionDecision deny, so the tool call is blocked and Claude Code continues. If either check fails to match, the hook is skipped and the tool call is allowed to proceed.](https://mintcdn.com/claude-code/ikqp3_70mqIahteV/images/hook-resolution.svg?fit=max&auto=format&n=ikqp3_70mqIahteV&q=85&s=be0bf3053550c26de5f54cd64674c197)
112
+ The matcher `Bash|PowerShell` covers the [PowerShell tool](https://code.claude.com/docs/en/hooks#powershell) as well as Bash. A single `if` rule matches only one toolโ€™s calls, so each tool gets its own handler: the first narrows to Bash subcommands matching `rm *`, the second to PowerShell commands matching `Remove-Item *`. Both run the same script through `powershell.exe`:
105
113
 
106
- 1
114
+ ```
115
+ {
116
+ "hooks": {
117
+ "PreToolUse": [\
118
+ {\
119
+ "matcher": "Bash|PowerShell",\
120
+ "hooks": [\
121
+ {\
122
+ "type": "command",\
123
+ "if": "Bash(rm *)",\
124
+ "command": "powershell.exe",\
125
+ "args": [\
126
+ "-NoProfile",\
127
+ "-ExecutionPolicy",\
128
+ "Bypass",\
129
+ "-File",\
130
+ "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"\
131
+ ]\
132
+ },\
133
+ {\
134
+ "type": "command",\
135
+ "if": "PowerShell(Remove-Item *)",\
136
+ "command": "powershell.exe",\
137
+ "args": [\
138
+ "-NoProfile",\
139
+ "-ExecutionPolicy",\
140
+ "Bypass",\
141
+ "-File",\
142
+ "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"\
143
+ ]\
144
+ }\
145
+ ]\
146
+ }\
147
+ ]
148
+ }
149
+ }
150
+ ```
151
+
152
+ The `-NoProfile` flag skips loading your PowerShell profile so the hook starts fast, and `-ExecutionPolicy Bypass` lets PowerShell run the local script file.The script reads the JSON input from stdin, extracts the command, and returns a `permissionDecision` of `"deny"` if it contains `rm -rf` or `Remove-Item` followed by `-Recurse`. Save it to `.claude/hooks/block-rm.ps1` in your project:
107
153
 
108
- [Navigate to header](https://code.claude.com/docs/en/hooks#)
154
+ ```
155
+ # .claude/hooks/block-rm.ps1
156
+ $callInput = [Console]::In.ReadToEnd() | ConvertFrom-Json
157
+ $command = $callInput.tool_input.command
158
+
159
+ if ($command -match 'rm -rf|Remove-Item.*-Recurse') {
160
+ @{
161
+ hookSpecificOutput = @{
162
+ hookEventName = "PreToolUse"
163
+ permissionDecision = "deny"
164
+ permissionDecisionReason = "Destructive command blocked by hook"
165
+ }
166
+ } | ConvertTo-Json
167
+ } else {
168
+ exit 0 # no decision; normal permission flow applies
169
+ }
170
+ ```
171
+
172
+ Now suppose Claude Code decides to run `Bash "rm -rf /tmp/build"` against the macOS/Linux config. Hereโ€™s what happens:
173
+
174
+ ![Diagram of hook resolution: PreToolUse fires, the matcher checks for a Bash match, then the if condition checks for a Bash(rm *) match. If both match, the hook command runs and returns permissionDecision deny, so the tool call is blocked and Claude Code continues. If either check fails to match, the hook is skipped and the tool call is allowed to proceed.](https://mintcdn.com/claude-code/ikqp3_70mqIahteV/images/hook-resolution.svg?fit=max&auto=format&n=ikqp3_70mqIahteV&q=85&s=be0bf3053550c26de5f54cd64674c197)![Diagram of hook resolution: PreToolUse fires, the matcher checks for a Bash match, then the if condition checks for a Bash(rm *) match. If both match, the hook command runs and returns permissionDecision deny, so the tool call is blocked and Claude Code continues. If either check fails to match, the hook is skipped and the tool call is allowed to proceed.](https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/hook-resolution-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=e80af91f8507cee6bd51ac3c2dd92f63)
175
+
176
+ 1
109
177
 
110
178
  Event fires
111
179
 
@@ -117,24 +185,18 @@ The `PreToolUse` event fires. Claude Code sends the tool input as JSON on stdin
117
185
 
118
186
  2
119
187
 
120
- [Navigate to header](https://code.claude.com/docs/en/hooks#)
121
-
122
188
  Matcher checks
123
189
 
124
190
  The matcher `"Bash"` matches the tool name, so this hook group activates. If you omit the matcher or use `"*"`, the group activates on every occurrence of the event.
125
191
 
126
192
  3
127
193
 
128
- [Navigate to header](https://code.claude.com/docs/en/hooks#)
129
-
130
194
  If condition checks
131
195
 
132
196
  The `if` condition `"Bash(rm *)"` matches because `rm -rf /tmp/build` is a subcommand matching `rm *`, so this handler spawns. If the command had been `npm test`, the `if` check would fail and `block-rm.sh` would never run, avoiding the process spawn overhead. The `if` field is optional; without it, every handler in the matched group runs.
133
197
 
134
198
  4
135
199
 
136
- [Navigate to header](https://code.claude.com/docs/en/hooks#)
137
-
138
200
  Hook handler runs
139
201
 
140
202
  The script inspects the full command and finds `rm -rf`, so it prints a decision to stdout:
@@ -153,8 +215,6 @@ If the command had been a safer `rm` variant like `rm file.txt`, the script woul
153
215
 
154
216
  5
155
217
 
156
- [Navigate to header](https://code.claude.com/docs/en/hooks#)
157
-
158
218
  Claude Code acts on the result
159
219
 
160
220
  Claude Code reads the JSON decision, blocks the tool call, and shows Claude the reason.
@@ -181,12 +241,15 @@ Where you define a hook determines its scope:
181
241
  | --- | --- | --- |
182
242
  | `~/.claude/settings.json` | All your projects | No, local to your machine |
183
243
  | `.claude/settings.json` | Single project | Yes, can be committed to the repo |
184
- | `.claude/settings.local.json` | Single project | No, gitignored when Claude Code creates it |
244
+ | `.claude/settings.local.json` | Single project | No, gitignored when Claude Code saves a setting to it |
185
245
  | Managed policy settings | Organization-wide | Yes, admin-controlled |
186
246
  | [Plugin](https://code.claude.com/docs/en/plugins)`hooks/hooks.json` | When plugin is enabled | Yes, bundled with the plugin |
187
247
  | [Skill](https://code.claude.com/docs/en/skills) or [agent](https://code.claude.com/docs/en/sub-agents) frontmatter | While the component is active | Yes, defined in the component file |
188
248
 
189
- For details on settings file resolution, see [settings](https://code.claude.com/docs/en/settings).Enterprise administrators can use `allowManagedHooksOnly` to block user, project, and plugin hooks. Hooks from plugins force-enabled in managed settings `enabledPlugins` are exempt, so administrators can distribute vetted hooks through an organization marketplace. See [Hook configuration](https://code.claude.com/docs/en/settings#hook-configuration).
249
+ Cloud sessions on [Claude Code on the web](https://code.claude.com/docs/en/claude-code-on-the-web) donโ€™t read your local `~/.claude/settings.json`; hooks there come from the repo and from your organizationโ€™s server-managed settings. See [what carries over from your setup](https://code.claude.com/docs/en/cloud-environments#what-carries-over-from-your-setup) for which files reach a cloud session.For details on settings file resolution, see [settings](https://code.claude.com/docs/en/settings).Hooks from settings files, managed policy settings, and plugins also run inside [subagents](https://code.claude.com/docs/en/sub-agents). When a subagent calls a tool, tool events such as `PreToolUse` and `PostToolUse` fire the same configured hooks as in the main conversation, and the input carries the `agent_id` and `agent_type` [common input fields](https://code.claude.com/docs/en/hooks#common-input-fields) that identify the subagent.Enterprise administrators can use `allowManagedHooksOnly` to block user, project, and plugin hooks. Hooks from plugins force-enabled in managed settings `enabledPlugins` are exempt. See [Hook configuration](https://code.claude.com/docs/en/settings#hook-configuration).Hook entries merge across settings levels rather than replacing each other: user, project, and local settings add their own hooks without removing managed ones, and the [`disableAllHooks`](https://code.claude.com/docs/en/hooks#disable-or-remove-hooks) setting canโ€™t disable managed hooks from outside managed settings.The [HTTP hook allowlists](https://code.claude.com/docs/en/settings#hook-configuration) apply to hooks from every source, including managed policy settings:
250
+
251
+ - `allowedHttpHookUrls`: when defined at any settings level, Claude Code runs an HTTP hook handler only if its URL matches the merged allowlist
252
+ - `httpHookAllowedEnvVars`: when defined, Claude Code interpolates only the environment variables on that list into hook headers
190
253
 
191
254
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#matcher-patterns) Matcher patterns
192
255
 
@@ -203,7 +266,7 @@ A matcher on the regular-expression path is tested with JavaScriptโ€™s `RegExp.p
203
266
  | Event | What the matcher filters | Example matcher values |
204
267
  | --- | --- | --- |
205
268
  | `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `PermissionDenied` | tool name | `Bash`, `Edit|Write`, `mcp__.*` |
206
- | `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact` |
269
+ | `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact`, `fork` |
207
270
  | `Setup` | which CLI flag triggered setup | `init`, `maintenance` |
208
271
  | `SessionEnd` | why the session ended | `clear`, `resume`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` |
209
272
  | `Notification` | notification type | `permission_prompt`, `idle_prompt`, `auth_success`, `elicitation_dialog`, `elicitation_complete`, `elicitation_response`, `agent_needs_input`, `agent_completed` |
@@ -212,6 +275,7 @@ A matcher on the regular-expression path is tested with JavaScriptโ€™s `RegExp.p
212
275
  | `SubagentStop` | agent type | same values as `SubagentStart` |
213
276
  | `ConfigChange` | configuration source | `user_settings`, `project_settings`, `local_settings`, `policy_settings`, `skills` |
214
277
  | `CwdChanged` | no matcher support | always fires on every directory change |
278
+ | `DirectoryAdded` | how the directory was added | `slash_command`, `register_repo_root` |
215
279
  | `FileChanged` | literal filenames to watch (see [FileChanged](https://code.claude.com/docs/en/hooks#filechanged)) | `.envrc|.env` |
216
280
  | `StopFailure` | error type | `rate_limit`, `overloaded`, `authentication_failed`, `oauth_org_not_allowed`, `billing_error`, `invalid_request`, `model_not_found`, `server_error`, `max_output_tokens`, `unknown` |
217
281
  | `InstructionsLoaded` | load reason | `session_start`, `nested_traversal`, `path_glob_match`, `include`, `compact` |
@@ -240,7 +304,7 @@ The matcher runs against a field from the [JSON input](https://code.claude.com/d
240
304
  }
241
305
  ```
242
306
 
243
- `UserPromptSubmit`, `PostToolBatch`, `Stop`, `TeammateIdle`, `TaskCreated`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove`, `MessageDisplay`, and `CwdChanged` donโ€™t support matchers and always fire on every occurrence. If you add a `matcher` field to these events, it is silently ignored.For tool events, you can filter more narrowly by setting the [`if` field](https://code.claude.com/docs/en/hooks#common-fields) on individual hook handlers. `if` uses [permission rule syntax](https://code.claude.com/docs/en/permissions) to match against the tool name and arguments together, so `"Bash(git *)"` runs when any subcommand of the Bash input matches `git *` and `"Edit(*.ts)"` runs only for TypeScript files.
307
+ If you add a `matcher` field to an event without matcher support, it is silently ignored.For tool events, you can filter more narrowly by setting the [`if` field](https://code.claude.com/docs/en/hooks#common-fields) on individual hook handlers. `if` uses [permission rule syntax](https://code.claude.com/docs/en/permissions) to match against the tool name and arguments together, so `"Bash(git *)"` runs when any subcommand of the Bash input matches `git *` and `"Edit(*.ts)"` runs only for TypeScript files.
244
308
 
245
309
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#match-mcp-tools) Match MCP tools
246
310
 
@@ -295,7 +359,7 @@ Each object in the inner `hooks` array is a hook handler: the shell command, HTT
295
359
  - **[Prompt hooks](https://code.claude.com/docs/en/hooks#prompt-and-agent-hook-fields)** (`type: "prompt"`): send a prompt to a Claude model for single-turn evaluation. The model returns a yes/no decision as JSON. See [Prompt-based hooks](https://code.claude.com/docs/en/hooks#prompt-based-hooks).
296
360
  - **[Agent hooks](https://code.claude.com/docs/en/hooks#prompt-and-agent-hook-fields)** (`type: "agent"`): spawn a subagent that can use tools like Read, Grep, and Glob to verify conditions before returning a decision. Agent hooks are experimental and may change. See [Agent-based hooks](https://code.claude.com/docs/en/hooks#agent-based-hooks).
297
361
 
298
- All matching hooks run in parallel, and identical handlers are deduplicated automatically. Command hooks are deduplicated by command string and `args`, and HTTP hooks are deduplicated by URL.Handlers run in the current directory with Claude Codeโ€™s environment. The `$CLAUDE_CODE_REMOTE` environment variable is set to `"true"` in remote web environments and not set in the local CLI. As of v2.1.199, [`$CLAUDE_CODE_BRIDGE_SESSION_ID`](https://code.claude.com/docs/en/env-vars) is set to the [Remote Control](https://code.claude.com/docs/en/remote-control) session ID while the local session has an active Remote Control connection.
362
+ All matching hooks run in parallel. If you define the same handler in more than one settings file, it runs once. A pluginโ€™s or skillโ€™s copy of the same handler stays separate.Handlers run in the current directory with Claude Codeโ€™s environment. The `$CLAUDE_CODE_REMOTE` environment variable is set to `"true"` in remote web environments and not set in the local CLI. As of v2.1.199, [`$CLAUDE_CODE_BRIDGE_SESSION_ID`](https://code.claude.com/docs/en/env-vars) is set to the [Remote Control](https://code.claude.com/docs/en/remote-control) session ID while the local session has an active Remote Control connection.
299
363
 
300
364
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#common-fields) Common fields
301
365
 
@@ -305,11 +369,11 @@ These fields apply to all hook types:
305
369
  | --- | --- | --- |
306
370
  | `type` | yes | `"command"`, `"http"`, `"mcp_tool"`, `"prompt"`, or `"agent"` |
307
371
  | `if` | no | Permission rule syntax to filter when this hook runs, such as `"Bash(git *)"` or `"Edit(*.ts)"`. The hook command only runs if the tool call matches the pattern. See the [Bash matching table](https://code.claude.com/docs/en/hooks#bash-if-matching) below for how Bash patterns evaluate against subcommands, `$()`, and backticks. Only evaluated on tool events: `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, and `PermissionDenied`. On other events, a hook with `if` set never runs. Uses the same syntax as [permission rules](https://code.claude.com/docs/en/permissions) |
308
- | `timeout` | no | Seconds before canceling. Defaults: 600 for `command`, `http`, and `mcp_tool`; 30 for `prompt`; 60 for `agent`. [`UserPromptSubmit`](https://code.claude.com/docs/en/hooks#userpromptsubmit) lowers the `command`, `http`, and `mcp_tool` default to 30, and [`MessageDisplay`](https://code.claude.com/docs/en/hooks#messagedisplay) lowers it to 10 |
372
+ | `timeout` | no | Seconds before canceling. Defaults: 600 for `command`, `http`, and `mcp_tool`; 30 for `prompt`; 60 for `agent`. [`UserPromptSubmit`](https://code.claude.com/docs/en/hooks#userpromptsubmit) lowers the `command`, `http`, and `mcp_tool` default to 30, and [`MessageDisplay`](https://code.claude.com/docs/en/hooks#messagedisplay) lowers it to 10. [`SessionEnd`](https://code.claude.com/docs/en/hooks#sessionend) hooks share a 1.5-second budget; if your settings set a longer per-hook `timeout`, Claude Code raises the budget to match, up to 60 seconds |
309
373
  | `statusMessage` | no | Custom spinner message displayed while the hook runs |
310
374
  | `once` | no | If `true`, runs once per session then is removed. Only honored for hooks declared in [skill frontmatter](https://code.claude.com/docs/en/hooks#hooks-in-skills-and-agents); ignored in settings files and agent frontmatter |
311
375
 
312
- The `if` field holds exactly one permission rule. There is no `&&`, `||`, or list syntax for combining rules; to apply multiple conditions, define a separate hook handler for each.For Bash patterns, whether your hook command runs depends on the shape of the pattern and the Bash command Claude is invoking. Leading `VAR=value` assignments are stripped before matching.
376
+ The `if` field holds exactly one permission rule. There is no `&&`, `||`, or list syntax for combining rules; to apply multiple conditions, define a separate hook handler for each.In an `if` condition for a file tool, a single-segment directory pattern like `"Edit(src/**)"` matches only the `src` directory in the working directory and the files under it. To match a directory named `src` at any depth, write `"Edit(**/src/**)"`. Before v2.1.214, `"Edit(src/**)"` matched a directory named `src` at any depth under the working directory.For Bash patterns, whether your hook command runs depends on the shape of the pattern and the Bash command Claude is invoking. Leading `VAR=value` assignments are stripped before matching.
313
377
 
314
378
  | `if` pattern | Bash command | Hook runs? | Why |
315
379
  | --- | --- | --- | --- |
@@ -358,7 +422,7 @@ The equivalent shell form needs quoting to handle paths with spaces or special c
358
422
  }
359
423
  ```
360
424
 
361
- Both forms support the same [path placeholders](https://code.claude.com/docs/en/hooks#reference-scripts-by-path), and both export them as the environment variables `CLAUDE_PROJECT_DIR`, `CLAUDE_PLUGIN_ROOT`, and `CLAUDE_PLUGIN_DATA` on the spawned process, so a script can read `process.env.CLAUDE_PLUGIN_ROOT` regardless of how it was launched. Plugin hooks additionally substitute `${user_config.*}` values; see [User configuration](https://code.claude.com/docs/en/plugins-reference#user-configuration).
425
+ Both forms support the same [path placeholders](https://code.claude.com/docs/en/hooks#reference-scripts-by-path), and both export them as the environment variables `CLAUDE_PROJECT_DIR`, `CLAUDE_PLUGIN_ROOT`, and `CLAUDE_PLUGIN_DATA` on the spawned process, so a script can read `process.env.CLAUDE_PLUGIN_ROOT` regardless of how it was launched.Plugin hooks additionally substitute [`${user_config.*}`](https://code.claude.com/docs/en/plugins-reference#user-configuration) values, in exec form only: the value is substituted into `command` and into each `args` element as a plain string, so no shell re-parses it.A shell-form plugin hook whose `command` references `${user_config.*}` fails with an [error](https://code.claude.com/docs/en/errors#plugin-command-references-user-config) instead of running. To use an option value from a shell-form hook, read the `$CLAUDE_PLUGIN_OPTION_<KEY>` environment variable, such as `$CLAUDE_PLUGIN_OPTION_WEBHOOK_URL` for a `webhook_url` option, or set `args` to switch the hook to exec form. Before v2.1.207, shell-form plugin hook commands also substituted `${user_config.*}`.
362
426
 
363
427
  In exec form, `command` is the executable name or path only. If `command` is a bare name with no path separator and contains whitespace alongside `args`, Claude Code logs a warning because the spawn will fail: there is no executable named `node script.js`. Move the extra tokens into `args`. Absolute paths with spaces, such as `C:\Program Files\nodejs\node.exe`, are a single valid executable and donโ€™t trigger the warning.
364
428
 
@@ -372,7 +436,7 @@ In addition to the [common fields](https://code.claude.com/docs/en/hooks#common-
372
436
  | `headers` | no | Additional HTTP headers as key-value pairs. Values support environment variable interpolation using `$VAR_NAME` or `${VAR_NAME}` syntax. Only variables listed in `allowedEnvVars` are resolved |
373
437
  | `allowedEnvVars` | no | List of environment variable names that may be interpolated into header values. References to unlisted variables are replaced with empty strings. Required for any env var interpolation to work |
374
438
 
375
- Claude Code sends the hookโ€™s [JSON input](https://code.claude.com/docs/en/hooks#hook-input-and-output) as the POST request body with `Content-Type: application/json`. The response body uses the same [JSON output format](https://code.claude.com/docs/en/hooks#json-output) as command hooks.Error handling differs from command hooks: non-2xx responses, connection failures, and timeouts all produce non-blocking errors that allow execution to continue. To block a tool call or deny a permission, return a 2xx response with a JSON body containing `decision: "block"` or a `hookSpecificOutput` with `permissionDecision: "deny"`.This example sends `PreToolUse` events to a local validation service, authenticating with a token from the `MY_TOKEN` environment variable:
439
+ Claude Code sends the hookโ€™s [JSON input](https://code.claude.com/docs/en/hooks#hook-input-and-output) as the POST request body with `Content-Type: application/json`. The response body uses the same [JSON output format](https://code.claude.com/docs/en/hooks#json-output) as command hooks.Error handling differs from command hooks; see [HTTP response handling](https://code.claude.com/docs/en/hooks#http-response-handling).This example sends `PreToolUse` events to a local validation service, authenticating with a token from the `MY_TOKEN` environment variable:
376
440
 
377
441
  ```
378
442
  {
@@ -446,7 +510,7 @@ Use these placeholders to reference hook scripts relative to the project or plug
446
510
  - `${CLAUDE_PLUGIN_ROOT}`: the pluginโ€™s installation directory, for scripts bundled with a [plugin](https://code.claude.com/docs/en/plugins). Changes on each plugin update.
447
511
  - `${CLAUDE_PLUGIN_DATA}`: the pluginโ€™s [persistent data directory](https://code.claude.com/docs/en/plugins-reference#persistent-data-directory), for dependencies and state that should survive plugin updates.
448
512
 
449
- Prefer [exec form](https://code.claude.com/docs/en/hooks#exec-form-and-shell-form) for any hook that references a path placeholder. Exec form passes each `args` element as one argument with no shell tokenization, so paths with spaces or special characters need no quoting. In shell form, wrap each placeholder in double quotes.
513
+ Prefer [exec form](https://code.claude.com/docs/en/hooks#exec-form-and-shell-form) for any hook that references a path placeholder. In shell form, wrap each placeholder in double quotes.
450
514
 
451
515
  - Project scripts
452
516
 
@@ -516,18 +580,18 @@ hooks:
516
580
  ---
517
581
  ```
518
582
 
519
- Agents use the same format in their YAML frontmatter.
583
+ Subagents use the same format in their YAML frontmatter.Frontmatter hooks in a project subagent run only after you accept the [workspace trust dialog](https://code.claude.com/docs/en/permissions#project-allow-rules-and-workspace-trust) for the folder the agent file came from; see [which scopes are exempt](https://code.claude.com/docs/en/sub-agents#hooks-in-subagent-frontmatter). Before v2.1.218, these hooks could run from folders you hadnโ€™t trusted.
520
584
 
521
585
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#the-/hooks-menu) The `/hooks` menu
522
586
 
523
587
  Type `/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.The menu displays all five hook types: `command`, `prompt`, `agent`, `http`, and `mcp_tool`. Each hook is labeled with a `[type]` prefix and a source indicating where it was defined:
524
588
 
525
- - `User`: from `~/.claude/settings.json`
526
- - `Project`: from `.claude/settings.json`
527
- - `Local`: from `.claude/settings.local.json`
528
- - `Plugin`: from a pluginโ€™s `hooks/hooks.json`
529
- - `Session`: registered in memory for the current session
530
- - `Built-in`: registered internally by Claude Code
589
+ - `User Settings`: from `~/.claude/settings.json`
590
+ - `Project Settings`: from `.claude/settings.json`
591
+ - `Local Settings`: from `.claude/settings.local.json`
592
+ - `Plugin Hooks`: from a pluginโ€™s `hooks/hooks.json`
593
+ - `Session Hooks`: registered in memory for the current session
594
+ - `Built-in Hooks`: registered internally by Claude Code
531
595
 
532
596
  Selecting 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.
533
597
 
@@ -558,9 +622,9 @@ When running with `--agent` or inside a subagent, two additional fields are incl
558
622
  | Field | Description |
559
623
  | --- | --- |
560
624
  | `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. |
561
- | `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. For [custom subagents](https://code.claude.com/docs/en/sub-agents), this is the `name` field from the agentโ€™s frontmatter, not the filename. For subagents shipped by a [plugin](https://code.claude.com/docs/en/plugins), this is the plugin-scoped identifier such as `my-plugin:reviewer`, not the bare frontmatter name. See [SubagentStart](https://code.claude.com/docs/en/hooks#subagentstart) for how to write a matcher against a plugin-scoped name. |
625
+ | `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. See [SubagentStart](https://code.claude.com/docs/en/hooks#subagentstart) for the values custom and plugin subagents report and how to write a matcher against a plugin-scoped name. |
562
626
 
563
- Only [`SessionStart`](https://code.claude.com/docs/en/hooks#sessionstart) hooks can receive a `model` field, and it is not guaranteed to be present. There is no `$CLAUDE_MODEL` environment variable. A hook process inherits the parent environment, so it can read `$ANTHROPIC_MODEL` if you set it in your shell, but that value doesnโ€™t change when you switch models with `/model` during a session.For example, a `PreToolUse` hook for a Bash command receives this on stdin:
627
+ Only [`SessionStart`](https://code.claude.com/docs/en/hooks#sessionstart) hooks can receive a `model` field, and it is not guaranteed to be present. There is no `$CLAUDE_MODEL` environment variable. A hook process inherits the parent environment, so it can read `$ANTHROPIC_MODEL` if you set it in your shell, but that value doesnโ€™t change when you switch models with `/model` during a session. One set of variables is not inherited: Claude Code [removes `OTEL_*` exporter variables from every subprocess it spawns](https://code.claude.com/docs/en/monitoring-usage#administrator-configuration), including hooks.For example, a `PreToolUse` hook for a Bash command receives this on stdin:
564
628
 
565
629
  ```
566
630
  {
@@ -572,21 +636,26 @@ Only [`SessionStart`](https://code.claude.com/docs/en/hooks#sessionstart) hooks
572
636
  "hook_event_name": "PreToolUse",
573
637
  "tool_name": "Bash",
574
638
  "tool_input": {
575
- "command": "npm test"
576
- }
639
+ "command": "npm test",
640
+ "description": "Run test suite",
641
+ "timeout": 120000,
642
+ "run_in_background": false
643
+ },
644
+ "tool_use_id": "toolu_01ABC123..."
577
645
  }
578
646
  ```
579
647
 
580
- The `tool_name` and `tool_input` fields are event-specific. Each [hook event](https://code.claude.com/docs/en/hooks#hook-events) section documents the additional fields for that event.
648
+ The `tool_name`, `tool_input`, and `tool_use_id` fields are event-specific. Each [hook event](https://code.claude.com/docs/en/hooks#hook-events) section documents the additional fields for that event.
581
649
 
582
650
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#exit-code-output) Exit code output
583
651
 
584
- The exit code from your hook command tells Claude Code whether the action should proceed, be blocked, or be ignored.**Exit 0** means success. Claude Code parses stdout for [JSON output fields](https://code.claude.com/docs/en/hooks#json-output). JSON output is only processed on exit 0. For most events, stdout is written to the debug log but not shown in the transcript. The exceptions are `UserPromptSubmit`, `UserPromptExpansion`, and `SessionStart`, where stdout is added as context that Claude can see and act on.**Exit 2** means a blocking error. Claude Code ignores stdout and any JSON in it. Instead, stderr text is fed back to Claude as an error message. The effect depends on the event: `PreToolUse` blocks the tool call, `UserPromptSubmit` rejects the prompt, and so on. See [exit code 2 behavior](https://code.claude.com/docs/en/hooks#exit-code-2-behavior-per-event) for the full list.**Any other exit code** is a non-blocking error for most hook events. The transcript shows a `<hook name> hook error` notice followed by the first line of stderr, so you can identify the cause without `--debug`. Execution continues and the full stderr is written to the debug log.For example, a hook command script that blocks dangerous Bash commands:
652
+ The exit code from your hook command tells Claude Code whether the action should proceed, be blocked, or be ignored.**Exit 0** means success. Claude Code parses stdout for [JSON output fields](https://code.claude.com/docs/en/hooks#json-output). JSON output is only processed on exit 0. For most events, stdout is written to the debug log but not shown in the transcript. The exceptions are `UserPromptSubmit`, `UserPromptExpansion`, and `SessionStart`, where stdout is added as context that Claude can see and act on.Stderr from a hook that exits 0 goes to the debug log only, never the transcript, and Claude never sees it. To read it yourself, enable [debug logging](https://code.claude.com/docs/en/hooks#debug-hooks). To surface a warning to Claude from a `PostToolUse` or `PostToolUseFailure` hook, exit 2 instead so [Claude sees the stderr](https://code.claude.com/docs/en/hooks#exit-code-2-behavior-per-event) even though the tool already ran.**Exit 2** means a blocking error. Claude Code ignores stdout and any JSON in it. Instead, stderr text is fed back to Claude as an error message. The effect depends on the event: `PreToolUse` blocks the tool call, `UserPromptSubmit` rejects the prompt, and so on. See [exit code 2 behavior](https://code.claude.com/docs/en/hooks#exit-code-2-behavior-per-event) for the full list.A hook that exits 2 while printing JSON that fails [JSON output](https://code.claude.com/docs/en/hooks#json-output) schema validation still blocks: Claude Code uses stderr as the blocking reason and records the validation failure in the debug log. Before v2.1.214, Claude Code treated that combination as a non-blocking error and the action proceeded.**Any other exit code** is a non-blocking error for most hook events. The action proceeds, and the transcript shows a `<hook name> hook error` notice followed by the first line of stderr, prefixed with `Failed with non-blocking status code:`. To capture the full stderr, enable [debug logging](https://code.claude.com/docs/en/hooks#debug-hooks).For example, a hook command script that blocks dangerous Bash commands:
585
653
 
586
654
  ```
587
655
  #!/bin/bash
588
656
  # Reads JSON input from stdin, checks the command
589
- command=$(jq -r '.tool_input.command' < /dev/stdin)
657
+ input=$(cat)
658
+ command=$(jq -r '.tool_input.command' <<<"$input")
590
659
 
591
660
  if [[ "$command" == rm* ]]; then
592
661
  echo "Blocked: rm commands are not allowed" >&2
@@ -625,6 +694,7 @@ Exit code 2 is the way a hook signals โ€œstop, donโ€™t do this.โ€ The effect de
625
694
  | `Setup` | No | Shows stderr to user only |
626
695
  | `SessionEnd` | No | Shows stderr to user only |
627
696
  | `CwdChanged` | No | Shows stderr to user only |
697
+ | `DirectoryAdded` | No | Stderr goes to the debug log; the directory is already added |
628
698
  | `FileChanged` | No | Shows stderr to user only |
629
699
  | `PreCompact` | Yes | Blocks compaction |
630
700
  | `PostCompact` | No | Shows stderr to user only |
@@ -635,7 +705,7 @@ Exit code 2 is the way a hook signals โ€œstop, donโ€™t do this.โ€ The effect de
635
705
  | `InstructionsLoaded` | No | Exit code is ignored |
636
706
  | `MessageDisplay` | No | The original text is displayed |
637
707
 
638
- For `SessionStart`, `Setup`, and `SubagentStart`, the exit code 2 stderr renders in the transcript as a `<hook name> hook error` notice, the same way a [non-blocking error](https://code.claude.com/docs/en/hooks#exit-code-output) does. Claude doesnโ€™t see it, and the session or subagent proceeds. For `SubagentStart`, the notice appears in the subagentโ€™s own transcript, not in the parent conversation.As of Claude Code v2.1.199, `SessionStart`, `Setup`, and `SubagentStart` show exit code 2 stderr in the transcript. Earlier versions wrote it to the debug log only.
708
+ For `SessionStart`, `Setup`, and `SubagentStart`, the exit code 2 stderr renders in the transcript as a `<hook name> hook error` notice, the same way a [non-blocking error](https://code.claude.com/docs/en/hooks#exit-code-output) does. Claude doesnโ€™t see it, and the session or subagent proceeds. For `SubagentStart`, the notice appears in the subagentโ€™s own transcript, not in the parent conversation.
639
709
 
640
710
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#http-response-handling) HTTP response handling
641
711
 
@@ -655,7 +725,7 @@ Exit codes only let you block or stay silent, but JSON output gives you finer-gr
655
725
 
656
726
  You must choose one approach per hook, not both: either use exit codes alone for signaling, or exit 0 and print JSON for structured control. Claude Code only processes JSON on exit 0. If you exit 2, any JSON is ignored.
657
727
 
658
- Your hookโ€™s stdout must contain only the JSON object. If your shell profile prints text on startup, it can interfere with JSON parsing. See [JSON validation failed](https://code.claude.com/docs/en/hooks-guide#json-validation-failed) in the troubleshooting guide.Hook output strings, including `additionalContext`, `systemMessage`, and plain stdout, are capped at 10,000 characters. Output that exceeds this limit is saved to a file and replaced with a preview and file path, the same way large tool results are handled.The JSON object supports three kinds of fields:
728
+ Your hookโ€™s stdout must contain only the JSON object. If your shell profile prints text on startup, it can interfere with JSON parsing. See [JSON validation failed](https://code.claude.com/docs/en/hooks-guide#json-validation-failed) in the troubleshooting guide.Hook output strings, including `additionalContext`, `systemMessage`, and plain stdout, are capped at 10,000 characters. Output that exceeds this limit is saved to a file and replaced with a preview and file path, the same way a large valid Bash result is handled under [Output limits](https://code.claude.com/docs/en/tools-reference#output-limits).The JSON object supports three kinds of fields:
659
729
 
660
730
  - **Universal fields** like `continue` work across all events. These are listed in the table below.
661
731
  - **Top-level `decision` and `reason`** are used by some events to block or provide feedback.
@@ -675,6 +745,8 @@ To stop Claude entirely regardless of event type:
675
745
  { "continue": false, "stopReason": "Build failed, fix errors before continuing" }
676
746
  ```
677
747
 
748
+ For `PreToolUse` and `PostToolUse` hooks, the stop applies even when the tool call fails or completes while Claude is still streaming a response.
749
+
678
750
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#emit-terminal-notifications) Emit terminal notifications
679
751
 
680
752
  The `terminalSequence` field requires Claude Code v2.1.141 or later.Hooks run without a controlling terminal, so writing escape sequences directly to `/dev/tty` fails. Instead, return the escape sequence in the `terminalSequence` field and Claude Code emits it for you through its own terminal write path. This is race-free, works inside tmux and GNU screen, and works on Windows where there is no `/dev/tty`.The field accepts a string of one or more allowlisted escape sequences:
@@ -727,7 +799,7 @@ When several hooks return `additionalContext` for the same event, Claude receive
727
799
  - **Conditional project rules**: which test command applies to the file just edited, which directories are read-only in this worktree
728
800
  - **External data**: open issues assigned to you, recent CI results, content fetched from an internal service
729
801
 
730
- For instructions that never change, prefer [CLAUDE.md](https://code.claude.com/docs/en/memory). It loads without running a script and is the standard place for static project conventions.Write the text as factual statements rather than imperative system instructions. Phrasing such as โ€œThe deployment target is productionโ€ or โ€œThis repo uses `bun test`โ€ reads as project information. Text framed as out-of-band system commands can trigger Claudeโ€™s prompt-injection defenses, which causes Claude to surface the text to you instead of treating it as context.Once injected, the text is saved in the session transcript. For mid-session events like `PostToolUse` or `UserPromptSubmit`, resuming with `--continue` or `--resume` replays the saved text rather than re-running the hook for past turns, so values like timestamps or commit SHAs become stale on resume. `SessionStart` hooks run again on resume with `source` set to `"resume"`, so they can refresh their context.
802
+ For instructions that never change, prefer [CLAUDE.md](https://code.claude.com/docs/en/memory). It loads without running a script and is the standard place for static project conventions.Write the text as factual statements rather than imperative system instructions. Phrasing such as โ€œThe deployment target is productionโ€ or โ€œThis repo uses `bun test`โ€ reads as project information. Text framed as out-of-band system commands can trigger Claudeโ€™s prompt-injection defenses, which causes Claude to surface the text to you instead of treating it as context.Claude Code saves the injected text in the session transcript. For mid-session events like `PostToolUse` or `UserPromptSubmit`, when you resume with `--continue` or `--resume`, Claude Code replays the saved text rather than re-running the hook for past turns, so values like timestamps or commit SHAs become stale. `SessionStart` hooks run again on resume with `source` set to `"resume"`, or `"fork"` if you added `--fork-session`, so they can refresh their context.
731
803
 
732
804
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#decision-control) Decision control
733
805
 
@@ -745,7 +817,7 @@ Not every event supports blocking or controlling behavior through JSON. The even
745
817
  | ElicitationResult | `hookSpecificOutput` | `action` (accept/decline/cancel), `content` (form field values override) |
746
818
  | MessageDisplay | `hookSpecificOutput` | `displayContent` replaces the displayed text on screen. Display-only: the transcript and what Claude sees keep the original |
747
819
  | SessionStart, Setup, SubagentStart | Context only | `hookSpecificOutput.additionalContext` adds context for Claude. SessionStart also accepts [`initialUserMessage`, `watchPaths`, `sessionTitle`, and `reloadSkills`](https://code.claude.com/docs/en/hooks#sessionstart-decision-control). No blocking or decision control |
748
- | WorktreeRemove, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, FileChanged | None | No decision control. Used for side effects like logging or cleanup |
820
+ | WorktreeRemove, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, DirectoryAdded, FileChanged | None | No decision control. Used for side effects like logging or cleanup |
749
821
 
750
822
  A few events can also rewrite content rather than only allow or block it:
751
823
 
@@ -763,7 +835,7 @@ For redaction or transformation use cases, intercept at `PreToolUse` for outboun
763
835
  - PermissionRequest
764
836
 
765
837
 
766
- Used by `UserPromptSubmit`, `UserPromptExpansion`, `PostToolUse`, `PostToolUseFailure`, `PostToolBatch`, `Stop`, `SubagentStop`, `ConfigChange`, and `PreCompact`. The only value is `"block"`. To allow the action to proceed, omit `decision` from your JSON, or exit 0 without any JSON at all:
838
+ The only value for `decision` is `"block"`. To allow the action to proceed, omit `decision` from your JSON, or exit 0 without any JSON at all:
767
839
 
768
840
  ```
769
841
  {
@@ -816,6 +888,9 @@ Runs when Claude Code starts a new session or resumes an existing session. Usefu
816
888
  | `resume` | `--resume`, `--continue`, or `/resume` |
817
889
  | `clear` | `/clear` |
818
890
  | `compact` | Auto or manual compaction |
891
+ | `fork` | A new session forked from an existing one: `--fork-session` with `--resume` or `--continue`, the `/fork` background copy, or `/branch` |
892
+
893
+ Before v2.1.214, forked sessions reported source `"resume"`.
819
894
 
820
895
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#sessionstart-input) SessionStart input
821
896
 
@@ -823,7 +898,7 @@ In addition to the [common input fields](https://code.claude.com/docs/en/hooks#c
823
898
 
824
899
  | Field | Description |
825
900
  | --- | --- |
826
- | `source` | How the session started: `"startup"` for new sessions, `"resume"` for resumed sessions, `"clear"` after `/clear`, or `"compact"` after compaction |
901
+ | `source` | How the session started: `"startup"` for new sessions, `"resume"` for resumed sessions, `"clear"` after `/clear`, `"compact"` after compaction, or `"fork"` for a new session forked from an existing one |
827
902
  | `model` | The active model identifier. It can be omitted, for example after `/clear` or when a session is restored through conversation recovery, so check for the field before reading it |
828
903
  | `agent_type` | The agent name, present when you start Claude Code with `claude --agent <name>` |
829
904
  | `session_title` | The current session title if one is already set, for example via `--name` or `/rename`. A hook that emits `sessionTitle` can check `session_title` first to avoid overwriting a title the user set explicitly |
@@ -847,7 +922,7 @@ Any text your hook script prints to stdout is added as context for Claude. In ad
847
922
  | --- | --- |
848
923
  | `additionalContext` | String added to Claudeโ€™s context at the start of the conversation, before the first prompt. See [Add context for Claude](https://code.claude.com/docs/en/hooks#add-context-for-claude) for how the text is delivered and what to put in it |
849
924
  | `initialUserMessage` | String used as the first user message of the session. Applies in [non-interactive mode](https://code.claude.com/docs/en/headless) with the `-p` flag, where it becomes the first turn even if no prompt is provided. If a prompt is provided, it follows as the next turn. Unlike `additionalContext`, which attaches to an existing turn, this creates the turn |
850
- | `sessionTitle` | Sets the session title, with the same effect as `/rename`. Use to name sessions automatically from the launch folder, git branch, or worktree name. Applies only when `source` is `"startup"` or `"resume"`; ignored on `"clear"` and `"compact"` |
925
+ | `sessionTitle` | Sets the session title, with the same effect as `/rename`. Use to name sessions automatically from the launch folder, git branch, or worktree name. Applies when `source` is `"startup"`, `"resume"`, or `"fork"`; ignored on `"clear"` and `"compact"` |
851
926
  | `watchPaths` | Array of absolute paths to watch for [FileChanged](https://code.claude.com/docs/en/hooks#filechanged) events during this session |
852
927
  | `reloadSkills` | Boolean. When `true`, Claude Code re-scans the [skill](https://code.claude.com/docs/en/skills) and command directories after the SessionStart hooks complete, so skills the hook installed are available in the same session, starting with the first prompt |
853
928
 
@@ -872,6 +947,8 @@ git -C ~/.claude/skills/team-skills pull --quiet 2>/dev/null || \
872
947
  echo '{"hookSpecificOutput": {"hookEventName": "SessionStart", "reloadSkills": true}}'
873
948
  ```
874
949
 
950
+ The repository URL is a placeholder; replace it with your own skills repository. With the placeholder, the clone fails and prints a `fatal:` message to stderr. Stderr from a SessionStart hook that exits 0 is informational only, so the `reloadSkills` request still applies.
951
+
875
952
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#persist-environment-variables) Persist environment variables
876
953
 
877
954
  SessionStart hooks have access to the `CLAUDE_ENV_FILE` environment variable, which provides a file path where you can persist environment variables for subsequent Bash commands.To set individual environment variables, write `export` statements to `CLAUDE_ENV_FILE`. Use append (`>>`) to preserve variables set by other hooks:
@@ -907,8 +984,6 @@ fi
907
984
  exit 0
908
985
  ```
909
986
 
910
- Any variables written to this file will be available in all subsequent Bash commands that Claude Code executes during the session.
911
-
912
987
  `CLAUDE_ENV_FILE` is available for SessionStart, [Setup](https://code.claude.com/docs/en/hooks#setup), [CwdChanged](https://code.claude.com/docs/en/hooks#cwdchanged), and [FileChanged](https://code.claude.com/docs/en/hooks#filechanged) hooks. Other hook types donโ€™t have access to this variable.
913
988
 
914
989
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#setup) Setup
@@ -920,7 +995,7 @@ Fires only when you launch Claude Code with `--init-only`, or with `--init` or `
920
995
  | `init` | `claude --init-only` or `claude -p --init` |
921
996
  | `maintenance` | `claude -p --maintenance` |
922
997
 
923
- `--init-only` runs Setup hooks and `SessionStart` hooks with the `startup` matcher, then exits without starting a conversation. `--init` and `--maintenance` fire Setup hooks only when combined with `-p`; in an interactive session those two flags donโ€™t currently fire Setup hooks.Because Setup doesnโ€™t fire on every launch, a plugin that needs a dependency installed canโ€™t rely on Setup alone. The practical pattern is to check for the dependency on first use and install on miss, for example a hook or skill that tests for `${CLAUDE_PLUGIN_DATA}/node_modules` and runs `npm install` if absent. See the [persistent data directory](https://code.claude.com/docs/en/plugins-reference#persistent-data-directory) for where to store installed dependencies.
998
+ When you run `claude --init-only`, Claude Code runs Setup hooks and `SessionStart` hooks with the `startup` matcher, then exits without starting a conversation.`--init` and `--maintenance` fire Setup hooks only when you combine them with `-p`. In an interactive session, those two flags donโ€™t currently fire Setup hooks.When you start or continue a conversation with `-p`, you also need to supply a prompt, as an argument or piped on stdin. You can skip the prompt when a `SessionStart` hook supplies [`initialUserMessage`](https://code.claude.com/docs/en/hooks#sessionstart-decision-control) or when you resume a session with a [deferred tool call](https://code.claude.com/docs/en/hooks#defer-a-tool-call-for-later).On success, `--init-only` prints nothing to the terminal. To confirm the hooks ran, start with `claude --debug-file <path> --init-only`, replacing `<path>` with a log file location, and check the log for the Setup and SessionStart hook entries.Because Setup doesnโ€™t fire on every launch, a plugin that needs a dependency installed canโ€™t rely on Setup alone. The practical pattern is to check for the dependency on first use and install on miss, for example a hook or skill that tests for `${CLAUDE_PLUGIN_DATA}/node_modules` and runs `npm install` if absent. See the [persistent data directory](https://code.claude.com/docs/en/plugins-reference#persistent-data-directory) for where to store installed dependencies.
924
999
 
925
1000
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#setup-input) Setup input
926
1001
 
@@ -992,7 +1067,7 @@ InstructionsLoaded hooks have no decision control. They canโ€™t block or modify
992
1067
 
993
1068
  Runs when the user submits a prompt, before Claude processes it. This allows you
994
1069
  to add additional context based on the prompt/conversation, validate prompts, or
995
- block certain types of prompts.`UserPromptSubmit` hooks have a default timeout of 30 seconds for `command`, `http`, and `mcp_tool` types, shorter than the 600-second default for those types on most other events. Because this hook runs before every prompt and blocks model processing until it completes, a stuck hook stalls the session. If your hook needs more time, set the `timeout` field in the hook entry.A `UserPromptSubmit` hook that reaches its timeout is canceled and its output, including any `additionalContext`, is discarded. The prompt still reaches Claude without that context. As of v2.1.196, the transcript shows a notice naming the hook, the timeout that fired, and that the output was discarded. Earlier versions cancel the hook with no notice.
1070
+ block certain types of prompts.`UserPromptSubmit` hooks have a default timeout of 30 seconds for `command`, `http`, and `mcp_tool` types, shorter than the 600-second default for those types on most other events. Because this hook runs before every prompt and blocks model processing until it completes, a stuck hook stalls the session. If your hook needs more time, set the `timeout` field in the hook entry.A `UserPromptSubmit` command, HTTP, or MCP tool hook that reaches its timeout is canceled and its output, including any `additionalContext`, is discarded. The prompt still reaches Claude without that context. The transcript shows a notice naming the hook, the timeout that fired, and that the output was discarded.An [Agent SDK callback hook](https://code.claude.com/docs/en/agent-sdk/hooks) on `UserPromptSubmit` that reaches its timeout blocks the prompt with a message naming the hook and the timeout, because a callback there can be acting as a policy gate that must not fail open. The session continues. Before v2.1.208, a callback timeout on that event ended the turn with an execution error.
996
1071
 
997
1072
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#userpromptsubmit-input) UserPromptSubmit input
998
1073
 
@@ -1160,8 +1235,6 @@ Save this script to `.claude/hooks/plain-display.sh` in your project and make it
1160
1235
  jq '{hookSpecificOutput: {hookEventName: "MessageDisplay", displayContent: (.delta | gsub("\\*\\*"; "") | gsub("`"; ""))}}'
1161
1236
  ```
1162
1237
 
1163
- The script needs `jq` on your `PATH`.
1164
-
1165
1238
  Register a command hook that runs the script through PowerShell:
1166
1239
 
1167
1240
  ```
@@ -1205,15 +1278,36 @@ Batches with no markdown pass through unchanged. If the script fails, for exampl
1205
1278
 
1206
1279
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#pretooluse) PreToolUse
1207
1280
 
1208
- Runs after Claude creates tool parameters and before processing the tool call. Matches on tool name: `Bash`, `Edit`, `Write`, `Read`, `Glob`, `Grep`, `Agent`, `WebFetch`, `WebSearch`, `AskUserQuestion`, `ExitPlanMode`, and any [MCP tool names](https://code.claude.com/docs/en/hooks#match-mcp-tools).
1281
+ Runs after Claude creates tool parameters and before processing the tool call. Matches on tool name: `Bash`, `PowerShell`, `Edit`, `Write`, `Read`, `Glob`, `Grep`, `Agent`, `WebFetch`, `WebSearch`, `AskUserQuestion`, `ExitPlanMode`, and any [MCP tool names](https://code.claude.com/docs/en/hooks#match-mcp-tools).
1209
1282
 
1210
- PreToolUse runs only when Claude calls a tool. Files you [reference with `@` in your prompt](https://code.claude.com/docs/en/common-workflows#reference-files-and-directories) are added without any tool call: Claude Code inserts their contents while building the prompt, so no PreToolUse hook fires for them, including hooks matching `Read`. To block specific paths from `@` references, use a [`Read` deny rule](https://code.claude.com/docs/en/permissions#read-and-edit) instead.
1283
+ PreToolUse runs only when Claude calls a tool. Files you [reference with `@` in your prompt](https://code.claude.com/docs/en/common-workflows#reference-files-and-directories) are added without any tool call: Claude Code inserts their contents while building the prompt, so no PreToolUse hook fires for them, including hooks matching `Read`. To block specific paths from `@` references, use a [`Read` deny rule](https://code.claude.com/docs/en/permissions#read-and-edit) instead.PreToolUse also doesnโ€™t fire for [`EndConversation`](https://code.claude.com/docs/en/tools-reference#endconversation-tool-behavior).
1211
1284
 
1212
- Use [PreToolUse decision control](https://code.claude.com/docs/en/hooks#pretooluse-decision-control) to allow, deny, ask, or defer the tool call.
1285
+ Use [PreToolUse decision control](https://code.claude.com/docs/en/hooks#pretooluse-decision-control) to allow, deny, ask, or defer the tool call.An [Agent SDK callback hook](https://code.claude.com/docs/en/agent-sdk/hooks) on `PreToolUse` that exceeds its timeout blocks the tool call, and Claude receives an error result naming the timeout. An explicit deny returned by another hook still takes precedence.
1213
1286
 
1214
1287
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#pretooluse-input) PreToolUse input
1215
1288
 
1216
- In addition to the [common input fields](https://code.claude.com/docs/en/hooks#common-input-fields), PreToolUse hooks receive `tool_name`, `tool_input`, and `tool_use_id`. The `tool_input` fields depend on the tool:
1289
+ In addition to the [common input fields](https://code.claude.com/docs/en/hooks#common-input-fields), PreToolUse hooks receive `tool_name`, `tool_input`, and `tool_use_id`.For the file tools `Write`, `Edit`, and `Read`, `tool_input.file_path` is always absolute:
1290
+
1291
+ - Claude Code expands `~` and relative paths before hooks run, so a hook that matches on paths canโ€™t be bypassed via `~` or a relative spelling of the same path
1292
+ - On Windows, the path arrives with backslash separators, even when your hook runs under Git Bash where `$PWD` looks like `/c/project`
1293
+ - A comparison written with forward slashes, such as a `/src/` check, never matches a backslash path, and the tool call proceeds as if the hook had nothing to block
1294
+ - Normalize separators before comparing: `FILE_PATH="${FILE_PATH//\\//}"` in Bash, or `file_path.replace("\\", "/")` in Python, then match a path segment such as `/src/` rather than anchoring with `^`, since the path is absolute
1295
+
1296
+ A `Write` call on Windows delivers:
1297
+
1298
+ ```
1299
+ {
1300
+ "hook_event_name": "PreToolUse",
1301
+ "tool_name": "Write",
1302
+ "tool_input": {
1303
+ "file_path": "C:\\project\\src\\index.ts",
1304
+ "content": "..."
1305
+ },
1306
+ ...
1307
+ }
1308
+ ```
1309
+
1310
+ The `tool_input` fields depend on the tool:
1217
1311
 
1218
1312
  ##### Bash
1219
1313
 
@@ -1223,9 +1317,26 @@ Executes shell commands.
1223
1317
  | --- | --- | --- | --- |
1224
1318
  | `command` | string | `"npm test"` | The shell command to execute |
1225
1319
  | `description` | string | `"Run test suite"` | Optional description of what the command does |
1320
+ | `timeout` | number | `120000` | Optional timeout in milliseconds. Values above the [maximum](https://code.claude.com/docs/en/tools-reference#bash-tool-behavior) are reduced to the maximum rather than rejected |
1321
+ | `run_in_background` | boolean | `false` | Whether to run the command in background |
1322
+
1323
+ ##### PowerShell
1324
+
1325
+ Executes PowerShell commands. See the [PowerShell tool](https://code.claude.com/docs/en/tools-reference#powershell-tool) for availability by platform.The fields match the Bash tool, with the command string in `command`:
1326
+
1327
+ | Field | Type | Example | Description |
1328
+ | --- | --- | --- | --- |
1329
+ | `command` | string | `"Get-ChildItem -Recurse"` | The PowerShell command to execute |
1330
+ | `description` | string | `"List files recursively"` | Optional description of what the command does |
1226
1331
  | `timeout` | number | `120000` | Optional timeout in milliseconds |
1227
1332
  | `run_in_background` | boolean | `false` | Whether to run the command in background |
1228
1333
 
1334
+ Match `Bash|PowerShell` in hooks that inspect shell commands, so they cover both tools:
1335
+
1336
+ - On Windows, wherever the PowerShell tool is enabled, Claude treats PowerShell as the primary shell and routes shell commands through it.
1337
+ - On Windows without Git Bash, the tool is enabled automatically and Claude Code doesnโ€™t register the Bash tool at all.
1338
+ - A hook that matches only `Bash` never fires there.
1339
+
1229
1340
  ##### Write
1230
1341
 
1231
1342
  Creates or overwrites a file.
@@ -1315,13 +1426,14 @@ In `PostToolUse`, `tool_response` for a completed Agent call carries the subagen
1315
1426
  | `status` | string | `"completed"` | `"completed"` for foreground subagents, `"async_launched"` for background subagents. As of v2.1.198, subagents run in the background by default, so an omitted `run_in_background` also produces `"async_launched"` |
1316
1427
  | `agentId` | string | `"a4d2c8f1e0b3a297"` | Identifier for the subagent run |
1317
1428
  | `content` | array | `[{"type": "text", "text": "Found 12 endpoints..."}]` | The subagentโ€™s final text blocks |
1318
- | `resolvedModel` | string | `"claude-sonnet-4-5"` | Model the subagent ran on, which may differ from the requested model. Requires Claude Code v2.1.174 or later |
1429
+ | `resolvedModel` | string | `"claude-sonnet-4-5"` | Model the subagent started on, which may differ from the requested model. Requires Claude Code v2.1.174 or later |
1430
+ | `modelsUsed` | array | `["claude-sonnet-4-5", "claude-haiku-4-5"]` | Models used in order, with consecutive repeats collapsed; set only when the model was swapped mid-run. Requires Claude Code v2.1.212 or later |
1319
1431
  | `totalTokens` | number | `12450` | Total tokens billed across the subagentโ€™s turns |
1320
1432
  | `totalDurationMs` | number | `48211` | Wall-clock duration of the subagent run |
1321
1433
  | `totalToolUseCount` | number | `7` | Count of tool calls the subagent made |
1322
1434
  | `usage` | object | `{"input_tokens": 8320, ...}` | Per-type token breakdown: `input_tokens`, `output_tokens`, `cache_creation_input_tokens`, `cache_read_input_tokens` |
1323
1435
 
1324
- For background subagents, the tool returns immediately after launching, so `tool_response` carries no usage fields. It has `status: "async_launched"`, `agentId`, `description`, `prompt`, `outputFile`, and `resolvedModel`.The `resolvedModel` field names the model the subagent actually runs on, which can differ from the `model` value in `tool_input`, such as when `availableModels` or another override applies. It requires Claude Code v2.1.174 or later.
1436
+ For background subagents, the tool returns when the task moves to the background, so `tool_response` carries no usage fields: a background launch returns immediately, and a foreground task that Claude Code backgrounds mid-run returns at that transition. It has `status: "async_launched"`, `agentId`, `description`, `prompt`, `outputFile`, and `resolvedModel`.On a `completed` response, `resolvedModel` names the model the subagent started on, which can differ from the `model` value in `tool_input`, such as when `availableModels` or another override applies. It requires Claude Code v2.1.174 or later. On an `async_launched` response, `resolvedModel` names the model in use when the agent moved to the background, so a swap that happened before backgrounding is reflected there. `modelsUsed` and the backgrounding-time `resolvedModel` behavior require Claude Code v2.1.212 or later.
1325
1437
 
1326
1438
  ##### AskUserQuestion
1327
1439
 
@@ -1350,12 +1462,12 @@ In `PostToolUse`, `tool_response` is an object with `plan` and `filePath` fields
1350
1462
 
1351
1463
  | Field | Description |
1352
1464
  | --- | --- |
1353
- | `permissionDecision` | `"allow"` skips the permission prompt, except for [tools that require user interaction](https://code.claude.com/docs/en/hooks#pretooluse-decision-control). `"deny"` prevents the tool call. `"ask"` prompts the user to confirm. `"defer"` exits gracefully so the tool can be resumed later. [Deny and ask rules](https://code.claude.com/docs/en/permissions#manage-permissions) are still evaluated regardless of what the hook returns |
1465
+ | `permissionDecision` | `"allow"` skips the permission prompt, except for [tools that require user interaction](https://code.claude.com/docs/en/hooks#pretooluse-decision-control) and connector tools [your organization set to `ask`](https://code.claude.com/docs/en/mcp#organization-controls-on-connector-tools). `"deny"` prevents the tool call. `"ask"` prompts the user to confirm. `"defer"` exits gracefully so the tool can be resumed later. [Deny and ask rules](https://code.claude.com/docs/en/permissions#manage-permissions) are still evaluated regardless of what the hook returns |
1354
1466
  | `permissionDecisionReason` | For `"allow"` and `"ask"`, shown to the user but not Claude. For `"deny"`, shown to Claude. For `"defer"`, ignored |
1355
1467
  | `updatedInput` | Modifies the toolโ€™s input parameters before execution. Replaces the entire input object, so include unchanged fields alongside modified ones. Combine with `"allow"` to auto-approve, or `"ask"` to show the modified input to the user. For `"defer"`, ignored |
1356
1468
  | `additionalContext` | String added to Claudeโ€™s context alongside the tool result. Ignored when `permissionDecision` is `"defer"`. See [Add context for Claude](https://code.claude.com/docs/en/hooks#add-context-for-claude) |
1357
1469
 
1358
- When multiple PreToolUse hooks return different decisions, precedence is `deny` \> `defer` \> `ask` \> `allow`.When 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.
1470
+ When multiple PreToolUse hooks return different decisions, precedence is `deny` \> `defer` \> `ask` \> `allow`.When 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.A hookโ€™s `"ask"` also forces a permission prompt in [auto mode](https://code.claude.com/docs/en/permission-modes#eliminate-prompts-with-auto-mode): the classifier can still deny the tool call, but it canโ€™t approve the call silently. Before v2.1.211, the classifier could approve a Bash command running outside the [sandbox](https://code.claude.com/docs/en/sandboxing) without showing the prompt the hook requested; the classifier still applied its own safety rules to that command, and a hook `"deny"` was always honored.
1359
1471
 
1360
1472
  ```
1361
1473
  {
@@ -1377,11 +1489,7 @@ PreToolUse previously used top-level `decision` and `reason` fields, but these a
1377
1489
 
1378
1490
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#defer-a-tool-call-for-later) Defer a tool call for later
1379
1491
 
1380
- `"defer"` is for integrations that run `claude -p` as a subprocess and read its JSON output, such as an Agent SDK app or a custom UI built on top of Claude Code. It lets that calling process pause Claude at a tool call, collect input through its own interface, and resume where it left off. Claude Code honors this value only in [non-interactive mode](https://code.claude.com/docs/en/headless) with the `-p` flag. In interactive sessions it logs a warning and ignores the hook result.
1381
-
1382
- The `defer` value requires Claude Code v2.1.89 or later. Earlier versions donโ€™t recognize it and the tool proceeds through the normal permission flow.
1383
-
1384
- The `AskUserQuestion` tool is the typical case: Claude wants to ask the user something, but there is no terminal to answer in. The round trip works like this:
1492
+ `"defer"` is for integrations that run `claude -p` as a subprocess and read its JSON output, such as an Agent SDK app or a custom UI built on top of Claude Code. It lets that calling process pause Claude at a tool call, collect input through its own interface, and resume where it left off. Claude Code honors this value only in [non-interactive mode](https://code.claude.com/docs/en/headless) with the `-p` flag. In interactive sessions it logs a warning and ignores the hook result.The `AskUserQuestion` tool is the typical case: Claude wants to ask the user something, but there is no terminal to answer in. The round trip works like this:
1385
1493
 
1386
1494
  1. Claude calls `AskUserQuestion`. The `PreToolUse` hook fires.
1387
1495
  2. The hook returns `permissionDecision: "defer"`. The tool doesnโ€™t execute. The process exits with `stop_reason: "tool_deferred"` and the pending tool call preserved in the transcript.
@@ -1407,16 +1515,16 @@ The `deferred_tool_use` field carries the toolโ€™s `id`, `name`, and `input`. Th
1407
1515
 
1408
1516
  There is no timeout or retry limit. The session remains on disk until you resume it, subject to the [`cleanupPeriodDays`](https://code.claude.com/docs/en/settings#available-settings) retention sweep that deletes session files after 30 days by default. If the answer is not ready when you resume, the hook can return `"defer"` again and the process exits the same way. The calling process controls when to break the loop by eventually returning `"allow"` or `"deny"` from the hook.`"defer"` only works when Claude makes a single tool call in the turn. If Claude makes several tool calls at once, `"defer"` is ignored with a warning and the tool proceeds through the normal permission flow. The constraint exists because resume can only re-run one tool: there is no way to defer one call from a batch without leaving the others unresolved.If the deferred tool is no longer available when you resume, the process exits with `stop_reason: "tool_deferred_unavailable"` and `is_error: true` before the hook fires. This happens when an MCP server that provided the tool is not connected for the resumed session. The `deferred_tool_use` payload is still included so you can identify which tool went missing.
1409
1517
 
1410
- `--resume` restores the permission mode that was active when the tool was deferred, so you donโ€™t need to pass `--permission-mode` again. The exceptions are `plan` and `bypassPermissions`, which are never carried over. Passing `--permission-mode` explicitly on resume overrides the restored value.
1518
+ `--resume` restores the permission mode that was active when the tool was deferred, so you donโ€™t need to pass `--permission-mode` again. The exceptions are `plan` and `bypassPermissions`, which are never carried over, and `auto`, which is restored only when your account still meets the [auto mode requirements](https://code.claude.com/docs/en/permission-modes#eliminate-prompts-with-auto-mode). Passing `--permission-mode` explicitly on resume overrides the restored value.
1411
1519
 
1412
1520
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#permissionrequest) PermissionRequest
1413
1521
 
1414
- Runs when the user is shown a permission dialog.
1522
+ Runs when Claude Code is about to ask you for permission. In sessions that canโ€™t show a prompt, such as background subagents in [non-interactive mode](https://code.claude.com/docs/en/headless), Claude Code still runs these hooks, and if no hook returns a decision, it denies the tool call.
1415
1523
  Use [PermissionRequest decision control](https://code.claude.com/docs/en/hooks#permissionrequest-decision-control) to allow or deny on behalf of the user.Matches on tool name, same values as PreToolUse.
1416
1524
 
1417
1525
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#permissionrequest-input) PermissionRequest input
1418
1526
 
1419
- PermissionRequest hooks receive `tool_name` and `tool_input` fields like PreToolUse hooks, but without `tool_use_id`. An optional `permission_suggestions` array contains the โ€œalways allowโ€ options the user would normally see in the permission dialog. The difference is when the hook fires: PermissionRequest hooks run when a permission dialog is about to be shown to the user, while PreToolUse hooks run before tool execution regardless of permission status.
1527
+ PermissionRequest hooks receive `tool_name` and `tool_input` fields like PreToolUse hooks, but without `tool_use_id`. An optional `permission_suggestions` array contains the โ€œalways allowโ€ options the user would normally see in the permission dialog.PreToolUse hooks run before every tool call, whether or not it needs permission. PermissionRequest hooks run only when Claude Code is about to ask you for permission, or when it would otherwise auto-deny a call that canโ€™t prompt. Neither event fires for [`EndConversation`](https://code.claude.com/docs/en/tools-reference#endconversation-tool-behavior).
1420
1528
 
1421
1529
  ```
1422
1530
  {
@@ -1499,7 +1607,7 @@ Runs immediately after a tool completes successfully.Matches on tool name, same
1499
1607
 
1500
1608
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#posttooluse-input) PostToolUse input
1501
1609
 
1502
- `PostToolUse` hooks fire after a tool has already executed successfully. The input includes both `tool_input`, the arguments sent to the tool, and `tool_response`, the result it returned. The exact schema for both depends on the tool.
1610
+ `PostToolUse` hooks fire after a tool has already executed successfully. The input includes both `tool_input`, the arguments sent to the tool, and `tool_response`, the result it returned. The exact schema for both depends on the tool. File-tool `tool_input` paths arrive in the same format as for [PreToolUse](https://code.claude.com/docs/en/hooks#pretooluse-input): always absolute, with the platformโ€™s native separators, so backslashes on Windows.
1503
1611
 
1504
1612
  ```
1505
1613
  {
@@ -1559,11 +1667,13 @@ The example below replaces the output of a `Bash` call. The replacement value ma
1559
1667
 
1560
1668
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#posttoolusefailure) PostToolUseFailure
1561
1669
 
1562
- Runs when a tool execution fails. This event fires for tool calls that throw errors or return failure results. Use this to log failures, send alerts, or provide corrective feedback to Claude.Matches on tool name, same values as PreToolUse.
1670
+ Runs when a tool that started executing fails: the tool threw an error, or an MCP tool returned an error result. Use this to log failures, send alerts, or provide corrective feedback to Claude.Matches on tool name, same values as PreToolUse.
1671
+
1672
+ This event doesnโ€™t fire for tool calls rejected before execution: an unknown tool name, input that fails schema or tool-specific validation, or a permission denial. Validation rejections are returned as `tool_use_error` results and happen before hooks run, so they fire neither `PreToolUse` nor `PostToolUseFailure`. Permission denials fire `PreToolUse` but not this event; see [PermissionDenied](https://code.claude.com/docs/en/hooks#permissiondenied).
1563
1673
 
1564
1674
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#posttoolusefailure-input) PostToolUseFailure input
1565
1675
 
1566
- PostToolUseFailure hooks receive the same `tool_name` and `tool_input` fields as PostToolUse, along with error information as top-level fields:
1676
+ PostToolUseFailure hooks receive the same `tool_name` and `tool_input` fields as PostToolUse, along with error information as top-level fields. For example, a failed `npm test` command might deliver:
1567
1677
 
1568
1678
  ```
1569
1679
  {
@@ -1578,7 +1688,7 @@ PostToolUseFailure hooks receive the same `tool_name` and `tool_input` fields as
1578
1688
  "description": "Run test suite"
1579
1689
  },
1580
1690
  "tool_use_id": "toolu_01ABC123...",
1581
- "error": "Command exited with non-zero status code 1",
1691
+ "error": "Exit code 1\nError: Cannot find module 'express'",
1582
1692
  "is_interrupt": false,
1583
1693
  "duration_ms": 4187
1584
1694
  }
@@ -1586,10 +1696,16 @@ PostToolUseFailure hooks receive the same `tool_name` and `tool_input` fields as
1586
1696
 
1587
1697
  | Field | Description |
1588
1698
  | --- | --- |
1589
- | `error` | String describing what went wrong |
1590
- | `is_interrupt` | Optional boolean indicating whether the failure was caused by user interruption |
1699
+ | `error` | String describing what went wrong. The format depends on the tool that failed |
1700
+ | `is_interrupt` | Optional boolean. True when the failure reached Claude Code as an abort rather than as an error the tool reported. Cancelling a running tool does not fire this hook; the tool result carries the interruption message instead |
1591
1701
  | `duration_ms` | Optional. Tool execution time in milliseconds. Excludes time spent in permission prompts and PreToolUse hooks |
1592
1702
 
1703
+ The `error` string is generally the same text Claude receives as the failed toolโ€™s result. Its format varies by tool and failure. Key your hook on `tool_name`, `is_interrupt`, and the `Exit code N` first line; treat the rest of the string as display text, not a stable format.
1704
+
1705
+ - For Bash and PowerShell, a command that ran and exited produces a first line `Exit code N`, then any output the command produced as one block with stdout and stderr interleaved
1706
+ - A payload may also carry a bare failure message with no exit-code line, when Claude Code could not start the shell process itself
1707
+ - Claude Code middle-truncates strings longer than 10,000 characters around a `... [N characters truncated] ...` marker, and can insert lines of its own, such as `Command timed out after 2m 0s`
1708
+
1593
1709
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#posttoolusefailure-decision-control) PostToolUseFailure decision control
1594
1710
 
1595
1711
  `PostToolUseFailure` hooks can provide context to Claude after a tool failure. In addition to the [JSON output fields](https://code.claude.com/docs/en/hooks#json-output) available to all hooks, your hook script can return these event-specific fields:
@@ -1683,13 +1799,13 @@ In addition to the [common input fields](https://code.claude.com/docs/en/hooks#c
1683
1799
  "description": "Clean build directory"
1684
1800
  },
1685
1801
  "tool_use_id": "toolu_01ABC123...",
1686
- "reason": "Auto mode denied: command targets a path outside the project"
1802
+ "reason": "Blocked by classifier"
1687
1803
  }
1688
1804
  ```
1689
1805
 
1690
1806
  | Field | Description |
1691
1807
  | --- | --- |
1692
- | `reason` | The classifierโ€™s explanation for why the tool call was denied |
1808
+ | `reason` | The denial reason: the fixed text `Blocked by classifier` in most sessions, or the classifierโ€™s written explanation when the sessionโ€™s classifier model provides one. See [Review denials](https://code.claude.com/docs/en/auto-mode-config#review-denials) |
1693
1809
 
1694
1810
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#permissiondenied-decision-control) PermissionDenied decision control
1695
1811
 
@@ -1831,7 +1947,7 @@ SubagentStop hooks use the same decision control format as [Stop hooks](https://
1831
1947
 
1832
1948
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#taskcreated) TaskCreated
1833
1949
 
1834
- Runs when a task is being created via the `TaskCreate` tool. Use this to enforce naming conventions, require task descriptions, or prevent certain tasks from being created.When a `TaskCreated` hook exits with code 2, the task is not created 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": "..."}`. TaskCreated hooks donโ€™t support matchers and fire on every occurrence.
1950
+ Runs when a task is being created via the `TaskCreate` tool. Use this to enforce naming conventions, require task descriptions, or prevent certain tasks from being created.TaskCreated hooks donโ€™t support matchers and fire on every occurrence.
1835
1951
 
1836
1952
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#taskcreated-input) TaskCreated input
1837
1953
 
@@ -1884,7 +2000,7 @@ exit 0
1884
2000
 
1885
2001
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#taskcompleted) TaskCompleted
1886
2002
 
1887
- Runs 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](https://code.claude.com/docs/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.When 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 donโ€™t support matchers and fire on every occurrence.
2003
+ Runs 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](https://code.claude.com/docs/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.TaskCompleted hooks donโ€™t support matchers and fire on every occurrence.
1888
2004
 
1889
2005
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#taskcompleted-input) TaskCompleted input
1890
2006
 
@@ -2058,7 +2174,7 @@ StopFailure hooks have no decision control. They run for notification and loggin
2058
2174
 
2059
2175
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#teammateidle) TeammateIdle
2060
2176
 
2061
- Runs when an [agent team](https://code.claude.com/docs/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.When 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 donโ€™t support matchers and fire on every occurrence.
2177
+ Runs when an [agent team](https://code.claude.com/docs/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.TeammateIdle hooks donโ€™t support matchers and fire on every occurrence.
2062
2178
 
2063
2179
  #### [โ€‹](https://code.claude.com/docs/en/hooks\#teammateidle-input) TeammateIdle input
2064
2180
 
@@ -2195,6 +2311,46 @@ In addition to the [JSON output fields](https://code.claude.com/docs/en/hooks#js
2195
2311
 
2196
2312
  CwdChanged hooks have no decision control. They canโ€™t block the directory change.
2197
2313
 
2314
+ ### [โ€‹](https://code.claude.com/docs/en/hooks\#directoryadded) DirectoryAdded
2315
+
2316
+ Runs after you add a working directory mid-session with the `/add-dir` command, or after an SDK client adds one with the `register_repo_root` control request. Use this to prepare a newly added repository, for example by installing its dependencies.Claude Code doesnโ€™t fire this event when:
2317
+
2318
+ - You pass a directory with the `--add-dir` startup flag; [SessionStart](https://code.claude.com/docs/en/hooks#sessionstart) covers those directories
2319
+ - You add a directory on the `/permissions` Workspace tab
2320
+ - You add a directory that is already a working directory; the add fails with an error
2321
+
2322
+ Claude Code fires DirectoryAdded after refreshing sandbox and permission state, so sandboxed tools already see the new directory when your hook runs. Hook commands themselves run unsandboxed.Claude Code doesnโ€™t wait for the hook: the add completes immediately, and the hook runs in the background with the 600-second default timeout.The matcher filters on how the directory was added:
2323
+
2324
+ | Matcher | When it fires |
2325
+ | --- | --- |
2326
+ | `slash_command` | You add a directory with `/add-dir` |
2327
+ | `register_repo_root` | An SDK client adds a directory with the `register_repo_root` control request |
2328
+
2329
+ #### [โ€‹](https://code.claude.com/docs/en/hooks\#directoryadded-input) DirectoryAdded input
2330
+
2331
+ In addition to the [common input fields](https://code.claude.com/docs/en/hooks#common-input-fields), DirectoryAdded hooks receive `directory` and `source`.
2332
+
2333
+ | Field | Description |
2334
+ | --- | --- |
2335
+ | `directory` | Absolute path of the directory that was added |
2336
+ | `source` | How the directory was added, `"slash_command"` for `/add-dir` or `"register_repo_root"` for the SDK control request |
2337
+
2338
+ ```
2339
+ {
2340
+ "session_id": "abc123",
2341
+ "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
2342
+ "cwd": "/Users/my-project",
2343
+ "hook_event_name": "DirectoryAdded",
2344
+ "directory": "/Users/my-other-repo",
2345
+ "source": "slash_command"
2346
+ }
2347
+ ```
2348
+
2349
+ DirectoryAdded hooks have no decision control. They canโ€™t block the add, which has already completed when the hook runs. Claude Code surfaces hook output differently per source:
2350
+
2351
+ - `slash_command`: unlike on every other event, where you see the `systemMessage` and Claude doesnโ€™t, Claude Code delivers the hookโ€™s `systemMessage` to Claude as context on the next conversation turn. A count of failed hooks appears in the transcript; full failure output goes to the debug log
2352
+ - `register_repo_root`: Claude Code writes `systemMessage` output and failure output to the debug log only
2353
+
2198
2354
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#filechanged) FileChanged
2199
2355
 
2200
2356
  Runs when a watched file changes on disk. Useful for reloading environment variables when project configuration files are modified.The `matcher` for this event serves two roles:
@@ -2236,7 +2392,7 @@ FileChanged hooks have no decision control. They canโ€™t block the file change f
2236
2392
 
2237
2393
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#worktreecreate) WorktreeCreate
2238
2394
 
2239
- Runs when a worktree is being created, either from `claude --worktree` or from a [subagent using `isolation: "worktree"`](https://code.claude.com/docs/en/sub-agents#choose-the-subagent-scope). By default Claude Code creates the isolated working copy with `git worktree`. Configuring a WorktreeCreate hook replaces that default git behavior, letting you use a different version control system like SVN, Perforce, or Mercurial.Because the hook replaces the default behavior entirely, [`.worktreeinclude`](https://code.claude.com/docs/en/worktrees#copy-gitignored-files-into-worktrees) is not processed. If you need to copy local configuration files like `.env` into the new worktree, do it inside your hook script.The hook must return the path to the created worktree directory. Claude Code uses this path as the working directory for the isolated session. See [WorktreeCreate output](https://code.claude.com/docs/en/hooks#worktreecreate-output) for how each hook type returns the path.This example creates an SVN working copy and prints the path for Claude Code to use. Replace the repository URL with your own:
2395
+ Runs when a worktree is being created, whether from `claude --worktree`, from a [subagent using `isolation: "worktree"`](https://code.claude.com/docs/en/sub-agents#choose-the-subagent-scope), or for a [background session](https://code.claude.com/docs/en/agent-view#how-file-edits-are-isolated) that Claude Code isolates in its own worktree. By default Claude Code creates the isolated working copy with `git worktree`. Configuring a WorktreeCreate hook replaces that default git behavior, letting you use a different version control system like SVN, Perforce, or Mercurial.Because the hook replaces the default behavior entirely, [`.worktreeinclude`](https://code.claude.com/docs/en/worktrees#copy-gitignored-files-into-worktrees) is not processed. If you need to copy local configuration files like `.env` into the new worktree, do it inside your hook script.The hook must return the path to the created worktree directory. Claude Code uses this path as the working directory for the isolated session. See [WorktreeCreate output](https://code.claude.com/docs/en/hooks#worktreecreate-output) for how each hook type returns the path.This example creates an SVN working copy and prints the path for Claude Code to use. Replace the repository URL with your own:
2240
2396
 
2241
2397
  ```
2242
2398
  {
@@ -2278,11 +2434,17 @@ WorktreeCreate hooks donโ€™t use the standard allow/block decision model. Instea
2278
2434
  - **Command hooks** (`type: "command"`): print the path as the last non-empty line of stdout. Claude Code strips ANSI escape codes before reading that line, so shell startup banners printed before your `echo` are ignored. Redirect any other hook output to stderr.
2279
2435
  - **HTTP hooks** (`type: "http"`): return `{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }` in the response body.
2280
2436
 
2281
- If the hook fails or produces no path, worktree creation fails with an error.Claude Code resolves a relative path against the directory the hook ran in. If the resulting path isnโ€™t a directory Claude Code can enter, the session prints an error naming the path and exits with code 1. Before v2.1.205, a relative path or a path that didnโ€™t exist on disk crashed the session at startup, and with `-p` it stalled for about 30 seconds before exiting with code 0.
2437
+ If the hook fails or produces no path, worktree creation fails with an error.Claude Code resolves a relative path against the directory the hook ran in, collapsing any `.` or `..` segments in it. If the resulting path isnโ€™t a directory Claude Code can enter, the session prints an error naming the path and exits with code 1. Before v2.1.205, a relative path or a path that didnโ€™t exist on disk crashed the session at startup, and with `-p` it stalled for about 30 seconds before exiting with code 0.Claude Code refuses an absolute path that contains `.` or `..` segments, and any path that passes through a symlink below the repository root, because a symlink committed to the repository could redirect the worktree outside it. The error names the rejected component. Return a normalized path that doesnโ€™t pass through a symlink inside the repository. Before v2.1.216, worktree creation followed the hookโ€™s path without this screening.
2282
2438
 
2283
2439
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#worktreeremove) WorktreeRemove
2284
2440
 
2285
- Runs when a worktree is being removed, either when you exit a `--worktree` session and choose to remove it, or when a subagent with `isolation: "worktree"` finishes. This is the cleanup counterpart to [WorktreeCreate](https://code.claude.com/docs/en/hooks#worktreecreate).For git-based worktrees, Claude Code handles cleanup automatically with `git worktree remove`. If you configured a WorktreeCreate hook for a non-git version control system, pair it with a WorktreeRemove hook to handle cleanup. Without one, the worktree directory is left on disk.Claude Code passes the path returned by WorktreeCreate as `worktree_path` in the hook input. This example reads that path and removes the directory:
2441
+ Runs when a worktree is being removed. This is the cleanup counterpart to [WorktreeCreate](https://code.claude.com/docs/en/hooks#worktreecreate). The event fires when:
2442
+
2443
+ - you exit a `--worktree` session and choose to remove it
2444
+ - a subagent with `isolation: "worktree"` finishes
2445
+ - you delete a [background session](https://code.claude.com/docs/en/agent-view#what-deleting-a-session-removes) whose worktree the hook created
2446
+
2447
+ For git-based worktrees, Claude Code handles cleanup automatically with `git worktree remove`. If you configured a WorktreeCreate hook for a non-git version control system, pair it with a WorktreeRemove hook to handle cleanup. Without one, the worktree directory is left on disk.For a background-session delete, Claude Code verifies the stored worktree path before running the hook and refuses a path that is a symlink or passes through one below the repository root. The hook runs for a worktree that still contains files only when you confirm the delete in [agent view](https://code.claude.com/docs/en/agent-view#what-deleting-a-session-removes); for such a worktree, [`claude rm`](https://code.claude.com/docs/en/agent-view#manage-sessions-from-the-shell) keeps the session and worktree instead. Before v2.1.216, the hook ran on the stored path without these checks.Claude Code passes the path returned by WorktreeCreate as `worktree_path` in the hook input. This example reads that path and removes the directory:
2286
2448
 
2287
2449
  ```
2288
2450
  {
@@ -2535,6 +2697,7 @@ Events that support `command`, `http`, and `mcp_tool` hooks but not `prompt` or
2535
2697
 
2536
2698
  - `ConfigChange`
2537
2699
  - `CwdChanged`
2700
+ - `DirectoryAdded`
2538
2701
  - `Elicitation`
2539
2702
  - `ElicitationResult`
2540
2703
  - `FileChanged`
@@ -2560,7 +2723,7 @@ Instead of executing a Bash command, prompt-based hooks:
2560
2723
 
2561
2724
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#prompt-hook-configuration) Prompt hook configuration
2562
2725
 
2563
- Set `type` to `"prompt"` and provide a `prompt` string instead of a `command`. Use the `$ARGUMENTS` placeholder to inject the hookโ€™s JSON input data into your prompt text. Claude Code sends the combined prompt and input to a fast Claude model, which returns a JSON decision.This `Stop` hook asks the LLM to evaluate whether all tasks are complete before allowing Claude to finish:
2726
+ Set `type` to `"prompt"` and provide a `prompt` string instead of a `command`. Use the `$ARGUMENTS` placeholder to inject the hookโ€™s JSON input data into your prompt text.This `Stop` hook asks the LLM to evaluate whether all tasks are complete before allowing Claude to finish:
2564
2727
 
2565
2728
  ```
2566
2729
  {
@@ -2606,10 +2769,11 @@ The LLM must respond with JSON containing:
2606
2769
  What happens on `ok: false` depends on the event:
2607
2770
 
2608
2771
  - `Stop` and `SubagentStop`: the reason is fed back to Claude as its next instruction and the turn continues
2609
- - `PreToolUse`: the tool call is denied and the reason is returned to Claude as the tool error, equivalent to a command hookโ€™s `permissionDecision: "deny"`
2772
+ - `PreToolUse`: the tool call is denied; by default the turn ends and the deny reason appears in the chat as a warning line. Set `continueOnBlock: true` to instead return the reason to Claude as the tool error so it can adjust and continue, equivalent to a command hookโ€™s `permissionDecision: "deny"`. Before v2.1.210, the deny reason was returned to Claude as the tool error and the turn continued
2610
2773
  - `PostToolUse`: by default the turn ends and the reason appears in the chat as a warning line. Set `continueOnBlock: true` to feed the reason back to Claude and continue the turn instead
2611
2774
  - `PostToolBatch`, `UserPromptSubmit`, and `UserPromptExpansion`: the turn ends and the reason appears as a warning line. These events end the turn on `decision: "block"` regardless of `continue`
2612
- - `PostToolUseFailure`, `TaskCreated`, and `TaskCompleted`: the reason is returned to Claude as a tool error, similar to `PreToolUse`
2775
+ - `PostToolUseFailure` and `TaskCreated`: the reason is returned to Claude as a tool error and the turn continues, regardless of `continueOnBlock`
2776
+ - `TaskCompleted`: when it fires because a task is marked completed during a turn, the reason is returned to Claude as a tool error and the turn continues, regardless of `continueOnBlock`. When it fires because a teammate stops, it behaves like `TeammateIdle` and halts the teammate by default
2613
2777
  - `TeammateIdle`: by default the teammate stops and the reason appears as a warning line. Set `continueOnBlock: true` to feed the reason back to the teammate and keep it working instead
2614
2778
  - `PermissionRequest`: `ok: false` has no effect. To deny an approval from a hook, use a [command hook](https://code.claude.com/docs/en/hooks#command-hook-fields) returning `hookSpecificOutput.decision.behavior: "deny"`
2615
2779
  - `PermissionDenied`: `ok: false` has no effect because the denial already happened. The only output this event reads is `hookSpecificOutput.retry`, which prompt and agent hooks canโ€™t set. They run on this event, but their output is discarded. Use a [command hook](https://code.claude.com/docs/en/hooks#command-hook-fields) to return `retry`
@@ -2714,7 +2878,10 @@ Add `"async": true` to a command hookโ€™s configuration to run it in the backgro
2714
2878
  }
2715
2879
  ```
2716
2880
 
2717
- The `timeout` field sets the maximum time in seconds for the background process. If not specified, async hooks use the same 10-minute default as sync hooks.
2881
+ The `timeout` field sets the maximum time in seconds for the background process. If not specified, async hooks use the same 10-minute default as sync hooks.You receive an async hookโ€™s results only while the session runs:
2882
+
2883
+ - In [non-interactive mode](https://code.claude.com/docs/en/headless) with the `-p` flag, Claude Code kills any async hook still running at teardown and finalizes it with outcome `cancelled`
2884
+ - If your hookโ€™s work must outlive a `claude -p` session, start a fully detached process from it
2718
2885
 
2719
2886
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#how-async-hooks-execute) How async hooks execute
2720
2887
 
@@ -2774,10 +2941,8 @@ Then add this configuration to `.claude/settings.json` in your project root. The
2774
2941
 
2775
2942
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#limitations) Limitations
2776
2943
 
2777
- Async hooks have several constraints compared to synchronous hooks:
2944
+ Async hooks have additional constraints compared to synchronous hooks:
2778
2945
 
2779
- - Only `type: "command"` hooks support `async`. Prompt-based hooks canโ€™t run asynchronously.
2780
- - Async hooks canโ€™t block tool calls or return decisions. By the time the hook completes, the triggering action has already proceeded.
2781
2946
  - Hook output is delivered on the next conversation turn. If the session is idle, the response waits until the next user interaction. Exception: an `asyncRewake` hook that exits with code 2 wakes Claude immediately even when the session is idle.
2782
2947
  - Each execution creates a separate background process. There is no deduplication across multiple firings of the same async hook.
2783
2948
 
@@ -2785,8 +2950,6 @@ Async hooks have several constraints compared to synchronous hooks:
2785
2950
 
2786
2951
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#disclaimer) Disclaimer
2787
2952
 
2788
- Command hooks run with your system userโ€™s full permissions.
2789
-
2790
2953
  Command hooks execute shell commands with your full user permissions. They can modify, delete, or access any files your user account can access. Review and test all hook commands before adding them to your configuration.
2791
2954
 
2792
2955
  ### [โ€‹](https://code.claude.com/docs/en/hooks\#security-best-practices) Security best practices
@@ -2801,7 +2964,7 @@ Keep these practices in mind when writing hooks:
2801
2964
 
2802
2965
  ## [โ€‹](https://code.claude.com/docs/en/hooks\#windows-powershell-tool) Windows PowerShell tool
2803
2966
 
2804
- On Windows, you can run individual hooks in PowerShell by setting `"shell": "powershell"` on a command hook. Hooks spawn PowerShell directly, so this works regardless of whether `CLAUDE_CODE_USE_POWERSHELL_TOOL` is set. Claude Code auto-detects `pwsh.exe`, the PowerShell 7 and later executable, and falls back to `powershell.exe` for Windows PowerShell 5.1.
2967
+ On Windows, you can run individual hooks in PowerShell by setting `"shell": "powershell"` on a command hook. Claude Code auto-detects `pwsh.exe`, the PowerShell 7 and later executable, and falls back to `powershell.exe` for Windows PowerShell 5.1.
2805
2968
 
2806
2969
  ```
2807
2970
  {
@@ -2834,13 +2997,12 @@ To reference the project root from a PowerShell shell-form command, write `${CLA
2834
2997
 
2835
2998
  ## [โ€‹](https://code.claude.com/docs/en/hooks\#debug-hooks) Debug hooks
2836
2999
 
2837
- Hook execution details, including which hooks matched, their exit codes, and full stdout and stderr, are written to the debug log file. Start Claude Code with `claude --debug-file <path>` to write the log to a known location, or run `claude --debug` and read the log at `~/.claude/debug/<session-id>.txt`. The `--debug` flag doesnโ€™t print to the terminal.
3000
+ Hook execution details, including which hooks matched, their exit codes, and full stdout and stderr, are written to the debug log file. Start Claude Code with `claude --debug-file <path>` to write the log to a known location, or run `claude --debug` and read the log at `~/.claude/debug/<session-id>.txt`. The `--debug` flag doesnโ€™t print to the terminal.For example, a `PostToolUse` hook on `Write` whose command prints `hook-ran` produces entries like:
2838
3001
 
2839
3002
  ```
2840
- [DEBUG] Executing hooks for PostToolUse:Write
2841
- [DEBUG] Found 1 hook commands to execute
2842
- [DEBUG] Executing hook command: <Your command> with timeout 600000ms
2843
- [DEBUG] Hook command completed with status 0: <Your stdout>
3003
+ 2026-07-19T02:03:24.382Z [DEBUG] Hook output does not start with {, treating as plain text
3004
+ 2026-07-19T02:03:24.382Z [DEBUG] Hook PostToolUse:Write (PostToolUse) success:
3005
+ hook-ran
2844
3006
  ```
2845
3007
 
2846
3008
  For more granular hook matching details, set `CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose` to see additional log lines such as hook matcher counts and query matching.For troubleshooting common issues like hooks not firing, Stop hooks that keep blocking, or configuration errors, see [Limitations and troubleshooting](https://code.claude.com/docs/en/hooks-guide#limitations-and-troubleshooting) in the guide. For a broader diagnostic walkthrough covering `/context`, `/doctor`, and settings precedence, see [Debug your config](https://code.claude.com/docs/en/debug-your-config).
@@ -2849,14 +3011,16 @@ Was this page helpful?
2849
3011
 
2850
3012
  YesNo
2851
3013
 
2852
- [Checkpointing](https://code.claude.com/docs/en/checkpointing) [Plugins reference](https://code.claude.com/docs/en/plugins-reference)
2853
-
2854
3014
  Ctrl+I
2855
3015
 
2856
3016
  Assistant
2857
3017
 
2858
3018
  Responses are generated using AI and may contain mistakes.
2859
3019
 
2860
- ![Hook lifecycle diagram showing optional Setup feeding into SessionStart, then a per-turn loop containing UserPromptSubmit, UserPromptExpansion for slash commands, the nested agentic loop (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), and Stop or StopFailure, followed by TeammateIdle, PreCompact, PostCompact, and SessionEnd, with Elicitation and ElicitationResult nested inside MCP tool execution, PermissionDenied as a side branch from PermissionRequest for auto-mode denials, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, and FileChanged as standalone async events, and MessageDisplay as a display-only event that runs while assistant message text streams](https://mintcdn.com/claude-code/uLsR38F1U_5zPppm/images/hooks-lifecycle.svg?w=1100&fit=max&auto=format&n=uLsR38F1U_5zPppm&q=85&s=3fab734aa1c51acc4b37a49d9019d182)
3020
+ ![Hook lifecycle diagram showing optional Setup feeding into SessionStart, then a per-turn loop containing UserPromptSubmit, UserPromptExpansion for slash commands, the nested agentic loop (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), and Stop or StopFailure, followed by TeammateIdle, PreCompact, PostCompact, and SessionEnd, with Elicitation and ElicitationResult nested inside MCP tool execution, PermissionDenied as a side branch from PermissionRequest for auto-mode denials, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged, and DirectoryAdded as standalone async events, and MessageDisplay as a display-only event that runs while assistant message text streams](https://mintcdn.com/claude-code/jhXrDR5TrSZ5hgXM/images/hooks-lifecycle.svg?w=1100&fit=max&auto=format&n=jhXrDR5TrSZ5hgXM&q=85&s=a826842ea5a2b035369bde6ec4f23ac1)
3021
+
3022
+ ![Hook lifecycle diagram showing optional Setup feeding into SessionStart, then a per-turn loop containing UserPromptSubmit, UserPromptExpansion for slash commands, the nested agentic loop (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), and Stop or StopFailure, followed by TeammateIdle, PreCompact, PostCompact, and SessionEnd, with Elicitation and ElicitationResult nested inside MCP tool execution, PermissionDenied as a side branch from PermissionRequest for auto-mode denials, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged, and DirectoryAdded as standalone async events, and MessageDisplay as a display-only event that runs while assistant message text streams](https://mintcdn.com/claude-code/jhXrDR5TrSZ5hgXM/images/hooks-lifecycle-dark.svg?w=1100&fit=max&auto=format&n=jhXrDR5TrSZ5hgXM&q=85&s=fc08f63611d3c4321b1acf4adae5dc84)
3023
+
3024
+ ![Diagram of hook resolution: PreToolUse fires, the matcher checks for a Bash match, then the if condition checks for a Bash(rm *) match. If both match, the hook command runs and returns permissionDecision deny, so the tool call is blocked and Claude Code continues. If either check fails to match, the hook is skipped and the tool call is allowed to proceed.](https://mintcdn.com/claude-code/ikqp3_70mqIahteV/images/hook-resolution.svg?w=1100&fit=max&auto=format&n=ikqp3_70mqIahteV&q=85&s=12622bb46f39fae9e28e994c0e778399)
2861
3025
 
2862
- ![Diagram of hook resolution: PreToolUse fires, the matcher checks for a Bash match, then the if condition checks for a Bash(rm *) match. If both match, the hook command runs and returns permissionDecision deny, so the tool call is blocked and Claude Code continues. If either check fails to match, the hook is skipped and the tool call is allowed to proceed.](https://mintcdn.com/claude-code/ikqp3_70mqIahteV/images/hook-resolution.svg?w=1100&fit=max&auto=format&n=ikqp3_70mqIahteV&q=85&s=12622bb46f39fae9e28e994c0e778399)
3026
+ ![Diagram of hook resolution: PreToolUse fires, the matcher checks for a Bash match, then the if condition checks for a Bash(rm *) match. If both match, the hook command runs and returns permissionDecision deny, so the tool call is blocked and Claude Code continues. If either check fails to match, the hook is skipped and the tool call is allowed to proceed.](https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/hook-resolution-dark.svg?w=1100&fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=2f60e716dc01cc0f41783a1138f4ff72)
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module ClaudeHooks
6
+ class DirectoryAdded < Base
7
+ def self.hook_type
8
+ 'DirectoryAdded'
9
+ end
10
+
11
+ def self.input_fields
12
+ %w[directory source]
13
+ end
14
+
15
+ def directory
16
+ @input_data['directory']
17
+ end
18
+
19
+ # Values: slash_command | register_repo_root
20
+ def source
21
+ @input_data['source']
22
+ end
23
+ end
24
+ end
@@ -180,6 +180,8 @@ module ClaudeHooks
180
180
  ElicitationResult.new(data)
181
181
  when 'WorktreeCreate'
182
182
  WorktreeCreate.new(data)
183
+ when 'DirectoryAdded'
184
+ DirectoryAdded.new(data)
183
185
  else
184
186
  raise ArgumentError, "Unknown hook type: #{hook_type}"
185
187
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module ClaudeHooks
6
+ module Output
7
+ # DirectoryAdded is non-blocking โ€” the directory is already added.
8
+ # Only `systemMessage` is consumed (as Claude context for `slash_command`,
9
+ # debug log for `register_repo_root`).
10
+ class DirectoryAdded < Base
11
+ def exit_code
12
+ 0
13
+ end
14
+
15
+ def self.merge(*outputs)
16
+ merged = super(*outputs)
17
+ new(merged.data)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeHooks
4
- VERSION = "1.2.1"
4
+ VERSION = "1.3.0"
5
5
  end
data/lib/claude_hooks.rb CHANGED
@@ -37,6 +37,7 @@ require_relative "claude_hooks/permission_denied"
37
37
  require_relative "claude_hooks/elicitation"
38
38
  require_relative "claude_hooks/elicitation_result"
39
39
  require_relative "claude_hooks/worktree_create"
40
+ require_relative "claude_hooks/directory_added"
40
41
 
41
42
  # Output classes
42
43
  require_relative "claude_hooks/output/base"
@@ -70,6 +71,7 @@ require_relative "claude_hooks/output/permission_denied"
70
71
  require_relative "claude_hooks/output/elicitation"
71
72
  require_relative "claude_hooks/output/elicitation_result"
72
73
  require_relative "claude_hooks/output/worktree_create"
74
+ require_relative "claude_hooks/output/directory_added"
73
75
 
74
76
  module ClaudeHooks
75
77
  class Error < StandardError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: claude_hooks
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriel Dehan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-14 00:00:00.000000000 Z
11
+ date: 2026-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -73,6 +73,7 @@ files:
73
73
  - docs/API/COMMON.md
74
74
  - docs/API/CONFIG_CHANGE.md
75
75
  - docs/API/CWD_CHANGED.md
76
+ - docs/API/DIRECTORY_ADDED.md
76
77
  - docs/API/ELICITATION.md
77
78
  - docs/API/ELICITATION_RESULT.md
78
79
  - docs/API/FILE_CHANGED.md
@@ -124,6 +125,7 @@ files:
124
125
  - lib/claude_hooks/config_change.rb
125
126
  - lib/claude_hooks/configuration.rb
126
127
  - lib/claude_hooks/cwd_changed.rb
128
+ - lib/claude_hooks/directory_added.rb
127
129
  - lib/claude_hooks/elicitation.rb
128
130
  - lib/claude_hooks/elicitation_result.rb
129
131
  - lib/claude_hooks/file_changed.rb
@@ -134,6 +136,7 @@ files:
134
136
  - lib/claude_hooks/output/base.rb
135
137
  - lib/claude_hooks/output/config_change.rb
136
138
  - lib/claude_hooks/output/cwd_changed.rb
139
+ - lib/claude_hooks/output/directory_added.rb
137
140
  - lib/claude_hooks/output/elicitation.rb
138
141
  - lib/claude_hooks/output/elicitation_result.rb
139
142
  - lib/claude_hooks/output/file_changed.rb