ClaudeCode钩子 claude-code-hooks

这个技能提供创建Claude Code事件驱动钩子的权威参考,用于自动化、安全验证、代码格式化、测试运行等,适用于CI/CD集成和开发运维。关键词:Claude Code, 钩子, 自动化, 事件驱动, 安全, 脚本, DevOps。

DevOps 0 次安装 0 次浏览 更新于 3/7/2026

名称:claude-code-hooks 描述:为Claude Code自动化创建事件驱动钩子。配置设置或frontmatter中的钩子事件,解析stdin JSON输入,返回决策控制JSON,并实现安全钩子脚本。

Claude Code 钩子 — 元参考

这个技能提供创建Claude Code钩子的权威参考。在构建Claude Code事件触发的自动化时使用此技能。


何时使用此技能

  • 为Claude Code构建事件驱动自动化
  • 创建PreToolUse防护以阻止危险命令
  • 实现PostToolUse格式化器、linter或审核器
  • 添加Stop钩子用于测试或通知
  • 设置SessionStart/SessionEnd用于环境管理
  • 将Claude Code与CI/CD管道集成(无头模式)

快速参考

事件 触发器 用例
SessionStart 会话开始/恢复 初始化环境
UserPromptSubmit 用户提交提示 预处理/验证输入
PreToolUse 工具执行前 验证、阻止危险命令
PermissionRequest 权限对话框显示 自动允许/拒绝权限
PostToolUse 工具成功后 格式化、审核、通知
PostToolUseFailure 工具失败后 捕获失败、添加指导
SubagentStart 子代理生成 检查子代理元数据
Stop Claude完成时 运行测试、总结
SubagentStop 子代理完成 验证子代理完成
Notification 通知时 警报集成
PreCompact 上下文压缩前 保留关键上下文
Setup --init/--maintenance 初始化仓库/环境
SessionEnd 会话结束 清理、保存状态

钩子结构

.claude/hooks/
├── pre-tool-validate.sh
├── post-tool-format.sh
├── post-tool-audit.sh
├── stop-run-tests.sh
└── session-start-init.sh

配置

settings.json

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-tool-format.sh"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-tool-validate.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/stop-run-tests.sh"
          }
        ]
      }
    ]
  }
}

执行模型(2026年1月)

  • 钩子通过stdin接收JSON有效负载(视为不受信任的输入),并以用户权限运行(在Bash工具沙箱外)。
  • 默认超时为每个钩子命令60秒;所有匹配的钩子并行运行;相同的命令被去重。

钩子输入(stdin)

{
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "ls -la"
  }
}

环境变量(shell)

变量 描述
CLAUDE_PROJECT_DIR Claude Code启动的绝对项目根目录
CLAUDE_PLUGIN_ROOT 插件根目录(仅插件钩子)
CLAUDE_CODE_REMOTE 在远程/网络环境中为"true";否则为空/本地
CLAUDE_ENV_FILE 持久化export ...行的文件路径(在SessionStart中可用;检查文档以获取Setup支持)

退出代码

代码 含义 注释
0 成功 写入stdout的JSON被解析为结构化控制
2 阻塞错误 stderr成为消息;stdout中的JSON被忽略
其他 非阻塞错误 执行继续;stderr在详细模式中可见

Stdout注入说明:对于UserPromptSubmitSessionStartSetup,非JSON stdout(退出0)被注入到Claude的上下文中;大多数其他事件仅在详细模式中显示stdout。


决策控制 + 输入修改(v2.0.10+)

PreToolUse钩子可以允许/拒绝/询问,并可选地通过updatedInput修改工具输入。

钩子输出模式

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "向用户显示的原因(以及在拒绝时向Claude显示)",
    "updatedInput": { "command": "echo 'modified'" },
    "additionalContext": "在工具运行前添加的额外上下文"
  }
}

注意:旧的decision/reason字段已弃用;推荐使用hookSpecificOutput.*字段。

示例:重定向敏感文件编辑

#!/bin/bash
set -euo pipefail

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

# 重定向package-lock.json编辑到/dev/null
if [[ "$FILE_PATH" == *"package-lock.json" ]]; then
  UPDATED_INPUT="$(echo "$INPUT" | jq -c '.tool_input | .file_path = "/dev/null"')"
  jq -cn --argjson updatedInput "$UPDATED_INPUT" '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "allow",
      permissionDecisionReason: "重定向写入到/dev/null",
      updatedInput: $updatedInput
    }
  }'
  exit 0
fi

echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'

示例:从Git Add中剥离敏感文件

#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
CMD="$(echo "$INPUT" | jq -r '.tool_input.command // empty')"

if [[ "$TOOL_NAME" == "Bash" && "$CMD" =~ ^git[[:space:]]+add ]]; then
  # 从暂存中移除.env文件
  SAFE_CMD="$(echo "$CMD" | sed 's/\.env[^ ]*//g')"
  if [[ "$SAFE_CMD" != "$CMD" ]]; then
    echo '{}' | jq -cn --arg cmd "$SAFE_CMD" '{
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "allow",
        permissionDecisionReason: "从git add中移除.env",
        updatedInput: { command: $cmd }
      }
    }'
    exit 0
  fi
fi

echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'

基于提示的钩子

对于复杂决策,使用LLM评估的钩子(type: "prompt")而不是bash脚本。它们对于StopSubagentStop决策最有用。

配置

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "评估Claude是否应停止。上下文JSON:$ARGUMENTS。返回{\"ok\": true}如果所有任务完成,否则{\"ok\": false, \"reason\": \"剩余内容\"}。",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

