Claude Code Hooks 完全指南:让 AI 编程更智能、更安全

TLDR;

给非计算机领域的:Hook 就是你先向系统‘登记’一个你自己的运行逻辑,并告诉系统:‘等某某事情发生的时候,你记得调一下我这个逻辑。系统就会在合适的时候回调你。

Claude Code hooks 提供了,工具使用,对话,会话等的开始结束时机,在这些时机里,把运行的会话信息传递给你指定的逻辑,借助这个机制,你可以做很多自定义的事。

你有没有想过,如果能在 Claude Code 执行某些操作之前或之后自动运行自己的脚本,会是什么样的体验?比如:

  • ✅ Claude 刚写完代码,自动运行格式化工具
  • 🚫 Claude 准备删除重要文件时,自动阻止并警告
  • 📊 每次会话结束,自动记录工作日志
  • 🔒 检测到敏感信息泄露,立即阻止请求

这些都可以通过 Claude Code Hooks 功能实现!无论你是编程新手还是经验丰富的开发者,这篇文章都会让你快速掌握这个强大功能。

什么是 Hooks?

通俗理解

想象一下,你雇佣了一位超级聪明的 AI 助手(Claude)帮你写代码。但有时候你希望:

  1. 在它做某些事情之前先检查一下(比如删除文件)
  2. 在它做完某些事情之后自动处理(比如代码写完后自动格式化)
  3. 在特定时刻自动执行某些任务(比如启动时加载配置)

**Hooks(钩子)**就是这些"关键时刻"的触发器。就像游戏里的检查点系统,让你能在关键节点进行干预或自动化处理。

技术定义

Hooks 是用户自定义的 Shell 命令,在 Claude Code 生命周期的特定节点自动执行。它们提供了对 Claude Code 行为的确定性控制,让 AI 编程更可靠、更安全。

Hooks 能做什么?

实际应用场景

1. 代码质量自动检查

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint"
          }
        ]
      }
    ]
  }
}

每当 Claude 写完或编辑完文件,自动运行代码检查工具。

2. 阻止危险操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 scripts/check-dangerous.sh"
          }
        ]
      }
    ]
  }
}

在 Claude 执行 Shell 命令前,检查是否包含危险操作(如 rm -rf)。

3. 自动添加上下文

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 scripts/load-issues.py"
          }
        ]
      }
    ]
  }
}

会话开始时,自动加载项目中的 Issues 或近期变更。

4. 敏感信息防护

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/usr/bin/env python3
import json
import sys
import re

# 从 stdin 读取用户输入
input_data = json.load(sys.stdin)
prompt = input_data.get("prompt", "")

# 检测敏感模式
if re.search(r'(?i)\b(password|secret|key)\s*[:=]', prompt):
    print(json.dumps({
        "decision": "block",
        "reason": "检测到可能的敏感信息,请重新表述请求"
    }))
    sys.exit(0)

Hooks 的 9 个触发时机

Claude Code 提供了 9 个关键事件点,让你可以在不同阶段介入:

1. PreToolUse - 工具使用前

在 Claude 执行工具(如写文件、运行命令)之前触发。

  • 用途: 验证、阻止、自动批准操作
  • 常见场景: 检查危险命令、验证文件路径、自动批准安全操作

2. PostToolUse - 工具使用后

在 Claude 执行完工具之后立即触发。

  • 用途: 自动化后续任务
  • 常见场景: 代码格式化、运行测试、发送通知

3. UserPromptSubmit - 用户提交提示时

在你发送消息给 Claude 之前触发。

  • 用途: 验证用户输入、添加上下文
  • 常见场景: 敏感信息检测、自动补充项目信息

4. Stop - Claude 完成响应时

在 Claude 完成一轮对话之后触发。

  • 用途: 质量检查、自动继续
  • 常见场景: 运行测试套件、代码质量门禁

5. SubagentStop - 子任务完成时

当 Claude 的子任务(独立执行的专门任务)完成时触发。

  • 用途: 子任务特定的后处理
  • 常见场景: 处理子任务结果、日志记录

6. Notification - 通知时

当 Claude 需要你授权使用工具,或等待你输入时触发。

  • 用途: 自定义通知处理
  • 常见场景: 发送外部通知、记录待处理事项

7. SessionStart - 会话开始时

当你启动 Claude Code 或恢复会话时触发。

  • 用途: 环境准备、上下文加载
  • 常见场景: 安装依赖、加载项目信息、设置环境变量

8. SessionEnd - 会话结束时

当你关闭 Claude Code 时触发。

  • 用途: 清理任务、日志记录
  • 常见场景: 保存工作日志、清理临时文件

9. PreCompact - 压缩上下文前

当 Claude 准备压缩对话历史以节省 Token 时触发。

  • 用途: 保存重要信息
  • 常见场景: 提取关键决策、保存重要上下文

如何配置 Hooks?

配置文件位置

Hooks 配置保存在 settings.json 文件中,有三个层级:

  1. 全局配置 - 作用于所有项目

    ~/.claude/settings.json
    
  2. 项目配置 - 作用于特定项目

    .claude/settings.json
    
  3. 本地项目配置 - 不提交到版本控制

    .claude/settings.local.json
    

基本配置结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "hooks": {
    "事件名称": [
      {
        "matcher": "工具匹配模式",
        "hooks": [
          {
            "type": "command",
            "command": "你的命令",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

字段说明

  • matcher: 匹配工具名称的模式

    • "Write" - 只匹配 Write 工具
    • "Edit|Write" - 匹配 Edit 或 Write
    • "*""" - 匹配所有工具
  • type: 目前只支持 "command"

  • command: 要执行的 Shell 命令

    • 可以使用环境变量 $CLAUDE_PROJECT_DIR(项目根目录)
  • timeout: (可选)超时时间(秒),默认 60 秒

简单示例:自动格式化代码

创建项目配置文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 在项目根目录创建配置
mkdir -p .claude
cat > .claude/settings.json << 'EOF'
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "prettier --write \"$CLAUDE_PROJECT_DIR\"/**/*.{js,jsx,ts,tsx,json,md}",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
EOF

进阶:用脚本实现智能控制

Hooks 的真正威力在于可以用编程方式实现复杂逻辑。

示例 1:阻止危险命令

创建 scripts/check-dangerous.sh:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash

# 读取 Claude 传来的 JSON 输入
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // empty')

# 危险命令列表
dangerous_patterns=(
    "rm -rf /"
    "rm -rf \\.\\./"
    "dd if=/dev/zero"
    "> /dev/sd[a-z]"
    "mkfs\\."
)

# 检查是否包含危险模式
for pattern in "${dangerous_patterns[@]}"; do
    if echo "$command" | grep -qE "$pattern"; then
        echo "⚠️  检测到危险命令: $command" >&2
        echo "此操作可能导致数据丢失,请确认是否继续。" >&2
        exit 2  # 退出码 2 会阻止操作
    fi
done

exit 0  # 允许执行

配置 Hook:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/scripts/check-dangerous.sh"
          }
        ]
      }
    ]
  }
}

示例 2:智能代码质量门禁

创建 scripts/quality-gate.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/env python3
import json
import sys
import subprocess

def read_input():
    """读取 Claude 传来的输入"""
    return json.load(sys.stdin)

def run_tests():
    """运行测试套件"""
    try:
        result = subprocess.run(
            ["npm", "test", "--", "--coverage"],
            capture_output=True,
            text=True,
            timeout=60
        )
        return result.returncode == 0
    except Exception as e:
        print(f"测试执行失败: {e}", file=sys.stderr)
        return False

def check_coverage():
    """检查代码覆盖率"""
    # 简化的示例 - 实际应该解析覆盖率报告
    return True

def main():
    input_data = read_input()

    # 运行质量检查
    tests_passed = run_tests()
    coverage_ok = check_coverage()

    if not (tests_passed and coverage_ok):
        # 返回 JSON 让 Claude 知道需要修复
        output = {
            "decision": "block",
            "reason": "❌ 代码质量检查失败:\n"
                     f"- 测试: {'✅ 通过' if tests_passed else '❌ 失败'}\n"
                     f"- 覆盖率: {'✅ 符合要求' if coverage_ok else '❌ 不足'}\n\n"
                     "请修复这些问题后再继续。"
        }
        print(json.dumps(output))
        sys.exit(0)

    print("✅ 所有质量检查通过")
    sys.exit(0)

