757 Hook 输入和输出757 Hook 输入和输出
758</h2>758</h2>
759 759
760命令 hook 通过 stdin 接收 JSON 数据,并通过退出代码、stdout 和 stderr 传达结果。HTTP hook 接收与 POST 请求体相同的 JSON,并通过 HTTP 响应体传达结果。本节涵盖所有事件通用的字段和行为。[Hook 事件](#hook-events)下的每个事件部分包括其特定的输入架构和决策控制选项。760命令 hook 通过 stdin 接收 JSON 数据,并通过退出码、stdout 和 stderr 传达结果。HTTP hook 接收与 POST 请求体相同的 JSON,并通过 HTTP 响应体传达结果。本节涵盖所有事件通用的字段和行为。[Hook 事件](#hook-events)下的每个事件部分包括其特定的输入 schema 和决策控制选项。
761 761
762在 macOS 和 Linux 上,命令 hook 在没有控制终端的自己的会话中运行。hook 进程和任何子进程无法打开 `/dev/tty` 或直接向 Claude Code 界面发送转义序列。Windows 没有 `/dev/tty`。762在 macOS 和 Linux 上,命令 hook 在没有控制终端的自己的会话中运行。hook 进程和任何子进程无法打开 `/dev/tty` 或直接向 Claude Code 界面发送转义序列。Windows 没有 `/dev/tty`。
763 763
773| :- | :- |773| :- | :- |
774| `session_id` | 当前会话标识符 |774| `session_id` | 当前会话标识符 |
775| `prompt_id` | 标识当前正在处理的用户提示词的 UUID。与 [OpenTelemetry 事件上的 `prompt.id` 属性](/docs/zh-CN/monitoring-usage#event-correlation-attributes)匹配,因此您可以将 hook 输出与单个提示词的遥测关联起来。在第一个用户输入之前不存在 |775| `prompt_id` | 标识当前正在处理的用户提示词的 UUID。与 [OpenTelemetry 事件上的 `prompt.id` 属性](/docs/zh-CN/monitoring-usage#event-correlation-attributes)匹配,因此您可以将 hook 输出与单个提示词的遥测关联起来。在第一个用户输入之前不存在 |
776| `transcript_path` | 对话 JSON 的路径。转录文件异步写入,可能滞后于内存中的对话,因此当 hook 触发时,它可能还不包括当前轮次的最新消息。需要当前轮次最终助手文本的 hook 应在 [Stop](#stop) 和 [SubagentStop](#subagentstop) 上使用 `last_assistant_message`,而不是读取转录 |776| `transcript_path` | 对话 JSON 的路径。会话记录文件异步写入,可能滞后于内存中的对话,因此当 hook 触发时,它可能还不包括当前轮次的最新消息。需要当前轮次最终助手文本的 hook 应在 [Stop](#stop) 和 [SubagentStop](#subagentstop) 上使用 `last_assistant_message`,而不是读取会话记录 |
777| `cwd` | 调用 hook 时的当前工作目录 |777| `cwd` | 调用 hook 时的当前工作目录 |
778| `scratchpad_dir` | 会话的 [scratchpad 目录](/docs/zh-CN/claude-directory#session-scratchpad-directory)的路径,Claude 在其中保存临时工作文件。当会话没有 scratchpad 或 temp 目录不可用时不存在。需要 Claude Code v2.1.257 或更高版本 |778| `scratchpad_dir` | 会话的 [scratchpad 目录](/docs/zh-CN/claude-directory#session-scratchpad-directory)的路径,Claude 在其中保存临时工作文件。当会话没有 scratchpad 或 temp 目录不可用时不存在。需要 Claude Code v2.1.257 或更高版本 |
779| `permission_mode` | 当前[权限模式](/docs/zh-CN/permissions#permission-modes):`"default"`、`"plan"`、`"acceptEdits"`、`"auto"`、`"dontAsk"` 或 `"bypassPermissions"`。标记为**手动**的模式作为 `"default"` 到达,从不作为 `"manual"`,因此匹配 `"default"` 的脚本继续工作。并非所有事件都接收此字段。检查每个 [hook 事件](#hook-events)部分中的 JSON 示例 |779| `permission_mode` | 当前[权限模式](/docs/zh-CN/permissions#permission-modes):`"default"`、`"plan"`、`"acceptEdits"`、`"auto"`、`"dontAsk"` 或 `"bypassPermissions"`。标记为**手动**的模式作为 `"default"` 到达,从不作为 `"manual"`,因此匹配 `"default"` 的脚本继续工作。并非所有事件都接收此字段。检查每个 [hook 事件](#hook-events)部分中的 JSON 示例 |
780| `effort` | 对象,其 `level` 字段保存 hook 运行时生效的[工作量级别](/docs/zh-CN/model-config#adjust-effort-level):`"low"`、`"medium"`、`"high"`、`"xhigh"` 或 `"max"`。如果您设置了活跃模型不支持的级别,`level` 会报告 Claude Code 运行的级别;[调整工作量级别](/docs/zh-CN/model-config#adjust-effort-level)说明它如何选择该级别。该对象与[状态行](/docs/zh-CN/statusline#available-data) `effort` 字段匹配。对于在工具使用上下文中触发的事件(如 `PreToolUse`、`PostToolUse`、`Stop` 和 `SubagentStop`),当当前模型支持工作量参数时存在。该级别也可作为 `$CLAUDE_EFFORT` 环境变量供 hook 命令和 Bash 工具使用。 |780| `effort` | 对象,其 `level` 字段保存 hook 运行时生效的 [effort 级别](/docs/zh-CN/model-config#adjust-effort-level):`"low"`、`"medium"`、`"high"`、`"xhigh"` 或 `"max"`。如果您设置了活跃模型不支持的级别,`level` 会报告 Claude Code 实际运行的级别;[调整 effort 级别](/docs/zh-CN/model-config#adjust-effort-level)说明它如何选择该级别。该对象与[状态栏](/docs/zh-CN/statusline#available-data) `effort` 字段匹配。对于在工具使用上下文中触发的事件(如 `PreToolUse`、`PostToolUse`、`Stop` 和 `SubagentStop`),当当前模型支持 effort 参数时存在。该级别也可作为 `$CLAUDE_EFFORT` 环境变量供 hook 命令和 Bash 工具使用。 |
781| `hook_event_name` | 触发的事件的名称 |781| `hook_event_name` | 触发的事件的名称 |
782 782
783使用 `--agent` 运行或在 subagent 内部时,包括两个额外字段:783使用 `--agent` 运行或在子代理内部时,包括两个额外字段:
784 784
785| 字段 | 描述 |785| 字段 | 描述 |
786| :- | :- |786| :- | :- |
787| `agent_id` | subagent 的唯一标识符。仅当 hook 在 subagent 调用内触发时存在。使用此来区分 subagent hook 调用和主线程调用。 |787| `agent_id` | 子代理的唯一标识符。仅当 hook 在子代理调用内触发时存在。使用此来区分子代理 hook 调用和主线程调用。 |
788| `agent_type` | Agent 名称(例如,`"Explore"` 或 `"security-reviewer"`)。当会话使用 `--agent` 或 hook 在 subagent 内触发时存在。对于 subagent,subagent 的类型优先于会话的 `--agent` 值。请参阅 [SubagentStart](#subagentstart) 了解自定义和插件 subagent 报告的值以及如何针对插件范围的名称编写匹配器。 |788| `agent_type` | Agent 名称(例如,`"Explore"` 或 `"security-reviewer"`)。当会话使用 `--agent` 或 hook 在子代理内触发时存在。对于子代理,子代理的类型优先于会话的 `--agent` 值。请参阅 [SubagentStart](#subagentstart) 了解自定义和插件子代理报告的值以及如何针对限定于插件的名称编写匹配器。 |
789 789
790只有 [`SessionStart`](#sessionstart) hook 可以接收 `model` 字段,Claude Code 并不总是包括它。[`PreModelSwitch`](#premodelswitch) 和 [`PostModelSwitch`](#postmodelswitch) hook 改为接收 `from_model` 和 `to_model`,因此使用 PostModelSwitch hook 来跟踪模型在会话期间的变化。790只有 [`SessionStart`](#sessionstart) hook 可以接收 `model` 字段,Claude Code 并不总是包括它。[`PreModelSwitch`](#premodelswitch) 和 [`PostModelSwitch`](#postmodelswitch) hook 改为接收 `from_model` 和 `to_model`,因此使用 PostModelSwitch hook 来跟踪模型在会话期间的变化。
791 791
818`tool_name`、`tool_input` 和 `tool_use_id` 字段是事件特定的。每个 [hook 事件](#hook-events)部分记录该事件的额外字段。818`tool_name`、`tool_input` 和 `tool_use_id` 字段是事件特定的。每个 [hook 事件](#hook-events)部分记录该事件的额外字段。
819 819
820<h3 id="exit-code-output">820<h3 id="exit-code-output">
821 退出代码输出821 退出码输出
822</h3>822</h3>
823 823
824来自 hook 命令的退出代码告诉 Claude Code 该操作是否应继续、被阻止或被忽略。退出代码不单独起作用。Claude Code 从 stdout 读取[JSON 输出字段](#json-output),无论退出代码是什么,对于使用标准决策模型的事件,通过架构验证的解析对象与代码一起生效。退出 2 的阻止是 JSON 无法覆盖的唯一结果。824来自 hook 命令的退出码告诉 Claude Code 该操作是否应继续、被阻止或被忽略。退出码不单独起作用。Claude Code 从 stdout 读取 [JSON 输出字段](#json-output),无论退出码是什么(不仅仅是 0),对于使用标准决策模型的事件,通过 schema 验证的解析对象与退出码一起生效。退出 2 的阻止是 JSON 无法覆盖的唯一结果。
825 825
826两个表拥有每个事件的例外:[每个事件的退出代码 2 行为](#exit-code-2-behavior-per-event)说明退出代码对每个事件的作用,[决策控制](#decision-control)说明每个事件接受哪些决策字段。通用字段如 `systemMessage` 在大多数事件中工作,并在 [JSON 输出](#json-output)表中列出。826两个表负责说明每个事件的例外:[每个事件的退出码 2 行为](#exit-code-2-behavior-per-event)说明退出码对每个事件的作用,[决策控制](#decision-control)说明每个事件接受哪些决策字段。通用字段如 `systemMessage` 在大多数事件中工作,并在 [JSON 输出](#json-output)表中列出。
827 827
828<h4 id="exit-code-0">828<h4 id="exit-code-0">
829 退出代码 0829 退出码 0
830</h4>830</h4>
831 831
832退出 0 表示成功,是您打印 JSON 进行结构化控制时的预期退出代码。832退出 0 表示成功,是您打印 JSON 进行结构化控制时的预期退出码。
833 833
834对于大多数事件,Claude Code 将 stdout 写入调试日志,不在转录中显示。例外是 `UserPromptSubmit`、`UserPromptExpansion`、`SessionStart` 和 `PostModelSwitch`,其中 Claude Code 添加纯文本 stdout 作为 Claude 可以看到和作用的上下文。834对于大多数事件,Claude Code 将 stdout 写入调试日志,不在会话记录中显示。例外是 `UserPromptSubmit`、`UserPromptExpansion`、`SessionStart` 和 `PostModelSwitch`,其中 Claude Code 添加纯文本 stdout 作为 Claude 可以看到并据此行动的上下文。
835 835
836Claude Code 是否将您的 stdout 读取为 [JSON 输出](#json-output)或纯文本取决于它如何开始和结束,忽略周围的空格:836Claude Code 是否将您的 stdout 读取为 [JSON 输出](#json-output)或纯文本取决于它如何开始和结束,忽略周围的空白:
837 837
838* **以 `{` 开始并以 `}` 结束**:Claude Code 将其解析为 JSON。当输出是两行或更多行,每行本身都解析为 JSON,且没有行是设置字段的 [JSON 输出](#json-output)对象时,Claude Code 将整个输出视为纯文本。当其中一行确实设置了字段时,整个输出是解析失败,如下所述。838* **以 `{` 开始并以 `}` 结束**:Claude Code 将其解析为 JSON。当输出是两行或更多行,每行本身都解析为 JSON,且没有行是设置字段的 [JSON 输出](#json-output)对象时,Claude Code 将整个输出视为纯文本。当其中一行确实设置了字段时,整个输出是解析失败,如下所述。
839* **以 `{` 开始但不以 `}` 结束**:Claude Code 将其视为纯文本。839* **以 `{` 开始但不以 `}` 结束**:Claude Code 将其视为纯文本。
840* **以其他任何内容开始**:Claude Code 将其视为纯文本,即使它是 JSON 数组或带引号的 JSON 字符串也是如此。840* **以其他任何内容开始**:Claude Code 将其视为纯文本,即使它是 JSON 数组或带引号的 JSON 字符串也是如此。
841 841
842对于使用标准决策模型的事件,退出 0 且解析对象未通过架构验证是非阻止错误:操作继续,转录显示 `<hook name> hook error` 通知,带有验证消息。在任何退出代码(除 2 外)上都会发生相同情况,而[退出 2 仍然阻止](#exit-code-2)。842对于使用标准决策模型的事件,退出 0 且解析对象未通过 schema 验证是非阻止错误:操作继续,会话记录显示 `<hook name> hook error` 通知,带有验证消息。在除 2 以外的任何退出码上都会发生相同情况,而[退出 2 仍然阻止](#exit-code-2)。
843 843
844对于使用标准决策模型的事件,当 Claude Code 尝试将您的 stdout 解析为 JSON 且无法解析时,它在除 2 外的每个退出代码上报告非阻止错误。转录显示 `<hook name> hook error` 通知,带有解析消息。在添加纯文本 stdout 作为上下文的事件上,Claude Code 不添加文本。在 v2.1.248 之前,Claude Code 将该 stdout 视为纯文本。844对于使用标准决策模型的事件,当 Claude Code 尝试将您的 stdout 解析为 JSON 且无法解析时,它在除 2 外的每个退出码上报告非阻止错误。会话记录显示 `<hook name> hook error` 通知,带有解析消息。在添加纯文本 stdout 作为上下文的事件上,Claude Code 不添加文本。在 v2.1.248 之前,Claude Code 将该 stdout 视为纯文本。
845 845
846来自退出 0 的 hook 的 Stderr 仅进入调试日志,从不进入转录,Claude 从不看到它。要自己读取它,请启用[调试日志](#debug-hooks)。要从 `PostToolUse` 或 `PostToolUseFailure` hook 向 Claude 显示警告,请改为退出 2,以便[Claude 看到 stderr](#exit-code-2-behavior-per-event),即使工具已经运行。846来自退出 0 的 hook 的 stderr 仅进入调试日志,从不进入会话记录,Claude 从不看到它。要自己读取它,请启用[调试日志](#debug-hooks)。要从 `PostToolUse` 或 `PostToolUseFailure` hook 向 Claude 显示警告,请改为退出 2,以便 [Claude 看到 stderr](#exit-code-2-behavior-per-event),即使工具已经运行。
847 847
848<h4 id="exit-code-2">848<h4 id="exit-code-2">
849 退出代码 2849 退出码 2
850</h4>850</h4>
851 851
852退出 2 表示阻止错误。在[可以阻止的事件](#exit-code-2-behavior-per-event)上,退出 2 无论您是否打印 JSON 都会阻止:即使 JSON `permissionDecision` 为 `"allow"` 也无法覆盖它。Claude Code 仍然读取 stdout 上的任何有效 [JSON 输出](#json-output)。在 `Elicitation` 和 `ElicitationResult` 上,退出 2 hook 的 `hookSpecificOutput` 被忽略。852退出 2 表示阻止错误。在[可以阻止的事件](#exit-code-2-behavior-per-event)上,无论您是否打印 JSON,退出 2 都会阻止:即使 JSON `permissionDecision` 为 `"allow"` 也无法覆盖它。Claude Code 仍然读取 stdout 上的任何有效 [JSON 输出](#json-output)。在 `Elicitation` 和 `ElicitationResult` 上,退出 2 的 hook 的 `hookSpecificOutput` 被忽略。
853 853
854阻止消息是您的 JSON 的阻止决策的原因(当它做出一个时),否则是您的 stderr 文本。阻止做什么因事件而异:`PreToolUse` 阻止工具调用,`UserPromptSubmit` 拒绝提示,等等。[每个事件的退出代码 2 行为](#exit-code-2-behavior-per-event)列出每个事件的效果,每个事件的部分说明消息去向。854阻止消息是您的 JSON 阻止决策中的原因(如果它做出了阻止决策),否则是您的 stderr 文本。阻止的作用因事件而异:`PreToolUse` 阻止工具调用,`UserPromptSubmit` 拒绝提示词,等等。[每个事件的退出码 2 行为](#exit-code-2-behavior-per-event)列出每个事件的效果,每个事件的部分说明消息去向。
855 855
856退出 2 的 hook 同时打印 JSON 且未通过 [JSON 输出](#json-output)架构验证仍然阻止:Claude Code 使用 stderr 作为阻止原因,并在调试日志中记录验证失败。在 v2.1.214 之前,Claude Code 将该组合视为非阻止错误,操作继续。856退出 2 的 hook 同时打印未通过 [JSON 输出](#json-output) schema 验证的 JSON 时仍然阻止:Claude Code 使用 stderr 作为阻止原因,并在调试日志中记录验证失败。在 v2.1.214 之前,Claude Code 将该组合视为非阻止错误,操作继续。
857 857
858此脚本通过退出 2 阻止 `rm` 命令,并将所有其他命令留给正常权限流:858此脚本通过退出 2 阻止 `rm` 命令,并将所有其他命令留给正常权限流程:
859 859
860```bash theme={null}860```bash theme={null}
861#!/bin/bash861#!/bin/bash
872```872```
873 873
874<h4 id="other-exit-codes">874<h4 id="other-exit-codes">
875 其他退出代码875 其他退出码
876</h4>876</h4>
877 877
878任何其他退出代码对于大多数 hook 事件本身不会阻止。发生什么取决于您的 stdout:878对于大多数 hook 事件,任何其他退出码本身不会阻止。发生什么取决于您的 stdout:
879 879
880* 使用通过架构验证的解析对象,对于使用标准决策模型的事件,Claude Code 忽略退出代码,JSON 单独决定结果:880* 使用通过 schema 验证的解析对象时,对于使用标准决策模型的事件,Claude Code 忽略退出码,仅由 JSON 决定结果:
881 * 事件支持的每个字段都被接受,包括 `permissionDecision`、`additionalContext`、`updatedInput` 和 `systemMessage`,hook 不被报告为错误。881 * 事件支持的每个字段都会生效,包括 `permissionDecision`、`additionalContext`、`updatedInput` 和 `systemMessage`,hook 不被报告为错误。
882 * [决策控制](#decision-control)列出每个事件的决策字段;通用字段如 `systemMessage` 遵循 [JSON 输出](#json-output)表。882 * [决策控制](#decision-control)列出每个事件的决策字段;通用字段如 `systemMessage` 遵循 [JSON 输出](#json-output)表。
883* 使用未通过架构验证的解析对象,对于使用标准决策模型的事件,它与[退出 0 上](#exit-code-0)相同的非阻止错误:操作继续,`<hook name> hook error` 通知带有验证消息。883* 使用未通过 schema 验证的解析对象时,对于使用标准决策模型的事件,它与[退出 0 时](#exit-code-0)相同,是非阻止错误:操作继续,`<hook name> hook error` 通知带有验证消息。
884* 使用 Claude Code [尝试解析为 JSON](#exit-code-0)且无法解析的 stdout,Claude Code 报告与退出 0 上相同的非阻止错误,用于使用标准决策模型的事件。操作继续,通知带有解析消息。884* 使用 Claude Code [尝试解析为 JSON](#exit-code-0) 但无法解析的 stdout 时,对于使用标准决策模型的事件,Claude Code 报告与退出 0 时相同的非阻止错误。操作继续,通知带有解析消息。
885* 使用 Claude Code [视为纯文本](#exit-code-0)的 stdout,或使用空 stdout,对于大多数 hook 事件是非阻止错误:操作继续,转录显示 `<hook name> hook error` 通知,后跟 stderr 的第一行,前缀为 `Failed with non-blocking status code:`。要捕获完整 stderr,请启用[调试日志](#debug-hooks)。885* 使用 Claude Code [视为纯文本](#exit-code-0)的 stdout,或使用空 stdout 时,对于大多数 hook 事件是非阻止错误:操作继续,会话记录显示 `<hook name> hook error` 通知,后跟 stderr 的第一行,前缀为 `Failed with non-blocking status code:`。要捕获完整 stderr,请启用[调试日志](#debug-hooks)。
886 886
887标准决策模型之外的事件在[每个事件表](#exit-code-2-behavior-per-event)中保持自己的行:`WorktreeCreate` 在任何非零退出时失败创建,无论您的 JSON 说什么,事件丢弃 hook 输出(如 `StopFailure`)在每个退出代码上忽略您的 JSON,除了副作用字段如 `terminalSequence`,它仍然触发。887标准决策模型之外的事件在[每个事件表](#exit-code-2-behavior-per-event)中保留自己的行:`WorktreeCreate` 在任何非零退出时都会使创建失败,无论您的 JSON 说什么;完全丢弃 hook 输出的事件(如 `StopFailure`)在每个退出码上都忽略您的 JSON,但 `terminalSequence` 等副作用字段除外,它们仍会触发。
888 888
889无法启动的 hook 落入相同的非阻止桶。当脚本路径不存在或不可执行时,shell 以代码(如 127)退出,您看到相同的通知,带有解释器的消息,例如 `Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: No such file or directory`。对于大多数 hook 事件,操作继续。当您设置策略 hook 时,在其第一次运行时观察此通知:`settings.json` 中的拼写错误的路径使门无声地禁用。889无法启动的 hook 也归入相同的非阻止类别。当脚本路径不存在或不可执行时,shell 以某个代码(如 127)退出,您会看到相同的通知,带有解释器的消息,例如 `Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: No such file or directory`。对于大多数 hook 事件,操作继续。当您设置策略 hook 时,请在其第一次运行时留意此通知:`settings.json` 中拼写错误的路径会使该关卡被悄无声息地禁用。
890 890
891<Warning>891<Warning>
892 对于大多数 hook 事件,退出代码 2 是唯一通过代码单独阻止的退出代码。没有 stdout 上的有效 JSON,Claude Code 将退出代码 1 视为非阻止错误并继续操作,即使 1 是传统的 Unix 失败代码。如果您的 hook 旨在强制执行策略,请使用 `exit 2`。worktree 事件不同:来自 `WorktreeCreate` 的任何非零退出代码中止 worktree 创建,来自 `WorktreeRemove` 的任何非零退出代码使 worktree 移除失败(如果目录仍然存在)。892 对于大多数 hook 事件,退出码 2 是唯一仅凭退出码即可阻止的退出码。如果 stdout 上没有有效 JSON,Claude Code 将退出码 1 视为非阻止错误并继续操作,即使 1 是传统的 Unix 失败代码。如果您的 hook 旨在强制执行策略,请使用 `exit 2`。worktree 事件不同:来自 `WorktreeCreate` 的任何非零退出码都会中止 worktree 创建,来自 `WorktreeRemove` 的任何非零退出码会在目录之后仍然存在时使 worktree 移除失败。
893</Warning>893</Warning>
894 894
895<h4 id="timeouts">895<h4 id="timeouts">
896 超时896 超时
897</h4>897</h4>
898 898
899除了您使用 [`async: true`](#run-hooks-in-the-background) 运行的命令 hook,Claude Code 取消达到其 [`timeout`](#common-fields) 的 `command`、`http` 或 `mcp_tool` hook,丢弃 hook 的输出,因此在大多数事件上超时的 hook 不呈现决策。899除了您使用 [`async: true`](#run-hooks-in-the-background) 运行的命令 hook 外,Claude Code 会取消达到其 [`timeout`](#common-fields) 的 `command`、`http` 或 `mcp_tool` hook,并丢弃 hook 的输出,因此在大多数事件上,超时的 hook 不会做出决策。
900 900
901在 [`PreModelSwitch`](#premodelswitch) 上,在其超时处取消的 hook 阻止模型切换。在 `PreToolUse` 上,两个 hook 系列不同:901在 [`PreModelSwitch`](#premodelswitch) 上,因超时被取消的 hook 会阻止模型切换。在 `PreToolUse` 上,两类 hook 的行为不同:
902 902
903* 超时的 `command`、`http` 或 `mcp_tool` hook 不阻止工具调用。调用通过正常[权限流](/docs/zh-CN/permissions)继续,因此不要指望停滞的 hook 充当门。903* 超时的 `command`、`http` 或 `mcp_tool` hook 不阻止工具调用。调用通过正常[权限流程](/docs/zh-CN/permissions)继续,因此不要指望停滞的 hook 充当关卡。
904* 超过其超时的 [Agent SDK 回调 hook](/docs/zh-CN/agent-sdk/hooks) [阻止工具调用](#pretooluse)。904* 超过其超时时间的 [Agent SDK 回调 hook](/docs/zh-CN/agent-sdk/hooks) 会[阻止工具调用](#pretooluse)。
905 905
906<h4 id="exit-code-2-behavior-per-event">906<h4 id="exit-code-2-behavior-per-event">
907 每个事件的退出代码 2 行为907 每个事件的退出码 2 行为
908</h4>908</h4>
909 909
910退出代码 2 是 hook 发出"停止,不要这样做"信号的方式。效果取决于事件,因为某些事件代表可以被阻止的操作(如尚未发生的工具调用),而其他事件代表已经发生或无法防止的事情。910退出码 2 是 hook 发出"停止,不要这样做"信号的方式。效果取决于事件,因为某些事件代表可以被阻止的操作(如尚未发生的工具调用),而其他事件代表已经发生或无法防止的事情。
911 911
912| Hook 事件 | 可以阻止? | 退出 2 时发生什么 |912| Hook 事件 | 可以阻止? | 退出 2 时发生什么 |
913| :- | :- | :- |913| :- | :- | :- |
914| `PreToolUse` | 是 | 阻止工具调用 |914| `PreToolUse` | 是 | 阻止工具调用 |
915| `PermissionRequest` | 否 | 此事件不接受退出代码 2,权限流程保持不变。改为通过 [`decision` 对象](#permissionrequest-decision-control)拒绝 |915| `PermissionRequest` | 否 | 此事件不接受退出码 2,权限流程保持不变。改为通过 [`decision` 对象](#permissionrequest-decision-control)拒绝 |
916| `UserPromptSubmit` | 是 | 阻止提示,所以它永远不会到达 Claude。请参阅[被阻止的提示留下什么](#what-a-blocked-prompt-leaves-behind) |916| `UserPromptSubmit` | 是 | 阻止提示词,使其永远不会到达 Claude。请参阅[被阻止的提示词留下什么](#what-a-blocked-prompt-leaves-behind) |
917| `UserPromptExpansion` | 是 | 阻止扩展 |917| `UserPromptExpansion` | 是 | 阻止扩展 |
918| `Stop` | 是 | 防止 Claude 停止,继续对话 |918| `Stop` | 是 | 防止 Claude 停止,继续对话 |
919| `SubagentStop` | 是 | 防止 subagent 停止 |919| `SubagentStop` | 是 | 防止子代理停止 |
920| `TeammateIdle` | 是 | 防止队友空闲,因此它继续工作 |920| `TeammateIdle` | 是 | 防止队友空闲,因此它继续工作 |
921| `TaskCreated` | 是 | 回滚任务创建 |921| `TaskCreated` | 是 | 回滚任务创建 |
922| `TaskCompleted` | 是 | 防止任务被标记为已完成 |922| `TaskCompleted` | 是 | 防止任务被标记为已完成 |
923| `ConfigChange` | 是 | 阻止配置更改生效(除 `policy_settings` 外) |923| `ConfigChange` | 是 | 阻止配置更改生效(除 `policy_settings` 外) |
924| `StopFailure` | 否 | 输出和退出代码被忽略,除 `terminalSequence` 外 |924| `StopFailure` | 否 | 输出和退出码被忽略,除 `terminalSequence` 外 |
925| `PostToolUse` | 否 | 向 Claude 显示 stderr;工具已经运行 |925| `PostToolUse` | 否 | 向 Claude 显示 stderr;工具已经运行 |
926| `PostToolUseFailure` | 否 | 向 Claude 显示 stderr;工具已经失败 |926| `PostToolUseFailure` | 否 | 向 Claude 显示 stderr;工具已经失败 |
927| `PostToolBatch` | 是 | 在下一个模型调用之前停止代理循环 |927| `PostToolBatch` | 是 | 在下一次模型调用之前停止智能体循环 |
928| `PermissionDenied` | 否 | 退出代码和 stderr 被忽略,因为拒绝已经发生。使用 JSON `hookSpecificOutput.retry: true` 告诉模型它可能重试;Claude Code 对[无判决拒绝](#permissiondenied-decision-control)忽略 `retry: true` |928| `PermissionDenied` | 否 | 退出码和 stderr 被忽略,因为拒绝已经发生。使用 JSON `hookSpecificOutput.retry: true` 告诉模型它可以重试;Claude Code 对[无判决拒绝](#permissiondenied-decision-control)忽略 `retry: true` |
929| `Notification` | 否 | 退出代码和 stderr 被忽略 |929| `Notification` | 否 | 退出码和 stderr 被忽略 |
930| `SubagentStart` | 否 | 仅向用户显示 stderr |930| `SubagentStart` | 否 | 仅向用户显示 stderr |
931| `SessionStart` | 否 | 仅向用户显示 stderr |931| `SessionStart` | 否 | 仅向用户显示 stderr |
932| `Setup` | 否 | 退出代码和 stderr 被忽略 |932| `Setup` | 否 | 退出码和 stderr 被忽略 |
933| `SessionEnd` | 否 | 仅向用户显示 stderr |933| `SessionEnd` | 否 | 仅向用户显示 stderr |
934| `CwdChanged` | 否 | 仅向用户显示 stderr |934| `CwdChanged` | 否 | 仅向用户显示 stderr |
935| `DirectoryAdded` | 否 | Stderr 进入调试日志;目录已经添加 |935| `DirectoryAdded` | 否 | stderr 进入调试日志;目录已经添加 |
936| `FileChanged` | 否 | 仅向用户显示 stderr |936| `FileChanged` | 否 | 仅向用户显示 stderr |
937| `PreCompact` | 是 | 阻止压缩 |937| `PreCompact` | 是 | 阻止压缩 |
938| `PostCompact` | 否 | 仅向用户显示 stderr |938| `PostCompact` | 否 | 仅向用户显示 stderr |
939| `PreModelSwitch` | 是 | 阻止模型切换并向用户显示 stderr |939| `PreModelSwitch` | 是 | 阻止模型切换并向用户显示 stderr |
940| `PostModelSwitch` | 否 | 仅向用户显示 stderr;模型已经切换 |940| `PostModelSwitch` | 否 | 仅向用户显示 stderr;模型已经切换 |
941| `Elicitation` | 是 | 拒绝引出 |941| `Elicitation` | 是 | 拒绝该请求,且不显示对话框 |
942| `ElicitationResult` | 是 | 阻止响应(操作变为拒绝) |942| `ElicitationResult` | 是 | 阻止响应(操作变为拒绝) |
943| `WorktreeCreate` | 是 | 任何非零退出代码导致 worktree 创建失败 |943| `WorktreeCreate` | 是 | 任何非零退出码导致 worktree 创建失败 |
944| `WorktreeRemove` | 是 | 任何非零退出代码导致 worktree 移除失败(如果目录仍然存在)。请参阅 [WorktreeRemove](#worktreeremove) 了解目录发生什么 |944| `WorktreeRemove` | 是 | 任何非零退出码会在目录之后仍然存在时导致 worktree 移除失败。请参阅 [WorktreeRemove](#worktreeremove) 了解目录会发生什么 |
945| `InstructionsLoaded` | 否 | 退出代码被忽略 |945| `InstructionsLoaded` | 否 | 退出码被忽略 |
946| `MessageDisplay` | 否 | 显示原始文本 |946| `MessageDisplay` | 否 | 显示原始文本 |
947 947
948对于 `SessionStart`、`SubagentStart` 和 `PostModelSwitch`,Claude Code 在转录中呈现退出代码 2 stderr 作为 `<hook name> hook error` 通知,与呈现[非阻止错误](#exit-code-output)的方式相同。Claude 看不到它,会话或 subagent 继续。对于 `SubagentStart`,通知出现在 subagent 自己的转录中,而不是在父对话中。948对于 `SessionStart`、`SubagentStart` 和 `PostModelSwitch`,Claude Code 在会话记录中将退出码 2 的 stderr 呈现为 `<hook name> hook error` 通知,与呈现[非阻止错误](#exit-code-output)的方式相同。Claude 看不到它,会话或子代理继续。对于 `SubagentStart`,通知出现在子代理自己的会话记录中,而不是在父对话中。
949 949
950<h3 id="http-response-handling">950<h3 id="http-response-handling">
951 HTTP 响应处理951 HTTP 响应处理
952</h3>952</h3>
953 953
954HTTP hook 使用 HTTP 状态代码和响应体而不是退出代码和 stdout。下面的结果适用于大多数事件;在[每个事件表](#exit-code-2-behavior-per-event)中有自己的失败合约的事件(如 `WorktreeCreate`)将该合约应用于失败的 HTTP hook:954HTTP hook 使用 HTTP 状态码和响应体,而不是退出码和 stdout。下面的结果适用于大多数事件;在[每个事件表](#exit-code-2-behavior-per-event)中有自己失败约定的事件(如 `WorktreeCreate`)也会将该约定应用于失败的 HTTP hook:
955 955
956* **2xx 且空体**:成功,等同于退出代码 0 且无输出956* **2xx 且空响应体**:成功,等同于退出码 0 且无输出
957* **2xx 且 JSON 对象体**:使用与命令 hook 相同的 [JSON 输出](#json-output)架构解析。未通过架构验证的体是非阻止错误957* **2xx 且 JSON 对象响应体**:使用与命令 hook 相同的 [JSON 输出](#json-output) schema 解析。未通过 schema 验证的响应体是非阻止错误
958* **2xx 且任何其他体,如纯文本**:非阻止错误,处理方式与非 2xx 状态相同。Claude Code 不将文本添加到 Claude 的上下文958* **2xx 且任何其他响应体,如纯文本**:非阻止错误,处理方式与非 2xx 状态相同。Claude Code 不将文本添加到 Claude 的上下文
959* **非 2xx 状态**:非阻止错误,执行继续959* **非 2xx 状态**:非阻止错误,执行继续
960* **连接失败**:非阻止错误,执行继续960* **连接失败**:非阻止错误,执行继续
961* **超时**:hook 被取消,如 [Timeouts](#timeouts) 下所述961* **超时**:hook 被取消,如[超时](#timeouts)下所述
962 962
963与命令 hook 不同,HTTP hook 无法仅通过状态代码发出阻止错误信号。要阻止工具调用或拒绝权限,返回 2xx 响应,其 JSON 体包含适当的决策字段。963与命令 hook 不同,HTTP hook 无法仅通过状态码发出阻止错误信号。要阻止工具调用或拒绝权限,请返回 2xx 响应,其 JSON 响应体包含相应的决策字段。
964 964
965<h3 id="json-output">965<h3 id="json-output">
966 JSON 输出966 JSON 输出
967</h3>967</h3>
968 968
969退出代码只让您阻止或保持沉默,但 JSON 输出给您更细粒度的控制。与其退出代码 2 来阻止,不如退出 0 并将 JSON 对象打印到 stdout。Claude Code 从该 JSON 读取特定字段来控制行为,包括[决策控制](#decision-control)来阻止、允许或升级给用户。969退出码只能让您阻止或保持沉默,而 JSON 输出为您提供更细粒度的控制。与其以代码 2 退出来阻止,不如退出 0 并将 JSON 对象打印到 stdout。Claude Code 从该 JSON 读取特定字段来控制行为,包括用于阻止、允许或升级给用户的[决策控制](#decision-control)。
970 970
971<Note>971<Note>
972 每个 hook 选择一种方法:要么单独使用退出代码进行信号,要么退出 0 并打印 JSON 进行结构化控制。如果您混合它们,退出 2 保持其[阻止效果](#exit-code-2-behavior-per-event),Claude Code 仍然读取 JSON 字段,除了 [Exit code 2](#exit-code-2) 下注明的一个引出例外。972 每个 hook 选择一种方法:要么仅使用退出码发出信号,要么退出 0 并打印 JSON 进行结构化控制。如果您混合使用,退出 2 保持其[阻止效果](#exit-code-2-behavior-per-event),Claude Code 仍然读取 JSON 字段,但[退出码 2](#exit-code-2) 下注明的 elicitation 例外除外。
973</Note>973</Note>
974 974
975您的 hook 的 stdout 必须仅包含 JSON 对象。如果您的 shell 配置文件在启动时打印文本,它可能会干扰 JSON 解析。请参阅故障排除指南中的 [Hook JSON 无效](/docs/zh-CN/hooks-guide#hook-json-has-no-effect)。975您的 hook 的 stdout 必须仅包含 JSON 对象。如果您的 shell 配置文件在启动时打印文本,它可能会干扰 JSON 解析。请参阅故障排除指南中的 [Hook JSON 无效](/docs/zh-CN/hooks-guide#hook-json-has-no-effect)。
977hook 的 `additionalContext`、`systemMessage` 和 `initialUserMessage` 字符串,以及其纯 stdout,限制为 10,000 个字符:977hook 的 `additionalContext`、`systemMessage` 和 `initialUserMessage` 字符串,以及其纯 stdout,限制为 10,000 个字符:
978 978
979* **范围**:Claude Code 单独测量每个字符串,即使多个 hook 为同一事件运行。对于 JSON 输出,每个字段单独测量;纯 stdout 整体测量。979* **范围**:Claude Code 单独测量每个字符串,即使多个 hook 为同一事件运行。对于 JSON 输出,每个字段单独测量;纯 stdout 整体测量。
980* **超过限制**:Claude Code 将输出保存到会话目录中的文件,并用文件路径和最多前 2,000 个字符的预览替换它。大型有效 Bash 结果的处理方式相同,在 [Output limits](/docs/zh-CN/tools-reference#output-limits) 下描述。与该 Bash 上限不同,此上限没有设置或环境变量来提高它。980* **超过限制**:Claude Code 将输出保存到会话目录中的文件,并用文件路径和最多前 2,000 个字符的预览替换它。大型有效 Bash 结果的处理方式相同,在[输出限制](/docs/zh-CN/tools-reference#output-limits)下描述。与该 Bash 上限不同,此上限没有可提高它的设置或环境变量。
981* **读取文件**:Claude Code 不要求 Claude 读取文件,因此将 Claude 必须始终看到的任何内容保持在上限内。981* **读取文件**:Claude Code 不要求 Claude 读取文件,因此请将 Claude 必须始终看到的任何内容保持在上限内。
982 982
983JSON 对象支持三种字段:983JSON 对象支持三种字段:
984 984
985* **通用字段**如 `continue` 在下表中列出。每个事件都接受它们,但某些事件丢弃它们或将 `systemMessage` 传递到转录以外的地方。每个事件的部分说明这一点。`terminalSequence` 也在这些事件上工作,除了 [Emit terminal notifications](#emit-terminal-notifications) 下列出的例外。985* **通用字段**如 `continue` 在下表中列出。每个事件都接受它们,但某些事件会丢弃它们或将 `systemMessage` 传递到会话记录以外的地方。每个事件的部分会说明这一点。`terminalSequence` 也在这些事件上工作,但[发出终端通知](#emit-terminal-notifications)下列出的例外除外。
986* **顶级 `decision` 和 `reason`** 由某些事件用来阻止或提供反馈。986* **顶级 `decision` 和 `reason`** 由某些事件用来阻止或提供反馈。
987* **`hookSpecificOutput`** 是需要更丰富控制的事件的嵌套对象。它需要一个 `hookEventName` 字段设置为事件名称。987* **`hookSpecificOutput`** 是用于需要更丰富控制的事件的嵌套对象。它需要一个设置为事件名称的 `hookEventName` 字段。
988 988
989| 字段 | 默认 | 描述 |989| 字段 | 默认 | 描述 |
990| :- | :- | :- |990| :- | :- | :- |
991| `continue` | `true` | 如果 `false`,Claude 在 hook 运行后完全停止处理。优先于任何事件特定的决策字段 |991| `continue` | `true` | 如果为 `false`,Claude 在 hook 运行后完全停止处理。优先于任何事件特定的决策字段 |
992| `stopReason` | 无 | 当 `continue` 为 `false` 时向用户显示的消息。它保留在对话中,因此如果对话继续,Claude 会看到它 |992| `stopReason` | 无 | 当 `continue` 为 `false` 时向用户显示的消息。它保留在对话中,因此如果对话继续,Claude 会看到它 |
993| `suppressOutput` | `false` | 无效果:Claude Code 接受字段但不作用。成功的 hook 的 stdout 从不在转录中显示,并在调试日志中记录 |993| `suppressOutput` | `false` | 无效果:Claude Code 接受该字段但不据此采取行动。成功的 hook 的 stdout 从不在会话记录中显示,并记录在调试日志中 |
994| `systemMessage` | 无 | 向用户显示的警告消息。在 [Agent SDK](/docs/zh-CN/agent-sdk/overview) 和 [`--output-format stream-json`](/docs/zh-CN/headless) 输出中,它可以作为 [`SDKInformationalMessage`](/docs/zh-CN/agent-sdk/typescript#sdkinformationalmessage) 到达 |994| `systemMessage` | 无 | 向用户显示的警告消息。在 [Agent SDK](/docs/zh-CN/agent-sdk/overview) 和 [`--output-format stream-json`](/docs/zh-CN/headless) 输出中,它可以作为 [`SDKInformationalMessage`](/docs/zh-CN/agent-sdk/typescript#sdkinformationalmessage) 到达 |
995| `terminalSequence` | 无 | Claude Code 代表您发出的终端转义序列,如桌面通知、窗口标题或响铃。限制为 OSC `0`/`1`/`2`/`9`/`99`/`777` 和 BEL。如果值包含允许列表外的任何内容,字段被忽略。使用此而不是写入 `/dev/tty`,这对 hook 不可用 |995| `terminalSequence` | 无 | Claude Code 代表您发出的终端转义序列,如桌面通知、窗口标题或响铃。限制为 OSC `0`/`1`/`2`/`9`/`99`/`777` 和 BEL。如果值包含允许列表之外的任何内容,该字段被忽略。请使用此字段而不是写入 `/dev/tty`,后者对 hook 不可用 |
996 996
997要完全停止 Claude:997要完全停止 Claude:
998 998
1000{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }1000{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }
1001```1001```
1002 1002
1003对于 `PreToolUse` 和 `PostToolUse` hook,停止适用,即使工具调用失败或在 Claude 仍在流式传输响应时完成。1003对于 `PreToolUse` 和 `PostToolUse` hook,即使工具调用在 Claude 仍在流式输出回复时失败或完成,停止也同样适用。
1004 1004
1005<h4 id="emit-terminal-notifications">1005<h4 id="emit-terminal-notifications">
1006 发出终端通知1006 发出终端通知
1007</h4>1007</h4>
1008 1008
1009Hook 运行时没有控制终端,因此直接写入转义序列到 `/dev/tty` 失败。改为在 `terminalSequence` 字段中返回转义序列,Claude Code 通过其自己的终端写入路径代表您发出它。这是无竞争的,在 tmux 和 GNU screen 内工作,并在 Windows 上工作,其中没有 `/dev/tty`。1009Hook 运行时没有控制终端,因此直接将转义序列写入 `/dev/tty` 会失败。请改为在 `terminalSequence` 字段中返回转义序列,Claude Code 会通过其自己的终端写入路径为您发出它。这不存在竞争问题,可在 tmux 和 GNU screen 中工作,也可在没有 `/dev/tty` 的 Windows 上工作。
1010 1010
1011该字段接受一个或多个允许列表转义序列的字符串:1011该字段接受包含一个或多个允许列表中转义序列的字符串:
1012 1012
1013* OSC `0`、`1`、`2`:窗口和图标标题1013* OSC `0`、`1`、`2`:窗口和图标标题
1014* OSC `9`:iTerm2、ConEmu、Windows Terminal 和 WezTerm 通知,包括 `9;4` 任务栏进度1014* OSC `9`:iTerm2、ConEmu、Windows Terminal 和 WezTerm 通知,包括 `9;4` 任务栏进度
1015* OSC `99`:Kitty 通知1015* OSC `99`:Kitty 通知
1016* OSC `777`:urxvt、Ghostty 和 Warp 通知1016* OSC `777`:urxvt、Ghostty 和 Warp 通知
1017* 裸 BEL1017* 单独的 BEL
1018 1018
1019序列可以用 BEL 或 ST 终止。允许列表外的任何内容,包括 CSI 光标和颜色序列、OSC 调色板序列、OSC 8 超链接、OSC 52 剪贴板写入和 OSC 1337,被拒绝,字段被忽略。1019序列可以用 BEL 或 ST 终止。允许列表之外的任何内容,包括 CSI 光标和颜色序列、OSC 调色板序列、OSC 8 超链接、OSC 52 剪贴板写入和 OSC 1337,都会被拒绝,该字段被忽略。
1020 1020
1021Claude Code 在处理您的 hook 输出时写入序列本身,因此字段在丢弃 `systemMessage` 和 `continue` 的事件上工作,如 `Notification` 和 `StopFailure`。它有两个限制:1021Claude Code 在处理您的 hook 输出时自行写入序列,因此该字段在丢弃 `systemMessage` 和 `continue` 的事件(如 `Notification` 和 `StopFailure`)上也能工作。它有两个限制:
1022 1022
1023* Claude Code 仅在交互式会话中写入序列,仅当其界面在屏幕上时。在使用 `-p` 标志的非交互式模式和 Agent SDK 中,它忽略字段。1023* Claude Code 仅在交互式会话中且仅当其界面在屏幕上时写入序列。在使用 `-p` 标志的非交互模式和 Agent SDK 中,它忽略该字段。
1024* `WorktreeCreate` 命令 hook 无法返回 JSON,因为 Claude Code 将其 stdout 读取为 worktree 路径。HTTP `WorktreeCreate` hook 返回 JSON 并可以包括字段。1024* `WorktreeCreate` 命令 hook 无法返回 JSON,因为 Claude Code 将其 stdout 读取为 worktree 路径。HTTP `WorktreeCreate` hook 返回 JSON,可以包括该字段。
1025 1025
1026下面的示例从 `Notification` hook 触发桌面通知。转义序列用 `printf` 八进制转义构建,因此控制字节从不出现在 shell 命令行上,`jq -n --arg` 构建 JSON 输出,因此通知消息中的引号、反斜杠和换行符被正确转义:1026下面的示例从 `Notification` hook 触发桌面通知。转义序列使用 `printf` 八进制转义构建,因此控制字节从不出现在 shell 命令行上,`jq -n --arg` 构建 JSON 输出,因此通知消息中的引号、反斜杠和换行符会被正确转义:
1027 1027
1028```bash theme={null}1028```bash theme={null}
1029#!/bin/bash1029#!/bin/bash
1035jq -nc --arg seq "$seq" '{terminalSequence: $seq}'1035jq -nc --arg seq "$seq" '{terminalSequence: $seq}'
1036```1036```
1037 1037
1038`{ "terminalSequence": "..." }` 形状从任何 shell 或语言都相同。1038`{ "terminalSequence": "..." }` 的结构在任何 shell 或语言中都相同。
1039 1039
1040<h4 id="add-context-for-claude">1040<h4 id="add-context-for-claude">
1041 为 Claude 添加上下文1041 为 Claude 添加上下文
1042</h4>1042</h4>
1043 1043
1044`additionalContext` 字段将字符串从您的 hook 传递到 Claude 的上下文窗口。Claude Code 将字符串包装在[系统提醒](/docs/zh-CN/glossary#system-reminder)中,并在 hook 触发的点将其插入对话。Claude 在下一个模型请求时读取提醒,但它不作为聊天消息出现在界面中。1044`additionalContext` 字段将字符串从您的 hook 传递到 Claude 的上下文窗口。Claude Code 将字符串包装在[系统提醒](/docs/zh-CN/glossary#system-reminder)中,并在 hook 触发的位置将其插入对话。Claude 在下一次模型请求时读取提醒,但它不会作为聊天消息出现在界面中。
1045 1045
1046在 `hookSpecificOutput` 中返回 `additionalContext` 以及事件名称:1046在 `hookSpecificOutput` 中将 `additionalContext` 与事件名称一起返回:
1047 1047
1048```json theme={null}1048```json theme={null}
1049{1049{
1056 1056
1057提醒出现的位置取决于事件:1057提醒出现的位置取决于事件:
1058 1058
1059* [SessionStart](#sessionstart) 和 [SubagentStart](#subagentstart):在对话开始,在第一个提示之前1059* [SessionStart](#sessionstart) 和 [SubagentStart](#subagentstart):在对话开始处,第一个提示词之前
1060* [UserPromptSubmit](#userpromptsubmit) 和 [UserPromptExpansion](#userpromptexpansion):与提交的提示一起1060* [UserPromptSubmit](#userpromptsubmit) 和 [UserPromptExpansion](#userpromptexpansion):与提交的提示词一起
1061* [PreToolUse](#pretooluse)、[PostToolUse](#posttooluse)、[PostToolUseFailure](#posttoolusefailure) 和 [PostToolBatch](#posttoolbatch):在工具结果旁边1061* [PreToolUse](#pretooluse)、[PostToolUse](#posttooluse)、[PostToolUseFailure](#posttoolusefailure) 和 [PostToolBatch](#posttoolbatch):在工具结果旁边
1062* [Stop](#stop) 和 [SubagentStop](#subagentstop):在轮次末尾。对话继续,因此 Claude 可以对反馈采取行动。请参阅 [Stop decision control](#stop-decision-control)1062* [Stop](#stop) 和 [SubagentStop](#subagentstop):在轮次末尾。对话继续,因此 Claude 可以根据反馈采取行动。请参阅 [Stop 决策控制](#stop-decision-control)
1063* [PostModelSwitch](#postmodelswitch):与切换后的下一个请求一起。请参阅 [PostModelSwitch decision control](#postmodelswitch-decision-control) 了解时间1063* [PostModelSwitch](#postmodelswitch):与切换后的下一个请求一起。请参阅 [PostModelSwitch 决策控制](#postmodelswitch-decision-control)了解时机
1064 1064
1065当多个 hook 为同一事件返回 `additionalContext` 时,Claude 接收所有值。1065当多个 hook 为同一事件返回 `additionalContext` 时,Claude 会接收所有值。
1066 1066
1067如果值超过 10,000 个字符,Claude Code 将文本写入会话目录中的文件,并改为传递 Claude 文件路径,带有最多前 2,000 个字符的预览。Claude 可以读取文件,但 Claude Code 不要求它。1067如果值超过 10,000 个字符,Claude Code 会将文本写入会话目录中的文件,并改为向 Claude 传递文件路径以及最多前 2,000 个字符的预览。Claude 可以读取该文件,但 Claude Code 不会要求它读取。
1068 1068
1069使用 `additionalContext` 获取 Claude 应该知道的关于您的环境当前状态或刚刚运行的操作的信息:1069使用 `additionalContext` 提供 Claude 应了解的有关您环境当前状态或刚刚运行的操作的信息:
1070 1070
1071* **环境状态**:当前分支、部署目标或活跃功能标志1071* **环境状态**:当前分支、部署目标或活跃的功能标志
1072* **条件项目规则**:哪个测试命令适用于刚编辑的文件,哪些目录在此 worktree 中是只读的1072* **条件项目规则**:哪个测试命令适用于刚编辑的文件,哪些目录在此 worktree 中是只读的
1073* **外部数据**:分配给您的开放问题、最近的 CI 结果、从内部服务获取的内容1073* **外部数据**:分配给您的未解决问题、最近的 CI 结果、从内部服务获取的内容
1074 1074
1075对于从不改变的说明,更喜欢 [CLAUDE.md](/docs/zh-CN/memory)。它加载而不运行脚本,是静态项目约定的标准位置。1075对于从不改变的指令,首选 [CLAUDE.md](/docs/zh-CN/memory)。它无需运行脚本即可加载,是存放静态项目约定的标准位置。
1076 1076
1077将文本写成事实陈述而不是命令式系统说明。措辞如"部署目标是生产"或"此 repo 使用 `bun test`"读作项目信息。框架为带外系统命令的文本可以触发 Claude 的提示注入防御,这导致 Claude 向您显示文本而不是将其视为上下文。1077将文本写成事实陈述,而不是命令式的系统指令。诸如"部署目标是生产环境"或"此 repo 使用 `bun test`"之类的措辞会被视为项目信息。以带外系统命令形式表述的文本可能会触发 Claude 的提示词注入防御,导致 Claude 将文本展示给您,而不是将其视为上下文。
1078 1078
1079Claude Code 在会话转录中保存注入的文本。对于 `PostToolUse` 或 `UserPromptSubmit` 等中期会话事件,当您使用 `--continue` 或 `--resume` 恢复时,Claude Code 重放保存的文本而不是为过去的轮次重新运行 hook,因此时间戳或提交 SHA 等值变得陈旧。`SessionStart` hook 在使用 `source` 设置为 `"resume"` 或 `"fork"`(如果您添加了 `--fork-session`)恢复时再次运行,因此它们可以刷新其上下文。1079Claude Code 在会话记录中保存注入的文本。对于 `PostToolUse` 或 `UserPromptSubmit` 等会话中途的事件,当您使用 `--continue` 或 `--resume` 恢复时,Claude Code 会重放保存的文本,而不是为过去的轮次重新运行 hook,因此时间戳或提交 SHA 等值会变得过时。`SessionStart` hook 在恢复时会再次运行,此时 `source` 设置为 `"resume"`,如果您添加了 `--fork-session` 则为 `"fork"`,因此它们可以刷新其上下文。
1080 1080
1081<h4 id="decision-control">1081<h4 id="decision-control">
1082 决策控制1082 决策控制
1083</h4>1083</h4>
1084 1084
1085并非每个事件都支持通过 JSON 阻止或控制行为。支持的事件各自使用不同的字段集来表达该决策。在编写 hook 之前,使用此表作为快速参考:1085并非每个事件都支持通过 JSON 阻止或控制行为。支持的事件各自使用不同的字段集来表达该决策。在编写 hook 之前,请将此表用作快速参考:
1086 1086
1087| 事件 | 决策模式 | 关键字段 |1087| 事件 | 决策模式 | 关键字段 |
1088| :- | :- | :- |1088| :- | :- | :- |
1089| UserPromptSubmit、UserPromptExpansion、PostToolUse、PostToolUseFailure、PostToolBatch、Stop、SubagentStop、ConfigChange、PreCompact | 顶级 `decision` | `decision: "block"`、`reason`。Stop 和 SubagentStop 也接受 `hookSpecificOutput.additionalContext` 用于[继续对话的非错误反馈](#stop-decision-control) |1089| UserPromptSubmit、UserPromptExpansion、PostToolUse、PostToolUseFailure、PostToolBatch、Stop、SubagentStop、ConfigChange、PreCompact | 顶级 `decision` | `decision: "block"`、`reason`。Stop 和 SubagentStop 也接受 `hookSpecificOutput.additionalContext` 用于[继续对话的非错误反馈](#stop-decision-control) |
1090| TeammateIdle、TaskCompleted | 退出代码或 `continue: false` | 退出代码 2 用 stderr 反馈阻止操作。JSON `{"continue": false, "stopReason": "..."}` 也完全停止队友,匹配 `Stop` hook 行为;[TaskCompleted 在 `TaskUpdate` 工具触发事件时忽略它](#taskcompleted-decision-control) |1090| TeammateIdle、TaskCompleted | 退出码或 `continue: false` | 退出码 2 阻止操作并提供 stderr 反馈。JSON `{"continue": false, "stopReason": "..."}` 也会完全停止队友,与 `Stop` hook 行为一致;[当 `TaskUpdate` 工具触发该事件时,TaskCompleted 会忽略它](#taskcompleted-decision-control) |
1091| TaskCreated | 退出代码或顶级 `decision` | 退出代码 2 或 `decision: "block"` [取消任务](#taskcreated-decision-control)并将消息返回给 Claude。`continue: false` 被忽略 |1091| TaskCreated | 退出码或顶级 `decision` | 退出码 2 或 `decision: "block"` [取消任务](#taskcreated-decision-control)并将消息返回给 Claude。`continue: false` 被忽略 |
1092| PreToolUse | `hookSpecificOutput` | `permissionDecision`(allow/deny/ask/defer)、`permissionDecisionReason` |1092| PreToolUse | `hookSpecificOutput` | `permissionDecision`(allow/deny/ask/defer)、`permissionDecisionReason` |
1093| PreModelSwitch | `hookSpecificOutput` 或顶级 `decision` | `permissionDecision`(allow/deny/ask)、`permissionDecisionReason`。`decision: "block"` 也[取消切换](#premodelswitch-decision-control) |1093| PreModelSwitch | `hookSpecificOutput` 或顶级 `decision` | `permissionDecision`(allow/deny/ask)、`permissionDecisionReason`。`decision: "block"` 也会[取消切换](#premodelswitch-decision-control) |
1094| PermissionRequest | `hookSpecificOutput` | `decision.behavior`(allow/deny) |1094| PermissionRequest | `hookSpecificOutput` | `decision.behavior`(allow/deny) |
1095| PermissionDenied | `hookSpecificOutput` | `retry: true` 告诉模型它可能重试被拒绝的工具调用;Claude Code 对[无判决拒绝](#permissiondenied-decision-control)忽略它 |1095| PermissionDenied | `hookSpecificOutput` | `retry: true` 告诉模型它可以重试被拒绝的工具调用;Claude Code 对[无判决拒绝](#permissiondenied-decision-control)忽略它 |
1096| WorktreeCreate | 路径返回 | 命令 hook 在 stdout 上打印路径;HTTP hook 返回 `hookSpecificOutput.worktreePath`。Hook 失败或缺少路径失败创建 |1096| WorktreeCreate | 路径返回 | 命令 hook 在 stdout 上打印路径;HTTP hook 返回 `hookSpecificOutput.worktreePath`。hook 失败或缺少路径会使创建失败 |
1097| WorktreeRemove | 退出代码 | 任何非零退出代码使移除失败(如果目录仍然存在)。JSON 输出被丢弃 |1097| WorktreeRemove | 退出码 | 任何非零退出码会在目录之后仍然存在时使移除失败。JSON 输出被丢弃 |
1098| Elicitation | `hookSpecificOutput` | `action`(accept/decline/cancel)、`content`(接受的表单字段值) |1098| Elicitation、ElicitationResult | `hookSpecificOutput` 或顶级 `decision` | `action`(accept/decline/cancel)、`content`(表单字段值)。`decision: "block"` 也会[拒绝](#other-ways-to-decline-an-elicitation) |
1099| ElicitationResult | `hookSpecificOutput` | `action`(accept/decline/cancel)、`content`(表单字段值覆盖) |1099| MessageDisplay | `hookSpecificOutput` | `displayContent` 替换屏幕上显示的文本。仅影响显示:会话记录和 Claude 看到的内容保持原样 |
1100| MessageDisplay | `hookSpecificOutput` | `displayContent` 替换屏幕上显示的文本。仅显示:转录和 Claude 看到的保持原始 |
1101| SessionStart、SubagentStart、PostModelSwitch | 仅上下文 | `hookSpecificOutput.additionalContext` 为 Claude 添加上下文。SessionStart 也接受 [`initialUserMessage`、`watchPaths`、`sessionTitle` 和 `reloadSkills`](#sessionstart-decision-control)。无阻止或决策控制 |1100| SessionStart、SubagentStart、PostModelSwitch | 仅上下文 | `hookSpecificOutput.additionalContext` 为 Claude 添加上下文。SessionStart 也接受 [`initialUserMessage`、`watchPaths`、`sessionTitle` 和 `reloadSkills`](#sessionstart-decision-control)。无阻止或决策控制 |
1102| Setup、Notification、SessionEnd、PostCompact、InstructionsLoaded、StopFailure、CwdChanged、DirectoryAdded、FileChanged | 无 | 无决策控制。用于日志或清理等副作用 |1101| Setup、Notification、SessionEnd、PostCompact、InstructionsLoaded、StopFailure、CwdChanged、DirectoryAdded、FileChanged | 无 | 无决策控制。用于记录日志或清理等副作用 |
1103 1102
1104少数事件也可以重写内容而不仅仅允许或阻止它:1103少数事件还可以改写内容,而不仅仅是允许或阻止:
1105 1104
1106* `PreToolUse`:`updatedInput` 直接在 `hookSpecificOutput` 下替换工具的参数,然后它运行。请参阅 [PreToolUse decision control](#pretooluse-decision-control)1105* `PreToolUse`:直接位于 `hookSpecificOutput` 下的 `updatedInput` 会在工具运行前替换其参数。请参阅 [PreToolUse 决策控制](#pretooluse-decision-control)
1107* `PermissionRequest`:`updatedInput` 在 `decision` 对象内。请参阅 [PermissionRequest decision control](#permissionrequest-decision-control)1106* `PermissionRequest`:`updatedInput` 位于 `decision` 对象内。请参阅 [PermissionRequest 决策控制](#permissionrequest-decision-control)
1108* `PostToolUse`:`updatedToolOutput` 替换工具的结果。请参阅 [PostToolUse decision control](#posttooluse-decision-control)1107* `PostToolUse`:`updatedToolOutput` 替换工具的结果。请参阅 [PostToolUse 决策控制](#posttooluse-decision-control)
1109* `UserPromptSubmit`:无法替换提示;它仅在其旁边注入 `additionalContext`1108* `UserPromptSubmit`:无法替换提示词;它仅在其旁边注入 `additionalContext`
1110 1109
1111对于编辑或转换用例,在 `PreToolUse` 处拦截出站工具输入,在 `PostToolUse` 处拦截入站工具结果。1110对于脱敏或转换用例,请在 `PreToolUse` 处拦截出站工具输入,在 `PostToolUse` 处拦截入站工具结果。
1112 1111
1113以下是每种模式的实际示例:1112以下是每种模式的实际示例:
1114 1113
1115<Tabs>1114<Tabs>
1116 <Tab title="顶级 decision">1115 <Tab title="顶级 decision">
1117 `decision` 的唯一值是 `"block"`。要允许操作继续,从您的 JSON 中省略 `decision`,或退出 0 而不带任何 JSON:1116 `decision` 的唯一值是 `"block"`。要允许操作继续,请从您的 JSON 中省略 `decision`,或退出 0 且不输出任何 JSON:
1118 1117
1119 ```json theme={null}1118 ```json theme={null}
1120 {1119 {
1125 </Tab>1124 </Tab>
1126 1125
1127 <Tab title="PreToolUse">1126 <Tab title="PreToolUse">
1128 使用 `hookSpecificOutput` 进行更丰富的控制:允许、拒绝或升级给用户。您也可以在运行前修改工具输入或为 Claude 注入额外上下文。请参阅 [PreToolUse decision control](#pretooluse-decision-control) 了解完整的选项集。1127 使用 `hookSpecificOutput` 进行更丰富的控制:允许、拒绝或升级给用户。您也可以在工具运行前修改工具输入,或为 Claude 注入额外上下文。请参阅 [PreToolUse 决策控制](#pretooluse-decision-control)了解完整的选项集。
1129 1128
1130 ```json theme={null}1129 ```json theme={null}
1131 {1130 {
1139 </Tab>1138 </Tab>
1140 1139
1141 <Tab title="PermissionRequest">1140 <Tab title="PermissionRequest">
1142 使用 `hookSpecificOutput` 代表用户允许或拒绝权限请求。允许时,您也可以修改工具的输入或应用权限规则,以便用户不会再次被提示。请参阅 [PermissionRequest decision control](#permissionrequest-decision-control) 了解完整的选项集。1141 使用 `hookSpecificOutput` 代表用户允许或拒绝权限请求。允许时,您也可以修改工具的输入或应用权限规则,使用户不会再次被询问。请参阅 [PermissionRequest 决策控制](#permissionrequest-decision-control)了解完整的选项集。
1143 1142
1144 ```json theme={null}1143 ```json theme={null}
1145 {1144 {
1157 </Tab>1156 </Tab>
1158</Tabs>1157</Tabs>
1159 1158
1160有关扩展示例,包括 Bash 命令验证、提示过滤和自动批准脚本,请参阅指南中的 [What you can automate](/docs/zh-CN/hooks-guide#what-you-can-automate) 和 [Bash command validator reference implementation](https://github.com/anthropics/claude-code/blob/main/examples/hooks/bash_command_validator_example.py)。1159有关扩展示例,包括 Bash 命令验证、提示词过滤和自动批准脚本,请参阅指南中的[您可以自动化什么](/docs/zh-CN/hooks-guide#what-you-can-automate)和 [Bash 命令验证器参考实现](https://github.com/anthropics/claude-code/blob/main/examples/hooks/bash_command_validator_example.py)。
1161 1160
1162<h2 id="hook-events">1161<h2 id="hook-events">
1163 Hook 事件1162 Hook 事件
1164</h2>1163</h2>
1165 1164
1166每个事件都对应 Claude Code 生命周期中可以运行 hook 的一个时间点。以下各节按照生命周期的顺序排列:从会话设置开始,经过智能体循环,直到会话结束。每一节都会说明事件何时触发、支持哪些匹配器、接收什么 JSON 输入,以及如何通过输出控制行为。1165每个事件都对应 Claude Code 生命周期中可以运行 hook 的一个时间点。以下各节按照生命周期的顺序排列:从会话设置,到智能体循环,再到会话结束。每一节都会说明事件何时触发、支持哪些匹配器、接收的 JSON 输入,以及如何通过输出控制行为。
1167 1166
1168<h3 id="sessionstart">1167<h3 id="sessionstart">
1169 SessionStart1168 SessionStart
1170</h3>1169</h3>
1171 1170
1172在 Claude Code 启动新会话或恢复现有会话时运行。适用于加载开发上下文(例如现有 issue 或代码库的最近更改),或设置环境变量。对于不需要脚本的静态上下文,请改用 [CLAUDE.md](/docs/zh-CN/memory)。1171在 Claude Code 启动新会话或恢复现有会话时运行。适用于加载开发上下文(例如现有问题或代码库的最近更改),或设置环境变量。对于不需要脚本的静态上下文,请改用 [CLAUDE.md](/docs/zh-CN/memory)。
1173 1172
1174SessionStart 在每个会话中都会运行,因此请保持这些 hook 快速执行。仅支持 `type: "command"` 和 `type: "mcp_tool"` hook。有关 `mcp_tool` hook 何时运行,请参阅 [MCP 工具 hook 字段](#mcp-tool-hook-fields)。1173SessionStart 在每个会话中都会运行,因此请确保这些 hook 运行迅速。仅支持 `type: "command"` 和 `type: "mcp_tool"` hook。关于 `mcp_tool` hook 何时运行,请参阅 [MCP 工具 hook 字段](#mcp-tool-hook-fields)。
1175 1174
1176匹配器值对应于会话的启动方式:1175匹配器的值对应会话的启动方式:
1177 1176
1178| 匹配器 | 触发时机 |1177| 匹配器 | 触发时机 |
1179| :- | :- |1178| :- | :- |
1185 1184
1186在 v2.1.214 之前,分叉的会话报告的 source 为 `"resume"`。1185在 v2.1.214 之前,分叉的会话报告的 source 为 `"resume"`。
1187 1186
1188当您启动交互式会话、在启动时使用 `--continue` 或 `--resume` 恢复对话,或运行 `/clear` 时,SessionStart hook 会在后台运行。您可以立即输入,恢复的对话也会直接显示,无需等待 hook。Claude 的第一条回复仍会等待 hook 完成,以便其上下文能够传递给 Claude。1187当您启动交互式会话、在启动时使用 `--continue` 或 `--resume` 恢复对话,或运行 `/clear` 时,SessionStart hook 会在后台运行。您可以立即开始输入,恢复的对话也会直接显示,无需等待 hook。Claude 的第一次回复仍会等待 hook 完成,以便其上下文能传达给 Claude。
1189 1188
1190当您在会话中使用 `/resume` 切换对话时,切换操作则会等待 hook 完成。如果您在后台 hook 仍在运行时运行 `/clear` 或切换到其他对话,它们返回的任何内容都不会应用于该会话。1189当您在会话内使用 `/resume` 切换对话时,切换操作则会等待 hook 完成。如果您在后台 hook 仍在运行时运行 `/clear` 或切换到另一个对话,它们返回的任何内容都不会应用到该会话。
1191 1190
1192启动时也存在同样的等待,包括恢复的会话:在 SessionStart hook 仍在运行时发送的提示词,要等到它们完成后才会传递给 Claude。1191启动时(包括恢复的会话)也存在同样的等待:在 SessionStart hook 仍在运行时发送的提示词,要等它们完成后才会传达给 Claude。
1193 1192
1194在上述任一等待期间,按 `Esc` 可将提示词收回到输入框中而不发送。hook 会继续运行。1193在任一种等待期间,按 `Esc` 可将提示词收回输入框而不发送。hook 会继续运行。
1195 1194
1196<h4 id="sessionstart-input">1195<h4 id="sessionstart-input">
1197 SessionStart 输入1196 SessionStart 输入
1198</h4>1197</h4>
1199 1198
1200除了[通用输入字段](#common-input-fields)之外,SessionStart hook 还会接收 `source`,以及可选的 `model`、`agent_type` 和 `session_title`:1199除[通用输入字段](#common-input-fields)外,SessionStart hook 还会接收 `source`,以及可选的 `model`、`agent_type` 和 `session_title`:
1201 1200
1202| 字段 | 描述 |1201| 字段 | 描述 |
1203| :- | :- |1202| :- | :- |
1204| `source` | 会话的启动方式:新会话为 `"startup"`,恢复的会话为 `"resume"`,`/clear` 之后为 `"clear"`,压缩之后为 `"compact"`,从现有会话分叉出的新会话为 `"fork"` |1203| `source` | 会话的启动方式:新会话为 `"startup"`,恢复的会话为 `"resume"`,`/clear` 之后为 `"clear"`,压缩之后为 `"compact"`,从现有会话分叉出的新会话为 `"fork"` |
1205| `model` | 当前活动的模型标识符。该字段可能被省略,例如在 `/clear` 之后或通过对话恢复还原会话时,因此请在读取前检查该字段是否存在 |1204| `model` | 当前活动模型的标识符。该字段可能缺失,例如在 `/clear` 之后或通过对话恢复还原会话时,因此请在读取前检查该字段是否存在 |
1206| `agent_type` | Agent 名称,在您使用 `claude --agent <name>` 启动 Claude Code 时出现 |1205| `agent_type` | Agent 名称,当您使用 `claude --agent <name>` 启动 Claude Code 时存在 |
1207| `session_title` | 会话的自定义标题,在已设置时出现,例如通过 `--name`、`/rename`、hook 的 `sessionTitle` 输出或 Agent SDK 的 `renameSession()` 设置。输出 `sessionTitle` 的 hook 可以先检查此字段,以避免覆盖现有的自定义标题 |1206| `session_title` | 会话的自定义标题,在已设置时存在,例如通过 `--name`、`/rename`、hook 的 `sessionTitle` 输出或 Agent SDK 的 `renameSession()` 设置。输出 `sessionTitle` 的 hook 可以先检查此字段,以避免覆盖现有的自定义标题 |
1208 1207
1209您未命名的会话仍可能拥有[自动生成的标题](/docs/zh-CN/sessions#name-your-sessions)。该标题不是自定义标题,不会出现在 `session_title` 中。1208未命名的会话仍可能有一个[自动生成的标题](/docs/zh-CN/sessions#name-your-sessions)。该标题不是自定义标题,不会出现在 `session_title` 中。
1210 1209
1211当 `source` 为 `"resume"` 或 `"fork"`,且会话记录中至少包含一条 Claude 的回复时,SessionStart hook 还会接收以下四个字段。您的 hook 可以使用它们在第一个请求之前报告恢复一个陈旧对话的成本,例如通过 [`systemMessage`](#json-output)。这些字段需要 Claude Code v2.1.251 或更高版本。1210当 `source` 为 `"resume"` 或 `"fork"`,且会话记录中至少包含一条 Claude 的回复时,SessionStart hook 还会接收下面四个字段。您的 hook 可以利用它们在第一个请求之前报告恢复一个陈旧对话的成本,例如通过 [`systemMessage`](#json-output)。这些字段需要 Claude Code v2.1.251 或更高版本。
1212 1211
1213| 字段 | 描述 |1212| 字段 | 描述 |
1214| :- | :- |1213| :- | :- |
1215| `seconds_since_last_response` | 自恢复的会话记录中最后一条回复以来经过的实际秒数 |1214| `seconds_since_last_response` | 自恢复的会话记录中最后一条回复以来经过的实际秒数 |
1216| `context_tokens` | 恢复的会话的第一个请求作为提示词重新发送的 token 数 |1215| `context_tokens` | 恢复后会话的第一个请求作为提示词重新发送的 token 数 |
1217| `prompt_cache_likely_expired` | 当最后一条回复早于会话的[提示缓存生命周期](/docs/zh-CN/prompt-caching#cache-lifetime),或之后的压缩替换了已缓存的对话时为 `true` |1216| `prompt_cache_likely_expired` | 当最后一条回复早于会话的[提示缓存有效期](/docs/zh-CN/prompt-caching#cache-lifetime),或之后的压缩替换了已缓存的对话时为 `true` |
1218| `estimated_cache_write_usd` | 在会话所用模型上将 `context_tokens` 写入提示缓存的估计成本(美元),不包括回复 |1217| `estimated_cache_write_usd` | 在会话所用模型上将 `context_tokens` 写入提示缓存的估计成本(美元),不包括回复 |
1219 1218
1220以下示例展示了在最后一条回复 90 分钟后恢复的会话的输入:1219此示例展示了一个在最后一条回复 90 分钟后恢复的会话的输入:
1221 1220
1222```json theme={null}1221```json theme={null}
1223{1222{
1238 SessionStart 决策控制1237 SessionStart 决策控制
1239</h4>1238</h4>
1240 1239
1241Claude Code 会将其[视为纯文本](#exit-code-0)的 stdout 添加到 Claude 的上下文中。除了所有 hook 都可用的 [JSON 输出字段](#json-output)之外,您还可以返回以下特定于事件的字段:1240Claude Code 会将其[视为纯文本](#exit-code-0)的 stdout 添加到 Claude 的上下文中。除所有 hook 都可用的 [JSON 输出字段](#json-output)外,您还可以返回以下事件专属字段:
1242 1241
1243| 字段 | 描述 |1242| 字段 | 描述 |
1244| :- | :- |1243| :- | :- |
1245| `additionalContext` | 在对话开始时、第一个提示词之前添加到 Claude 上下文中的字符串。有关文本的传递方式以及应放入的内容,请参阅[为 Claude 添加上下文](#add-context-for-claude) |1244| `additionalContext` | 在对话开始时、第一个提示词之前添加到 Claude 上下文中的字符串。关于文本的传递方式以及应放入的内容,请参阅[为 Claude 添加上下文](#add-context-for-claude) |
1246| `initialUserMessage` | 用作会话第一条用户消息的字符串。适用于使用 `-p` 标志的[非交互模式](/docs/zh-CN/headless),此时即使未提供提示词,它也会成为第一轮。如果提供了提示词,则提示词作为下一轮紧随其后。与附加到现有轮次的 `additionalContext` 不同,此字段会创建轮次 |1245| `initialUserMessage` | 用作会话第一条用户消息的字符串。适用于使用 `-p` 标志的[非交互模式](/docs/zh-CN/headless),此时即使未提供提示词,它也会成为第一个轮次。如果提供了提示词,该提示词将作为下一个轮次跟随其后。与附加到现有轮次的 `additionalContext` 不同,此字段会创建轮次 |
1247| `sessionTitle` | 设置会话标题,效果与 `/rename` 相同。可用于根据启动文件夹、git 分支或 worktree 名称自动命名会话。在 `source` 为 `"startup"`、`"resume"` 或 `"fork"` 时生效;在 `"clear"` 和 `"compact"` 时被忽略 |1246| `sessionTitle` | 设置会话标题,效果与 `/rename` 相同。可用于根据启动文件夹、git 分支或 worktree 名称自动命名会话。当 `source` 为 `"startup"`、`"resume"` 或 `"fork"` 时生效;在 `"clear"` 和 `"compact"` 时被忽略 |
1248| `watchPaths` | 在此会话期间要监视 [FileChanged](#filechanged) 事件的绝对路径数组 |1247| `watchPaths` | 在此会话期间监视 [FileChanged](#filechanged) 事件的绝对路径数组 |
1249| `reloadSkills` | 布尔值。为 `true` 时,Claude Code 会在 SessionStart hook 完成后重新扫描 [skill](/docs/zh-CN/skills) 和命令目录,使 hook 安装的 skill 在同一会话中从第一个提示词开始即可使用 |1248| `reloadSkills` | 布尔值。为 `true` 时,Claude Code 会在 SessionStart hook 完成后重新扫描 [skill](/docs/zh-CN/skills) 和命令目录,使 hook 安装的 skill 在同一会话中(从第一个提示词开始)即可使用 |
1250 1249
1251```json theme={null}1250```json theme={null}
1252{1251{
1258}1257}
1259```1258```
1260 1259
1261由于对于此事件,纯 stdout 已经会传递给 Claude,因此仅加载上下文的 hook 可以直接打印到 stdout,无需构建 JSON。当您需要将上下文与 `sessionTitle` 等其他字段组合时,请使用 JSON 形式。1260由于对于此事件,纯 stdout 已经会传达给 Claude,因此仅加载上下文的 hook 可以直接打印到 stdout,而无需构建 JSON。当您需要将上下文与 `sessionTitle` 等其他字段结合使用时,请使用 JSON 形式。
1262 1261
1263当 SessionStart hook 安装或更新 skill 时,请使用 `reloadSkills`。skill 发现通常在 SessionStart hook 完成之前运行,因此 hook 写入 `~/.claude/skills/` 或 `.claude/skills/` 的文件原本只会在下一个会话中出现。以下示例同步一个共享的 skill 仓库并请求重新扫描:1262当 SessionStart hook 安装或更新 skill 时,请使用 `reloadSkills`。skill 发现通常在 SessionStart hook 完成之前运行,因此 hook 写入 `~/.claude/skills/` 或 `.claude/skills/` 的文件否则只会在下一个会话中出现。此示例同步一个共享的 skill 仓库并请求重新扫描:
1264 1263
1265```bash theme={null}1264```bash theme={null}
1266#!/bin/bash1265#!/bin/bash
1271echo '{"hookSpecificOutput": {"hookEventName": "SessionStart", "reloadSkills": true}}'1270echo '{"hookSpecificOutput": {"hookEventName": "SessionStart", "reloadSkills": true}}'
1272```1271```
1273 1272
1274该仓库 URL 是一个占位符;请将其替换为您自己的 skill 仓库。使用占位符时,克隆会失败并向 stderr 打印一条 `fatal:` 消息。以 0 退出的 SessionStart hook 的 stderr 仅供参考,因此 `reloadSkills` 请求仍然生效。1273仓库 URL 只是一个占位符;请将其替换为您自己的 skill 仓库。使用占位符时,clone 会失败并向 stderr 打印一条 `fatal:` 消息。以 0 退出的 SessionStart hook 的 stderr 仅供参考,因此 `reloadSkills` 请求仍然有效。
1275 1274
1276<h4 id="persist-environment-variables">1275<h4 id="persist-environment-variables">
1277 持久化环境变量1276 持久化环境变量
1278</h4>1277</h4>
1279 1278
1280SessionStart hook 可以访问 `CLAUDE_ENV_FILE` 环境变量,该变量提供一个文件路径,您可以在其中为后续的 Bash 命令持久化环境变量。1279SessionStart hook 可以访问 `CLAUDE_ENV_FILE` 环境变量,该变量提供一个文件路径,您可以在其中持久化环境变量,供后续 Bash 命令使用。
1281 1280
1282要设置单个环境变量,请将 `export` 语句写入 `CLAUDE_ENV_FILE`。使用追加(`>>`)以保留其他 hook 设置的变量:1281要设置单个环境变量,请将 `export` 语句写入 `CLAUDE_ENV_FILE`。使用追加(`>>`)以保留其他 hook 设置的变量:
1283 1282
1293exit 01292exit 0
1294```1293```
1295 1294
1296要捕获设置命令产生的所有环境更改,请比较执行前后导出的变量:1295要捕获设置命令带来的所有环境更改,请比较执行前后导出的变量:
1297 1296
1298```bash theme={null}1297```bash theme={null}
1299#!/bin/bash1298#!/bin/bash
1313```1312```
1314 1313
1315<Note>1314<Note>
1316 `CLAUDE_ENV_FILE` 可用于 SessionStart、[Setup](#setup)、[CwdChanged](#cwdchanged) 和 [FileChanged](#filechanged) hook。其他类型的 hook 无法访问此变量。1315 `CLAUDE_ENV_FILE` 可用于 SessionStart、[Setup](#setup)、[CwdChanged](#cwdchanged) 和 [FileChanged](#filechanged) hook。其他 hook 类型无法访问此变量。
1317</Note>1316</Note>
1318 1317
1319<h3 id="setup">1318<h3 id="setup">
1320 Setup1319 Setup
1321</h3>1320</h3>
1322 1321
1323仅在您使用 `--init-only` 启动 Claude Code,或在使用 `-p` 标志的[非交互模式](/docs/zh-CN/headless)下使用 `--init` 或 `--maintenance` 启动时触发。正常启动时不会触发。可将其用于从 CI 或脚本中显式触发的一次性依赖安装或定期清理,与正常的会话启动分开。对于每个会话的初始化,请改用 [SessionStart](#sessionstart)。1322仅当您使用 `--init-only` 启动 Claude Code,或在使用 `-p` 标志的[非交互模式](/docs/zh-CN/headless)中使用 `--init` 或 `--maintenance` 启动时才会触发。正常启动时不会触发。可将其用于您从 CI 或脚本中显式触发的一次性依赖安装或定期清理,与正常的会话启动分开。对于每个会话的初始化,请改用 [SessionStart](#sessionstart)。
1324 1323
1325匹配器值对应于触发该 hook 的 CLI 标志:1324匹配器的值对应触发该 hook 的 CLI 标志:
1326 1325
1327| 匹配器 | 触发时机 |1326| 匹配器 | 触发时机 |
1328| :- | :- |1327| :- | :- |
1329| `init` | `claude --init-only` 或 `claude -p --init` |1328| `init` | `claude --init-only` 或 `claude -p --init` |
1330| `maintenance` | `claude -p --maintenance` |1329| `maintenance` | `claude -p --maintenance` |
1331 1330
1332当您运行 `claude --init-only` 时,Claude Code 会运行 Setup hook 以及带有 `startup` 匹配器的 `SessionStart` hook,然后退出,不会开始对话。1331当您运行 `claude --init-only` 时,Claude Code 会运行 Setup hook 和带有 `startup` 匹配器的 `SessionStart` hook,然后在不开始对话的情况下退出。
1333 1332
1334当您使用 `-p` 开始或继续对话时,还需要提供提示词,可以作为参数提供,也可以通过 stdin 管道传入。当 `SessionStart` hook 提供了 [`initialUserMessage`](#sessionstart-decision-control),或者您恢复带有[延迟工具调用](#defer-a-tool-call-for-later)的会话时,可以省略提示词。1333当您使用 `-p` 开始或继续对话时,还需要提供一个提示词,作为参数或通过 stdin 管道传入。当 `SessionStart` hook 提供了 [`initialUserMessage`](#sessionstart-decision-control),或者您恢复一个带有[推迟的工具调用](#defer-a-tool-call-for-later)的会话时,可以省略提示词。
1335 1334
1336成功时,`--init-only` 不会向终端打印任何内容。要确认 hook 已运行,请使用 `claude --debug-file <path> --init-only` 启动,将 `<path>` 替换为日志文件位置,然后在日志中检查 Setup 和 SessionStart hook 条目。1335成功时,`--init-only` 不会向终端打印任何内容。要确认 hook 已运行,请使用 `claude --debug-file <path> --init-only` 启动,将 `<path>` 替换为日志文件位置,然后在日志中查看 Setup 和 SessionStart hook 的条目。
1337 1336
1338由于 Setup 并非每次启动都会触发,需要安装依赖的插件不能仅依赖 Setup。实用的模式是在首次使用时检查依赖,缺失时再安装,例如由 hook 或 skill 检测 `${CLAUDE_PLUGIN_DATA}/node_modules`,若不存在则运行 `npm install`。有关存储已安装依赖的位置,请参阅[持久数据目录](/docs/zh-CN/plugins/components#path-variables-and-persistent-data)。如果您通过市场分发插件,则可能不需要此模式:Claude Code 在缓存插件时会[自动安装符合条件的 Node.js 包依赖](/docs/zh-CN/plugins/loading#node-js-package-dependencies)。1337由于 Setup 不会在每次启动时触发,需要安装依赖的插件不能仅依赖 Setup。实用的模式是在首次使用时检查依赖,缺失时再安装,例如使用一个 hook 或 skill 检测 `${CLAUDE_PLUGIN_DATA}/node_modules`,若不存在则运行 `npm install`。关于已安装依赖的存放位置,请参阅[持久数据目录](/docs/zh-CN/plugins/components#path-variables-and-persistent-data)。如果您通过市场分发插件,则可能不需要此模式:Claude Code 在缓存插件时会[自动安装符合条件的 Node.js 包依赖](/docs/zh-CN/plugins/loading#node-js-package-dependencies)。
1339 1338
1340<h4 id="setup-input">1339<h4 id="setup-input">
1341 Setup 输入1340 Setup 输入
1342</h4>1341</h4>
1343 1342
1344除了[通用输入字段](#common-input-fields)之外,Setup hook 还会接收一个 `trigger` 字段,其值为 `"init"` 或 `"maintenance"`:1343除[通用输入字段](#common-input-fields)外,Setup hook 还会接收一个 `trigger` 字段,其值为 `"init"` 或 `"maintenance"`:
1345 1344
1346```json theme={null}1345```json theme={null}
1347{1346{
1357 Setup 决策控制1356 Setup 决策控制
1358</h4>1357</h4>
1359 1358
1360Setup hook 无法阻止执行;无论退出码如何,执行都会继续。对于任何退出码,Claude Code 都会丢弃 Setup hook 的 [JSON 输出字段](#json-output),例如 `systemMessage`、`continue` 和 `hookSpecificOutput.additionalContext`。使用 `-p` 时,只有在以 `--output-format stream-json --verbose` 启动时,Setup hook 的 stdout、stderr 和退出码才会作为 [`hook_response` 事件](/docs/zh-CN/headless#read-session-metadata)出现在运行输出中。1359Setup hook 无法阻止执行;无论退出码是什么,执行都会继续。无论退出码是什么,Claude Code 都会丢弃 Setup hook 的 [JSON 输出字段](#json-output),例如 `systemMessage`、`continue` 和 `hookSpecificOutput.additionalContext`。使用 `-p` 时,只有在您使用 `--output-format stream-json --verbose` 启动的情况下,Setup hook 的 stdout、stderr 和退出码才会以 [`hook_response` 事件](/docs/zh-CN/headless#read-session-metadata)的形式出现在运行输出中。
1361 1360
1362Setup hook 可以访问 `CLAUDE_ENV_FILE`。写入该文件的变量会持久保留到该会话后续的 Bash 命令中,与 [SessionStart hook](#persist-environment-variables) 相同。只有 `type: "command"` hook 会在 `Setup` 上运行。`Setup` 上的 `type: "mcp_tool"` hook 始终会被跳过,如 [MCP 工具 hook 字段](#mcp-tool-hook-fields)中所述。1361Setup hook 可以访问 `CLAUDE_ENV_FILE`。写入该文件的变量会在会话的后续 Bash 命令中持久存在,与 [SessionStart hook](#persist-environment-variables) 相同。`Setup` 上只运行 `type: "command"` hook。`Setup` 上的 `type: "mcp_tool"` hook 始终会被跳过,详见 [MCP 工具 hook 字段](#mcp-tool-hook-fields)。
1363 1362
1364<h3 id="instructionsloaded">1363<h3 id="instructionsloaded">
1365 InstructionsLoaded1364 InstructionsLoaded
1366</h3>1365</h3>
1367 1366
1368当 `CLAUDE.md` 或 `.claude/rules/*.md` 文件被加载到上下文中时触发。此事件会在会话开始时为预先加载的文件触发,之后在文件被延迟加载时再次触发,例如当 Claude 访问包含嵌套 `CLAUDE.md` 的子目录时,或带有 `paths:` frontmatter 的条件规则匹配时。该 hook 不支持阻止或决策控制。它以异步方式运行,用于可观测性目的。1367当 `CLAUDE.md` 或 `.claude/rules/*.md` 文件被加载到上下文中时触发。此事件在会话开始时针对立即加载的文件触发,之后在文件被延迟加载时再次触发,例如当 Claude 访问包含嵌套 `CLAUDE.md` 的子目录时,或当带有 `paths:` frontmatter 的条件规则匹配时。该 hook 不支持阻止或决策控制。它以异步方式运行,用于可观测性目的。
1369 1368
1370当 Claude 通过 **Project instructions** 设置[直接读取 `AGENTS.md`](/docs/zh-CN/memory#agents-md) 时,此事件不会触发。当 `CLAUDE.md` 导入您的 `AGENTS.md` 时,此事件会触发,`load_reason` 与其他任何导入文件一样设置为 `include`;当 `CLAUDE.md` 是指向它的符号链接时,此事件也会作为普通的 `CLAUDE.md` 加载而触发。1369当 Claude 通过 **Project instructions** 设置[直接读取 `AGENTS.md`](/docs/zh-CN/memory#agents-md) 时,此事件不会触发。当 `CLAUDE.md` 导入您的 `AGENTS.md` 时,它会触发,`load_reason` 设置为 `include`,与任何其他被导入的文件相同;当 `CLAUDE.md` 是指向它的符号链接时,它也会作为一次普通的 `CLAUDE.md` 加载触发。
1371 1370
1372匹配器针对 `load_reason` 运行。例如,使用 `"matcher": "session_start"` 仅针对会话开始时加载的文件触发,或使用 `"matcher": "path_glob_match|nested_traversal"` 仅针对延迟加载触发。1371匹配器针对 `load_reason` 运行。例如,使用 `"matcher": "session_start"` 仅针对会话开始时加载的文件触发,或使用 `"matcher": "path_glob_match|nested_traversal"` 仅针对延迟加载触发。
1373 1372
1375 InstructionsLoaded 输入1374 InstructionsLoaded 输入
1376</h4>1375</h4>
1377 1376
1378除了[通用输入字段](#common-input-fields)之外,InstructionsLoaded hook 还会接收以下字段:1377除[通用输入字段](#common-input-fields)外,InstructionsLoaded hook 还会接收以下字段:
1379 1378
1380| 字段 | 描述 |1379| 字段 | 描述 |
1381| :- | :- |1380| :- | :- |
1382| `file_path` | 已加载的指令文件的绝对路径 |1381| `file_path` | 被加载的指令文件的绝对路径 |
1383| `memory_type` | 文件的作用域:`"User"`、`"Project"`、`"Local"` 或 `"Managed"` |1382| `memory_type` | 文件的作用域:`"User"`、`"Project"`、`"Local"` 或 `"Managed"` |
1384| `load_reason` | 文件被加载的原因:`"session_start"`、`"nested_traversal"`、`"path_glob_match"`、`"include"` 或 `"compact"`。`"compact"` 值在压缩事件后重新加载指令文件时触发 |1383| `load_reason` | 文件被加载的原因:`"session_start"`、`"nested_traversal"`、`"path_glob_match"`、`"include"` 或 `"compact"`。`"compact"` 值在压缩事件之后重新加载指令文件时触发 |
1385| `globs` | 文件 `paths:` frontmatter 中的路径 glob 模式(如有)。仅在 `path_glob_match` 加载时出现 |1384| `globs` | 文件 `paths:` frontmatter 中的路径 glob 模式(如有)。仅在 `path_glob_match` 加载时存在 |
1386| `trigger_file_path` | 对于延迟加载,指其访问触发了此次加载的文件的路径 |1385| `trigger_file_path` | 对于延迟加载,其访问触发了此次加载的文件的路径 |
1387| `parent_file_path` | 对于 `include` 加载,指包含此文件的父指令文件的路径 |1386| `parent_file_path` | 对于 `include` 加载,包含此文件的父指令文件的路径 |
1388 1387
1389```json theme={null}1388```json theme={null}
1390{1389{
1402 InstructionsLoaded 决策控制1401 InstructionsLoaded 决策控制
1403</h4>1402</h4>
1404 1403
1405InstructionsLoaded hook 没有决策控制。它们无法阻止或修改指令加载。Claude Code 会丢弃它们的 [JSON 输出字段](#json-output),例如 `systemMessage` 和 `continue`。可将此事件用于审计日志记录、合规跟踪或可观测性。1404InstructionsLoaded hook 没有决策控制。它们无法阻止或修改指令的加载。Claude Code 会丢弃它们的 [JSON 输出字段](#json-output),例如 `systemMessage` 和 `continue`。可将此事件用于审计日志、合规跟踪或可观测性。
1406 1405
1407<h3 id="userpromptsubmit">1406<h3 id="userpromptsubmit">
1408 UserPromptSubmit1407 UserPromptSubmit
1409</h3>1408</h3>
1410 1409
1411在提交提示词时、Claude 处理它之前运行。这使您可以1410在提交提示词后、Claude 处理之前运行。这使您能够
1412根据提示词/对话添加额外的上下文、验证提示词,或1411根据提示词/对话添加额外的上下文、验证提示词,或
1413阻止某些类型的提示词。1412阻止某些类型的提示词。
1414 1413
1415`UserPromptSubmit` hook 不仅在您输入的提示词上触发。Claude Code 还会在以下情况下运行它们:1414`UserPromptSubmit` hook 不仅在您输入的提示词上触发。Claude Code 还会在以下情况下运行它们:
1416 1415
1417* [定时任务](/docs/zh-CN/scheduled-tasks)触发,包括 `/loop` 的每次迭代1416* [定时任务](/docs/zh-CN/scheduled-tasks)触发时,包括 `/loop` 的一次迭代
1418* [后台子代理](/docs/zh-CN/sub-agents#run-subagents-in-foreground-or-background)向启动它的会话回报1417* [后台子代理](/docs/zh-CN/sub-agents#run-subagents-in-foreground-or-background)向启动它的会话回报时
1419* [另一个会话发送的消息](/docs/zh-CN/cross-session-messaging)到达您的主对话1418* [另一个会话发送消息](/docs/zh-CN/cross-session-messaging)到您的主对话时
1420 1419
1421对于 `command`、`http` 和 `mcp_tool` 类型,`UserPromptSubmit` hook 的默认超时时间为 30 秒,短于这些类型在大多数其他事件上的 600 秒默认值。由于此 hook 在每个提示词之前运行,并会阻塞模型处理直到完成,卡住的 hook 会使会话停滞。如果您的 hook 需要更多时间,请在 hook 条目中设置 `timeout` 字段。1420对于 `command`、`http` 和 `mcp_tool` 类型,`UserPromptSubmit` hook 的默认超时时间为 30 秒,短于这些类型在大多数其他事件上 600 秒的默认值。由于此 hook 在每个提示词之前运行,并且在完成前会阻塞模型处理,卡住的 hook 会使会话停滞。如果您的 hook 需要更多时间,请在 hook 条目中设置 `timeout` 字段。
1422 1421
1423除了您使用 [`async: true`](#run-hooks-in-the-background) 运行的命令 hook 之外,达到超时的 `UserPromptSubmit` 命令、HTTP 或 MCP 工具 hook 会被取消,其输出(包括任何 `additionalContext`)会被丢弃。提示词仍会传递给 Claude,但不带该上下文。会话记录中会显示一条通知,指明该 hook、触发的超时时间,以及输出已被丢弃。1422除了使用 [`async: true`](#run-hooks-in-the-background) 运行的 command hook 外,达到超时的 `UserPromptSubmit` command、HTTP 或 MCP 工具 hook 会被取消,其输出(包括任何 `additionalContext`)会被丢弃。提示词仍会传达给 Claude,只是不带该上下文。会话记录中会显示一条通知,指明该 hook、触发的超时时间,以及输出已被丢弃。
1424 1423
1425`UserPromptSubmit` 上达到超时的 [Agent SDK 回调 hook](/docs/zh-CN/agent-sdk/hooks) 会阻止该提示词,并显示一条指明该 hook 和超时时间的消息,因为此处的回调可能充当不能在失败时放行的策略关卡。会话会继续。在 v2.1.208 之前,该事件上的回调超时会以执行错误结束该轮次。1424`UserPromptSubmit` 上的 [Agent SDK 回调 hook](/docs/zh-CN/agent-sdk/hooks) 达到超时时,会阻止该提示词,并显示一条指明该 hook 和超时时间的消息,因为此处的回调可能充当不能失败放行的策略关卡。会话会继续。在 v2.1.208 之前,该事件上的回调超时会以执行错误结束该轮次。
1426 1425
1427<h4 id="userpromptsubmit-input">1426<h4 id="userpromptsubmit-input">
1428 UserPromptSubmit 输入1427 UserPromptSubmit 输入
1429</h4>1428</h4>
1430 1429
1431除了[通用输入字段](#common-input-fields)之外,UserPromptSubmit hook 还会接收包含所提交文本的 `prompt` 字段。折叠为 `[Pasted text #N]` 占位符的粘贴内容会在原位置展开后传入。在 Claude Code [为 Claude 标记粘贴文本](/docs/zh-CN/terminal-config#how-claude-treats-pasted-text)的会话中,展开的内容位于 `<pasted_content id="…">` 行和 `</pasted_content id="…">` 行之间,因此如果您的 hook 解析提示词,请考虑这些行。1430除[通用输入字段](#common-input-fields)外,UserPromptSubmit hook 还会接收包含所提交文本的 `prompt` 字段。被折叠为 `[Pasted text #N]` 占位符的粘贴内容会在原处展开后传入。在 Claude Code [为 Claude 标记粘贴文本](/docs/zh-CN/terminal-config#how-claude-treats-pasted-text)的会话中,展开的内容位于 `<pasted_content id="…">` 行和 `</pasted_content id="…">` 行之间,因此如果您的 hook 需要解析提示词,请考虑这些行。
1432 1431
1433当会话具有自定义标题时,UserPromptSubmit hook 还会接收 `session_title`,其含义与 [SessionStart 的 `session_title` 字段](#sessionstart-input)相同。1432当会话有自定义标题时,UserPromptSubmit hook 还会接收 `session_title`,其含义与 [SessionStart 的 `session_title` 字段](#sessionstart-input)相同。
1434 1433
1435```json theme={null}1434```json theme={null}
1436{1435{
1447 UserPromptSubmit 决策控制1446 UserPromptSubmit 决策控制
1448</h4>1447</h4>
1449 1448
1450`UserPromptSubmit` hook 可以控制是否处理已提交的提示词,并添加上下文。所有 [JSON 输出字段](#json-output)均可用。1449`UserPromptSubmit` hook 可以控制是否处理提交的提示词,并添加上下文。所有 [JSON 输出字段](#json-output)均可用。
1451 1450
1452在退出码为 0 时,有两种方式向对话添加上下文:1451在退出码为 0 时,有两种方式可以向对话添加上下文:
1453 1452
1454* **纯文本 stdout**:Claude Code 会将其[视为纯文本](#exit-code-0)的 stdout 添加到 Claude 的上下文中1453* **纯文本 stdout**:Claude Code 会将其[视为纯文本](#exit-code-0)的 stdout 添加到 Claude 的上下文中
1455* **带有 `additionalContext` 的 JSON**:使用下面的 JSON 格式以获得更多控制。`additionalContext` 字段会作为上下文添加1454* **带有 `additionalContext` 的 JSON**:使用下面的 JSON 格式以获得更多控制。`additionalContext` 字段会作为上下文添加
1456 1455
1457这两种方式都不会在会话记录中产生可见条目。纯 stdout 和 `additionalContext` 值各自作为以 hook 名称开头的系统提醒注入;Claude 会读取两者。要确认是否已传递,请查看[调试日志](#debug-hooks)。1456两种方式都不会在会话记录中生成可见条目。纯 stdout 和 `additionalContext` 的值都会各自作为以 hook 名称开头的系统提醒注入;Claude 会读取两者。要确认已送达,请查看[调试日志](#debug-hooks)。
1458 1457
1459要阻止提示词,请返回一个 `decision` 设置为 `"block"` 的 JSON 对象:1458要阻止提示词,请返回一个 `decision` 设置为 `"block"` 的 JSON 对象:
1460 1459
1461| 字段 | 描述 |1460| 字段 | 描述 |
1462| :- | :- |1461| :- | :- |
1463| `decision` | `"block"` 会在提示词到达 Claude 之前将其拦截。省略则允许提示词继续 |1462| `decision` | `"block"` 会在提示词到达 Claude 之前将其拦截。省略则允许提示词继续 |
1464| `reason` | 当 `decision` 为 `"block"` 时显示给用户。不会添加到上下文中 |1463| `reason` | 当 `decision` 为 `"block"` 时向用户显示。不会添加到上下文中 |
1465| `additionalContext` | 与提交的提示词一起添加到 Claude 上下文中的字符串。请参阅[为 Claude 添加上下文](#add-context-for-claude) |1464| `additionalContext` | 与所提交的提示词一同添加到 Claude 上下文中的字符串。请参阅[为 Claude 添加上下文](#add-context-for-claude) |
1466| `sessionTitle` | 设置会话标题。可用于根据提示词内容自动命名会话 |1465| `sessionTitle` | 设置会话标题。可用于根据提示词内容自动命名会话 |
1467| `suppressOriginalPrompt` | 如果在 hook 阻止提示词时为 `true`,则阻止消息中不包含提示词文本。请参阅[被阻止的提示词会留下什么](#what-a-blocked-prompt-leaves-behind) |1466| `suppressOriginalPrompt` | 如果在 hook 阻止提示词时为 `true`,则阻止消息中不包含提示词文本。请参阅[被阻止的提示词会留下什么](#what-a-blocked-prompt-leaves-behind) |
1468 1467
1469通过以退出码 2 退出来阻止的 hook,其处理方式与 `reason` 相同:阻止消息会向用户显示 stderr 文本,且不会添加到上下文中。1468通过以 2 退出来阻止的 hook,其处理方式与 `reason` 相同:阻止消息会向用户显示 stderr 文本,且不会添加到上下文中。
1470 1469
1471```json theme={null}1470```json theme={null}
1472{1471{
1485 被阻止的提示词会留下什么1484 被阻止的提示词会留下什么
1486</h4>1485</h4>
1487 1486
1488被阻止的提示词永远不会到达 Claude,但其文本并不会从所有地方移除。默认情况下,显示给用户的阻止消息以 `Original prompt:` 结尾,后跟所提交的文本,并且 Claude Code 会将该消息写入磁盘上的会话记录文件。要在消息中省略该文本,请在 `hookSpecificOutput` 中打印带有 `"suppressOriginalPrompt": true` 的 JSON。无论 hook 是通过 `decision: "block"` 还是以退出码 2 退出来阻止,此设置都有效。1487被阻止的提示词永远不会到达 Claude,但其文本并不会在所有地方被删除。默认情况下,向用户显示的阻止消息以 `Original prompt:` 结尾,后接所提交的文本,并且 Claude Code 会将该消息写入磁盘上会话的会话记录文件。要在消息中省略该文本,请在 `hookSpecificOutput` 中打印带有 `"suppressOriginalPrompt": true` 的 JSON。无论 hook 是通过 `decision: "block"` 还是通过以 2 退出来阻止,此方法均有效。
1489 1488
1490`suppressOriginalPrompt` 仅更改阻止消息。提交的文本仍可能出现在本地文件中,例如会话记录和您的提示词历史记录,因此阻止型 hook 并不是防止机密写入磁盘的方法。要限制或删除这些文件,请参阅[明文存储](/docs/zh-CN/claude-directory#plaintext-storage)和[清除本地数据](/docs/zh-CN/claude-directory#clear-local-data)。1489`suppressOriginalPrompt` 只会改变阻止消息。所提交的文本仍可能出现在本地文件中,例如会话记录和您的提示词历史记录,因此阻止型 hook 并不能让机密信息不落盘。要限制或删除这些文件,请参阅[明文存储](/docs/zh-CN/claude-directory#plaintext-storage)和[清除本地数据](/docs/zh-CN/claude-directory#clear-local-data)。
1491 1490
1492<h3 id="userpromptexpansion">1491<h3 id="userpromptexpansion">
1493 UserPromptExpansion1492 UserPromptExpansion
1494</h3>1493</h3>
1495 1494
1496在用户输入的命令展开为提示词、到达 Claude 之前运行。可用于阻止直接调用特定命令、为特定 skill 注入上下文,或记录用户调用了哪些命令。例如,匹配 `deploy` 的 hook 可以在不存在批准文件时阻止 `/deploy`,或者匹配某个审查 skill 的 hook 可以将团队的审查清单作为 `additionalContext` 追加。1495当用户输入的命令在到达 Claude 之前展开为提示词时运行。可用于阻止直接调用特定命令、为特定 skill 注入上下文,或记录用户调用了哪些命令。例如,匹配 `deploy` 的 hook 可以在不存在批准文件时阻止 `/deploy`,或者匹配某个审查 skill 的 hook 可以将团队的审查清单作为 `additionalContext` 附加。
1497 1496
1498此事件覆盖了 `PreToolUse` 未覆盖的路径:匹配 `Skill` 工具的 `PreToolUse` hook 只在 Claude 调用该工具时触发,而直接输入 `/skillname` 会绕过 `PreToolUse`。`UserPromptExpansion` 会在这条直接路径上触发。1497此事件覆盖了 `PreToolUse` 无法覆盖的路径:匹配 `Skill` 工具的 `PreToolUse` hook 只在 Claude 调用该工具时触发,但直接输入 `/skillname` 会绕过 `PreToolUse`。`UserPromptExpansion` 会在这条直接路径上触发。
1499 1498
1500针对 `command_name` 进行匹配。将匹配器留空可在每个提示词类型的命令上触发。1499匹配 `command_name`。将匹配器留空可在每个提示词类型的命令上触发。
1501 1500
1502<h4 id="userpromptexpansion-input">1501<h4 id="userpromptexpansion-input">
1503 UserPromptExpansion 输入1502 UserPromptExpansion 输入
1504</h4>1503</h4>
1505 1504
1506除了[通用输入字段](#common-input-fields)之外,UserPromptExpansion hook 还会接收 `expansion_type`、`command_name`、`command_args`、`command_source` 以及原始的 `prompt` 字符串。对于 skill 和自定义命令,`expansion_type` 字段为 `slash_command`;对于 MCP 服务器提示词,该字段为 `mcp_prompt`。1505除[通用输入字段](#common-input-fields)外,UserPromptExpansion hook 还会接收 `expansion_type`、`command_name`、`command_args`、`command_source` 以及原始的 `prompt` 字符串。对于 skill 和自定义命令,`expansion_type` 字段为 `slash_command`;对于 MCP 服务器提示词,则为 `mcp_prompt`。
1507 1506
1508```json theme={null}1507```json theme={null}
1509{1508{
1524 UserPromptExpansion 决策控制1523 UserPromptExpansion 决策控制
1525</h4>1524</h4>
1526 1525
1527`UserPromptExpansion` hook 可以阻止展开或添加上下文。所有 [JSON 输出字段](#json-output)均可使用。1526`UserPromptExpansion` hook 可以阻止展开或添加上下文。所有 [JSON 输出字段](#json-output)均可用。
1528 1527
1529| 字段 | 描述 |1528| 字段 | 描述 |
1530| :- | :- |1529| :- | :- |
1531| `decision` | `"block"` 会阻止命令展开。省略则允许其继续 |1530| `decision` | `"block"` 会阻止命令展开。省略则允许其继续 |
1532| `reason` | 当 `decision` 为 `"block"` 时显示给用户 |1531| `reason` | 当 `decision` 为 `"block"` 时向用户显示 |
1533| `additionalContext` | 与展开后的提示词一起添加到 Claude 上下文中的字符串。请参阅[为 Claude 添加上下文](#add-context-for-claude) |1532| `additionalContext` | 与展开后的提示词一同添加到 Claude 上下文中的字符串。请参阅[为 Claude 添加上下文](#add-context-for-claude) |
1534 1533
1535通过以退出码 2 退出来阻止的 hook,其处理方式与 `reason` 相同:阻止消息会向用户显示 stderr 文本。1534通过以 2 退出来阻止的 hook,其处理方式与 `reason` 相同:阻止消息会向用户显示 stderr 文本。
1536 1535
1537```json theme={null}1536```json theme={null}
1538{1537{
1549 MessageDisplay1548 MessageDisplay
1550</h3>1549</h3>
1551 1550
1552在助手消息流式显示到屏幕上时运行。Claude Code 以增量方式显示消息:每当一批新完成的行准备好渲染时,hook 就会以这些行运行一次,Claude Code 会在原位置渲染 hook 的替换文本。长消息会产生多次调用;短消息可能只产生一次。1551在助手消息流式显示到屏幕上时运行。Claude Code 会分批显示消息:每当一批新完成的行准备好渲染时,hook 会使用这些行运行一次,Claude Code 会在原位置渲染 hook 返回的替换文本。长消息会产生多次调用;短消息可能只产生一次。
1553 1552
1554可使用 MessageDisplay 来:1553使用 MessageDisplay 可以:
1555 1554
1556* 去除 markdown 以实现极简显示1555* 去除 markdown 以获得极简显示
1557* 转换 Agent SDK 应用程序向其用户显示的文本1556* 转换 Agent SDK 应用向其用户显示的文本
1558* 从 Claude 的回复中编辑隐去 API 密钥或内部主机名1557* 从 Claude 的回复中隐去 API 密钥或内部主机名
1559 1558
1560Claude Code 会保留每一批内容直到您的 hook 返回,因此请保持 hook 快速执行。如果 hook 失败或超时,Claude Code 会显示原始文本。此事件的默认超时时间为 10 秒;如果您的 hook 需要更多时间,请在 hook 条目中设置 `timeout` 字段。1559Claude Code 会保留每一批内容,直到您的 hook 返回,因此请确保 hook 运行迅速。如果 hook 失败或超时,Claude Code 会显示原始文本。此事件的默认超时时间为 10 秒;如果您的 hook 需要更多时间,请在 hook 条目中设置 `timeout` 字段。
1561 1560
1562MessageDisplay 仅影响显示:替换文本只改变屏幕上渲染的内容。会话记录和 Claude 看到的内容保留原始文本,因此 Claude 永远看不到替换内容,详细模式也会显示原始文本。该 hook 仅接收助手消息文本,因此工具结果和您输入的文本会原样渲染。1561MessageDisplay 仅作用于显示:替换文本只会改变屏幕上渲染的内容。会话记录以及 Claude 看到的内容都保持原始文本,因此 Claude 永远看不到替换内容,详细模式也会显示原始文本。该 hook 只接收助手消息文本,因此工具结果和您输入的文本会原样渲染。
1563 1562
1564MessageDisplay 不支持匹配器,会针对每条流式输出文本的助手消息触发;不含文本的消息(例如仅包含工具调用的回复)不会触发它。1563MessageDisplay 不支持匹配器,并会针对每条流式输出文本的助手消息触发;不含文本的消息(例如仅包含工具调用的响应)不会触发它。
1565 1564
1566在非交互运行中(包括 Agent SDK 查询和 `claude -p`),MessageDisplay 对每条助手消息运行一次,而不是对每批行运行一次。这一次调用在消息完成后到达,并携带完整的消息文本:`index` 为 `0`,`final` 为 `true`,`delta` 包含整条消息。收集每条消息 `delta` 文本的 hook 在两种模式下收到的总文本相同。1565在非交互式运行中(包括 Agent SDK 查询和 `claude -p`),MessageDisplay 对每条助手消息只运行一次,而不是每批行运行一次。这唯一的一次调用在消息完成后到达,并携带完整的消息文本:`index` 为 `0`,`final` 为 `true`,`delta` 包含整条消息。收集每条消息 `delta` 文本的 hook 在两种模式下接收到的总文本相同。
1567 1566
1568<h4 id="messagedisplay-input">1567<h4 id="messagedisplay-input">
1569 MessageDisplay 输入1568 MessageDisplay 输入
1570</h4>1569</h4>
1571 1570
1572除了[通用输入字段](#common-input-fields)之外,MessageDisplay hook 还会接收轮次和消息的标识符、此次调用在消息中的位置,以及 `delta` 中的新文本。批次边界取决于文本的流式传输方式,因此请使用 `index` 和 `final` 跟踪消息的进度,而不要期望行以特定方式分组。1571除[通用输入字段](#common-input-fields)外,MessageDisplay hook 还会接收轮次和消息的标识符、此次调用在消息中的位置,以及 `delta` 中的新文本。批次边界取决于文本的流式传输方式,因此请使用 `index` 和 `final` 来跟踪消息的进度,而不要假定各行会以特定方式分组。
1573 1572
1574| 字段 | 描述 |1573| 字段 | 描述 |
1575| :- | :- |1574| :- | :- |
1577| `message_id` | 正在显示的助手消息的 UUID。在同一消息的每一批中保持不变。这不是 API 的 `msg_…` id,因此无法与会话记录中的消息 id 关联 |1576| `message_id` | 正在显示的助手消息的 UUID。在同一消息的每一批中保持不变。这不是 API 的 `msg_…` id,因此无法与会话记录中的消息 id 关联 |
1578| `index` | 此批次在消息中的从零开始的索引 |1577| `index` | 此批次在消息中的从零开始的索引 |
1579| `final` | 在消息的最后一批上为 `true`。每条消息恰好有一个最终批次 |1578| `final` | 在消息的最后一批上为 `true`。每条消息恰好有一个最终批次 |
1580| `delta` | 自上一批次以来新完成的行,包含结尾的换行符。始终为完整的行,但最终批次可能在行中间结束。在交互运行中,当消息以换行符结尾时,最终批次的 delta 为空,因此请将 `final`(而非非空的 delta)视为消息结束的信号。在 Agent SDK 和 `claude -p` 运行中,单次调用携带整条消息 |1579| `delta` | 自上一批次以来新完成的行,包括结尾的换行符。始终是完整的行,但最终批次可能在行中间结束。在交互式运行中,当消息以换行符结尾时,最终批次的 delta 为空,因此请将 `final`(而不是非空的 delta)作为消息结束的信号。在 Agent SDK 和 `claude -p` 运行中,这唯一的一次调用携带整条消息 |
1581 1580
1582```json theme={null}1581```json theme={null}
1583{1582{
1597 MessageDisplay 输出1596 MessageDisplay 输出
1598</h4>1597</h4>
1599 1598
1600除了所有 hook 都可用的 [JSON 输出字段](#json-output)之外,MessageDisplay hook 还可以返回 `displayContent`,以在屏幕上替换 delta:1599除所有 hook 都可用的 [JSON 输出字段](#json-output)外,MessageDisplay hook 还可以返回 `displayContent`,以在屏幕上替换 delta:
1601 1600
1602| 字段 | 描述 |1601| 字段 | 描述 |
1603| :- | :- |1602| :- | :- |
1604| `displayContent` | 代替 delta 显示的文本。省略则显示原始内容 |1603| `displayContent` | 代替 delta 显示的文本。省略则显示原始文本 |
1605 1604
1606MessageDisplay hook 没有决策控制。它们无法阻止消息,也无法更改存储在会话记录中或发送给 Claude 的内容。Claude Code 会处理其 JSON 输出中的 `displayContent`,并丢弃 `systemMessage` 和 `continue`。1605MessageDisplay hook 没有决策控制。它们无法阻止消息,也无法更改存储在会话记录中或发送给 Claude 的内容。Claude Code 会处理其 JSON 输出中的 `displayContent`,并丢弃 `systemMessage` 和 `continue`。
1607 1606
1608以下示例从 Claude 的回复中去除 markdown 格式,以实现纯文本显示。该脚本从 stdin 读取每一批内容,从 `delta` 中移除粗体标记和行内代码反引号,并将结果作为 `displayContent` 返回。1607此示例从 Claude 的回复中去除 markdown 格式,以实现纯文本显示。该脚本从 stdin 读取每一批内容,从 `delta` 中移除粗体标记和行内代码反引号,并将结果作为 `displayContent` 返回。
1609 1608
1610<Tabs>1609<Tabs>
1611 <Tab title="macOS/Linux">1610 <Tab title="macOS/Linux">
1612 在您的设置文件中为该事件注册一个命令 hook:1611 在您的设置文件中为该事件注册一个 command hook:
1613 1612
1614 ```json theme={null}1613 ```json theme={null}
1615 {1614 {
1638 </Tab>1637 </Tab>
1639 1638
1640 <Tab title="Windows (PowerShell)">1639 <Tab title="Windows (PowerShell)">
1641 注册一个通过 PowerShell 运行脚本的命令 hook:1640 注册一个通过 PowerShell 运行脚本的 command hook:
1642 1641
1643 ```json theme={null}1642 ```json theme={null}
1644 {1643 {
1664 }1663 }
1665 ```1664 ```
1666 1665
1667 `-NoProfile` 标志会跳过加载您的 PowerShell 配置文件,使 hook 快速启动;`-ExecutionPolicy Bypass` 则允许 PowerShell 运行本地脚本文件。1666 `-NoProfile` 标志会跳过加载您的 PowerShell 配置文件,使 hook 快速启动,`-ExecutionPolicy Bypass` 则允许 PowerShell 运行本地脚本文件。
1668 1667
1669 将此脚本保存到项目中的 `.claude/hooks/plain-display.ps1`:1668 将此脚本保存到项目中的 `.claude/hooks/plain-display.ps1`:
1670 1669
1681 </Tab>1680 </Tab>
1682</Tabs>1681</Tabs>
1683 1682
1684不含 markdown 的批次会原样通过。如果脚本失败(例如因为缺少 `jq`),Claude Code 会显示原始文本,并且仅在[调试输出](#debug-hooks)中记录该失败,而不会在会话中显示。1683不含 markdown 的批次会原样通过。如果脚本失败(例如因为缺少 `jq`),Claude Code 会显示原始文本,并且只在[调试输出](#debug-hooks)中记录该失败,而不会在会话中显示。
1685 1684
1686<h3 id="pretooluse">1685<h3 id="pretooluse">
1687 PreToolUse1686 PreToolUse
1688</h3>1687</h3>
1689 1688
1690在 Claude 创建工具参数之后、处理工具调用之前运行。可匹配除 `EndConversation` 以外的任何工具名称:内置工具,例如 `Bash`、`PowerShell`、`Edit`、`Write`、`Read`、`Glob`、`Grep`、`Agent`、`Workflow`、`WebFetch`、`WebSearch`、`AskUserQuestion` 和 `ExitPlanMode`,以及任何 [MCP 工具名称](#match-mcp-tools)。1689在 Claude 创建工具参数之后、处理工具调用之前运行。可匹配除 `EndConversation` 之外的任何工具名称:包括 `Bash`、`PowerShell`、`Edit`、`Write`、`Read`、`Glob`、`Grep`、`Agent`、`Workflow`、`WebFetch`、`WebSearch`、`AskUserQuestion` 和 `ExitPlanMode` 等内置工具,以及任何 [MCP 工具名称](#match-mcp-tools)。
1691 1690
1692要在特定文件于磁盘上发生更改时运行 hook(无论是什么写入的),请使用 [FileChanged](#filechanged),而不是按名称匹配文件编辑工具。与 PreToolUse 不同,Claude Code 在更改之后运行 FileChanged hook,且它们没有决策控制,因此无法阻止写入。1691要在磁盘上某个特定文件发生变化时运行 hook(无论是谁写入的),请使用 [FileChanged](#filechanged),而不是按名称匹配文件编辑工具。与 PreToolUse 不同,Claude Code 在更改发生后运行 FileChanged hook,并且它们没有决策控制,因此无法阻止写入。
1693 1692
1694<Warning>1693<Warning>
1695 PreToolUse 仅在 Claude 调用工具时运行。您[在提示词中使用 `@` 引用](/docs/zh-CN/common-workflows#reference-files-and-directories)的文件是在没有任何工具调用的情况下添加的:Claude Code 在构建提示词时插入其内容,因此不会为它们触发任何 PreToolUse hook,包括匹配 `Read` 的 hook。要阻止特定路径被 `@` 引用,请改用 [`Read` 拒绝规则](/docs/zh-CN/permissions#read-and-edit)。1694 PreToolUse 仅在 Claude 调用工具时运行。您[在提示词中使用 `@` 引用](/docs/zh-CN/common-workflows#reference-files-and-directories)的文件无需任何工具调用即可添加:Claude Code 在构建提示词时插入其内容,因此不会为它们触发任何 PreToolUse hook,包括匹配 `Read` 的 hook。要阻止通过 `@` 引用特定路径,请改用 [`Read` 拒绝规则](/docs/zh-CN/permissions#read-and-edit)。
1696 1695
1697 PreToolUse 也不会为 [`EndConversation`](/docs/zh-CN/tools-reference#endconversation-tool-behavior) 触发。1696 PreToolUse 也不会为 [`EndConversation`](/docs/zh-CN/tools-reference#endconversation-tool-behavior) 触发。
1698</Warning>1697</Warning>
1699 1698
1700使用 [PreToolUse 决策控制](#pretooluse-decision-control)来允许、拒绝、询问或延迟工具调用。1699使用 [PreToolUse 决策控制](#pretooluse-decision-control)来允许、拒绝、询问或推迟工具调用。
1701 1700
1702`PreToolUse` 上超过其超时时间的 [Agent SDK 回调 hook](/docs/zh-CN/agent-sdk/hooks) 会阻止该工具调用,Claude 会收到一个指明该超时的错误结果。其他 hook 返回的显式拒绝仍然优先。1701`PreToolUse` 上的 [Agent SDK 回调 hook](/docs/zh-CN/agent-sdk/hooks) 超过其超时时间时,会阻止该工具调用,Claude 会收到一个指明超时的错误结果。其他 hook 返回的显式拒绝仍然优先。
1703 1702
1704<h4 id="pretooluse-input">1703<h4 id="pretooluse-input">
1705 PreToolUse 输入1704 PreToolUse 输入
1706</h4>1705</h4>
1707 1706
1708除了[通用输入字段](#common-input-fields)之外,PreToolUse hook 还会接收 `tool_name`、`tool_input` 和 `tool_use_id`。1707除[通用输入字段](#common-input-fields)外,PreToolUse hook 还会接收 `tool_name`、`tool_input` 和 `tool_use_id`。
1709 1708
1710对于 [MCP 工具](#match-mcp-tools),输入中还会携带 `mcp_server`,这是一个包含服务器 `name` 和 `source` 的对象,`source` 说明服务器定义的来源。`source` 的值包括 `plugin`、`sdk`,以及 `user` 和 `project` 等配置作用域。Agent SDK 参考中的 [`McpServerProvenance`](/docs/zh-CN/agent-sdk/typescript#mcpserverprovenance) 列出了所有值,并说明了如何处理无法识别的值。请基于 `source` 做出信任决策,而不是基于 `name` 或 `mcp__<server>__` 工具名称前缀。`mcp_server` 字段需要 Claude Code v2.1.274 或更高版本。1709对于 [MCP 工具](#match-mcp-tools),输入中还会包含 `mcp_server`,这是一个对象,包含服务器的 `name`,以及说明服务器定义来源的 `source`。`source` 的值包括 `plugin`、`sdk`,以及 `user` 和 `project` 等配置作用域。Agent SDK 参考中的 [`McpServerProvenance`](/docs/zh-CN/agent-sdk/typescript#mcpserverprovenance) 列出了所有值,并说明了如何处理无法识别的值。请基于 `source` 而不是 `name` 或 `mcp__<server>__` 工具名前缀做出信任决策。`mcp_server` 字段需要 Claude Code v2.1.274 或更高版本。
1711 1710
1712对于文件工具 `Write`、`Edit` 和 `Read`,`tool_input.file_path` 始终是绝对路径:1711对于文件工具 `Write`、`Edit` 和 `Read`,`tool_input.file_path` 始终是绝对路径:
1713 1712
1714* Claude Code 会在 hook 运行之前展开 `~` 和相对路径,因此基于路径匹配的 hook 无法通过 `~` 或同一路径的相对写法被绕过1713* Claude Code 会在 hook 运行前展开 `~` 和相对路径,因此基于路径匹配的 hook 无法通过 `~` 或同一路径的相对写法被绕过
1715* 在 Windows 上,路径以反斜杠分隔符传入,即使您的 hook 在 Git Bash 下运行且 `$PWD` 看起来像 `/c/project`1714* 在 Windows 上,路径以反斜杠分隔符传入,即使您的 hook 在 Git Bash 下运行(其中 `$PWD` 看起来像 `/c/project`)也是如此
1716* 使用正斜杠编写的比较(例如 `/src/` 检查)永远不会匹配反斜杠路径,工具调用会像 hook 没有可阻止的内容一样继续进行1715* 使用正斜杠编写的比较(例如 `/src/` 检查)永远不会匹配反斜杠路径,工具调用会像 hook 没有任何需要阻止的内容一样继续进行
1717* 在比较之前规范化分隔符:Bash 中使用 `FILE_PATH="${FILE_PATH//\\//}"`,Python 中使用 `file_path.replace("\\", "/")`,然后匹配诸如 `/src/` 之类的路径片段,而不是用 `^` 锚定,因为路径是绝对路径1716* 比较之前请先规范化分隔符:在 Bash 中使用 `FILE_PATH="${FILE_PATH//\\//}"`,在 Python 中使用 `file_path.replace("\\", "/")`,然后匹配诸如 `/src/` 这样的路径片段,而不要用 `^` 锚定,因为路径是绝对路径
1718 1717
1719Windows 上的 `Write` 调用会传入:1718Windows 上的 `Write` 调用会传入:
1720 1719
1743| 字段 | 类型 | 示例 | 描述 |1742| 字段 | 类型 | 示例 | 描述 |
1744| :- | :- | :- | :- |1743| :- | :- | :- | :- |
1745| `command` | string | `"npm test"` | 要执行的 shell 命令 |1744| `command` | string | `"npm test"` | 要执行的 shell 命令 |
1746| `description` | string | `"Run test suite"` | 可选的命令功能描述 |1745| `description` | string | `"Run test suite"` | 可选,描述该命令的作用 |
1747| `timeout` | number | `120000` | 可选的超时时间(毫秒)。超过[最大值](/docs/zh-CN/tools-reference#bash-tool-behavior)的值会被降低为最大值,而不会被拒绝 |1746| `timeout` | number | `120000` | 可选,以毫秒为单位的超时时间。超过[最大值](/docs/zh-CN/tools-reference#bash-tool-behavior)的值会被降至最大值,而不会被拒绝 |
1748| `run_in_background` | boolean | `false` | 是否在后台运行命令 |1747| `run_in_background` | boolean | `false` | 是否在后台运行该命令 |
1749 1748
1750当 Bash 命令更改 Git 仓库中的文件时,Claude Code 可以记录更改的内容。当 [`bashEditDiffEnabled`](/docs/zh-CN/settings-reference#basheditdiffenabled) 设置启用记录时,它会在所有权限模式下记录更改;该设置的条目说明了哪些文件可以设置它。否则,它仅在自动模式和 `bypassPermissions` 模式下记录,并且仅在 Claude Code 指示 Claude 通过 Bash 编辑文件时记录。将 `bashEditDiffEnabled` 设置为 `false` 可关闭记录。后台命令和只读命令不携带 diff。1749当 Bash 命令更改 Git 仓库中的文件时,Claude Code 可以记录更改的内容。当 [`bashEditDiffEnabled`](/docs/zh-CN/settings-reference#basheditdiffenabled) 设置启用记录时,它会在所有权限模式下记录更改;该设置的条目说明了哪些文件可以设置它。否则,它仅在自动模式和 `bypassPermissions` 模式下记录,并且仅在 Claude Code 指示 Claude 通过 Bash 编辑文件时记录。将 `bashEditDiffEnabled` 设置为 `false` 可关闭记录。后台命令和只读命令不携带 diff。
1751 1750
1752随后,您的 [PostToolUse hook](#posttooluse) 会在 `tool_response.bashEditDiff` 中接收更改的文件。该列表涵盖命令运行期间仓库下发生更改的内容。Git 忽略的文件和子模块中的文件不会列出。需要 Claude Code v2.1.269 或更高版本。1751随后,您的 [PostToolUse hook](#posttooluse) 会在 `tool_response.bashEditDiff` 中接收到已更改的文件。该列表涵盖命令运行期间仓库下发生的更改。Git 忽略的文件和子模块中的文件不会列出。需要 Claude Code v2.1.269 或更高版本。
1753 1752
1754<Note>1753<Note>
1755 该列表是尽力而为的,目前处于公测阶段。Claude Code 可能会遗漏更改、包含同时被另一个进程更改的文件,或在达到大小限制时停止。字段结构可能会发生变化。请使用该列表来确定需要审查的内容,而不要用它来执行策略。1754 该列表为尽力而为,目前处于公测阶段。Claude Code 可能会遗漏更改、包含同一时间被其他进程更改的文件,或在达到其大小限制时停止。字段结构可能会变化。请使用该列表来确定需要审查的内容,而不要用它来强制执行策略。
1756</Note>1755</Note>
1757 1756
1758`changedFiles` 和 `files` 列出命令更改的内容;其余字段说明该列表的完整程度和可靠程度。1757`changedFiles` 和 `files` 列出命令更改的内容;其余字段说明该列表的完整程度和可靠程度。
1759 1758
1760| 字段 | 类型 | 示例 | 描述 |1759| 字段 | 类型 | 示例 | 描述 |
1761| :- | :- | :- | :- |1760| :- | :- | :- | :- |
1762| `changedFiles` | array | `["/path/to/src/app.ts"]` | 命令更改的文件的绝对路径,最多 200 个。只要 `files` 包含 diff 或 `moreFiles` 大于零,该字段就会出现 |1761| `changedFiles` | array | `["/path/to/src/app.ts"]` | 命令更改的文件的绝对路径,最多 200 个。只要 `files` 中包含 diff 或 `moreFiles` 大于零,该字段就会存在 |
1763| `files` | array | `[{"filePath": "/path/to/src/app.ts", "hunks": [...]}]` | 最多 5 个已更改文件的 diff,用于显示。对于命令添加或删除的文件,`created` 或 `deleted` 为 `true` |1762| `files` | array | `[{"filePath": "/path/to/src/app.ts", "hunks": [...]}]` | 最多 5 个已更改文件的 diff,用于显示。对于命令新增或删除的文件,`created` 或 `deleted` 为 `true` |
1764| `moreFiles` | number | `2` | 在 `files` 中没有 diff 的已更改文件数量 |1763| `moreFiles` | number | `2` | 在 `files` 中没有 diff 的已更改文件数量 |
1765| `unavailable` | boolean | `true` | 当 diff 不完整或无法获取时设置 |1764| `unavailable` | boolean | `true` | 当 diff 不完整或无法获取时设置 |
1766| `skipped` | boolean | `true` | 针对会移动工作树的 Git 命令(例如 `git checkout` 或 `git stash`)设置,此时 Claude Code 不获取 diff |1765| `skipped` | boolean | `true` | 针对会移动工作树的 Git 命令(例如 `git checkout` 或 `git stash`)设置,此时 Claude Code 不获取 diff |
1767| `shared` | boolean | `true` | 当另一个 Bash 工具调用(例如子代理的调用)同时在同一仓库中运行时设置,因此列出的部分更改可能来自该命令 |1766| `shared` | boolean | `true` | 当另一个 Bash 工具调用(例如子代理的调用)同时在同一仓库中运行时设置,因此列出的某些更改可能来自该命令 |
1768 1767
1769<a id="powershell" />1768<a id="powershell" />
1770 1769
1772 PowerShell1771 PowerShell
1773</h5>1772</h5>
1774 1773
1775执行 PowerShell 命令。有关各平台的可用性,请参阅 [PowerShell 工具](/docs/zh-CN/tools-reference#powershell-tool)。1774执行 PowerShell 命令。关于各平台的可用性,请参阅 [PowerShell 工具](/docs/zh-CN/tools-reference#powershell-tool)。
1776 1775
1777字段与 Bash 工具相同,命令字符串位于 `command` 中:1776字段与 Bash 工具相同,命令字符串位于 `command` 中:
1778 1777
1779| 字段 | 类型 | 示例 | 描述 |1778| 字段 | 类型 | 示例 | 描述 |
1780| :- | :- | :- | :- |1779| :- | :- | :- | :- |
1781| `command` | string | `"Get-ChildItem -Recurse"` | 要执行的 PowerShell 命令 |1780| `command` | string | `"Get-ChildItem -Recurse"` | 要执行的 PowerShell 命令 |
1782| `description` | string | `"List files recursively"` | 可选的命令功能描述 |1781| `description` | string | `"List files recursively"` | 可选,描述该命令的作用 |
1783| `timeout` | number | `120000` | 可选的超时时间(毫秒) |1782| `timeout` | number | `120000` | 可选,以毫秒为单位的超时时间 |
1784| `run_in_background` | boolean | `false` | 是否在后台运行命令 |1783| `run_in_background` | boolean | `false` | 是否在后台运行该命令 |
1785 1784
1786在检查 shell 命令的 hook 中匹配 `Bash|PowerShell`,以便同时覆盖这两个工具:1785在检查 shell 命令的 hook 中匹配 `Bash|PowerShell`,以便同时覆盖这两个工具:
1787 1786
1788* 在 Windows 上,只要启用了 PowerShell 工具,Claude 就会将 PowerShell 视为主 shell,并通过它执行 shell 命令。1787* 在 Windows 上,只要启用了 PowerShell 工具,Claude 就会将 PowerShell 视为主 shell,并通过它路由 shell 命令。
1789* 在没有 Git Bash 的 Windows 上,该工具会自动启用,且 Claude Code 根本不会注册 Bash 工具。1788* 在没有 Git Bash 的 Windows 上,该工具会自动启用,Claude Code 根本不会注册 Bash 工具。
1790* 仅匹配 `Bash` 的 hook 在那里永远不会触发。1789* 仅匹配 `Bash` 的 hook 在那里永远不会触发。
1791 1790
1792<h5 id="write">1791<h5 id="write">
1811| `file_path` | string | `"/path/to/file.txt"` | 要编辑的文件的绝对路径 |1810| `file_path` | string | `"/path/to/file.txt"` | 要编辑的文件的绝对路径 |
1812| `old_string` | string | `"original text"` | 要查找并替换的文本 |1811| `old_string` | string | `"original text"` | 要查找并替换的文本 |
1813| `new_string` | string | `"replacement text"` | 替换文本 |1812| `new_string` | string | `"replacement text"` | 替换文本 |
1814| `replace_all` | boolean | `false` | 是否替换所有匹配项 |1813| `replace_all` | boolean | `false` | 是否替换所有出现之处 |
1815 1814
1816<h5 id="read">1815<h5 id="read">
1817 Read1816 Read
1822| 字段 | 类型 | 示例 | 描述 |1821| 字段 | 类型 | 示例 | 描述 |
1823| :- | :- | :- | :- |1822| :- | :- | :- | :- |
1824| `file_path` | string | `"/path/to/file.txt"` | 要读取的文件的绝对路径 |1823| `file_path` | string | `"/path/to/file.txt"` | 要读取的文件的绝对路径 |
1825| `offset` | number | `10` | 可选的开始读取的行号 |1824| `offset` | number | `10` | 可选,开始读取的行号 |
1826| `limit` | number | `50` | 可选的要读取的行数 |1825| `limit` | number | `50` | 可选,要读取的行数 |
1827 1826
1828<h5 id="glob">1827<h5 id="glob">
1829 Glob1828 Glob
1830</h5>1829</h5>
1831 1830
1832查找匹配 glob 模式的文件。1831查找与 glob 模式匹配的文件。
1833 1832
1834| 字段 | 类型 | 示例 | 描述 |1833| 字段 | 类型 | 示例 | 描述 |
1835| :- | :- | :- | :- |1834| :- | :- | :- | :- |
1836| `pattern` | string | `"**/*.ts"` | 用于匹配文件的 glob 模式 |1835| `pattern` | string | `"**/*.ts"` | 用于匹配文件的 glob 模式 |
1837| `path` | string | `"/path/to/dir"` | 可选的搜索目录。默认为当前工作目录 |1836| `path` | string | `"/path/to/dir"` | 可选,要搜索的目录。默认为当前工作目录 |
1838 1837
1839<h5 id="grep">1838<h5 id="grep">
1840 Grep1839 Grep
1845| 字段 | 类型 | 示例 | 描述 |1844| 字段 | 类型 | 示例 | 描述 |
1846| :- | :- | :- | :- |1845| :- | :- | :- | :- |
1847| `pattern` | string | `"TODO.*fix"` | 要搜索的正则表达式模式 |1846| `pattern` | string | `"TODO.*fix"` | 要搜索的正则表达式模式 |
1848| `path` | string | `"/path/to/dir"` | 可选的搜索文件或目录 |1847| `path` | string | `"/path/to/dir"` | 可选,要搜索的文件或目录 |
1849| `glob` | string | `"*.ts"` | 可选的用于过滤文件的 glob 模式 |1848| `glob` | string | `"*.ts"` | 可选,用于过滤文件的 glob 模式 |
1850| `output_mode` | string | `"content"` | `"content"`、`"files_with_matches"` 或 `"count"`。默认为 `"files_with_matches"` |1849| `output_mode` | string | `"content"` | `"content"`、`"files_with_matches"` 或 `"count"`。默认为 `"files_with_matches"` |
1851| `-i` | boolean | `true` | 不区分大小写的搜索 |1850| `-i` | boolean | `true` | 不区分大小写的搜索 |
1852| `multiline` | boolean | `false` | 启用多行匹配 |1851| `multiline` | boolean | `false` | 启用多行匹配 |
1860| 字段 | 类型 | 示例 | 描述 |1859| 字段 | 类型 | 示例 | 描述 |
1861| :- | :- | :- | :- |1860| :- | :- | :- | :- |
1862| `url` | string | `"https://example.com/api"` | 要获取内容的 URL |1861| `url` | string | `"https://example.com/api"` | 要获取内容的 URL |
1863| `prompt` | string | `"Extract the API endpoints"` | 在获取的内容上运行的提示词 |1862| `prompt` | string | `"Extract the API endpoints"` | 对获取到的内容运行的提示词 |
1864 1863
1865<h5 id="websearch">1864<h5 id="websearch">
1866 WebSearch1865 WebSearch
1867</h5>1866</h5>
1868 1867
1869搜索网页。1868搜索网络。
1870 1869
1871| 字段 | 类型 | 示例 | 描述 |1870| 字段 | 类型 | 示例 | 描述 |
1872| :- | :- | :- | :- |1871| :- | :- | :- | :- |
1873| `query` | string | `"react hooks best practices"` | 搜索查询 |1872| `query` | string | `"react hooks best practices"` | 搜索查询 |
1874| `allowed_domains` | array | `["docs.example.com"]` | 可选:仅包含来自这些域名的结果 |1873| `allowed_domains` | array | `["docs.example.com"]` | 可选:仅包含来自这些域的结果 |
1875| `blocked_domains` | array | `["spam.example.com"]` | 可选:排除来自这些域名的结果 |1874| `blocked_domains` | array | `["spam.example.com"]` | 可选:排除来自这些域的结果 |
1876 1875
1877<h5 id="agent">1876<h5 id="agent">
1878 Agent1877 Agent
1885| `prompt` | string | `"Find all API endpoints"` | Agent 要执行的任务 |1884| `prompt` | string | `"Find all API endpoints"` | Agent 要执行的任务 |
1886| `description` | string | `"Find API endpoints"` | 任务的简短描述 |1885| `description` | string | `"Find API endpoints"` | 任务的简短描述 |
1887| `subagent_type` | string | `"Explore"` | 要使用的专用 Agent 类型 |1886| `subagent_type` | string | `"Explore"` | 要使用的专用 Agent 类型 |
1888| `model` | string | `"sonnet"` | 可选的模型别名,用于覆盖默认值 |1887| `model` | string | `"sonnet"` | 可选,用于覆盖默认值的模型别名 |
1889 1888
1890当前台 Agent 调用完成时,您的 [PostToolUse hook](#posttooluse) 会在 `tool_response` 中接收子代理的结果和运行遥测数据。读取这些字段以检查运行情况;对于跨子代理的 token 和成本汇总,请使用按 `query_source` `"subagent"` 过滤的 [token 和成本计数器](/docs/zh-CN/monitoring-usage#token-counter),因为 `totalTokens` 和 `usage` 仅涵盖最终请求:1889当前台 Agent 调用完成时,您的 [PostToolUse hook](#posttooluse) 会在 `tool_response` 中接收到子代理的结果和运行遥测数据。读取这些字段即可检查该次运行;如需汇总各子代理的 token 和成本,请使用按 `query_source` `"subagent"` 过滤的 [token 和成本计数器](/docs/zh-CN/monitoring-usage#token-counter),因为 `totalTokens` 和 `usage` 只涵盖最后一个请求:
1891 1890
1892| 字段 | 类型 | 示例 | 描述 |1891| 字段 | 类型 | 示例 | 描述 |
1893| :- | :- | :- | :- |1892| :- | :- | :- | :- |
1894| `status` | string | `"completed"` | 前台子代理为 `"completed"`,后台子代理为 `"async_launched"`。子代理默认在后台运行,因此省略 `run_in_background` 的 Agent 调用也会产生 `"async_launched"` |1893| `status` | string | `"completed"` | 前台子代理为 `"completed"`,后台子代理为 `"async_launched"`。子代理默认在后台运行,因此省略 `run_in_background` 的 Agent 调用也会产生 `"async_launched"` |
1895| `agentId` | string | `"a4d2c8f1e0b3a297"` | 子代理运行的标识符 |1894| `agentId` | string | `"a4d2c8f1e0b3a297"` | 子代理运行的标识符 |
1896| `content` | array | `[{"type": "text", "text": "Found 12 endpoints..."}]` | 子代理的最终文本块;对于通过 `SubagentHandback` 提交报告的子代理,则改为一条关于该交回的简短说明 |1895| `content` | array | `[{"type": "text", "text": "Found 12 endpoints..."}]` | 子代理的最终文本块;对于通过 `SubagentHandback` 提交报告的子代理,则以一条关于该交回的简短说明代替 |
1897| `resolvedModel` | string | `"claude-sonnet-4-5"` | 子代理启动时使用的模型,可能与请求的模型不同 |1896| `resolvedModel` | string | `"claude-sonnet-4-5"` | 子代理启动时使用的模型,可能与请求的模型不同 |
1898| `modelsUsed` | array | `["claude-sonnet-4-5", "claude-haiku-4-5"]` | 按顺序使用的模型,连续重复项会被合并;仅在运行中途切换了模型时设置。需要 Claude Code v2.1.212 或更高版本 |1897| `modelsUsed` | array | `["claude-sonnet-4-5", "claude-haiku-4-5"]` | 按顺序使用的模型,连续重复项会被合并;仅在运行中途更换模型时设置。需要 Claude Code v2.1.212 或更高版本 |
1899| `totalTokens` | number | `12450` | 子代理最终 API 请求的 token 数:输入、输出和缓存 token 的总和。这不是整个运行的总数 |1898| `totalTokens` | number | `12450` | 子代理最后一次 API 请求的 token 数:输入、输出和缓存 token 之和。这不是整个运行的总数 |
1900| `totalDurationMs` | number | `48211` | 子代理运行的实际耗时 |1899| `totalDurationMs` | number | `48211` | 子代理运行的实际时长 |
1901| `totalToolUseCount` | number | `7` | 子代理进行的工具调用次数 |1900| `totalToolUseCount` | number | `7` | 子代理进行的工具调用次数 |
1902| `usage` | object | `{"input_tokens": 8320, ...}` | 最终 API 请求按类型划分的 token 明细:`input_tokens`、`output_tokens`、`cache_creation_input_tokens`、`cache_read_input_tokens` |1901| `usage` | object | `{"input_tokens": 8320, ...}` | 最后一次 API 请求按类型划分的 token 明细:`input_tokens`、`output_tokens`、`cache_creation_input_tokens`、`cache_read_input_tokens` |
1903 1902
1904在 Claude Code v2.1.271 或更高版本中,使用 [`SubagentHandback`](/docs/zh-CN/tools-reference) 工具(Claude Code 在[自动模式](/docs/zh-CN/permission-modes#eliminate-prompts-with-auto-mode)中提供)运行的子代理会通过该工具提交报告,而不是以文本形式返回。此时,其 `completed` 结果的 `content` 字段携带的是关于该交回的简短说明,而不是报告本身。要读取报告,请让 `PreToolUse` 或 `PostToolUse` hook 匹配 `SubagentHandback`,并读取 `tool_input.message`。1903在 Claude Code v2.1.271 或更高版本中,使用 [`SubagentHandback`](/docs/zh-CN/tools-reference) 工具(Claude Code 在[自动模式](/docs/zh-CN/permission-modes#eliminate-prompts-with-auto-mode)中提供)运行的子代理会通过该工具提交其报告,而不是以文本形式返回。此时其 `completed` 结果的 `content` 字段携带的是关于该交回的简短说明,而不是报告本身。要读取报告,请让 `PreToolUse` 或 `PostToolUse` hook 匹配 `SubagentHandback`,并读取 `tool_input.message`。
1905 1904
1906对于后台子代理,工具会在任务移至后台时返回,因此 `tool_response` 不携带用量字段:后台启动会立即返回,而被 Claude Code 在运行中途移至后台的前台任务会在该转换时返回。它包含 `status: "async_launched"`、`agentId`、`description`、`prompt`、`outputFile` 和 `resolvedModel`。1905对于后台子代理,工具会在任务转入后台时返回,因此 `tool_response` 不携带任何用量字段:后台启动会立即返回,而 Claude Code 在运行中途转入后台的前台任务会在转换时返回。它包含 `status: "async_launched"`、`agentId`、`description`、`prompt`、`outputFile` 和 `resolvedModel`。
1907 1906
1908在 `completed` 响应中,`resolvedModel` 指子代理启动时使用的模型,它可能与 `tool_input` 中的 `model` 值不同,例如在 `availableModels` 或其他覆盖生效时。在 `async_launched` 响应中,`resolvedModel` 指 Agent 移至后台时正在使用的模型,因此在移至后台之前发生的切换会反映在其中。`modelsUsed` 以及移至后台时的 `resolvedModel` 行为需要 Claude Code v2.1.212 或更高版本。1907在 `completed` 响应中,`resolvedModel` 表示子代理启动时使用的模型,它可能与 `tool_input` 中的 `model` 值不同,例如当 `availableModels` 或其他覆盖生效时。在 `async_launched` 响应中,`resolvedModel` 表示 Agent 转入后台时正在使用的模型,因此转入后台之前发生的模型更换会反映在其中。`modelsUsed` 以及转入后台时的 `resolvedModel` 行为需要 Claude Code v2.1.212 或更高版本。
1909 1908
1910<a id="askuserquestion" />1909<a id="askuserquestion" />
1911 1910
1913 AskUserQuestion1912 AskUserQuestion
1914</h5>1913</h5>
1915 1914
1916向用户提出一到四个多项选择题。1915向用户提出一到四个多选题。
1917 1916
1918| 字段 | 类型 | 示例 | 描述 |1917| 字段 | 类型 | 示例 | 描述 |
1919| :- | :- | :- | :- |1918| :- | :- | :- | :- |
1920| `questions` | array | `[{"question": "Which framework?", "header": "Framework", "options": [{"label": "React", "description": "Component library"}, {"label": "Vue", "description": "Progressive framework"}], "multiSelect": false}]` | 要呈现的问题,每个问题包含一个 `question` 字符串、简短的 `header`、`options` 数组,以及可选的 `multiSelect` 标志 |1919| `questions` | array | `[{"question": "Which framework?", "header": "Framework", "options": [{"label": "React", "description": "Component library"}, {"label": "Vue", "description": "Progressive framework"}], "multiSelect": false}]` | 要呈现的问题,每个问题包含 `question` 字符串、简短的 `header`、`options` 数组以及可选的 `multiSelect` 标志 |
1921| `answers` | object | `{"Which framework?": "React"}` | 可选。将问题文本映射到所选选项的标签。多选答案以逗号连接标签。Claude 不会设置此字段;可通过 `updatedInput` 提供它以编程方式作答 |1920| `answers` | object | `{"Which framework?": "React"}` | 可选。将问题文本映射到所选选项的标签。多选答案用逗号连接各标签。Claude 不会设置此字段;如需以编程方式作答,请通过 `updatedInput` 提供 |
1922 1921
1923<h5 id="exitplanmode">1922<h5 id="exitplanmode">
1924 ExitPlanMode1923 ExitPlanMode
1925</h5>1924</h5>
1926 1925
1927在 Claude 离开[计划模式](/docs/zh-CN/permission-modes#analyze-before-you-edit-with-plan-mode)之前呈现计划并请求用户批准。Claude 会在调用该工具之前将计划写入磁盘上的文件,因此模型给出的原始 `tool_input` 通常为空。Claude Code 会在将输入传递给 hook 之前注入计划内容和文件路径。1926呈现一个计划,并在 Claude 离开[计划模式](/docs/zh-CN/permission-modes#analyze-before-you-edit-with-plan-mode)之前请求用户批准。Claude 在调用该工具之前会将计划写入磁盘上的文件,因此模型给出的原始 `tool_input` 通常为空。Claude Code 会在将输入传递给 hook 之前注入计划内容和文件路径。
1928 1927
1929| 字段 | 类型 | 示例 | 描述 |1928| 字段 | 类型 | 示例 | 描述 |
1930| :- | :- | :- | :- |1929| :- | :- | :- | :- |
1931| `plan` | string | `"## Refactor auth\n1. Extract..."` | Markdown 格式的计划内容。从磁盘上的计划文件注入 |1930| `plan` | string | `"## Refactor auth\n1. Extract..."` | Markdown 格式的计划内容。从磁盘上的计划文件注入 |
1932| `planFilePath` | string | `"/Users/.../plans/refactor-auth.md"` | 计划文件的路径。注入 |1931| `planFilePath` | string | `"/Users/.../plans/refactor-auth.md"` | 计划文件的路径。由注入得到 |
1933| `allowedPrompts` | array | `[{"tool": "Bash", "prompt": "run tests"}]` | 已弃用。Claude Code 接受该字段但会忽略它。在 v2.1.205 之前,它携带 Claude 为实施计划而请求的基于提示词的权限 |1932| `allowedPrompts` | array | `[{"tool": "Bash", "prompt": "run tests"}]` | 已弃用。Claude Code 接受该字段但会忽略它。在 v2.1.205 之前,它携带 Claude 为实施计划而请求的基于提示词的权限 |
1934 1933
1935在 `PostToolUse` 中,`tool_response` 是一个对象,其中 `plan` 和 `filePath` 字段保存已批准的计划,另外还有内部状态标志。请读取 `tool_response.plan` 获取计划内容,而不要从磁盘重新读取文件。1934在 `PostToolUse` 中,`tool_response` 是一个对象,其 `plan` 和 `filePath` 字段包含已批准的计划,另外还有一些内部状态标志。请读取 `tool_response.plan` 获取计划内容,而不要从磁盘重新读取文件。
1936 1935
1937<h4 id="pretooluse-decision-control">1936<h4 id="pretooluse-decision-control">
1938 PreToolUse 决策控制1937 PreToolUse 决策控制
1939</h4>1938</h4>
1940 1939
1941`PreToolUse` hook 可以控制工具调用是否继续。与使用顶层 `decision` 字段的其他 hook 不同,PreToolUse 在 `hookSpecificOutput` 对象中返回其决策。这为其提供了更丰富的控制:四种结果(allow、deny、ask 或 defer),以及在执行前修改工具输入的能力。1940`PreToolUse` hook 可以控制工具调用是否继续。与使用顶层 `decision` 字段的其他 hook 不同,PreToolUse 在 `hookSpecificOutput` 对象中返回其决策。这为其提供了更丰富的控制能力:四种结果(允许、拒绝、询问或推迟),以及在执行前修改工具输入的能力。
1942 1941
1943| 字段 | 描述 |1942| 字段 | 描述 |
1944| :- | :- |1943| :- | :- |
1945| `permissionDecision` | `"allow"` 会跳过权限提示,但[任何模式都不会自动批准的操作](/docs/zh-CN/permission-modes#actions-no-mode-auto-approves)以及 `AskUserQuestion` 和 `ExitPlanMode` 除外,后两者需要[与 `updatedInput` 搭配使用](#allow-with-updatedinput)。`"deny"` 会阻止工具调用。`"ask"` 会提示用户确认。`"defer"` 会正常退出,以便稍后恢复该工具。无论 hook 返回什么,[拒绝和询问规则](/docs/zh-CN/permissions#manage-permissions)仍会被评估 |1944| `permissionDecision` | `"allow"` 会跳过权限提示,但[任何模式都不会自动批准的操作](/docs/zh-CN/permission-modes#actions-no-mode-auto-approves)除外,`AskUserQuestion` 和 `ExitPlanMode` 也除外,它们需要[与 `updatedInput` 配合使用](#allow-with-updatedinput)。`"deny"` 会阻止工具调用。`"ask"` 会提示用户确认。`"defer"` 会正常退出,以便稍后恢复该工具。无论 hook 返回什么,[拒绝和询问规则](/docs/zh-CN/permissions#manage-permissions)仍会被评估 |
1946| `permissionDecisionReason` | 对于 `"ask"`,在权限提示中显示给用户。在无人能回答该提示的 `-p` 运行中,当 Claude Code [拒绝该调用](/docs/zh-CN/headless#turn-off-permission-prompts-in-unattended-runs)时,Claude 会改为在工具结果中读到该原因。对于 `"deny"`,显示给 Claude。对于 `"allow"` 和 `"defer"`,仅写入[调试日志](#debug-hooks) |1945| `permissionDecisionReason` | 对于 `"ask"`,在权限提示中向用户显示。当 Claude Code 在无人能响应该提示的 `-p` 运行中[拒绝调用](/docs/zh-CN/headless#turn-off-permission-prompts-in-unattended-runs)时,Claude 会改为在工具结果中读取该原因。对于 `"deny"`,向 Claude 显示。对于 `"allow"` 和 `"defer"`,仅写入[调试日志](#debug-hooks) |
1947| `updatedInput` | 在执行前修改工具的输入参数。会替换整个输入对象,因此请将未更改的字段与修改后的字段一并包含。Claude Code 会针对您的 hook 返回的输入(而不是 Claude 发送的输入)评估权限规则以及 Bash 命令的[自动移至后台资格](/docs/zh-CN/tools-reference#foreground-commands-that-move-to-the-background)。与 `"allow"` 组合可自动批准,与 `"ask"` 组合可向用户显示修改后的输入。对于 `"defer"`,该字段被忽略 |1946| `updatedInput` | 在执行前修改工具的输入参数。会替换整个输入对象,因此请将未更改的字段与修改后的字段一并包含在内。Claude Code 会针对您的 hook 返回的输入(而不是 Claude 发送的输入)评估权限规则以及 Bash 命令的[自动转入后台资格](/docs/zh-CN/tools-reference#foreground-commands-that-move-to-the-background)。与 `"allow"` 结合可自动批准,与 `"ask"` 结合可向用户显示修改后的输入。对于 `"defer"` 会被忽略 |
1948| `additionalContext` | 与工具结果一起添加到 Claude 上下文中的字符串。当 `permissionDecision` 为 `"defer"` 时被忽略。请参阅[为 Claude 添加上下文](#add-context-for-claude) |1947| `additionalContext` | 与工具结果一同添加到 Claude 上下文中的字符串。当 `permissionDecision` 为 `"defer"` 时会被忽略。请参阅[为 Claude 添加上下文](#add-context-for-claude) |
1949 1948
1950当多个 PreToolUse hook 返回不同的决策时,优先级为 `deny` > `defer` > `ask` > `allow`。1949当多个 PreToolUse hook 返回不同的决策时,优先级为 `deny` > `defer` > `ask` > `allow`。
1951 1950
1952通过以退出码 2 退出来阻止的 hook,其处理方式与 `"deny"` 相同:Claude 会将 stderr 消息视为拒绝原因。1951通过以 2 退出来阻止的 hook,其处理方式与 `"deny"` 相同:Claude 会将 stderr 消息视为拒绝原因。
1953 1952
1954当 hook 返回 `"ask"` 时,显示给用户的权限提示会包含一个标签,标明该 hook 的来源:来自任何设置文件或 Agent frontmatter 的 hook 标记为 `[settings]`,插件的 hook 标记为 `[plugin:<name>]`,来自 skill frontmatter 的 hook 标记为 `[skill]`。这有助于用户了解是哪个配置来源在请求确认。1953当 hook 返回 `"ask"` 时,向用户显示的权限提示中会包含一个标签,标明该 hook 的来源:来自任何设置文件或 Agent frontmatter 的 hook 为 `[settings]`,插件的 hook 为 `[plugin:<name>]`,来自 skill frontmatter 的 hook 为 `[skill]`。这有助于用户了解是哪个配置来源在请求确认。
1955 1954
1956hook 的 `"ask"` 在[自动模式](/docs/zh-CN/permission-modes#eliminate-prompts-with-auto-mode)中也会强制显示权限提示:分类器仍然可以拒绝该工具调用,但无法静默批准该调用。在 v2.1.211 之前,分类器可以批准在[沙箱](/docs/zh-CN/sandboxing)外运行的 Bash 命令,而不显示 hook 所请求的提示;分类器仍会对该命令应用其自身的安全规则,并且 hook 的 `"deny"` 始终会被遵守。1955hook 的 `"ask"` 在[自动模式](/docs/zh-CN/permission-modes#eliminate-prompts-with-auto-mode)中也会强制显示权限提示:分类器仍然可以拒绝该工具调用,但不能在不提示的情况下批准它。在 v2.1.211 之前,分类器可以在不显示 hook 所请求提示的情况下批准在[沙箱](/docs/zh-CN/sandboxing)外运行的 Bash 命令;分类器仍会对该命令应用其自身的安全规则,并且 hook 的 `"deny"` 始终会被遵守。
1957 1956
1958```json theme={null}1957```json theme={null}
1959{1958{
1970```1969```
1971 1970
1972<Note>1971<Note>
1973 PreToolUse 以前使用顶层的 `decision` 和 `reason` 字段,但这些字段对于此事件已弃用。请改用 `hookSpecificOutput.permissionDecision` 和 `hookSpecificOutput.permissionDecisionReason`。已弃用的值 `"approve"` 和 `"block"` 分别映射到 `"allow"` 和 `"deny"`。PostToolUse 和 Stop 等其他事件继续使用顶层 `decision` 和 `reason` 作为其当前格式。1972 PreToolUse 以前使用顶层的 `decision` 和 `reason` 字段,但对于此事件,这些字段已弃用。请改用 `hookSpecificOutput.permissionDecision` 和 `hookSpecificOutput.permissionDecisionReason`。已弃用的值 `"approve"` 和 `"block"` 分别映射到 `"allow"` 和 `"deny"`。PostToolUse 和 Stop 等其他事件仍将顶层的 `decision` 和 `reason` 作为其当前格式使用。
1974</Note>1973</Note>
1975 1974
1976<h4 id="allow-with-updatedinput">1975<h4 id="allow-with-updatedinput">
1977 需要用户交互的工具1976 需要用户交互的工具
1978</h4>1977</h4>
1979 1978
1980`AskUserQuestion` 和 `ExitPlanMode` 需要用户交互。在使用 `-p` 标志的[非交互模式](/docs/zh-CN/headless)下,只有当运行具有接收提示的[权限宿主](/docs/zh-CN/headless#turn-off-permission-prompts-in-unattended-runs)(例如 Agent SDK 的 `canUseTool` 回调)时,Claude Code 才会提供它们。1979`AskUserQuestion` 和 `ExitPlanMode` 需要用户交互。在使用 `-p` 标志的[非交互模式](/docs/zh-CN/headless)中,只有当运行具有可接收提示的[权限宿主](/docs/zh-CN/headless#turn-off-permission-prompts-in-unattended-runs)(例如 Agent SDK 的 `canUseTool` 回调)时,Claude Code 才会提供这些工具。
1981 1980
1982当 `PreToolUse` hook 执行以下操作时,即满足该要求:1981当 `PreToolUse` hook 执行以下操作时,即可满足该要求:
1983 1982
19841. 从 stdin 读取工具的输入19831. 从 stdin 读取工具的输入
19852. 通过您自己的 UI 收集答案19842. 通过您自己的 UI 收集答案
19863. 返回 `permissionDecision: "allow"` 以及包含答案的 `updatedInput`,使工具在不提示的情况下运行19853. 返回 `permissionDecision: "allow"`,并附带包含答案的 `updatedInput`,使工具无需提示即可运行
1987 1986
1988对于这些工具,仅返回 `"allow"` 是不够的。1987对于这些工具,仅返回 `"allow"` 是不够的。
1989 1988
1990对于 `AskUserQuestion`,请回传原始的 `questions` 数组,并添加一个 [`answers`](#askuserquestion) 对象,将每个问题的文本映射到所选答案。以下输出用 `React` 回答了一个问题:1989对于 `AskUserQuestion`,请原样回传原始的 `questions` 数组,并添加一个 [`answers`](#askuserquestion) 对象,将每个问题的文本映射到所选答案。以下输出用 `React` 回答了一个问题:
1991 1990
1992```json theme={null}1991```json theme={null}
1993{1992{
2009}2008}
2010```2009```
2011 2010
2012对于其服务器使用 [`_meta["anthropic/requiresUserInteraction"]`](/docs/zh-CN/mcp#require-approval-for-a-specific-tool) 标记的 MCP 工具,要求更为严格:hook 无法通过 `"allow"` 跳过其批准提示,无论是否带有 `updatedInput`,因为 Claude Code 无法确认 hook 是否收集了该工具所需的交互。2011其服务器使用 [`_meta["anthropic/requiresUserInteraction"]`](/docs/zh-CN/mcp#require-approval-for-a-specific-tool) 标记的 MCP 工具要求更严格:无论是否带有 `updatedInput`,hook 都无法通过 `"allow"` 跳过其批准提示,因为 Claude Code 无法确认 hook 已完成该工具所需的交互。
2013 2012
2014<h4 id="defer-a-tool-call-for-later">2013<h4 id="defer-a-tool-call-for-later">
2015 延迟工具调用以便稍后处理2014 推迟工具调用
2016</h4>2015</h4>
2017 2016
2018`"defer"` 适用于将 `claude -p` 作为子进程运行并读取其 JSON 输出的集成,例如 Agent SDK 应用或基于 Claude Code 构建的自定义 UI。它允许调用进程在工具调用处暂停 Claude,通过自己的界面收集输入,然后从中断处恢复。Claude Code 仅在使用 `-p` 标志的[非交互模式](/docs/zh-CN/headless)下遵守此值。在交互式会话中,它会记录一条警告并忽略该 hook 结果。2017`"defer"` 适用于将 `claude -p` 作为子进程运行并读取其 JSON 输出的集成,例如 Agent SDK 应用或基于 Claude Code 构建的自定义 UI。它让调用进程能够在某个工具调用处暂停 Claude,通过自己的界面收集输入,然后从中断处继续。Claude Code 仅在使用 `-p` 标志的[非交互模式](/docs/zh-CN/headless)中遵守此值。在交互式会话中,它会记录一条警告并忽略该 hook 结果。
2019 2018
2020`AskUserQuestion` 工具是典型场景:Claude 想向用户提问,但没有可以作答的终端。`-p` 运行只有在具有[权限宿主](/docs/zh-CN/headless#turn-off-permission-prompts-in-unattended-runs)(例如通过 `--permission-prompt-tool` 传入的 MCP 工具)时才会提供 `AskUserQuestion`,因此请使用权限宿主启动运行。完整的往返流程如下:2019`AskUserQuestion` 工具是典型场景:Claude 想向用户提问,但没有终端可供作答。`-p` 运行只有在具有[权限宿主](/docs/zh-CN/headless#turn-off-permission-prompts-in-unattended-runs)(例如您通过 `--permission-prompt-tool` 传入的 MCP 工具)时才会提供 `AskUserQuestion`,因此请带上一个权限宿主启动运行。整个往返流程如下:
2021 2020
20221. Claude 调用 `AskUserQuestion`。`PreToolUse` hook 触发。20211. Claude 调用 `AskUserQuestion`。`PreToolUse` hook 触发。
20232. hook 返回 `permissionDecision: "defer"`。工具不会执行。进程以 `stop_reason: "tool_deferred"` 退出,待处理的工具调用保留在会话记录中。20222. hook 返回 `permissionDecision: "defer"`。工具不执行。进程以 `stop_reason: "tool_deferred"` 退出,待处理的工具调用保留在会话记录中。
20243. 调用进程从 SDK 结果中读取 `deferred_tool_use`,在自己的 UI 中呈现问题,并等待答案。20233. 调用进程从 SDK 结果中读取 `deferred_tool_use`,在自己的 UI 中展示问题,并等待回答。
20254. 调用进程使用相同的权限宿主运行 `claude -p --resume <session-id>`。同一个工具调用会再次触发 `PreToolUse`。20244. 调用进程使用相同的权限宿主运行 `claude -p --resume <session-id>`。同一个工具调用再次触发 `PreToolUse`。
20265. hook 返回 `permissionDecision: "allow"`,并在 `updatedInput` 中提供答案。工具执行,Claude 继续。20255. hook 返回 `permissionDecision: "allow"`,并在 `updatedInput` 中附带答案。工具执行,Claude 继续。
2027 2026
2028`deferred_tool_use` 字段携带工具的 `id`、`name` 和 `input`。`input` 是 Claude 为该工具调用生成的参数,在执行之前捕获:2027`deferred_tool_use` 字段携带工具的 `id`、`name` 和 `input`。`input` 是 Claude 为该工具调用生成的参数,在执行前捕获:
2029 2028
2030```json theme={null}2029```json theme={null}
2031{2030{
2041}2040}
2042```2041```
2043 2042
2044没有超时或重试次数限制。会话会保留在磁盘上直到您恢复它,但受 [`cleanupPeriodDays`](/docs/zh-CN/settings-reference#cleanupperioddays) 保留期清理的约束,该清理默认在 30 天后删除会话文件,遵循[保留期清理规则](/docs/zh-CN/claude-directory#cleaned-up-automatically)。如果恢复时答案尚未就绪,hook 可以再次返回 `"defer"`,进程会以相同方式退出。调用进程通过最终从 hook 返回 `"allow"` 或 `"deny"` 来控制何时跳出该循环。2043没有超时或重试次数限制。会话会一直保留在磁盘上,直到您恢复它,但受 [`cleanupPeriodDays`](/docs/zh-CN/settings-reference#cleanupperioddays) 保留清理的约束,该清理默认会在 30 天后删除会话文件,并遵循[保留清理规则](/docs/zh-CN/claude-directory#cleaned-up-automatically)。如果恢复时答案尚未准备好,hook 可以再次返回 `"defer"`,进程会以相同方式退出。调用进程通过最终让 hook 返回 `"allow"` 或 `"deny"` 来控制何时结束循环。
2045 2044
2046`"defer"` 仅在 Claude 在该轮次中只进行单个工具调用时有效。如果 Claude 同时进行多个工具调用,`"defer"` 会被忽略并发出警告,工具将按正常权限流程继续。存在此限制是因为恢复时只能重新运行一个工具:无法在不让其他调用悬而未决的情况下延迟一批调用中的某一个。2045`"defer"` 仅在 Claude 在该轮次中只进行一次工具调用时有效。如果 Claude 同时进行多次工具调用,`"defer"` 会被忽略并发出警告,工具会经过正常的权限流程继续。存在此限制是因为恢复时只能重新运行一个工具:无法在推迟一批调用中的某一个的同时,不让其他调用处于未解决状态。
2047 2046
2048如果恢复时被延迟的工具已不可用,进程会在 hook 触发之前以 `stop_reason: "tool_deferred_unavailable"` 和 `is_error: true` 退出。当提供该工具的 MCP 服务器在恢复的会话中未连接时,就会发生这种情况。`deferred_tool_use` 数据仍会包含在内,以便您识别是哪个工具缺失了。2047如果恢复时被推迟的工具已不可用,进程会在 hook 触发之前以 `stop_reason: "tool_deferred_unavailable"` 和 `is_error: true` 退出。当提供该工具的 MCP 服务器在恢复的会话中未连接时,就会发生这种情况。`deferred_tool_use` 负载仍会包含在内,以便您确定缺失的是哪个工具。
2049 2048
2050<Note>2049<Note>
2051 要在计划模式下恢复被延迟的会话,请将 [`--permission-prompt-tool`](/docs/zh-CN/cli-reference#cli-flags) 与 `--resume` 一起传入,以便 Claude Code 可以呈现计划供批准。如果您传入某些其他启动标志,恢复的运行不会返回计划模式;请参阅[使用 `-p` 在计划模式下恢复](/docs/zh-CN/sessions#resume-in-plan-mode-with-p)。需要 Claude Code v2.1.246 或更高版本。2050 要在计划模式下恢复被推迟的会话,请将 [`--permission-prompt-tool`](/docs/zh-CN/cli-reference#cli-flags) 与 `--resume` 一起传入,以便 Claude Code 可以呈现计划供批准。如果您传入了某些其他启动标志,恢复的运行将不会回到计划模式;请参阅[使用 `-p` 在计划模式下恢复](/docs/zh-CN/sessions#resume-in-plan-mode-with-p)。需要 Claude Code v2.1.246 或更高版本。
2052 2051
2053 当您使用 `-p` 恢复时,Claude Code 不会还原任何其他已存储的权限模式。它会以新的 `claude -p` 运行所使用的权限模式启动,因此如果被延迟的会话使用了 `--permission-mode` 或 `--dangerously-skip-permissions`,请再次传入。当您不带 `-p` 使用 `claude --resume <session-id>` 恢复时,Claude Code 会还原已存储的权限模式,但[恢复时的权限模式](/docs/zh-CN/sessions#permission-mode-on-resume)中列出的例外情况除外。2052 当您使用 `-p` 恢复时,Claude Code 不会还原任何其他已存储的权限模式。它会以新的 `claude -p` 运行所使用的权限模式启动,因此如果被推迟的会话使用了 `--permission-mode` 或 `--dangerously-skip-permissions`,请再次传入。当您不带 `-p` 使用 `claude --resume <session-id>` 恢复时,Claude Code 会还原已存储的权限模式,例外情况列于[恢复时的权限模式](/docs/zh-CN/sessions#permission-mode-on-resume)中。
2054</Note>2053</Note>
2055 2054
2056<h3 id="permissionrequest">2055<h3 id="permissionrequest">
2057 PermissionRequest2056 PermissionRequest
2058</h3>2057</h3>
2059 2058
2060在 Claude Code 即将请求您授予使用某个工具的权限时运行。在无法显示提示的会话中,例如[非交互模式](/docs/zh-CN/headless)下的后台子代理,Claude Code 仍会运行这些 hook,如果没有 hook 返回决策,它会拒绝该工具调用。对于到达 `--permission-prompt-tool` 或 Agent SDK 的 [`canUseTool` 回调](/docs/zh-CN/agent-sdk/permissions)的调用,hook 会与您的宿主并行运行,以先做出决策者为准。2059在 Claude Code 即将向您请求使用某个工具的权限时运行。在无法显示提示的会话中,例如[非交互模式](/docs/zh-CN/headless)下的后台子代理,Claude Code 仍会运行这些 hook,如果没有 hook 返回决策,它会拒绝该工具调用。对于到达 `--permission-prompt-tool` 或 Agent SDK 的 [`canUseTool` 回调](/docs/zh-CN/agent-sdk/permissions)的调用,hook 会与您的宿主并行运行,以先做出决策者为准。
2061使用 [PermissionRequest 决策控制](#permissionrequest-decision-control)代表用户允许或拒绝。2060使用 [PermissionRequest 决策控制](#permissionrequest-decision-control)代表用户允许或拒绝。
2062 2061
2063当您需要在 Claude 请求使用工具的权限时立即获得信号,请使用此事件。Claude Code 仅在提示等待约六秒后才会运行 `permission_prompt` 类型的 [Notification](#notification) hook。2062当您需要在 Claude 请求使用工具权限的那一刻获得信号时,请使用此事件。Claude Code 只有在提示已等待约六秒后,才会运行 `permission_prompt` 类型的 [Notification](#notification) hook。
2064 2063
2065对于沙箱中命令的[网络请求](/docs/zh-CN/sandboxing#network-isolation),Claude Code 不会运行 PermissionRequest hook。要获得该提示的信号,请使用 `permission_prompt` 通知类型。2064对于沙箱中命令的[网络请求](/docs/zh-CN/sandboxing#network-isolation),Claude Code 不会运行 PermissionRequest hook。要获取该提示的信号,请使用 `permission_prompt` 通知类型。
2066 2065
2067针对工具名称进行匹配,取值与 PreToolUse 相同。2066按工具名称匹配,取值与 PreToolUse 相同。
2068 2067
2069<h4 id="permissionrequest-input">2068<h4 id="permissionrequest-input">
2070 PermissionRequest 输入2069 PermissionRequest 输入
2071</h4>2070</h4>
2072 2071
2073PermissionRequest hook 会像 PreToolUse hook 一样接收 `tool_name` 和 `tool_input` 字段,但没有 `tool_use_id`。对于 MCP 工具,它们还会接收 [`mcp_server`](#pretooluse-input) 对象。可选的 `permission_suggestions` 数组包含 Claude Code 针对此请求建议的[权限更新](#permission-update-entries),例如添加允许规则或更改权限模式。2072PermissionRequest hook 与 PreToolUse hook 一样接收 `tool_name` 和 `tool_input` 字段,但没有 `tool_use_id`。对于 MCP 工具,它们还会接收 [`mcp_server`](#pretooluse-input) 对象。可选的 `permission_suggestions` 数组包含 Claude Code 针对此请求建议的[权限更新](#permission-update-entries),例如添加允许规则或更改权限模式。
2074 2073
2075`permission_suggestions` 数组并不是您所看到选项的精确列表,因为每个权限对话框都会构建自己的选项。有些对话框(例如用于文件编辑的对话框)根本不读取该数组,而是从请求本身派生其选项。读取该数组的对话框仍可能隐藏某个建议仍保留在数组中的选项,例如当 [`allowManagedPermissionRulesOnly`](/docs/zh-CN/settings-reference#allowmanagedpermissionrulesonly) 隐藏保存规则的选项时。它也可能提供没有对应建议条目的选项,例如 [**Yes, and switch to auto mode**](/docs/zh-CN/permission-modes#switch-permission-modes),它直接更改权限模式,而不是通过权限更新。2074`permission_suggestions` 数组并不是您所看到选项的精确列表,因为每个权限对话框都会构建自己的选项。某些对话框(例如文件编辑的对话框)根本不读取该数组,而是根据请求本身得出选项。读取该数组的对话框仍可能隐藏某个建议仍保留在数组中的选项,例如当 [`allowManagedPermissionRulesOnly`](/docs/zh-CN/settings-reference#allowmanagedpermissionrulesonly) 隐藏保存规则的选项时。它也可能提供没有对应建议条目的选项,例如 [**Yes, and switch to auto mode**](/docs/zh-CN/permission-modes#switch-permission-modes),该选项直接更改权限模式,而不是通过权限更新。
2076 2075
2077PreToolUse hook 在每次工具调用之前运行,无论是否需要权限。PermissionRequest hook 仅在 Claude Code 即将向您请求权限时运行,或在它原本会自动拒绝无法提示的调用时运行。这两个事件都不会为 [`EndConversation`](/docs/zh-CN/tools-reference#endconversation-tool-behavior) 触发。2076PreToolUse hook 在每次工具调用之前运行,无论该调用是否需要权限。PermissionRequest hook 仅在 Claude Code 即将向您请求权限时,或者在它原本会自动拒绝一个无法提示的调用时运行。这两个事件都不会为 [`EndConversation`](/docs/zh-CN/tools-reference#endconversation-tool-behavior) 触发。
2078 2077
2079```json theme={null}2078```json theme={null}
2080{2079{
2103 PermissionRequest 决策控制2102 PermissionRequest 决策控制
2104</h4>2103</h4>
2105 2104
2106`PermissionRequest` hook 可以允许或拒绝权限请求。除了所有 hook 都可用的 [JSON 输出字段](#json-output)之外,您的 hook 脚本还可以返回一个包含以下特定于事件字段的 `decision` 对象:2105`PermissionRequest` hook 可以允许或拒绝权限请求。除所有 hook 都可用的 [JSON 输出字段](#json-output)外,您的 hook 脚本还可以返回一个包含以下事件专属字段的 `decision` 对象:
2107 2106
2108| 字段 | 描述 |2107| 字段 | 描述 |
2109| :- | :- |2108| :- | :- |
2110| `behavior` | `"allow"` 授予权限,`"deny"` 拒绝权限。[拒绝和询问规则](/docs/zh-CN/permissions#manage-permissions)仍会被评估,因此返回 `"allow"` 的 hook 不会覆盖匹配的拒绝规则 |2109| `behavior` | `"allow"` 授予权限,`"deny"` 拒绝权限。[拒绝和询问规则](/docs/zh-CN/permissions#manage-permissions)仍会被评估,因此返回 `"allow"` 的 hook 不会覆盖匹配的拒绝规则 |
2111| `updatedInput` | 仅适用于 `"allow"`:在执行前修改工具的输入参数。会替换整个输入对象,因此请将未更改的字段与修改后的字段一并包含。修改后的输入会根据拒绝和询问规则重新评估 |2110| `updatedInput` | 仅用于 `"allow"`:在执行前修改工具的输入参数。会替换整个输入对象,因此请将未更改的字段与修改后的字段一并包含在内。修改后的输入会针对拒绝和询问规则重新评估 |
2112| `updatedPermissions` | 仅适用于 `"allow"`:要应用的[权限更新条目](#permission-update-entries)数组,例如添加允许规则或更改会话权限模式 |2111| `updatedPermissions` | 仅用于 `"allow"`:要应用的[权限更新条目](#permission-update-entries)数组,例如添加允许规则或更改会话的权限模式 |
2113| `message` | 仅适用于 `"deny"`:告诉 Claude 权限被拒绝的原因 |2112| `message` | 仅用于 `"deny"`:告诉 Claude 权限被拒绝的原因 |
2114| `interrupt` | 仅适用于 `"deny"`:如果为 `true`,则停止 Claude |2113| `interrupt` | 仅用于 `"deny"`:如果为 `true`,则停止 Claude |
2115 2114
2116以退出码 2 退出但没有 `decision` 对象的 hook 不会改变权限流程,其 stderr 会被丢弃。只有 `decision` 对象才能授予或拒绝请求。2115以 2 退出但不带 `decision` 对象的 hook 不会改变权限流程,其 stderr 会被丢弃。只有 `decision` 对象才能授予或拒绝请求。
2117 2116
2118```json theme={null}2117```json theme={null}
2119{2118{
2133 权限更新条目2132 权限更新条目
2134</h4>2133</h4>
2135 2134
2136`updatedPermissions` 输出字段和 [`permission_suggestions` 输入字段](#permissionrequest-input)都使用相同的条目对象数组。每个条目都有一个决定其其他字段的 `type`,以及一个控制更改写入位置的 `destination`。2135`updatedPermissions` 输出字段和 [`permission_suggestions` 输入字段](#permissionrequest-input)都使用相同的条目对象数组。每个条目都有一个 `type`,它决定该条目的其他字段;还有一个 `destination`,它控制更改写入的位置。
2137 2136
2138| `type` | 字段 | 效果 |2137| `type` | 字段 | 效果 |
2139| :- | :- | :- |2138| :- | :- | :- |
2140| `addRules` | `rules`、`behavior`、`destination` | 添加权限规则。`rules` 是 `{toolName, ruleContent?}` 对象的数组。省略 `ruleContent` 可匹配整个工具。`behavior` 为 `"allow"`、`"deny"` 或 `"ask"` |2139| `addRules` | `rules`、`behavior`、`destination` | 添加权限规则。`rules` 是由 `{toolName, ruleContent?}` 对象组成的数组。省略 `ruleContent` 即匹配整个工具。`behavior` 为 `"allow"`、`"deny"` 或 `"ask"` |
2141| `replaceRules` | `rules`、`behavior`、`destination` | 用提供的 `rules` 替换 `destination` 处给定 `behavior` 的所有规则 |2140| `replaceRules` | `rules`、`behavior`、`destination` | 用提供的 `rules` 替换 `destination` 中给定 `behavior` 的所有规则 |
2142| `removeRules` | `rules`、`behavior`、`destination` | 删除给定 `behavior` 的匹配规则 |2141| `removeRules` | `rules`、`behavior`、`destination` | 移除给定 `behavior` 的匹配规则 |
2143| `setMode` | `mode`、`destination` | 更改权限模式。有效模式为 `default`、`auto`、`acceptEdits`、`dontAsk`、`bypassPermissions`、`plan`,以及作为 `default` 别名的 `manual` |2142| `setMode` | `mode`、`destination` | 更改权限模式。有效模式为 `default`、`auto`、`acceptEdits`、`dontAsk`、`bypassPermissions`、`plan`,以及作为 `default` 别名的 `manual` |
2144| `addDirectories` | `directories`、`destination` | 添加工作目录。`directories` 是路径字符串数组 |2143| `addDirectories` | `directories`、`destination` | 添加工作目录。`directories` 是路径字符串数组 |
2145| `removeDirectories` | `directories`、`destination` | 移除工作目录 |2144| `removeDirectories` | `directories`、`destination` | 移除工作目录 |
2146 2145
2147<Note>2146<Note>
2148 只有当您启动会话时已经可以使用绕过模式,`setMode` 配合 `bypassPermissions` 才会生效:即使用了 `--dangerously-skip-permissions`、`--permission-mode bypassPermissions`、`--allow-dangerously-skip-permissions`,或在[用户设置、`--settings` 或托管设置](/docs/zh-CN/settings-reference#permissions-defaultmode)中设置了 `permissions.defaultMode: "bypassPermissions"`。否则该更新不产生任何效果。当 [`permissions.disableBypassPermissionsMode`](/docs/zh-CN/permissions#managed-settings) 禁用了该模式,或会话以[受限模式](/docs/zh-CN/cli-reference#cli-flags)启动时,该更新同样不产生任何效果。2147 仅当您启动会话时 bypass 模式已可用,`setMode` 设为 `bypassPermissions` 才会生效:即使用了 `--dangerously-skip-permissions`、`--permission-mode bypassPermissions`、`--allow-dangerously-skip-permissions`,或在[用户设置、`--settings` 或托管设置](/docs/zh-CN/settings-reference#permissions-defaultmode)中设置了 `permissions.defaultMode: "bypassPermissions"`。否则该更新不起任何作用。当 [`permissions.disableBypassPermissionsMode`](/docs/zh-CN/permissions#managed-settings) 禁用了该模式,或会话以[受限模式](/docs/zh-CN/cli-reference#cli-flags)启动时,该更新同样不起作用。
2149 2148
2150 无论 `destination` 为何值,`bypassPermissions` 都不会被持久化为 `defaultMode`。2149 无论 `destination` 为何值,`bypassPermissions` 都不会被持久化为 `defaultMode`。
2151</Note>2150</Note>
2152 2151
2153每个条目上的 `destination` 字段决定该更改是仅保留在内存中,还是持久化到设置文件。2152每个条目上的 `destination` 字段决定更改是仅保留在内存中,还是持久化到设置文件。
2154 2153
2155| `destination` | 写入位置 |2154| `destination` | 写入位置 |
2156| :- | :- |2155| :- | :- |
2169 2168
2170按工具名称匹配,取值与 PreToolUse 相同。2169按工具名称匹配,取值与 PreToolUse 相同。
2171 2170
2172当工具名称不是合适的过滤条件时,可以进行更宽泛的匹配:2171当工具名称不是合适的过滤条件时,可以更宽泛地匹配:
2173 2172
2174* 要在任意工具成功完成后运行 hook,请省略 `matcher` 或将其设为 `"*"`。然后您的 hook 可以自行发现发生了哪些更改,例如运行 `git status --porcelain`,它还会列出 `git diff` 遗漏的未跟踪文件。对于失败的工具调用,请在 [PostToolUseFailure](#posttoolusefailure) 下添加相同的 hook。2173* 要在任何工具成功完成后运行 hook,请省略 `matcher` 或将其设为 `"*"`。然后您的 hook 可以自行发现发生了哪些更改,例如运行 `git status --porcelain`,它还会列出 `git diff` 遗漏的未跟踪文件。对于失败的工具调用,请在 [PostToolUseFailure](#posttoolusefailure) 下添加相同的 hook。
2175* 要在特定文件在磁盘上发生更改时运行 hook(无论是由什么写入的),请使用 [FileChanged](#filechanged)。当 `Bash` 命令或 Claude Code 之外的进程重写同一文件时,Claude Code 不会运行匹配 `Edit|Write` 的 `PostToolUse` hook。2174* 要在特定文件在磁盘上发生更改时运行 hook(无论由谁写入),请使用 [FileChanged](#filechanged)。当 `Bash` 命令或 Claude Code 之外的进程重写同一文件时,Claude Code 不会运行匹配 `Edit|Write` 的 `PostToolUse` hook。
2176 2175
2177<h4 id="posttooluse-input">2176<h4 id="posttooluse-input">
2178 PostToolUse 输入2177 PostToolUse 输入
2179</h4>2178</h4>
2180 2179
2181`PostToolUse` hook 在工具已成功执行后触发。输入同时包含 `tool_input`(发送给工具的参数)和 `tool_response`(工具返回的结果)。两者的确切 schema 取决于具体工具。文件工具的 `tool_input` 路径格式与 [PreToolUse](#pretooluse-input) 相同:始终为绝对路径,使用平台原生分隔符,因此在 Windows 上为反斜杠。对于 MCP 工具,输入还会携带 [`mcp_server`](#pretooluse-input) 对象。2180`PostToolUse` hook 在工具已成功执行后触发。输入同时包含 `tool_input`(发送给工具的参数)和 `tool_response`(工具返回的结果)。两者的确切 schema 取决于具体工具。文件类工具的 `tool_input` 路径格式与 [PreToolUse](#pretooluse-input) 相同:始终为绝对路径,使用平台原生分隔符,因此在 Windows 上为反斜杠。对于 MCP 工具,输入还包含 [`mcp_server`](#pretooluse-input) 对象。
2182 2181
2183```json theme={null}2182```json theme={null}
2184{2183{
2203 2202
2204| 字段 | 描述 |2203| 字段 | 描述 |
2205| :- | :- |2204| :- | :- |
2206| `duration_ms` | 可选。工具执行时间,以毫秒为单位。不包括在权限提示和 PreToolUse hook 中花费的时间 |2205| `duration_ms` | 可选。工具执行时间(毫秒)。不包括在权限提示和 PreToolUse hook 中花费的时间 |
2207 2206
2208<h4 id="posttooluse-decision-control">2207<h4 id="posttooluse-decision-control">
2209 PostToolUse 决策控制2208 PostToolUse 决策控制
2210</h4>2209</h4>
2211 2210
2212`PostToolUse` hook 可以在工具执行后向 Claude 提供反馈。除了所有 hook 都可用的 [JSON 输出字段](#json-output)之外,您的 hook 脚本还可以返回以下特定于事件的字段:2211`PostToolUse` hook 可以在工具执行后向 Claude 提供反馈。除了所有 hook 均可使用的 [JSON 输出字段](#json-output)外,您的 hook 脚本还可以返回以下事件特定字段:
2213 2212
2214| 字段 | 描述 |2213| 字段 | 描述 |
2215| :- | :- |2214| :- | :- |
2216| `decision` | `"block"` 会在工具结果旁边添加 `reason`。Claude 仍会看到原始输出;要替换它,请使用 `updatedToolOutput` |2215| `decision` | `"block"` 会在工具结果旁添加 `reason`。Claude 仍会看到原始输出;要替换它,请使用 `updatedToolOutput` |
2217| `reason` | 当 `decision` 为 `"block"` 时向 Claude 显示的说明 |2216| `reason` | 当 `decision` 为 `"block"` 时向 Claude 显示的说明 |
2218| `additionalContext` | 与工具结果一起添加到 Claude 上下文中的字符串。请参阅[为 Claude 添加上下文](#add-context-for-claude) |2217| `additionalContext` | 与工具结果一起添加到 Claude 上下文中的字符串。请参阅[为 Claude 添加上下文](#add-context-for-claude) |
2219| `classifierContext` | 关于本次调用结果的简短说明,供[自动模式](/docs/zh-CN/permission-modes#eliminate-prompts-with-auto-mode)分类器而非 Claude 使用。请参阅[为自动模式分类器注释结果](#annotate-a-result-for-the-auto-mode-classifier)。需要 Claude Code v2.1.236 或更高版本 |2218| `classifierContext` | 关于此次调用结果的简短说明,提供给[自动模式](/docs/zh-CN/permission-modes#eliminate-prompts-with-auto-mode)分类器而非 Claude。请参阅[为自动模式分类器注释结果](#annotate-a-result-for-the-auto-mode-classifier)。需要 Claude Code v2.1.236 或更高版本 |
2220| `updatedToolOutput` | 在工具输出发送给 Claude 之前,用提供的值替换它。该值必须与工具的输出结构相匹配 |2219| `updatedToolOutput` | 在工具输出发送给 Claude 之前,用提供的值替换它。该值必须与工具的输出结构相匹配 |
2221| `updatedMCPToolOutput` | 仅替换 [MCP 工具](#match-mcp-tools)的输出。建议优先使用适用于所有工具的 `updatedToolOutput` |2220| `updatedMCPToolOutput` | 仅替换 [MCP 工具](#match-mcp-tools)的输出。建议优先使用适用于所有工具的 `updatedToolOutput` |
2222 2221
2238```2237```
2239 2238
2240<Warning>2239<Warning>
2241 `updatedToolOutput` 只会改变 Claude 看到的内容。hook 触发时工具已经运行,因此任何已写入的文件、已执行的命令或已发送的网络请求都已生效。OpenTelemetry 工具 span 和分析事件等遥测数据也会在 hook 运行之前捕获原始输出。要在工具调用运行之前阻止或修改它,请改用 [PreToolUse](#pretooluse) hook。2240 `updatedToolOutput` 只会改变 Claude 看到的内容。hook 触发时工具已经运行完毕,因此任何已写入的文件、已执行的命令或已发送的网络请求都已生效。诸如 OpenTelemetry 工具 span 和分析事件之类的遥测数据也会在 hook 运行之前捕获原始输出。要在工具调用运行之前阻止或修改它,请改用 [PreToolUse](#pretooluse) hook。
2242 2241
2243 替换值必须与工具的输出结构相匹配。内置工具返回的是结构化对象,而不是纯字符串。例如,`Bash` 返回一个包含 `stdout`、`stderr`、`interrupted` 和 `isImage` 字段的对象。对于内置工具,与工具输出 schema 不匹配的值会被忽略,并使用原始输出。MCP 工具的输出会直接传递,不进行 schema 验证。删除 Claude 需要的错误详细信息可能会导致它基于错误的假设继续执行。2242 替换值必须与工具的输出结构相匹配。内置工具返回的是结构化对象,而非纯字符串。例如,`Bash` 返回一个包含 `stdout`、`stderr`、`interrupted` 和 `isImage` 字段的对象。对于内置工具,与工具输出 schema 不匹配的值会被忽略,并使用原始输出。MCP 工具的输出会直接传递,不进行 schema 验证。删除 Claude 所需的错误详情可能会导致它基于错误的假设继续操作。
2244</Warning>2243</Warning>
2245 2244
2246<h4 id="annotate-a-result-for-the-auto-mode-classifier">2245<h4 id="annotate-a-result-for-the-auto-mode-classifier">
2247 为自动模式分类器注释结果2246 为自动模式分类器注释结果
2248</h4>2247</h4>
2249 2248
2250返回 `classifierContext`,可以将关于工具调用结果的简短说明发送给[自动模式](/docs/zh-CN/permission-modes#eliminate-prompts-with-auto-mode)分类器,而不是发送给 Claude。分类器[从不接收工具结果本身](/docs/zh-CN/permission-modes#how-the-classifier-evaluates-actions),因此该字段是在分类器审查后续操作之前,告知它某次调用返回内容的受支持方式。该字段需要 Claude Code v2.1.236 或更高版本。2249返回 `classifierContext`,即可将关于工具调用结果的简短说明发送给[自动模式](/docs/zh-CN/permission-modes#eliminate-prompts-with-auto-mode)分类器,而不是发送给 Claude。分类器[从不接收工具结果本身](/docs/zh-CN/permission-modes#how-the-classifier-evaluates-actions),因此在它审查后续操作之前,此字段是告知它某次调用返回内容的受支持方式。该字段需要 Claude Code v2.1.236 或更高版本。
2251 2250
2252以下示例告诉分类器某次查询的输出来自何处:2251以下示例告知分类器某个查询输出的来源:
2253 2252
2254```json theme={null}2253```json theme={null}
2255{2254{
2260}2259}
2261```2260```
2262 2261
2263分类器对该说明的重视程度取决于您在何处配置了该 hook:2262分类器对该说明的重视程度取决于您配置 hook 的位置:
2264 2263
2265* **在 Claude Code 中配置的 hook**:对于来自设置文件、插件、skill 和 Agent frontmatter 的 hook,分类器将该说明视为未经验证的、由应用程序提供的上下文。该说明永远不能确立用户意图;如果它声称您批准或请求了某事,分类器会将该声明与您在对话中发送的消息进行核对2264* **在 Claude Code 中配置的 hook**:对于来自设置文件、插件、skill 和 Agent frontmatter 的 hook,分类器将该说明视为未经验证的、由应用程序提供的上下文。该说明永远不能确立用户意图;如果它声称您批准或请求了某事,分类器会将该声明与您在对话中发送的消息进行核对
2266* **进程内 Agent SDK 回调**:当嵌入 Claude Code 的应用程序将该 hook 注册为 [TypeScript SDK 回调](/docs/zh-CN/agent-sdk/hooks)并在实时会话期间返回该说明时,分类器可能会将说明中转述的用户陈述视为用户意图。此类陈述可以满足分类器原本会从您发送的消息中接受的同意要求,但它永远不能解除您自己的消息也无法解除的阻止。会话恢复后,Claude Code 会将恢复的说明视为未经验证的上下文。当两类 hook 都对同一调用添加注释时,分类器会将合并后的说明视为未经验证的内容2265* **进程内 Agent SDK 回调**:当嵌入 Claude Code 的应用程序将 hook 注册为 [TypeScript SDK 回调](/docs/zh-CN/agent-sdk/hooks),并在实时会话期间返回该说明时,分类器可能会将说明中转述的用户陈述视为用户意图。这样的陈述可以满足分类器本会从您发送的消息中接受的同意要求,但它永远不能解除您自己的消息也无法解除的阻止。会话恢复后,Claude Code 会将恢复的说明视为未经验证的上下文。当两组中的 hook 都对同一调用进行注释时,分类器会将合并后的说明视为未经验证的内容
2267 2266
2268Claude Code 在传递说明时会应用以下限制:2267Claude Code 在传递该说明时会应用以下限制:
2269 2268
2270* **长度**:Claude Code 将单次工具调用的说明上限设为 2,000 个字符,并截断其余部分。该上限由响应该调用的所有 hook 共享2269* **长度**:Claude Code 将单次工具调用的说明上限设为 2,000 个字符,并截断其余部分。该上限由响应该调用的所有 hook 共享
2271* **仅限同步响应**:对于[在后台运行](#run-hooks-in-the-background)的 hook,Claude Code 会忽略其响应中的该字段,因为该响应在 Claude Code 记录工具结果之后才到达2270* **仅限同步响应**:对于[在后台运行](#run-hooks-in-the-background)的 hook,Claude Code 会忽略其响应中的该字段,因为该响应在 Claude Code 记录工具结果之后才到达
2272* **分类器不记录的调用**:分类器的会话记录会省略只读查找,例如文件读取和搜索。附加到这些调用上的说明会被 Claude Code 丢弃2271* **分类器不记录的调用**:分类器的会话记录会省略只读查找,例如文件读取和搜索。Claude Code 会丢弃附加在这类调用上的说明
2273* **与重写的交互**:当说明描述的是您正在用 `updatedToolOutput` 替换的输出时,请在同一个 hook 响应中同时返回这两个字段。如果该重写被拒绝或被另一个 hook 的重写替换,Claude Code 会丢弃该说明。即使另一个 hook 重写了输出,Claude Code 仍会传递您在没有重写的情况下返回的说明2272* **与重写的交互**:当说明描述的是您要用 `updatedToolOutput` 替换的输出时,请在同一个 hook 响应中返回这两个字段。如果该重写被拒绝,或被另一个 hook 的重写替换,Claude Code 会丢弃该说明。即使另一个 hook 重写了输出,Claude Code 仍会传递您在没有重写的情况下返回的说明
2274 2273
2275<Warning>2274<Warning>
2276 分类器会将您放入 `classifierContext` 的内容视为来自托管该会话的应用程序的信息,因此请勿将不受信任的工具输出或第三方文本复制到其中。请将说明限定为关于这一次调用的简短断言,例如关于其来源的事实或用户对它的陈述;不要使用该字段传递无关消息或事件流。2275 分类器会将您放入 `classifierContext` 的内容视为来自托管该会话的应用程序的信息,因此不要将不受信任的工具输出或第三方文本复制到其中。请将说明保持为关于这一次调用的简短断言,例如关于其来源的事实或用户对其的陈述;不要使用该字段传递无关消息或事件流。
2277</Warning>2276</Warning>
2278 2277
2279<h3 id="posttoolusefailure">2278<h3 id="posttoolusefailure">
2280 PostToolUseFailure2279 PostToolUseFailure
2281</h3>2280</h3>
2282 2281
2283当已开始执行的工具失败时运行:工具抛出了错误,或 MCP 工具返回了错误结果。可用于记录失败、发送警报或向 Claude 提供纠正性反馈。2282当已开始执行的工具失败时运行:工具抛出错误,或 MCP 工具返回错误结果。可用于记录失败、发送警报或向 Claude 提供纠正性反馈。
2284 2283
2285按工具名称匹配,取值与 PreToolUse 相同。2284按工具名称匹配,取值与 PreToolUse 相同。
2286 2285
2287<Note>2286<Note>
2288 对于在执行前被拒绝的工具调用,此事件不会触发:包括未知的工具名称、未通过 schema 或工具特定验证的输入,以及权限拒绝。验证拒绝会以 `tool_use_error` 结果返回,并且发生在 hook 运行之前,因此既不会触发 `PreToolUse`,也不会触发 `PostToolUseFailure`。权限拒绝会触发 `PreToolUse`,但不会触发此事件;请参阅 [PermissionDenied](#permissiondenied)。2287 对于在执行前被拒绝的工具调用,此事件不会触发:未知的工具名称、未通过 schema 或工具特定验证的输入,或权限拒绝。验证拒绝会作为 `tool_use_error` 结果返回,且发生在 hook 运行之前,因此既不会触发 `PreToolUse`,也不会触发 `PostToolUseFailure`。权限拒绝会触发 `PreToolUse`,但不会触发此事件;请参阅 [PermissionDenied](#permissiondenied)。
2289</Note>2288</Note>
2290 2289
2291<h4 id="posttoolusefailure-input">2290<h4 id="posttoolusefailure-input">
2292 PostToolUseFailure 输入2291 PostToolUseFailure 输入
2293</h4>2292</h4>
2294 2293
2295PostToolUseFailure hook 接收与 PostToolUse 相同的 `tool_name` 和 `tool_input` 字段,以及作为顶层字段的错误信息。对于 MCP 工具,它们还会接收 [`mcp_server`](#pretooluse-input) 对象。例如,一次失败的 `npm test` 命令可能会传递:2294PostToolUseFailure hook 接收与 PostToolUse 相同的 `tool_name` 和 `tool_input` 字段,以及作为顶层字段的错误信息。对于 MCP 工具,它们还会接收 [`mcp_server`](#pretooluse-input) 对象。例如,一个失败的 `npm test` 命令可能会传递:
2296 2295
2297```json theme={null}2296```json theme={null}
2298{2297{
2315 2314
2316| 字段 | 描述 |2315| 字段 | 描述 |
2317| :- | :- |2316| :- | :- |
2318| `error` | 描述出错内容的字符串。其格式取决于失败的工具 |2317| `error` | 描述出错原因的字符串。格式取决于失败的工具 |
2319| `is_interrupt` | 可选布尔值。当失败以中止而非工具报告的错误形式到达 Claude Code 时为 true。取消正在运行的工具不会触发此 hook;此时工具结果会携带中断消息 |2318| `is_interrupt` | 可选布尔值。当失败是以中止形式而非工具报告的错误到达 Claude Code 时为 true。取消正在运行的工具不会触发此 hook;工具结果中会改为携带中断消息 |
2320| `duration_ms` | 可选。工具执行时间,以毫秒为单位。不包括在权限提示和 PreToolUse hook 中花费的时间 |2319| `duration_ms` | 可选。工具执行时间(毫秒)。不包括在权限提示和 PreToolUse hook 中花费的时间 |
2321 2320
2322`error` 字符串通常与 Claude 作为失败工具结果收到的文本相同。其格式因工具和失败类型而异。请让您的 hook 基于 `tool_name`、`is_interrupt` 以及第一行的 `Exit code N` 进行判断;将字符串的其余部分视为显示文本,而非稳定的格式。2321`error` 字符串通常与 Claude 作为失败工具结果收到的文本相同。其格式因工具和失败情况而异。请让您的 hook 依据 `tool_name`、`is_interrupt` 以及 `Exit code N` 首行进行判断;将字符串的其余部分视为显示文本,而非稳定格式。
2323 2322
2324* 对于 Bash 和 PowerShell,已运行并退出的命令会生成第一行 `Exit code N`,随后是命令产生的所有输出,作为一个整体块,其中 stdout 和 stderr 交错排列2323* 对于 Bash 和 PowerShell,已运行并退出的命令会产生首行 `Exit code N`,随后是命令产生的所有输出,stdout 和 stderr 交错合并为一个块
2325* 当 Claude Code 无法启动 shell 进程本身时,负载也可能只携带一条没有退出码行的失败消息2324* 当 Claude Code 无法启动 shell 进程本身时,数据中也可能只携带一条不含退出码行的失败消息
2326* Claude Code 会对长字符串进行中间截断,并插入 `... [N characters truncated] ...` 标记,还可能插入自己的行,例如 `Command timed out after 2m 0s`2325* Claude Code 会在 `... [N characters truncated] ...` 标记周围对长字符串进行中间截断,并可能插入其自身的行,例如 `Command timed out after 2m 0s`
2327 2326
2328<h4 id="posttoolusefailure-decision-control">2327<h4 id="posttoolusefailure-decision-control">
2329 PostToolUseFailure 决策控制2328 PostToolUseFailure 决策控制
2330</h4>2329</h4>
2331 2330
2332`PostToolUseFailure` hook 可以在工具失败后向 Claude 提供上下文。除了所有 hook 都可用的 [JSON 输出字段](#json-output)之外,您的 hook 脚本还可以返回以下特定于事件的字段:2331`PostToolUseFailure` hook 可以在工具失败后向 Claude 提供上下文。除了所有 hook 均可使用的 [JSON 输出字段](#json-output)外,您的 hook 脚本还可以返回以下事件特定字段:
2333 2332
2334| 字段 | 描述 |2333| 字段 | 描述 |
2335| :- | :- |2334| :- | :- |
2348 PostToolBatch2347 PostToolBatch
2349</h3>2348</h3>
2350 2349
2351在一批中的每个工具调用都已完成后、Claude Code 向模型发送下一个请求之前运行一次。`PostToolUse` 对每个工具触发一次,这意味着当 Claude 进行并行工具调用时它会并发触发。`PostToolBatch` 针对整个批次只触发一次,因此适合注入依赖于已运行工具集合而非单个工具的上下文。此事件没有匹配器。2350在一批工具调用全部完成后运行一次,时机在 Claude Code 向模型发送下一个请求之前。`PostToolUse` 对每个工具触发一次,这意味着当 Claude 进行并行工具调用时它会并发触发。`PostToolBatch` 针对整批调用恰好触发一次,因此适合注入依赖于所运行工具集合(而非任何单个工具)的上下文。此事件没有匹配器。
2352 2351
2353<h4 id="posttoolbatch-input">2352<h4 id="posttoolbatch-input">
2354 PostToolBatch 输入2353 PostToolBatch 输入
2355</h4>2354</h4>
2356 2355
2357除了[通用输入字段](#common-input-fields)之外,PostToolBatch hook 还会接收 `tool_calls`,这是一个描述批次中每个工具调用的数组:2356除了[通用输入字段](#common-input-fields)外,PostToolBatch hook 还会接收 `tool_calls`,这是一个描述该批次中每个工具调用的数组:
2358 2357
2359```json theme={null}2358```json theme={null}
2360{2359{
2380}2379}
2381```2380```
2382 2381
2383`tool_response` 包含的内容与模型在对应 `tool_result` 块中收到的内容相同。该值是序列化字符串或内容块数组,与工具发出的完全一致。对于 `Read`,这意味着是带行号前缀的文本,而不是原始文件内容。响应可能很大,因此请只解析您需要的字段。2382`tool_response` 包含模型在对应 `tool_result` 块中收到的相同内容。该值是序列化字符串或内容块数组,与工具发出的完全一致。对于 `Read`,这意味着它是带行号前缀的文本,而不是原始文件内容。响应可能很大,因此只解析您需要的字段。
2384 2383
2385<Note>2384<Note>
2386 `tool_response` 的结构与 `PostToolUse` 的不同。`PostToolUse` 传递的是工具的结构化 `Output` 对象,例如 `Write` 的 `{filePath: "...", type: "create"}`;`PostToolBatch` 传递的是模型看到的序列化 `tool_result` 内容。2385 `tool_response` 的结构与 `PostToolUse` 的不同。`PostToolUse` 传递的是工具的结构化 `Output` 对象,例如 `Write` 的 `{filePath: "...", type: "create"}`;`PostToolBatch` 传递的是模型看到的序列化 `tool_result` 内容。
2390 PostToolBatch 决策控制2389 PostToolBatch 决策控制
2391</h4>2390</h4>
2392 2391
2393`PostToolBatch` hook 可以为 Claude 注入上下文。除了所有 hook 都可用的 [JSON 输出字段](#json-output)之外,您的 hook 脚本还可以返回以下特定于事件的字段:2392`PostToolBatch` hook 可以为 Claude 注入上下文。除了所有 hook 均可使用的 [JSON 输出字段](#json-output)外,您的 hook 脚本还可以返回以下事件特定字段:
2394 2393
2395| 字段 | 描述 |2394| 字段 | 描述 |
2396| :- | :- |2395| :- | :- |
2397| `additionalContext` | 在下一次模型调用之前注入一次的上下文字符串。有关传递细节、应放入的内容以及恢复的会话如何处理以往的值,请参阅[为 Claude 添加上下文](#add-context-for-claude) |2396| `additionalContext` | 在下一次模型调用之前注入一次的上下文字符串。有关传递细节、应放入的内容以及恢复的会话如何处理过去的值,请参阅[为 Claude 添加上下文](#add-context-for-claude) |
2398 2397
2399```json theme={null}2398```json theme={null}
2400{2399{
2405}2404}
2406```2405```
2407 2406
2408返回 `decision: "block"` 或 `continue: false` 会在下一次模型调用之前停止智能体循环。阻止消息来自 JSON 中的 `reason` 或 `stopReason`,或退出码 2 时的 stderr。您会在会话记录中看到它显示为警告,并且它会保留在对话中,因此当对话继续时 Claude 会看到它。2407返回 `decision: "block"` 或 `continue: false` 会在下一次模型调用之前停止智能体循环。阻止消息来自 JSON 中的 `reason` 或 `stopReason`,或在退出码为 2 时来自 stderr。您会在会话记录中看到一条警告,并且它会保留在对话中,因此对话继续时 Claude 也能看到它。
2409 2408
2410<h3 id="permissiondenied">2409<h3 id="permissiondenied">
2411 PermissionDenied2410 PermissionDenied
2412</h3>2411</h3>
2413 2412
2414当[自动模式](/docs/zh-CN/permission-modes#eliminate-prompts-with-auto-mode)拒绝工具调用时运行,包括在没有分类器判定的情况下拒绝——原因是[独立于自动模式的安全检查拒绝了分类器自身的请求](/docs/zh-CN/errors#auto-mode-cannot-determine-the-safety-of-an-action),或其响应无法解析。此 hook 仅在自动模式下触发:当您手动拒绝权限对话框、`PreToolUse` hook 阻止调用或 `deny` 规则匹配时,它不会运行。可用于记录拒绝、调整配置,或告诉模型它可以重试该工具调用。2413当[自动模式](/docs/zh-CN/permission-modes#eliminate-prompts-with-auto-mode)拒绝工具调用时运行,包括在没有分类器裁定的情况下拒绝的情形,原因可能是[独立于自动模式的安全检查拒绝了分类器自身的请求](/docs/zh-CN/errors#auto-mode-cannot-determine-the-safety-of-an-action),或分类器的响应无法解析。此 hook 仅在自动模式下触发:当您手动拒绝权限对话框、`PreToolUse` hook 阻止调用或 `deny` 规则匹配时,它不会运行。可用于记录拒绝情况、调整配置,或告知模型可以重试该工具调用。
2415 2414
2416按工具名称匹配,取值与 PreToolUse 相同。2415按工具名称匹配,取值与 PreToolUse 相同。
2417 2416
2419 PermissionDenied 输入2418 PermissionDenied 输入
2420</h4>2419</h4>
2421 2420
2422除了[通用输入字段](#common-input-fields)之外,PermissionDenied hook 还会接收 `tool_name`、`tool_input`、`tool_use_id` 和 `reason`。对于 MCP 工具,它们还会接收 [`mcp_server`](#pretooluse-input) 对象。2421除了[通用输入字段](#common-input-fields)外,PermissionDenied hook 还会接收 `tool_name`、`tool_input`、`tool_use_id` 和 `reason`。对于 MCP 工具,它们还会接收 [`mcp_server`](#pretooluse-input) 对象。
2423 2422
2424```json theme={null}2423```json theme={null}
2425{2424{
2440 2439
2441| 字段 | 描述 |2440| 字段 | 描述 |
2442| :- | :- |2441| :- | :- |
2443| `reason` | 拒绝原因。对于分类器判定,在大多数会话中它会用方括号标出匹配的规则,例如 `[Data Exfiltration]`;其他形式请参阅[查看拒绝](/docs/zh-CN/auto-mode-config#review-denials)。对于[无判定拒绝](#permissiondenied-decision-control),它以 `Auto mode could not evaluate this action and is blocking it for safety` 开头。对于因分类器模型不可用而导致的拒绝,它是固定文本 `Classifier unavailable` |2442| `reason` | 拒绝原因。对于分类器裁定,在大多数会话中它会用方括号注明所匹配的规则,例如 `[Data Exfiltration]`;其他形式请参阅[审查拒绝](/docs/zh-CN/auto-mode-config#review-denials)。对于[无裁定拒绝](#permissiondenied-decision-control),它以 `Auto mode could not evaluate this action and is blocking it for safety` 开头。对于因分类器模型不可用而产生的拒绝,它是固定文本 `Classifier unavailable` |
2444 2443
2445<h4 id="permissiondenied-decision-control">2444<h4 id="permissiondenied-decision-control">
2446 PermissionDenied 决策控制2445 PermissionDenied 决策控制
2447</h4>2446</h4>
2448 2447
2449PermissionDenied hook 可以告诉模型它可以重试被拒绝的工具调用。返回一个 `hookSpecificOutput.retry` 设为 `true` 的 JSON 对象:2448PermissionDenied hook 可以告知模型可以重试被拒绝的工具调用。返回一个将 `hookSpecificOutput.retry` 设为 `true` 的 JSON 对象:
2450 2449
2451```json theme={null}2450```json theme={null}
2452{2451{
2457}2456}
2458```2457```
2459 2458
2460当 `retry` 为 `true` 时,Claude Code 会向对话中添加一条消息,告诉模型它可以重试该工具调用。Claude Code 本身不会撤销该拒绝。如果您的 hook 没有返回 JSON,或返回 `retry: false`,拒绝将保持有效,模型会收到原始的拒绝消息。2459当 `retry` 为 `true` 时,Claude Code 会在对话中添加一条消息,告知模型可以重试该工具调用。Claude Code 本身不会撤销拒绝。如果您的 hook 不返回 JSON,或返回 `retry: false`,则拒绝保持有效,模型会收到原始的拒绝消息。
2461 2460
2462当分类器[未对该操作作出判定](/docs/zh-CN/errors#auto-mode-cannot-determine-the-safety-of-an-action)时(其响应无法解析,或独立于自动模式的安全检查拒绝了分类器自身的请求),Claude Code 会忽略 `retry: true`。对于这些拒绝,Claude Code 已经在拒绝消息中告诉模型是稍后重试还是继续其他工作。2461当分类器[未对该操作作出裁定](/docs/zh-CN/errors#auto-mode-cannot-determine-the-safety-of-an-action)时(其响应无法解析,或独立于自动模式的安全检查拒绝了分类器自身的请求),Claude Code 会忽略 `retry: true`。对于这类拒绝,Claude Code 已在拒绝消息中告知模型是稍后重试还是继续其他工作。
2463 2462
2464<h3 id="notification">2463<h3 id="notification">
2465 Notification2464 Notification
2466</h3>2465</h3>
2467 2466
2468当 Claude Code 发送通知时运行。按通知类型匹配。省略匹配器即可为所有通知类型运行 hook。2467在 Claude Code 发送通知时运行。按通知类型匹配。省略匹配器即可为所有通知类型运行 hook。
2469 2468
2470即使关闭了桌面通知,您也会收到这些 hook 事件:`preferredNotifChannel` 设置(包括 `notifications_disabled`)只改变提醒您的方式,而不影响您的 hook 是否运行。2469即使关闭了桌面通知,您仍会收到这些 hook 事件:`preferredNotifChannel` 设置(包括 `notifications_disabled`)只会改变提醒您的方式,而不会影响您的 hook 是否运行。
2471 2470
2472| 匹配器 | 触发时机 |2471| 匹配器 | 触发时机 |
2473| :- | :- |2472| :- | :- |
2474| `permission_prompt` | Claude 需要您批准一次工具使用或沙箱化命令的[网络请求](/docs/zh-CN/sandboxing#network-isolation),且该提示已等待约六秒 |2473| `permission_prompt` | Claude 需要您批准某次工具使用或某个沙箱命令的[网络请求](/docs/zh-CN/sandboxing#network-isolation),且该提示已等待约六秒 |
2475| `idle_prompt` | Claude 大约在 60 秒前完成回复,且您此后未输入任何内容 |2474| `idle_prompt` | Claude 在约 60 秒前完成回复,且此后您没有输入 |
2476| `auth_success` | 身份验证完成 |2475| `auth_success` | 身份验证完成 |
2477| `elicitation_dialog` | MCP 服务器打开了一个 elicitation 表单,且您约六秒未输入任何内容 |2476| `elicitation_dialog` | MCP 服务器打开了一个信息征询表单,且您约六秒没有输入 |
2478| `elicitation_url_dialog` | MCP 服务器要求您打开一个浏览器 URL,且您约六秒未输入任何内容 |2477| `elicitation_url_dialog` | MCP 服务器请求您打开一个浏览器 URL,且您约六秒没有输入 |
2479| `elicitation_complete` | MCP 服务器报告 [URL 模式 elicitation](#elicitation-input) 已完成 |2478| `elicitation_complete` | MCP 服务器报告某个 [URL 模式信息征询](#elicitation-input)已完成 |
2480| `elicitation_response` | MCP elicitation 响应被发回服务器 |2479| `elicitation_response` | MCP 信息征询响应被发送回服务器 |
2481| `agent_needs_input` | 当 [agent view](/docs/zh-CN/agent-view) 在终端中打开时,某个后台会话开始等待您的输入。当终端会话向您显示 [agent team 队友的终端设置问题](/docs/zh-CN/agent-teams#choose-a-display-mode)或自动模式关于[分类器请求费用](/docs/zh-CN/auto-mode-classifier-billing)的提示,且您约六秒未输入任何内容时,也会触发 |2480| `agent_needs_input` | 在终端中打开 [Agent 视图](/docs/zh-CN/agent-view)期间,某个后台会话开始等待您的输入。当终端会话向您显示 [agent team 队友的终端设置问题](/docs/zh-CN/agent-teams#choose-a-display-mode)或自动模式关于[分类器请求费用](/docs/zh-CN/auto-mode-classifier-billing)的通知,且您约六秒没有输入时,也会触发 |
2482| `agent_completed` | 某个后台会话完成或失败。仅在 [agent view](/docs/zh-CN/agent-view) 在终端中打开时触发 |2481| `agent_completed` | 某个后台会话完成或失败。仅在终端中打开 [Agent 视图](/docs/zh-CN/agent-view)时触发 |
2483| `quota_auto_resume_fired` | 在 claude.ai 用量限制暂停您的任务后,Claude Code 继续执行该任务:在限制重置时,或者在等待期间您在 Claude Code 中执行的某些操作(例如添加使用额度、升级套餐或切换模型)使用量再次可用时提前继续,但存在[模型设置例外](/docs/zh-CN/interactive-mode#wait-for-a-usage-limit-to-reset) |2482| `quota_auto_resume_fired` | 在 claude.ai 用量限制暂停您的任务后,Claude Code 继续执行该任务:在重置时,或者当您在等待期间在 Claude Code 中执行的某些操作(例如添加使用额度、升级套餐或切换模型)使用量再次可用时提前继续,[模型设置例外](/docs/zh-CN/interactive-mode#wait-for-a-usage-limit-to-reset)除外 |
2484| `quota_auto_resume_stale` | claude.ai 用量限制在您的计算机休眠超过约 30 分钟期间重置。Claude Code 会等待您按 `Enter`,而不是继续执行。如果休眠时间较短,它会继续执行并改为触发 `quota_auto_resume_fired` |2483| `quota_auto_resume_stale` | 在您的计算机休眠超过约 30 分钟期间,claude.ai 用量限制已重置。Claude Code 会等待您按 `Enter`,而不是继续执行。休眠时间较短时,它会继续执行并改为触发 `quota_auto_resume_fired` |
2485| `quota_auto_resume_disabled` | Claude Code 结束了对 claude.ai 用量限制的等待,但没有继续您的任务:[`autoContinueAtUsageLimit`](/docs/zh-CN/settings-reference#autocontinueatusagelimit) 被关闭,或在 Claude Code 自行开始的等待期间重置时间推迟到 24 小时以后,或继续执行的任务反复触及限制,或继续执行在到达模型之前被阻止。当您按 `Esc` 或 `Ctrl+C`,或选择 **Don't continue automatically** 时不会触发 |2484| `quota_auto_resume_disabled` | Claude Code 结束对 claude.ai 用量限制的等待而未继续您的任务:在 Claude Code 自行发起的等待期间,[`autoContinueAtUsageLimit`](/docs/zh-CN/settings-reference#autocontinueatusagelimit) 被关闭或重置时间推迟到 24 小时以后;继续执行的任务不断触及限制;或继续执行在到达模型之前被阻止。当您按 `Esc` 或 `Ctrl+C`,或选择 **Don't continue automatically** 时不会触发 |
2486 2485
2487`quota_auto_resume_fired`、`quota_auto_resume_stale` 和 `quota_auto_resume_disabled` 类型需要 Claude Code v2.1.234 或更高版本。2486`quota_auto_resume_fired`、`quota_auto_resume_stale` 和 `quota_auto_resume_disabled` 类型需要 Claude Code v2.1.234 或更高版本。
2488 2487
2489在终端会话中,针对沙箱化命令网络请求的 `permission_prompt` 需要 Claude Code v2.1.246 或更高版本。2488在终端会话中,针对沙箱命令网络请求的 `permission_prompt` 需要 Claude Code v2.1.246 或更高版本。
2490 2489
2491针对队友终端设置问题的 `agent_needs_input` 需要 Claude Code v2.1.248 或更高版本。2490针对队友终端设置问题的 `agent_needs_input` 需要 Claude Code v2.1.248 或更高版本。
2492 2491
2493<Note>2492<Note>
2494 `permission_prompt`、`idle_prompt`、`elicitation_dialog` 和 `elicitation_url_dialog` 类型与桌面通知共享计时方式,因此在终端会话中,只有当您看起来已离开终端时才会看到它们:2493 `permission_prompt`、`idle_prompt`、`elicitation_dialog` 和 `elicitation_url_dialog` 类型与桌面通知共享计时方式,因此在终端会话中,只有当您看起来已离开终端时才会看到它们:
2495 2494
2496 * 当您约六秒未输入任何内容时,预期会出现 `permission_prompt`。计时器在权限提示出现时开始,每次按键都会推迟它。要在 Claude 请求使用工具的权限时立即运行 hook,请改用 [PermissionRequest](#permissionrequest)。2495 * 在您约六秒没有输入后,预计会触发 `permission_prompt`。计时器在权限提示出现时开始,每次按键都会推迟它。要在 Claude 请求使用工具的权限时立即运行 hook,请改用 [PermissionRequest](#permissionrequest)。
2497 * 预期 `idle_prompt` 会在 Claude 完成回复约 60 秒后出现,并且仅当您此后未输入任何内容且没有后台 Agent(例如后台[子代理](/docs/zh-CN/sub-agents))仍在运行时才会出现。在等待 claude.ai 用量限制重置期间,Claude Code 不会发送 `idle_prompt`。当等待自行结束时,会改为触发某个 `quota_auto_resume_*` 类型。2496 * 在 Claude 完成回复约 60 秒后,预计会触发 `idle_prompt`,前提是此后您没有输入,且没有后台 Agent(例如后台[子代理](/docs/zh-CN/sub-agents))仍在运行。Claude Code 在等待 claude.ai 用量限制重置期间不会发送 `idle_prompt`。当等待自行结束时,会改为触发某个 `quota_auto_resume_*` 类型。
2498 * 对于 elicitation 表单预期会出现 `elicitation_dialog`,对于浏览器 URL 请求预期会出现 `elicitation_url_dialog`,前提是您约六秒未输入任何内容。两者与 `permission_prompt` 共享相同的六秒门槛:计时器在对话框出现时开始,每次按键都会推迟它。2497 * 对于信息征询表单,预计会触发 `elicitation_dialog`;对于浏览器 URL 请求,预计会触发 `elicitation_url_dialog`,二者都在您约六秒没有输入后触发。两者与 `permission_prompt` 共享相同的六秒门槛:计时器在对话框出现时开始,每次按键都会推迟它。
2499 2498
2500 在另一个对话框显示在屏幕上时到达的权限请求或 elicitation 同样适用六秒门槛,从请求到达时开始计时。其通知可能会在请求仍排在已打开对话框之后等待时就送达您。2499 在另一个对话框显示期间到达的权限请求或信息征询,仍适用相同的六秒门槛,从请求到达时开始计时。即使该请求仍在已打开的对话框后面等待,它的通知也可能已经送达您。
2501</Note>2500</Note>
2502 2501
2503在 Claude Code 将权限请求发送给 Agent SDK 的 [`canUseTool` 回调](/docs/zh-CN/agent-sdk/user-input)的会话中(Claude Desktop 和 VS Code 扩展就是以这种方式托管 Claude Code 的),Claude Code 对 `permission_prompt` 的计时方式有所不同:2502在 Claude Code 将权限请求发送到 Agent SDK 的 [`canUseTool` 回调](/docs/zh-CN/agent-sdk/user-input)的会话中(Claude Desktop 和 VS Code 扩展就是以这种方式托管 Claude Code 的),Claude Code 对 `permission_prompt` 的计时方式不同:
2504 2503
2505* 预期 `permission_prompt` 会在 Claude 请求权限约六秒后出现。在您输入时,Claude Code 不会推迟它。2504* 预计在 Claude 请求权限约六秒后触发 `permission_prompt`。Claude Code 不会因您输入而推迟它。
2506* 如果您或 [PermissionRequest](#permissionrequest) hook 提前作出回应,Claude Code 不会运行 `permission_prompt`。2505* 如果您或 [PermissionRequest](#permissionrequest) hook 更早作出回应,Claude Code 不会运行 `permission_prompt`。
2507* 将 [`CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKS`](/docs/zh-CN/env-vars) 设为 `1`,即可在这些会话中关闭 `permission_prompt`。2506* 将 [`CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKS`](/docs/zh-CN/env-vars) 设为 `1`,即可在这些会话中关闭 `permission_prompt`。
2508 2507
2509在 v2.1.233 之前,`permission_prompt` 不会在这些会话中触发。2508在 v2.1.233 之前,`permission_prompt` 在这些会话中不会触发。
2510 2509
2511使用不同的匹配器,可以根据通知类型运行不同的处理程序。以下配置会在 Claude 需要权限批准时触发一个专门用于权限的警报脚本,在 Claude 处于空闲状态时触发另一个通知:2510使用不同的匹配器,可根据通知类型运行不同的处理程序。以下配置在 Claude 需要权限批准时触发一个专用于权限的提醒脚本,在 Claude 空闲时触发另一个通知:
2512 2511
2513```json theme={null}2512```json theme={null}
2514{2513{
2541 Notification 输入2540 Notification 输入
2542</h4>2541</h4>
2543 2542
2544除了[通用输入字段](#common-input-fields)之外,Notification hook 还会接收包含通知文本的 `message`、可选的 `title`,以及指示触发了哪种类型的 `notification_type`。2543除了[通用输入字段](#common-input-fields)外,Notification hook 还会接收包含通知文本的 `message`、可选的 `title`,以及指示触发类型的 `notification_type`。
2545 2544
2546```json theme={null}2545```json theme={null}
2547{2546{
2555}2554}
2556```2555```
2557 2556
2558Notification hook 无法阻止或修改通知。Claude Code 会丢弃它们的 `systemMessage` 和 `continue` 字段,但仍会发出 [`terminalSequence`](#emit-terminal-notifications),桌面通知示例正是依赖于此。Notification hook 旨在用于副作用,例如将通知转发到外部服务。2557Notification hook 无法阻止或修改通知。Claude Code 会丢弃它们的 `systemMessage` 和 `continue` 字段,但仍会发出 [`terminalSequence`](#emit-terminal-notifications),桌面通知示例正是依赖于此。Notification hook 适用于产生副作用,例如将通知转发到外部服务。
2559 2558
2560<h3 id="subagentstart">2559<h3 id="subagentstart">
2561 SubagentStart2560 SubagentStart
2562</h3>2561</h3>
2563 2562
2564当 Claude 使用 Agent 工具生成子代理、当 Claude [恢复子代理](/docs/zh-CN/sub-agents#resume-subagents),以及每次进程内 [agent team](/docs/zh-CN/agent-teams) 队友处理新消息时运行。支持使用匹配器按 Agent 类型名称进行过滤。对于内置 Agent,这是 Agent 名称,例如 `general-purpose`、`Explore` 或 `Plan`。对于[自定义子代理](/docs/zh-CN/sub-agents),这是 Agent frontmatter 中的 `name` 字段,而不是文件名。2563当 Claude 使用 Agent 工具生成子代理、当 Claude [恢复子代理](/docs/zh-CN/sub-agents#resume-subagents),以及每次进程内 [agent team](/docs/zh-CN/agent-teams) 队友处理新消息时运行。支持使用匹配器按 Agent 类型名称过滤。对于内置 Agent,这是诸如 `general-purpose`、`Explore` 或 `Plan` 之类的 Agent 名称。对于[自定义子代理](/docs/zh-CN/sub-agents),这是 Agent frontmatter 中的 `name` 字段,而非文件名。
2565 2564
2566对于由[插件](/docs/zh-CN/plugins/overview)提供的子代理,Agent 类型是插件范围的标识符,例如 `my-plugin:reviewer`,而不是单纯的 frontmatter 名称。冒号会使插件范围的名称走正则表达式匹配路径,因此请用 `^` 和 `$` 锚定匹配器以进行精确匹配:`^my-plugin:reviewer$`。2565对于由[插件](/docs/zh-CN/plugins/overview)提供的子代理,Agent 类型是插件作用域的标识符,例如 `my-plugin:reviewer`,而不是单纯的 frontmatter 名称。冒号会使插件作用域的名称走正则表达式路径,因此请用 `^` 和 `$` 锚定匹配器以实现精确匹配:`^my-plugin:reviewer$`。
2567 2566
2568<h4 id="subagentstart-input">2567<h4 id="subagentstart-input">
2569 SubagentStart 输入2568 SubagentStart 输入
2570</h4>2569</h4>
2571 2570
2572除了[通用输入字段](#common-input-fields)之外,SubagentStart hook 还会接收包含子代理唯一标识符的 `agent_id`,以及包含匹配器所过滤的 Agent 名称的 `agent_type`。2571除了[通用输入字段](#common-input-fields)外,SubagentStart hook 还会接收包含子代理唯一标识符的 `agent_id`,以及包含匹配器所过滤的 Agent 名称的 `agent_type`。
2573 2572
2574```json theme={null}2573```json theme={null}
2575{2574{
2582}2581}
2583```2582```
2584 2583
2585SubagentStart hook 无法阻止子代理的创建,但可以向子代理注入上下文。除了所有 hook 都可用的 [JSON 输出字段](#json-output)之外,您还可以返回:2584SubagentStart hook 无法阻止子代理的创建,但可以向子代理注入上下文。除了所有 hook 均可使用的 [JSON 输出字段](#json-output)外,您还可以返回:
2586 2585
2587| 字段 | 描述 |2586| 字段 | 描述 |
2588| :- | :- |2587| :- | :- |
2597}2596}
2598```2597```
2599 2598
2600当 hook 针对同一子代理再次运行时,仅当子代理的上下文中尚未包含先前运行注入的副本时,Claude Code 才会注入返回的上下文。启动时注入的副本会保留在原位,从而使子代理的[提示缓存](/docs/zh-CN/prompt-caching#subagents-and-the-cache)保持完整。在[自动压缩](/docs/zh-CN/sub-agents#auto-compaction)丢弃该副本后,Claude Code 会再次注入下一次运行的上下文。2599当 hook 针对同一子代理再次运行时,仅当子代理的上下文中尚未包含先前运行注入的副本时,Claude Code 才会注入返回的上下文。启动时注入的副本会保留在原位,从而保持子代理的[提示缓存](/docs/zh-CN/prompt-caching#subagents-and-the-cache)不变。在[自动压缩](/docs/zh-CN/sub-agents#auto-compaction)丢弃该副本后,Claude Code 会再次注入下一次运行的上下文。
2601 2600
2602<h3 id="subagentstop">2601<h3 id="subagentstop">
2603 SubagentStop2602 SubagentStop
2604</h3>2603</h3>
2605 2604
2606当 Claude Code 子代理完成回复时运行。按 Agent 类型匹配,取值与 SubagentStart 相同。2605在 Claude Code 子代理完成回复时运行。按 Agent 类型匹配,取值与 SubagentStart 相同。
2607 2606
2608<h4 id="subagentstop-input">2607<h4 id="subagentstop-input">
2609 SubagentStop 输入2608 SubagentStop 输入
2610</h4>2609</h4>
2611 2610
2612除了[通用输入字段](#common-input-fields)之外,SubagentStop hook 还会接收 `stop_hook_active`、`agent_id`、`agent_type`、`agent_transcript_path` 和 `last_assistant_message`。`agent_type` 字段是用于匹配器过滤的值。`transcript_path` 是主会话的会话记录,而 `agent_transcript_path` 是存储在嵌套 `subagents/` 文件夹中的子代理自身的会话记录。`last_assistant_message` 字段包含子代理最终回复的文本内容,因此 hook 无需解析会话记录文件即可访问它。2611除了[通用输入字段](#common-input-fields)外,SubagentStop hook 还会接收 `stop_hook_active`、`agent_id`、`agent_type`、`agent_transcript_path` 和 `last_assistant_message`。`agent_type` 字段是用于匹配器过滤的值。`transcript_path` 是主会话的会话记录,而 `agent_transcript_path` 是子代理自身的会话记录,存储在嵌套的 `subagents/` 文件夹中。`last_assistant_message` 字段包含子代理最终回复的文本内容,因此 hook 无需解析会话记录文件即可访问它。
2613 2612
2614并非每个 SubagentStop 事件都来自 Claude 生成的子代理。Claude Code 还会为其自身的某些功能运行内部 Agent,例如[提示词建议](/docs/zh-CN/interactive-mode#prompt-suggestions)和 [`/btw` 旁支问题](/docs/zh-CN/interactive-mode#side-questions-with-%2Fbtw),当这些 Agent 之一完成时,SubagentStop 也会触发。对于这些事件,`agent_type` 是会话本身运行所用的 Agent 名称,例如通过 [`--agent`](/docs/zh-CN/cli-reference#cli-flags) 或 [`agent` 设置](/docs/zh-CN/settings-reference#agent)设定的名称;当会话未使用 Agent 运行时则为空字符串。2613并非每个 SubagentStop 事件都来自 Claude 生成的子代理。Claude Code 还会为其自身的某些功能运行内部 Agent,例如[提示词建议](/docs/zh-CN/interactive-mode#prompt-suggestions)和 [`/btw` 附带问题](/docs/zh-CN/interactive-mode#side-questions-with-%2Fbtw),这些内部 Agent 完成时也会触发 SubagentStop。对于这些事件,`agent_type` 是会话本身运行所用的 Agent 名称,例如通过 [`--agent`](/docs/zh-CN/cli-reference#cli-flags) 或 [`agent` 设置](/docs/zh-CN/settings-reference#agent)设定的名称;当会话未使用 Agent 运行时,它为空字符串。
2615 2614
2616指定了 Agent 类型的 `matcher` 不会匹配空的 `agent_type`。匹配器被省略、为 `""` 或 `"*"`,或者是能匹配空字符串的正则表达式的 hook,也会针对 `agent_type` 为空的事件运行。2615指定了 Agent 类型的 `matcher` 不会匹配空的 `agent_type`。匹配器被省略、为 `""` 或 `"*"`,或者是能匹配空字符串的正则表达式的 hook,也会针对 `agent_type` 为空的事件运行。
2617 2616
2618在 Claude Code v2.1.271 或更高版本中,使用 [`SubagentHandback`](/docs/zh-CN/tools-reference) 工具运行的子代理会在停止之前通过该工具传递其报告。此时 `last_assistant_message` 字段保存的是子代理的结束文本(如果有),而不是所传递的报告。报告是该调用的 `message` 输入,匹配 `SubagentHandback` 的 `PreToolUse` 或 `PostToolUse` hook 会以 `tool_input.message` 的形式收到它。2617在 Claude Code v2.1.271 或更高版本中,使用 [`SubagentHandback`](/docs/zh-CN/tools-reference) 工具运行的子代理会在停止前通过该工具传递其报告。此时 `last_assistant_message` 字段保存的是子代理的结束文本(如有),而不是所传递的报告。报告是该调用的 `message` 输入,匹配 `SubagentHandback` 的 `PreToolUse` 或 `PostToolUse` hook 会以 `tool_input.message` 的形式接收它。
2619 2618
2620SubagentStop hook 还会接收 [Stop 输入](#stop-input)中描述的 `background_tasks` 和 `session_crons` 数组。这两个数组的范围是父会话,而不是子代理。2619SubagentStop hook 还会接收 [Stop 输入](#stop-input)中所述的 `background_tasks` 和 `session_crons` 数组。这两个数组的作用域都是父会话,而不是子代理。
2621 2620
2622```json theme={null}2621```json theme={null}
2623{2622{
2636}2635}
2637```2636```
2638 2637
2639SubagentStop hook 使用与 [Stop hook](#stop-decision-control) 相同的决策控制格式,包括将 `hookEventName` 设为 `"SubagentStop"` 的 `hookSpecificOutput.additionalContext`,用于提供让子代理继续运行的非错误反馈。返回带有 `reason` 的 `decision: "block"` 会让子代理继续运行,并将 `reason` 作为下一条指令传递给子代理。通过退出码 2 进行阻止的 hook 会以同样方式传递其 stderr 消息。要在子代理返回后向父会话注入上下文,请改为在 `Agent` 工具上使用 [`PostToolUse`](#posttooluse) hook。2638SubagentStop hook 使用与 [Stop hook](#stop-decision-control) 相同的决策控制格式,包括将 `hookEventName` 设为 `"SubagentStop"` 的 `hookSpecificOutput.additionalContext`,用于提供让子代理继续运行的非错误反馈。返回带有 `reason` 的 `decision: "block"` 会让子代理继续运行,并将 `reason` 作为下一条指令传递给子代理。通过以退出码 2 退出来阻止的 hook 会以相同方式传递其 stderr 消息。要在子代理返回后向父会话注入上下文,请改为在 `Agent` 工具上使用 [`PostToolUse`](#posttooluse) hook。
2640 2639
2641<h3 id="taskcreated">2640<h3 id="taskcreated">
2642 TaskCreated2641 TaskCreated
2643</h3>2642</h3>
2644 2643
2645当通过 `TaskCreate` 工具创建任务时运行。可用于强制执行命名约定、要求提供任务描述或阻止创建某些任务。在[没有 Task 工具的会话](/docs/zh-CN/tools-reference#task-tool-availability)中,此事件不会触发。2644在通过 `TaskCreate` 工具创建任务时运行。可用于强制执行命名约定、要求提供任务描述,或阻止创建某些任务。在[不含 Task 工具的会话](/docs/zh-CN/tools-reference#task-tool-availability)中,此事件不会触发。
2646 2645
2647TaskCreated hook 不支持匹配器,每次发生时都会触发。2646TaskCreated hook 不支持匹配器,每次发生时都会触发。
2648 2647
2650 TaskCreated 输入2649 TaskCreated 输入
2651</h4>2650</h4>
2652 2651
2653除了[通用输入字段](#common-input-fields)之外,TaskCreated hook 还会接收 `task_id`、`task_subject`,以及可选的 `task_description`、`teammate_name` 和 `team_name`。2652除了[通用输入字段](#common-input-fields)外,TaskCreated hook 还会接收 `task_id`、`task_subject`,以及可选的 `task_description`、`teammate_name` 和 `team_name`。
2654 2653
2655```json theme={null}2654```json theme={null}
2656{2655{
2673| `task_description` | 任务的详细描述。可能不存在 |2672| `task_description` | 任务的详细描述。可能不存在 |
2674| `teammate_name` | 创建该任务的队友名称。可能不存在 |2673| `teammate_name` | 创建该任务的队友名称。可能不存在 |
2675| `team_name` | 已弃用。从会话派生的团队名称;将在未来版本中移除 |2674| `team_name` | 已弃用。从会话派生的团队名称;将在未来版本中移除 |
2675| `agent_id` | 在此事件中,该[通用输入字段](#common-input-fields)标识正在创建任务的子代理或[进程内队友](/docs/zh-CN/agent-teams#choose-a-display-mode)。可能不存在。需要 Claude Code v2.1.290 或更高版本 |
2676 2676
2677<h4 id="taskcreated-decision-control">2677<h4 id="taskcreated-decision-control">
2678 TaskCreated 决策控制2678 TaskCreated 决策控制
2702 TaskCompleted2702 TaskCompleted
2703</h3>2703</h3>
2704 2704
2705当任务被标记为已完成时运行。它会在两种情况下触发:任何 Agent 通过 TaskUpdate 工具显式将任务标记为已完成时,或 [agent team](/docs/zh-CN/agent-teams) 队友在仍有进行中任务的情况下结束其轮次时。可用于在任务关闭之前强制执行完成标准,例如测试通过或 lint 检查通过。2705在任务被标记为已完成时运行。它在两种情况下触发:任何 Agent 通过 TaskUpdate 工具显式将任务标记为已完成时,或 [agent team](/docs/zh-CN/agent-teams) 队友在仍有进行中任务的情况下结束其轮次时。可用于在任务关闭前强制执行完成标准,例如测试通过或 lint 检查通过。
2706 2706
2707TaskCompleted hook 不支持匹配器,每次发生时都会触发。2707TaskCompleted hook 不支持匹配器,每次发生时都会触发。
2708 2708
2710 TaskCompleted 输入2710 TaskCompleted 输入
2711</h4>2711</h4>
2712 2712
2713除了[通用输入字段](#common-input-fields)之外,TaskCompleted hook 还会接收 `task_id`、`task_subject`,以及可选的 `task_description`、`teammate_name` 和 `team_name`。2713除了[通用输入字段](#common-input-fields)外,TaskCompleted hook 还会接收 `task_id`、`task_subject`,以及可选的 `task_description`、`teammate_name` 和 `team_name`。
2714 2714
2715```json theme={null}2715```json theme={null}
2716{2716{
2734| `task_description` | 任务的详细描述。可能不存在 |2734| `task_description` | 任务的详细描述。可能不存在 |
2735| `teammate_name` | 完成该任务的队友名称。可能不存在 |2735| `teammate_name` | 完成该任务的队友名称。可能不存在 |
2736| `team_name` | 已弃用。从会话派生的团队名称;将在未来版本中移除 |2736| `team_name` | 已弃用。从会话派生的团队名称;将在未来版本中移除 |
2737| `agent_id` | 在此事件中,该[通用输入字段](#common-input-fields)标识正在完成任务的子代理或[进程内队友](/docs/zh-CN/agent-teams#choose-a-display-mode)。可能不存在。需要 Claude Code v2.1.290 或更高版本 |
2737 2738
2738<h4 id="taskcompleted-decision-control">2739<h4 id="taskcompleted-decision-control">
2739 TaskCompleted 决策控制2740 TaskCompleted 决策控制
2741 2742
2742TaskCompleted hook 支持两种控制任务完成的方式:2743TaskCompleted hook 支持两种控制任务完成的方式:
2743 2744
2744* **退出码 2**:任务不会被标记为已完成,stderr 消息会作为反馈传回给模型。2745* **退出码 2**:任务不会被标记为已完成,stderr 消息会作为反馈回传给模型。
2745* **JSON `{"continue": false, "stopReason": "..."}`**:当事件由队友结束其轮次触发时,完全停止该队友,与 `Stop` hook 的行为一致。`stopReason` 会显示给用户。当事件由 `TaskUpdate` 工具触发时,Claude Code 会忽略 `continue: false`;退出码 2 仍会阻止完成。2746* **JSON `{"continue": false, "stopReason": "..."}`**:当事件由队友结束其轮次触发时,完全停止该队友,与 `Stop` hook 的行为一致。`stopReason` 会显示给用户。当事件由 `TaskUpdate` 工具触发时,Claude Code 会忽略 `continue: false`;退出码 2 仍会阻止完成。
2746 2747
2747以下示例运行测试,如果测试失败则阻止任务完成:2748以下示例运行测试,并在测试失败时阻止任务完成:
2748 2749
2749```bash theme={null}2750```bash theme={null}
2750#!/bin/bash2751#!/bin/bash
2764 Stop2765 Stop
2765</h3>2766</h3>
2766 2767
2767当主 Claude Code Agent 完成回复时运行。如果停止是由用户中断导致的,则不会运行。API 错误会改为触发2768在主 Claude Code Agent 完成回复时运行。如果停止是由用户中断引起的,则不会运行。API 错误会改为触发
2768[StopFailure](#stopfailure)。2769[StopFailure](#stopfailure)。
2769 2770
2770<Tip>2771<Tip>
2771 [`/goal`](/docs/zh-CN/goal) 命令是会话范围内基于提示词的 Stop hook 的内置快捷方式。当您希望 Claude 朝着某个条件持续工作而无需编写 hook 配置时,请使用它。2772 [`/goal`](/docs/zh-CN/goal) 命令是会话作用域的、基于提示词的 Stop hook 的内置快捷方式。当您希望 Claude 持续朝某个条件努力而无需编写 hook 配置时,可以使用它。
2772</Tip>2773</Tip>
2773 2774
2774<h4 id="stop-input">2775<h4 id="stop-input">
2775 Stop 输入2776 Stop 输入
2776</h4>2777</h4>
2777 2778
2778除了[通用输入字段](#common-input-fields)之外,Stop hook 还会收到 `stop_hook_active`、`last_assistant_message`、`background_tasks` 和 `session_crons`。当 Claude Code 已经因 stop hook 而在继续运行时,`stop_hook_active` 字段为 `true`。请检查此值或处理会话记录,以避免因永远无法满足的条件而持续阻止。2779除了[通用输入字段](#common-input-fields)外,Stop hook 还会接收 `stop_hook_active`、`last_assistant_message`、`background_tasks` 和 `session_crons`。当 Claude Code 已经因 stop hook 而继续运行时,`stop_hook_active` 字段为 `true`。请检查此值或处理会话记录,以避免因一个永远无法满足的条件而持续阻止。
2779 2780
2780Claude Code 设有连续 8 次继续的上限:在 stop hook 已连续八次让轮次继续之后,Claude Code 会覆盖下一次阻止并结束该轮次。每当 Claude 调用工具时,连续继续的计数都会重置。要提高上限,请设置 [`CLAUDE_CODE_STOP_HOOK_BLOCK_CAP`](/docs/zh-CN/env-vars)。2781Claude Code 设有连续 8 次继续的上限:在 stop hook 连续八次让轮次继续后,Claude Code 会覆盖下一次阻止并结束该轮次。每次 Claude 调用工具时,连续继续的计数都会重置。要提高该上限,请设置 [`CLAUDE_CODE_STOP_HOOK_BLOCK_CAP`](/docs/zh-CN/env-vars)。
2781 2782
2782`last_assistant_message` 字段包含 Claude 最终回复的文本内容,因此 hook 无需解析会话记录文件即可访问它。对于需要处理刚完成的轮次的 hook(例如朗读或通知 hook),请使用此字段,而不是读取 `transcript_path`:在所有版本中,并不能保证会话记录文件在 Stop 时已包含最终消息。2783`last_assistant_message` 字段包含 Claude 最终回复的文本内容,因此 hook 无需解析会话记录文件即可访问它。对于作用于刚刚完成的轮次的 hook(例如朗读或通知 hook),请使用此字段而不是读取 `transcript_path`:并非所有版本都能保证在 Stop 时会话记录文件中已包含最终消息。
2783 2784
2784`background_tasks` 和 `session_crons` 数组让 hook 能够区分"会话已完成"和"会话已暂停,正在等待后台工作将其重新唤醒"。当任务注册表可访问时,这两个数组都会存在;当没有正在进行或已计划的内容时,它们为空。2785`background_tasks` 和 `session_crons` 数组使 hook 能够区分"会话已完成"和"会话已暂停,正在等待后台工作将其唤醒"。当任务注册表可访问时,这两个数组都会存在;当没有正在进行或已计划的内容时,它们为空。
2785 2786
2786`background_tasks` 中的每个条目描述一个正在进行的任务,并使用以下字段:2787`background_tasks` 中的每个条目描述一个正在进行的任务,并使用以下字段:
2787 2788
2788| 字段 | 描述 |2789| 字段 | 描述 |
2789| :- | :- |2790| :- | :- |
2790| `id` | 任务标识符 |2791| `id` | 任务标识符 |
2791| `type` | 易读的任务类型标签,例如 `shell`、`subagent`、`monitor`、`workflow`、`teammate`、`cloud session` 或 `MCP task`。每个标签标识了创建该任务的 Claude Code 功能。对于无法识别的类型,回退为原始判别值 |2792| `type` | 友好的任务类型标签,例如 `shell`、`subagent`、`monitor`、`workflow`、`teammate`、`cloud session` 或 `MCP task`。每个标签标识创建该任务的 Claude Code 功能。对于无法识别的类型,回退为原始判别值 |
2792| `status` | 当前任务状态 |2793| `status` | 当前任务状态 |
2793| `description` | 自由文本描述,上限为 1000 个字符,被截断时在字符串中带有 `… [+N chars]` 标记 |2794| `description` | 自由文本描述,上限为 1000 个字符,截断时会在字符串内带有 `… [+N chars]` 标记 |
2794| `command` | shell 命令行,上限为 1000 个字符。仅存在于 `shell` 任务中 |2795| `command` | Shell 命令行,上限为 1000 个字符。仅对 `shell` 任务存在 |
2795| `agent_type` | 子代理类型名称。仅存在于 `subagent` 任务中 |2796| `agent_type` | 子代理类型名称。仅对 `subagent` 任务存在 |
2796| `server` | MCP 服务器名称。仅存在于 `monitor` 和 `MCP task` 任务中 |2797| `server` | MCP 服务器名称。仅对 `monitor` 和 `MCP task` 任务存在 |
2797| `tool` | MCP 工具名称。仅存在于 `monitor` 和 `MCP task` 任务中 |2798| `tool` | MCP 工具名称。仅对 `monitor` 和 `MCP task` 任务存在 |
2798| `name` | 工作流名称。仅存在于 `workflow` 任务中 |2799| `name` | 工作流名称。仅对 `workflow` 任务存在 |
2799 2800
2800`session_crons` 中的每个条目描述一个会话范围内的计划唤醒,来源于 `CronCreate`、`ScheduleWakeup` 和 `/loop`:2801`session_crons` 中的每个条目描述一个会话作用域的计划唤醒,来源为 `CronCreate`、`ScheduleWakeup` 和 `/loop`:
2801 2802
2802| 字段 | 描述 |2803| 字段 | 描述 |
2803| :- | :- |2804| :- | :- |
2804| `id` | Cron 任务标识符 |2805| `id` | Cron 任务标识符 |
2805| `schedule` | Cron 表达式,例如 `0 9 * * 1-5` |2806| `schedule` | Cron 表达式,例如 `0 9 * * 1-5` |
2806| `recurring` | 对于计划中只编码了单个触发时间的一次性唤醒为 `false`,对于每次匹配都会重新触发的任务为 `true` |2807| `recurring` | 对于计划只编码单次触发时间的一次性唤醒为 `false`,对于每次匹配都会重新触发的任务为 `true` |
2807| `prompt` | cron 触发时提交的提示词,上限为 1000 个字符,带有相同的 `… [+N chars]` 标记 |2808| `prompt` | cron 触发时提交的提示词,上限为 1000 个字符,截断时带有相同的 `… [+N chars]` 标记 |
2808 2809
2809以下示例展示了一个包含一个正在进行的 shell 任务和一个周期性 cron 的 Stop 输入:2810以下示例展示了一个包含一个正在进行的 shell 任务和一个周期性 cron 的 Stop 输入:
2810 2811
2841 Stop 决策控制2842 Stop 决策控制
2842</h4>2843</h4>
2843 2844
2844`Stop` 和 `SubagentStop` hook 可以控制 Claude 是否继续。除了所有 hook 都可用的 [JSON 输出字段](#json-output)之外,您的 hook 脚本还可以返回以下特定于事件的字段:2845`Stop` 和 `SubagentStop` hook 可以控制 Claude 是否继续。除了所有 hook 均可使用的 [JSON 输出字段](#json-output)外,您的 hook 脚本还可以返回以下事件特定字段:
2845 2846
2846| 字段 | 描述 |2847| 字段 | 描述 |
2847| :- | :- |2848| :- | :- |
2848| `decision` | `"block"` 会阻止 Claude 停止。省略则允许 Claude 停止 |2849| `decision` | `"block"` 阻止 Claude 停止。省略则允许 Claude 停止 |
2849| `reason` | 当 `decision` 为 `"block"` 时必填。告诉 Claude 为什么应该继续 |2850| `reason` | 当 `decision` 为 `"block"` 时必填。告知 Claude 为何应继续 |
2850| `hookSpecificOutput.additionalContext` | 给 Claude 的非错误反馈。对话会继续,以便 Claude 据此采取行动,但与 `decision: "block"` 不同,它在会话记录中显示为 hook 反馈,而不是 hook 错误 |2851| `hookSpecificOutput.additionalContext` | 给 Claude 的非错误反馈。对话会继续,以便 Claude 据此采取行动,但与 `decision: "block"` 不同,它在会话记录中显示为 hook 反馈而非 hook 错误 |
2851 2852
2852通过退出码 2 进行阻止的 hook 与 `reason` 的传递方式相同:Claude 会收到 stderr 消息,作为它应该继续的原因说明。2853通过以退出码 2 退出来阻止的 hook,其路由方式与 `reason` 相同:Claude 会将 stderr 消息作为它应继续的原因说明接收。
2853 2854
2854```json theme={null}2855```json theme={null}
2855{2856{
2858}2859}
2859```2860```
2860 2861
2861当 hook 按设计正常工作并为 Claude 提供指导时(例如"完成前运行测试套件"),请使用 `additionalContext`。它通过与 `decision: "block"` 相同的循环保护机制(即 `stop_hook_active` 输入和 8 次连续继续上限)让对话继续,但会话记录会将其标记为 `Stop hook feedback`,并且不会显示 hook 错误通知:2862当 hook 按设计正常工作并为 Claude 提供指导时(例如"在完成之前运行测试套件"),请使用 `additionalContext`。它通过与 `decision: "block"` 相同的循环保护机制(即 `stop_hook_active` 输入和连续 8 次继续的上限)让对话继续进行,但会话记录会将其标记为 `Stop hook feedback`,且不会显示 hook 错误通知:
2862 2863
2863```json theme={null}2864```json theme={null}
2864{2865{
2873 StopFailure2874 StopFailure
2874</h3>2875</h3>
2875 2876
2876当轮次因 API 错误而结束时,代替 [Stop](#stop) 运行。除 [`terminalSequence`](#emit-terminal-notifications) 外,Claude Code 会忽略该 hook 的输出和退出码。当 Claude 由于速率限制、身份验证问题或其他 API 错误而无法完成回复时,可用于记录失败、发送警报或采取恢复措施。2877当轮次因 API 错误而结束时,代替 [Stop](#stop) 运行。除 [`terminalSequence`](#emit-terminal-notifications) 外,Claude Code 会忽略该 hook 的输出和退出码。当 Claude 因速率限制、身份验证问题或其他 API 错误而无法完成回复时,可用于记录失败、发送警报或执行恢复操作。
2877 2878
2878<h4 id="stopfailure-input">2879<h4 id="stopfailure-input">
2879 StopFailure 输入2880 StopFailure 输入
2880</h4>2881</h4>
2881 2882
2882除了[通用输入字段](#common-input-fields)之外,StopFailure hook 还会接收 `error`、可选的 `error_details` 和可选的 `last_assistant_message`。`error` 字段标识错误类型,并用于匹配器过滤。2883除了[通用输入字段](#common-input-fields)外,StopFailure hook 还会接收 `error`、可选的 `error_details` 和可选的 `last_assistant_message`。`error` 字段标识错误类型,并用于匹配器过滤。
2883 2884
2884| 字段 | 描述 |2885| 字段 | 描述 |
2885| :- | :- |2886| :- | :- |
2886| `error` | 错误类型:`rate_limit`、`overloaded`、`authentication_failed`、`oauth_org_not_allowed`、`account_on_hold`、`billing_error`、`invalid_request`、`model_not_found`、`server_error`、`max_output_tokens`、`cloud_credential_error` 或 `unknown` |2887| `error` | 错误类型:`rate_limit`、`overloaded`、`authentication_failed`、`oauth_org_not_allowed`、`account_on_hold`、`billing_error`、`invalid_request`、`model_not_found`、`server_error`、`max_output_tokens`、`cloud_credential_error` 或 `unknown` |
2887| `error_details` | 关于该错误的其他详细信息(如有) |2888| `error_details` | 有关错误的其他详细信息(如有) |
2888| `last_assistant_message` | 在对话中显示的渲染后错误文本。与 `Stop` 和 `SubagentStop` 中该字段保存 Claude 的对话输出不同,对于 `StopFailure`,它包含 API 错误字符串本身,例如 `"API Error: Rate limit reached"` |2889| `last_assistant_message` | 对话中显示的渲染后错误文本。与 `Stop` 和 `SubagentStop`(此字段保存 Claude 的对话输出)不同,对于 `StopFailure`,它包含 API 错误字符串本身,例如 `"API Error: Rate limit reached"` |
2889 2890
2890```json theme={null}2891```json theme={null}
2891{2892{
2899}2900}
2900```2901```
2901 2902
2902StopFailure hook 没有决策控制。它们仅用于通知和日志记录目的。2903StopFailure hook 没有决策控制。它们仅用于通知和日志记录。
2903 2904
2904<h3 id="teammateidle">2905<h3 id="teammateidle">
2905 TeammateIdle2906 TeammateIdle
2906</h3>2907</h3>
2907 2908
2908当 [agent team](/docs/zh-CN/agent-teams) 队友在结束其轮次后即将进入空闲状态时运行。可用于在队友停止工作之前强制执行质量关卡,例如要求 lint 检查通过或验证输出文件是否存在。2909当 [agent team](/docs/zh-CN/agent-teams) 队友在结束其轮次后即将进入空闲状态时运行。可用于在队友停止工作前强制执行质量关卡,例如要求 lint 检查通过或验证输出文件是否存在。
2909 2910
2910TeammateIdle hook 不支持匹配器,每次发生时都会触发。2911TeammateIdle hook 不支持匹配器,每次发生时都会触发。
2911 2912
2913 TeammateIdle 输入2914 TeammateIdle 输入
2914</h4>2915</h4>
2915 2916
2916除了[通用输入字段](#common-input-fields)之外,TeammateIdle hook 还会接收 `teammate_name` 和 `team_name`。2917除了[通用输入字段](#common-input-fields)外,TeammateIdle hook 还会接收 `teammate_name` 和 `team_name`。
2917 2918
2918```json theme={null}2919```json theme={null}
2919{2920{
2931| :- | :- |2932| :- | :- |
2932| `teammate_name` | 即将进入空闲状态的队友名称 |2933| `teammate_name` | 即将进入空闲状态的队友名称 |
2933| `team_name` | 已弃用。从会话派生的团队名称;将在未来版本中移除 |2934| `team_name` | 已弃用。从会话派生的团队名称;将在未来版本中移除 |
2935| `agent_id` | 在此事件中,该[通用输入字段](#common-input-fields)标识即将进入空闲状态的[进程内队友](/docs/zh-CN/agent-teams#choose-a-display-mode)。可能不存在。需要 Claude Code v2.1.290 或更高版本 |
2934 2936
2935<h4 id="teammateidle-decision-control">2937<h4 id="teammateidle-decision-control">
2936 TeammateIdle 决策控制2938 TeammateIdle 决策控制
2938 2940
2939TeammateIdle hook 支持两种控制队友行为的方式:2941TeammateIdle hook 支持两种控制队友行为的方式:
2940 2942
2941* **退出码 2**:队友会收到 stderr 消息作为反馈,并继续工作而不是进入空闲状态。2943* **退出码 2**:队友会将 stderr 消息作为反馈接收,并继续工作而不是进入空闲状态。
2942* **JSON `{"continue": false, "stopReason": "..."}`**:完全停止该队友,与 `Stop` hook 的行为一致。`stopReason` 会显示给用户。2944* **JSON `{"continue": false, "stopReason": "..."}`**:完全停止该队友,与 `Stop` hook 的行为一致。`stopReason` 会显示给用户。
2943 2945
2944以下示例在允许队友进入空闲状态之前检查构建产物是否存在:2946以下示例在允许队友进入空闲状态之前检查构建产物是否存在:
2958 ConfigChange2960 ConfigChange
2959</h3>2961</h3>
2960 2962
2961当会话期间配置文件发生更改时运行。可用于审计设置更改、强制执行安全策略,或阻止对配置文件的未经授权的修改。2963在会话期间配置文件发生更改时运行。可用于审计设置更改、强制执行安全策略,或阻止对配置文件的未授权修改。
2962 2964
2963当设置文件、托管策略文件或 skill 文件发生更改时,Claude Code 会运行 ConfigChange hook。对于托管策略,仅当 `managed-settings.json` 或 `managed-settings.d/` 中的文件发生更改时才会运行。对于[服务器托管设置](/docs/zh-CN/server-managed-settings)以及 macOS 托管偏好设置或 Windows 注册表策略的更改,Claude Code 会直接应用而不运行这些 hook。在启用了 [`wslInheritsWindowsSettings`](/docs/zh-CN/settings-reference#wslinheritswindowssettings) 的 WSL 上,它在策略轮询时也会直接应用已更改的 Windows 端托管设置文件,而不运行这些 hook。2965当设置文件、托管策略文件或 skill 文件发生更改时,Claude Code 会运行 ConfigChange hook。对于托管策略,仅当 `managed-settings.json` 或 `managed-settings.d/` 中的文件发生更改时才会运行它们。它会应用[服务器托管设置](/docs/zh-CN/server-managed-settings)以及对 macOS 托管偏好设置或 Windows 注册表策略的更改,而不运行这些 hook。在启用了 [`wslInheritsWindowsSettings`](/docs/zh-CN/settings-reference#wslinheritswindowssettings) 的 WSL 上,它还会在策略轮询时应用已更改的 Windows 端托管设置文件,同样不运行这些 hook。
2964 2966
2965匹配器按配置来源进行过滤:2967匹配器按配置来源过滤:
2966 2968
2967| 匹配器 | 触发时机 |2969| 匹配器 | 触发时机 |
2968| :- | :- |2970| :- | :- |
2972| `policy_settings` | `managed-settings.json` 或 `managed-settings.d/` 中的文件发生更改 |2974| `policy_settings` | `managed-settings.json` 或 `managed-settings.d/` 中的文件发生更改 |
2973| `skills` | `.claude/skills/` 中的 skill 文件发生更改 |2975| `skills` | `.claude/skills/` 中的 skill 文件发生更改 |
2974 2976
2975以下示例记录所有配置更改以进行安全审计:2977以下示例记录所有配置更改以用于安全审计:
2976 2978
2977```json theme={null}2979```json theme={null}
2978{2980{
2996 ConfigChange 输入2998 ConfigChange 输入
2997</h4>2999</h4>
2998 3000
2999除了[通用输入字段](#common-input-fields)之外,ConfigChange hook 还会接收 `source` 和可选的 `file_path`。`source` 字段指示哪种配置类型发生了更改,`file_path` 提供被修改的具体文件的路径。3001除了[通用输入字段](#common-input-fields)外,ConfigChange hook 还会接收 `source` 和可选的 `file_path`。`source` 字段指示哪种配置类型发生了更改,`file_path` 提供被修改的具体文件的路径。
3000 3002
3001```json theme={null}3003```json theme={null}
3002{3004{
3017 3019
3018| 字段 | 描述 |3020| 字段 | 描述 |
3019| :- | :- |3021| :- | :- |
3020| `decision` | `"block"` 会阻止应用该配置更改。省略则允许更改 |3022| `decision` | `"block"` 阻止应用配置更改。省略则允许更改 |
3021| `reason` | 可接受但永远不会显示 |3023| `reason` | 会被接受,但从不显示 |
3022 3024
3023```json theme={null}3025```json theme={null}
3024{3026{
3027}3029}
3028```3030```
3029 3031
3030`policy_settings` 更改无法被阻止。当机器上的托管设置文件发生更改时,hook 仍会针对 `policy_settings` 来源触发,因此您可以使用它们记录这些编辑,但任何阻止决策都会被忽略。这确保了企业托管设置始终生效。当[服务器托管设置](/docs/zh-CN/server-managed-settings)到达或刷新时,Claude Code 不会运行 `ConfigChange` hook。3032`policy_settings` 更改无法被阻止。当机器上的托管设置文件发生更改时,hook 仍会针对 `policy_settings` 来源触发,因此您可以用它们记录这些编辑,但任何阻止决策都会被忽略。这可确保企业托管设置始终生效。当[服务器托管设置](/docs/zh-CN/server-managed-settings)到达或刷新时,Claude Code 不会运行 `ConfigChange` hook。
3031 3033
3032Claude Code 会根据 ConfigChange hook JSON 输出中的阻止决策采取行动,并丢弃 `systemMessage` 和 `continue`。无论您是通过 `reason` 还是通过退出码 2 时的 stderr 进行阻止,被阻止的更改都不会向您或 Claude 显示任何消息。Claude Code 只会在调试日志中写入一行。3034Claude Code 会根据 ConfigChange hook JSON 输出中的阻止决策采取行动,并丢弃 `systemMessage` 和 `continue`。被阻止的更改不会向您或 Claude 显示任何消息,无论您是通过 `reason` 还是通过退出码 2 时的 stderr 进行阻止。Claude Code 只会向调试日志写入一行。
3033 3035
3034<h3 id="cwdchanged">3036<h3 id="cwdchanged">
3035 CwdChanged3037 CwdChanged
3036</h3>3038</h3>
3037 3039
3038当主对话中的 shell 命令更改了工作目录时运行,例如 Claude 执行 `cd` 命令时。可用于响应目录更改:重新加载环境变量、激活特定于项目的工具链,或自动运行设置脚本。可与 [FileChanged](#filechanged) 配合使用,以支持 [direnv](https://direnv.net/) 等管理每目录环境的工具。3040当主对话中的 shell 命令更改了工作目录时运行,例如当 Claude 执行 `cd` 命令时。可用于响应目录更改:重新加载环境变量、激活项目特定的工具链,或自动运行设置脚本。可与 [FileChanged](#filechanged) 搭配使用,以支持 [direnv](https://direnv.net/) 等按目录管理环境的工具。
3039 3041
3040CwdChanged hook 可以访问 [`CLAUDE_ENV_FILE`](#persist-environment-variables)。写入该文件的变量会在后续 Bash 命令中持续有效,直到下一个 CwdChanged 事件时由 Claude Code 清除。3042CwdChanged hook 可以访问 [`CLAUDE_ENV_FILE`](#persist-environment-variables)。写入该文件的变量会持续作用于后续的 Bash 命令,直到下一个 CwdChanged 事件发生,届时 Claude Code 会清除它们。
3041 3043
3042CwdChanged 不支持匹配器,每次发生时都会触发。3044CwdChanged 不支持匹配器,每次发生时都会触发。
3043 3045
3045 CwdChanged 输入3047 CwdChanged 输入
3046</h4>3048</h4>
3047 3049
3048除了[通用输入字段](#common-input-fields)之外,CwdChanged hook 还会接收 `old_cwd` 和 `new_cwd`。3050除了[通用输入字段](#common-input-fields)外,CwdChanged hook 还会接收 `old_cwd` 和 `new_cwd`。
3049 3051
3050```json theme={null}3052```json theme={null}
3051{3053{
3062 CwdChanged 输出3064 CwdChanged 输出
3063</h4>3065</h4>
3064 3066
3065除了所有 hook 都可用的 [JSON 输出字段](#json-output)之外,CwdChanged hook 还可以返回 `watchPaths`,以动态设置 [FileChanged](#filechanged) 监视哪些文件路径:3067除了所有 hook 均可使用的 [JSON 输出字段](#json-output)外,CwdChanged hook 还可以返回 `watchPaths`,以动态设置 [FileChanged](#filechanged) 监视的文件路径:
3066 3068
3067| 字段 | 描述 |3069| 字段 | 描述 |
3068| :- | :- |3070| :- | :- |
3069| `watchPaths` | 绝对路径数组。替换当前的动态监视列表。您的 `matcher` 配置中的路径始终会被监视。返回空数组会清除动态列表,这在进入新目录时很常见 |3071| `watchPaths` | 绝对路径数组。替换当前的动态监视列表。来自 `matcher` 配置的路径始终会被监视。返回空数组会清空动态列表,这在进入新目录时很常见 |
3070 3072
3071CwdChanged hook 没有决策控制。它们无法阻止目录更改。3073CwdChanged hook 没有决策控制。它们无法阻止目录更改。
3072 3074
3073Claude Code 会从其 JSON 输出中读取 `watchPaths` 和 `systemMessage`,并丢弃 `continue`。在交互式会话中,它会将 `systemMessage` 显示为简短的终端通知。该消息不会进入 SDK 消息流。3075Claude Code 会从其 JSON 输出中读取 `watchPaths` 和 `systemMessage`,并丢弃 `continue`。在交互式会话中,它会将 `systemMessage` 显示为简短的终端通知。该消息不会到达 SDK 消息流。
3074 3076
3075<h3 id="directoryadded">3077<h3 id="directoryadded">
3076 DirectoryAdded3078 DirectoryAdded
3077</h3>3079</h3>
3078 3080
3079在您于会话中途使用 `/add-dir` 命令添加工作目录之后,或在 SDK 客户端通过 `register_repo_root` 控制请求添加工作目录之后运行。可用于准备新添加的仓库,例如安装其依赖。3081在您于会话中途使用 `/add-dir` 命令添加工作目录后,或 SDK 客户端使用 `register_repo_root` 控制请求添加工作目录后运行。可用于准备新添加的仓库,例如安装其依赖。
3080 3082
3081在以下情况下,Claude Code 不会触发此事件:3083在以下情况下,Claude Code 不会触发此事件:
3082 3084
3083* 您通过 `--add-dir` 启动标志传入目录;这些目录由 [SessionStart](#sessionstart) 覆盖3085* 您通过 `--add-dir` 启动标志传入目录;这些目录由 [SessionStart](#sessionstart) 处理
3084* 您在 `/permissions` 的 Workspace 选项卡上添加目录3086* 您在 `/permissions` 的 Workspace 选项卡上添加目录
3085* 您添加的目录已经是工作目录或位于某个工作目录内3087* 您添加的目录已经是工作目录或位于某个工作目录内
3086 3088
3087Claude Code 会在刷新沙箱和权限状态之后触发 DirectoryAdded,因此当您的 hook 运行时,沙箱化工具已经能看到新目录。hook 命令本身在沙箱之外运行。3089Claude Code 会在刷新沙箱和权限状态之后触发 DirectoryAdded,因此当您的 hook 运行时,沙箱中的工具已经可以看到新目录。hook 命令本身在沙箱之外运行。
3088 3090
3089Claude Code 不会等待该 hook:添加操作会立即完成,hook 在后台运行,使用 600 秒的默认超时时间。3091Claude Code 不会等待该 hook:添加操作会立即完成,hook 在后台运行,默认超时时间为 600 秒。
3090 3092
3091匹配器按目录的添加方式进行过滤:3093匹配器按目录的添加方式过滤:
3092 3094
3093| 匹配器 | 触发时机 |3095| 匹配器 | 触发时机 |
3094| :- | :- |3096| :- | :- |
3095| `slash_command` | 您使用 `/add-dir` 添加目录 |3097| `slash_command` | 您使用 `/add-dir` 添加目录 |
3096| `register_repo_root` | SDK 客户端通过 `register_repo_root` 控制请求添加目录 |3098| `register_repo_root` | SDK 客户端使用 `register_repo_root` 控制请求添加目录 |
3097 3099
3098<h4 id="directoryadded-input">3100<h4 id="directoryadded-input">
3099 DirectoryAdded 输入3101 DirectoryAdded 输入
3100</h4>3102</h4>
3101 3103
3102除了[通用输入字段](#common-input-fields)之外,DirectoryAdded hook 还会接收 `directory` 和 `source`。3104除了[通用输入字段](#common-input-fields)外,DirectoryAdded hook 还会接收 `directory` 和 `source`。
3103 3105
3104| 字段 | 描述 |3106| 字段 | 描述 |
3105| :- | :- |3107| :- | :- |
3106| `directory` | 所添加目录的绝对路径 |3108| `directory` | 所添加目录的绝对路径 |
3107| `source` | 目录的添加方式:`/add-dir` 对应 `"slash_command"`,SDK 控制请求对应 `"register_repo_root"` |3109| `source` | 目录的添加方式,`/add-dir` 对应 `"slash_command"`,SDK 控制请求对应 `"register_repo_root"` |
3108 3110
3109```json theme={null}3111```json theme={null}
3110{3112{
3119 3121
3120DirectoryAdded hook 没有决策控制。它们无法阻止添加操作,因为 hook 运行时添加已经完成。Claude Code 会丢弃其 JSON 输出中的 `continue` 字段,并根据来源以不同方式呈现其余内容:3122DirectoryAdded hook 没有决策控制。它们无法阻止添加操作,因为 hook 运行时添加已经完成。Claude Code 会丢弃其 JSON 输出中的 `continue` 字段,并根据来源以不同方式呈现其余内容:
3121 3123
3122* `slash_command`:Claude Code 会在下一个对话轮次中将 hook 的 `systemMessage` 作为上下文传递给 Claude,而不是向您显示。失败 hook 的数量会显示在会话记录中。完整的失败输出会写入调试日志3124* `slash_command`:Claude Code 会在下一个对话轮次中将 hook 的 `systemMessage` 作为上下文传递给 Claude,而不是向您显示。会话记录中会显示失败 hook 的数量。完整的失败输出会写入调试日志
3123* `register_repo_root`:Claude Code 仅将 `systemMessage` 输出和失败输出写入调试日志3125* `register_repo_root`:Claude Code 仅将 `systemMessage` 输出和失败输出写入调试日志
3124 3126
3125<h3 id="filechanged">3127<h3 id="filechanged">
3128 3130
3129当被监视的文件在磁盘上发生更改时运行。Claude Code 通过文件系统监视器而非检查工具调用来检测更改,因此无论是什么更改了文件,它都会运行该 hook:`Edit` 或 `Write` 工具调用、Claude 通过 `Bash` 运行的脚本,或完全在 Claude Code 之外的进程。一个常见用途是在项目配置文件更改时重新加载环境变量。3131当被监视的文件在磁盘上发生更改时运行。Claude Code 通过文件系统监视器而非检查工具调用来检测更改,因此无论是什么更改了文件,它都会运行该 hook:`Edit` 或 `Write` 工具调用、Claude 通过 `Bash` 运行的脚本,或完全在 Claude Code 之外的进程。一个常见用途是在项目配置文件更改时重新加载环境变量。
3130 3132
3131此事件的 `matcher` 有两个作用:3133此事件的 `matcher` 承担两个角色:
3132 3134
3133* **构建监视列表**:该值按 `|` 拆分,每个片段都被注册为工作目录中的字面文件名,因此 `".envrc|.env"` 恰好监视这两个文件。正则表达式模式在这里没有用处:像 `^\.env` 这样的值会监视一个字面名为 `^\.env` 的文件。3135* **构建监视列表**:该值按 `|` 拆分,每个片段都被注册为工作目录中的一个字面文件名,因此 `".envrc|.env"` 恰好监视这两个文件。正则表达式模式在这里没有用处:像 `^\.env` 这样的值会监视一个字面名为 `^\.env` 的文件。
3134* **过滤运行哪些 hook**:当被监视的文件发生更改时,同一个值会按照标准[匹配器规则](#matcher-patterns),针对已更改文件的基本名称过滤运行哪些 hook 组。3136* **过滤要运行的 hook**:当被监视的文件发生更改时,同一个值会按照标准[匹配器规则](#matcher-patterns),针对已更改文件的基本名称过滤要运行的 hook 组。
3135 3137
3136以下示例会在 `data.csv` 发生任何更改后(包括 `Bash` 命令或外部脚本重写该文件)规范化其行尾:3138以下示例会在 `data.csv` 发生任何更改(包括由 `Bash` 命令或外部脚本重写文件)后规范化其行尾:
3137 3139
3138```json theme={null}3140```json theme={null}
3139{3141{
3153}3155}
3154```3156```
3155 3157
3156该 hook 从 stdin 上 [JSON 输入](#filechanged-input)的 `file_path` 字段读取已更改文件的绝对路径。它的 `grep` 守卫检查的正是 `perl` 要删除的内容,即行尾的 CR,因此规范化之后的那次运行会直接退出而不触碰文件。较宽松的守卫会导致无限循环,因为即使没有替换任何内容,`perl -i` 也会重写文件,而 Claude Code 在每次重写后都会再次运行该 hook。请将此脚本保存到 `/path/to/normalize-line-endings.sh` 并使其可执行:3158该 hook 从 stdin 上 [JSON 输入](#filechanged-input)的 `file_path` 字段中读取已更改文件的绝对路径。其 `grep` 守卫检测的正是 `perl` 要移除的内容,即行尾的 CR,因此规范化之后的那次运行会直接退出而不触碰文件。更宽松的守卫会导致无限循环,因为即使没有替换任何内容,`perl -i` 也会重写文件,而 Claude Code 在每次重写后都会再次运行该 hook。将此脚本保存到 `/path/to/normalize-line-endings.sh` 并使其可执行:
3157 3159
3158```bash theme={null}3160```bash theme={null}
3159#!/bin/bash3161#!/bin/bash
3163fi3165fi
3164```3166```
3165 3167
3166要确认 hook 是否正常工作,请让 Claude 使用 `Bash` 命令向 `data.csv` 追加一行 CRLF。Claude Code 会运行该 hook,文件最终将使用 LF 行尾。3168要确认该 hook 正常工作,请让 Claude 使用 `Bash` 命令向 `data.csv` 追加一行 CRLF。Claude Code 会运行该 hook,文件最终会使用 LF 行尾。
3167 3169
3168要监视无法预先命名的文件,请从 hook 返回 [`watchPaths`](#filechanged-output) 以动态更新监视列表。只有当有内容指定了要监视的文件时,Claude Code 才会启动监视器,因此请用一个匹配器至少指定了一个文件的 FileChanged 组,或一个返回 `watchPaths` 的 [SessionStart](#sessionstart-decision-control) 或 [CwdChanged](#cwdchanged) hook 来初始化该列表。当被监视的文件发生更改时,匹配器仍会过滤运行哪些 hook 组,因此请为处理动态路径的组省略匹配器,这样它会匹配每个被监视的文件,且不会向监视列表添加任何内容。`"*"` 匹配器同样匹配每个文件,但 Claude Code 会像处理其他值一样将其注册到监视列表中,作为一个字面名为 `*` 的文件。3170要监视无法预先命名的文件,请从 hook 中返回 [`watchPaths`](#filechanged-output) 以动态更新监视列表。Claude Code 仅在有内容指定了要监视的文件时才会启动监视器,因此请用一个匹配器至少指定一个文件的 FileChanged 组,或用一个返回 `watchPaths` 的 [SessionStart](#sessionstart-decision-control) 或 [CwdChanged](#cwdchanged) hook 来初始化列表。当被监视的文件发生更改时,匹配器仍会过滤要运行的 hook 组,因此请为处理动态路径的组省略匹配器,这样它会匹配所有被监视的文件,且不会向监视列表添加任何内容。`"*"` 匹配器同样匹配所有文件,但 Claude Code 会像对待其他值一样将其注册到监视列表中,作为一个字面名为 `*` 的文件。
3169 3171
3170FileChanged hook 可以访问 [`CLAUDE_ENV_FILE`](#persist-environment-variables)。写入该文件的变量会在后续 Bash 命令中持续有效,直到下一个 [CwdChanged](#cwdchanged) 事件时由 Claude Code 清除。3172FileChanged hook 可以访问 [`CLAUDE_ENV_FILE`](#persist-environment-variables)。写入该文件的变量会持续作用于后续的 Bash 命令,直到下一个 [CwdChanged](#cwdchanged) 事件发生,届时 Claude Code 会清除它们。
3171 3173
3172<h4 id="filechanged-input">3174<h4 id="filechanged-input">
3173 FileChanged 输入3175 FileChanged 输入
3174</h4>3176</h4>
3175 3177
3176除了[通用输入字段](#common-input-fields)之外,FileChanged hook 还会接收 `file_path` 和 `event`。3178除了[通用输入字段](#common-input-fields)外,FileChanged hook 还会接收 `file_path` 和 `event`。
3177 3179
3178| 字段 | 描述 |3180| 字段 | 描述 |
3179| :- | :- |3181| :- | :- |
3195 FileChanged 输出3197 FileChanged 输出
3196</h4>3198</h4>
3197 3199
3198除了所有 hook 都可用的 [JSON 输出字段](#json-output)之外,FileChanged hook 还可以返回 `watchPaths`,以动态更新监视哪些文件路径:3200除了所有 hook 均可使用的 [JSON 输出字段](#json-output)外,FileChanged hook 还可以返回 `watchPaths`,以动态更新被监视的文件路径:
3199 3201
3200| 字段 | 描述 |3202| 字段 | 描述 |
3201| :- | :- |3203| :- | :- |
3202| `watchPaths` | 绝对路径数组。替换当前的动态监视列表。您的 `matcher` 配置中的路径始终会被监视。当您的 hook 脚本根据已更改的文件发现需要额外监视的文件时,请使用此字段 |3204| `watchPaths` | 绝对路径数组。替换当前的动态监视列表。来自 `matcher` 配置的路径始终会被监视。当您的 hook 脚本根据已更改的文件发现需要监视的其他文件时,请使用此字段 |
3203 3205
3204FileChanged hook 没有决策控制。它们无法阻止文件更改的发生。3206FileChanged hook 没有决策控制。它们无法阻止文件更改的发生。
3205 3207
3206Claude Code 会从其 JSON 输出中读取 `watchPaths` 和 `systemMessage`,并丢弃 `continue`。在交互式会话中,它会将 `systemMessage` 显示为简短的终端通知。该消息不会进入 SDK 消息流。3208Claude Code 会从其 JSON 输出中读取 `watchPaths` 和 `systemMessage`,并丢弃 `continue`。在交互式会话中,它会将 `systemMessage` 显示为简短的终端通知。该消息不会到达 SDK 消息流。
3207 3209
3208<h3 id="worktreecreate">3210<h3 id="worktreecreate">
3209 WorktreeCreate3211 WorktreeCreate
3210</h3>3212</h3>
3211 3213
3212在创建 worktree 时运行,无论是来自 `claude --worktree`、来自[使用 `isolation: "worktree"` 的子代理](/docs/zh-CN/sub-agents#choose-the-subagent-scope),还是用于 Claude Code 隔离在其自身 worktree 中的[后台会话](/docs/zh-CN/agent-view#how-file-edits-are-isolated)。默认情况下,Claude Code 使用 `git worktree` 创建隔离的工作副本。配置 WorktreeCreate hook 会替换该默认 git 行为,让您可以使用 SVN、Perforce 或 Mercurial 等其他版本控制系统。3214在创建 worktree 时运行,无论是通过 `claude --worktree`、通过[使用 `isolation: "worktree"` 的子代理](/docs/zh-CN/sub-agents#choose-the-subagent-scope),还是为 Claude Code 隔离在其自身 worktree 中的[后台会话](/docs/zh-CN/agent-view#how-file-edits-are-isolated)创建。默认情况下,Claude Code 使用 `git worktree` 创建隔离的工作副本。配置 WorktreeCreate hook 会替换此默认的 git 行为,让您可以使用其他版本控制系统,例如 SVN、Perforce 或 Mercurial。
3213 3215
3214由于该 hook 完全替换了默认行为,因此不会处理 [`.worktreeinclude`](/docs/zh-CN/worktrees#copy-gitignored-files-into-worktrees)。如果您需要将 `.env` 等本地配置文件复制到新的 worktree 中,请在您的 hook 脚本中完成。3216由于该 hook 完全替换了默认行为,因此不会处理 [`.worktreeinclude`](/docs/zh-CN/worktrees#copy-gitignored-files-into-worktrees)。如果需要将 `.env` 等本地配置文件复制到新的 worktree 中,请在 hook 脚本中完成。
3215 3217
3216该 hook 必须返回所创建 worktree 目录的路径。Claude Code 将此路径用作隔离会话的工作目录。有关每种 hook 类型如何返回路径,请参阅 [WorktreeCreate 输出](#worktreecreate-output)。3218该 hook 必须返回所创建 worktree 目录的路径。Claude Code 将此路径用作隔离会话的工作目录。有关每种 hook 类型如何返回路径,请参阅 [WorktreeCreate 输出](#worktreecreate-output)。
3217 3219
3218Claude Code 会根据 hook 的成功状态和返回的路径采取行动,并丢弃 `systemMessage` 和 `continue`。3220Claude Code 依据 hook 是否成功以及返回的路径执行操作,并丢弃 `systemMessage` 和 `continue`。
3219 3221
3220以下示例创建一个 SVN 工作副本,并打印路径供 Claude Code 使用。请将仓库 URL 替换为您自己的 URL:3222此示例创建一个 SVN 工作副本,并打印路径供 Claude Code 使用。请将仓库 URL 替换为您自己的 URL:
3221 3223
3222```json theme={null}3224```json theme={null}
3223{3225{
3236}3238}
3237```3239```
3238 3240
3239该 hook 从 stdin 上的 JSON 输入中读取 worktree 的 `name`,将全新副本检出到新目录中,并打印目录路径。最后一行的 `echo` 就是 Claude Code 读取为 worktree 路径的内容。请将任何其他输出重定向到 stderr,以免干扰该路径。3241该 hook 从 stdin 上的 JSON 输入中读取 worktree 的 `name`,将一个全新副本检出到新目录中,并打印目录路径。最后一行的 `echo` 就是 Claude Code 读取为 worktree 路径的内容。请将其他所有输出重定向到 stderr,以免干扰路径。
3240 3242
3241<h4 id="worktreecreate-input">3243<h4 id="worktreecreate-input">
3242 WorktreeCreate 输入3244 WorktreeCreate 输入
3243</h4>3245</h4>
3244 3246
3245除了[通用输入字段](#common-input-fields)之外,WorktreeCreate hook 还会接收 `name` 字段。这是新 worktree 的 slug 标识符,由用户指定或自动生成,例如 `bold-oak-a3f2`。3247除[通用输入字段](#common-input-fields)外,WorktreeCreate hook 还会接收 `name` 字段。这是新 worktree 的 slug 标识符,由用户指定或自动生成,例如 `bold-oak-a3f2`。
3246 3248
3247```json theme={null}3249```json theme={null}
3248{3250{
3258 WorktreeCreate 输出3260 WorktreeCreate 输出
3259</h4>3261</h4>
3260 3262
3261WorktreeCreate hook 不使用标准的允许/阻止决策模型,而是由 hook 的成功或失败决定结果。hook 必须返回所创建的 worktree 目录的路径:3263WorktreeCreate hook 不使用标准的允许/阻止决策模型,而是由 hook 的成功或失败决定结果。该 hook 必须返回所创建 worktree 目录的路径:
3262 3264
3263* **命令 hook**(`type: "command"`):将路径作为 stdout 的最后一个非空行打印。Claude Code 在读取该行之前会去除 ANSI 转义码,因此在您的 `echo` 之前打印的 shell 启动横幅会被忽略。请将其他任何 hook 输出重定向到 stderr。3265* **命令 hook**(`type: "command"`):将路径作为 stdout 的最后一个非空行打印。Claude Code 在读取该行之前会去除 ANSI 转义码,因此在 `echo` 之前打印的 shell 启动横幅会被忽略。请将 hook 的其他所有输出重定向到 stderr。
3264* **HTTP hook**(`type: "http"`):在响应体中返回 `{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }`。3266* **HTTP hook**(`type: "http"`):在响应正文中返回 `{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }`。
3265 3267
3266如果 hook 失败或未生成路径,worktree 创建将失败并报错。3268如果 hook 失败或未生成路径,worktree 创建将失败并报错。
3267 3269
3268Claude Code 会相对于 hook 运行所在的目录解析相对路径,并折叠其中的任何 `.` 或 `..` 段。如果解析得到的路径不是 Claude Code 可以进入的目录,会话会打印一条指明该路径的错误,并以代码 1 退出。3270Claude Code 会相对于 hook 运行时所在的目录解析相对路径,并折叠其中的 `.` 或 `..` 段。如果生成的路径不是 Claude Code 可以进入的目录,会话会打印一条指明该路径的错误,并以退出码 1 退出。
3269 3271
3270Claude Code 会拒绝包含 `.` 或 `..` 段的绝对路径,以及任何经过仓库根目录下符号链接的路径,因为提交到仓库中的符号链接可能会将 worktree 重定向到仓库之外。错误信息会指明被拒绝的路径组成部分。请返回一个规范化的、不经过仓库内符号链接的路径。在 v2.1.216 之前,worktree 创建会直接采用 hook 返回的路径,不进行此项检查。3272Claude Code 会拒绝包含 `.` 或 `..` 段的绝对路径,以及任何经过仓库根目录下符号链接的路径,因为提交到仓库中的符号链接可能会将 worktree 重定向到仓库之外。错误信息会指明被拒绝的路径组成部分。请返回一个规范化的、不经过仓库内符号链接的路径。在 v2.1.216 之前,worktree 创建会直接使用 hook 返回的路径,不进行此项检查。
3271 3273
3272<h3 id="worktreeremove">3274<h3 id="worktreeremove">
3273 WorktreeRemove3275 WorktreeRemove
3274</h3>3276</h3>
3275 3277
3276当 Claude Code 清理由您的 [`WorktreeCreate`](#worktreecreate) hook 创建的 worktree 时运行。该事件在以下情况下触发:3278在 Claude Code 清理由您的 [`WorktreeCreate`](#worktreecreate) hook 创建的 worktree 时运行。该事件在以下情况下触发:
3277 3279
3278* 您退出交互式 [worktree 会话](/docs/zh-CN/worktrees#start-claude-in-a-worktree),并在 Claude Code 提示时选择删除该 worktree3280* 您退出交互式 [worktree 会话](/docs/zh-CN/worktrees#start-claude-in-a-worktree),并在 Claude Code 提示时选择删除该 worktree
3279* 您退出一个尚未[命名](/docs/zh-CN/sessions#name-your-sessions)的交互式 worktree 会话,Claude Code 未发现已更改或未跟踪的文件,并在不提示您的情况下删除该 worktree3281* 您退出一个尚未[命名](/docs/zh-CN/sessions#name-your-sessions)的交互式 worktree 会话,Claude Code 未发现已更改或未跟踪的文件,并在不提示的情况下删除该 worktree
3280* 您删除在该 worktree 中运行的[后台会话](/docs/zh-CN/agent-view#what-deleting-a-session-removes)3282* 您删除一个在该 worktree 中运行的[后台会话](/docs/zh-CN/agent-view#what-deleting-a-session-removes)
3281 3283
3282Claude Code 使用 git 查找已更改或未跟踪的文件,因此对于不是 git checkout 或不在 git checkout 内的 worktree,即使目录中有未提交的工作,它也找不到任何文件。请在 WorktreeRemove hook 删除任何内容之前检查这类工作。3284Claude Code 使用 git 查找已更改或未跟踪的文件,因此对于不是 git 检出或不位于 git 检出内的 worktree,即使目录中有未提交的工作,它也找不到任何内容。请在 WorktreeRemove hook 删除任何内容之前检查此类工作。
3283 3285
3284对于基于 git 的 worktree,Claude Code 会使用 `git worktree remove` 自动处理清理。如果您配置了 WorktreeCreate hook,请搭配一个 WorktreeRemove hook,以控制其所创建的 worktree 的清理:3286对于基于 git 的 worktree,Claude Code 会使用 `git worktree remove` 自动处理清理。如果您配置了 WorktreeCreate hook,请将其与 WorktreeRemove hook 配对使用,以控制对其所创建 worktree 的清理:
3285 3287
3286* **没有 WorktreeRemove hook**:当 Claude Code 在您退出 worktree 会话时删除该 worktree,它会回退到对 WorktreeCreate hook 返回的路径执行 `git worktree remove --force`,因此 git 能识别的 worktree 会被删除。git 无法识别的 worktree(例如您的 hook 使用非 git 版本控制系统创建的 worktree)会保留在磁盘上。关于删除[后台会话](/docs/zh-CN/agent-view#what-deleting-a-session-removes)时如何处理由 hook 创建的 worktree,请参阅 agent view 的删除规则。3288* **没有 WorktreeRemove hook**:当您退出 worktree 会话、Claude Code 删除该 worktree 时,它会回退为对您的 WorktreeCreate hook 返回的路径执行 `git worktree remove --force`,因此 git 能识别的 worktree 会被删除。git 无法识别的 worktree(例如您的 hook 使用非 git 版本控制系统创建的 worktree)会保留在磁盘上。有关删除[后台会话](/docs/zh-CN/agent-view#what-deleting-a-session-removes)时如何处理由 hook 创建的 worktree,请参阅 agent view 的删除规则。
3287* **Hook 以 0 退出**:该 worktree 视为已移除。Claude Code 不会从 hook 读取其他任何内容,因此请确保您的 hook 已删除该目录。3289* **Hook 以 0 退出**:该 worktree 被视为已删除。Claude Code 不会从 hook 读取其他任何内容,因此请确保您的 hook 已删除该目录。
3288* **Hook 以非零值退出**:如果 `worktree_path` 处的目录在之后仍然存在,则移除失败,worktree 保留在磁盘上,且不会回退到 git。在以非零值退出之前已删除目录的 hook 视为已移除。关于失败的报告方式,请参阅 [WorktreeRemove 输入](#worktreeremove-input)。3290* **Hook 以非零值退出**:如果之后 `worktree_path` 处的目录仍然存在,则删除失败,worktree 保留在磁盘上,且不会回退到 git。在以非零值退出之前已删除目录的 hook 被视为已删除。有关如何报告失败,请参阅 [WorktreeRemove 输入](#worktreeremove-input)。
3289 3291
3290Claude Code 从不删除属于 hook 创建的 worktree 的分支,因为它只知道您的 WorktreeCreate hook 返回的路径。如果您的 WorktreeCreate hook 创建了分支,请在 WorktreeRemove hook 中删除该分支。3292Claude Code 永远不会删除属于由 hook 创建的 worktree 的分支,因为它只知道您的 WorktreeCreate hook 返回的路径。如果您的 WorktreeCreate hook 创建了分支,请在 WorktreeRemove hook 中删除它。
3291 3293
3292Claude Code 会丢弃 WorktreeRemove hook 的 [JSON 输出字段](#json-output),例如 `systemMessage` 和 `continue`。3294Claude Code 会丢弃 WorktreeRemove hook 的 [JSON 输出字段](#json-output),例如 `systemMessage` 和 `continue`。
3293 3295
3294对于后台会话的删除,Claude Code 会在运行 hook 之前验证存储的 worktree 路径,并拒绝本身是符号链接或经过仓库根目录下符号链接的路径。对于仍包含文件的 worktree,只有当您在 [Agent 视图](/docs/zh-CN/agent-view#what-deleting-a-session-removes)中确认删除时,hook 才会运行;对于这类 worktree,[`claude rm`](/docs/zh-CN/agent-view#manage-sessions-from-the-shell) 会保留会话和 worktree。在 v2.1.216 之前,hook 会直接在存储的路径上运行,不进行这些检查。3296对于后台会话删除,Claude Code 会在运行 hook 之前验证存储的 worktree 路径,并拒绝本身是符号链接或经过仓库根目录下符号链接的路径。对于仍包含文件的 worktree,只有当您在 [agent view](/docs/zh-CN/agent-view#what-deleting-a-session-removes) 中确认删除时,hook 才会运行;对于此类 worktree,[`claude rm`](/docs/zh-CN/agent-view#manage-sessions-from-the-shell) 会保留会话和 worktree。在 v2.1.216 之前,hook 会在未经这些检查的情况下对存储的路径运行。
3295 3297
3296Claude Code 会将 WorktreeCreate 返回的路径作为 hook 输入中的 `worktree_path` 传入。以下示例读取该路径并删除该目录:3298Claude Code 会将 WorktreeCreate 返回的路径作为 hook 输入中的 `worktree_path` 传递。此示例读取该路径并删除目录:
3297 3299
3298```json theme={null}3300```json theme={null}
3299{3301{
3316 WorktreeRemove 输入3318 WorktreeRemove 输入
3317</h4>3319</h4>
3318 3320
3319除[通用输入字段](#common-input-fields)外,WorktreeRemove hook 还会接收 `worktree_path` 字段,即正在移除的 worktree 的绝对路径。3321除[通用输入字段](#common-input-fields)外,WorktreeRemove hook 还会接收 `worktree_path` 字段,即要删除的 worktree 的绝对路径。
3320 3322
3321```json theme={null}3323```json theme={null}
3322{3324{
3328}3330}
3329```3331```
3330 3332
3331WorktreeRemove hook 的退出码决定结果。当 hook 以非零值退出且 `worktree_path` 处的目录之后仍然存在时,移除失败:3333WorktreeRemove hook 的退出码决定结果。当 hook 以非零值退出,且之后 `worktree_path` 处的目录仍然存在时,删除失败:
3332 3334
3333* worktree 保留在磁盘上,hook 的命令和 stderr 会写入[调试日志](#debug-hooks)。3335* worktree 保留在磁盘上,hook 的命令和 stderr 会写入[调试日志](#debug-hooks)。
3334* 如果您正在删除后台会话,该会话也会保留。[Agent 视图](/docs/zh-CN/agent-view#what-deleting-a-session-removes)中的拒绝消息会报告 hook 的结束方式(例如 `exited 1`),引用其 stderr 的开头部分,并说明再次删除该会话是否仍会移除该目录。3336* 如果您正在删除后台会话,该会话也会保留。[agent view](/docs/zh-CN/agent-view#what-deleting-a-session-removes) 中的拒绝消息会报告 hook 的结束方式(例如 `exited 1`),引用其 stderr 的开头部分,并说明再次删除该会话是否仍会删除该目录。
3335 3337
3336<h3 id="precompact">3338<h3 id="precompact">
3337 PreCompact3339 PreCompact
3339 3341
3340在 Claude Code 即将执行压缩操作之前运行。3342在 Claude Code 即将执行压缩操作之前运行。
3341 3343
3342匹配器值表示压缩是手动触发还是自动触发:3344匹配器值表示压缩是手动触发还是自动触发的:
3343 3345
3344| 匹配器 | 触发时机 |3346| 匹配器 | 触发时机 |
3345| :- | :- |3347| :- | :- |
3346| `manual` | `/compact` |3348| `manual` | `/compact` |
3347| `auto` | 对话达到[自动压缩窗口](/docs/zh-CN/model-config#set-the-auto-compact-window)时的自动压缩 |3349| `auto` | 对话达到[自动压缩窗口](/docs/zh-CN/model-config#set-the-auto-compact-window)时的自动压缩 |
3348 3350
3349以代码 2 退出可阻止压缩。对于手动 `/compact`,stderr 消息会显示给用户。您也可以通过返回带有 `"decision": "block"` 的 JSON 来阻止。3351以退出码 2 退出可阻止压缩。对于手动 `/compact`,stderr 消息会显示给用户。您也可以通过返回包含 `"decision": "block"` 的 JSON 来阻止。
3350 3352
3351阻止自动压缩的效果取决于其触发时机。如果压缩是在达到上下文限制之前主动触发的,Claude Code 会跳过压缩,对话在未压缩的情况下继续。如果压缩是为了从 API 已返回的上下文限制错误中恢复而触发的,底层错误会显示出来,当前请求失败。3353阻止自动压缩的效果取决于其触发时机。如果压缩是在达到上下文限制之前主动触发的,Claude Code 会跳过它,对话在未压缩的情况下继续。如果压缩是为了从 API 已返回的上下文限制错误中恢复而触发的,则底层错误会显示出来,当前请求失败。
3352 3354
3353Claude Code 会丢弃 PreCompact hook 的 `systemMessage` 和 `continue` 字段。3355Claude Code 会丢弃 PreCompact hook 的 `systemMessage` 和 `continue` 字段。
3354 3356
3356 PreCompact 输入3358 PreCompact 输入
3357</h4>3359</h4>
3358 3360
3359除[通用输入字段](#common-input-fields)外,PreCompact hook 还会接收 `trigger` 和 `custom_instructions`。对于 `manual`,`custom_instructions` 包含用户传给 `/compact` 的内容,用户未传入任何内容时为 `null`。对于 `auto`,`custom_instructions` 为 `null`。3361除[通用输入字段](#common-input-fields)外,PreCompact hook 还会接收 `trigger` 和 `custom_instructions`。对于 `manual`,`custom_instructions` 包含用户传入 `/compact` 的内容,未传入任何内容时为 `null`。对于 `auto`,`custom_instructions` 为 `null`。
3360 3362
3361```json theme={null}3363```json theme={null}
3362{3364{
3373 PostCompact3375 PostCompact
3374</h3>3376</h3>
3375 3377
3376在 Claude Code 完成压缩操作后运行。使用此事件对压缩后的新状态作出响应,例如记录生成的摘要或更新外部状态。Claude Code 会丢弃 PostCompact hook 的 `systemMessage` 和 `continue` 字段。3378在 Claude Code 完成压缩操作后运行。使用此事件对新的压缩状态作出响应,例如记录生成的摘要或更新外部状态。Claude Code 会丢弃 PostCompact hook 的 `systemMessage` 和 `continue` 字段。
3377 3379
3378适用与 `PreCompact` 相同的匹配器值:3380适用与 `PreCompact` 相同的匹配器值:
3379 3381
3405 PreModelSwitch3407 PreModelSwitch
3406</h3>3408</h3>
3407 3409
3408在 Claude Code 应用您或客户端请求的模型切换之前运行。可用于阻止切换、要求确认,或在切换发生前显示切换的成本。3410在 Claude Code 应用由您或客户端请求的模型切换之前运行。可用于阻止切换、要求确认,或在切换发生前显示切换的成本。
3409 3411
3410PreModelSwitch 需要 Claude Code v2.1.251 或更高版本。Claude Code 会针对以下请求运行它:3412PreModelSwitch 需要 Claude Code v2.1.251 或更高版本。Claude Code 会针对以下请求运行它:
3411 3413
3412* `/model <name>` 和 `/model` 选择器3414* `/model <name>` 和 `/model` 选择器
3413* `Option+P` 或 `Alt+P` 模型选择器3415* `Option+P` 或 `Alt+P` 模型选择器
3414* `/config` 中的 Model 设置3416* `/config` 中的 Model 设置
3415* 启用[快速模式](/docs/zh-CN/fast-mode)且这会改变会话的模型时3417* 启用[快速模式](/docs/zh-CN/fast-mode)且这会更改会话的模型时
3416* 来自 [Agent SDK](/docs/zh-CN/agent-sdk/typescript#query-object) 宿主或 [Remote Control](/docs/zh-CN/remote-control) 的 `set_model` 请求,或 `apply_flag_settings` 请求中的模型更改3418* 来自 [Agent SDK](/docs/zh-CN/agent-sdk/typescript#query-object) 宿主或 [Remote Control](/docs/zh-CN/remote-control) 的 `set_model` 请求,或 `apply_flag_settings` 请求中的模型更改
3417 3419
3418对于 Claude Code 自行进行的切换,例如[自动模型回退](/docs/zh-CN/model-config#automatic-model-fallback)或在您恢复会话时恢复模型,Claude Code 不会运行 PreModelSwitch hook。这些更改只会触发 [PostModelSwitch](#postmodelswitch)。3420对于 Claude Code 自行执行的切换,例如[自动模型回退](/docs/zh-CN/model-config#automatic-model-fallback)或恢复会话时还原模型,Claude Code 不会运行 PreModelSwitch hook。这些更改只会触发 [PostModelSwitch](#postmodelswitch)。
3419 3421
3420Claude Code 会将匹配器与会话要切换到的模型的规范名称进行比较,忽略任何 `[1m]` 后缀。别名(如 `opus`)、带日期的模型 ID 以及特定于提供商的 ID(如 Amazon Bedrock 模型 ID)都会匹配它们所解析到的同一个规范名称,因此 `claude-opus-5` 涵盖 Opus 5 的所有写法。3422Claude Code 会将匹配器与会话要切换到的模型的规范名称进行比较,并忽略任何 `[1m]` 后缀。别名(如 `opus`)、带日期的模型 ID 以及特定提供商的 ID(如 Amazon Bedrock 模型 ID)都会匹配它们所解析到的同一个规范名称,因此 `claude-opus-5` 涵盖 Opus 5 的所有写法。
3421 3423
3422当 Claude Code 无法确定目标的规范名称时,例如只有您的 [LLM 网关](/docs/zh-CN/llm-gateway)才知道的自定义模型 ID,它会运行所有 PreModelSwitch hook,无论匹配器如何。因此,执行阻止的 hook 应检查其输入中的 `to_model`,而不是仅依赖匹配器。3424当 Claude Code 无法确定目标的规范名称时(例如只有您的 [LLM 网关](/docs/zh-CN/llm-gateway)知道的自定义模型 ID),它会运行所有 PreModelSwitch hook,无论匹配器为何。因此,用于阻止的 hook 应检查其输入中的 `to_model`,而不是仅依赖匹配器。
3423 3425
3424匹配器可以写为确切名称、以 `|` 分隔的列表(如 `claude-opus-4-6|claude-opus-5`),或正则表达式(如 `.*opus.*`)。以下示例使用确切名称匹配器,同时检查 hook 输入中的 `to_model`,因此它会通过以代码 2 退出来拒绝切换到 Opus 4.6,并允许切换到任何其他目标:3426匹配器可以写成精确名称、以 `|` 分隔的列表(如 `claude-opus-4-6|claude-opus-5`),或正则表达式(如 `.*opus.*`)。此示例使用精确名称匹配器,同时检查 hook 输入中的 `to_model`,因此它通过以退出码 2 退出来拒绝切换到 Opus 4.6,并允许切换到任何其他目标:
3425 3427
3426<Tabs>3428<Tabs>
3427 <Tab title="macOS/Linux">3429 <Tab title="macOS/Linux">
3474 }3476 }
3475 ```3477 ```
3476 3478
3477 将以下脚本保存到项目中的 `.claude/hooks/block-opus-46.ps1`:3479 将此脚本保存到项目中的 `.claude/hooks/block-opus-46.ps1`:
3478 3480
3479 ```powershell theme={null}3481 ```powershell theme={null}
3480 $hookInput = [Console]::In.ReadToEnd() | ConvertFrom-Json3482 $hookInput = [Console]::In.ReadToEnd() | ConvertFrom-Json
3487 </Tab>3489 </Tab>
3488</Tabs>3490</Tabs>
3489 3491
3490要确认 hook 是否生效,请在运行其他模型的会话中运行 `/model claude-opus-4-6`。Claude Code 会保留当前模型,并报告 PreModelSwitch hook 阻止了切换,同时将您的消息作为原因。3492要确认 hook 是否生效,请在运行其他模型的会话中运行 `/model claude-opus-4-6`。Claude Code 会保留当前模型,并报告 PreModelSwitch hook 阻止了切换,以您的消息作为原因。
3491 3493
3492<h4 id="premodelswitch-input">3494<h4 id="premodelswitch-input">
3493 PreModelSwitch 输入3495 PreModelSwitch 输入
3494</h4>3496</h4>
3495 3497
3496除[通用输入字段](#common-input-fields)外,PreModelSwitch hook 还会接收下表中的字段。最后五个字段描述将对话重新发送给新模型的成本,以便 hook 在切换发生之前显示该数值。3498除[通用输入字段](#common-input-fields)外,PreModelSwitch hook 还会接收下表中的字段。最后五个字段描述将对话重新发送到新模型的成本,以便 hook 可以在切换发生之前显示该数字。
3497 3499
3498| 字段 | 类型 | 描述 |3500| 字段 | 类型 | 描述 |
3499| :- | :- | :- |3501| :- | :- | :- |
3500| `from_model` | string | 切换前的模型 ID |3502| `from_model` | string | 切换前的模型 ID |
3501| `to_model` | string | 切换后的模型 ID。匹配器与该模型的规范名称进行比较 |3503| `to_model` | string | 切换后的模型 ID。匹配器会与此模型的规范名称进行比较 |
3502| `requested_model` | string 或 `null` | 请求中指定的模型:别名(如 `opus`)、完整模型 ID,或在请求默认模型时为 `null` |3504| `requested_model` | string 或 `null` | 请求中指定的模型:别名(如 `opus`)、完整模型 ID,或在请求默认模型时为 `null` |
3503| `source` | string | 请求的来源:`"command"` 表示 `/model <name>`、`/config` 中的 Model 设置或启用快速模式;`"picker"` 表示模型选择器;`"sdk"` 表示来自 Agent SDK 宿主或 Remote Control 的 `set_model` 请求,或 `apply_flag_settings` 请求中的模型更改 |3505| `source` | string | 请求的来源:`"command"` 表示 `/model <name>`、`/config` 中的 Model 设置或启用快速模式;`"picker"` 表示模型选择器;`"sdk"` 表示来自 Agent SDK 宿主或 Remote Control 的 `set_model` 请求,或 `apply_flag_settings` 请求中的模型更改 |
3504| `context_tokens` | number | 下一个请求作为提示词重新发送的 token 数:主对话中最后一个响应的输入、缓存读取、缓存创建和输出 token 之和。在第一个响应之前为 `0` |3506| `context_tokens` | number | 下一个请求作为提示词重新发送的 token 数:主对话中最后一个响应的输入、缓存读取、缓存创建和输出 token 的总和。第一个响应之前为 `0` |
3505| `prompt_cache_warm` | boolean | 当前模型的提示缓存是否可能仍处于预热状态,即切换会使其失效 |3507| `prompt_cache_warm` | boolean | 当前模型的提示缓存是否可能仍处于热状态,即切换会使其失效 |
3506| `cache_ttl` | string | Claude Code 为此会话请求的[提示缓存生命周期](/docs/zh-CN/prompt-caching#cache-lifetime):`"5m"` 或 `"1h"` |3508| `cache_ttl` | string | Claude Code 为此会话请求的[提示缓存生存期](/docs/zh-CN/prompt-caching#cache-lifetime):`"5m"` 或 `"1h"` |
3507| `estimated_cache_write_usd` | number | 以 `cache_ttl` 费率在 `to_model` 上将 `context_tokens` 写入提示缓存的估计成本(美元),不包括下一个响应。服务器可能无需重新缓存整个上下文,因此请将其视为估计值 |3509| `estimated_cache_write_usd` | number | 按 `cache_ttl` 费率在 `to_model` 上将 `context_tokens` 写入提示缓存的估计成本(美元),不包括下一个响应。服务器可能不需要重新缓存整个上下文,因此请将其视为估计值 |
3508| `pricing` | string | Claude Code 为 `estimated_cache_write_usd` 定价的方式:`"configured"` 表示按您的组织已配置的自有费率,`"catalog"` 表示按标价,`"default"` 表示 `to_model` 没有已知价格,Claude Code 采用了默认费率 |3510| `pricing` | string | Claude Code 如何为 `estimated_cache_write_usd` 定价:`"configured"` 表示在您的组织已配置费率时按其自有费率计算,`"catalog"` 表示按标价计算,`"default"` 表示 `to_model` 没有已知价格、Claude Code 采用了默认费率 |
3509 3511
3510以下示例展示在运行 Sonnet 5 的会话中执行 `/model opus` 时的输入:3512此示例显示在运行 Sonnet 5 的会话中执行 `/model opus` 时的输入:
3511 3513
3512```json theme={null}3514```json theme={null}
3513{3515{
3531 PreModelSwitch 决策控制3533 PreModelSwitch 决策控制
3532</h4>3534</h4>
3533 3535
3534`PreModelSwitch` hook 可以取消切换、请用户确认切换,或允许切换继续。退出码 2 或顶层的 `decision: "block"` 会取消切换。3536`PreModelSwitch` hook 可以取消切换、请用户确认,或让切换继续进行。退出码 2 或顶层 `decision: "block"` 会取消切换。
3535 3537
3536如需更精细的控制,请在 `hookSpecificOutput` 对象中返回 `permissionDecision` 和 `permissionDecisionReason`,与 [PreToolUse](#pretooluse-decision-control) 相同。`PreModelSwitch` 接受 `"allow"`、`"deny"` 和 `"ask"`。它不接受 `"defer"`、`updatedInput` 或 `additionalContext`。下表描述这两个字段:3538如需更精细的控制,请在 `hookSpecificOutput` 对象中返回 `permissionDecision` 和 `permissionDecisionReason`,与 [PreToolUse](#pretooluse-decision-control) 相同。`PreModelSwitch` 接受 `"allow"`、`"deny"` 和 `"ask"`,不接受 `"defer"`、`updatedInput` 或 `additionalContext`。下表描述了这两个字段:
3537 3539
3538| 字段 | 描述 |3540| 字段 | 描述 |
3539| :- | :- |3541| :- | :- |
3540| `permissionDecision` | `"allow"` 继续切换,并跳过[提示缓存处于预热状态时 Claude Code 显示的确认](/docs/zh-CN/prompt-caching#switching-models)。`"deny"` 取消切换。`"ask"` 提示用户进行确认 |3542| `permissionDecision` | `"allow"` 继续切换,并跳过[提示缓存处于热状态时 Claude Code 显示的确认](/docs/zh-CN/prompt-caching#switching-models)。`"deny"` 取消切换。`"ask"` 提示用户确认 |
3541| `permissionDecisionReason` | 对于 `"deny"`,作为切换被阻止的原因显示给用户,或作为 `set_model` 请求的错误返回。对于 `"ask"`,显示在确认提示中。对于 `"allow"` 则被忽略 |3543| `permissionDecisionReason` | 对于 `"deny"`,作为切换被阻止的原因显示给用户,或作为 `set_model` 请求的错误返回。对于 `"ask"`,显示在确认提示中。对于 `"allow"` 则忽略 |
3542 3544
3543只有交互式会话中的 `/model` 才能显示 `"ask"` 提示。在其他所有使用入口上,包括使用 `-p` 标志的非交互模式、`/config` 和 `set_model` 请求,Claude Code 都会将 `"ask"` 视为拒绝。3545只有交互式会话中的 `/model` 才能显示 `"ask"` 提示。在其他所有使用入口上,包括使用 `-p` 标志的非交互模式、`/config` 和 `set_model` 请求,Claude Code 都会将 `"ask"` 视为拒绝。
3544 3546
3545以下示例请用户确认,并引用 `context_tokens` 中的 token 数:3547此示例请用户确认,并引用 `context_tokens` 中的 token 数:
3546 3548
3547```json theme={null}3549```json theme={null}
3548{3550{
3556 3558
3557当多个 PreModelSwitch hook 返回不同的决策时,优先级为 `deny` > `ask` > `allow`。3559当多个 PreModelSwitch hook 返回不同的决策时,优先级为 `deny` > `ask` > `allow`。
3558 3560
3559无论决策如何,Claude Code 都会向用户显示 hook 返回的任何 `systemMessage`,因此成本报告 hook 可以返回 `{"systemMessage": "..."}` 并以 0 退出。3561无论决策如何,Claude Code 都会向用户显示您的 hook 返回的任何 `systemMessage`,因此用于报告成本的 hook 可以返回 `{"systemMessage": "..."}` 并以 0 退出。
3560 3562
3561在超时前未响应的 PreModelSwitch hook 会阻止切换。相比之下,在 [PreToolUse](#timeouts) 上,超时的命令 hook 会让工具调用继续。此事件的默认超时时间为 30 秒。`PreModelSwitch` 仅运行 `command`、`http` 和 `mcp_tool` hook,因此 `prompt` 和 `agent` 的默认值不适用。3563未在超时时间内响应的 PreModelSwitch hook 会阻止切换。相比之下,在 [PreToolUse](#timeouts) 上,超时的命令 hook 会让工具调用继续进行。此事件的默认超时时间为 30 秒。`PreModelSwitch` 仅运行 `command`、`http` 和 `mcp_tool` hook,因此 `prompt` 和 `agent` 的默认值不适用。
3562 3564
3563以 0 或 2 以外的代码退出且未打印 JSON 决策的 hook 不会阻止切换:Claude Code 会显示其 stderr 并应用切换,如[其他退出码](#other-exit-codes)中所述。3565以 0 或 2 以外的代码退出且未打印 JSON 决策的 hook 不会阻止切换:Claude Code 会显示其 stderr 并应用切换,如[其他退出码](#other-exit-codes)中所述。
3564 3566
3566 PostModelSwitch3568 PostModelSwitch
3567</h3>3569</h3>
3568 3570
3569在会话的模型更改后运行。可用于向 Claude 提供特定于模型的指导,而无需编辑每个 CLAUDE.md,例如适用于某些模型的组织级指令。3571在会话的模型更改后运行。可用于在不编辑每个 CLAUDE.md 的情况下为 Claude 提供特定于模型的指导,例如适用于某些模型的全组织范围指令。
3570 3572
3571PostModelSwitch 需要 Claude Code v2.1.251 或更高版本。它无法阻止,因为模型已经更改。Claude Code 会在以下任何更改后运行 PostModelSwitch hook:3573PostModelSwitch 需要 Claude Code v2.1.251 或更高版本。它无法阻止,因为模型已经更改。Claude Code 会在以下任一更改之后运行 PostModelSwitch hook:
3572 3574
3573* 您或客户端请求的切换3575* 您或客户端请求的切换
3574* [自动模型回退](/docs/zh-CN/model-config#automatic-model-fallback),它会更改会话的模型3576* [自动模型回退](/docs/zh-CN/model-config#automatic-model-fallback),这会更改会话的模型
3575* [`opusplan`](/docs/zh-CN/model-config#opusplan-model-setting) 等设置在进入或退出计划模式时3577* 诸如 [`opusplan`](/docs/zh-CN/model-config#opusplan-model-setting) 之类的设置进入或离开计划模式
3576* 您恢复会话时 Claude Code 恢复模型3578* 恢复会话时 Claude Code 还原模型
3577 3579
3578当[备用模型链](/docs/zh-CN/model-config#fallback-model-chains)中的模型为某一轮次提供服务时,Claude Code 不会运行 PostModelSwitch hook,因为这种替换只持续一轮,会话的模型保持不变。3580当[备用模型链](/docs/zh-CN/model-config#fallback-model-chains)中的某个模型服务于某一轮次时,Claude Code 不会运行 PostModelSwitch hook,因为该替换仅持续一轮,且不会更改会话的模型。
3579 3581
3580匹配器遵循与 [PreModelSwitch](#premodelswitch) 相同的规则:Claude Code 将其与会话所切换到的模型的规范名称进行比较。3582匹配器遵循与 [PreModelSwitch](#premodelswitch) 相同的规则:Claude Code 会将其与会话切换到的模型的规范名称进行比较。
3581 3583
3582以下示例在会话的模型更改为任何 Opus 模型时添加指导:3584此示例在会话的模型更改为任何 Opus 模型时添加指导:
3583 3585
3584```json theme={null}3586```json theme={null}
3585{3587{
3599}3601}
3600```3602```
3601 3603
3602要确认 hook 是否生效,请在运行其他模型的会话中切换到 Opus 模型,例如在 Sonnet 会话中运行 `/model opus`,然后询问 Claude 它对当前模型有哪些指导。3604要确认 hook 是否生效,请在运行其他模型的会话中切换到 Opus 模型,例如在 Sonnet 会话中运行 `/model opus`,然后询问 Claude 它对当前模型有什么指导。
3603 3605
3604<h4 id="postmodelswitch-input">3606<h4 id="postmodelswitch-input">
3605 PostModelSwitch 输入3607 PostModelSwitch 输入
3606</h4>3608</h4>
3607 3609
3608PostModelSwitch hook 接收与 [PreModelSwitch](#premodelswitch-input) 相同的字段,其中 `hook_event_name` 设置为 `"PostModelSwitch"`,并增加两个 `source` 值:`"auto"` 表示自动回退或 Claude Code 自行进行的其他更改,`"resume"` 表示您恢复会话时恢复的模型。3610PostModelSwitch hook 接收与 [PreModelSwitch](#premodelswitch-input) 相同的字段,其中 `hook_event_name` 设置为 `"PostModelSwitch"`,并且 `source` 多两个值:`"auto"` 表示自动回退或 Claude Code 自行做出的其他更改,`"resume"` 表示恢复会话时还原的模型。
3609 3611
3610当 `source` 为 `"auto"` 时,`requested_model` 为 `null`。当 `source` 为 `"resume"` 时,它是 Claude Code 恢复的已保存模型设置。3612当 `source` 为 `"auto"` 时,`requested_model` 为 `null`。当 `source` 为 `"resume"` 时,它是 Claude Code 还原的已保存模型设置。
3611 3613
3612<h4 id="postmodelswitch-decision-control">3614<h4 id="postmodelswitch-decision-control">
3613 PostModelSwitch 决策控制3615 PostModelSwitch 决策控制
3614</h4>3616</h4>
3615 3617
3616Claude Code 会在退出码为 0 时获取 hook 的[纯文本 stdout](#exit-code-0),或获取 JSON 输出中的 `additionalContext`,并在切换后随下一个请求将其传递给 Claude。除所有 hook 均可用的 [JSON 输出字段](#json-output)外,您还可以返回:3618Claude Code 会在退出码为 0 时获取 hook 的[纯文本 stdout](#exit-code-0),或从 JSON 输出中获取 `additionalContext`,并在切换后的下一个请求中将其传递给 Claude。除所有 hook 均可使用的 [JSON 输出字段](#json-output)外,您还可以返回:
3617 3619
3618| 字段 | 描述 |3620| 字段 | 描述 |
3619| :- | :- |3621| :- | :- |
3620| `additionalContext` | 随下一个请求添加到 Claude 上下文中的字符串。请参阅[为 Claude 添加上下文](#add-context-for-claude) |3622| `additionalContext` | 随下一个请求添加到 Claude 上下文中的字符串。请参阅[为 Claude 添加上下文](#add-context-for-claude) |
3621 3623
3622如果在您发送下一个提示词后五秒内 hook 仍未完成,Claude Code 会在不含该输出的情况下发送该请求,并改为将输出附加到再下一个请求。如果模型在下一个请求之前更改了多次,Claude Code 只会传递最后一次切换的目标模型对应的输出。3624如果在您发送下一个提示词后五秒内 hook 仍未完成,Claude Code 会在不附带该输出的情况下发送该请求,并将输出附加到后续请求中。如果在下一个请求之前模型更改了多次,Claude Code 只会传递针对最后一次切换目标模型的输出。
3623 3625
3624<h3 id="sessionend">3626<h3 id="sessionend">
3625 SessionEnd3627 SessionEnd
3635| `clear` | 使用 `/clear` 命令清除了会话 |3637| `clear` | 使用 `/clear` 命令清除了会话 |
3636| `resume` | 通过交互式 `/resume` 切换了会话 |3638| `resume` | 通过交互式 `/resume` 切换了会话 |
3637| `logout` | 用户已注销 |3639| `logout` | 用户已注销 |
3638| `prompt_input_exit` | 用户在输入框可见时退出 |3640| `prompt_input_exit` | 用户在提示词输入可见时退出 |
3639| `other` | 其他退出原因 |3641| `other` | 其他退出原因 |
3640| `bypass_permissions_disabled` | 已在 v2.1.234 中移除;Claude Code 不再发送此值。请将其从您的 `SessionEnd` 匹配器中删除 |3642| `bypass_permissions_disabled` | 已在 v2.1.234 中移除;Claude Code 不会发送此值。请将其从您的 `SessionEnd` 匹配器中删除 |
3641 3643
3642<h4 id="sessionend-input">3644<h4 id="sessionend-input">
3643 SessionEnd 输入3645 SessionEnd 输入
3644</h4>3646</h4>
3645 3647
3646除[通用输入字段](#common-input-fields)外,SessionEnd hook 还会接收表示会话结束原因的 `reason` 字段。有关所有取值,请参阅上方的[原因表](#sessionend)。3648除[通用输入字段](#common-input-fields)外,SessionEnd hook 还会接收一个 `reason` 字段,表示会话结束的原因。有关所有值,请参阅上方的[原因表](#sessionend)。
3647 3649
3648```json theme={null}3650```json theme={null}
3649{3651{
3657 3659
3658SessionEnd hook 没有决策控制。它们无法阻止会话终止,但可以执行清理任务。Claude Code 会丢弃它们的 [JSON 输出字段](#json-output),例如 `systemMessage`。3660SessionEnd hook 没有决策控制。它们无法阻止会话终止,但可以执行清理任务。Claude Code 会丢弃它们的 [JSON 输出字段](#json-output),例如 `systemMessage`。
3659 3661
3660SessionEnd hook 的默认超时时间为 1.5 秒。它适用于您退出、运行 `/clear` 或通过交互式 `/resume` 切换会话时。您可以通过两种方式为 hook 提供更多时间:3662SessionEnd hook 的默认超时时间为 1.5 秒。该超时适用于您退出、运行 `/clear` 或通过交互式 `/resume` 切换会话时。您可以通过两种方式为 hook 提供更多时间:
3661 3663
3662* **单个 hook 的 `timeout`**:在该 hook 的配置中设置 `timeout`。总体时间预算会自动提高,以匹配您的设置文件中最大的单个 hook `timeout`,最长 60 秒。如果以这种方式提高预算,未设置自身 `timeout` 的 hook 仍保持默认值。在插件提供的 hook 上设置的超时时间不会提高预算。3664* **每个 hook 的 `timeout`**:在该 hook 的配置中设置 `timeout`。总体时间预算会自动提高,以匹配您设置文件中最高的单个 hook `timeout`,最长 60 秒。如果以这种方式提高预算,没有自己 `timeout` 的 hook 仍保持默认值。在插件提供的 hook 上设置的超时时间不会提高预算。
3663* **`CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS`**:以毫秒为单位设置此环境变量,以显式覆盖预算。您设置的值也会成为每个未设置自身 `timeout` 的 hook 的超时时间。3665* **`CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS`**:以毫秒为单位设置此环境变量,以显式覆盖预算。您设置的值也会成为每个没有自己 `timeout` 的 hook 的超时时间。
3664 3666
3665以下示例将预算设置为 5 秒:3667此示例将预算设置为 5 秒:
3666 3668
3667```bash theme={null}3669```bash theme={null}
3668CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude3670CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude
3669```3671```
3670 3672
3671在 v2.1.268 之前,`CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS` 只会提高总体预算,未设置自身 `timeout` 的 hook 仍会在 1.5 秒后被取消。3673在 v2.1.268 之前,`CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS` 只会提高总体预算,没有自己 `timeout` 的 hook 仍会在 1.5 秒后被取消。
3672 3674
3673<h3 id="elicitation">3675<h3 id="elicitation">
3674 Elicitation3676 Elicitation
3675</h3>3677</h3>
3676 3678
3677在 MCP 服务器于任务执行过程中请求用户输入时运行。默认情况下,Claude Code 会显示一个交互式对话框供用户响应。hook 可以拦截此请求并以编程方式响应,完全跳过对话框。3679在 MCP 服务器于任务中途请求用户输入时运行。默认情况下,Claude Code 会显示一个交互式对话框供用户响应。hook 可以拦截此请求并以编程方式响应,完全跳过对话框。
3680
3681有关包含设置条目和脚本的完整 hook,请参阅[通过脚本回答表单请求](#answer-a-form-request-from-a-script)。
3678 3682
3679matcher 字段与 MCP 服务器名称进行匹配。3683matcher 字段与 MCP 服务器名称进行匹配。
3680 3684
3704}3708}
3705```3709```
3706 3710
3707对于 URL 模式的 elicitation(用于基于浏览器的身份验证):3711对于用于基于浏览器的身份验证的 URL 模式 elicitation:
3708 3712
3709```json theme={null}3713```json theme={null}
3710{3714{
3723 Elicitation 输出3727 Elicitation 输出
3724</h4>3728</h4>
3725 3729
3726要在不显示对话框的情况下以编程方式响应,请返回带有 `hookSpecificOutput` 的 JSON 对象:3730Elicitation hook 可以代替用户回答请求、拒绝或取消请求,或将其交给对话框处理。要回答、拒绝或取消,请以 0 退出并打印一个带有 `action` 的 `hookSpecificOutput` 对象。服务器会收到您的回答,并且不会出现对话框。下表的每一行显示某一结果应返回的内容以及 MCP 服务器收到的内容:
3731
3732| 目的 | 返回 | 服务器收到 |
3733| :- | :- | :- |
3734| 代替用户回答 | `"action": "accept"`,并在 `content` 中提供表单字段值 | `accept` 以及您的 `content` |
3735| 拒绝请求 | `"action": "decline"` | `decline` |
3736| 取消请求 | `"action": "cancel"` | `cancel` |
3737| 将请求交给用户 | 无输出,退出码为 0 | 用户在[对话框](/docs/zh-CN/mcp#respond-to-mcp-elicitation-requests)中的回答 |
3738
3739此输出回答了 [Elicitation 输入](#elicitation-input)中所示的表单模式请求。`content` 中的键是该请求的 `requested_schema` 中的属性名称:
3727 3740
3728```json theme={null}3741```json theme={null}
3729{3742{
3737}3750}
3738```3751```
3739 3752
3740| 字段 | 取值 | 描述 |3753此输出拒绝请求:
3741| :- | :- | :- |3754
3742| `action` | `accept`、`decline`、`cancel` | 接受、拒绝还是取消该请求 |3755```json theme={null}
3743| `content` | object | 要提交的表单字段值。仅在 `action` 为 `accept` 时使用 |3756{
3757 "hookSpecificOutput": {
3758 "hookEventName": "Elicitation",
3759 "action": "decline"
3760 }
3761}
3762```
3763
3764在对话框中,选择 **Decline** 会发送 `decline`,按 `Esc` 会发送 `cancel`,因此请返回您希望服务器看到的那个值。
3765
3766对于 URL 模式请求,返回 `accept` 的 hook 会跳过对话框,因此该 URL 永远不会打开。
3767
3768无论您返回哪个 `action`,Claude Code 都会丢弃 Elicitation hook JSON 输出中的 `reason`、`systemMessage` 和 `continue`。
3769
3770<h4 id="other-ways-to-decline-an-elicitation">
3771 拒绝 elicitation 的其他方式
3772</h4>
3773
3774您的 hook 还可以通过以下方式拒绝。服务器收到的 `decline` 与 `"action": "decline"` 相同:
3775
3776* **以退出码 2 退出**:Claude Code 会忽略同一 hook 打印的 `hookSpecificOutput`
3777* **打印顶层 `"decision": "block"`**:该阻止会覆盖同一输出中的 `action`
3778
3779当多个 hook 匹配同一请求时,其中一个 hook 的拒绝会覆盖另一个 hook 的 `accept` 或 `cancel`。
3780
3781此脚本拒绝 URL 模式请求,并将表单请求交给对话框处理:
3782
3783```bash theme={null}
3784#!/bin/bash
3785if [ "$(jq -r '.mode')" = "url" ]; then
3786 exit 2
3787fi
3788```
3789
3790用户和服务器都看不到您的 hook 拒绝的原因,因为 Claude Code 不会显示您的 stderr 或 `reason`。
3744 3791
3745退出码 2 会拒绝该 elicitation。Claude Code 不会在任何地方显示您的 stderr 消息。3792从 v2.1.105 起直到 v2.1.284 中的修复之前,Claude Code 会忽略来自 `Elicitation` 和 `ElicitationResult` hook 的顶层 `decision`。
3746 3793
3747Claude Code 会处理 Elicitation hook JSON 输出中的 `hookSpecificOutput`,并丢弃 `systemMessage` 和 `continue`。3794<h4 id="answer-a-form-request-from-a-script">
3795 通过脚本回答表单请求
3796</h4>
3797
3798此示例代替用户回答一个反复出现的问题。名为 `issue-tracker` 的 MCP 服务器在表单中请求项目键,hook 会填入 `DOCS`。当 `project_key` 是表单的唯一字段时,脚本会接受。对于任何其他请求,它不打印任何内容,因此会显示对话框。
3799
3800<Tabs>
3801 <Tab title="macOS/Linux">
3802 在您的设置文件中为该事件注册一个命令 hook,并将服务器名称作为匹配器:
3803
3804 ```json theme={null}
3805 {
3806 "hooks": {
3807 "Elicitation": [
3808 {
3809 "matcher": "issue-tracker",
3810 "hooks": [
3811 {
3812 "type": "command",
3813 "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.sh",
3814 "args": []
3815 }
3816 ]
3817 }
3818 ]
3819 }
3820 }
3821 ```
3822
3823 将此脚本保存到项目中的 `.claude/hooks/answer-project-key.sh`,并使用 `chmod +x` 使其可执行:
3824
3825 ```bash theme={null}
3826 #!/bin/bash
3827 input=$(cat)
3828 fields=$(jq -c '.requested_schema.properties // {} | keys' <<<"$input")
3829
3830 if [ "$fields" = '["project_key"]' ]; then
3831 jq -n '{hookSpecificOutput: {hookEventName: "Elicitation", action: "accept", content: {project_key: "DOCS"}}}'
3832 fi
3833 ```
3834 </Tab>
3835
3836 <Tab title="Windows (PowerShell)">
3837 注册一个通过 PowerShell 运行脚本的命令 hook,并将服务器名称作为匹配器:
3838
3839 ```json theme={null}
3840 {
3841 "hooks": {
3842 "Elicitation": [
3843 {
3844 "matcher": "issue-tracker",
3845 "hooks": [
3846 {
3847 "type": "command",
3848 "command": "powershell.exe",
3849 "args": [
3850 "-NoProfile",
3851 "-ExecutionPolicy",
3852 "Bypass",
3853 "-File",
3854 "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.ps1"
3855 ]
3856 }
3857 ]
3858 }
3859 ]
3860 }
3861 }
3862 ```
3863
3864 将此脚本保存到项目中的 `.claude/hooks/answer-project-key.ps1`:
3865
3866 ```powershell theme={null}
3867 $request = [Console]::In.ReadToEnd() | ConvertFrom-Json
3868 $fields = @($request.requested_schema.properties.PSObject.Properties.Name)
3869
3870 if ($fields.Count -eq 1 -and $fields[0] -eq 'project_key') {
3871 @{
3872 hookSpecificOutput = @{
3873 hookEventName = "Elicitation"
3874 action = "accept"
3875 content = @{ project_key = "DOCS" }
3876 }
3877 } | ConvertTo-Json -Depth 3
3878 }
3879 ```
3880 </Tab>
3881</Tabs>
3882
3883要确认 hook 是否生效,请使用 `claude --debug` 启动 Claude Code,并给 Claude 一个会让服务器请求项目键的任务。不会出现对话框,并且[调试日志](#debug-hooks)中会有一行以 `Elicitation resolved by hook: {"action":"accept","content":{"project_key":"DOCS"}}` 结尾。
3748 3884
3749<h3 id="elicitationresult">3885<h3 id="elicitationresult">
3750 ElicitationResult3886 ElicitationResult
3752 3888
3753在用户响应 MCP elicitation 后运行。hook 可以在响应发回 MCP 服务器之前观察、修改或阻止该响应。3889在用户响应 MCP elicitation 后运行。hook 可以在响应发回 MCP 服务器之前观察、修改或阻止该响应。
3754 3890
3891当 [Elicitation](#elicitation) hook 回答了请求时,Claude Code 会将该回答发送给服务器,而不运行 ElicitationResult hook。
3892
3755matcher 字段与 MCP 服务器名称进行匹配。3893matcher 字段与 MCP 服务器名称进行匹配。
3756 3894
3757<h4 id="elicitationresult-input">3895<h4 id="elicitationresult-input">
3769 "mcp_server_name": "my-mcp-server",3907 "mcp_server_name": "my-mcp-server",
3770 "action": "accept",3908 "action": "accept",
3771 "content": { "username": "alice" },3909 "content": { "username": "alice" },
3772 "mode": "form",3910 "mode": "form"
3773 "elicitation_id": "elicit-123"
3774}3911}
3775```3912```
3776 3913
3778 ElicitationResult 输出3915 ElicitationResult 输出
3779</h4>3916</h4>
3780 3917
3781要覆盖用户的响应,请返回带有 `hookSpecificOutput` 的 JSON 对象:3918ElicitationResult hook 可以让用户的响应通过、更改其值或阻止它。要更改或阻止响应,请以 0 退出并打印一个带有 `action` 的 `hookSpecificOutput` 对象。下表的每一行显示某一结果应返回的内容以及 MCP 服务器收到的内容:
3919
3920| 目的 | 返回 | 服务器收到 |
3921| :- | :- | :- |
3922| 让响应通过 | 无输出,退出码为 0 | 用户的响应,未经更改 |
3923| 更改提交的值 | `"action": "accept"`,并在 `content` 中提供新值 | `accept` 以及您的 `content`,替代用户的值 |
3924| 阻止响应 | `"action": "decline"` | `decline`,不含用户的值 |
3925| 取消请求 | `"action": "cancel"` | `cancel`,以及用户提交的值。要隐去这些值,请返回 `"decline"` |
3926
3927此输出更改了 [ElicitationResult 输入](#elicitationresult-input)中所示的响应,因此在用户提交 `alice` 的位置,服务器收到的是 `alice@example.com`:
3782 3928
3783```json theme={null}3929```json theme={null}
3784{3930{
3785 "hookSpecificOutput": {3931 "hookSpecificOutput": {
3786 "hookEventName": "ElicitationResult",3932 "hookEventName": "ElicitationResult",
3787 "action": "decline",3933 "action": "accept",
3788 "content": {}3934 "content": {
3935 "username": "alice@example.com"
3936 }
3789 }3937 }
3790}3938}
3791```3939```
3792 3940
3793| 字段 | 取值 | 描述 |3941您的 `content` 会替换用户的整个 `content` 对象,因此请包含您未更改的字段。请同时返回 `action`,因为 Claude Code 会忽略没有 `action` 的 `hookSpecificOutput`。
3794| :- | :- | :- |3942
3795| `action` | `accept`、`decline`、`cancel` | 覆盖用户的操作 |3943当用户拒绝或取消时,ElicitationResult hook 也会运行,并且您的 `action` 会替换用户的 `action`。在返回 `accept` 之前,请检查输入的 `action` 是否为 `accept`,否则您的 hook 会将已拒绝的请求变为已接受的请求。此脚本在用户接受时进行相同的更改,保留其他字段,否则不打印任何内容:
3796| `content` | object | 覆盖表单字段值。仅在 `action` 为 `accept` 时有意义 |3944
3945```bash theme={null}
3946#!/bin/bash
3947input=$(cat)
3797 3948
3798退出码 2 会阻止该响应,将实际生效的操作更改为 `decline`。Claude Code 不会在任何地方显示您的 stderr 消息。3949if [ "$(jq -r '.action' <<<"$input")" = "accept" ]; then
3950 jq '{hookSpecificOutput: {hookEventName: "ElicitationResult", action: "accept", content: (.content + {username: (.content.username + "@example.com")})}}' <<<"$input"
3951fi
3952```
3799 3953
3800Claude Code 会处理 ElicitationResult hook JSON 输出中的 `hookSpecificOutput`,并丢弃 `systemMessage` 和 `continue`。3954此输出阻止响应:
3955
3956```json theme={null}
3957{
3958 "hookSpecificOutput": {
3959 "hookEventName": "ElicitationResult",
3960 "action": "decline"
3961 }
3962}
3963```
3964
3965退出码 2 和顶层 `"decision": "block"` 也会阻止响应。[拒绝 elicitation 的其他方式](#other-ways-to-decline-an-elicitation)介绍了当 hook 组合使用这些方式时哪一种生效、用户会看到什么,以及哪些版本会忽略 `decision`。
3966
3967无论您返回哪个 `action`,Claude Code 都会丢弃 ElicitationResult hook JSON 输出中的 `reason`、`systemMessage` 和 `continue`。
3801 3968
3802<h2 id="prompt-based-hooks">3969<h2 id="prompt-based-hooks">
3803 基于提示的 hooks3970 基于提示的 hooks
3861 4028
3862将 `type` 设置为 `"prompt"` 并提供 `prompt` 字符串而不是 `command`。使用 `$ARGUMENTS` 占位符将 hook 的 JSON 输入数据注入到您的提示文本中。4029将 `type` 设置为 `"prompt"` 并提供 `prompt` 字符串而不是 `command`。使用 `$ARGUMENTS` 占位符将 hook 的 JSON 输入数据注入到您的提示文本中。
3863 4030
4031在提示词 hook 或 [Agent hook](#agent-based-hooks) 中,您可以将 `prompt` 写成关于阻止或允许什么的规则,例如"阻止任何读取 `.env` 文件的 Bash 命令",也可以写成必须成立的条件,例如"所有单元测试都通过"。
4032
3864此 `Stop` hook 要求 LLM 在允许 Claude 完成之前评估是否应该停止:4033此 `Stop` hook 要求 LLM 在允许 Claude 完成之前评估是否应该停止:
3865 4034
3866```json theme={null}4035```json theme={null}