Claude Code 的 Hooks(事件钩子)系统是整个平台自动化能力的“中枢神经系统”。通过在 AI Agent 运行的关键生命周期节点上注册自定义处理程序,你可以实现危险操作拦截、审计日志记录、自动通知推送、上下文压缩管理、会话初始化等高级自动化场景。v2.1.154+ 版本还新增了 HTTP Hooks、异步 Hooks 和 LLM Prompt Hooks 三种高级处理程序类型,极大地...

Claude Code Hooks — 10 种事件钩子与高级特性全解

简介

Claude Code 的 Hooks(事件钩子)系统是整个平台自动化能力的“中枢神经系统”。通过在 AI Agent 运行的关键生命周期节点上注册自定义处理程序,你可以实现危险操作拦截、审计日志记录、自动通知推送、上下文压缩管理、会话初始化等高级自动化场景。v2.1.154+ 版本还新增了 HTTP Hooks、异步 Hooks 和 LLM Prompt Hooks 三种高级处理程序类型,极大地扩展了 Hooks 的能力边界。

如果把 Claude Code 比作一台全自动工厂,Hooks 就是安装在每个工位的传感器和控制阀门:你可以在工具执行前检查参数是否安全(PreToolUse),在工具执行后自动记录结果(PostToolUse),在上下文即将溢出前触发压缩(PreCompact),在会话启动时自动加载项目配置(SessionStart)。这种"在关键时刻插入自定义逻辑"的能力,是 Claude Code 从"好用的 AI 工具"跃升为"可编程的自动化平台"的核心要素。

本文将深度解析 10 种事件钩子 的配置方法、触发时机、使用场景,并介绍 HTTP、异步、LLM Prompt 三种高级处理程序类型,最后提供生产级别的安全钩子配置方案。无论你是想保护生产环境不被误操作破坏,还是想打造一套完整的开发审计系统,本文都能为你提供实用指南。

目录

一、Hooks 系统概述

1.1 事件生命周期总览

Claude Code 运行过程中会触发一系列事件,每种事件对应一个可注册的钩子:

text
用户启动 Claude Code
        │
        ▼
┌────────────────────┐
│  SessionStart      │ ← 会话初始化:加载配置、检查环境
└────────┬───────────┘
         │
         ▼
   用户输入提示词
         │
         ▼
┌────────────────────┐
│  UserPromptSubmit  │ ← 用户消息提交前:过滤、增强、校验
└────────┬───────────┘
         │
         ▼
   Claude Code 推理并选择工具
         │
         ▼
┌────────────────────┐
│  PreToolUse        │ ← 工具执行前:安全检查、参数修改、拒绝
└────────┬───────────┘
         │
         ▼
   工具实际执行(Bash/Edit/Write/Read...)
         │
         ▼
┌────────────────────┐
│  PostToolUse       │ ← 工具执行后:审计、触发后续操作
└────────┬───────────┘
         │
         ▼
   [可选] 上下文即将达到上限
         │
         ▼
┌────────────────────┐
│  PreCompact        │ ← 压缩前:保留关键信息、清理冗余
└────────┬───────────┘
         │
         ▼
   [可选] 收到系统通知
         │
         ▼
┌────────────────────┐
│  Notification      │ ← 通知处理:转发、过滤、自动响应
└────────┬───────────┘
         │
         ▼
   [可选] 用户停止 / Ctrl+C
         │
         ▼
┌────────────────────┐
│  Stop              │ ← 会话停止:清理、保存状态
└────────┬───────────┘
         │
         ▼
   [可选] 子 Agent 完成
         │
         ▼
┌────────────────────┐
│  SubagentStop      │ ← 子 Agent 停止:结果收集、日志记录
└────────────────────┘

1.2 10 种钩子速查表

钩子名称 触发时机 核心用途 可阻断
PreToolUse 工具执行前 安全检查、参数验证、危险拦截
PostToolUse 工具执行后 审计日志、自动测试、结果处理
Notification 收到通知时 通知转发、自动响应、过滤
Stop 会话停止时 清理资源、保存状态、统计报告
SubagentStop 子 Agent 停止时 结果收集、状态合并、日志记录
SessionEnd 会话完全结束时 最终清理、数据持久化、上报
PreCompact 上下文压缩前 保留关键信息、标记重要上下文
SessionStart 会话启动时 环境初始化、配置加载、欢迎消息
UserPromptSubmit 用户提交前 输入过滤、指令预处理、上下文增强
MessageDisplay 消息展示时 拦截/修改 Claude 输出内容

二、配置文件格式与环境变量

2.1 全局 vs 项目级配置

Hooks 可以在两个层级配置:

bash
# 全局配置 — 对所有项目生效
~/.claude/hooks.json

# 项目级配置 — 仅对当前项目生效
.claude/settings.json  # 在 "hooks" 字段中

项目级配置会覆盖全局配置中的同名钩子。推荐的实践是:在全局配置中定义安全相关的通用规则(如危险命令拦截),在项目级配置中定义业务相关的自动化规则(如自动测试、自动格式化)。

