
Claude Code Hooks:自动化你的智能体的高阶用户指南
Claude Code hooks 能在生命周期事件发生时自动运行你的命令——无需人工介入即可完成校验、代码检查、拦截或日志记录。本文涵盖各类事件、配置方法、五个实用配方、退出码的坑,以及无需任何配置即可运行 Claude Code 的方法。
Claude Code hooks 是用户自定义的命令,会在 Claude Code 生命周期的特定节点自动运行——在一次工具调用之前、一次编辑之后、会话开始时、Claude 完成一轮回复时——这样你就可以在不必每次都亲自介入的情况下进行校验、格式化、记录日志或阻止操作。这正是"只会建议"的 AI 编码代理与"确定性地遵循你的规则"的代理之间的区别。本指南将介绍什么是 hooks、它们可以触发的每一种事件、如何配置它们、五个你可以直接套用的实用范例、几乎所有人都会踩坑的退出码陷阱,以及如何在不进行任何本地配置的情况下使用 Claude Code 的全部能力。
什么是 Claude Code Hooks?
Hook 是一个处理程序——可以是一条 shell 命令、一个 HTTP 端点、一次 MCP 工具调用,甚至是一次模型提示——当特定事件发生时,Claude Code 会自动运行它。该处理程序会接收结构化输入(命令类 hook 通过 stdin,HTTP 类 hook 通过 POST 请求体),可以检查正在发生的事情、采取行动,并可选择返回一个决策来改变 Claude 接下来的行为。
正是最后这一点让 hooks 变得强大,而不仅仅是方便。Hook 不只是一个通知——它可以阻止一次工具调用、改写工具的输入或输出、注入额外的上下文,或者完全停止 Claude。换句话说,hooks 把 Claude Code 从一个聪明的助手变成了一个可编程的助手,能够确定性地执行你团队的护栏规则,而不是仅仅依赖模型碰巧记住这些规则。
Hooks 何时触发:生命周期
Hooks 挂载在事件上,而 Claude Code 暴露了大量这样的事件。它们按触发频率分组:有些每次会话触发一次,有些每轮对话触发一次,还有些在每次工具调用时触发。
Hooks 在一次 Claude Code 会话中触发的位置——从 SessionStart 到 SessionEnd。
你最常用到的事件有:
SessionStart/SessionEnd—— 会话开始或结束时各触发一次。非常适合用来加载上下文(未解决的 issue、分支信息、环境变量)或做清理工作。UserPromptSubmit—— 当你提交一个提示时触发,在 Claude 处理之前。你可以对提示进行过滤或增强。PreToolUse—— 在任何工具调用之前触发。这是你阻止危险操作的地方。PostToolUse(以及PostToolUseFailure)—— 在一次工具调用成功(或失败)之后触发。适合做代码检查、格式化和验证。Stop/StopFailure—— 当 Claude 完成响应时,或该轮对话因错误而结束时触发。Notification—— 当 Claude Code 发出通知时触发(适合用于桌面提醒)。
除此之外,Claude Code 还会为子代理(SubagentStart/SubagentStop)、任务(TaskCreated/TaskCompleted)、上下文压缩(PreCompact/PostCompact)、工作目录变更(CwdChanged)、磁盘上的文件变更(FileChanged)、指令加载(InstructionsLoaded)等触发 hooks。这种广度正是重点所在:代理循环中几乎任何一个时刻,你都可以在此挂载策略。
五种 Hook 处理程序类型
一个 hook 事件可以触发五种不同类型的处理程序,这正是该系统灵活性的来源:
command—— 运行一条 shell 命令;通过 stdin 读取输入,通过退出码和 stdout 传达决策。http—— 将事件 JSON 以 POST 方式发送到某个 URL,并读取 JSON 响应。mcp_tool—— 调用已连接 MCP 服务器上的一个工具。prompt—— 一次单轮的模型评估,返回是/否的 JSON 决策(适合模糊性检查)。agent—— 生成一个子代理(实验性功能)。
对大多数团队来说,command 类型的 hooks 就能完成 90% 的工作——一个 shell 脚本就足以完成检查、阻止或记录日志。
如何配置一个 Hook
Hooks 存放在配置文件中(权威参考请见官方 Claude Code hooks 文档),你把它放在哪里决定了它的作用范围:
| 位置 | 作用范围 |
|---|---|
~/.claude/settings.json | 你的所有项目 |
.claude/settings.json | 单个项目(可提交——可与团队共享) |
.claude/settings.local.json | 单个项目,仅本地(已加入 gitignore) |
| 托管策略设置 | 整个组织范围(管理员) |
其结构嵌套了三层:选择一个事件,添加一个匹配器(matcher)分组,然后定义处理程序(handlers)。匹配器决定一个 hook 适用于哪些工具调用——"*"(或省略不写)匹配所有工具,简单的 Edit|Write 只匹配这些具体工具,更复杂的写法则会被当作正则表达式处理。(MCP 工具匹配 mcp__<server>__<tool> 这种模式。)
下面是一个 PostToolUse hook,它会在每次编辑之后运行一次代码检查:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": "/path/to/lint-check.sh" }]
}
]
}
}五个实用的 Hook 范例
以下是团队最先会用到的模式:
- 每次编辑后进行代码检查或格式化。 一个匹配
Edit|Write的PostToolUsehook,运行你的格式化工具或 linter,让代理写出的代码始终符合你的风格规范。 - 阻止破坏性命令。 一个匹配
Bash的PreToolUsehook,检查命令内容,在rm -rf及类似命令真正执行之前将其阻止。 - 桌面通知。 一个
Notificationhook,在 Claude 需要你关注或完成一项长任务时提醒你。 - 审计日志。 一个
PostToolUse(或针对 MCP)的 hook,记录每次工具调用以满足合规要求——运行了什么、何时运行、使用了什么参数。 - 启动时加载项目上下文。 一个
SessionStarthook,拉取未解决的 issue、当前分支或环境变量,让代理在每次会话开始时就已经进入状态。
五个常见的 hook 范例及其挂载的事件。
退出码陷阱
这是几乎所有人都会踩的一个细节,务必牢记:对于 command 类型的 hooks,只有退出码 2 才会阻止操作。退出码 0 表示成功(且 stdout 会被解析,以获取任何 JSON 决策)。退出码 2 是一个阻止性错误——其 stderr 会被反馈给 Claude。其他任何退出码,包括退出码 1,都是非阻止性错误——Claude 会继续执行。
所以,如果你写了一个"阻止这个"的 hook,却用 exit 1,它不会阻止操作——该操作依然会继续进行。要真正从一个命令类 hook 中强制执行某项策略,必须 exit 2。(命令类 hooks 还可以在 stdout 上以 JSON 形式返回更丰富的控制信息——PreToolUse 的 permissionDecision: "deny"、用于改写参数的 updatedInput、用于注入信息的 additionalContext,以及用于彻底停止 Claude 的 continue: false。)
一个完整示例:阻止 rm -rf
让我们从头到尾构建这个破坏性命令防护,因为它展示了每一个关键环节。首先是配置——一个匹配 Bash 的 PreToolUse hook:
{ "hooks": { "PreToolUse": [{ "matcher": "Bash",
"hooks": [{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/guard.sh" }] }] } }然后是脚本 guard.sh。Claude Code 会将事件以 JSON 形式通过 stdin 发送,其中包含该工具的输入;脚本读取它、检查命令内容,并做出决策:
#!/usr/bin/env bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command // ""')
if echo "$cmd" | grep -Eq 'rm +-rf|mkfs'; then
echo "Blocked: destructive command refused by policy." >&2
exit 2 # exit 2 会阻止——exit 1 则不会
fi
exit 0关键的一行是 exit 2。返回 0,命令就会执行;返回 1——反直觉的是——它仍然会作为非阻止性错误继续执行;只有 exit 2 才会阻止操作,并把你的 stderr 消息反馈给 Claude,让它理解原因。让这个脚本可执行,把它提交到 .claude/hooks/ 下,此后每一次 Bash 调用都会经过你的这道防护——对你的整个团队都生效,因为项目级的 settings.json 是可以共享的。如果需要硬性保证而不只是尽力而为的防护网,可以再配合 Claude Code 的权限规则;而作为一个确定性的、纳入版本控制的安全网,这个 hook 已经能胜任这项工作。
Hooks 不是万能答案的场景
Hooks 适用于确定性的、可重复的策略——"编辑后总是要做代码检查""永远不要运行 rm -rf"。对于需要判断力的事情(用模型或 prompt 类型的 hook 来做模糊判断)以及需要硬性安全保证的场景(用权限系统,因为命令类 hook 的 if 过滤器是失败即放行的),它们并不是合适的工具。同样也不要滥用 hooks:每一个命令类 hook 都会带着超时限制运行一个进程,一堆缓慢的 hooks 会给每次工具调用都增加延迟。让它们保持快速、精简,并且只聚焦于真正重要的规则。
值得了解的安全须知
文档中明确说明了几个现实情况:
if过滤器是失败即放行的。 如果你使用if字段将一个 hook 的作用范围限定到某条权限规则,而该命令无法被解析,该 hook 仍然会照常运行。要实现硬性的允许/拒绝强制执行,应使用 Claude Code 的权限系统,而不是 hooks。- Hooks 运行时没有可控制的终端——它们无法在
/dev/tty上进行交互式提示。面向用户的消息应使用systemMessage或受限的terminalSequence输出。 - Stdout 必须保持干净。 stdout 上应该只有 JSON 决策对象;杂散的 shell 配置文件输出可能会破坏解析。
- 谨慎处理注入的上下文。
additionalContext应写成陈述事实的语句,而不是祈使句式的命令,这样才能更好地配合防提示注入的防御机制。
Hooks 是微缩版的 Harness 工程
退一步看,hooks 是一个更大理念的具体体现:模型并不是整个代理——围绕它的系统才是。Hooks 是让编码代理变得可靠的 harness(运行框架)的一部分,与循环、工具、记忆和沙箱并列。(我们在harness engineering一文中深入探讨了这个心智模型。)当你编写一个 PreToolUse 阻止规则或一个 PostToolUse linter 时,你正在做的就是 harness 工程——确定性地塑造代理的行为,而不是寄希望于模型自己表现良好。
这个框架同样也告诉你 hooks 何时值得投入精力:任何一条你原本需要每次都提醒代理的规则,都是使用 hook 的候选对象。
无需本地配置即可使用 Claude Code 的全部能力
Hooks 存在于 Claude Code CLI 之中,这意味着你需要安装并配置好它才能使用——对于任何没有在终端环境中配置好的人来说,这是一道门槛,而对于非开发者的团队成员来说更是不可能完成的任务。如果你想要 Claude Code 的代理能力,却不想管理本地安装,你可以在 Happycapy 上在浏览器中运行 Claude Code:它会在一个托管的云端沙箱中运行 Claude Code,其中 harness——也就是 hooks 所挂载的循环、工具、记忆和隔离机制——已经为你搭建完毕。你只需描述一项任务,然后在一个可视化桌面上观看代理工作,完全不需要终端。
可以这样理解:hooks 让高级用户能够手动调优 Claude Code 的 harness;而 Happycapy 则为每个人提供了一个开箱即用的托管 harness。如果你一直想让 Claude Code 为你工作,却被 CLI 的安装配置挡住了脚步,那就**在 happycapy.ai 免费开始**,今天就在浏览器里运行一项真实的任务吧。
常见问题
问:什么是 Claude Code hooks?
它们是用户自定义的处理程序——shell 命令、HTTP 端点、MCP 工具调用,或模型提示——会在生命周期事件(如一次工具调用之前 PreToolUse、一次编辑之后 PostToolUse,或会话开始时)自动被 Claude Code 运行。它们可以校验、格式化、记录日志、阻止或改写操作。
问:Claude Code 支持哪些 hook 事件?
有很多——包括 SessionStart/SessionEnd、UserPromptSubmit、PreToolUse、PostToolUse(以及 PostToolUseFailure)、Stop/StopFailure、Notification,以及子代理和任务相关事件、压缩相关事件。它们分别在每次会话一次、每轮对话一次,或每次工具调用时触发。
问:如何让一个 hook 阻止一次工具调用?
对于命令类 hooks,以代码 2 退出——只有退出码 2 才会阻止(退出码 1 不会)。对于 PreToolUse,你也可以返回带有 permissionDecision: "deny" 的 JSON。要实现硬性强制执行,建议优先使用 Claude Code 的权限系统,因为 hook 的 if 过滤器是失败即放行的。
问:在哪里配置 Claude Code hooks?
在配置文件中:~/.claude/settings.json(所有项目)、.claude/settings.json(单个项目,可共享),或 .claude/settings.local.json(仅本地)。你需要选择一个事件、添加一个匹配器,并定义处理程序。
问:不安装 CLI 能使用 Claude Code hooks 吗?
Hooks 本身需要 Claude Code CLI。如果你的目标是无需本地配置就使用 Claude Code,可以在像 Happycapy 这样的托管浏览器沙箱中运行它,它已经准备好了代理及其 harness——当你不想自己安装和配置 CLI 时,这是理想的选择。
问:适合入门的第一个 hook 是什么?
一个匹配 Edit|Write 的 PostToolUse linter——它会在每次代码变更后运行你的格式化工具,让代理的输出始终符合你的风格规范。这个 hook 风险低,而且立刻就能见效。

