Skip to content

钩子(Hooks)

钩子是在 Claude Code 会话中特定事件触发时自动执行的脚本,可实现自动化、校验、权限管理和自定义工作流。

概述

钩子是自动执行的动作(Shell 命令、HTTP Webhook、LLM 提示词或子代理评估),在 Claude Code 中特定事件发生时自动触发。钩子通过 stdin 接收 JSON 输入,通过退出码和 JSON 输出传递结果。

核心特性:

  • 事件驱动自动化
  • 基于 JSON 的输入/输出
  • 支持 command、prompt、http、agent 四种钩子类型
  • 工具特定钩子的模式匹配

配置

钩子在设置文件中配置:

  • ~/.claude/settings.json —— 用户设置(所有项目)
  • .claude/settings.json —— 项目设置(可共享,可提交)
  • .claude/settings.local.json —— 本地项目设置(不提交)
  • 托管策略 —— 组织级设置
  • 插件 hooks/hooks.json —— 插件作用域的钩子
  • Skill/Agent frontmatter —— 组件生命周期钩子

基本配置结构

json
{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here",
            "timeout": 60
          }
        ]
      }
    ]
  }
}

关键字段:

字段说明示例
matcher匹配工具名称的模式(区分大小写)"Write""Edit|Write""*"
hooks钩子定义数组[{ "type": "command", ... }]
type钩子类型:"command"(bash)、"prompt"(LLM)、"http"(webhook)或 "agent"(子代理)"command"
command要执行的 Shell 命令"$CLAUDE_PROJECT_DIR/.claude/hooks/format.sh"
timeout可选超时时间(秒,默认 60)30
once若为 true,每次会话只运行一次true

匹配模式

模式说明示例
精确字符串匹配特定工具"Write"
正则表达式匹配多个工具"Edit|Write"
通配符匹配所有工具"*"""
MCP 工具服务器和工具模式"mcp__memory__.*"

钩子类型

Claude Code 支持四种钩子类型:

Command 钩子

默认钩子类型,执行 Shell 命令,通过 JSON stdin/stdout 和退出码通信。

HTTP 钩子

v2.1.63 起可用。

远程 Webhook 端点,接收与 command 钩子相同的 JSON 输入。通过 POST 发送 JSON 并接收 JSON 响应。

json
{
  "hooks": {
    "PostToolUse": [{
      "type": "http",
      "url": "https://my-webhook.example.com/hook",
      "matcher": "Write"
    }]
  }
}

Prompt 钩子

LLM 评估的提示词钩子,主要用于 StopSubagentStop 事件的智能任务完成检查。

json
{
  "type": "prompt",
  "prompt": "评估 Claude 是否完成了所有请求的任务。",
  "timeout": 30
}

Agent 钩子

基于子代理的验证钩子,生成专用代理来评估条件或执行复杂检查。与 prompt 钩子不同,agent 钩子可以使用工具并进行多步推理。

json
{
  "type": "agent",
  "prompt": "验证代码变更是否符合架构规范,检查相关设计文档并对比。",
  "timeout": 120
}

钩子事件

Claude Code 支持 25 个钩子事件

事件触发时机是否可阻断常见用途
SessionStart会话开始/恢复/清空/压缩环境初始化
InstructionsLoaded加载 CLAUDE.md 或规则文件后过滤/修改指令
UserPromptSubmit用户提交提示词校验输入
PreToolUse工具执行前是(allow/deny/ask)校验、修改输入
PermissionRequest显示权限对话框自动批准/拒绝
PostToolUse工具执行成功后添加上下文、反馈
PostToolUseFailure工具执行失败错误处理、日志
Notification发送通知时自定义通知
SubagentStart子代理启动子代理初始化
SubagentStop子代理完成子代理验证
StopClaude 完成响应任务完成检查
StopFailureAPI 错误终止轮次错误恢复、日志
TeammateIdle代理团队成员空闲成员协调
TaskCompleted任务标记完成任务后置动作
TaskCreated通过 TaskCreate 创建任务任务跟踪、日志
ConfigChange配置文件变更响应配置更新
CwdChanged工作目录变更目录特定初始化
FileChanged被监视文件变更文件监控、重建
PreCompact上下文压缩前压缩前动作
PostCompact压缩完成后压缩后动作
WorktreeCreate创建 worktreeWorktree 初始化
WorktreeRemove删除 worktreeWorktree 清理
ElicitationMCP 服务器请求用户输入输入校验
ElicitationResult用户响应 elicitation响应处理
SessionEnd会话终止清理、最终日志

钩子输入输出

JSON 输入(via stdin)

所有钩子通过 stdin 接收 JSON 输入:

json
{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/current/working/directory",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.js",
    "content": "..."
  }
}

退出码

退出码含义行为
0成功继续,解析 JSON stdout
2阻断错误阻断操作,stderr 作为错误显示
其他非阻断错误继续,详细模式下显示 stderr

JSON 输出(stdout,退出码 0)

json
{
  "continue": true,
  "suppressOutput": false,
  "systemMessage": "可选警告消息",
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "文件在允许的目录中",
    "updatedInput": {
      "file_path": "/modified/path.js"
    }
  }
}