2.2 Hook 配置结构

每个钩子条目由三部分组成:

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "echo hello" }]
      }
    ]
  }
}

格式说明:Claude Code v2.1+ 统一使用上述“事件类型为键”的对象格式。早期版本和部分旧文档中使用的是数组格式("hooks": [{ "hook": "PreToolUse", ... }]),建议统一迁移到新格式以避免兼容性问题。

2.3 可用环境变量

不同钩子可访问的环境变量不同:

变量名 可用钩子 说明
CLAUDE_TOOL_NAME PreToolUse, PostToolUse 工具名称(Bash/Edit/Write 等)
CLAUDE_TOOL_INPUT PreToolUse, PostToolUse 工具的输入内容
CLAUDE_FILE_PATH PreToolUse, PostToolUse 被操作的文件路径
CLAUDE_USER_MESSAGE UserPromptSubmit 用户输入的原始消息
CLAUDE_SESSION_ID 所有钩子 当前会话的唯一标识
CLAUDE_PROJECT_DIR 所有钩子 项目根目录路径
CLAUDE_SUBAGENT_NAME SubagentStop 子 Agent 的名称
CLAUDE_COMPACT_REASON PreCompact 触发压缩的原因
CLAUDE_STOP_REASON Stop 会话停止的原因

三、PreToolUse 钩子

3.1 概述

PreToolUse 是 Hooks 系统中最重要、最常用的钩子类型。它在 Claude Code 调用任何工具(Bash、Edit、Write、Read、Glob 等)之前被触发,允许你:

  • 检查工具调用的参数是否安全
  • 修改工具调用的参数
  • 拒绝危险的调用(脚本退出码非 0 时拒绝执行)

3.2 示例 1:拦截危险命令

json
{
  "hooks": [
    {
      "hook": "PreToolUse",
      "matcher": "Bash",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "DANGEROUS_PATTERNS='rm -rf /|rm -rf ~|mkfs|dd if=/dev/zero|chmod -R 777 /|:>/etc/passwd'\nif echo \"$CLAUDE_TOOL_INPUT\" | grep -qiE \"$DANGEROUS_PATTERNS\"; then\n  echo \"🚫 DENY: 检测到危险命令: $CLAUDE_TOOL_INPUT\"\n  exit 1\nfi\necho \"✅ 命令检查通过\"\nexit 0"
          ],
          "env": {
            "CLAUDE_TOOL_INPUT": "{{tool_input}}"
          }
        }
      ]
    }
  ]
}

工作原理:脚本通过 grep -qiE 匹配一组已知的危险命令模式。如果匹配成功,输出拒绝信息并以 exit 1 终止,Claude Code 收到非零退出码后会阻止该工具的执行。

3.3 示例 2:保护关键文件

json
{
  "hooks": [
    {
      "hook": "PreToolUse",
      "matcher": "Write",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "# 禁止覆盖以下类型的关键文件\nPROTECTED='\\.env$|\\.pem$|\\.key$|id_rsa|secrets\\.json$|credentials\\.yaml$'\nif echo \"$CLAUDE_FILE_PATH\" | grep -qiE \"$PROTECTED\"; then\n  echo \"🚫 DENY: 禁止写入受保护文件: $CLAUDE_FILE_PATH\"\n  exit 1\nfi\nexit 0"
          ],
          "env": {
            "CLAUDE_FILE_PATH": "{{file_path}}"
          }
        }
      ]
    }
  ]
}

3.4 示例 3:限制 Git 操作范围

json
{
  "hooks": [
    {
      "hook": "PreToolUse",
      "matcher": "Bash",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "# 禁止 force push 到 main/master\nif echo \"$CLAUDE_TOOL_INPUT\" | grep -qE 'git push.*--force.*(main|master)'; then\n  echo \"🚫 DENY: 禁止强制推送到主分支\"\n  exit 1\nfi\n# 禁止删除远程分支\nif echo \"$CLAUDE_TOOL_INPUT\" | grep -qE 'git push.*--delete'; then\n  echo \"🚫 DENY: 禁止删除远程分支\"\n  exit 1\nfi\nexit 0"
          ],
          "env": {
            "CLAUDE_TOOL_INPUT": "{{tool_input}}"
          }
        }
      ]
    }
  ]
}

3.5 匹配器模式说明

matcher 字段支持三种匹配模式:

匹配类型 示例 说明
精确匹配 "matcher": "Bash" 仅匹配 Bash 工具
模式匹配 "matcher": "rm -rf" 匹配包含该模式的 Bash 命令
正则匹配 "matcher": ".*" 匹配所有工具调用

四、PostToolUse 钩子

4.1 概述

PostToolUse 在工具成功执行后触发,用于事后处理场景。与 PreToolUse 不同,PostToolUse 无法阻止工具执行(因为工具已经执行完毕),但可以做以下事情:

  • 记录审计日志
  • 自动运行测试
  • 触发后续操作(如 Git 暂存、代码格式化)
  • 发送通知

4.2 示例 1:自动审计日志

