新手上路(五):Claude Code Hooks 深度实战:6 大生命周期事件 × 10 个生产级自动化规则 × 5 种处理器类型

4 阅读17分钟

Windows 10 · Claude Code CLI v2.x · DeepSeek V4 Pro / Anthropic API · 2026-05-01

一、这篇教程解决什么问题

一句话定位:Claude Code 能自主执行工具调用,但你无法保证它不会误删生产文件、编辑 .env 秘密文件、或者在没跑测试的情况下就提交代码。Hooks 就是你的"安全看门狗"——在工具执行前拦截、执行后审计、会话开始时加载上下文、会话结束时清理资源,全程自动化守护。

阅读前提

  • 已完成 Claude Code CLI 安装,claude --version 正常输出版本号
  • 已完成 /init 项目初始化,项目根目录存在 CLAUDE.md
  • 了解 settings.json 的三层配置机制(全局 → 项目共享 → 项目本地)

读完能得到什么

  • 理解 Hooks 的事件驱动机制(6 大核心事件 + 30+ 生命周期事件)
  • 掌握 5 种 Hook 处理器类型(command / http / mcp_tool / prompt / agent)
  • 10 个生产级实战 Hook 示例(拦截危险命令、格式化代码、加载 Git 上下文、强制测试等)
  • 现成的优质-hooks插件推荐-不用自己写,全功能覆盖
  • Windows 环境下 4 个特有坑位的五段式 Debug
  • 一张速查卡,包含完整事件表、匹配器语法、退出码语义

二、Hooks 是什么

2.1 一句话解释

Hook 是你注册在 Claude Code 生命周期特定节点上的自定义脚本。当事件触发时,Claude Code 通过 stdin 向你的脚本发送 JSON 上下文,你的脚本通过退出码和 stdout 告诉 Claude Code"放行"或"拦截"。

2.2 Hooks 与 Skills / MCP 的区别

维度HooksSkillsMCP
本质事件驱动的自动化脚本知识注入(CLAUDE.md 风格)外部工具连接协议
触发方式生命周期事件自动触发Claude 按需调用 / 用户手动 /skillClaude 主动调用 MCP 工具
能否拦截/阻止操作✅ 可以(exit 2 或 JSON decision)❌ 不能❌ 不能
能否修改工具输入✅ 可以(updatedInput)❌ 不能❌ 不能
典型用途安全守护、代码格式化、审计日志编码规范、设计模式、最佳实践查询数据库、操作 GitHub、抓网页

2.3 事件生命周期全景

会话启动
  ├── SessionStart          ← 加载 Git 状态、环境变量
  ├── Setup                 ← CI 一次性依赖安装
  └── InstructionsLoaded    ← CLAUDE.md 被加载时
用户输入
  ├── UserPromptSubmit      ← 拦截/验证用户提示
  └── UserPromptExpansion   ← 拦截斜杠命令展开
Claude 执行工具(循环)
  ├── PreToolUse            ← 工具执行前:拦截/审批/修改输入
  ├── PermissionRequest     ← 权限弹窗出现时
  ├── PermissionDenied      ← 自动模式拒绝时
  ├── PostToolUse           ← 工具成功后:格式化/审计
  ├── PostToolUseFailure    ← 工具失败后
  └── PostToolBatch         ← 并行工具批次完成后
其他事件
  ├── Notification          ← 通知发送时
  ├── SubagentStart/Stop    ← 子代理生命周期
  ├── TaskCreated/Completed ← 任务创建/完成
  ├── ConfigChange          ← 配置文件变更
  ├── CwdChanged            ← 工作目录切换
  ├── FileChanged           ← 监视文件变更
  ├── WorktreeCreate/Remove ← Git Worktree 生命周期
  ├── PreCompact/PostCompact← 上下文压缩前后
  └── Elicitation/Result    ← MCP 服务器请求用户输入
会话结束
  ├── Stop / StopFailure    ← Claude 停止响应时
  └── SessionEnd            ← 会话终止

三、快速上手:5 分钟写出第一个 Hook

3.1 创建项目级 hooks 目录

mkdir .claude\hooks

3.2 编写拦截脚本

创建文件 .claude/hooks/block-rm-rf.sh

#!/bin/bash
# 读取 stdin 中的 JSON,提取 Bash 命令
COMMAND=$(jq -r '.tool_input.command' < /dev/stdin)

if echo "$COMMAND" | grep -q 'rm -rf\|rm -r -f'; then
    echo "Blocked: rm -rf is not allowed by project hook" >&2
    exit 2  # 阻止工具执行
fi

exit 0  # 放行

3.3 在 settings.json 中注册 Hook

编辑项目级 .claude/settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/block-rm-rf.sh\""
          }
        ]
      }
    ]
  }
}