if __name__ == "__main__":
    main()

示例 3:会话开始时自动加载项目上下文

创建 scripts/load-context.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/usr/bin/env python3
import json
import sys
import subprocess
from datetime import datetime

def get_recent_commits():
    """获取最近的 Git 提交"""
    try:
        result = subprocess.run(
            ["git", "log", "-5", "--oneline"],
            capture_output=True,
            text=True
        )
        if result.returncode == 0:
            return f"\n最近的提交:\n{result.stdout}"
    except:
        pass
    return ""

def get_open_issues():
    """获取打开的 Issues(如果有 GitHub CLI)"""
    try:
        result = subprocess.run(
            ["gh", "issue", "list", "--limit", "5"],
            capture_output=True,
            text=True
        )
        if result.returncode == 0:
            return f"\n当前打开的 Issues:\n{result.stdout}"
    except:
        pass
    return ""

def main():
    # 收集上下文信息
    context = []
    context.append(f"当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}")

    commits = get_recent_commits()
    if commits:
        context.append(commits)

    issues = get_open_issues()
    if issues:
        context.append(issues)

    # 返回 JSON 格式,添加到 Claude 的上下文
    output = {
        "hookSpecificOutput": {
            "hookEventName": "SessionStart",
            "additionalContext": "\n".join(context)
        }
    }

    print(json.dumps(output))
    sys.exit(0)

if __name__ == "__main__":
    main()

Hook 的输入和输出

输入:JSON 数据

每个 Hook 都会通过 stdin 接收 JSON 数据,包含:

1
2
3
4
5
6
7
8
{
  "session_id": "会话ID",
  "transcript_path": "对话记录路径",
  "cwd": "当前工作目录",
  "permission_mode": "权限模式",
  "hook_event_name": "事件名称",
  // ... 事件特定字段
}

PreToolUse 额外字段:

1
2
3
4
5
6
7
{
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "文件内容"
  }
}

PostToolUse 额外字段:

1
2
3
4
5
6
7
{
  "tool_name": "Write",
  "tool_input": { /* ... */ },
  "tool_response": {
    "success": true
  }
}

输出:控制 Claude 的行为

方式 1:退出码(简单)

  • 退出码 0: 成功,继续执行
  • 退出码 2: 阻止操作,stderr 会显示给 Claude
  • 其他退出码: 非阻塞错误,显示给用户

方式 2:JSON 输出(高级)

stdout 输出 JSON 以获得精细控制:

1
2
3
4
5
6
{
  "continue": true,           // 是否继续
  "stopReason": "原因",       // 停止原因(如果不继续)
  "suppressOutput": false,    // 是否隐藏输出
  "systemMessage": "警告消息" // 显示给用户的警告
}

PreToolUse 特定输出:

1
2
3
4
{
  "permissionDecision": "allow|deny|ask",
  "permissionDecisionReason": "原因"
}

UserPromptSubmit 特定输出:

1
2
3
4
5
6
7
8
{
  "decision": "block",
  "reason": "阻止原因",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "要添加的上下文"
  }
}

实战案例集合

案例 1:防止意外提交敏感文件

scripts/block-secrets.sh:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash

input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')

# 检查是否是敏感文件
sensitive_files=(
    "\\.env$"
    "\\.pem$"
    "\\.key$"
    "credentials\\.json"
    "secrets\\.yaml"
)

for pattern in "${sensitive_files[@]}"; do
    if echo "$file_path" | grep -qE "$pattern"; then
        echo "⚠️  警告: 可能正在提交敏感文件: $file_path" >&2
        echo "请确认不会泄露密钥或密码。" >&2
        exit 2
    fi
done

exit 0

案例 2:自动记录工作日志

scripts/log-work.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python3
import json
import sys
from datetime import datetime

def log_session_end(input_data):
    reason = input_data.get("reason", "unknown")
    timestamp = datetime.now().isoformat()

    log_entry = {
        "timestamp": timestamp,
        "reason": reason,
        "session_id": input_data.get("session_id")
    }

    # 追加到日志文件
    with open("~/claude-work-log.jsonl", "a") as f:
        f.write(json.dumps(log_entry) + "\n")

    print(f"✅ 会话已记录: {timestamp}")

