返回
Claude Code Hooks:自动化你的智能体的高阶用户指南
June 18, 2026
10 分钟阅读
分享这篇文章

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 暴露了大量这样的事件。它们按触发频率分组:有些每次会话触发一次,有些每轮对话触发一次,还有些在每次工具调用时触发。

Claude Code hook 生命周期示意图:开始时的 SessionStart,然后每轮的 UserPromptSubmit,接着每次工具调用时围绕每个工具的 PreToolUse 和 PostToolUse,然后是一轮结束时的 Stop,最后是 SessionEnd 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 事件可以触发五种不同类型的处理程序,这正是该系统灵活性的来源:

  1. command —— 运行一条 shell 命令;通过 stdin 读取输入,通过退出码和 stdout 传达决策。
  2. http —— 将事件 JSON 以 POST 方式发送到某个 URL,并读取 JSON 响应。
  3. mcp_tool —— 调用已连接 MCP 服务器上的一个工具。
  4. prompt —— 一次单轮的模型评估,返回是/否的 JSON 决策(适合模糊性检查)。
  5. 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 范例

以下是团队最先会用到的模式:

  1. 每次编辑后进行代码检查或格式化。 一个匹配 Edit|WritePostToolUse hook,运行你的格式化工具或 linter,让代理写出的代码始终符合你的风格规范。
  2. 阻止破坏性命令。 一个匹配 BashPreToolUse hook,检查命令内容,在 rm -rf 及类似命令真正执行之前将其阻止。
  3. 桌面通知。 一个 Notification hook,在 Claude 需要你关注或完成一项长任务时提醒你。
  4. 审计日志。 一个 PostToolUse(或针对 MCP)的 hook,记录每次工具调用以满足合规要求——运行了什么、何时运行、使用了什么参数。
  5. 启动时加载项目上下文。 一个 SessionStart hook,拉取未解决的 issue、当前分支或环境变量,让代理在每次会话开始时就已经进入状态。

将五个 Claude Code hook 范例映射到其对应事件的示意图:在 PostToolUse Edit/Write 上做代码检查,在 PreToolUse Bash 上阻止破坏性命令,在 Notification 上通知,在 PostToolUse 上做审计日志,在 SessionStart 上加载上下文 五个常见的 hook 范例及其挂载的事件。

退出码陷阱

这是几乎所有人都会踩的一个细节,务必牢记:对于 command 类型的 hooks,只有退出码 2 才会阻止操作。退出码 0 表示成功(且 stdout 会被解析,以获取任何 JSON 决策)。退出码 2 是一个阻止性错误——其 stderr 会被反馈给 Claude。其他任何退出码,包括退出码 1,都是非阻止性错误——Claude 会继续执行。

所以,如果你写了一个"阻止这个"的 hook,却用 exit 1,它不会阻止操作——该操作依然会继续进行。要真正从一个命令类 hook 中强制执行某项策略,必须 exit 2。(命令类 hooks 还可以在 stdout 上以 JSON 形式返回更丰富的控制信息——PreToolUsepermissionDecision: "deny"、用于改写参数的 updatedInput、用于注入信息的 additionalContext,以及用于彻底停止 Claude 的 continue: false。)

一个完整示例:阻止 rm -rf

让我们从头到尾构建这个破坏性命令防护,因为它展示了每一个关键环节。首先是配置——一个匹配 BashPreToolUse 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/SessionEndUserPromptSubmitPreToolUsePostToolUse(以及 PostToolUseFailure)、Stop/StopFailureNotification,以及子代理和任务相关事件、压缩相关事件。它们分别在每次会话一次、每轮对话一次,或每次工具调用时触发。

问:如何让一个 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|WritePostToolUse linter——它会在每次代码变更后运行你的格式化工具,让代理的输出始终符合你的风格规范。这个 hook 风险低,而且立刻就能见效。

相关指南

发布于 June 18, 2026
更多文章