「我想在每次编辑后自动格式化」「绝不能让任何东西改写 .env 或 package-lock.json」「要拦住危险的 rm -rf 命令」。要把这些「必须始终发生」的规则真正落地——而不依赖 AI 一时的判断,靠的就是 Claude Code hooks(钩子)。钩子是在 Claude Code 生命周期的特定时点自动执行的 shell 命令,同时也是插件的构成要素。
本文基于官方文档,梳理什么是钩子、事件一览、配置方法、输入/输出约定、使用场景与安全性。先说三个要点。① 钩子是「由 harness(Claude Code 本体)执行的确定性处理」——它一定会触发,不必等模型「决定」去做。② 你在 settings.json 中以「事件名 → matcher → command」的形式编写钩子。 ③ PreToolUse 等事件可通过退出码 2 或 JSON「拦截」——阻止对受保护文件的编辑或危险命令。
在关键生命周期时点「必定」运行
— 由 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 命令的机制」就足够了。
2. 钩子事件一览
事件表示「在生命周期的哪个位置触发」。先掌握经典的 9 个(「可拦截」=可通过退出码 2 或 JSON 阻止该动作)。
| 事件 | 触发时机 | 拦截 |
|---|---|---|
| SessionStart | 会话开始或恢复(stdout 被注入为上下文) | 否 |
| UserPromptSubmit | 你提交提示词、Claude 处理之前 | 是 |
| PreToolUse | 工具调用之前(主要的守门人) | 是 |
| PostToolUse | 工具成功后(动作本身无法撤销) | 是 |
| Notification | 通知时(等待输入/许可等) | 否 |
| Stop | Claude 结束应答时(拦截=继续工作) | 是 |
| 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 输出被忽略)。
# 示例:通过结构化 JSON 输出从 PreToolUse 返回「deny」(打印到 stdout)
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Database writes are not allowed"
}
}
# 常见字段: continue (false = 完全停止) / decision:"block"+reason /
# additionalContext (追加给 Claude) / updatedInput (改写参数)
最重要的原则:「钩子可以收紧限制,但不能放松限制。」返回 allow 只是跳过提示;任何作用域(包括管理设置)的 deny 规则始终优先。PreToolUse 的 deny 即便在 bypassPermissions / --dangerously-skip-permissions 下也会拦截。关于更广义的权限模型,参见权限模式与安全。
5. 使用示例
官方的经典用例,连同它们的配置一起列出。
经典自动化
PostToolUse + Edit|Write 自动运行 prettier / linter。PreToolUse 对 .env、.git/ 等的编辑拦截(exit 2)。PreToolUse 检测 rm -rf 等并返回 deny。SessionStart 的 stdout 每次都重新加载规约与笔记(即便压缩后也是)。Notification 用于桌面提醒;PostToolUse 用于记录命令历史。Stop 在测试通过前不让 Claude 停下(持续工作到通过为止)。
注意:Claude 也能通过 Bash 修改文件。若要捕获每一处变更,可加一个 Stop 钩子,每轮扫描一次工作树。
6. 安全性
钩子很强大,但它会以你的权限自动运行任意 shell 命令——切勿等闲视之。
⚠️ 官方警告
「钩子会自动执行任意 shell 命令。你所配置钩子的安全性完全由你自己负责。后果自负地使用。」钩子可以读取文件、修改你的代码库、外泄数据,或运行任何命令——只配置你完全信任的钩子。
启动时快照(关键的安全特性):钩子配置在会话启动时被捕获固定,因此会话中途的配置改动不会生效。这能防止恶意提示词或工具输出在会话期间改写你的钩子配置。要应用改动,请在新会话中进行。
实践上:务必校验并加引号处理输入(用 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 始终胜出)。经典用例:自动格式化、保护文件、拦住危险命令、重新注入上下文、审计、停止前先测试。但由于它运行任意代码,要严格做到只信任安全的钩子,并校验/加引号处理输入,并理解配置在启动时被捕获固定。相关:插件、MCP、Claude Code 错误集。
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 下也会停止。钩子只在「收紧限制」的方向上起作用。
Q. 我改了配置但没有生效。
A. 这是有意为之(一项安全特性)。钩子配置在会话启动时被捕获为快照固定,因此会话中途的改动不会生效。这能防止恶意提示词或工具输出悄悄改写你的钩子配置。开启一个新会话即可应用。注意 /hooks 是只读列表;要添加或修改钩子,请直接编辑 settings.json。
Q. 钩子安全吗?
A. 并非无条件安全。钩子以你的权限运行任意 shell 命令,这正是文档说「后果自负」的原因。只配置你信任的钩子,用 jq 校验并加引号处理输入,使用绝对路径,不碰 .env / .ssh,并绝不 eval 工具输出。在组织中,管理员可用 allowManagedHooksOnly 限制钩子。