One read of Claude Code CLIclaude-code-20261016T200701Z
5 pages moved out of 228 read.
Pages moved
5
significant first
Pages read
228
in this capture
Captured
20:07 UTC
Corpus hash
5439a5a62cbe
corpus-hash
What this read moved
1-5 of 5agent-sdk/hooks Changed · +22 / -18 lines
from line 5
55Hooks are callback functions that run your code in response to agent events, like a tool being called, a session starting, or execution stopping. With hooks, you can:
66
77* **Block dangerous operations** before they execute, like destructive shell commands or unauthorized file access
8* **Log and audit** every tool call for compliance, debugging, or analytics
8* **Log and audit** tool calls for compliance, debugging, or analytics
99* **Transform inputs and outputs** to sanitize data, inject credentials, or redirect file paths
1010* **Require human approval** for sensitive actions like database writes or API calls
1111* **Track session lifecycle** to manage state, clean up resources, or send notifications
from line 34
3434 </Step>
3535</Steps>
3636
37The following example puts these steps together. It registers a `PreToolUse` hook (step 1) with a `"Write|Edit"` matcher (step 3) so the callback only fires for file-writing tools. When triggered, the callback receives the tool's input (step 4), checks if the file path targets a `.env` file, and returns `permissionDecision: "deny"` to block the operation (step 5):
37This example registers a `PreToolUse` hook (step 1) with a `"Write|Edit"` matcher (step 3) so the callback fires for the Write and Edit tools. When triggered, the callback receives the tool's input (step 4), checks if the file path targets a `.env` file, and returns `permissionDecision: "deny"` to block the operation (step 5):
3838
3939<CodeGroup>
4040 ```python Python theme={null}
from line 55
5555 file_name = file_path.split("/")[-1]
5656
5757 # Block the operation if targeting a .env file
58 if file_name == ".env":
58 if file_name.lower() == ".env":
5959 return {
6060 "hookSpecificOutput": {
6161 "hookEventName": input_data["hook_event_name"],
from line 102
102102 const fileName = filePath?.split("/").pop();
103103
104104 // Block the operation if targeting a .env file
105 if (fileName === ".env") {
105 if (fileName?.toLowerCase() === ".env") {
106106 return {
107107 hookSpecificOutput: {
108108 hookEventName: preInput.hook_event_name,
from line 134
134134 ```
135135</CodeGroup>
136136
137When you run either script, Claude attempts to create the `.env` file and the hook denies the tool call.
137The hook denies a Write or Edit call whose `file_path` ends in `/.env`, in any letter case. On Windows, `file_path` uses backslashes, such as `C:\project\.env`, and this hook allows a Write or Edit call to `C:\project\.env`.
138138
139<Warning>
140 The `Write|Edit` matcher limits this hook to the Write and Edit tools. If Claude writes `.env` with a Bash command, the hook doesn't run. To block Bash writes to a file where [the sandbox runs](/docs/en/sandboxing), see the sandbox configuration for [Python](/docs/en/agent-sdk/python#sandbox-configuration) or [TypeScript](/docs/en/agent-sdk/typescript#sandbox-configuration).
141</Warning>
142
139143## Available hooks
140144
141145The SDK provides hooks for different stages of agent execution. Some hooks are available in both SDKs, while others are TypeScript-only.
from line 147
143147| Hook Event | Python SDK | TypeScript SDK | What triggers it | Example use case |
144148| - | - | - | - | - |
145149| `PreToolUse` | Yes | Yes | Tool call request (can block or modify) | Block dangerous shell commands |
146| `PostToolUse` | Yes | Yes | Tool execution result | Log all file changes to audit trail |
150| `PostToolUse` | Yes | Yes | Tool execution result | Log file changes to an audit trail |
147151| `PostToolUseFailure` | Yes | Yes | Tool execution failure | Handle or log tool errors |
148152| `PostToolBatch` | No | Yes | A full batch of tool calls resolves, once per batch before the next model call | Inject conventions once for the whole batch |
149153| [`UserPromptSubmit`](/docs/en/hooks#userpromptsubmit) | Yes | Yes | A prompt is submitted, including a turn Claude Code starts on its own | Inject additional context into prompts |
from line 227
223227| `hooks` | `HookCallback[]` | - | Required. Array of callback functions to execute when the pattern matches |
224228| `timeout` | `number` | `undefined` | Timeout in seconds. When omitted, Claude Code applies the [event's default timeout](#hook-timeout). Your SDK callbacks follow the `command` hook defaults |
225229
226Use the `matcher` pattern to target specific tools whenever possible. A matcher with `'Bash'` only runs for Bash commands, while omitting the pattern runs your callbacks for every occurrence of the event. Omit it on purpose to log every tool call your session makes.
230Use the `matcher` pattern to target specific tools whenever possible. A matcher with `'Bash'` only runs for Bash commands, while omitting the pattern runs your callbacks for every occurrence of the event.
227231
228232### Callback functions
229233
from line 254
250254Return `{}` to allow the operation without changes. SDK callback hooks use the same JSON output format as [Claude Code shell command hooks](/docs/en/hooks#json-output), which documents every field and event-specific option. For the SDK type definitions, see the [TypeScript](/docs/en/agent-sdk/typescript#synchookjsonoutput) and [Python](/docs/en/agent-sdk/python#synchookjsonoutput) SDK references.
251255
252256<Note>
253 When multiple hooks or permission rules apply, `deny` takes priority over `defer`, which takes priority over `ask`, which takes priority over `allow`. If any hook returns `deny`, the operation is blocked regardless of other hooks.
257 When multiple `PreToolUse` hooks return different [decisions](/docs/en/hooks#pretooluse-decision-control), `deny` takes priority over `defer`, which takes priority over `ask`, which takes priority over `allow`.
254258</Note>
255259
256260#### Asynchronous output
from line 293
289293
290294### Modify tool input
291295
292This example intercepts Write tool calls and rewrites the `file_path` argument to prepend `/sandbox`, redirecting all file writes to a sandboxed directory. The callback returns `updatedInput` with the modified path and `permissionDecision: 'allow'` to auto-approve the rewritten operation:
296This example intercepts Write tool calls and rewrites the `file_path` argument to prepend `/sandbox`. The callback returns `updatedInput` with the modified path and `permissionDecision: 'allow'` to auto-approve the rewritten operation:
293297
294298<CodeGroup>
295299 ```python Python theme={null}
from line 348
344348
345349### Add context and block a tool
346350
347This example blocks writes to the `/etc` directory and explains why to both the model and the user:
351This example denies tool calls whose `file_path` starts with `/etc` and explains why to both the model and the user:
348352
349353* `permissionDecision: 'deny'` stops the tool call.
350* `permissionDecisionReason` tells the model why, so it avoids retrying.
354* `permissionDecisionReason` tells the model why.
351355* `systemMessage` shows the user what happened.
352356
353357<CodeGroup>
from line 442
438442
439443### Register multiple hooks
440444
441When an event fires, all matching hooks run in parallel. For permission decisions, the most restrictive result applies: a single `deny` blocks the tool call regardless of what the other hooks return. Because completion order is non-deterministic, write each hook to act independently rather than relying on another hook having run first.
445When an event fires, the matching hooks run in parallel. For permission decisions, the most restrictive result applies. One hook's `deny` blocks the tool call. Because completion order is non-deterministic, write each hook to act independently rather than relying on another hook having run first.
442446
443The example below registers three independent checks for every tool call. The hook names in it, such as `audit_logger` in Python or `auditLogger` in TypeScript, stand in for callbacks you define:
447The example below registers three independent checks, each with an [omitted matcher](/docs/en/hooks#matcher-patterns). The hook names in it, such as `audit_logger` in Python or `auditLogger` in TypeScript, stand in for callbacks you define:
444448
445449<CodeGroup>
446450 ```python Python theme={null}
from line 476
472476
473477Use multi-tool matchers to share one callback across related tools. This example registers three matchers with different scopes, and each hook it names stands in for a callback you define:
474478
475* A pipe-separated exact list (`Write|Edit|NotebookEdit`) triggers `file_security_hook` only for file modification tools.
479* A pipe-separated exact list (`Write|Edit|NotebookEdit`) triggers `file_security_hook` for the Write, Edit, and NotebookEdit tools.
476480* A regex (`^mcp__`) triggers `mcp_audit_hook` for any MCP tool whose name starts with `mcp__`.
477* An omitted matcher triggers `global_logger` for every tool call regardless of name.
481* An omitted matcher triggers `global_logger` without filtering by tool name.
478482
479483<CodeGroup>
480484 ```python Python theme={null}
from line 790
786790
787791When a callback exceeds its timeout, Claude Code cancels it and discards its output, and the session continues rather than hanging. What happens next depends on the event:
788792
789* `PreToolUse`: Claude Code doesn't run the tool call, Claude receives a tool result stating the hook didn't respond before its timeout, and the turn continues. If another `PreToolUse` hook returned an explicit deny, Claude receives that denial instead of the timeout error. Before v2.1.210, Claude Code reported the timeout to Claude as a user rejection, which made unattended sessions stop and wait for input.
793* `PreToolUse`: Claude Code blocks the tool call. In its place, Claude receives a tool result saying the hook didn't respond before its timeout, and the turn continues. Before v2.1.210, Claude Code reported the timeout to Claude as a user rejection, which made unattended sessions stop and wait for input.
790794* `PostToolUse` and `PostToolUseFailure`: Claude Code keeps the tool result and the turn continues.
791* `UserPromptSubmit` and [`UserPromptExpansion`](/docs/en/hooks#userpromptexpansion): Claude Code blocks the prompt with a message naming the hook and the timeout, and the session continues. Because a callback on these events can act as a policy gate, Claude Code never lets a timed-out prompt through unscreened. Before v2.1.208, Claude Code ended the query with `error_during_execution` when a callback on these events timed out.
795* `UserPromptSubmit` and [`UserPromptExpansion`](/docs/en/hooks#userpromptexpansion): Claude Code blocks the prompt with a message naming the hook and the timeout, and the session continues. Before v2.1.208, Claude Code ended the query with `error_during_execution` when a callback on these events timed out.
792796* `Stop` and `SubagentStop`: the timed-out callback counts as returning no decision. The agent or subagent stops as if that callback had allowed it, and a decision from your other hooks on the event still applies. Before Claude Code v2.1.273, a timed-out `Stop` or `SubagentStop` callback counted as a failed hook run, and Claude Code discarded the decisions of your other hooks on the event.
793797* `SessionStart`: the timed-out callback counts as returning no output, and the session continues with the output of your other `SessionStart` hooks.
794798* `PreModelSwitch`: Claude Code blocks the model switch. A hook that doesn't answer hasn't approved the switch.
agent-sdk/python Changed · +3 / -1 lines
from line 3559
35593559<Note>
35603560 The sandbox depends on platform support and, on Linux, tools like `bubblewrap` and `socat`. By default, when `enabled` is `True` but the sandbox can't start, commands run unsandboxed with a warning on stderr. This default differs from the TypeScript SDK, where `failIfUnavailable` defaults to `true`.
35613561
3562 Set `"failIfUnavailable": True` in your sandbox settings to stop instead. The key isn't declared on `SandboxSettings` yet, but the SDK forwards it to Claude Code, which honors it. `query()` then reports a `ResultMessage` with `subtype="error_during_execution"` and the reason in `errors`. Because this is a single-shot `query()` call, the SDK raises after yielding that error result, so wrap the loop in a try block to continue past it. See [Handle the result](/docs/en/agent-sdk/agent-loop#handle-the-result) for the error contract.
3562 Set `"failIfUnavailable": True` in your sandbox settings to stop instead. The SDK forwards `failIfUnavailable` to Claude Code along with the declared `SandboxSettings` fields. `query()` then reports a `ResultMessage` with `subtype="error_during_execution"` and the reason in `errors`. Because this is a single-shot `query()` call, the SDK raises after yielding that error result, so wrap the loop in a try block to continue past it. See [Handle the result](/docs/en/agent-sdk/agent-loop#handle-the-result) for the error contract.
35633563</Note>
3564
3565To keep sandboxed commands from writing to a file, list its absolute path under [`denyWrite`](/docs/en/settings-reference#sandbox-filesystem-denywrite) in your sandbox settings, such as `"filesystem": {"denyWrite": ["/home/user/project/.env"]}`. The SDK forwards `filesystem` to Claude Code along with the declared `SandboxSettings` fields. Once the sandbox starts, it blocks writes to each listed path. If you list `.env` or `./.env`, sandboxed commands can write to the `.env` file in the session's working directory.
35643566
35653567#### Example usage
35663568
agent-sdk/typescript Changed · +1 / -1 lines
This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.
Nothing in the body moved in this read. What changed is above.
claude-tag Changed · +1 / -1 lines
This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.
Nothing in the body moved in this read. What changed is above.
hooks Changed · +1 / -1 lines
This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.
from line 1710
17101710
17111711Use [PreToolUse decision control](#pretooluse-decision-control) to allow, deny, ask, or defer the tool call.
17121712
1713An [Agent SDK callback hook](/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.
1713An [Agent SDK callback hook](/docs/en/agent-sdk/hooks) on `PreToolUse` that exceeds its timeout blocks the tool call, and Claude receives an error result naming the timeout.
17141714
17151715#### PreToolUse input
17161716
from line 4050
40504050
40514051When an async hook fires, Claude Code starts the hook process and immediately continues without waiting for it to finish. The hook receives the same JSON input via stdin as a synchronous hook.
40524052
4053After the background process exits, Cl
4053After the background process exits, Claude Code delivers the `additionalContext` and `systemMessage` fie