响应模式

  • 允许:{"ok": true}
  • 阻止:{"ok": false, "reason": "向Claude显示的解释"}

组合命令和提示钩子

使用命令钩子进行快速、确定性检查。使用提示钩子进行细微决策:

{
  "Stop": [
    {
      "hooks": [
        { "type": "command", "command": ".claude/hooks/quick-check.sh" },
        { "type": "prompt", "prompt": "验证代码质量符合标准" }
      ]
    }
  ]
}

钩子模板

预工具验证

#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
CMD="$(echo "$INPUT" | jq -r '.tool_input.command // empty')"

if [[ "$TOOL_NAME" == "Bash" ]]; then
  # 阻止rm -rf /
  if echo "$CMD" | grep -qE 'rm\s+-rf\s+/'; then
    echo '{}' | jq -cn '{
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "deny",
        permissionDecisionReason: "检测到危险的rm命令"
      }
    }'
    exit 0
  fi

  # 阻止强制推送到main
  if echo "$CMD" | grep -qE 'git\s+push.*--force.*(main|master)'; then
    echo '{}' | jq -cn '{
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "deny",
        permissionDecisionReason: "不允许强制推送到main/master"
      }
    }'
    exit 0
  fi

  # 软警告:可能的凭据暴露
  if echo "$CMD" | grep -qE '(password|secret|api_key)\s*='; then
    echo '{}' | jq -cn '{
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "ask",
        permissionDecisionReason: "命令中可能包含凭据暴露",
        additionalContext: "命令可能包含秘密。确认意图并避免提交秘密。"
      }
    }'
    exit 0
  fi
fi

exit 0

后工具格式化

#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"

if [[ "$TOOL_NAME" =~ ^(Edit|Write)$ && -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
  case "$FILE_PATH" in
    *.js|*.ts|*.jsx|*.tsx|*.json|*.md)
      npx prettier --write "$FILE_PATH" 2>/dev/null || true
      ;;
    *.py)
      ruff format "$FILE_PATH" 2>/dev/null || true
      ;;
    *.go)
      gofmt -w "$FILE_PATH" 2>/dev/null || true
      ;;
    *.rs)
      rustfmt "$FILE_PATH" 2>/dev/null || true
      ;;
  esac
fi

exit 0

后工具安全审核

#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"

if [[ "$TOOL_NAME" =~ ^(Edit|Write)$ && -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
  # 检查硬编码的秘密
  if grep -qE '(password|secret|api_key|token)\s*[:=]\s*["\x27][^"\x27]+["\x27]' "$FILE_PATH"; then
    echo "警告:$FILE_PATH中可能包含硬编码秘密" >&2
  fi

  # 检查生产代码中的console.log
  if [[ "$FILE_PATH" =~ \.(ts|js|tsx|jsx)$ ]] && grep -q 'console.log' "$FILE_PATH"; then
    echo "注意:$FILE_PATH中找到console.log" >&2
  fi
fi

exit 0

停止钩子(运行测试)

#!/bin/bash
set -euo pipefail

# 在Claude完成后运行测试
cd "$CLAUDE_PROJECT_DIR"

# 检测测试框架
if [[ -f "package.json" ]]; then
  if grep -q '"vitest"' package.json; then
    npm run test 2>&1 | head -50
  elif grep -q '"jest"' package.json; then
    npm test 2>&1 | head -50
  fi
elif [[ -f "pytest.ini" ]] || [[ -f "pyproject.toml" ]]; then
  pytest --tb=short 2>&1 | head -50
fi

exit 0

会话开始

#!/bin/bash
set -euo pipefail

cd "$CLAUDE_PROJECT_DIR"

# 检查git状态
echo "=== Git状态 ==="
git status --short

# 检查未提交的更改
if ! git diff --quiet; then
  echo "警告:检测到未提交的更改"
fi

# 验证依赖
if [[ -f "package.json" ]]; then
  if [[ ! -d "node_modules" ]]; then
    echo "注意:node_modules缺失,运行npm install"
  fi
fi

exit 0

匹配器

匹配器筛选触发钩子的工具:

  • 精确匹配:Write 仅匹配Write工具
  • 正则表达式:Edit|WriteNotebook.*
  • 匹配所有:*(也适用于""或省略匹配器)

安全最佳实践

钩子安全检查清单

[ ] 使用正则表达式验证所有输入
[ ] 引用所有变量:"$VAR" 而不是 $VAR
[ ] 使用绝对路径
[ ] 不使用不受信任输入的eval
[ ] 在顶部设置 -euo pipefail
[ ] 保持钩子快速(<1秒)
[ ] 记录操作以进行审计
[ ] 部署前手动测试

钩子组合

同一事件上的多个钩子

{
  "PostToolUse": [
    {
      "matcher": "Edit|Write",
      "hooks": [
        { "type": "command", "command": ".claude/hooks/format.sh" },
        { "type": "command", "command": ".claude/hooks/audit.sh" },
        { "type": "command", "command": ".claude/hooks/notify.sh" }
      ]
    }
  ]
}

所有匹配的钩子并行运行。如果您需要严格的顺序(格式化 → lint → 测试),请创建一个按顺序运行它们的包装脚本。


调试钩子

# 手动测试PostToolUse钩子(stdin JSON)
export CLAUDE_PROJECT_DIR="$(pwd)"
echo '{"hook_event_name":"PostToolUse","tool_name":"Edit","tool_input":{"file_path":"'"$(pwd)"'/src/app.ts"}}' \
  | bash .claude/hooks/post-tool-format.sh

# 检查退出代码
echo $?

导航

资源

相关技能