06 · Hook 与自动化

06 · Hook 与自动化

唯一的硬保证。出现"每次"就该想到它。

开场比喻

对前端开发者,Claude Code 的 hook 最像 Git hooks——概念几乎 1:1 对应:

Git hookClaude Code hook
pre-commit 拦截提交PreToolUse 拦截工具调用
post-commit 提交后跑通知PostToolUse 工具调用后跑
prepare-commit-msg 注入模板UserPromptSubmit 注入 context

Git hook 的道理在 Claude Code 同样成立:真正重要的事不靠程序员记得,靠 hook 强制

为什么这篇最重要

前面几篇讲的都是软约束(CLAUDE.md、auto-memory、skill)——Claude 应该记得、应该遵守。

但 Claude 会:

  • 判断错
  • 在复杂任务里顾此失彼

硬约束唯一靠得住的,只有 hook

记忆 vs Hook:最关键的分水岭

这是全系列最重要的一张表,记住它能避免 80% 的误用:

你想要的效果选哪个
"Claude 知道项目用 pnpm"记忆(CLAUDE.md)
"Claude 遇到 X 时参考 Y 文档"记忆 + skill
"每次改 TS 文件都必须过 typecheck"Hook
"提交前必须跑测试"Hook(git pre-commit)
"禁止 Claude 触碰 legacy/"Hook(PreToolUse)
"停止响应时提醒我有未提交改动"Hook(Stop)

识别信号词——prompt 里出现下面任何一个,多半该 hook:

每次…… / 从此以后…… / 一旦…… / 只要…… / 必须…… / 总是……

这些都是自动化需求,不是偏好。记忆解决不了。

Hook Event 全景

Claude Code 支持的主要 event:

Event触发时机典型用法
PreToolUse工具调用前(可阻断禁区保护、敏感操作二次确认
PostToolUse工具调用后Edit 后跑 typecheck、Write 后 format
UserPromptSubmit用户按 Enter 后注入当天 TODO、注入项目状态
StopClaude 结束响应提醒未提交、跑总结命令
SessionStart会话开始注入欢迎信息、检查环境
SubagentStop子 Agent 结束子 Agent 交付后自动 verify
NotificationClaude Code 发通知转发到 Slack / 系统通知
PreCompact自动压缩前保存关键上下文到文件

日常最常用的四个:PreToolUse / PostToolUse / UserPromptSubmit / Stop。

Hook 的契约

Hook 是一条命令行。它通过四个通道和 Claude Code 通信:

通道含义
环境变量$CLAUDE_TOOL_INPUT 等)读当前事件数据
stdout写输出 → 注入到 Claude 的 context
stderr写错误 → 在 exit 2 时反馈给 Claude
exit code0 = 放行;2 = 阻断并把 stderr 喂给 Claude;其他非 0 = warning

exit 2 的 stderr 是 hook 最强大的地方——它不是报错终止,而是把错误作为反馈给 Claude,让 Claude 自己调整后重试。

四个能直接抄的实战 Hook

① 禁区保护(PreToolUse + 阻断)

场景:"legacy/ 目录不可修改"——CLAUDE.md 里写过,但 Claude 偶尔会忘。

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{
        "type": "command",
        "command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -q '\"file_path\":\"[^\"]*legacy/'; then echo 'BLOCKED: legacy/ 是冻结目录,不要修改。如确需改动,请和用户讨论后用临时权限。' >&2; exit 2; fi"
      }]
    }]
  }
}

效果:Claude 一旦想写 legacy/xxx.ts → hook 抛 exit 2 → Claude 看到 stderr 信息 → 自己意识到并换方案

② TS 改完自动跑 typecheck(PostToolUse)

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{
        "type": "command",
        "command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -qE '\\.(ts|tsx)\"'; then pnpm tsc --noEmit 2>&1 | tail -30; fi"
      }]
    }]
  }
}

效果:每次改 .ts / .tsx,自动跑 typecheck,错误注入 Claude 的 context——Claude 立即看到类型错误并修。

注意:hook 会阻塞主流程,不要在这跑几十秒的构建。大项目可以只跑增量 typecheck 或只 lint 变动文件。

③ Stop 时提醒未提交(Stop)

{
  "hooks": {
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "if [[ -n $(git status --porcelain 2>/dev/null) ]]; then echo \"⚠️  还有未提交改动:\"; git status --short; fi"
      }]
    }]
  }
}

效果:每次 Claude 停下来,若有未提交改动就在 terminal 输出提醒——防止你切任务时忘掉。

④ 注入每日 TODO(UserPromptSubmit)

{
  "hooks": {
    "UserPromptSubmit": [{
      "hooks": [{
        "type": "command",
        "command": "if [ -f ~/today-todo.md ]; then echo '---今日 TODO---'; cat ~/today-todo.md; fi"
      }]
    }]
  }
}

效果:每次你输入 prompt 前,把 ~/today-todo.md 内容注入 context——Claude 每次都知道你今天的重点。

设计五条铁律

铁律原因
① 快hook 阻塞主流程;超过 3 秒就是灾难
② 幂等hook 可能连续触发,有副作用累积就炸
③ 失败可读exit 2 的 stderr 要清楚说明原因和改法
④ matcher 精准Edit|Write.* 好;配合 path 过滤更佳
⑤ 少即是多每个 hook 都给所有工具调用加税,只加真重要的

六大反模式

① 记忆能搞定的硬做 hook

"让 Claude 用简洁语气"——是偏好不是强制,写 CLAUDE.md 够了。强加 hook 是过度工程。

② Hook 里跑超过 3 秒的任务

每次都等 tests 跑完 → Claude 每步都卡 10 秒 → 崩溃。长任务走 CI,不走 hook。

③ Matcher 太宽

"matcher": ".*"      // 所有工具都跑,包括 Read / Grep — 灾难

应该精确到真正重要的工具(Edit / Write / Bash)。

④ 无感 hook

exit 2 但 stderr 空 → Claude 不知为何被阻断 → 乱试。阻断必须带理由

⑤ 鼓励绕过

hook 报错时,绝不能因为"太烦"就 --no-verify 或临时关 hook——hook 存在是有原因的。

⑥ Skill 能做的事做成 hook

"用户说 '加地区' 时走一个流程"—— 这是 skill(按需触发)。做成 hook 会在无关场景也跑。

Hook 存哪

<repo>/.claude/settings.json           ← 项目级,入 git(团队共享)
<repo>/.claude/settings.local.json     ← 本机覆盖,不入 git
~/.claude/settings.json                ← 用户级,跨项目

建议分布

  • 项目级放项目特有的保护(禁区、项目测试命令)
  • 用户级放跨项目习惯("stop 时提醒 git status"这种通用的)
  • local 放只有你这台机器有的(本地路径、账号)

一句话总结

记忆靠 Claude 自觉,skill 靠匹配触发,只有 hook 是"只要我在,就会跑"。

把"每次必须"级别的事交给 hook,把"遇到时考虑"级别的事留给记忆和 skill——位置摆对,效果才硬。

判断速查

一条需求来了

出现"每次" / "必须" / "一旦"?── 否 → 记忆 or skill
  ↓ 是
动作频繁(每次工具调用级别)?── 否 → 可能是 skill
  ↓ 是
执行时间 < 3 秒?             ── 否 → 走 CI,不走 hook
  ↓ 是
能用 matcher 精准框定范围?    ── 否 → 先收窄场景
  ↓ 是
Hook ✓

← 05 · 子 Agent 的成本账 | 目录 | 07 · Commit / PR 工作流 →