json
{
  "hooks": [
    {
      "hook": "PostToolUse",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "LOG_DIR=\"$CLAUDE_PROJECT_DIR/.claude/audit\"\nmkdir -p \"$LOG_DIR\"\necho \"[$(date '+%Y-%m-%d %H:%M:%S')] Session=$CLAUDE_SESSION_ID Tool=$CLAUDE_TOOL_NAME Path=${CLAUDE_FILE_PATH:-N/A}\" >> \"$LOG_DIR/tools.log\""
          ],
          "env": {
            "CLAUDE_TOOL_NAME": "{{tool_name}}",
            "CLAUDE_FILE_PATH": "{{file_path}}",
            "CLAUDE_PROJECT_DIR": "{{project_dir}}"
          }
        }
      ]
    }
  ]
}

4.3 示例 2:编辑后自动运行测试

json
{
  "hooks": [
    {
      "hook": "PostToolUse",
      "matcher": "Edit",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "FILE=\"$CLAUDE_FILE_PATH\"\n# 根据文件类型自动运行对应测试\nif echo \"$FILE\" | grep -q '\\.py$'; then\n  TEST_FILE=$(echo \"$FILE\" | sed 's|src/|tests/|;s|/test_|/|;s|\\.py$|_test\\.py|')\n  [ -f \"$TEST_FILE\" ] && pytest \"$TEST_FILE\" -q --tb=short || true\nelif echo \"$FILE\" | grep -q '\\.ts$\\|\\.tsx$'; then\n  npx jest --findRelatedTests \"$FILE\" --passWithNoTests 2>/dev/null || true\nfi"
          ],
          "env": {
            "CLAUDE_FILE_PATH": "{{file_path}}"
          }
        }
      ]
    }
  ]
}

4.4 示例 3:文件写入后自动 Git 暂存

json
{
  "hooks": [
    {
      "hook": "PostToolUse",
      "matcher": "Write",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "cd \"$CLAUDE_PROJECT_DIR\"\ngit add \"$CLAUDE_FILE_PATH\" 2>/dev/null\necho \"📝 已自动暂存: $CLAUDE_FILE_PATH\""
          ],
          "env": {
            "CLAUDE_FILE_PATH": "{{file_path}}",
            "CLAUDE_PROJECT_DIR": "{{project_dir}}"
          }
        }
      ]
    }
  ]
}

五、Notification 钩子

5.1 概述

Notification 钩子在 Claude Code 收到系统通知时触发。这些通知可能来自:

  • 长时间运行的后台任务完成
  • 外部系统集成事件(如 CI/CD 结果)
  • 用户通过通知面板发送的消息

5.2 示例 1:桌面通知推送

json
{
  "hooks": [
    {
      "hook": "Notification",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "# macOS 桌面通知\nif command -v osascript &>/dev/null; then\n  osascript -e \"display notification \\\"$CLAUDE_NOTIFICATION_BODY\\\" with title \\\"Claude Code 通知\\\"\"\nfi\n# Linux notify-desktop\nif command -v notify-send &>/dev/null; then\n  notify-send \"Claude Code\" \"$CLAUDE_NOTIFICATION_BODY\"\nfi"
          ],
          "env": {
            "CLAUDE_NOTIFICATION_BODY": "{{notification_body}}"
          }
        }
      ]
    }
  ]
}

5.3 示例 2:Slack 通知转发

json
{
  "hooks": [
    {
      "hook": "Notification",
      "matcher": "build.*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "curl -s -X POST \"$SLACK_WEBHOOK_URL\" \\\n  -H 'Content-type: application/json' \\\n  --data \"{\\\"text\\\": \\\"🔔 Claude Code 通知: $CLAUDE_NOTIFICATION_BODY\\\"}\""
          ],
          "env": {
            "CLAUDE_NOTIFICATION_BODY": "{{notification_body}}",
            "SLACK_WEBHOOK_URL": "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
          }
        }
      ]
    }
  ]
}

六、Stop 钩子

6.1 概述

Stop 钩子在用户主动停止 Claude Code 会话时触发(按 Ctrl+C 或发送停止信号)。适用于:

  • 保存中间状态
  • 清理临时文件
  • 生成会话统计报告
  • 发送会话结束通知

6.2 示例 1:会话统计报告

json
{
  "hooks": [
    {
      "hook": "Stop",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "REPORT=\"$CLAUDE_PROJECT_DIR/.claude/sessions/report-$(date '+%Y%m%d-%H%M%S').md\"\ncat > \"$REPORT\" << EOF\n# 会话终止报告\n\n- **会话 ID**: $CLAUDE_SESSION_ID\n- **终止原因**: $CLAUDE_STOP_REASON\n- **终止时间**: $(date '+%Y-%m-%d %H:%M:%S')\n- **项目目录**: $CLAUDE_PROJECT_DIR\n\n## 本次会话变更\n\n\\`\\`\\`bash\ngit diff --stat 2>/dev/null || echo '无 Git 变更'\n\\`\\`\\`\nEOF\necho \"📋 会话报告已保存: $REPORT\""
          ],
          "env": {
            "CLAUDE_STOP_REASON": "{{stop_reason}}",
            "CLAUDE_PROJECT_DIR": "{{project_dir}}"
          }
        }
      ]
    }
  ]
}

6.3 示例 2:清理临时文件

json
{
  "hooks": [
    {
      "hook": "Stop",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "# 清理 Claude Code 运行时产生的临时文件\nTMP_DIR=\"/tmp/claude-session-$CLAUDE_SESSION_ID\"\nif [ -d \"$TMP_DIR\" ]; then\n  echo \"🧹 清理临时目录: $TMP_DIR\"\n  rm -rf \"$TMP_DIR\"\nfi\n# 清理临时创建的分支(如果是在 review 会话中)\ncd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null\ngit branch --list 'claude-review-*' | xargs -r git branch -D 2>/dev/null\necho \"✅ 清理完成\""
          ]
        }
      ]
    }
  ]
}

七、SubagentStop 钩子

7.1 概述

当 Claude Code 派生的子 Agent(通过 --agent 模式或 bash 工具中的子进程)完成工作并停止时,SubagentStop 钩子被触发。这对于:

  • 收集子 Agent 的输出结果
  • 将子 Agent 的工作产物合并到主会话
  • 记录子 Agent 的执行统计

7.2 示例 1:子 Agent 结果收集

json
{
  "hooks": [
    {
      "hook": "SubagentStop",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "RESULT_DIR=\"$CLAUDE_PROJECT_DIR/.claude/subagents\"\nmkdir -p \"$RESULT_DIR\"\ncat > \"$RESULT_DIR/$CLAUDE_SUBAGENT_NAME-$(date '+%Y%m%d-%H%M%S').json\" << EOF\n{\n  \"subagent\": \"$CLAUDE_SUBAGENT_NAME\",\n  \"stopped_at\": \"$(date '+%Y-%m-%d %H:%M:%S')\",\n  \"exit_code\": $CLAUDE_SUBAGENT_EXIT_CODE,\n  \"session_id\": \"$CLAUDE_SESSION_ID\"\n}\nEOF\necho \"📦 子 Agent [$CLAUDE_SUBAGENT_NAME] 结果已保存\""
          ],
          "env": {
            "CLAUDE_SUBAGENT_NAME": "{{subagent_name}}",
            "CLAUDE_SUBAGENT_EXIT_CODE": "{{exit_code}}"
          }
        }
      ]
    }
  ]
}

7.3 示例 2:特定子 Agent 的定制处理

json
{
  "hooks": [
    {
      "hook": "SubagentStop",
      "matcher": "test-runner",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "# 专门处理测试运行器子 Agent 的结果\nif [ \"$CLAUDE_SUBAGENT_EXIT_CODE\" -eq 0 ]; then\n  echo \"✅ 测试全部通过\"\nelse\n  echo \"❌ 测试失败,退出码: $CLAUDE_SUBAGENT_EXIT_CODE\"\n  # 自动将失败信息写入报告\n  echo \"[FAIL] test-runner 退出码 $CLAUDE_SUBAGENT_EXIT_CODE\" >> \"$CLAUDE_PROJECT_DIR/.claude/test-results.log\"\nfi"
          ]
        }
      ]
    }
  ]
}

八、PreCompact 钩子

8.1 概述

当 Claude Code 的上下文窗口即将达到上限时,系统会自动触发上下文压缩(Compact)。PreCompact 钩子在压缩之前被触发,允许你:

  • 标记需要保留的关键上下文(如重要的代码片段、配置信息)
  • 清理无用的中间输出
  • 自定义压缩策略

这在长会话中非常关键——压缩会丢弃部分历史上下文,如果重要的指令或代码被压缩掉了,Claude Code 可能"忘记"你的要求。

8.2 示例 1:保留关键上下文

json
{
  "hooks": [
    {
      "hook": "PreCompact",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "# 在压缩前保存当前对话摘要\nSUMMARY_FILE=\"$CLAUDE_PROJECT_DIR/.claude/compact/summary-$(date '+%H%M%S').md\"\necho \"# 上下文压缩摘要\" > \"$SUMMARY_FILE\"\necho \"- 压缩原因: $CLAUDE_COMPACT_REASON\" >> \"$SUMMARY_FILE\"\necho \"- 压缩时间: $(date '+%Y-%m-%d %H:%M:%S')\" >> \"$SUMMARY_FILE\"\necho \"- 会话 ID: $CLAUDE_SESSION_ID\" >> \"$SUMMARY_FILE\"\n\n# 保存当前 Git 状态\ncd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null\necho \"## Git 状态\" >> \"$SUMMARY_FILE\"\ngit status --short >> \"$SUMMARY_FILE\" 2>/dev/null\necho \"\\n## 未暂存变更\" >> \"$SUMMARY_FILE\"\ngit diff --stat >> \"$SUMMARY_FILE\" 2>/dev/null\necho \"📝 压缩前摘要已保存: $SUMMARY_FILE\""
          ],
          "env": {
            "CLAUDE_COMPACT_REASON": "{{compact_reason}}",
            "CLAUDE_PROJECT_DIR": "{{project_dir}}"
          }
        }
      ]
    }
  ]
}

