第 06 章

Hooks

事件驱动自动化——在关键时刻自动执行约束

不再靠人自觉 — 让质量门禁变成自动化的

靠人自觉不靠谱

你有没有发现,"记得做"的事情经常被忘掉?

提交前忘跑测试

团队规定"提交前必须跑测试"。但你急着下班一敲 git commit 就走了。第二天 CI 红了,全组等你修。

不小心提交密钥

"别提交密钥"——说了无数遍。但某次赶进度,.env 文件混进了提交。密钥泄露,连夜轮换。

代码忘了格式化

审查 PR 时发现满屏的格式问题。不是什么大 bug,但每次都要花时间指出同样的格式问题,审查者和提交者都很烦。

Hooks 就是为这个设计的:在特定事件发生时自动执行检查——写入文件前自动格式化、提交前自动扫描敏感信息、每次操作后自动记录审计日志。不合规的操作直接被阻断,不靠人自觉。

开始之前

适合谁

  • 需要把"规范检查、日志记录、自动修复"融入日常工作流的个人和团队
  • 厌倦了口头约定"记得做 XXX"却总是被忘掉的人
  • 了解命令和 Skill 的基本使用,想让流程更自动化的开发者

读完后你应该已经能

  • 说清 Hooks 的触发时机和能做什么
  • 设计典型用例的 Hook(安全门禁、格式校验、质量检查)
  • 理解失败策略(提醒 / 阻断 / 自动修复)的选择依据

什么是 Hook

定义 → 白话 → 能做什么

Hook是在 Claude Code 事件发生时自动执行的 shell 命令。它们在特定动作的前后触发,不需要手动操作。

说白了,Hook 就是一个"自动触发的检查点"——你设好"什么时候检查"和"检查什么",之后每次到那个时间点,它就自动跑一遍。过了就放行,没过就拦截或提醒。

校验

检查输入/输出是否符合预期

格式化

写入前自动格式化代码

告警

发现问题时提醒用户

阻断

阻止不合规的操作执行

审计

记录所有操作到日志

自动修复

检测到问题后自动修正

四类事件

Hook 能挂在哪些时间点上

图:Hook 事件时间线——一次工具调用的完整生命周期中各 Hook 挂载点
类别事件典型用途
Tool PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest 格式化、安全扫描、日志、自动审批
Session SessionStart、SessionEnd、Stop、StopFailure、SubagentStart、SubagentStop 初始化、清理、审计、子 agent 追踪
Task UserPromptSubmit、TaskCompleted、TaskCreated、TeammateIdle 输入校验、任务追踪、协作调度
Lifecycle ConfigChange、CwdChanged、FileChanged、PreCompact、PostCompact、WorktreeCreate、WorktreeRemove、Notification、InstructionsLoaded、Elicitation、ElicitationResult 环境变更、上下文管理、工作树、通知、交互问答
最常用的两个:PreToolUse(工具执行前拦截或修改)和 PostToolUse(工具执行后校验或记录)。先掌握这两个,其他的按需取用。

配置方式

在 settings.json 中声明 Hook

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Write",
      "hooks": ["~/.claude/hooks/format-code.sh"]
    }],
    "PostToolUse": [{
      "matcher": "Write",
      "hooks": [{
        "type": "command",
        "command": "prettier --write $CLAUDE_FILE_PATH"
      }]
    }]
  }
}

两种 hook 格式

脚本路径(字符串):适合复杂逻辑,指向一个 .sh 文件。

内联命令(对象):适合一行命令搞定的事,如 {"type": "command", "command": "prettier --write $CLAUDE_FILE_PATH"}。不用单独写脚本文件。

Hook 可用的环境变量

  • $CLAUDE_FILE_PATH — 当前操作的文件路径
  • $CLAUDE_TOOL_NAME — 当前使用的工具名
  • $CLAUDE_SESSION_ID — 当前会话 ID
  • $CLAUDE_PROJECT_DIR — 项目目录路径

三种结果

退出码决定 Hook 的行为——通过、提醒、阻断

图:Hook 执行决策流程——从触发到最终结果的分支判断
退出码效果什么时候用
0通过,操作正常继续检查全部通过
1提醒,不阻断但提示用户非致命问题,建议关注
2阻断,操作被取消严重违规,必须拦截
建议:新 Hook 先设为"提醒"模式(exit 1)观察一段时间。确认没有误报后再升级为"阻断"(exit 2)。一上来就阻断,容易把正常工作流也卡住。

