Page history
Automate actions with hooks
hooks-guide
History
hooks-guide Changed · +4 / -4 lines
from line 184
| `auth_success` | Authentication completes | | `elicitation_dialog` | An MCP server opens an elicitation form and you haven't typed for about six seconds | | `elicitation_url_dialog` | An MCP server asks you to open a browser URL and you haven't typed for about six seconds | -| `elicitation_complete` | An MCP elicitation form is submitted or dismissed | +| `elicitation_complete` | An MCP server reports that a [URL-mode elicitation](/docs/en/hooks#elicitation-input) is complete | | `elicitation_response` | An MCP elicitation response is sent back to the server | | `agent_needs_input` | A background session starts waiting on your input. Fires only while [agent view](/docs/en/agent-view) is open | | `agent_completed` | A background session finishes or fails. Fires only while [agent view](/docs/en/agent-view) is open |
from line 592
The exit code determines what happens next: * **Exit 0**: the hook reports no objection through its exit code. For a `PreToolUse` hook this doesn't approve the tool call: the normal [permission flow](/docs/en/permissions) still applies. For `UserPromptSubmit`, `UserPromptExpansion`, and `SessionStart` hooks, Claude Code adds stdout it [treats as plain text](/docs/en/hooks#exit-code-0) to Claude's context. -* **Exit 2**: Claude Code blocks the action. Write a reason to stderr. Where it lands depends on the event: some events feed it to Claude as feedback so it can adjust, others show it to the user, and a few, such as `ConfigChange` and `Elicitation`, surface no message. Some events can't be blocked: for `SessionStart`, `Setup`, and others, exit 2 shows stderr to the user and execution continues. See [exit code 2 behavior per event](/docs/en/hooks#exit-code-2-behavior-per-event) for the full list. +* **Exit 2**: Claude Code blocks the action. Write a reason to stderr. Where it lands depends on the event: some events feed it to Claude as feedback so it can adjust, others show it to the user, and a few, such as `ConfigChange` and `Elicitation`, surface no message. Some events can't be blocked: for `SessionStart` and others, exit 2 shows stderr to the user and execution continues. See [exit code 2 behavior per event](/docs/en/hooks#exit-code-2-behavior-per-event) for the full list. * **Any other exit code**: for most events, the outcome depends on what your hook printed to stdout: * A parsed object that passes schema validation: Claude Code ignores the exit code, the JSON alone decides the outcome, and the hook isn't reported as an error. The per-event exceptions, like `WorktreeCreate` failing on any nonzero exit, are listed in the reference's [Exit code output](/docs/en/hooks#exit-code-output) section. * A parsed object that fails schema validation: a non-blocking error; the notice carries the validation message.
from line 620
With `"deny"`, Claude Code cancels the tool call and feeds `permissionDecisionReason` back to Claude. These `permissionDecision` values are specific to `PreToolUse`: -* `"allow"`: skip the interactive permission prompt. Deny and ask rules, including enterprise managed deny lists, still apply, as do prompts for connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) and MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) +* `"allow"`: skip the interactive permission prompt. Deny and ask rules, including enterprise managed deny lists, still apply, as do prompts for MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) and for connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) in sessions where that setting reaches Claude Code * `"deny"`: cancel the tool call and send the reason to Claude * `"ask"`: show the permission prompt to the user as normal
from line 949
`PreToolUse` hooks fire before any permission-mode check, in every [permission mode](/docs/en/permission-modes), including `dontAsk`. A hook that returns `permissionDecision: "deny"` blocks the tool even in `bypassPermissions` mode or with `--dangerously-skip-permissions`. This lets you enforce policy that users can't bypass by changing their permission mode. -The reverse is not true: a hook returning `"allow"` doesn't bypass deny rules from settings, and it can't suppress the prompt for connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) or MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool). Hooks can tighten restrictions but not loosen them past what permission rules allow. +The reverse is not true: a hook returning `"allow"` doesn't bypass deny rules from settings, and it can't suppress the prompt for MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) or for connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) in sessions where that setting reaches Claude Code. Hooks can tighten restrictions but not loosen them past what permission rules allow. ### Hook not firing
hooks-guide Changed · +1 / -1 lines
from line 446
To set a specific permission mode instead, your hook's output can include an `updatedPermissions` array with a `setMode` entry. The `mode` value is any permission mode like `default`, `acceptEdits`, or `bypassPermissions`, and `destination: "session"` applies it for the current session only. <Note> - `bypassPermissions` only applies if the session was launched with bypass mode already available: `--dangerously-skip-permissions`, `--permission-mode bypassPermissions`, `--allow-dangerously-skip-permissions`, or `permissions.defaultMode: "bypassPermissions"` in settings, and not disabled by [`permissions.disableBypassPermissionsMode`](/docs/en/permissions#managed-settings). It is never persisted as `defaultMode`. + `bypassPermissions` only applies if the session was launched with bypass mode already available: `--dangerously-skip-permissions`, `--permission-mode bypassPermissions`, `--allow-dangerously-skip-permissions`, or `permissions.defaultMode: "bypassPermissions"` in settings, and not disabled by [`permissions.disableBypassPermissionsMode`](/docs/en/permissions#managed-settings) or by starting the session in [restricted mode](/docs/en/cli-reference#cli-flags). It is never persisted as `defaultMode`. </Note> To switch the session to `acceptEdits`, your hook writes this JSON to stdout:
hooks-guide Changed · +4 / -2 lines
from line 179
| Matcher | Fires when | | :--------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `permission_prompt` | Claude needs you to approve a tool use and the prompt has waited about six seconds | +| `permission_prompt` | Claude needs you to approve a tool use or a sandboxed command's [network request](/docs/en/sandboxing#network-isolation), and the prompt has waited about six seconds | | `idle_prompt` | Claude finished responding about 60 seconds ago and you haven't typed since | | `auth_success` | Authentication completes | | `elicitation_dialog` | An MCP server opens an elicitation form and you haven't typed for about six seconds |
from line 198
The `quota_auto_resume_fired`, `quota_auto_resume_stale`, and `quota_auto_resume_disabled` matchers require Claude Code v2.1.234 or later. +In terminal sessions, `permission_prompt` for a sandboxed command's network request requires Claude Code v2.1.246 or later. + Type `/hooks` and select `Notification` to confirm the hook is registered. For the full event schema, see the [Notification reference](/docs/en/hooks#notification). ### Auto-format code after edits
from line 465
} ``` -Keep the matcher as narrow as possible. Matching on `.*` or leaving the matcher empty would auto-approve every permission prompt, including file writes and shell commands. See the [PermissionRequest reference](/docs/en/hooks#permissionrequest-decision-control) for the full set of decision fields. +Keep the matcher as narrow as possible. Matching on `.*` or leaving the matcher empty would auto-approve every tool permission prompt, including file writes and shell commands. See the [PermissionRequest reference](/docs/en/hooks#permissionrequest-decision-control) for the full set of decision fields. ## How hooks work
hooks-guide Changed · +1 / -1 lines
from line 817
Run [`/hooks`](/docs/en/hooks#the-%2Fhooks-menu) in Claude Code to browse all configured hooks grouped by event. -To disable hooks, set `"disableAllHooks": true` in your settings file. Claude Code reads the value left after [settings precedence](/docs/en/hooks#disable-or-remove-hooks) applies, so a project's settings file can override yours. Hooks configured in managed settings still run unless `disableAllHooks` is also set there. +To disable hooks, set `"disableAllHooks": true` in your settings file. Claude Code reads the value left after [settings precedence](/docs/en/hooks#disable-or-remove-hooks) applies, so a project's settings file can override yours. Hooks configured in managed settings still run unless `disableAllHooks` is also set there. For the full reach of each level, see [`disableAllHooks`](/docs/en/settings-reference#disableallhooks). If you edit settings files directly while Claude Code is running, the file watcher normally picks up hook changes automatically.
hooks-guide Changed · +35 / -30 lines
from line 177
The empty `matcher` fires on all notification types. To fire only on specific events, set it to one of these values: -| Matcher | Fires when | -| :----------------------- | :------------------------------------------------------------------------------------------------------- | -| `permission_prompt` | Claude needs you to approve a tool use and the prompt has waited about six seconds | -| `idle_prompt` | Claude finished responding about 60 seconds ago and you haven't typed since | -| `auth_success` | Authentication completes | -| `elicitation_dialog` | An MCP server opens an elicitation form and you haven't typed for about six seconds | -| `elicitation_url_dialog` | An MCP server asks you to open a browser URL and you haven't typed for about six seconds | -| `elicitation_complete` | An MCP elicitation form is submitted or dismissed | -| `elicitation_response` | An MCP elicitation response is sent back to the server | -| `agent_needs_input` | A background session starts waiting on your input. Fires only while [agent view](/docs/en/agent-view) is open | -| `agent_completed` | A background session finishes or fails. Fires only while [agent view](/docs/en/agent-view) is open | +| Matcher | Fires when | +| :--------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `permission_prompt` | Claude needs you to approve a tool use and the prompt has waited about six seconds | +| `idle_prompt` | Claude finished responding about 60 seconds ago and you haven't typed since | +| `auth_success` | Authentication completes | +| `elicitation_dialog` | An MCP server opens an elicitation form and you haven't typed for about six seconds | +| `elicitation_url_dialog` | An MCP server asks you to open a browser URL and you haven't typed for about six seconds | +| `elicitation_complete` | An MCP elicitation form is submitted or dismissed | +| `elicitation_response` | An MCP elicitation response is sent back to the server | +| `agent_needs_input` | A background session starts waiting on your input. Fires only while [agent view](/docs/en/agent-view) is open | +| `agent_completed` | A background session finishes or fails. Fires only while [agent view](/docs/en/agent-view) is open | +| `quota_auto_resume_fired` | Claude Code continues your task after a claude.ai usage limit paused it: at the reset, or sooner when something you do in Claude Code during the wait, such as adding usage credits, upgrading your plan, or switching models, makes usage available again, with the [model-setting exception](/docs/en/interactive-mode#wait-for-a-usage-limit-to-reset) | +| `quota_auto_resume_stale` | A claude.ai usage limit reset while your computer slept for more than about 30 minutes. Claude Code waits for you to press `Enter` instead of continuing. After a shorter sleep it continues and fires `quota_auto_resume_fired` instead | +| `quota_auto_resume_disabled` | Claude Code ends its wait for a claude.ai usage limit without continuing your task: [`autoContinueAtUsageLimit`](/docs/en/settings-reference#autocontinueatusagelimit) turned off or the reset moved more than 24 hours away during a wait Claude Code started on its own, the continued task kept hitting the limit, or the continuation was blocked before it reached the model. Doesn't fire when you press `Esc` or `Ctrl+C`, or pick **Don't continue automatically** | Claude Code times `permission_prompt` differently in a terminal and in Claude Desktop, the VS Code extension, and other hosts that answer permission requests through the Agent SDK. See [when each notification type fires](/docs/en/hooks#notification) for both timings. The `agent_needs_input` and `agent_completed` matchers require Claude Code v2.1.198 or later. +The `quota_auto_resume_fired`, `quota_auto_resume_stale`, and `quota_auto_resume_disabled` matchers require Claude Code v2.1.234 or later. + Type `/hooks` and select `Notification` to confirm the hook is registered. For the full event schema, see the [Notification reference](/docs/en/hooks#notification). ### Auto-format code after edits
from line 668
Each event type matches on a specific field: -| Event | What the matcher filters | Example matcher values | -| :-------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `PermissionDenied` | tool name | `Bash`, `Edit\|Write`, `mcp__.*` | -| `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact`, `fork` | -| `Setup` | which CLI flag triggered setup | `init`, `maintenance` | -| `SessionEnd` | why the session ended | `clear`, `resume`, `logout`, `prompt_input_exit`, `other` | -| `Notification` | notification type | `permission_prompt`, `idle_prompt`, `auth_success`, `elicitation_dialog`, `elicitation_url_dialog`, `elicitation_complete`, `elicitation_response`, `agent_needs_input`, `agent_completed` | -| `SubagentStart` | agent type | `general-purpose`, `Explore`, `Plan`, or custom agent names | -| `PreCompact`, `PostCompact` | what triggered compaction | `manual`, `auto` | -| `SubagentStop` | agent type | same values as `SubagentStart` | -| `ConfigChange` | configuration source | `user_settings`, `project_settings`, `local_settings`, `policy_settings`, `skills` | -| `DirectoryAdded` | how the directory was added | `slash_command`, `register_repo_root` | -| `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` | -| `InstructionsLoaded` | load reason | `session_start`, `nested_traversal`, `path_glob_match`, `include`, `compact` | -| `Elicitation` | MCP server name | your configured MCP server names | -| `ElicitationResult` | MCP server name | same values as `Elicitation` | -| `FileChanged` | literal filenames to watch (see [FileChanged](/docs/en/hooks#filechanged)) | `.envrc\|.env` | -| `UserPromptExpansion` | command name | your skill or command names | -| `UserPromptSubmit`, `PostToolBatch`, `Stop`, `TeammateIdle`, `TaskCreated`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove`, `CwdChanged`, `MessageDisplay` | no matcher support | always fires on every occurrence | +| Event | What the matcher filters | Example matcher values | +| :-------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `PermissionDenied` | tool name | `Bash`, `Edit\|Write`, `mcp__.*` | +| `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact`, `fork` | +| `Setup` | which CLI flag triggered setup | `init`, `maintenance` | +| `SessionEnd` | why the session ended | `clear`, `resume`, `logout`, `prompt_input_exit`, `other` | +| `Notification` | notification type | `permission_prompt`, `idle_prompt`, `auth_success`, `elicitation_dialog`, `elicitation_url_dialog`, `elicitation_complete`, `elicitation_response`, `agent_needs_input`, `agent_completed`, `quota_auto_resume_fired`, `quota_auto_resume_stale`, `quota_auto_resume_disabled` | +| `SubagentStart` | agent type | `general-purpose`, `Explore`, `Plan`, or custom agent names | +| `PreCompact`, `PostCompact` | what triggered compaction | `manual`, `auto` | +| `SubagentStop` | agent type | same values as `SubagentStart` | +| `ConfigChange` | configuration source | `user_settings`, `project_settings`, `local_settings`, `policy_settings`, `skills` | +| `DirectoryAdded` | how the directory was added | `slash_command`, `register_repo_root` | +| `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` | +| `InstructionsLoaded` | load reason | `session_start`, `nested_traversal`, `path_glob_match`, `include`, `compact` | +| `Elicitation` | MCP server name | your configured MCP server names | +| `ElicitationResult` | MCP server name | same values as `Elicitation` | +| `FileChanged` | literal filenames to watch (see [FileChanged](/docs/en/hooks#filechanged)) | `.envrc\|.env` | +| `UserPromptExpansion` | command name | your skill or command names | +| `UserPromptSubmit`, `PostToolBatch`, `Stop`, `TeammateIdle`, `TaskCreated`, `TaskCompleted`, `WorktreeCreate`, `WorktreeRemove`, `CwdChanged`, `MessageDisplay` | no matcher support | always fires on every occurrence | The tabs below show a few more matchers on different event types.
hooks-guide Changed · +3 / -1 lines
from line 223
When the hook succeeds, Claude Code shows nothing in the conversation. To confirm the hook ran, check that the edited file is reformatted, or see [Debug techniques](#debug-techniques). +To reformat a specific file however it changes, including when a `Bash` command rewrites it, use a [FileChanged](/docs/en/hooks#filechanged) hook instead. + <Note> The Bash examples on this page use `jq` for JSON parsing. Install it with `brew install jq` on macOS, `apt-get install jq` on Debian and Ubuntu, or see [`jq` downloads](https://jqlang.org/download/). </Note>
from line 658
The `"Edit|Write"` matcher fires only when Claude uses the `Edit` or `Write` tool, not when it uses `Bash`, `Read`, or any other tool. On Claude Code v2.1.191 or later, a comma separates alternatives the same way, so `"Edit, Write"` is equivalent. See [Matcher patterns](/docs/en/hooks#matcher-patterns) for how plain names and regular expressions are evaluated. <Note> - Claude can also create or modify files by running shell commands. If your hook must see every file change, such as for compliance scanning or audit logging, add a [`Stop`](/docs/en/hooks#stop) hook that scans the working tree once per turn. For per-call coverage instead, also match `Bash|PowerShell` and have your script list modified and untracked files with `git status --porcelain`. The [PowerShell hook input section](/docs/en/hooks#powershell) explains why matching `Bash` alone is not enough. + Claude can also create or modify files by running shell commands. If your hook must see every file change, such as for compliance scanning or audit logging, add a [`Stop`](/docs/en/hooks#stop) hook that scans the working tree once per turn. For per-call coverage instead, also match `Bash|PowerShell` and have your script list modified and untracked files with `git status --porcelain`. The [PowerShell hook input section](/docs/en/hooks#powershell) explains why matching `Bash` alone is not enough. To run a hook when a specific file changes on disk, whatever wrote it, use a [FileChanged](/docs/en/hooks#filechanged) hook. </Note> Each event type matches on a specific field:
hooks-guide Changed · +1 / -1 lines
from line 666
| `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `PermissionDenied` | tool name | `Bash`, `Edit\|Write`, `mcp__.*` | | `SessionStart` | how the session started | `startup`, `resume`, `clear`, `compact`, `fork` | | `Setup` | which CLI flag triggered setup | `init`, `maintenance` | -| `SessionEnd` | why the session ended | `clear`, `resume`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` | +| `SessionEnd` | why the session ended | `clear`, `resume`, `logout`, `prompt_input_exit`, `other` | | `Notification` | notification type | `permission_prompt`, `idle_prompt`, `auth_success`, `elicitation_dialog`, `elicitation_url_dialog`, `elicitation_complete`, `elicitation_response`, `agent_needs_input`, `agent_completed` | | `SubagentStart` | agent type | `general-purpose`, `Explore`, `Plan`, or custom agent names | | `PreCompact`, `PostCompact` | what triggered compaction | `manual`, `auto` |
hooks-guide Changed · +5 / -3 lines
from line 179
| Matcher | Fires when | | :----------------------- | :------------------------------------------------------------------------------------------------------- | -| `permission_prompt` | Claude needs you to approve a tool use and you haven't typed for about 6 seconds | +| `permission_prompt` | Claude needs you to approve a tool use and the prompt has waited about six seconds | | `idle_prompt` | Claude finished responding about 60 seconds ago and you haven't typed since | | `auth_success` | Authentication completes | -| `elicitation_dialog` | An MCP server opens an elicitation form and you haven't typed for about 6 seconds | -| `elicitation_url_dialog` | An MCP server asks you to open a browser URL and you haven't typed for about 6 seconds | +| `elicitation_dialog` | An MCP server opens an elicitation form and you haven't typed for about six seconds | +| `elicitation_url_dialog` | An MCP server asks you to open a browser URL and you haven't typed for about six seconds | | `elicitation_complete` | An MCP elicitation form is submitted or dismissed | | `elicitation_response` | An MCP elicitation response is sent back to the server | | `agent_needs_input` | A background session starts waiting on your input. Fires only while [agent view](/docs/en/agent-view) is open | | `agent_completed` | A background session finishes or fails. Fires only while [agent view](/docs/en/agent-view) is open | + +Claude Code times `permission_prompt` differently in a terminal and in Claude Desktop, the VS Code extension, and other hosts that answer permission requests through the Agent SDK. See [when each notification type fires](/docs/en/hooks#notification) for both timings. The `agent_needs_input` and `agent_completed` matchers require Claude Code v2.1.198 or later.
hooks-guide Changed · +1 / -1 lines
from line 67
</Step> <Step title="Test the hook"> - Press `Esc` to return to the CLI. Ask Claude to do something that requires permission, then switch away from the terminal. You should receive a desktop notification. + Press `Esc` to return to the CLI. Press `Shift+Tab` until the status bar shows `⏸ manual mode on`, ask Claude to do something that requires permission, then switch away from the terminal. You should receive a desktop notification. </Step> </Steps>
hooks-guide Changed · +4 / -4 lines
from line 816
For decisions that require judgment rather than deterministic rules, use `type: "prompt"` hooks. Instead of running a shell command, Claude Code sends your prompt and the hook's input data to a Claude model, Haiku by default, to make the decision. You can specify a different model with the `model` field if you need more capability. -The model's only job is to return a yes/no decision as JSON: +The model's only job is to return its decision as JSON: * `"ok": true`: the action proceeds * `"ok": false`: what happens depends on the event: - * `Stop` and `SubagentStop`: the `reason` is fed back to Claude so it keeps working + * `Stop` and `SubagentStop`: the `reason` is fed back to Claude so it keeps working, unless the response also sets `"impossible": true` to mark the condition as one that can never be satisfied, in which case Claude Code allows the stop and the turn ends * `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` on the hook to instead return the `reason` to Claude as the tool error, so it can adjust and continue. Before v2.1.210, the deny `reason` was returned to Claude as the tool error and the turn continued * `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 * `PostToolBatch`, `UserPromptSubmit`, and `UserPromptExpansion`: the turn ends and the `reason` appears in the chat as a warning line -This example uses a `Stop` hook to ask the model whether all requested tasks are complete. If the model returns `"ok": false`, Claude keeps working and uses the `reason` as its next instruction: +This example uses a `Stop` hook to ask the model whether all requested tasks are complete. If the model returns `"ok": false` because the condition isn't met yet, Claude keeps working and uses the `reason` as its next instruction: ```json theme={null} {
from line 854
When verification requires inspecting files or running commands, use `type: "agent"` hooks. Unlike prompt hooks, which make a single LLM call, agent hooks spawn a subagent that can read files, search code, and use other tools to verify conditions before returning a decision. -Agent hooks use the same `"ok"` / `"reason"` response format as prompt hooks, with a longer default timeout of 60 seconds and up to 50 tool-use turns. On `ok: false`, Claude Code handles an agent hook the way it handles a prompt hook with `continueOnBlock: true` on the same event, so on `PreToolUse` and `PostToolUse` the turn continues; agent hooks have no `continueOnBlock` field. See [agent hook configuration](/docs/en/hooks#agent-hook-configuration) for the fields, including the `$ARGUMENTS` placeholder that Claude Code replaces with the hook's JSON input. +Agent hooks use the `"ok"` / `"reason"` response format with a longer default timeout of 60 seconds and up to 50 tool-use turns. They don't support the prompt-hook `impossible` field. On `ok: false`, Claude Code handles an agent hook the way it handles a prompt hook with `continueOnBlock: true` on the same event, so on `PreToolUse` and `PostToolUse` the turn continues; agent hooks have no `continueOnBlock` field. See [agent hook configuration](/docs/en/hooks#agent-hook-configuration) for the fields, including the `$ARGUMENTS` placeholder that Claude Code replaces with the hook's JSON input. This example verifies that tests pass before allowing Claude to stop: