ClaudeCode Hook 中文文档

· 5217字 · 11分钟

这份文档详细介绍了 Claude Code Hooks 的配置与使用方法。Hooks 提供了一种灵活的机制,让你能够在 Claude Code 的不同事件触发点(如工具调用前后、会话开始或结束、用户提交提示时等)自动执行自定义脚本或命令。通过合理配置钩子,你可以扩展 Claude Code 的能力,实现工作流自动化、代码质量检查、安全策略执行以及项目环境定制化等功能。文档同时涵盖了输入输出规范、安全注意事项、调试方法以及与 MCP 工具的集成方式,帮助你在保证安全和稳定的前提下,充分发挥 Claude Code 的可扩展性。

配置 🔗

Claude Code 的钩子通过你的 设置文件 来配置:

  • ~/.claude/settings.json - 用户设置
  • .claude/settings.json - 项目设置
  • .claude/settings.local.json - 本地项目设置(不会被提交)
  • 企业托管的策略设置

结构 🔗

钩子通过匹配器(matcher)来组织,每个匹配器可以包含多个钩子:

{
 "hooks": {
    "EventName": [
        {
            "matcher": "ToolPattern",
            "hooks": [
                {
                "type": "command",
                "command": "your-command-here"
                }
            ]
        }
    ]
 }
} 
  • matcher: 用于匹配工具名称的模式,区分大小写(仅适用于 PreToolUsePostToolUse

    • 简单字符串完全匹配:Write 仅匹配 Write 工具
    • 支持正则:Edit|WriteNotebook.*
    • 使用 * 匹配所有工具。你也可以使用空字符串 ("") 或者省略 matcher
  • hooks: 当模式匹配时要执行的命令数组

    • type: 当前仅支持 "command"
    • command: 要执行的 bash 命令(可使用 $CLAUDE_PROJECT_DIR 环境变量)
    • timeout: (可选)命令的最长运行时间,单位为秒,超时则取消该命令。

对于像 UserPromptSubmitNotificationStopSubagentStop 这类不使用匹配器的事件,可以省略 matcher 字段:

{
 "hooks": {
 "UserPromptSubmit": [
    {
    "hooks": [
        {
        "type": "command",
        "command": "/path/to/prompt-validator.py"
        }
      ]
     }
    ]
 }
} 

项目专用钩子脚本 🔗

你可以使用环境变量 CLAUDE_PROJECT_DIR(仅当 Claude Code 调用钩子命令时可用)来引用存储在项目中的脚本,确保它们无论 Claude 当前目录如何都能运行:

{
 "hooks": {
    "PostToolUse": [
        {
            "matcher": "Write|Edit",
            "hooks": [
                {
                "type": "command",
                "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-style.sh"
                }
            ]
        }
    ]
 }
} 

钩子事件 🔗

PreToolUse 🔗

在 Claude 创建工具参数之后、调用工具之前运行。常见匹配器:

  • Task - 子代理任务(见 子代理文档
  • Bash - Shell 命令
  • Glob - 文件模式匹配
  • Grep - 内容搜索
  • Read - 文件读取
  • Edit, MultiEdit - 文件编辑
  • Write - 文件写入
  • WebFetch, WebSearch - 网络操作

PostToolUse 🔗

在工具成功完成后立即运行。识别与 PreToolUse 相同的匹配器值。

Notification 🔗

在 Claude Code 发送通知时运行。通知发送的场景包括:

  1. Claude 需要你授权才能使用某个工具。例如:“Claude 需要你的许可才能使用 Bash”
  2. 输入提示(prompt)空闲至少 60 秒时。“Claude 正在等待你的输入”

UserPromptSubmit 🔗

当用户提交提示时运行,在 Claude 处理之前。这样可以基于提示或对话添加额外上下文,验证提示,或屏蔽某些类型的提示。

Stop 🔗

当主 Claude Code 代理完成响应时运行。如果因用户中断而停止则不会运行。

SubagentStop 🔗

当 Claude Code 子代理(Task 工具调用)完成响应时运行。

PreCompact 🔗

在 Claude Code 即将执行压缩操作之前运行。匹配器:

  • manual - 由 /compact 调用
  • auto - 由自动压缩调用(因为上下文窗口已满)

SessionStart 🔗

当 Claude Code 开启新会话或恢复会话时运行(当前底层总是会开启一个新会话)。常用于加载开发上下文,如现有问题或代码库的最近更改。匹配器:

  • startup - 由启动调用
  • resume - 由 --resume--continue/resume 调用
  • clear - 由 /clear 调用
  • compact - 由自动或手动压缩调用

SessionEnd 🔗

当 Claude Code 会话结束时运行。常用于清理任务、记录会话统计或保存会话状态。钩子输入中的 reason 字段可能是:

  • clear - 使用 /clear 命令清除会话
  • logout - 用户登出
  • prompt_input_exit - 用户在提示输入可见时退出
  • other - 其他退出原因

钩子输入 🔗

钩子通过 stdin 接收 JSON 数据,其中包含会话信息和事件相关数据:

{
  // 通用字段
  "session_id": "string",
  "transcript_path": "string", // 对话 JSON 文件路径
  "cwd": "string",             // 调用钩子时的当前工作目录

  // 事件相关字段
  "hook_event_name": "string",
  ...
}

PreToolUse 输入 🔗

tool_input 的具体结构依赖于工具。

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PreToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  }
}

PostToolUse 输入 🔗

tool_inputtool_response 的具体结构依赖于工具。

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "success": true
  }
}

Notification 输入 🔗

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Notification",
  "message": "Task completed successfully"
}

UserPromptSubmit 输入 🔗

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}

Stop 和 SubagentStop 输入 🔗

当 Claude Code 因 Stop 钩子而继续执行时,stop_hook_active 会为 true。可以检查此值或处理对话记录,以避免 Claude Code 无限运行。

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}

PreCompact 输入 🔗

对于 manualcustom_instructions 来自用户在 /compact 中输入的内容。对于 autocustom_instructions 为空。

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": ""
}

SessionStart 输入 🔗

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "hook_event_name": "SessionStart",
  "source": "startup"
}

SessionEnd 输入 🔗

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SessionEnd",
  "reason": "exit"
}

钩子输出 🔗

钩子有两种方式将输出返回给 Claude Code。输出用于传达是否阻止操作以及要显示给 Claude 和用户的反馈信息。

简单方式:退出码 🔗

钩子通过 退出码 (exit code)stdoutstderr 来传递状态:

  • 退出码 0:成功。stdout 会在对话模式(CTRL-R)中显示给用户,除了 UserPromptSubmitSessionStart,在这两种情况下 stdout 会被加入上下文。
  • 退出码 2:阻塞性错误。stderr 会反馈给 Claude 自动处理。具体行为见下表。
  • 其他退出码:非阻塞错误。stderr 显示给用户,执行继续。

退出码 2 的行为 🔗

钩子事件 行为
PreToolUse 阻止工具调用,将 stderr 显示给 Claude
PostToolUse 将 stderr 显示给 Claude(工具已运行)
Notification 不适用,仅将 stderr 显示给用户
UserPromptSubmit 阻止提示处理,清空提示,仅将 stderr 显示给用户
Stop 阻止停止,将 stderr 显示给 Claude
SubagentStop 阻止子代理停止,将 stderr 显示给 Claude 子代理
PreCompact 不适用,仅将 stderr 显示给用户
SessionStart 不适用,仅将 stderr 显示给用户
SessionEnd 不适用,仅将 stderr 显示给用户

高级方式:JSON 输出 🔗

钩子可以在 stdout 中返回结构化 JSON,用于更复杂的控制:

通用 JSON 字段 🔗

所有钩子类型都可以包含以下可选字段:

{
  "continue": true,           // Claude 是否在钩子执行后继续(默认: true)
  "stopReason": "string",     // 当 continue = false 时,显示给用户的原因
  "suppressOutput": true,     // 是否隐藏 stdout 的输出(默认: false)
  "systemMessage": "string"   // 可选,显示给用户的警告信息
}

如果 continue = false,Claude 会在钩子执行后停止处理。

  • 对于 PreToolUse,这不同于 "permissionDecision": "deny",后者只阻止某个工具调用,并提供自动反馈给 Claude。
  • 对于 PostToolUse,这不同于 "decision": "block",后者提供自动反馈给 Claude。
  • 对于 UserPromptSubmit,这会阻止提示被处理。
  • 对于 StopSubagentStop,这优先于任何 "decision": "block" 输出。
  • 在所有情况下,"continue": false 的优先级高于 "decision": "block"

stopReasoncontinue 一起使用,显示给用户而不会显示给 Claude。

PreToolUse 决策控制 🔗

PreToolUse 钩子可以控制工具调用是否继续:

  • "allow":绕过权限系统。permissionDecisionReason 显示给用户,但不显示给 Claude。
  • "deny":阻止工具调用执行。permissionDecisionReason 显示给 Claude。
  • "ask":在 UI 中请求用户确认工具调用。permissionDecisionReason 显示给用户,但不显示给 Claude。
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow" | "deny" | "ask",
    "permissionDecisionReason": "My reason here"
  }
}

PostToolUse 决策控制 🔗

PostToolUse 钩子可以在工具执行后为 Claude 提供反馈:

  • "block":自动提示 Claude 并传递 reason
  • undefined:什么也不做,reason 被忽略。
  • "hookSpecificOutput.additionalContext":为 Claude 提供额外上下文。
{
  "decision": "block" | undefined,
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude"
  }
}

UserPromptSubmit 决策控制 🔗

UserPromptSubmit 钩子可以控制用户提示是否被处理。

  • "block" 阻止提示被处理。提交的提示会从上下文中清除,"reason" 显示给用户,但不会加入上下文。
  • undefined 允许提示正常处理,"reason" 会被忽略。
  • "hookSpecificOutput.additionalContext" 在未阻止时,会将字符串加入上下文。
{
  "decision": "block" | undefined,
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here"
  }
}

Stop/SubagentStop 决策控制 🔗

StopSubagentStop 钩子可以控制 Claude 是否必须继续执行。

  • "block" 阻止 Claude 停止。必须提供 reason,以便 Claude 知道如何继续。
  • undefined 允许 Claude 停止,reason 会被忽略。
{
  "decision": "block" | undefined,
  "reason": "Must be provided when Claude is blocked from stopping"
}

SessionStart 决策控制 🔗

SessionStart 钩子允许在会话开始时加载上下文。

  • "hookSpecificOutput.additionalContext" 将字符串加入上下文。
  • 多个钩子返回的 additionalContext 会被拼接。
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "My additional context here"
  }
}

SessionEnd 决策控制 🔗

SessionEnd 钩子会在会话结束时运行。它们不能阻止会话终止,但可以执行清理任务。

退出码示例:Bash 命令验证 🔗

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

# 定义验证规则,形式为 (正则模式, 消息) 的元组列表
VALIDATION_RULES = [
    (
        r"\bgrep\b(?!.*\|)",
        "使用 'rg' (ripgrep) 替代 'grep' 以获得更好的性能和功能",
    ),
    (
        r"\bfind\s+\S+\s+-name\b",
        "使用 'rg --files | rg pattern' 或 'rg --files -g pattern' 替代 'find -name' 以获得更好的性能",
    ),
]

def validate_command(command: str) -> list[str]:
    issues = []
    for pattern, message in VALIDATION_RULES:
        if re.search(pattern, command):
            issues.append(message)
    return issues

try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
command = tool_input.get("command", "")

if tool_name != "Bash" or not command:
    sys.exit(1)

# 验证命令
issues = validate_command(command)
if issues:
    for message in issues:
        print(f"• {message}", file=sys.stderr)
    # 退出码 2 阻止工具调用,并将 stderr 显示给 Claude
    sys.exit(2) 

JSON 输出示例:UserPromptSubmit 添加上下文和验证 🔗

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

# 从 stdin 加载输入
try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

prompt = input_data.get("prompt", "")

# 检查敏感模式
sensitive_patterns = [
    (r"(?i)\b(password|secret|key|token)\s*[:=]", "提示包含潜在的敏感信息"),
]

for pattern, message in sensitive_patterns:
    if re.search(pattern, prompt):
        # 使用 JSON 输出阻止并提供原因
        output = {
            "decision": "block",
            "reason": f"安全策略违规: {message}。请重新表述请求,不要包含敏感信息。"
        }
        print(json.dumps(output))
        sys.exit(0)

# 将当前时间加入上下文
context = f"Current time: {datetime.datetime.now()}"
print(context)

"""
以下写法也等效:
print(json.dumps({
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": context,
  },
}))
"""

# 允许提示继续,并附带额外上下文
sys.exit(0) 

JSON 输出示例:PreToolUse 自动批准 🔗

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

# 从 stdin 加载输入
try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})

# 示例:自动批准对文档文件的读取
if tool_name == "Read":
    file_path = tool_input.get("file_path", "")
    if file_path.endswith((".md", ".mdx", ".txt", ".json")):
        # 使用 JSON 输出自动批准工具调用
        output = {
            "decision": "approve",
            "reason": "文档文件已自动批准",
            "suppressOutput": True  # 不在对话模式中显示
        }
        print(json.dumps(output))
        sys.exit(0)

# 其他情况交给正常的权限流程
sys.exit(0) 

