“我想在每次编辑后自动格式化”“绝不能让任何东西改写 .env 或 package-lock.json”“要拦住危险的 rm -rf 命令”。要把这些“必须始终发生”的规则真正落地——而不依赖 AI 一时的判断,靠的就是 Claude Code hooks(钩子)。钩子是在 Claude Code 生命周期的特定时点自动执行的 shell 命令,同时也是插件的构成要素。

本文基于官方文档,梳理什么是钩子、事件一览、配置方法、输入/输出约定、使用场景与安全性。先说三个要点。① 钩子是“由 harness(Claude Code 本体)执行的确定性处理”——它一定会触发,不必等模型“决定”去做。② 你在 settings.json 中以“事件名 → matcher → command”的形式编写钩子。 ③ PreToolUse 等事件可通过退出码 2 或 JSON“拦截”——阻止对受保护文件的编辑或危险命令。

CLAUDE CODE · HOOKS

在关键生命周期时点“必定”运行

— 由 harness 确定性地执行,而非模型的判断

SessionStart 会话开始。输出被注入为上下文
UserPromptSubmit 提交提示词时 [可拦截]
PreToolUse 工具运行之前=守门人 [可拦截]
PostToolUse 工具成功后=自动格式化 / 审计
Stop 应答结束时=持续到测试通过等 [可拦截]

在每个时点,你的 shell 命令会运行。经典做法:用 PreToolUse 在门口拦下危险操作,用 PostToolUse 自动格式化。

1. 什么是 Claude Code hooks?

官方定义是这样的:“钩子是用户定义的 shell 命令,在 Claude Code 生命周期的特定时点执行。它们提供对 Claude Code 行为的确定性控制,确保某些动作总会发生,而不是依赖 LLM 去选择运行它们。”你可以用它来强制项目规则、自动化重复任务,并把 Claude Code 与现有工具集成起来。

核心是确定性(determinism)。你让 Claude“跑一下测试”,它会不会跑取决于它的“判断”。但有了钩子,harness 一定会运行它——不掺杂一时兴起。除了命令(command),现在还有 HTTP、MCP 工具、LLM 提示词、子智能体校验等处理器类型——但入门阶段,把钩子理解成“在关键时点必定运行一条 shell 命令的机制”就足够了。另外,LLM 提示词型和子智能体型钩子曾有一个缺陷:写成“拦截……的命令”这样的指令时,会放行本该拦截的内容;这个问题已在 v2.1.294 修复(官方 changelog)。

2. 钩子事件一览

事件表示“在生命周期的哪个位置触发”。先掌握经典的 9 个(“可拦截”=可通过退出码 2 或 JSON 阻止该动作)。

事件触发时机拦截
SessionStart会话开始或恢复(stdout 被注入为上下文)否
UserPromptSubmit你提交提示词、Claude 处理之前(v2.1.299 起,会在技能和自定义斜杠命令展开之前触发,因此拦截后,命令中嵌入的 shell 命令也不会运行。官方文档·更新日志,2026年10月18日确认)是
PreToolUse工具调用之前(主要的守门人)是
PostToolUse工具成功后(动作本身无法撤销)否
Notification通知时(等待输入/许可等)否
StopClaude 结束应答时(拦截=继续工作)是
SubagentStop子智能体结束时是
SessionEnd会话终止时否
PreCompact上下文压缩之前是

2026 年的文档新增了许多事件——PermissionRequest、PostToolUseFailure、ConfigChange、FileChanged、WorktreeCreate 等。但事件名可能在不同版本间增减或改动,因此本文以稳定的经典 9 个事件,加上下一节的约定为锚点(完整的最新列表请查阅官方文档)。

3. 配置方法

你把钩子写在 settings.json 的 "hooks" 键下。放置位置决定作用域:用户(~/.claude/settings.json)/项目(.claude/settings.json,可通过 git 共享)/本地(.claude/settings.local.json)/管理策略(组织)/插件的 hooks/hooks.json。JSON 的形式如下。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

结构是“hooks → 事件名 → { matcher, hooks:[...] } 数组 → 每个钩子有 type + command”。matcher 指定目标工具名:"*"(或省略)匹配全部,"Edit|Write" 是以 | 分隔的列表,其他字符为正则(例如 mcp__memory__.*)。它区分大小写。/hooks 命令会列出已配置的钩子,但它是只读的——要添加或修改,需直接编辑 settings.json。全部禁用用 "disableAllHooks": true。

