Aklman · Library

07 / 第七章

自动化 · Hooks 让它按规矩来.

有些事你不想靠「希望模型记得」:每次改完自动跑 formatter、提交前必须过 lint、绝不允许它碰生产库、每次收尾发个通知。这些不能是建议,得是规矩。Hooks 就是规矩:它们是你在特定时机(工具调用前后、会话结束时)自动执行的脚本 —— 由 harness 执行,不由模型自觉,所以它跳不过。这一章讲 hooks 能钉住哪些行为、几个真正值得设的(安全闸、质量门、通知),以及它和 CLAUDE.md「叮嘱」的根本区别:一个是请求,一个是硬约束。


有些事你不想靠「希望模型记得」:每次改完文件跑格式化,提交前必须过 lint,生产配置绝对不许碰,收工时给你发条通知。写进 CLAUDE.md,它十次里九次照做 —— 而这四件事你要的是十次里十次。差的那一次,不能靠更大的字体解决。这一章讲 hooks:不求模型自觉、由 harness 到点必然执行的规矩。

I

请求与规矩的分界线.

这本书已经两次踩到这条线:第 3 章说记忆是「上下文,不是强制配置」3注 3Claude Code Docs · Memory —— CLAUDE.md 与 auto memory 是「上下文,不是强制配置」;要不论模型怎么决定都拦住某个动作,用 PreToolUse hook。截至 2026-07-17。,第 4 章说 skill 由模型理解执行、结果可能有出入。hooks 站在线的另一侧 —— 定义原话:「用户定义的 shell 命令、HTTP 端点或 LLM 提示,在 Claude Code 生命周期的特定时点自动执行」。2注 2Claude Code Docs · Hooks reference —— 定义原话:「hooks 是用户定义的 shell 命令、HTTP 端点或 LLM 提示,在 Claude Code 生命周期的特定时点自动执行」;事件约三十个,含 PreToolUse、PostToolUse、Stop、SessionStart、SessionEnd、Notification、UserPromptSubmit、SubagentStop 等;能力包括拦下工具调用、改写工具输入输出、注入上下文、发通知。截至 2026-07-17。关键词是自动:hook 由 harness 在事件上触发,不经过模型的判断,它想跳也跳不过。官方把分工说得很干脆:必须每次发生、零例外的动作,用 hooks;CLAUDE.md 是建议,hooks 是确定。4注 4Claude Code Docs · Best practices —— 「hooks 用于必须每次发生、零例外的动作」,CLAUDE.md 的指令是建议性的,hooks 才是确定性的;可以直接让 Claude 替你写 hook;Stop hook 把检查设为收工硬门,连续拦 8 次后放行。截至 2026-07-17。

II

装第一条:通知 + 格式化.

hooks 住在 settings 文件里(~/.claude/settings.json 归你,.claude/settings.json 随仓库),结构三层:事件名 → matcher 筛工具 → 要跑的命令。下面这份配置装了两条最常用的:改完文件自动 prettier,Claude 等你输入时发桌面通知 —— 后者治好的病叫「盯终端」:
.claude/settings.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude 在等你\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

两条入门 hook:PostToolUse 格式化、Notification 桌面提醒(macOS)