8.3 示例 2:压缩前自动保存待办事项

json
{
  "hooks": [
    {
      "hook": "PreCompact",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "# 检查是否有未完成的 TODO 注释\nif grep -rn 'TODO\\|FIXME\\|HACK' --include='*.py' --include='*.ts' --include='*.js' \"$CLAUDE_PROJECT_DIR/src/\" 2>/dev/null | head -20; then\n  echo \"⚠️ 发现未完成的 TODO 项,已记录到压缩摘要\"\nfi"
          ]
        }
      ]
    }
  ]
}

九、SessionStart 钩子

9.1 概述

SessionStart 钩子在 Claude Code 会话启动时触发,是初始化工作环境的理想位置:

  • 检查项目依赖是否安装
  • 加载项目特定的环境变量
  • 显示当前分支状态
  • 初始化审计目录

9.2 示例 1:项目环境初始化

json
{
  "hooks": [
    {
      "hook": "SessionStart",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "echo '🚀 Claude Code 会话启动'\necho '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'\necho \"📁 项目: $CLAUDE_PROJECT_DIR\"\n\n# 显示 Git 信息\ncd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null\nif [ -d .git ]; then\n  BRANCH=$(git branch --show-current 2>/dev/null)\n  echo \"🌿 分支: $BRANCH\"\n  UNSAVED=$(git status --porcelain 2>/dev/null | wc -l)\n  echo \"📝 未保存更改: $UNSAVED 个文件\"\nfi\n\n# 检查虚拟环境\nif [ -d .venv ]; then\n  echo \"🐍 Python 虚拟环境: 已激活\"\nelif [ -f package.json ]; then\n  echo \"📦 Node.js 项目: $(node --version 2>/dev/null)\"\nfi\n\n# 确保审计目录存在\nmkdir -p \"$CLAUDE_PROJECT_DIR/.claude/audit\"\nmkdir -p \"$CLAUDE_PROJECT_DIR/.claude/sessions\"\n\n# 记录会话启动\necho \"[$(date '+%Y-%m-%d %H:%M:%S')] Session=$CLAUDE_SESSION_ID Started\" >> \"$CLAUDE_PROJECT_DIR/.claude/audit/sessions.log\"\necho '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'"
          ]
        }
      ]
    }
  ]
}

9.3 示例 2:自动加载项目规则

json
{
  "hooks": [
    {
      "hook": "SessionStart",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "# 如果存在项目特定的 hooks 配置,加载它\nif [ -f \"$CLAUDE_PROJECT_DIR/.claude/hooks-extra.json\" ]; then\n  echo \"📋 加载项目扩展 Hooks 配置\"\n  cat \"$CLAUDE_PROJECT_DIR/.claude/hooks-extra.json\"\nfi\n\n# 检查是否有未解决的 merge conflict\ncd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null\nif git diff --name-only --diff-filter=U 2>/dev/null | grep -q .; then\n  echo \"⚠️ 警告: 当前分支存在未解决的合并冲突\"\n  git diff --name-only --diff-filter=U\nfi"
          ]
        }
      ]
    }
  ]
}

十、UserPromptSubmit 钩子

10.1 概述

UserPromptSubmit 钩子在用户消息提交给 Claude 模型之前触发,是输入层的第一道关卡:

  • 过滤敏感信息(如密码、API Key)
  • 自动附加项目上下文
  • 预处理特殊指令
  • 校验输入格式

10.2 示例 1:敏感信息过滤

json
{
  "hooks": [
    {
      "hook": "UserPromptSubmit",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "# 检测用户输入中是否包含敏感信息\nMSG=\"$CLAUDE_USER_MESSAGE\"\n\n# 检测 API Key 模式\nif echo \"$MSG\" | grep -qiE '(sk-[a-zA-Z0-9]{40,}|AKIA[0-9A-Z]{16}|ghp_[a-zA-Z0-9]{36})'; then\n  echo \"⚠️ 警告: 检测到可能的 API Key,建议不要在对话中直接粘贴密钥\"\n  echo \"建议使用环境变量或 .env 文件管理密钥\"\nfi\n\n# 检测密码模式\nif echo \"$MSG\" | grep -qiE 'password[=\": ]+[a-zA-Z0-9]{8,}'; then\n  echo \"⚠️ 警告: 检测到可能的明文密码,请注意信息安全\"\nfi\n\nexit 0"
          ],
          "env": {
            "CLAUDE_USER_MESSAGE": "{{user_message}}"
          }
        }
      ]
    }
  ]
}

10.3 示例 2:自动附加上下文

json
{
  "hooks": [
    {
      "hook": "UserPromptSubmit",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "# 在用户提交消息时自动附加当前环境信息\ncd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null\nBRANCH=$(git branch --show-current 2>/dev/null || echo 'unknown')\nLAST_COMMIT=$(git log -1 --format='%h %s' 2>/dev/null || echo 'N/A')\necho \"[环境] 分支=$BRANCH | 最新提交=$LAST_COMMIT\""
          ]
        }
      ]
    }
  ]
}

