Claude Code Hooks 学习

Claude Code Hooks

------官方 8 大事件、全部字段、10+ 脚本,一步到位


一、一分钟总览

  1. Hooks = 在 Claude Code 生命周期 8 个固定节点插入「用户脚本」。
  2. 用 JSON 声明,分「全局」(~/.claude/settings.json) 与「项目」(.claude/settings.local.json)。
  3. 每个 Hook 由「事件 + 匹配器 + 命令」三元组组成。
  4. 官方事件 8 个,变量 10+ 个,全部列在后面。

二、官方 8 大事件全表

事件 触发时机 官方给出变量 能否阻断 典型用途
SessionStart 会话新建 / resume 初始化环境
UserPromptSubmit 用户按回车前 prompt 危险指令过滤
PreToolUse 工具准备执行前 tool_name, tool_input 权限/审计
PostToolUse 工具执行结束 tool_name, tool_input, tool_output 自动格式化
Notification Claude 需要用户输入 notification_text 桌面/Slack 通知
Stop 回答整体结束 汇总日志
SubagentStop 子代理任务结束 subagent_name, result 子任务统计
PreCompact 压缩对话缓存前 备份历史

三、配置文件结构(全局 vs 项目)

路径规则

• 全局:~/.claude/settings.json

• 项目:.claude/settings.local.json(优先级高)

最小合法骨架

json5 复制代码
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "echo PreToolUse" }
        ]
      }
    ]
  }
}

字段说明

字段 类型 必填 说明
matcher string 工具名或正则,支持 Bash|Edit|Write*
type enum command / script
command string 二选一 直接写 shell
script string 二选一 指向可执行文件路径
env object 额外环境变量
working_dir string 工作目录,缺省为项目根

四、匹配器(Matcher)语法精讲

  1. 单工具:Bash
  2. 多工具:Edit|Write|MultiEdit
  3. 正则:Bash(rm -rf .*) 仅匹配含 rm -rf 的 Bash 命令
  4. 通配:* 匹配所有

五、官方环境变量全集

变量 出现事件 内容示例
CLAUDE_EVENT 全部 PreToolUse
CLAUDE_TOOL_NAME Pre/PostToolUse Bash
CLAUDE_TOOL_INPUT Pre/PostToolUse JSON 字符串
CLAUDE_TOOL_OUTPUT PostToolUse 工具返回值
CLAUDE_FILE_PATH Edit/Write /home/me/src/index.ts
CLAUDE_LINE_START Edit/Write 12
CLAUDE_LINE_END Edit/Write 15
CLAUDE_NOTIFICATION_TEXT Notification Claude needs your input
CLAUDE_SUBAGENT_NAME SubagentStop planner
CLAUDE_SUBAGENT_RESULT SubagentStop JSON 结果
CLAUDE_PROJECT_DIR 全部 /home/me/project

六、可视化配置 vs 手写 JSON

• 运行 /hooks 进入 TUI,可增删改查。

• 改动后需 ccr restart 或重启 Claude Code 才生效。

• 最终仍落盘到 settings.json,可直接编辑。


七、10 个示例

  1. 全局 Bash 命令审计(PreToolUse)
json 复制代码
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "type": "command",
        "command": "jq -r '\"[$(date -Iseconds)] $tool_name: $tool_input.command\"' <<< \"$CLAUDE_TOOL_INPUT\" >> ~/.claude/audit.log"
      }
    ]
  }
}
  1. TypeScript 自动 Prettier(PostToolUse)
json 复制代码
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "type": "command",
        "command": "[[ \"$CLAUDE_FILE_PATH\" == *.ts ]] && npx prettier --write \"$CLAUDE_FILE_PATH\""
      }
    ]
  }
}
  1. 阻止删除生产文件(PreToolUse)
json 复制代码
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "type": "script",
        "script": ".claude/hooks/protect_rm.sh"
      }
    ]
  }
}

protect_rm.sh

bash 复制代码
#!/usr/bin/env bash
set -e
cmd=$(jq -r '.tool_input.command' <<< "$CLAUDE_TOOL_INPUT")
[[ $cmd =~ rm\ -rf.*(src|dist|\.env) ]] && { echo "Protected!"; exit 2; }
  1. Markdown 自动补语言标签 (PostToolUse)
    Python 脚本:.claude/hooks/md_lang.py