matcher 字段:精确匹配工具名。Bash 只匹配 Bash 工具,Edit|Write 匹配任一,* 或省略匹配所有工具。

3.4 验证

启动 Claude Code,尝试让它执行 rm -rf

帮我删除 /tmp/build 目录:rm -rf /tmp/build

预期结果:Claude 尝试执行 rm -rf /tmp/build 时被 Hook 拦截,终端显示:

Blocked: rm -rf is not allowed by project hook

Claude 看到这个错误信息后会尝试其他安全的删除方式。


四、配置详解:三层嵌套结构

4.1 settings.json 中的 hooks 字段

Hook 配置有三层嵌套:事件 → 匹配器组 → 处理器列表。

{
  "hooks": {
    "事件名": [
      {
        "matcher": "匹配器",
        "hooks": [
          {
            "type": "command",
            "command": "脚本路径或命令",
            "timeout": 600
          }
        ]
      }
    ]
  }
}
层级字段说明
第 1 层hooks.<事件名>生命周期事件,如 PreToolUsePostToolUseStop
第 2 层matcher过滤器:当事件触发时,只有匹配的工具名才激活此组
第 3 层hooks[]处理器列表:匹配成功后执行的脚本/HTTP/MCP/提示

4.2 匹配器语法(matcher)

含义示例
"*""" 或省略匹配所有每次事件触发都执行
纯字母/数字/下划线/竖线精确匹配或 `` 分隔列表BashEdit|Write
含其他字符(.*^ 等)JavaScript 正则表达式^Notebookmcp__memory__.*

MCP 工具命名格式mcp__<服务器名>__<工具名>。匹配某个服务器所有工具用 mcp__memory__.*(必须加 .*,否则精确匹配不生效)。

4.3 处理器类型(5 种)

类型说明关键字段
command执行 Shell 命令(默认)commandshellbash/powershell)、asynctimeout
http发送 HTTP POST 请求urlheadersallowedEnvVarstimeout
mcp_tool调用已连接的 MCP 服务器工具servertoolinput
prompt发送提示词给 Claude 模型做单轮评估promptmodeltimeout(默认 30s)
agent启动子代理验证条件(实验性)promptmodeltimeout(默认 60s)

4.4 if 条件字段(精细过滤)

if 使用权限规则语法,在处理器级别进一步过滤:

{
  "matcher": "Bash",
  "hooks": [
    {
      "type": "command",
      "if": "Bash(rm *)",
      "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/block-rm.sh\""
    }
  ]
}

matcherif 的区别:

  • matcher:事件级过滤,匹配工具名
  • if:处理器级过滤,匹配工具名 + 参数(如 Bash(git push *)Edit(*.ts)
  • 两者都匹配时,Hook 才执行

注意if 仅在工具事件(PreToolUsePostToolUsePostToolUseFailurePermissionRequestPermissionDenied)上生效。其他事件设置 if 会导致该 Hook 永远不执行。

4.5 配置文件位置与优先级

位置作用域可 Git 提交
~/.claude/settings.json所有项目❌ 仅本机
.claude/settings.json当前项目✅ 团队共享
.claude/settings.local.json当前项目❌ gitignore
插件 hooks/hooks.json插件启用时✅ 随插件分发
Skill/Agent frontmatter组件活跃时✅ 随组件分发

五、退出码与 JSON 输出:Hook 的"决策语言"

5.1 退出码语义

退出码含义PreToolUse 效果PostToolUse 效果Stop 效果
0成功/放行工具继续执行无额外效果Claude 停止
2阻止/拦截阻止工具执行stderr 展示给 Claude阻止停止,继续对话
其他非阻塞错误工具继续执行stderr 展示给 ClaudeClaude 停止

5.2 退出码 2 在各事件上的行为

事件能阻止?退出码 2 的效果
PreToolUse阻止工具调用
UserPromptSubmit阻止提示处理并清除提示
Stop阻止 Claude 停止,继续对话
PreCompact阻止上下文压缩
PermissionRequest拒绝权限请求
SubagentStop阻止子代理停止
PostToolUsestderr 展示给 Claude(工具已执行)
PostToolUseFailurestderr 展示给 Claude(工具已失败)
SessionStartstderr 仅展示给用户
SessionEndstderr 仅展示给用户
Notificationstderr 仅展示给用户

5.3 JSON 输出(精细控制)

退出码只区分"放行"和"阻止"两档。JSON 输出让你有更精细的控制权——允许、拒绝、询问用户、注入上下文、修改工具输入。

Hook 脚本退出码为 0 时,stdout 中的 JSON 会被解析。退出码非 0 时 JSON 被忽略。

通用字段

{
  "continue": true,
  "stopReason": "停止原因(continue=false 时展示)",
  "suppressOutput": false,
  "systemMessage": "展示给用户的警告消息"
}

注入上下文(additionalContext)

{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "当前分支: feat/auth. 注意: config/prod.yml 是只读的。"
  }
}

additionalContext 会被包装为系统提醒注入 Claude 的上下文窗口。适合传递动态环境信息(当前分支、部署目标、活跃 feature flag 等)。

PreToolUse 精细决策(permissionDecision)

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "数据库写操作被 Hook 禁止"
  }
}
permissionDecision效果
"allow"跳过权限提示,直接执行
"deny"阻止工具调用
"ask"提示用户确认
"defer"优雅退出,工具稍后恢复(仅 -p 非交互模式)

优先级(多个 Hook 返回不同决策时):deny > defer > ask > allow

修改工具输入(updatedInput)

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "updatedInput": {
      "command": "npm run lint && npm test"
    }
  }
}

updatedInput 替换整个 tool_input 对象,所以必须包含未修改的字段。

停止 Claude(全局控制)

{
  "continue": false,
  "stopReason": "构建失败,请先修复错误再继续"
}

此字段跨所有事件生效。


六、实战 Hook 示例

6.1 PreToolUse:拦截危险命令

场景:阻止 rm -rfdd if=mkfsformat 等破坏性命令。

脚本 .claude/hooks/block-dangerous.sh

#!/bin/bash
COMMAND=$(jq -r '.tool_input.command' < /dev/stdin)

if echo "$COMMAND" | grep -qE 'rm -rf|rm -r -f|dd if=|mkfs|format '; then
    echo "Destructive command blocked: $COMMAND" >&2
    exit 2
fi

exit 0

配置:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/block-dangerous.sh\""
          }
        ]
      }
    ]
  }
}

if 字段的作用"if": "Bash(rm *)" 使得只有 Bash 命令以 rm 开头时才运行脚本,避免每次 Bash 调用都启动一个进程。

6.2 PreToolUse:禁止编辑敏感文件

场景:阻止 Claude 编辑 .envcredentials.jsonsecrets.yaml 等秘密文件。

脚本 .claude/hooks/protect-secrets.sh

#!/bin/bash
FILE_PATH=$(jq -r '.tool_input.file_path // ""' < /dev/stdin)

if echo "$FILE_PATH" | grep -qE '\.env$|credentials|secrets|\.pem$|\.key$'; then
    echo "Sensitive file blocked: $FILE_PATH" >&2
    exit 2
fi

exit 0

配置:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/protect-secrets.sh\""
          }
        ]
      }
    ]
  }
}

6.3 PostToolUse:自动格式化代码

场景:Claude 写入或编辑 Python 文件后,自动运行 ruff format

脚本 .claude/hooks/auto-format.sh

#!/bin/bash
TOOL_NAME=$(jq -r '.tool_name' < /dev/stdin)
FILE_PATH=$(jq -r '.tool_input.file_path // ""' < /dev/stdin)

# 只处理 Python 文件
if [[ "$FILE_PATH" == *.py ]]; then
    ruff format "$FILE_PATH" 2>/dev/null || true
    ruff check --fix "$FILE_PATH" 2>/dev/null || true
fi

exit 0

配置:

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

PostToolUse 不能阻止操作(工具已执行),但可以做审计和后处理。如果格式化失败,stderr 会被展示给 Claude,Claude 可能会自行修复。

6.4 SessionStart:加载 Git 状态到上下文

场景:每次会话开始时,自动把当前分支名、未提交变更文件列表注入 Claude 的上下文。

脚本 .claude/hooks/load-git-status.sh

#!/bin/bash
# 检查是否在 Git 仓库中
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
    exit 0
fi

BRANCH=$(git branch --show-current 2>/dev/null)
CHANGED=$(git diff --name-only 2>/dev/null)
STAGED=$(git diff --cached --name-only 2>/dev/null)
UNTRACKED=$(git ls-files --others --exclude-standard 2>/dev/null)

OUTPUT="Current branch: $BRANCH"

if [ -n "$CHANGED" ]; then
    OUTPUT="$OUTPUT\nUnstaged changes: $CHANGED"
fi
if [ -n "$STAGED" ]; then
    OUTPUT="$OUTPUT\nStaged files: $STAGED"
fi
if [ -n "$UNTRACKED" ]; then
    OUTPUT="$OUTPUT\nUntracked files: $UNTRACKED"
fi

echo -e "$OUTPUT"
exit 0

配置:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume",
        "hooks": [
          {
            "type": "command",
            "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/load-git-status.sh\"",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

SessionStart 的 stdout 直接注入 Claude 上下文。无需构建 JSON,纯文本即可。

6.5 SessionStart:持久化环境变量

场景:会话开始时设置 NODE_ENVPATH 等环境变量,后续所有 Bash 命令都生效。

脚本 .claude/hooks/setup-env.sh

#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
    echo 'export NODE_ENV=development' >> "$CLAUDE_ENV_FILE"
    echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi
exit 0

配置:

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/setup-env.sh\""
          }
        ]
      }
    ]
  }
}

$CLAUDE_ENV_FILE 是 Claude Code 提供的特殊路径。写入该文件的 export 语句会在后续所有 Bash 工具中自动生效。仅在 SessionStartSetupCwdChangedFileChanged 事件中可用。

6.6 Stop:强制运行测试才能停止

场景:Claude 想停止对话前,检查本次会话是否运行过测试。没跑过就阻止停止。

脚本 .claude/hooks/require-tests.sh

#!/bin/bash
# 读取会话 transcript 检查是否执行过测试命令
TRANSCRIPT=$(jq -r '.transcript_path' < /dev/stdin 2>/dev/null)

if [ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ]; then
    if grep -qE '"(npm test|pytest|cargo test|go test)"' "$TRANSCRIPT" 2>/dev/null; then
        exit 0  # 已运行过测试,放行
    fi
fi

echo "Tests have not been run in this session. Please run tests before stopping." >&2
exit 2  # 未运行测试,阻止停止

配置:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/require-tests.sh\"",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

Stop 事件退出码 2 的效果:阻止 Claude 停止,Claude 看到 stderr 中的错误信息后会继续对话(自动运行测试)。

6.7 UserPromptSubmit:自动为提示注入上下文

场景:用户提交提示时,自动附加当前 Git 分支信息。

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo \"Git branch: $(git branch --show-current 2>/dev/null)\""
          }
        ]
      }
    ]
  }
}

UserPromptSubmit 的 stdout 直接注入 Claude 上下文。此事件不支持 matcher(每次都触发)。

6.8 PreToolUse:JSON 精细控制——自动审批 git status

场景:Claude 执行 git statusgit loggit diff 等只读 Git 命令时,自动跳过权限提示。

脚本 .claude/hooks/auto-approve-git-readonly.sh

#!/bin/bash
COMMAND=$(jq -r '.tool_input.command' < /dev/stdin)

# 匹配只读 git 命令
if echo "$COMMAND" | grep -qE '^git (status|log|diff|show|branch|remote)'; then
    jq -n '{
        hookSpecificOutput: {
            hookEventName: "PreToolUse",
            permissionDecision: "allow",
            permissionDecisionReason: "Read-only git command auto-approved"
        }
    }'
    exit 0
fi

exit 0  # 其他命令走正常流程

配置:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(git *)",
            "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/auto-approve-git-readonly.sh\""
          }
        ]
      }
    ]
  }
}

6.9 异步 Hook:文件变更后后台运行测试

场景:Claude 每次编辑文件后,在后台自动运行测试,不阻塞主流程。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "cd \"$CLAUDE_PROJECT_DIR\" && npm test 2>&1 | tail -20",
            "async": true,
            "timeout": 120
          }
        ]
      }
    ]
  }
}

async: true:Hook 在后台运行,不阻塞 Claude 的下一次工具调用。 asyncRewake: true:后台运行,且退出码为 2 时唤醒 Claude(stderr/stdout 作为系统提醒注入)。

6.10 Prompt Hook:用 AI 判断是否放行

场景:用轻量模型评估 Bash 命令是否安全。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate this Bash command for safety. Is it destructive or could it cause data loss? Command: $ARGUMENTS. Reply with JSON: {\"decision\": \"block\", \"reason\": \"...\"} if unsafe, or {} if safe.",
            "timeout": 15
          }
        ]
      }
    ]
  }
}

$ARGUMENTS:占位符,会被替换为 Hook 的完整 JSON 输入。Prompt hook 默认使用快速模型(30 秒超时)。


七、Windows 环境专题

7.1 使用 PowerShell 作为 Hook Shell

在 Windows 上,可以将 shell 设置为 powershell

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "shell": "powershell",
            "command": "$input = $input | ConvertFrom-Json; if ($input.tool_input.command -match 'rm -rf') { Write-Error 'Blocked'; exit 2 }"
          }
        ]
      }
    ]
  }
}

shell: "powershell":Claude Code 直接通过 PowerShell 执行命令,无需 cmd /c 包装。此字段仅在 Windows 上有效。

7.2 使用 Git Bash 执行 .sh 脚本

如果你安装了 Git for Windows(自带 Git Bash),可以直接运行 .sh 脚本:

{
  "type": "command",
  "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/block-rm-rf.sh\""
}

前提:Git Bash(bash.exe)在系统 PATH 中。安装 Git for Windows 时勾选"Add to PATH"即可。

7.3 用 Python 替代 Bash 脚本

对于复杂逻辑,Python 比 Bash 更可靠(Windows 上 Bash 行为不一致):

{
  "type": "command",
  "command": "python \"$CLAUDE_PROJECT_DIR/.claude/hooks/block-dangerous.py\""
}