环境变量

变量可用范围说明
CLAUDE_PROJECT_DIR所有钩子项目根目录绝对路径
CLAUDE_ENV_FILESessionStart、CwdChanged、FileChanged持久化环境变量的文件路径
CLAUDE_CODE_REMOTE所有钩子在远程环境运行时为 "true"
${CLAUDE_PLUGIN_ROOT}插件钩子插件目录路径
${CLAUDE_PLUGIN_DATA}插件钩子插件数据目录路径

示例

示例一:Bash 命令校验器(PreToolUse)

文件: .claude/hooks/validate-bash.py

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

BLOCKED_PATTERNS = [
    (r"\brm\s+-rf\s+/", "阻断危险命令 rm -rf /"),
    (r"\bsudo\s+rm", "阻断 sudo rm 命令"),
]

def main():
    input_data = json.load(sys.stdin)
    if input_data.get("tool_name") != "Bash":
        sys.exit(0)
    command = input_data.get("tool_input", {}).get("command", "")
    for pattern, message in BLOCKED_PATTERNS:
        if re.search(pattern, command):
            print(message, file=sys.stderr)
            sys.exit(2)  # 退出码 2 = 阻断错误
    sys.exit(0)

if __name__ == "__main__":
    main()

配置:

json
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py\""
      }]
    }]
  }
}

示例二:安全扫描器(PostToolUse)

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

SECRET_PATTERNS = [
    (r"password\s*=\s*['\"][^'\"]+['\"]", "潜在硬编码密码"),
    (r"api[_-]?key\s*=\s*['\"][^'\"]+['\"]", "潜在硬编码 API Key"),
]

def main():
    input_data = json.load(sys.stdin)
    if input_data.get("tool_name") not in ["Write", "Edit"]:
        sys.exit(0)
    content = input_data.get("tool_input", {}).get("content", "")
    file_path = input_data.get("tool_input", {}).get("file_path", "")
    warnings = [msg for pat, msg in SECRET_PATTERNS if re.search(pat, content, re.IGNORECASE)]
    if warnings:
        print(json.dumps({
            "hookSpecificOutput": {
                "hookEventName": "PostToolUse",
                "additionalContext": f"安全警告 {file_path}: " + "; ".join(warnings)
            }
        }))
    sys.exit(0)

示例三:自动格式化代码(PostToolUse)

bash
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | python3 -c "import sys, json; print(json.load(sys.stdin).get('tool_name', ''))")
FILE_PATH=$(echo "$INPUT" | python3 -c "import sys, json; print(json.load(sys.stdin).get('tool_input', {}).get('file_path', ''))")

[ "$TOOL_NAME" != "Write" ] && [ "$TOOL_NAME" != "Edit" ] && exit 0

case "$FILE_PATH" in
    *.js|*.jsx|*.ts|*.tsx|*.json)
        command -v prettier &>/dev/null && prettier --write "$FILE_PATH" 2>/dev/null ;;
    *.py)
        command -v black &>/dev/null && black "$FILE_PATH" 2>/dev/null ;;
    *.go)
        command -v gofmt &>/dev/null && gofmt -w "$FILE_PATH" 2>/dev/null ;;
esac
exit 0

示例四:智能 Stop 钩子(基于 Prompt)

json
{
  "hooks": {
    "Stop": [{
      "hooks": [{
        "type": "prompt",
        "prompt": "检查 Claude 是否完成了所有请求的任务。检查:1)所有文件是否已创建/修改?2)是否有未解决的错误?如未完成,说明缺少什么。",
        "timeout": 30
      }]
    }]
  }
}

完整配置示例

json
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{"type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py\"", "timeout": 10}]
    }],
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "hooks": [
        {"type": "command", "command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/format-code.sh\"", "timeout": 30},
        {"type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/security-scan.py\"", "timeout": 10}
      ]
    }],
    "UserPromptSubmit": [{
      "hooks": [{"type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-prompt.py\""}]
    }],
    "Stop": [{
      "hooks": [{"type": "prompt", "prompt": "在停止前验证所有任务已完成。", "timeout": 30}]
    }]
  }
}

安全注意事项

使用风险自负:钩子执行任意 Shell 命令,请确保:

  • 测试钩子在安全环境中运行
  • 校验和清理所有输入
  • 为 Shell 变量加引号:"$VAR"
  • 使用 $CLAUDE_PROJECT_DIR 的绝对路径
  • HTTP 钩子需显式指定 allowedEnvVars

安装步骤

bash
# 1. 创建钩子目录
mkdir -p ~/.claude/hooks

# 2. 复制示例钩子
cp 06-hooks/*.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/*.sh

# 3. 在设置文件中配置
# 编辑 ~/.claude/settings.json 或 .claude/settings.json

调试

bash
# 开启调试模式
claude --debug

# 独立测试钩子
echo '{"tool_name": "Bash", "tool_input": {"command": "ls"}}' | python3 .claude/hooks/validate-bash.py
echo $?

相关内容