4. 输入/输出约定

钩子从标准输入(stdin)接收 JSON,并通过退出码或标准输出的 JSON 返回结果。

输入(stdin):常见字段包括 session_id、transcript_path、cwd、hook_event_name。工具事件会增加 tool_name 和 tool_input(例如 {"command":"rm -rf /tmp/build"});UserPromptSubmit 会增加 prompt。退出码:0=成功(对部分事件,stdout 会被加入上下文——但 PreToolUse 上的退出 0 并非“批准”,仍然走正常的许可流程),2=拦截(stderr 会被传给 Claude 作为调整的素材,即使 JSON 返回 allow 也无法推翻这次拦截)。

钩子本身失败时(无法启动、超时、以意料之外的退出码结束),在大多数事件上默认操作会照常放行。路径写错的把关钩子只会显示一条错误提示,什么也拦不住。从 v2.1.295 起,给 command 或 http 钩子加上 "onFailure": "block",钩子失败时就会拦截该操作(默认值是 "continue")。

# 示例:通过结构化 JSON 输出从 PreToolUse 返回「deny」(打印到 stdout)
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Database writes are not allowed"
  }
}
# 常见字段: continue (false = 完全停止) / decision:"block"+reason /
#                additionalContext (追加给 Claude) / updatedInput (改写参数)

自 v2.1.300(2026年10月14日)起,PreToolUse 和 PermissionRequest 钩子返回的 updatedInput 会与 Claude 自身的输入同样校验。工具不认识的键会被丢弃;若钩子返回的输入工具不接受,该调用会被拒绝(更新日志,2026年10月18日确认)。

最重要的原则:“钩子可以收紧限制,但不能放松限制。”返回 allow 只是跳过提示;任何作用域(包括管理设置)的 deny 规则始终优先。PreToolUse 的 deny 即便在 bypassPermissions / --dangerously-skip-permissions 下也会拦截。这一原则针对的是写在设置文件中的钩子(mod 的官方页面称之为“settings hook”):自 v2.1.287 起,响应 tool.check 的 mod(以函数编写钩子的插件)可以批准本应由 ask 规则弹出确认、或被 PreToolUse 钩子拦截的调用(该钩子位于管理设置中时除外),在 auto 模式下它批准的调用不经过分类器;除了有管理设置的机器或以 Team、Enterprise 计划登录时,它甚至能批准被 deny 规则拒绝的调用(官方文档)。只安装可信作者的 mod。也就是说,钩子可以扩展权限的评估方式,但基线规则在别处——Claude Code 的权限规则(allow/ask/deny)在 settings.json 中定义。关于更广义的权限模型,参见权限模式与安全。

5. 使用示例

官方的经典用例,连同它们的配置一起列出。

USE CASES

经典自动化

编辑后自动格式化
PostToolUse + Edit|Write 自动运行 prettier / linter。
保护关键文件
PreToolUse 对 .env、.git/ 等的编辑拦截(exit 2)。
拦住危险命令
PreToolUse 检测 rm -rf 等并返回 deny。
重新注入上下文
SessionStart 的 stdout 每次都重新加载规约与笔记(即便压缩后也是)。
通知与审计日志
Notification 用于桌面提醒;PostToolUse 用于记录命令历史。
停止前先测试
Stop 在测试通过前不让 Claude 停下(持续工作到通过为止)。

注意:Claude 也能通过 Bash 修改文件。若要捕获每一处变更,可加一个 Stop 钩子,每轮扫描一次工作树。

如果只需要“持续运行直到满足条件”这一功能,而不想自己编写 Stop 钩子,可以直接使用 /goal(仅在对话中生效的提示词型钩子)。

6. 安全性

钩子很强大,但它会以你的权限自动运行任意 shell 命令——切勿等闲视之。

⚠️ 官方警告

“命令钩子会以你的完整用户权限执行 shell 命令。凡是你的用户账户能访问的文件,它们都能修改、删除或读取。把钩子加入配置之前,请审查并测试所有钩子命令。”钩子可以读取文件、修改你的代码库、外泄数据,或运行任何命令——只配置你完全信任的钩子。

