Skip to content

Claude Code Hooks 是一套事件驱动自动化机制,允许你在 Claude Code 会话中,针对如 PreToolUse、PostToolUse、Stop 等关键事件,自动执行校验、格式化、安全扫描等脚本。支持 command、prompt、HTTP、agent 四种 Hook 类型,结合 matcher 精准匹配工具,实现从 pre-commit 安全扫描到代码格式化的全流程自动化。本文将通过实用示例、JSON 配置和脚本模板,手把手教你用 Hooks 构建高效、可控的 AI 编程环境。

Claude Code Hooks 完全指南:用事件驱动让 AI 自动执行校验和格式化

Claude Code Hooks 是什么?它是一组可以在 Claude Code 会话生命周期内,自动响应事件并执行自定义脚本的自动化机制。通过 Hooks,开发者可以在 AI 生成或修改代码前后,自动进行安全校验、格式化、权限管理、上下文监控等操作,极大提升 AI 编程的安全性和规范性。

如果你已掌握 Claude Code 的基础用法,Hooks 能让你的开发流程从“AI 辅助”跃升到“AI 驱动自动化”。本文将系统讲解 Hooks 的类型、事件、配置方式,并以“pre-commit 自动安全扫描”为核心案例,提供可直接复用的 JSON 配置和脚本模板。


一、Claude Code Hooks 能做什么?

Claude Code Hooks 通过监听会话中的特定事件(如工具调用前后、用户提交提示、会话结束等),自动执行你预设的脚本,实现:

  • 代码提交前自动安全扫描(如检测 hardcoded 密钥、敏感操作)
  • 文件写入后自动格式化(如 JS/TS 用 Prettier,Python 用 Black)
  • 用户输入 prompt 时自动过滤危险请求
  • 任务完成后用 LLM 智能判定是否真正完成
  • 实时追踪上下文 token 使用量,防止超限

这些自动化动作不仅提升了开发效率,也大幅降低了安全和合规风险。


二、Hook 的四大类型

Claude Code Hooks 支持四种类型,满足不同自动化场景:

  1. Command(命令行脚本)
    最常用。执行本地 shell/python 脚本,通过 stdin/stdout 传递 JSON 数据,适合校验、格式化、日志等操作。

  2. Prompt(LLM 提示)
    直接让 Claude AI 评估某个条件,如智能判断任务是否完成,适合 Stop/SubagentStop 等事件。

  3. HTTP(Webhook)
    远程 POST JSON 到指定 URL,适合与外部服务、CI/CD、审计系统集成。需显式配置 allowedEnvVars 保证安全。

  4. Agent(子代理)
    启动专用 subagent 进行多步复杂校验,如自动查阅设计文档、跨文件检查等,适合高阶 AI 审查。


三、五大核心 Hook 事件(及典型用法)

Claude Code 支持 25 种事件,最常用的五个如下:

事件名触发时机常见用途
PreToolUse工具执行前校验命令、阻断危险操作
PostToolUse工具执行后格式化、扫描、反馈上下文
UserPromptSubmit用户提交 prompt过滤敏感/危险请求
StopClaude 响应结束智能判定任务是否完成
SubagentStop子代理任务结束子代理结果复核

事件与 Hook 类型可自由组合,如 PreToolUse 可用 command 校验 Bash 命令,PostToolUse 可用 command 格式化文件,Stop 可用 prompt 让 AI 判断是否完成任务。


四、核心示例:pre-commit 自动安全扫描

以“AI 代码写入前自动安全扫描”为例,完整演示配置与脚本。

1. 配置 JSON(.claude/settings.json)

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/pre-commit.py\"",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

2. 校验脚本(.claude/hooks/pre-commit.py)

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

def main():
    data = json.load(sys.stdin)
    cmd = data.get("tool_input", {}).get("command", "")
    # 检查是否为 git commit
    if re.match(r"git\s+commit", cmd):
        # 这里可调用 npm test/pytest/go test/cargo test 等
        # 简单示例:阻断 commit -m "WIP"
        if re.search(r"WIP", cmd):
            print("阻止 WIP commit", file=sys.stderr)
            sys.exit(2)  # exit 2 = 阻断
    sys.exit(0)

if __name__ == "__main__":
    main()

3. 脚本权限

bash
chmod +x .claude/hooks/pre-commit.py

4. 工作原理

  • 每次 Bash 工具调用前,PreToolUse 事件触发
  • matcher 精确匹配 Bash 工具
  • 脚本分析命令内容,检测是否为 git commit,并可根据实际需求阻断或放行
  • exit 2 阻断操作,stderr 信息会在 Claude Code 中显示

你也可以用 shell 脚本实现更复杂的多语言测试逻辑,详见下方 Bash 版本模板。

Bash 版 pre-commit 示例(支持 Node/Python/Go/Rust)

bash
#!/bin/bash
# .claude/hooks/pre-commit.sh
echo "🧪 Running tests before commit..."

# Node.js
if [ -f "package.json" ]; then
  npm test || exit 1
fi
# Python
if [ -f "pytest.ini" ] || [ -f "setup.py" ]; then
  pytest || exit 1
fi
# Go
if [ -f "go.mod" ]; then
  go test ./... || exit 1
fi
# Rust
if [ -f "Cargo.toml" ]; then
  cargo test || exit 1
fi