安全门禁

写入文件前自动扫描敏感信息

# 文件:~/.claude/hooks/security-scan.sh
#!/bin/bash

FILE="$CLAUDE_FILE_PATH"
CONTENT=$(cat "$FILE" 2>/dev/null)

# 检查是否包含密钥模式
if echo "$CONTENT" | grep -qE '(AKIA|sk-|api_key|secret|password)\s*[:=]'; then
    echo "🚨 检测到可能的密钥或敏感信息!"
    echo "请检查文件: $FILE"
    exit 2  # 阻断操作
fi

echo "✅ 安全检查通过"
exit 0
这个 Hook 用 exit 2(阻断):密钥泄露是严重安全问题,宁可误报也不能放过。如果你担心误报,可以先改成 exit 1(提醒)观察一周。

格式校验

确保生成的 API 文档包含必需字段

# 文件:~/.claude/hooks/check-api-docs.sh
#!/bin/bash

FILE="$CLAUDE_FILE_PATH"

if [[ "$FILE" == *api-docs* ]]; then
    for section in "## 概述" "## 参数" "## 返回值" "## 示例"; do
        if ! grep -q "$section" "$FILE"; then
            echo "⚠️ API 文档缺少必需章节: $section"
            exit 1  # 提醒但不阻断
        fi
    done
fi

exit 0
这个 Hook 用 exit 1(提醒):缺少章节不是致命错误,但需要引起注意。Claude 看到提醒后会自行补充。

质量检查

提交前自动运行测试

# 文件:~/.claude/hooks/pre-commit-test.sh
#!/bin/bash
# PreToolUse hook for Bash(git commit*)

if ! npm test 2>/dev/null; then
    echo "❌ 测试失败!请修复后再提交。"
    exit 2  # 阻断提交
fi

echo "✅ 所有测试通过"
exit 0
三段式理解 Hooks:触发点(什么时候)→ 动作(做什么)→ 结果(通过/提醒/阻断)。把这三个想清楚,任何 Hook 都能设计出来。

更多示例

三个常见场景的 Hook 脚本

记录 Bash 命令

# log-bash.sh — PostToolUse
#!/bin/bash
echo "[$(date '+%Y-%m-%d %H:%M')] " \
  "cmd=$CLAUDE_TOOL_NAME " \
  "file=$CLAUDE_FILE_PATH" \
  >> ~/.claude/hooks/audit.log
exit 0

每次工具调用后写审计日志,exit 0 不阻断。

校验用户输入

# validate-prompt.sh — UserPromptSubmit
#!/bin/bash
PROMPT=$(cat)
if echo "$PROMPT" | grep -qiE '(删除所有|drop table)'; then
  echo "⚠️ 输入包含高危关键词"
  exit 1
fi
exit 0

检测用户输入中的危险指令,提醒但不阻断。

发送团队通知

# notify-team.sh — Notification
#!/bin/bash
MSG=$(cat)
curl -s -X POST "$WEBHOOK_URL" \
  -H 'Content-type: application/json' \
  -d "{\"text\":\"$MSG\"}" \
  >/dev/null 2>&1
exit 0

通过 Webhook 把通知转发到团队聊天工具。

安装与最佳实践

把 Hook 脚本放到正确位置,并给执行权限

# 1. 创建 hooks 目录
$ mkdir -p ~/.claude/hooks

# 2. 复制 Hook 脚本
$ cp security-scan.sh ~/.claude/hooks/
$ cp format-code.sh ~/.claude/hooks/

