1. 什么是 Hook?

Hook = 自动触发器,在 Claude Code 执行的关键时刻,自动运行你写的脚本——让 Claude 更像一个守纪律的工程师,而不是一个”有时候会忘事”的助理。

核心区别

你可以在 prompt 或 CLAUDE.md 里写”每次改完代码都要跑测试”,Claude 大概率会照做。但如果它在一个复杂任务里专注起来,可能就”忘了”(因为上下文太长了)。

Hook 是不同的东西:只要触发条件满足,脚本就一定运行,不依赖 Claude 的”记忆”。

2. Hooks 有什么用?

典型用途:

  • 自动格式化:Claude 改完文件,Prettier/Black 自动跑,不用多说一个字
  • 安全拦截:Claude 想执行 rm -rfgit push --force main,直接阻断
  • 自动注入上下文:每次会话开始,自动把当前 git 状态、未完成的 TODO 告诉 Claude
  • 任务完成检查:Claude 说”我做完了”,先跑一遍测试,没过就不让它停
  • 桌面通知:Claude 等你回复时,发一条通知,你不用一直盯着屏幕
  • 日志与备份:自动记录每次操作,或在上下文压缩前备份对话

3. 配置在哪里?

两种位置(和 CLAUDE.md 的逻辑类似):

位置路径适用范围
个人全局~/.claude/settings.json你所有项目
项目级.claude/settings.json当前项目(覆盖全局设置)

配置格式固定,三层嵌套:事件 → 匹配器 → 处理器:

{
  "hooks": {
    "事件名": [
      {
        "matcher": "正则,匹配哪些工具",
        "hooks": [
          {
            "type": "command",
            "command": "你的 shell 命令"
          }
        ]
      }
    ]
  }
}

matcher 留空 "" 则对所有情况都触发。多个 hooks 数组里的命令会并行执行

4. 全部 12 个生命周期事件

事件触发时机可阻断?说明
SessionStart会话开始stdout 被自动注入到 Claude 的上下文
SessionEnd会话结束(超时 1.5s)清理、备份、日志上报
PreToolUseClaude 要使用工具之前权限校验、参数修正、安全拦截
PostToolUse工具执行完后自动格式化、日志记录、但不能撤销已执行的操作
NotificationClaude 需要你的注意时发送桌面通知
StopClaude 想停止任务之前强制跑测试,不通过不让停
SubagentStart子 Agent 启动前设置子 Agent 的上下文
SubagentStop子 Agent 停止前检查子 Agent 的工作成果
ToolError工具执行出错记录错误、发送告警
FileChange文件被修改跟踪代码变更、触发重新编译
ContextLimitApproach上下文接近满决定是否压缩历史或生成摘要
ErrorOccurredClaude 出现错误记录、告警、对话恢复

5. 退出码——控制流程的信号

对于 PreToolUseStopSubagentStop 等可阻断的事件,你的脚本用退出码来告诉 Claude 该怎么办:

退出码含义使用场景
0通过,继续执行默认选择
1警告,但不阻断(Claude 照样执行)记录风险
2拦截,阻止操作(需把原因输出到 stderr)安全保护

常见踩坑

⚠️ 新手最容易踩的坑:用 exit 1 以为能拦截危险命令——不行,exit 1 只是警告,Claude 照样执行。安全类 Hook 必须用 exit 2

另一个细节:exit 2 要把原因输出到 stderr,不是 stdout。Claude 只会读取 stderr 的内容来理解”为什么被阻断”,stdout 的内容是给用户看的(在 transcript 模式下可见)。

6. 四种处理器类型

① command(最常用)

运行 shell 命令,通过 stdin 接收 JSON:

{
  "type": "command",
  "command": "python .claude/hooks/check.py",
  "timeout": 30
}

② http(2026年2月新增)

把事件 POST 到你的服务器,适合团队共享审计系统或远程验证:

{
  "type": "http",
  "url": "http://localhost:8080/hooks/pre-tool-use",
  "headers": { "Authorization": "Bearer $MY_TOKEN" },
  "allowedEnvVars": ["MY_TOKEN"]
}

注意:HTTP hook 不能靠 4xx/5xx 来阻断操作——非 2xx 响应只会产生一个非阻断警告。要真正阻断,需要返回 2xx 状态码 + JSON body 里写 decision: "deny"

③ prompt

让 Claude 本身来判断,返回 yes/no,适合语义判断:

{
  "type": "prompt",
  "prompt": "判断这个 Bash 命令是否可能影响生产环境:$ARGUMENTS。如果是,返回 {\"ok\": false, \"reason\": \"...\"}",
  "timeout": 30
}

④ agent

启动一个子 Agent,可以用 Read、Grep、Glob 工具深度核查代码库,再做决策。比 prompt 更彻底,但更慢(默认超时 60s vs prompt 的 30s):

{
  "type": "agent",
  "prompt": "检查被修改的文件是否都有对应的测试文件",
  "timeout": 60
}

7. 异步 Hook(2026年1月新增)

加一个 "async": true,Hook 就在后台运行,不阻塞 Claude 继续工作:

{
  "type": "command",
  "command": "node backup-script.js",
  "async": true,
  "timeout": 30
}

适合异步的场景:日志记录、桌面通知、备份——这些不需要结果,后台跑就行。

不适合异步的场景:安全拦截、权限审批——你需要结果才能决定”放行还是阻断”。

