Hooks 钩子与确定性自动化:让 Claude Code 真正自动运转

6 阅读4分钟

合集:Claude Code Skills 系列 · 中级篇(二)


前言

Skills 是"建议性的"——Claude 读取 Skill 内容后,决定如何执行。但有些操作需要强制执行、每次必做、不容遗忘,这就是 Hooks(钩子)的价值所在。

Hooks 是在 Claude Code 特定生命周期事件触发的确定性脚本,与 AI 的"理解和判断"无关——无论 AI 想不想,钩子都会执行。国内使用Claude Code 访问ccAiHub.com


一、Hooks 的核心概念

1.1 Hooks vs Skills

特性SkillsHooks
执行者AI(基于理解)Shell 脚本(强制执行)
可靠性依赖 AI 理解正确100% 确定性
适用场景有判断力的任务固定流程、安全卡点
配置方式SKILL.md 文件settings.json
灵活性

简单说:需要判断的事交给 Skills,需要强制执行的事交给 Hooks。

1.2 生命周期事件

Claude Code 提供三个钩子挂载点:

用户输入请求
    ↓
Claude 决定调用工具
    ↓ ← PreToolUse 钩子(工具执行前)
工具执行
    ↓ ← PostToolUse 钩子(工具执行后)
Claude 完成回复
    ↓ ← Stop 钩子(对话结束时)

二、配置 Hooks

2.1 基本配置结构

.claude/settings.json 中配置:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo '即将执行 Bash 命令' >> .claude/hook.log"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\" || true"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/post-session.sh || true"
          }
        ]
      }
    ]
  }
}

2.2 Matcher 匹配规则

matcher 字段决定这个 Hook 对哪些工具触发:

// 匹配所有 Bash 命令
{ "matcher": "Bash" }

// 匹配特定 Bash 命令(包含 rm 的命令)
{ "matcher": "Bash(rm *)" }

// 匹配 Edit 工具(文件编辑)
{ "matcher": "Edit" }

// 匹配 Write 工具(文件写入)
{ "matcher": "Write" }

// 无 matcher = 匹配所有工具
{ "hooks": [...] }

2.3 可用的环境变量

在钩子脚本中,Claude Code 会注入以下环境变量:

变量内容
CLAUDE_TOOL_NAME工具名称(如 Bash, Edit
CLAUDE_TOOL_INPUT工具的完整输入(JSON 格式)
CLAUDE_TOOL_INPUT_COMMANDBash 工具的命令字符串
CLAUDE_TOOL_INPUT_FILE_PATHEdit/Write 工具的文件路径
CLAUDE_TOOL_OUTPUT工具的输出结果(PostToolUse 可用)

三、实战案例

3.1 案例一:危险命令拦截(PreToolUse)

目标:阻止 Claude 执行 git push --force 等危险命令。

.claude/hooks/block-dangerous.sh

#!/bin/bash
# 危险命令拦截器

COMMAND="$CLAUDE_TOOL_INPUT_COMMAND"

# 定义危险模式
DANGEROUS_PATTERNS=(
    "git push --force"
    "git push -f"
    "rm -rf /"
    "rm -rf *"
    "DROP TABLE"
    "DROP DATABASE"
    "> /dev/sda"
)

for pattern in "${DANGEROUS_PATTERNS[@]}"; do
    if echo "$COMMAND" | grep -qi "$pattern"; then
        echo "❌ 拦截了危险命令:$pattern"
        echo "命令:$COMMAND"
        exit 1  # 非零退出码 = 阻止执行
    fi
done

exit 0  # 正常退出 = 允许执行

配置:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/block-dangerous.sh"
          }
        ]
      }
    ]
  }
}

效果:当 Claude 试图执行 git push --force 时,脚本返回非零退出码,Claude Code 会拒绝执行并通知用户。

3.2 案例二:自动格式化(PostToolUse)

目标:Claude 每次编辑文件后,自动运行格式化。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/auto-format.sh"
          }
        ]
      }
    ]
  }
}

.claude/hooks/auto-format.sh

#!/bin/bash
FILE="$CLAUDE_TOOL_INPUT_FILE_PATH"

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

# 根据文件类型选择格式化工具
case "$FILE" in
    *.ts|*.tsx|*.js|*.jsx)
        npx prettier --write "$FILE" 2>/dev/null || true
        ;;
    *.py)
        black "$FILE" 2>/dev/null || true
        ;;
    *.go)
        gofmt -w "$FILE" 2>/dev/null || true
        ;;
    *.rs)
        rustfmt "$FILE" 2>/dev/null || true
        ;;
esac

echo "✅ 已格式化:$FILE"

注意:使用 || true 防止格式化工具报错导致整个会话崩溃。

3.3 案例三:会话结束通知(Stop)