读法:PostToolUse 是「工具跑完之后」这个时点;matcher Edit|Write 表示只在编辑或写文件后触发;命令从 stdin 收到这次事件的 JSON,用 jq 取出文件路径喂给 prettier。1注 1Claude Code Docs · Hooks guide —— hooks 提供确定性控制,「保证某些动作必然发生,而不是依赖 LLM 选择去做」。配置放 settings 文件的 hooks 块:事件名 → matcher(如 Edit|Write)→ type: command 的命令;/hooks 面板只读浏览,增改要编辑 settings 或让 Claude 代写。协议:事件数据以 JSON 进 stdin;exit 0 放行,exit 2 拦下且 stderr 作为反馈交还给 Claude 让它调整;更复杂的决定用 exit 0 + stdout JSON(如 PreToolUse 的 permissionDecision: deny)。PreToolUse 在权限模式检查之前执行 —— 返回 deny 的 hook 连 bypassPermissions 也拦得住。示例即文档所载:PostToolUse 用 jq 取 file_path 交给 prettier;PreToolUse 脚本对 .env、.git/ 等保护路径 exit 2。截至 2026-07-17。不想手写 JSON 也行,这活可以派:直接说「写一个每次编辑后跑 eslint 的 hook」,让 Claude 自己给自己立规矩。4注 4Claude Code Docs · Best practices —— 「hooks 用于必须每次发生、零例外的动作」,CLAUDE.md 的指令是建议性的,hooks 才是确定性的;可以直接让 Claude 替你写 hook;Stop hook 把检查设为收工硬门,连续拦 8 次后放行。截至 2026-07-17。装好后用 /hooks 面板核对 —— 它是只读的浏览器,改动仍走 settings 文件。1注 1Claude Code Docs · Hooks guide —— hooks 提供确定性控制,「保证某些动作必然发生,而不是依赖 LLM 选择去做」。配置放 settings 文件的 hooks 块:事件名 → matcher(如 Edit|Write)→ type: command 的命令;/hooks 面板只读浏览,增改要编辑 settings 或让 Claude 代写。协议:事件数据以 JSON 进 stdin;exit 0 放行,exit 2 拦下且 stderr 作为反馈交还给 Claude 让它调整;更复杂的决定用 exit 0 + stdout JSON(如 PreToolUse 的 permissionDecision: deny)。PreToolUse 在权限模式检查之前执行 —— 返回 deny 的 hook 连 bypassPermissions 也拦得住。示例即文档所载:PostToolUse 用 jq 取 file_path 交给 prettier;PreToolUse 脚本对 .env、.git/ 等保护路径 exit 2。截至 2026-07-17。

III

时点地图与否决权.

生命周期上有约三十个可挂的事件,不用全记 —— 先认四个:PreToolUse,动手之前,唯一能拦下动作的时点;PostToolUse,动完之后,放质量动作;Stop,它想收工时,放验收闸;SessionEnd,会话结束,放通知和清理。2注 2Claude Code Docs · Hooks reference —— 定义原话:「hooks 是用户定义的 shell 命令、HTTP 端点或 LLM 提示,在 Claude Code 生命周期的特定时点自动执行」;事件约三十个,含 PreToolUse、PostToolUse、Stop、SessionStart、SessionEnd、Notification、UserPromptSubmit、SubagentStop 等;能力包括拦下工具调用、改写工具输入输出、注入上下文、发通知。截至 2026-07-17。hook 和 harness 的对话协议是退出码:exit 0 放行;exit 2 否决 —— 动作被拦下,你写进 stderr 的理由会交还给 Claude,它读了会调整做法。1注 1Claude Code Docs · Hooks guide —— hooks 提供确定性控制,「保证某些动作必然发生,而不是依赖 LLM 选择去做」。配置放 settings 文件的 hooks 块:事件名 → matcher(如 Edit|Write)→ type: command 的命令;/hooks 面板只读浏览,增改要编辑 settings 或让 Claude 代写。协议:事件数据以 JSON 进 stdin;exit 0 放行,exit 2 拦下且 stderr 作为反馈交还给 Claude 让它调整;更复杂的决定用 exit 0 + stdout JSON(如 PreToolUse 的 permissionDecision: deny)。PreToolUse 在权限模式检查之前执行 —— 返回 deny 的 hook 连 bypassPermissions 也拦得住。示例即文档所载:PostToolUse 用 jq 取 file_path 交给 prettier;PreToolUse 脚本对 .env、.git/ 等保护路径 exit 2。截至 2026-07-17。要更细的裁决(允许、询问、改写输入),exit 0 加一段 stdout JSON。1注 1Claude Code Docs · Hooks guide —— hooks 提供确定性控制,「保证某些动作必然发生,而不是依赖 LLM 选择去做」。配置放 settings 文件的 hooks 块:事件名 → matcher(如 Edit|Write)→ type: command 的命令;/hooks 面板只读浏览,增改要编辑 settings 或让 Claude 代写。协议:事件数据以 JSON 进 stdin;exit 0 放行,exit 2 拦下且 stderr 作为反馈交还给 Claude 让它调整;更复杂的决定用 exit 0 + stdout JSON(如 PreToolUse 的 permissionDecision: deny)。PreToolUse 在权限模式检查之前执行 —— 返回 deny 的 hook 连 bypassPermissions 也拦得住。示例即文档所载:PostToolUse 用 jq 取 file_path 交给 prettier;PreToolUse 脚本对 .env、.git/ 等保护路径 exit 2。截至 2026-07-17。 PreToolUse 有一个值得单独记住的性质:它跑在权限模式检查之前 —— 一条返回 deny 的 hook,连 bypassPermissions 模式也拦得住。1注 1Claude Code Docs · Hooks guide —— hooks 提供确定性控制,「保证某些动作必然发生,而不是依赖 LLM 选择去做」。配置放 settings 文件的 hooks 块:事件名 → matcher(如 Edit|Write)→ type: command 的命令;/hooks 面板只读浏览,增改要编辑 settings 或让 Claude 代写。协议:事件数据以 JSON 进 stdin;exit 0 放行,exit 2 拦下且 stderr 作为反馈交还给 Claude 让它调整;更复杂的决定用 exit 0 + stdout JSON(如 PreToolUse 的 permissionDecision: deny)。PreToolUse 在权限模式检查之前执行 —— 返回 deny 的 hook 连 bypassPermissions 也拦得住。示例即文档所载:PostToolUse 用 jq 取 file_path 交给 prettier;PreToolUse 脚本对 .env、.git/ 等保护路径 exit 2。截至 2026-07-17。这意味着 hooks 是比权限模式更硬的一层:模式管的是「问不问你」,hook 管的是「许不许做」。第 2 章那句「信不过的点用规矩钉死」,钉子就是它。