8. 进阶:JSON 输出精确控制

退出码只能说”通过/阻断”,如果想更精细地控制,可以在 stdout 输出 JSON:

让 PreToolUse 修改工具的输入参数

比如:把错误的命令自动纠正:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "安全的只读操作,自动审批",
    "updatedInput": { "command": "修正后的命令" },
    "additionalContext": "给 Claude 看的额外说明"
  }
}

让 Stop Hook 强制 Claude 继续工作

比如:测试没过就不让停:

{
  "decision": "block",
  "reason": "测试未通过,请先修复再结束任务。"
}

控制优先级从高到低:"continue": false(最终否决)→ JSON "decision": "block"exit 2

9. 实用示例(从简单到复杂)

示例 1:自动格式化(推荐第一个试这个)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\""
          }
        ]
      }
    ]
  }
}

示例 2:安全拦截危险命令(Python 版,更易维护)

.claude/hooks/guard.py

#!/usr/bin/env python3
import json, sys, re

DANGEROUS = [
    r'\brm\s+.*-[a-z]*r[a-z]*f',
    r'git\s+push\s+--force.*main',
    r'DROP\s+TABLE',
    r'chmod\s+777',
]

data = json.load(sys.stdin)
if data.get('tool_name') == 'Bash':
    cmd = data.get('tool_input', {}).get('command', '')
    for pattern in DANGEROUS:
        if re.search(pattern, cmd, re.IGNORECASE):
            print(f"被拦截:匹配到危险指令 `{pattern}`", file=sys.stderr)
            sys.exit(2)

sys.exit(0)

Settings.json 配置:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python .claude/hooks/guard.py"
          }
        ]
      }
    ]
  }
}

示例 3:会话开始自动注入上下文

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo '## 当前 Git 状态' && git status --short && echo '## 未完成 TODO' && grep -r 'TODO:' src/ | head -5"
          }
        ]
      }
    ]
  }
}

SessionStart hook 的 stdout 会被自动注入进 Claude 的上下文,它真的会读到这些信息。

示例 4:任务完成前强制跑测试

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "npm test 2>&1 || (echo '测试未通过,不能停止任务!' >&2 && exit 2)"
          }
        ]
      }
    ]
  }
}

⚠️ 防止死循环:Stop hook 里的脚本 exit 2 会让 Claude 继续工作,然后再次触发 Stop hook……记得检查 stop_hook_active 字段:

INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # 已经触发过一次了,放行
fi
npm test 2>&1 || (echo '测试未通过!' >&2 && exit 2)

示例 5:桌面通知(macOS)

{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude 需要你的注意\" with title \"Claude Code\"'",
            "async": true
          }
        ]
      }
    ]
  }
}

示例 6:文件修改时自动构建

{
  "hooks": {
    "FileChange": [
      {
        "matcher": "src/.*\\.(ts|tsx)$",
        "hooks": [
          {
            "type": "command",
            "command": "npm run build",
            "async": true
          }
        ]
      }
    ]
  }
}

示例 7:上下文接近满时自动压缩

{
  "hooks": {
    "ContextLimitApproach": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "当前对话已接近上下文限制。应该压缩历史对话还是生成任务总结?",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

示例 8:远程审计日志(HTTP Hook)

{
  "hooks": {
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "http",
            "url": "http://audit-server:8080/log",
            "headers": {
              "Authorization": "Bearer $AUDIT_TOKEN",
              "X-Project-ID": "myblog"
            },
            "allowedEnvVars": ["AUDIT_TOKEN"],
            "async": true
          }
        ]
      }
    ]
  }
}

10. 可用的环境变量

Hook 运行时,Claude Code 会自动提供一些变量:

变量说明示例
CLAUDE_SESSION_ID当前会话 ID550e8400-e29b-41d4-a716-446655440000
CLAUDE_TOOL_NAME使用的工具名Write, Bash, Read
CLAUDE_TOOL_INPUT_FILE_PATH文件工具的路径/src/main.ts
CLAUDE_TOOL_INPUT_COMMANDBash 命令npm run build
CLAUDE_WORKING_DIRECTORY当前工作目录/Users/harper_by/Documents/myblog
CLAUDE_PROJECT_ROOT项目根目录/Users/harper_by/Documents/myblog

更丰富的输入通过 stdin 传入 JSON,包含 tool_nametool_inputsession_id 等字段,用 json.load(sys.stdin) 读取。

11. 管理与调试

临时禁用所有 Hook

紧急情况下:

CLAUDE_SKIP_HOOKS=1 claude

开启调试日志

CLAUDE_DEBUG=1 claude

手动测试 Hook 脚本

不用真的触发 Claude:

echo '{"tool_name": "Bash", "tool_input": {"command": "rm -rf /"}}' | python .claude/hooks/guard.py
echo "退出码:$?"

12. 注意事项

  • 同步 Hook 会阻塞 Claude:脚本要尽量快,耗时操作加 "async": true
  • 安全审查.claude/settings.json 会进版本控制,克隆陌生项目前先检查这个文件,就像审查源代码一样
  • 脚本要有执行权限chmod +x .claude/hooks/my-script.sh
  • 默认超时:command 和 prompt hook 是 10 分钟,SessionEnd hook 是 1.5 秒(可用 CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS 调整)

13. 社区资源