echo "✅ All tests passed! Proceeding with commit."
exit 0

五、自动格式化:PostToolUse + 格式化脚本

自动格式化是 Claude Code Hooks 的高频场景。以下为 PostToolUse 事件结合格式化脚本的配置:

1. 配置 JSON

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/format-code.sh\"",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

2. 格式化脚本(.claude/hooks/format-code.sh)

bash
#!/bin/bash
FILE=$1
case "$FILE" in
  *.js|*.ts) prettier --write "$FILE" ;;
  *.py) black "$FILE" ;;
  *.go) gofmt -w "$FILE" ;;
  *.rs) rustfmt "$FILE" ;;
esac
exit 0

结合 Slash CommandsSkills 体系,你还可以将格式化能力封装为复用型 AI 技能。


六、上下文监控实用技巧:context-tracker

Claude Code 支持用 Hooks 实时追踪 token 消耗,避免上下文溢出。推荐用法如下:

1. 配置 JSON

json
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/context-tracker.py\""
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/context-tracker.py\""
          }
        ]
      }
    ]
  }
}

2. context-tracker.py 脚本

python
#!/usr/bin/env python3
import json, os, sys, tempfile

CONTEXT_LIMIT = 128000  # Claude 的上下文窗口

def get_state_file(session_id):
    return os.path.join(tempfile.gettempdir(), f"claude-context-{session_id}.json")

def count_tokens(text):
    return len(text) // 4  # 粗略估算

def read_transcript(path):
    if not path or not os.path.exists(path): return ""
    content = []
    with open(path) as f:
        for line in f:
            try:
                entry = json.loads(line.strip())
                if "message" in entry:
                    msg = entry["message"]
                    if isinstance(msg.get("content"), str):
                        content.append(msg["content"])
            except: continue
    return "\n".join(content)

def handle_user_prompt_submit(data):
    session_id = data.get("session_id", "unknown")
    transcript_path = data.get("transcript_path", "")
    tokens = count_tokens(read_transcript(transcript_path))
    with open(get_state_file(session_id), "w") as f:
        json.dump({"pre_tokens": tokens}, f)

def handle_stop(data):
    session_id = data.get("session_id", "unknown")
    transcript_path = data.get("transcript_path", "")
    tokens = count_tokens(read_transcript(transcript_path))
    state_file = get_state_file(session_id)
    pre_tokens = 0
    if os.path.exists(state_file):
        with open(state_file) as f:
            pre_tokens = json.load(f).get("pre_tokens", 0)
    delta = tokens - pre_tokens
    remaining = CONTEXT_LIMIT - tokens
    pct = (tokens / CONTEXT_LIMIT) * 100
    print(f"Context: ~{tokens} tokens ({pct:.1f}% used, ~{remaining} remaining)", file=sys.stderr)
    if delta > 0:
        print(f"This request: ~{delta} tokens", file=sys.stderr)

def main():
    data = json.load(sys.stdin)
    event = data.get("hook_event_name", "")
    if event == "UserPromptSubmit":
        handle_user_prompt_submit(data)
    elif event == "Stop":
        handle_stop(data)
    sys.exit(0)

if __name__ == "__main__":
    main()

效果:每次请求和响应后,stderr 输出本轮 token 消耗和剩余量,帮助你实时把控上下文窗口。


七、进阶用法与最佳实践

  • 组件级 Hook:可在 skill/agent 的 frontmatter 直接定义,支持 PreToolUse/PostToolUse/Stop 三类事件,便于封装复用。
  • 插件级 Hook:插件可在 hooks.json 内集成自定义 Hook,结合 Claude Code Plugins 实现即插即用。
  • MCP 工具 Hook:支持 mcp__server__tool 形式 matcher,实现跨服务自动化。
  • 安全建议:Hooks 执行任意脚本,务必验证输入、避免路径穿越,生产环境先在本地充分测试。

八、安装与调试

  1. 创建 hooks 目录

    bash
    mkdir -p ~/.claude/hooks
  2. 复制并授权脚本

    bash
    cp your-hooks/*.sh ~/.claude/hooks/
    chmod +x ~/.claude/hooks/*.sh
  3. 编辑配置文件
    修改 ~/.claude/settings.json.claude/settings.json,粘贴上文 JSON 配置。

  4. 调试技巧

    • claude --debug 查看详细日志
    • Ctrl+O 开启 verbose mode
    • 独立测试脚本:
      bash
      echo '{"tool_name": "Bash", "tool_input": {"command": "ls"}}' | python3 .claude/hooks/pre-commit.py
      echo $?

FAQ

Q: Hooks 会影响 Claude Code 的性能吗?
A: 合理配置下影响极小。建议为复杂脚本设置超时时间(如 10-30 秒),避免阻塞主流程。

Q: 如何只让某个 Hook 在每次 session 启动时运行一次?
A: 在 hook 配置中加 "once": true,即可实现只在每次会话首次触发时运行。

Q: Hooks 能否与 Plugins、Skills 等机制结合?
A: 可以。插件可集成 hooks.json,skills/agents 支持 frontmatter 级 hooks,灵活组合自动化能力。


通过灵活配置 Claude Code Hooks,你可以让 AI 自动完成校验、格式化、安全检查等繁琐任务,打造高效、安全、智能的开发工作流。更多进阶用法,参考 Claude Code 高级功能全解六大扩展机制对比