目标:Claude 完成任务后,发送桌面通知。

.claude/hooks/notify-done.sh

#!/bin/bash
# 跨平台桌面通知

MESSAGE="Claude Code 任务完成"
TIMESTAMP=$(date "+%H:%M:%S")

case "$(uname -s)" in
    Darwin)
        # macOS
        osascript -e "display notification \"$TIMESTAMP\" with title \"$MESSAGE\""
        ;;
    Linux)
        # Linux(需要 notify-send)
        notify-send "$MESSAGE" "$TIMESTAMP" 2>/dev/null || true
        ;;
    MINGW*|MSYS*|CYGWIN*)
        # Windows
        powershell -Command "
            Add-Type -AssemblyName System.Windows.Forms
            \$notification = New-Object System.Windows.Forms.NotifyIcon
            \$notification.Icon = [System.Drawing.SystemIcons]::Information
            \$notification.BalloonTipTitle = '$MESSAGE'
            \$notification.BalloonTipText = '$TIMESTAMP'
            \$notification.Visible = \$True
            \$notification.ShowBalloonTip(3000)
        " 2>/dev/null || true
        ;;
esac

配置:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/notify-done.sh || true"
          }
        ]
      }
    ]
  }
}

3.4 案例四:自动记录操作日志(PostToolUse)

目标:记录 Claude 对哪些文件做了什么操作,便于审计。

{
  "hooks": {
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/audit-log.sh || true"
          }
        ]
      }
    ]
  }
}

.claude/hooks/audit-log.sh

#!/bin/bash
LOG_FILE=".claude/audit.log"
TIMESTAMP=$(date -Iseconds)
TOOL="$CLAUDE_TOOL_NAME"
INPUT="$CLAUDE_TOOL_INPUT"

# 记录到日志(只记录关键信息,不记录完整输出避免日志过大)
echo "$TIMESTAMP | $TOOL | $(echo "$INPUT" | jq -r '.command // .file_path // "N/A"' 2>/dev/null || echo 'N/A')" >> "$LOG_FILE"

审计日志示例:

2024-01-15T14:23:01+08:00 | Bash | git diff --staged
2024-01-15T14:23:05+08:00 | Edit | src/auth/login.ts
2024-01-15T14:23:12+08:00 | Bash | npx vitest run auth
2024-01-15T14:23:45+08:00 | Write | docs/api/openapi.yaml

四、通过 /hooks 命令配置

除了直接编辑 JSON,也可以用交互式界面:

# 在 Claude Code 中
/hooks

这会打开配置向导,按提示选择:

  1. 事件类型(PreToolUse / PostToolUse / Stop)
  2. 匹配规则
  3. 要执行的命令

五、Hooks 最佳实践

5.1 始终用 || true 保护非关键钩子

# 格式化失败不应该阻断工作流
npx prettier --write "$FILE" || true

# 通知失败不应该影响会话
notify-send "Done" || true

5.2 关键安全钩子:明确返回非零码

# 安全检查失败必须明确阻断
if is_dangerous_command "$COMMAND"; then
    echo "❌ 危险命令已拦截"
    exit 1  # 必须显式返回非零
fi

5.3 不要在 PostToolUse 中读取被编辑的文件

Claude Code 在 PostToolUse 触发时,文件已经写入磁盘,但如果你的钩子又去读这个文件,会产生额外的系统提示("文件已被外部修改"),干扰 Claude 的上下文。

正确做法:在 Stop 钩子中批量处理,而不是每次编辑后都读取。

5.4 钩子脚本放入版本控制

# 推荐放在项目中
.claude/hooks/
├── block-dangerous.sh
├── auto-format.sh
├── notify-done.sh
└── audit-log.sh

提交到 Git,让整个团队受益于相同的安全卡点。


六、本篇小结

钩子类型典型用途关键技巧
PreToolUse安全检查、访问控制返回非零码阻断执行
PostToolUse自动格式化、日志记录用 `true` 保护非关键操作
Stop通知、清理、汇总适合批量处理,避免频繁触发

下一篇是中级篇的最后一篇:团队协作与 Git 共享实践,学习如何把 Skills 和配置体系高效地在团队中落地。


系列导航

  • 初级篇(一):什么是 Skills,为什么需要它
  • 初级篇(二):从零创建你的第一个斜杠命令
  • 初级篇(三):CLAUDE.md 与配置体系入门
  • 中级篇(一):自动触发机制与工具权限精细控制
  • 中级篇(二):Hooks 钩子与确定性自动化 ← 当前
  • 中级篇(三):团队协作与 Git 共享实践
  • 高级篇(一):CI/CD 集成与无头模式
  • 高级篇(二):动态上下文注入与多文件 Skill
  • 高级篇(三):多智能体编排与复杂工作流