if __name__ == "__main__":
    input_data = json.load(sys.stdin)
    log_session_end(input_data)
    sys.exit(0)

案例 3:自动优化代码

scripts/optimize-code.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python3
import json
import sys
import subprocess

input_data = json.load(sys.stdin)
file_path = input_data.get("tool_input", {}).get("file_path", "")

# 只处理 JavaScript/TypeScript 文件
if not file_path.endswith(('.js', '.jsx', '.ts', '.tsx')):
    sys.exit(0)

# 运行代码优化工具
try:
    subprocess.run([
        "prettier", "--write", file_path
    ], check=True)
    print(f"✨ 已优化: {file_path}")
except Exception as e:
    print(f"优化失败: {e}", file=sys.stderr)

sys.exit(0)

安全最佳实践

⚠️ 重要警告

Hooks 会自动执行你配置的命令,使用不当可能导致数据丢失!

安全检查清单

  1. 验证输入

    • 永远不要盲目信任输入数据
    • 检查路径遍历攻击(..)
    • 验证文件类型和路径
  2. 使用绝对路径

    • "${CLAUDE_PROJECT_DIR}/scripts/script.sh"
    • 而不是 scripts/script.sh
  3. 引号保护变量

    • 使用 "$VAR" 而不是 $VAR
    • 防止命令注入
  4. 避免敏感文件

    • 跳过 .env, .git/, 密钥文件
    • 添加显式检查
  5. 测试环境优先

    • 先在安全环境测试
    • 确认没有破坏性操作

不安全示例 ❌

1
2
3
{
  "command": "rm $CLAUDE_PROJECT_DIR/temp/*"
}

问题: 如果 $CLAUDE_PROJECT_DIR 为空,会删除系统根目录的 temp 文件!

安全示例 ✅

1
2
3
{
  "command": "rm -f \"$CLAUDE_PROJECT_DIR\"/temp/* 2>/dev/null || true"
}

改进:

  • 使用引号保护变量
  • 添加 -f 防止交互
  • 错误重定向
  • 失败也不中断

调试技巧

1. 查看 Hook 状态

1
2
# 在 Claude Code 中输入
/hooks

会显示所有已配置的 Hooks 及其状态。

2. 启用调试模式

1
claude --debug

会显示详细的 Hook 执行信息:

[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Hook command completed with status 0

3. 测试 Hook 脚本

手动测试脚本,确保逻辑正确:

1
2
3
# 模拟输入
echo '{"tool_name": "Write", "tool_input": {"file_path": "/tmp/test.txt"}}' \
  | python3 scripts/check-dangerous.py

4. 常见问题排查

问题可能原因解决方法
Hook 没有运行JSON 格式错误使用 jq . 验证 JSON
Hook 执行失败脚本没有执行权限运行 chmod +x script.sh
命令找不到没有使用绝对路径使用完整路径或 $CLAUDE_PROJECT_DIR
Hook 无限循环Stop Hook 触发新操作检查 stop_hook_active 字段

总结

Claude Code Hooks 是一个强大的自动化和质量控制工具:

核心优势

  1. 确定性控制 - 让 AI 编程更可预测
  2. 自动化工作流 - 减少重复操作
  3. 安全防护 - 阻止危险操作
  4. 质量保障 - 自动运行检查
  5. 灵活扩展 - 支持任意脚本

学习路径

初学者:

  1. 从简单配置开始(如自动格式化)
  2. 使用现有的 Hook 示例
  3. 理解 9 个事件的基本用途

进阶用户:

  1. 编写自定义脚本
  2. 组合多个 Hooks
  3. 实现 JSON 输出的高级控制

专家用户:

  1. 构建复杂的质量门禁系统
  2. 集成外部服务(如通知、日志)
  3. 创建可复用的 Hook 库

下一步

  • 查看 官方 Hooks 文档
  • 探索社区 Hooks 示例(GitHub 搜索 “claude-code-hooks”)
  • 分享你的 Hooks 配置!

记住: Hooks 是让 AI 为你工作的强大工具,但要用得聪明、用得安全。从简单的配置开始,逐步构建你的自动化工作流!

有问题或想法?欢迎在评论区交流! 🚀