python 复制代码
#!/usr/bin/env python3
import re, json, sys, os
path = os.environ["CLAUDE_FILE_PATH"]
if not path.endswith(".md"): sys.exit(0)
with open(path) as f: txt = f.read()
new = re.sub(r"```\n(.*?)\n```", lambda m: f"```{guess(m.group(1))}\n{m.group(1)}\n```", txt, flags=re.S)
if new != txt:
    with open(path,"w") as f: f.write(new)
    os.system(f"git add {path}")
def guess(code):
    if re.search(r"^\s*(import|def|class)\s", code, re.M): return "python"
    if re.search(r"^\s*(const|let|function|import)\s", code, re.M): return "javascript"
    return ""

Hook 配置

json 复制代码
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "type": "script",
        "script": ".claude/hooks/md_lang.py"
      }
    ]
  }
}
  1. 桌面通知"等你输入"(Notification)
json 复制代码
{
  "hooks": {
    "Notification": [
      {
        "matcher": "*",
        "type": "command",
        "command": "notify-send Claude \"$CLAUDE_NOTIFICATION_TEXT\""
      }
    ]
  }
}
  1. 会话启动打印欢迎横幅(SessionStart)
json 复制代码
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "*",
        "type": "command",
        "command": "echo 'Welcome to Claude Code 🧑‍💻' | lolcat"
      }
    ]
  }
}
  1. 子代理结束统计(SubagentStop)
json 复制代码
{
  "hooks": {
    "SubagentStop": [
      {
        "matcher": "*",
        "type": "command",
        "command": "jq -c '{agent: env.CLAUDE_SUBAGENT_NAME, status: env.CLAUDE_SUBAGENT_RESULT}' >> ~/.claude/subagent-report.jsonl"
      }
    ]
  }
}
  1. 压缩前备份对话(PreCompact)
json 复制代码
{
  "hooks": {
    "PreCompact": [
      {
        "matcher": "*",
        "type": "command",
        "command": "cp ~/.claude/session.json ~/.claude/session-backup-$(date +%s).json"
      }
    ]
  }
}
  1. 用户提示强制加前缀(UserPromptSubmit)
json 复制代码
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "matcher": "*",
        "type": "script",
        "script": ".claude/hooks/prefix.sh"
      }
    ]
  }
}

prefix.sh

bash 复制代码
#!/usr/bin/env bash
prompt=$(jq -r '.' <<< "$CLAUDE_PROMPT")
new="[SAFE MODE] $prompt"
jq -n --arg p "$new" '$p'
  1. Stop 事件自动提交 Git(Stop)
json 复制代码
{
  "hooks": {
    "Stop": [
      {
        "matcher": "*",
        "type": "command",
        "command": "git add -A && git commit -m 'claude auto save'"
      }
    ]
  }
}

八、调试与故障排查官方清单

  1. /hooks → TUI 查看配置。

  2. ccr restart 让修改生效。

  3. /debug 打开详细日志。

  4. 本地单测:

    bash 复制代码
    CLAUDE_EVENT=PreToolUse \
    CLAUDE_TOOL_INPUT='{"tool_input":{"command":"ls"}}' \
    ./your-hook.sh
  5. 日志追加:脚本首行 exec >> ~/.claude/hooks.log 2>&1 && set -x


九、安全红线(官方原文提炼)

  • Hook 以当前用户身份执行,勿用 root 跑 Claude Code。
  • 避免 evalbash -c "$untrusted"
  • 项目级钩子优先,全局钩子仅放审计类脚本。
  • 审查第三方脚本后再启用。

十、一键速查表(打印贴墙)

我做这件事 用哪个事件 示例 matcher
拦截 rm -rf PreToolUse Bash(rm -rf)
自动 prettier PostToolUse Edit|Write
提示词加前缀 UserPromptSubmit *
桌面通知 Notification *
备份对话 PreCompact *
子代理统计 SubagentStop *
欢迎横幅 SessionStart *
自动 commit Stop *

结语

掌握「8 大事件 + 2 个配置文件 + 10+ 环境变量」,你就能把 Claude Code 变成:

  • 自动格式化器
  • 文件守护者
  • 审计机器人
  • 通知中心