文件夹信任(一项重要的安全机制):在交互式会话中,接受该文件夹的信任对话框之前,设置文件里的钩子不会运行(包括你自己的 ~/.claude/settings.json 里的钩子)。而 -p 或 SDK 会话不会弹出对话框,并把文件夹视为已信任,所以在别人的仓库里运行之前,先检查它 .claude/ 中的设置。直接修改设置文件里的钩子,通常会被自动识别。

实践上:务必校验并加引号处理输入(用 jq 提取;未加引号的变量可能变成额外参数或 shell 语法),使用绝对路径与 ${CLAUDE_PROJECT_DIR},不要碰 .env 或 .ssh 等敏感文件,并绝不 eval 工具输出。钩子运行时没有控制终端(无 /dev/tty)。在组织中,管理员可用 allowManagedHooksOnly 限制钩子。

总结

Claude Code hooks(钩子)是在关键生命周期时点自动运行的用户定义 shell 命令,让“必须始终发生”的事真正落地、确定性执行,而不依赖模型的判断。经典事件有 9 个:SessionStart / UserPromptSubmit / PreToolUse / PostToolUse / Notification / Stop / SubagentStop / SessionEnd / PreCompact(PreToolUse 等可拦截——阻止受保护文件编辑或危险命令)。你在 settings.json 的 "hooks" 下以事件 → matcher → type + command 的形式配置它们。

输入/输出是 stdin 上的 JSON,退出码 0(成功)/ 2(拦截),或 stdout 上的结构化 JSON。原则是“可以收紧,但不能放松限制”(deny 始终胜出,mod 的例外见第4节)。经典用例:自动格式化、保护文件、拦住危险命令、重新注入上下文、审计、停止前先测试。但由于它运行任意代码,要严格做到只信任安全的钩子,并校验/加引号处理输入,并理解在交互式会话中,信任该文件夹之前,设置文件里的钩子不会运行。相关:插件、MCP、Claude Code 错误集、mod(官方概述),mod 的详细解说见“Claude Code mod 详解”。

FAQ

Q. 钩子是用来做什么的?
A. 它确定性地自动化“必须始终发生”的事。诸如“编辑后格式化”“绝不让受保护文件被改写”“拦住危险的 rm -rf”“停止前先通过测试”等,都由 harness 必定执行,而不依赖 AI 的判断。钩子是在关键生命周期时点运行的 shell 命令,在 settings.json 中配置。

Q. 有哪些事件(时机)?
A. 经典的有 9 个:SessionStart(开始)、UserPromptSubmit(提交时)、PreToolUse(工具之前)、PostToolUse(工具之后)、Notification(通知)、Stop(应答结束)、SubagentStop、SessionEnd(终止)、PreCompact(压缩之前)。其中 PreToolUse、UserPromptSubmit、Stop、PreCompact 等可拦截并阻止该动作。2026 年版新增了许多事件,但名称可能在不同版本间变化,因此最新情况请查阅官方文档。

Q. 能阻止对受保护文件的编辑或危险命令吗?
A. 可以——用 PreToolUse 钩子。脚本检查 tool_input 中的文件路径或命令,若匹配到 .env、rm -rf 等,就返回退出码 2(或带 permissionDecision:"deny" 的 JSON)来拦截它。关键在于 deny 始终优先,即便在 bypassPermissions 下也会停止。钩子只在“收紧限制”的方向上起作用。例外是 mod(以函数编写钩子的插件):它响应 tool.check 时可以推翻这些判定;除了有管理设置的机器或以 Team、Enterprise 计划登录时,它甚至能批准被 deny 规则拒绝的调用(官方文档)。只安装可信作者的 mod。

Q. 我改了配置但没有生效。
A. 通常会自动生效:直接编辑设置文件时,文件监视器会捕获这次改动。如果过了几秒仍未生效,可能是监视器漏掉了改动,请重启会话。也请检查 JSON 是否有效(不能有末尾逗号或注释)。在交互式会话中,接受文件夹的信任对话框之前,设置文件里的钩子不会运行。另外,/hooks 是只读列表,添加或修改请直接编辑 settings.json。

Q. 钩子安全吗?
A. 并非无条件安全。钩子以你的权限运行任意 shell 命令,因此文档警告它们会“以你的完整用户权限”运行。只配置你信任的钩子,用 jq 校验并加引号处理输入,使用绝对路径,不碰 .env / .ssh,并绝不 eval 工具输出。在组织中,管理员可用 allowManagedHooksOnly 限制钩子。