# 3. 赋予执行权限
$ chmod +x ~/.claude/hooks/*.sh

# 4. 在 settings.json 中配置触发规则
配置位置影响范围适合放什么
~/.claude/settings.json所有项目个人通用的安全扫描、格式化
.claude/settings.json当前项目团队共享的质量门禁、项目规范

性能要求

Hook 执行时间应控制在 1 秒以内。超时的 Hook 会拖慢整个工作流。耗时操作(完整测试套件)放 CI,Hook 只做快速检查。

权限提示自动化

PermissionRequest 事件可以自动审批特定工具调用。比如自动允许 Read 操作,但 Bash(rm *) 仍然需要手动确认——减少弹窗但不降低安全性。

单一职责

每个 Hook 只做一件事。不要在一个脚本里同时格式化、扫描密钥、跑测试。分开写便于调试、单独开关、逐步升级失败策略。

先本地再上线

新 Hook 先用 exit 1(提醒模式)在本地观察几天。确认没有误报后再改成 exit 2(阻断)。不要一上来就阻断——会把正常工作流卡住。

不要在 Hook 里放复杂业务逻辑。Hook 不是应用——它应该是短小的检查脚本。复杂逻辑容易出错、难维护、拖慢速度。对副作用保持谨慎:Hook 修改文件或发送请求时要考虑失败场景。

排错指南

误报太多

症状:规则太严,正常操作也被阻断,同事开始绕过 Hook。

修复:先降级为"提醒"模式(exit 1)。收集一周的告警数据,调整规则后再升级为"阻断"。

Hook 太慢

症状:每次操作都要等好几秒,拖慢整个工作流。

修复:保持 Hook 短小精悍。避免在 Hook 中跑完整测试套件——只做最关键的快速检查。耗时操作放到 CI。

别人跑不了

症状:你的 Hook 在别人机器上跑不通。

修复:用标准工具,不依赖特定环境。如果必须依赖,在 Hook 开头检查依赖是否可用,不可用则 exit 0(静默跳过)。

CI 环境差异

症状:本地 Hook 正常,CI 环境行为不同。

修复:注意环境变量和路径差异。CI 环境可能没有交互式权限确认——用 --permission-mode 控制。

配置语法错误

症状:Hook 配了但不生效,Claude Code 启动时报错或静默忽略。

修复:用 cat ~/.claude/settings.json | python3 -m json.tool 检查 JSON 语法。常见问题:缺少逗号、括号不匹配、数组/对象嵌套错误。

版本不兼容

症状:某些事件在旧版 Claude Code 中不被识别。

修复:运行 claude --version 检查版本。新事件(如 WorktreeCreate、Elicitation)需要较新版本。参考 更新日志 确认支持情况。

动手练习

设计一个"防止提交临时代码"的 Hook

题目:在提交前检查代码中是否有 console.log、debugger、TODO: 等临时调试代码。发现时提醒,但不阻断。
  • 确定触发点(哪个事件)
  • 编写检查逻辑(扫描什么内容)
  • 决定失败策略(提醒 or 阻断)
  • 思考:为什么这个场景用"提醒"而不是"阻断"?

参考答案

# 文件:~/.claude/hooks/check-debug-code.sh
#!/bin/bash
# PreToolUse hook for Bash(git commit*)

STAGED=$(git diff --cached --name-only --diff-filter=ACM \
  | grep -E '\.(js|ts|jsx|tsx)$')

if [ -z "$STAGED" ]; then
    exit 0
fi

FOUND=0
for file in $STAGED; do
    if git diff --cached "$file" | grep -qE \
      '^\+.*(console\.log|debugger|TODO:)'; then
        echo "⚠️ $file 中包含调试代码"
        FOUND=1
    fi
done

if [ $FOUND -eq 1 ]; then
    echo ""
    echo "发现临时调试代码,建议清理后再提交。"
    echo "如确认需要提交,可忽略此提醒。"
    exit 1  # 提醒但不阻断
fi

exit 0
为什么用提醒而非阻断:有些 console.log 是故意留的调试日志,有些 TODO: 是标记后续优化点。硬性阻断会误伤正常开发。提醒给开发者选择权。

本章回顾

到这里,你已经能

  • 说清 Hooks 解决什么问题
  • 理解四类触发时机和三种失败策略
  • 设计安全门禁、格式校验、质量检查三类 Hook
  • 判断一个检查该"提醒"还是"阻断"

它和前后章节的关系

  • Subagents — Hook 可以在 agent 执行前后做校验
  • MCP — Hook 可以校验 MCP 调用的输入输出
  • Hooks — 自动化的质量门禁
  • Plugins — 把 Hooks 打包成可安装的团队标准
核心结论:Hooks 把"应该做但容易忘"的事变成自动化的。好的 Hook = 精准的触发点 + 最小的开销 + 清晰的失败策略。先观察再阻断,别一上来就锁死。

延伸阅读

继续本课程

官方文档

本章源文件 (点击查看完整内容)

加载中…