Hooks 参考
Hook 配置结构、matcher 与 handler 字段、输入输出与退出码、JSON 输出、各事件能否被阻止,以及安全注意事项。
这是 Hook 事件、配置结构、JSON 输入输出格式、退出码、异步 Hook、HTTP Hook 等的参考。入门和常见用例见用 Hooks 自动化。官方原文逐事件详细列出了每个事件的输入字段和决策控制,本页只整理通用规则和速查表。
配置
Hook 在 JSON 设置文件里定义,配置有三层嵌套:
- 选择一个要响应的Hook 事件(如
PreToolUse或Stop) - 添加一个matcher 组来过滤何时触发(如「只对 Bash 工具」)
- 定义一个或多个在匹配时运行的Hook handler
「Hook 事件」指生命周期点,「matcher 组」指过滤器,「Hook handler」指实际运行的 shell 命令、HTTP 端点、MCP 工具、提示词或智能体。
Hook 位置
| 位置 | 范围 | 是否可共享 |
|---|---|---|
~/.claude/settings.json | 你的所有项目 | 否,仅本机 |
.claude/settings.json | 单个项目 | 是,可提交进仓库 |
.claude/settings.local.json | 单个项目 | 否 |
| 托管策略设置 | 整个组织 | 是,管理员控制 |
插件的 hooks/hooks.json | 插件启用时 | 是,随插件打包 |
| Skill 前置信息 | Skill 被调用后会话的剩余时间 | 是 |
| 子智能体前置信息 | 该子智能体运行期间 | 是 |
云端会话不读取你本地的 ~/.claude/settings.json。来自设置文件、托管策略设置和插件的 Hook 也会在子智能体内运行:子智能体调用工具时,PreToolUse、PostToolUse 等工具事件会像主对话里一样触发已配置的 Hook。管理员可以在托管设置里用 allowManagedHooksOnly 限制哪些 Hook 能运行(你的用户、项目、本地和插件 Hook 会被阻止)。Hook 条目在设置层级之间合并而不是互相替换。
matcher 模式
matcher 字段过滤 Hook 何时触发,如何评估取决于它包含的字符:
| matcher 值 | 评估为 | 示例 |
|---|---|---|
"*"、"" 或省略 | 匹配全部 | 在该事件每次发生时触发 |
只含字母、数字、_、-、空格、, 和 | | 精确字符串,或用 | 或 , 分隔的精确字符串列表 | Bash 只匹配 Bash 工具;Edit|Write 和 Edit, Write 各自精确匹配任一工具 |
| 包含任何其他字符 | JavaScript 正则表达式,不锚定 | ^Notebook 匹配名字以 Notebook 开头的任何工具;mcp__memory__.* 匹配 memory 服务器的每个工具 |
正则路径上的 matcher 用 RegExp.prototype.test 测试,在值的任何位置匹配都成功:Edit.* 同时匹配 Edit 和 NotebookEdit;需要整串匹配时用 ^ 和 $ 包起来,如 ^Edit$。
Hook handler 字段
内层 hooks 数组里的每个对象是一个 Hook handler。有五种类型:
- 命令 Hook(
type: "command"):运行 shell 命令,脚本在 stdin 收到事件的 JSON 输入,通过退出码和 stdout 返回结果 - HTTP Hook(
type: "http"):把事件的 JSON 输入作为 HTTP POST 请求发到某个 URL,端点通过响应体以与命令 Hook 相同的 JSON 输出格式返回结果 - MCP 工具 Hook(
type: "mcp_tool"):调用已配置 MCP 服务器上的工具,工具的文本输出被当作命令 Hook 的 stdout - 提示词 Hook(
type: "prompt"):把提示词发给 Claude 模型做单轮评估,模型以 JSON 返回决定 - 智能体 Hook(
type: "agent"):派生一个能用 Read、Grep、Glob 等工具的子智能体,在返回决定之前验证条件(实验性)
所有匹配的 Hook 并行运行;同一个 handler 在多个设置文件里定义时只运行一次。
通用字段:
| 字段 | 必填 | 说明 |
|---|---|---|
type | 是 | "command"、"http"、"mcp_tool"、"prompt" 或 "agent" |
if | 否 | 用权限规则语法过滤此 Hook 何时运行,如 "Bash(git *)" 或 "Edit(*.ts)";只有工具调用匹配该模式时才运行 Hook 命令。if 字段只放一条权限规则,没有 &&、|| 或列表语法,要应用多个条件就为每个条件定义单独的 handler |
timeout | 否 | 取消前的秒数。默认值:command、http、mcp_tool 为 600;prompt 为 30;agent 为 60 |
statusMessage | 否 | Hook 运行时显示的自定义旋转指示器消息 |
once | 否 | 为 true 时,Hook 在第一次成功运行后被移除;只对 Skill 前置信息里声明的 Hook 有效 |
命令 Hook 字段:command(必填,要执行的 shell 命令;带 args 时是直接启动的可执行文件)、args(参数列表;存在时 command 被当作可执行文件直接启动,不经 shell)、async(为 true 时在后台运行而不阻塞)、asyncRewake(后台运行,并在退出码 2 时唤醒 Claude)、shell("bash" 或 "powershell")。
执行形式与 shell 形式:设置了 args 就是 exec 形式:Claude Code 把 command 解析为 PATH 上的可执行文件并以 args 为参数向量直接启动,没有 shell,每个 args 元素就是原样的一个参数,${CLAUDE_PROJECT_DIR} 这样的路径占位符会在每个 args 元素里被替换。省略 args 就是 shell 形式:command 字符串传给 shell(macOS 和 Linux 上是 sh -c,Windows 上是 Git Bash,没有 Git Bash 时是 PowerShell)。引用路径占位符时设置 args;需要 shell 特性(管道、重定向、变量展开)时省略。
Hook 输入与输出
命令 Hook 通过 stdin 接收 JSON 数据,通过退出码、stdout 和 stderr 通信。HTTP Hook 把同样的 JSON 作为 POST 请求体接收,通过 HTTP 响应体通信。在 macOS 和 Linux 上,命令 Hook 在自己的会话里运行,没有控制终端,Hook 进程无法打开 /dev/tty;想向用户展示一条消息,在 JSON 输出里返回 systemMessage。
通用输入字段
| 字段 | 说明 |
|---|---|
session_id | 当前会话标识符 |
prompt_id | 标识当前正在处理的用户提示的 UUID,与 OpenTelemetry 事件上的 prompt.id 属性匹配 |
transcript_path | 对话 JSON 的路径(转录文件是异步写入的,Hook 触发时可能还没包含当前回合最新的消息) |
cwd | Hook 被调用时的当前工作目录 |
permission_mode | 当前权限模式:"default"、"plan"、"acceptEdits"、"auto"、"dontAsk" 或 "bypassPermissions"。标为 Manual 的模式以 "default" 到达,从不是 "manual" |
effort | 带 level 字段的对象,值是 Hook 运行时生效的努力等级 |
hook_event_name | 触发的事件名 |
agent_id、agent_type | 在 --agent 下运行或在子智能体内部时额外包含 |
比如 Bash 命令的 PreToolUse Hook 在 stdin 收到:
{
"session_id": "abc123",
"cwd": "/home/user/my-project",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test",
"description": "Run test suite",
"timeout": 120000,
"run_in_background": false
},
"tool_use_id": "toolu_01ABC123..."
}对文件工具 Write、Edit 和 Read,tool_input.file_path 始终是绝对路径:Claude Code 在 Hook 运行前展开 ~ 和相对路径,所以按路径匹配的 Hook 无法通过 ~ 或同一路径的相对写法被绕过。在 Windows 上,路径以反斜杠分隔符到达,比较前要规范化分隔符(Bash 里 FILE_PATH="${FILE_PATH//\\//}",Python 里 file_path.replace("\\", "/"))。
退出码输出
- 退出 0:成功,是打印 JSON 做结构化控制时的预期退出码。对多数事件,Claude Code 把 stdout 写入调试日志而不在转录里显示;例外是
UserPromptSubmit、UserPromptExpansion、SessionStart和PostModelSwitch,其纯文本 stdout 会被作为 Claude 能看到并据此行动的上下文加入。stdout 以{开头且以}结尾时按 JSON 解析,否则当作纯文本 - 退出 2:阻塞错误。在能阻塞的事件上,退出 2 不管是否打印 JSON 都会阻塞,连 JSON 里
permissionDecision为"allow"也无法覆盖它。阻塞消息是你 JSON 里阻塞决定的 reason,否则是你的 stderr 文本 - 其他退出码:对多数 Hook 事件本身不阻塞。取决于 stdout:能通过校验的 JSON 对象,退出码被忽略,只由 JSON 决定结果;校验失败或解析失败是非阻塞错误;纯文本或空输出,动作继续,转录里显示
<hook name> hook error通知
对多数 Hook 事件,只有退出码 2 能靠退出码本身阻塞。没有有效 JSON 时,Claude Code 把退出码 1 当作非阻塞错误并继续动作,尽管 1 是 Unix 的常规失败码。
超时:除了你用 async: true 运行的命令 Hook,Claude Code 会取消到达 timeout 的 command、http 或 mcp_tool Hook 并丢弃其输出,所以在多数事件上超时的 Hook 不产生决定。在 PreToolUse 上,超时的命令 Hook 不阻止工具调用,调用继续走正常的权限流程,所以不要指望一个卡住的 Hook 充当关卡。
各事件上退出码 2 的行为
| Hook 事件 | 能阻塞? | 退出 2 时发生什么 |
|---|---|---|
PreToolUse | 是 | 阻止工具调用 |
PermissionRequest | 否 | 不被采纳,权限流程原样继续;改用 decision 对象拒绝 |
UserPromptSubmit | 是 | 阻止该提示,它永远到不了 Claude |
UserPromptExpansion | 是 | 阻止展开 |
Stop | 是 | 阻止 Claude 停止,对话继续 |
SubagentStop | 是 | 阻止子智能体停止 |
TeammateIdle | 是 | 阻止队友进入空闲,让它继续工作 |
TaskCreated | 是 | 回滚任务创建 |
TaskCompleted | 是 | 阻止任务被标记为完成 |
ConfigChange | 是 | 阻止配置变更生效(policy_settings 除外) |
PostToolBatch | 是 | 在下一次模型调用之前停止智能体循环 |
PreCompact | 是 | 阻止压缩 |
PreModelSwitch | 是 | 阻止模型切换并向用户显示 stderr |
Elicitation / ElicitationResult | 是 | 拒绝 elicitation / 阻止响应(动作变为拒绝) |
WorktreeCreate / WorktreeRemove | 是 | 任何非零退出码都会让 worktree 创建 / 移除失败 |
PostToolUse / PostToolUseFailure | 否 | 向 Claude 显示 stderr;工具已经运行或已经失败 |
StopFailure、Notification、Setup、InstructionsLoaded、PermissionDenied | 否 | 输出和退出码被忽略 |
SessionStart、SubagentStart、SessionEnd、CwdChanged、FileChanged、PostCompact、PostModelSwitch | 否 | 只向用户显示 stderr |
JSON 输出
退出码只能让你阻止或保持沉默,JSON 输出给你更细的控制:退出 0 并向 stdout 打印 JSON 对象。每个 Hook 选一种方式:只用退出码,或退出 0 并打印 JSON。如果混用,退出 2 保持其阻塞效果,Claude Code 仍读取 JSON 字段。你的 Hook 的 stdout 必须只包含 JSON 对象,shell 配置文件在启动时打印文字会干扰 JSON 解析。Hook 的 additionalContext、systemMessage、initialUserMessage 字符串及其纯 stdout 上限为 10,000 个字符,超出的内容会被保存到会话目录的文件里,并被替换为文件路径和最多前 2000 个字符的预览。
JSON 对象支持三类字段:
- 通用字段(每个事件都接受):
| 字段 | 默认值 | 说明 |
|---|---|---|
continue | true | 为 false 时,Hook 运行后 Claude 完全停止处理,优先于任何事件特定的决策字段 |
stopReason | 无 | continue 为 false 时向用户显示的消息 |
systemMessage | 无 | 向用户显示的警告消息 |
terminalSequence | 无 | 让 Claude Code 代你发出的终端转义序列,如桌面通知、窗口标题或响铃;限于 OSC 0/1/2/9/99/777 和 BEL,其他序列会被拒绝 |
- 顶层
decision和reason:一些事件用来阻止或给出反馈 hookSpecificOutput:给需要更丰富控制的事件用的嵌套对象,需要一个设为事件名的hookEventName字段
要完全停止 Claude:
{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }发出终端通知:Hook 运行时没有控制终端,直接把转义序列写到 /dev/tty 会失败。改为在 terminalSequence 字段里返回转义序列,Claude Code 通过它自己的终端写入路径替你发出,这对 Notification 和 StopFailure 这类丢弃 systemMessage 和 continue 的事件也有效。
Hook 事件
事件按生命周期顺序分成四页,每页写明触发时机、matcher、输入字段和如何用输出控制行为:
- 会话与提示事件:
SessionStart、Setup、InstructionsLoaded、UserPromptSubmit、UserPromptExpansion、MessageDisplay - 工具事件:
PreToolUse(含各工具的tool_input)、PermissionRequest、PostToolUse、PostToolUseFailure、PostToolBatch、PermissionDenied - 智能体、任务与通知事件:
Notification、SubagentStart、SubagentStop、TaskCreated、TaskCompleted、Stop、StopFailure、TeammateIdle - 环境、压缩与模型事件:
ConfigChange、CwdChanged、DirectoryAdded、FileChanged、WorktreeCreate/WorktreeRemove、PreCompact/PostCompact、PreModelSwitch/PostModelSwitch、SessionEnd、Elicitation/ElicitationResult
在后台运行 Hook
默认情况下,Hook 会阻塞 Claude 的执行直到完成。对部署、测试套件或外部 API 调用这类长时间运行的任务,设置 "async": true 让 Hook 在后台运行,同时 Claude 继续工作。异步 Hook 不能阻塞或控制 Claude 的行为;这个字段只对 type: "command" 的 Hook 可用。下面的 Hook 在每次 Write 工具调用后运行一个测试脚本,Claude 立即继续工作,脚本完成时,它的输出在下一个对话回合送达:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "/path/to/run-tests.sh",
"async": true
}
]
}
]
}
}安全注意事项
命令 Hook 以你的完整用户权限执行 shell 命令,它们可以修改、删除或访问你的用户账号能访问的任何文件。添加到配置之前,请审查并测试所有 Hook 命令。
工作区信任:Claude Code 在运行任何设置文件里的 Hook 之前会检查工作区信任。交互式会话里,Claude Code 会保留每个设置文件(包括你自己的 ~/.claude/settings.json)里的 Hook,直到你接受该文件夹的工作区信任对话框;-p 或 SDK 会话从不显示对话框,并把文件夹视为受信任,所以提交在仓库 .claude/settings.json 里的 Hook 会在你从未信任过的文件夹里运行。在你对不是自己写的仓库编写 claude -p 脚本之前,审查它的 .claude/ 设置文件,以 --bare 开始,或用 --settings '{"disableAllHooks": true}' 为这次运行关闭 Hook。
安全最佳实践:
- 验证和清理输入:永远不要盲目信任输入数据
- 始终给 shell 变量加引号:用
"$VAR"而不是$VAR - 阻止路径遍历:检查文件路径里的
.. - 使用绝对路径:为脚本指定完整路径。在 exec 形式里用
${CLAUDE_PROJECT_DIR},路径不需要加引号;在 shell 形式里用双引号包起来 - 跳过敏感文件:避免
.env、.git/、密钥等
Windows 上的 PowerShell
在 Windows 上,可以给命令 Hook 设置 "shell": "powershell" 让单个 Hook 在 PowerShell 里运行。Claude Code 自动检测 pwsh.exe(PowerShell 7 及更新版本),并回退到 Windows PowerShell 5.1 的 powershell.exe。引用项目根时,在 PowerShell 的 shell 形式命令里写 ${CLAUDE_PROJECT_DIR} 或 $env:CLAUDE_PROJECT_DIR;不要在 PowerShell Hook 里写不带花括号的 $CLAUDE_PROJECT_DIR,PowerShell 会把它解析为未定义的局部变量,得到 $null。
调试 Hook
Hook 执行细节会被写入调试日志文件。用 claude --debug-file <path> 启动,把日志写到已知位置,或运行 claude --debug 并在 ~/.claude/debug/<session-id>.txt 读取日志。
本节页面
- 会话与提示事件SessionStart、Setup、InstructionsLoaded、UserPromptSubmit、UserPromptExpansion、MessageDisplay 六个事件的触发时机、输入字段和输出控制。
- 工具事件PreToolUse(含各工具的 tool_input 字段、allow/deny/ask/defer 决策)、PermissionRequest、PostToolUse、PostToolUseFailure、PostToolBatch、PermissionDenied。
- 智能体、任务与通知事件Notification 的各通知类型,SubagentStart/Stop,TaskCreated/Completed,Stop 与 StopFailure(含 background_tasks 字段与错误类型),TeammateIdle。
- 环境、压缩与模型事件ConfigChange、CwdChanged、DirectoryAdded、FileChanged、WorktreeCreate/Remove、PreCompact/PostCompact、PreModelSwitch/PostModelSwitch、SessionEnd、Elicitation/ElicitationResult。