Claude Code 钩子与 Model Context Protocol (MCP) 工具 无缝配合。当 MCP 服务器提供工具时,它们会以特殊的命名模式出现,你可以在钩子中进行匹配。

MCP 工具命名 🔗

MCP 工具遵循命名模式 mcp__<server>__<tool>,例如:

  • mcp__memory__create_entities - Memory 服务器的实体创建工具
  • mcp__filesystem__read_file - Filesystem 服务器的文件读取工具
  • mcp__github__search_repositories - GitHub 服务器的仓库搜索工具

为 MCP 工具配置钩子 🔗

你可以针对特定的 MCP 工具,或整个 MCP 服务器配置钩子:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}

示例 🔗

安全注意事项 🔗

免责声明 🔗

使用风险自负:Claude Code 钩子会在你的系统上自动执行任意 shell 命令。使用钩子即表示你已知晓并同意:

  • 你需对自己配置的命令负全部责任
  • 钩子可能修改、删除或访问你用户账户能访问的任何文件
  • 恶意或写得不当的钩子可能导致数据丢失或系统损坏
  • Anthropic 不提供任何担保,也不对因使用钩子导致的任何损害承担责任
  • 你应当在安全环境中充分测试钩子后再在生产环境中使用

在将钩子命令加入配置前,务必先审查并理解其作用。

安全最佳实践 🔗

以下是编写更安全钩子的一些关键做法:

  1. 验证并清理输入 —— 永远不要盲目信任输入数据
  2. 始终给 shell 变量加引号 —— 使用 "$VAR" 而不是 $VAR
  3. 阻止路径遍历 —— 检查文件路径中是否包含 ..
  4. 使用绝对路径 —— 为脚本指定完整路径(项目路径可用 $CLAUDE_PROJECT_DIR
  5. 跳过敏感文件 —— 避免处理 .env.git/、密钥等文件

配置安全性 🔗

直接修改设置文件中的钩子不会立即生效。Claude Code 的行为是:

  1. 在启动时捕获钩子的快照
  2. 会话期间始终使用该快照
  3. 如果外部修改了钩子,会发出警告
  4. 需要在 /hooks 菜单中确认后,修改才会生效

这样可以防止恶意钩子修改影响你当前的会话。

钩子执行细节 🔗

  • 超时:默认 60 秒执行上限,可按命令单独配置

    • 某个命令超时不会影响其他命令
  • 并行执行:所有匹配的钩子会并行运行

  • 去重:多个相同的钩子命令会自动去重

  • 环境:在当前目录下运行,并使用 Claude Code 的环境

    • 环境变量 CLAUDE_PROJECT_DIR 可用,包含项目根目录的绝对路径(Claude Code 启动位置)
  • 输入:通过 stdin 提供 JSON

  • 输出

    • PreToolUse / PostToolUse / Stop / SubagentStop: 进度显示在对话模式 (Ctrl-R)
    • Notification / SessionEnd: 仅记录到调试日志 (--debug)
    • UserPromptSubmit / SessionStart: stdout 会作为上下文传给 Claude

调试 🔗

基础排查 🔗

如果钩子没有生效:

  1. 检查配置 —— 运行 /hooks 查看钩子是否已注册
  2. 验证语法 —— 确保 JSON 设置合法
  3. 测试命令 —— 先手动运行钩子命令
  4. 检查权限 —— 确认脚本是可执行的
  5. 查看日志 —— 使用 claude --debug 查看钩子执行详情

常见问题:

  • 引号未转义 —— 在 JSON 字符串中使用 \"
  • 匹配器错误 —— 检查工具名是否完全匹配(区分大小写)
  • 找不到命令 —— 脚本使用完整路径

高级调试 🔗

对于复杂的钩子问题:

  1. 检查钩子执行 —— 使用 claude --debug 查看详细执行信息
  2. 验证 JSON 模式 —— 使用外部工具测试钩子输入/输出
  3. 检查环境变量 —— 确认 Claude Code 的环境正确
  4. 测试边界情况 —— 用异常文件路径或输入测试钩子
  5. 监控系统资源 —— 检查钩子执行时是否资源耗尽
  6. 使用结构化日志 —— 在钩子脚本中实现日志记录

调试输出示例 🔗

使用 claude --debug 查看钩子执行详情:

[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 60000ms
[DEBUG] Hook command completed with status 0: <Your stdout>

进度消息会显示在对话模式 (Ctrl-R),包括:

  • 哪个钩子正在运行
  • 执行的命令
  • 成功/失败状态
  • 输出或错误信息