十一、SessionEnd 钩子

11.1 概述

SessionEnd 在会话完全结束(包括所有子进程清理完毕)后触发,与 Stop 的区别在于:Stop 在用户主动停止时立即触发,而 SessionEnd 等待所有资源释放后才触发。这是做数据持久化和最终上报的最佳位置。

11.2 示例:会话数据上报

json
{
  "hooks": {
    "SessionEnd": [
      {
        "matcher": ".*",
        "hooks": [
          {
            "type": "command",
            "command": "bash",
            "args": [
              "-c",
              "REPORT=\"$CLAUDE_PROJECT_DIR/.claude/analytics/sessions.jsonl\"\nmkdir -p \"$(dirname $REPORT)\"\necho \"{\\\"session\\\": \\\"$CLAUDE_SESSION_ID\\\", \\\"ended_at\\\": \\\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\\\", \\\"project\\\": \\\"$(basename $CLAUDE_PROJECT_DIR)\\\"}\" >> \"$REPORT\""
            ]
          }
        ]
      }
    ]
  }
}

十二、MessageDisplay 钩子

12.1 概述

MessageDisplay 是 v2.1.154 新增的钩子,在 Claude 的回复消息展示给用户之前触发。它允许你拦截、过滤或修改 Claude 的输出内容,非常适合用于:

  • 过滤输出中的敏感信息(如 API Key、内部 URL)
  • 自动为输出添加格式化标记
  • 在特定关键词出现时触发额外操作

12.2 示例:过滤输出中的敏感信息

json
{
  "hooks": {
    "MessageDisplay": [
      {
        "matcher": ".*",
        "hooks": [
          {
            "type": "command",
            "command": "bash",
            "args": [
              "-c",
              "MSG=\"$CLAUDE_MESSAGE\"\n# 替换可能的 API Key 为占位符\nCLEANED=$(echo \"$MSG\" | sed -E 's/sk-[a-zA-Z0-9]{40,}/[REDACTED_API_KEY]/g')\nif [ \"$MSG\" != \"$CLEANED\" ]; then\n  echo \"$CLEANED\"\n  exit 2  # exit 2 = 用新内容替换原消息\nfi\nexit 0  # exit 0 = 不修改"
            ],
            "env": {
              "CLAUDE_MESSAGE": "{{message}}"
            }
          }
        ]
      }
    ]
  }
}

退出码说明

  • exit 0:不修改输出,正常展示
  • exit 2:用脚本的 stdout 内容替换原消息
  • 非零非 2:阻止消息展示

十三、HTTP Hooks

13.1 概述

除了传统的 shell 命令,Claude Code 还支持将 HTTP 端点作为钩子处理程序。这使得你可以将钩子事件转发到远程服务(如 Webhook、API、监控系统),实现跨系统的自动化联动。

13.2 配置格式

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "http",
            "url": "https://api.company.com/claude/audit",
            "method": "POST",
            "headers": {
              "Authorization": "Bearer ${AUDIT_API_TOKEN}",
              "Content-Type": "application/json"
            },
            "timeout": 5000
          }
        ]
      }
    ]
  }
}

13.3 适用场景

  • 将操作日志发送到企业 SIEM 系统
  • 在工具执行后触发 CI/CD Webhook
  • 远程调用合规检查服务
  • 与 Slack/Teams 集成发送实时通知

踩坑经验:HTTP Hooks 默认等待响应,超时时间为 5 秒。如果远程服务响应慢,会拖慢 Claude Code 的整体执行速度。对于不需要等待结果的场景,建议结合异步 Hooks 使用。

十四、异步 Hooks

14.1 概述

普通钩子是同步执行的,会阻塞 Claude Code 的主流程直到钩子完成。异步 Hooks 允许你在后台执行耗时操作,不干扰 Claude Code 的正常工作流。

14.2 配置方法

在钩子配置中添加 "async": true 字段:

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "bash",
            "args": ["-c", "sleep 2 && npx eslint --fix $CLAUDE_FILE_PATH"],
            "async": true,
            "env": {
              "CLAUDE_FILE_PATH": "{{file_path}}"
            }
          }
        ]
      }
    ]
  }
}

14.3 适用场景

  • 耗时的代码格式化或 lint 检查(如 ESLint、Prettier)
  • 上传审计日志到远程服务器
  • 触发耗时的测试套件
  • 构建产物或生成文档

注意:异步钩子无法阻断操作(因为主流程不会等待),也无法通过 stdout 向 Claude Code 传递反馈。如果需要阻断行为,必须使用同步钩子。

十五、LLM Prompt Hooks

15.1 概述

LLM Prompt Hooks 是 v2.1.154 引入的最新特性,允许你使用另一个 AI 模型来处理钩子事件。这意味着你可以用自然语言定义钩子逻辑,而无需编写 shell 脚本。