Python 脚本示例 .claude/hooks/block-dangerous.py

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

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

if any(kw in command for kw in ["rm -rf", "dd if=", "mkfs"]):
    print("Destructive command blocked", file=sys.stderr)
    sys.exit(2)

sys.exit(0)

7.4 路径中的空格和特殊字符

Hook 配置中引用项目目录时,始终用引号包裹 $CLAUDE_PROJECT_DIR

"command": "python \"$CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.py\""

Windows 用户名可能包含空格(如 C:\Users\John Doe\),不加引号会导致路径断裂。


八、高级特性速览

8.1 HTTP Hook

将事件数据 POST 到远程服务:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/audit",
            "timeout": 10,
            "headers": {
              "Authorization": "Bearer $MY_TOKEN"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

HTTP Hook 的非 2xx 响应、连接失败、超时都是非阻塞错误,不会阻止 Claude 执行。要阻止操作需返回 2xx + JSON body 中包含 decision: "block"

8.2 MCP Tool Hook

调用已连接的 MCP 服务器工具:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "my_security_server",
            "tool": "scan_file",
            "input": {
              "file_path": "${tool_input.file_path}"
            }
          }
        ]
      }
    ]
  }
}

${tool_input.file_path} 是变量替换语法,从 Hook 输入 JSON 中提取值。

8.3 环境变量

Hook 脚本可使用的环境变量:

变量说明可用事件
$CLAUDE_PROJECT_DIR项目根目录所有
${CLAUDE_PLUGIN_ROOT}插件安装目录所有
${CLAUDE_PLUGIN_DATA}插件持久数据目录所有
$CLAUDE_ENV_FILE环境变量持久化文件SessionStart、Setup、CwdChanged、FileChanged
$CLAUDE_CODE_REMOTE远程环境标记("true" 或未设置)所有

8.4 /hooks 菜单

在 Claude Code 交互会话中输入:

/hooks

可查看所有已注册的 Hook,按事件分类展示,包括来源(User / Project / Local / Plugin)和完整配置详情。

8.5 临时禁用所有 Hook

在 settings.json 中设置:

{
  "disableAllHooks": true
}

此设置无法禁用管理员通过 managed policy 下发的 Hook。要禁用 managed hooks,必须在 managed-settings.json 中设置。


八½、现成的优质 Hooks 插件——不用自己写

前面的实战 Hook 都是手写的。好消息是,社区已经有大量成熟的 Hooks 插件可以直接安装使用。以下按场景分类推荐。

8.6 官方插件(Anthropic 维护)

插件事件功能安装方式
security-guidancePreToolUse扫描 9 类安全漏洞(XSS、注入、eval、pickle 反序列化等),自动拦截/plugin install security-guidance@anthropic-plugins
hookifyPreToolUse / Stop / UserPromptSubmit用 Markdown + YAML 写规则,不需要编辑 JSON。支持 /hookify 命令交互式创建规则/plugin install hookify@anthropic-plugins
ralph-wiggumStop拦截 Claude 的停止尝试,自动重放提示词形成迭代循环,直到任务真正完成/plugin install ralph-wiggum@anthropic-plugins
explanatory-output-styleSessionStart会话启动时注入指令,让 Claude 在实现时解释设计决策/plugin install explanatory-output-style@anthropic-plugins
learning-output-styleSessionStart在关键决策点让 Claude 要求你手写 5-10 行代码,而非全部自动生成/plugin install learning-output-style@anthropic-plugins

security-guidance 是最推荐优先安装的官方插件。它覆盖了 OWASP Top 10 中最常见的前端漏洞模式,零配置开箱即用。

8.7 社区精选(按星标排序)

插件/仓库⭐ Stars事件功能安装方式
tdd-guard~2,050PreToolUse + StopTDD 铁律守护:没有失败的测试就禁止写实现代码。支持 Jest / Vitest / pytest / Go / Rust/plugin marketplace add nizos/tdd-guard/plugin install tdd-guard@tdd-guard/tdd-guard:setup
claude-code-safety-net~1,295PreToolUse语义级命令分析(非正则匹配),拦截 git push --force mainrm -rf / 等破坏性命令。支持 5 层 shell 包装检测/plugin marketplace add kenryu42/cc-marketplace/plugin install safety-net@cc-marketplace
claude-code-prompt-improver~1,407UserPromptSubmit拦截模糊提示词,自动追问 1-6 个澄清问题。清晰提示零开销,模糊提示提升质量,实测减少 31% token 消耗/plugin marketplace add severity1/severity1-marketplace/plugin install prompt-improver@severity1-marketplace
claude-hook-cookbook多种9 个零依赖独立 shell 脚本,覆盖:自动格式化、lint、git 安全、会话日志、上下文重载、停止前测试、桌面通知、对话备份、API Key 防泄漏git clone.claude/hooks/ 目录
claude-code-infrastructure-showcase~9,605多种6 个月生产验证的 Hook 模式集合:TypeScript 类型检查门禁、构建验证、错误处理提醒参考仓库中的 hooks 配置复制到项目
pilot-shell~1,676多种完整工程平台:lint / format / type-check / test 四重质量门禁 + token 优化(实测减少 60-90% 开销)curl -fsSL https://raw.githubusercontent.com/maxritter/pilot-shell/main/install.sh | bash