IV

真值得设的三类:安全闸、质量门、通知.

别一口气挂三十个事件。多数人长期真正在用的是三类:
类型挂在典型规矩
安全闸PreToolUse碰 .env、migrations、生产配置 → exit 2 拦下
质量门PostToolUse / Stop改完跑 lint;检查不过不许收工
通知Notification / SessionEnd等输入、收工时提醒你,人离开终端
安全闸的写法在文档里就有现成样:一个脚本核对目标路径,命中保护清单就 exit 2 并在 stderr 里说明为什么 —— Claude 收到理由,换路走。1注 1Claude Code Docs · Hooks guide —— hooks 提供确定性控制,「保证某些动作必然发生,而不是依赖 LLM 选择去做」。配置放 settings 文件的 hooks 块:事件名 → matcher(如 Edit|Write)→ type: command 的命令;/hooks 面板只读浏览,增改要编辑 settings 或让 Claude 代写。协议:事件数据以 JSON 进 stdin;exit 0 放行,exit 2 拦下且 stderr 作为反馈交还给 Claude 让它调整;更复杂的决定用 exit 0 + stdout JSON(如 PreToolUse 的 permissionDecision: deny)。PreToolUse 在权限模式检查之前执行 —— 返回 deny 的 hook 连 bypassPermissions 也拦得住。示例即文档所载:PostToolUse 用 jq 取 file_path 交给 prettier;PreToolUse 脚本对 .env、.git/ 等保护路径 exit 2。截至 2026-07-17。质量门的顶配是 Stop hook,第 9 章已经预告过:收工前跑你的检查,不过不放行;也记得它的边界 —— 连续拦 8 次后 harness 会放行,它是门,不是牢,兜底仍在测试和 CI。4注 4Claude Code Docs · Best practices —— 「hooks 用于必须每次发生、零例外的动作」,CLAUDE.md 的指令是建议性的,hooks 才是确定性的;可以直接让 Claude 替你写 hook;Stop hook 把检查设为收工硬门,连续拦 8 次后放行。截至 2026-07-17。 动手 · 一晚上装齐三类:

叮嘱是请求, hook 是规矩.

A reminder is a request; a hook is a rule.

Aklman Library

讨论

讨论.

评论区初始化中…