15.2 配置格式

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "检查这个 shell 命令是否会删除或覆盖任何用户数据。如果会,输出 BLOCK 并解释原因;否则输出 ALLOW。",
            "model": "claude-haiku-3-20240307"
          }
        ]
      }
    ]
  }
}

15.3 适用场景

  • 复杂的安全策略判断(难以用正则表达)
  • 代码质量审查(自然语言描述规范)
  • 上下文相关的智能过滤
  • 业务逻辑验证(需要理解代码语义)

踩坑经验:Prompt Hooks 会额外消耗一次 API 调用,对于高频触发的事件(如每次文件读取),可能导致成本快速上升。建议仅在对 PreToolUse 等高价值事件使用,并搭配 matcher 缩小触发范围。

十六、安全钩子配置方案

16.1 生产级完整安全配置

以下是一套面向生产环境的完整安全钩子配置,建议保存为 ~/.claude/hooks.json

json
{
  "hooks": [
    {
      "_comment": "=== 第 1 层:危险命令拦截 ===",
      "hook": "PreToolUse",
      "matcher": "Bash",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "DANGEROUS='rm -rf /|rm -rf ~[^/]|mkfs|dd if=|:>.*passwd|chmod -R 777 /|wget.*\\|.*sh|curl.*\\|.*bash|nc -e|ncat -e|reverse.*shell'\nif echo \"$CLAUDE_TOOL_INPUT\" | grep -qiE \"$DANGEROUS\"; then\n  echo \"🚫 [安全拦截] 危险命令已阻止\"\n  echo \"命令: $CLAUDE_TOOL_INPUT\"\n  exit 1\nfi\nexit 0"
          ],
          "env": {
            "CLAUDE_TOOL_INPUT": "{{tool_input}}"
          }
        }
      ]
    },
    {
      "_comment": "=== 第 2 层:关键文件保护 ===",
      "hook": "PreToolUse",
      "matcher": "Write",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "PROTECTED='\\.env$|\\.pem$|id_rsa|authorized_keys|shadow$|sudoers$|\\.kube/config$|secrets\\.(json|yaml|yml)$'\nif echo \"$CLAUDE_FILE_PATH\" | grep -qiE \"$PROTECTED\"; then\n  echo \"🚫 [安全拦截] 禁止写入受保护文件: $CLAUDE_FILE_PATH\"\n  exit 1\nfi\nexit 0"
          ],
          "env": {
            "CLAUDE_FILE_PATH": "{{file_path}}"
          }
        }
      ]
    },
    {
      "_comment": "=== 第 3 层:Git 分支保护 ===",
      "hook": "PreToolUse",
      "matcher": "git push.*--force",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "if echo \"$CLAUDE_TOOL_INPUT\" | grep -qE '(main|master|production|release)'; then\n  echo \"🚫 [安全拦截] 禁止 force push 到保护分支\"\n  exit 1\nfi\nexit 0"
          ],
          "env": {
            "CLAUDE_TOOL_INPUT": "{{tool_input}}"
          }
        }
      ]
    },
    {
      "_comment": "=== 第 4 层:敏感信息检测 ===",
      "hook": "UserPromptSubmit",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "MSG=\"$CLAUDE_USER_MESSAGE\"\n# 检测 API Key / Token / 密码\nif echo \"$MSG\" | grep -qiE '(sk-[a-zA-Z0-9]{20,}|ghp_|AKIA|password[=\": ]+[a-zA-Z0-9]{6,})'; then\n  echo \"⚠️ [安全警告] 输入中可能包含敏感信息,请注意安全\"\nfi\nexit 0"
          ],
          "env": {
            "CLAUDE_USER_MESSAGE": "{{user_message}}"
          }
        }
      ]
    },
    {
      "_comment": "=== 第 5 层:全量审计日志 ===",
      "hook": "PostToolUse",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "LOG=\"$CLAUDE_PROJECT_DIR/.claude/audit/$(date '+%Y-%m-%d').log\"\nmkdir -p \"$(dirname $LOG)\"\necho \"[$(date '+%H:%M:%S')] $CLAUDE_TOOL_NAME → ${CLAUDE_FILE_PATH:-<no-path>}\" >> \"$LOG\""
          ],
          "env": {
            "CLAUDE_TOOL_NAME": "{{tool_name}}",
            "CLAUDE_FILE_PATH": "{{file_path}}"
          }
        }
      ]
    },
    {
      "_comment": "=== 第 6 层:会话启动安全检查 ===",
      "hook": "SessionStart",
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash",
          "args": [
            "-c",
            "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null\n# 检查是否有未提交的敏感文件\nif git ls-files '*.env *.pem id_rsa secrets.*' 2>/dev/null | grep -q .; then\n  echo \"⚠️ 警告: 仓库中包含可能的敏感文件\"\n  git ls-files '*.env *.pem id_rsa secrets.*'\nfi\n# 创建审计目录\nmkdir -p \"$CLAUDE_PROJECT_DIR/.claude/audit\""
          ]
        }
      ]
    }
  ]
}

16.2 安全配置层级说明

上述配置采用**纵深防御(Defense in Depth)**策略:

层级 钩子 防护目标
第 1 层 PreToolUse (Bash) 拦截系统级危险命令
第 2 层 PreToolUse (Write) 保护敏感文件不被覆盖
第 3 层 PreToolUse (Git) 防止 force push 到保护分支
第 4 层 UserPromptSubmit 检测输入中的敏感信息泄露
第 5 层 PostToolUse 全量操作审计记录
第 6 层 SessionStart 启动时的安全检查与初始化

16.3 团队级 Hooks 分发

在团队项目中,建议通过版本控制分发 Hooks 配置:

bash
# 在项目的 .claude/settings.json 中引用团队标准配置
{
  "hooks": [
    // 从版本库加载的团队标准安全策略
  ]
}

# 使用 CLAUDE_CONFIG_PATH 环境变量指定配置路径
export CLAUDE_CONFIG_PATH=/shared/team-hooks/
claude

十七、最佳实践

17.1 1. 钩子脚本应快速执行

Hooks 会阻塞 Claude Code 的主流程,因此钩子脚本应当:

  • 避免网络请求(除非是异步的)
  • 避免长时间运行的计算
  • 超时时间建议 < 2 秒

17.2 2. 使用 exit 0 / exit 1 明确信号

  • exit 0 = 允许继续
  • exit 1 = 阻止操作(仅 PreToolUse 等可阻断的钩子)

17.3 3. 日志输出格式统一

bash
echo "🚫 [安全拦截] ..."    # 阻止
echo "⚠️ [安全警告] ..."    # 警告
echo "✅ [检查通过] ..."    # 放行

17.4 4. 项目级覆盖全局

安全规则放在全局(~/.claude/hooks.json),业务规则放在项目级(.claude/settings.json)。

十八、真实经验与踩坑

18.1 经验 1:PostToolUse 钩子拖慢编码速度

  • 场景:PostToolUse 钩子对每次文件编辑后运行 ESLint 格式化(同步执行)
  • 问题:每次编辑一个大文件(2000 行)需等待 8 秒,一个下午累计浪费 15 分钟
  • 解决方案:将格式化钩子改为 "async": true(异步执行),或者仅在 PostToolUse 中运行轻量级检查,将完整格式化放在 Stop 钩子中统一执行

18.2 经验 2:PreToolUse 正则匹配过于粗糙

  • 场景:PreToolUse 钩子用 grep -qiE "rm -rf" 拦截所有删除操作
  • 问题:安全的清理操作 rm -rf build/ 也被拦截,严重影响开发效率
  • 解决方案:改用精确正则 rm -rf /[^ ]*$|rm -rf ~[^ /],仅拦截根目录和用户目录的删除,允许项目内的清理操作

十九、落地检查清单

  • Claude Code 版本 >= v2.1.154(支持最新 Hook 格式和高级特性)
  • ~/.claude/hooks.json.claude/settings.json 中的 Hook 配置使用对象格式(非旧版数组格式)
  • PreToolUse 的危险命令拦截已测试可阻断 rm -rf /dd if=/dev/zero
  • PostToolUse 的审计日志可正常写入 .claude/audit/ 目录
  • SessionStart 的环境初始化可正常执行
  • 钩子脚本执行时间 < 2 秒,不会拖慢 Claude Code
  • 敏感信息检测已覆盖 API Key、密码、Token 等常见格式
  • 耗时操作已配置为异步钩子("async": true
  • LLM Prompt Hooks 搭配 matcher 缩小触发范围,控制额外 API 调用成本
  • 团队已通过版本控制共享统一的 Hook 配置

二十、总结

Hooks 系统是 Claude Code 从“AI 辅助工具”升级为“可编程自动化平台”的核心机制。本文介绍的 10 种事件钩子覆盖了 Claude Code 运行生命周期的每一个关键节点:

  • PreToolUse — 工具执行前的最后一道安全闸门
  • PostToolUse — 工具执行后的审计与自动化触发点
  • Notification — 通知事件的自定义处理入口
  • Stop — 会话停止时的清理与统计
  • SubagentStop — 子 Agent 工作结果的收集点
  • SessionEnd — 会话完全结束后的数据持久化与上报
  • PreCompact — 上下文压缩前的信息保留机制
  • SessionStart — 会话启动时的环境初始化
  • UserPromptSubmit — 用户输入的第一道过滤关卡
  • MessageDisplay — Claude 输出内容的拦截与脱敏

以及三种高级处理程序类型:HTTP Hooks(远程端点调用)、异步 Hooks(非阻塞执行)和 LLM Prompt Hooks(AI 驱动的智能判断)。

通过合理组合这些钩子与处理程序类型,你可以构建出涵盖安全防护、审计追踪、自动化测试、通知推送等完整功能的企业级开发工作流。

二十一、下篇预告

Git 工作树与 PR Review 实战 — 深入学习 Claude Code 的 -w 隔离工作树功能、--from-pr 命令和 /review Slash 命令,掌握如何在独立的工作树中进行安全的代码审查,完全不干扰主开发分支的正常工作。