8.8 按场景速查:该装哪个

你的需求推荐插件一句话理由
防止 Claude 写出安全漏洞security-guidance(官方)9 类漏洞模式自动扫描,零配置
用自然语言写 Hook 规则hookify(官方)Markdown 写规则,不用碰 JSON
强制 TDD 开发tdd-guard没有失败测试就禁止写代码
拦截危险 Git / 文件操作claude-code-safety-net语义分析,不只是正则匹配
提升提示词质量claude-code-prompt-improver模糊提问自动追问,省 31% token
停止前自动跑测试claude-hook-cookbooktest-on-stop.sh一个脚本搞定,零依赖
编辑后自动格式化claude-hook-cookbookauto-format.sh支持 prettier / black / gofmt / rustfmt
会话结束备份对话claude-hook-cookbooktranscript-backup.shSessionEnd 自动触发
Claude 停不下来(迭代循环)ralph-wiggum(官方)/ralph-loop 启动自动迭代
全面工程化(团队)pilot-shell四重门禁 + token 优化一步到位

8.9 快速安装指南

方式一:Plugin 市场安装(推荐)

# 在 Claude Code 交互会话中执行
/plugin marketplace add nizos/tdd-guard
/plugin install tdd-guard@tdd-guard
/tdd-guard:setup

方式二:git clone 安装

# 以 claude-hook-cookbook 为例
git clone https://github.com/echo-lumen/claude-hook-cookbook.git C:\Workspace\你的项目\.claude\hooks\cookbook

# 然后在 settings.json 中引用脚本路径

方式三:手动复制单个脚本

从仓库中挑选需要的单个脚本文件,复制到 .claude/hooks/ 目录,然后在 settings.json 中注册。

国内用户注意git clone 可能超时。解决方案同 MCP 章节——配置 GitHub 镜像或代理。Plugin 市场安装走 npm 通道,已配置 npmmirror 镜像的用户可直接使用。


九、Debug #1 — Hook 脚本不执行(无任何效果)

报错日志

在 settings.json 中配置了 Hook,但 Claude 执行匹配的工具时没有任何拦截或输出。
`/hooks` 菜单中可以看到 Hook 已注册。

根因

Hook 脚本不执行最常见的三个原因:

  1. 脚本文件没有执行权限(Linux/macOS):chmod +x 未执行
  2. matcher 或 if 过滤条件不匹配:matcher 是大小写敏感的,bashBash
  3. 脚本路径中的 $CLAUDE_PROJECT_DIR 未被正确解析:JSON 中未加引号,路径含空格时断裂

一览对比表

对比维度正常情况异常情况
matcher 值Bash(首字母大写)bash(小写,不匹配)
脚本路径"bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/script.sh\""bash $CLAUDE_PROJECT_DIR/.claude/hooks/script.sh(无引号)
脚本权限chmod +x script.sh(Linux)默认无执行权限
配置文件位置正确层级的 settings.json配置写在了不生效的层级

代码修复

检查一:确认 Hook 已注册

/hooks

在菜单中查看目标事件下是否有你的 Hook。如果没有,检查 settings.json 的 JSON 语法是否正确。

检查二:matcher 大小写

// 错误
"matcher": "bash"

// 正确
"matcher": "Bash"

检查三:启用调试日志

claude --debug

调试模式下,Hook 的执行过程(输入 JSON、退出码、stdout、stderr)会完整输出到日志。

验证

claude --debug
# 让 Claude 执行一个匹配的操作,查看调试输出中是否有 Hook 执行记录

十、Debug #2 — Hook JSON 解析失败

报错日志

Hook error: JSON validation failed
  "hookName": "my-pre-hook",
  "error": "Unexpected token '<' at position 0"

根因

Hook 脚本的 stdout 中混入了非 JSON 文本,Claude Code 解析失败。

常见原因:

  1. Shell profile 中有 echo 语句.bashrc.bash_profile 中的 echo 在 Hook 脚本执行时也会输出到 stdout
  2. Python 的 print 调试语句:Hook 脚本中残留的 print("debug") 混入 stdout
  3. jq 的格式化输出jq 默认带颜色高亮,在某些终端输出 ANSI 转义字符

一览对比表

对比维度正常情况异常情况
stdout 输出仅包含合法 JSON 或为空包含 echo/print 的调试文本
Shell profile无输出语句.bashrc 中有 echo "Welcome"
jq 调用jq -n '{...}'jq '{...}' 可能带 ANSI 颜色

代码修复

方案一:将调试输出重定向到 stderr

# 错误:print 输出到 stdout
echo "Debug: checking command"

# 正确:调试信息输出到 stderr
echo "Debug: checking command" >&2

方案二:Python 中使用 stderr

# 错误
print("Debug info")

# 正确
print("Debug info", file=sys.stderr)

方案三:确保 Shell profile 不干扰

在 Hook 脚本开头添加:

#!/bin/bash
# 确保 stdout 不被 profile 污染
exec >/dev/null 2>&1  # 如果不需要输出
# 或者只在需要输出 JSON 时恢复 stdout

验证

# 直接运行脚本,检查 stdout 是否只有 JSON
echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | python .claude/hooks/my-hook.py
# 应只输出 JSON 或空行,不包含调试文本

十一、Debug #3 — Windows 上 Bash 脚本执行失败

报错日志

Hook error: Command failed with exit code 127
  "hookName": "block-rm",
  "stderr": "bash: /c/Users/.../.claude/hooks/block-rm.sh: No such file or directory"

或:

Hook error: Command failed
  "stderr": "'jq' is not recognized as an internal or external command"

根因

Windows 上运行 Bash 脚本有两个常见坑:

  1. bash 命令不在 PATH 中:未安装 Git for Windows,或安装时未勾选"Add to PATH"
  2. jq 未安装:脚本中使用 jq 解析 JSON,但 Windows 默认不自带 jq
  3. 路径分隔符:Windows 使用 \,而 Bash 脚本期望 /

一览对比表

对比维度正常情况异常情况
bash 命令Git Bash 已安装且在 PATH 中bash 命令不可用
jq 工具已安装且在 PATH 中Windows 默认无 jq
路径分隔符$CLAUDE_PROJECT_DIR 展开为正斜杠或被引号包裹反斜杠导致 Bash 解析失败

代码修复

方案一:用 Python 替代 Bash + jq

#!/usr/bin/env python3
"""替代 Bash+jq 的跨平台 Hook 脚本"""
import json, sys

data = json.load(sys.stdin)
command = data.get("tool_input", {}).get("command", "")

if "rm -rf" in command:
    print("Blocked", file=sys.stderr)
    sys.exit(2)

方案二:安装 jq for Windows

winget install jqlang.jq
# 或
scoop install jq

方案三:使用 PowerShell 作为 Hook Shell

{
  "type": "command",
  "shell": "powershell",
  "command": "$data = [Console]::In.ReadToEnd() | ConvertFrom-Json; if ($data.tool_input.command -match 'rm -rf') { exit 2 }"
}

验证

# 确认 bash 可用
bash --version

# 确认 jq 可用
jq --version

# 测试 Hook 脚本
echo '{"tool_input":{"command":"rm -rf /tmp"}}' | python .claude/hooks/block-dangerous.py
echo %ERRORLEVEL%  # 应输出 2

十二、Debug #4 — Hook 超时导致 Claude 卡顿

报错日志

Hook timed out after 600 seconds
  "hookName": "auto-format",
  "timeout": 600

或 Claude 执行工具前长时间等待,无任何输出。

根因

Hook 脚本执行时间超过 timeout 设置(默认 600 秒),Claude Code 会终止脚本并记录超时错误。

常见原因:

  1. 网络请求:脚本中调用了外部 API 但目标服务不可达
  2. 无限循环:脚本逻辑错误导致死循环
  3. 未设置 timeout:默认 600 秒太长,实际上几秒就该完成的操作

一览对比表

对比维度正常情况异常情况
执行时间< 5 秒> timeout 值
timeout 设置根据操作类型合理设置使用默认 600 秒
外部依赖可达网络超时或服务宕机

代码修复

设置合理的 timeout

{
  "type": "command",
  "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/auto-format.sh\"",
  "timeout": 30
}
场景推荐 timeout
简单判断(文件路径检查)5-10 秒
代码格式化(ruff/prettier)30 秒
运行测试套件120-300 秒
网络请求15-30 秒

给脚本加超时保护

#!/bin/bash
# 使用 timeout 命令限制外部工具执行时间
timeout 20 ruff format "$1" 2>/dev/null
exit 0

验证

# 在 /hooks 菜单中确认 timeout 值已更新
/hooks

十三、Debug #5 — additionalContext 未注入 Claude 上下文

报错日志

Hook 执行成功(退出码 0),JSON 输出正确,但 Claude 似乎看不到注入的上下文信息。

根因

additionalContext 未生效的原因:

  1. JSON 格式错误hookEventName 必须与实际事件名完全匹配(如 "PreToolUse" 而非 "pretooluse"
  2. stdout 混入非 JSON 文本:Claude Code 只解析纯 JSON,如果 stdout 中包含其他文本则整个输出被忽略
  3. 超长输出被截断:输出超过 10,000 字符时会被写入文件,Claude 只收到文件路径

一览对比表

对比维度正常情况异常情况
hookEventName"PostToolUse"(与事件完全匹配)"posttooluse"(大小写不对)
stdout 内容仅包含合法 JSONJSON 前有 echo 输出的文本
输出长度< 10,000 字符超过 10,000 字符被截断为文件

代码修复

正确的 JSON 输出格式

#!/bin/bash
# ... 业务逻辑 ...

# 输出纯 JSON,不混入其他文本
jq -n '{
    hookSpecificOutput: {
        hookEventName: "PostToolUse",
        additionalContext: "当前分支: main. 最后提交: fix: auth bug"
    }
}'
exit 0

避免 stdout 污染

#!/bin/bash
# 所有调试信息输出到 stderr
echo "Debug: processing file" >&2

# stdout 只输出最终 JSON
jq -n '{ hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: "info" } }'

验证

# 使用 --debug 模式查看 Hook 输出
claude --debug
# 让 Claude 执行匹配的操作
# 在调试日志中检查 Hook 的 stdout 是否为纯 JSON

十四、速查卡

14.1 核心事件速查

事件触发时机能阻止?典型用途
SessionStart会话启动/恢复加载 Git 状态、设置环境变量
UserPromptSubmit用户提交提示提示验证、自动注入上下文
PreToolUse工具执行前拦截危险命令、保护敏感文件
PostToolUse工具执行后自动格式化、审计日志
StopClaude 想停止强制运行测试、检查完成度
SessionEnd会话终止清理临时文件、发送通知

14.2 匹配器语法速查

匹配器匹配范围
Bash仅 Bash 工具
Edit|WriteEdit 或 Write
mcp__memory__.*Memory MCP 服务器所有工具
^Notebook以 Notebook 开头的工具
* / 省略所有工具

14.3 退出码速查

退出码含义stdout 处理
0放行解析 JSON(如有)
2阻止忽略 JSON,stderr 展示给 Claude
其他非阻塞错误忽略 JSON,stderr 展示

14.4 JSON 输出字段速查

字段适用事件说明
continue: false所有停止 Claude
stopReason所有continue=false 时的原因
decision: "block"Stop, PostToolUse, UserPromptSubmit 等阻止特定行为
hookSpecificOutput.additionalContext大部分注入上下文到 Claude
hookSpecificOutput.permissionDecisionPreToolUseallow / deny / ask / defer
hookSpecificOutput.updatedInputPreToolUse修改工具输入

14.5 文件路径汇总

文件路径用途
全局配置C:\Users\<用户名>\.claude\settings.json全局 Hook
项目配置<项目>\.claude\settings.json项目共享 Hook
项目本地<项目>\.claude\settings.local.json个人 Hook(不提交)
项目脚本目录<项目>\.claude\hooks\Hook 脚本存放
调试日志claude --debug 输出Hook 执行详情

14.6 常见报错 → 解决方案

报错特征根因解决
Hook 无任何效果matcher 大小写不对 / 路径未加引号检查 matcher(Bashbash)→ Debug #1
JSON validation failedstdout 混入非 JSON 文本调试输出重定向到 stderr → Debug #2
No such file or directoryWindows 无 bash / jq用 Python 替代 → Debug #3
Hook timed out脚本执行过久设置合理 timeout → Debug #4
上下文未注入hookEventName 不匹配 / stdout 污染确保纯 JSON 输出 → Debug #5
disableAllHooks 无效managed policy 优先级更高在 managed-settings.json 中设置
Hook 重复执行多个配置层级都定义了相同 Hook检查全局 + 项目 + 本地三层配置

参考文献

  1. Hooks reference — Claude Code 官方文档
  2. Automate workflows with hooks — Claude Code 官方指南
  3. security-guidance plugin — Anthropic 官方插件
  4. hookify plugin — Anthropic 官方插件
  5. Claude Code Plugins README — Anthropic GitHub
  6. Claude Code settings reference — 官方文档
  7. tdd-guard — TDD 铁律守护插件 — PreToolUse + Stop 双事件强制 TDD 流程
  8. claude-code-safety-net — 语义级命令拦截 — 支持 5 层 shell 包装检测
  9. claude-code-prompt-improver — 提示词质量提升 — UserPromptSubmit 拦截模糊提问
  10. claude-hook-cookbook — 9 个零依赖 Hook 脚本 — 覆盖格式化、lint、安全、日志、通知等场景
  11. pilot-shell — 完整工程化平台 — 四重质量门禁 + token 优化
  12. claude-code-infrastructure-showcase — 生产级 Hook 模式 — 6 个月生产验证
  13. awesome-claude-code — 社区生态总表 — Skills / Hooks / Plugins / Agents 精选列表