Claude Code hooks — 在会话和工具事件上自动运行 shell 命令
settings.json 中的 hooks 可在会话开始、工具调用前后及停止时插入 shell 命令。五个示例:安全拦截、自动格式化、日志记录、外部告警。
Claude Code 的 Hooks — 定义在 settings.json 的 hooks 键下 — 让你能够在会话开始、工具调用前后以及会话结束时自动运行 shell 命令。可用于拦截 AI 即将执行的操作、事后修复,以及收尾清理。这是一个强大的扩展点。
我认为 Hooks 是 Claude Code 配置中最容易被低估的部分。不是工具数量决定自动化水平,而是事件响应能力——因为只有将行为锁定在事件钩子上,才能真正保证一致性。
本指南面向 Claude Code 1.x / 官方 Hooks 规范,在 Claude Code 配置 基础上进一步增加自动化和安全保障层。
TL;DR
| Hook 事件 | 触发时机 | 典型用途 |
|---|---|---|
SessionStart | 会话开始后立即 | 内存同步提示、环境验证 |
UserPromptSubmit | 用户提交 prompt 后立即 | 斜杠命令识别、日志记录、后处理 |
PreToolUse | 工具调用前立即 | 阻止危险命令、保护密钥文件 |
PostToolUse | 工具调用后立即 | 自动格式化(prettier)、lint、副作用日志 |
Stop | 会话结束前立即 | 变更摘要、备份、通知 |
关键规则:
- 退出码 2 会阻止工具调用(PreToolUse)。
- JSON 通过 hook stdin 传入 — 用
jq解析。 - Hooks 是 shell 命令 — 注意 OS 差异(macOS/Linux/Windows WSL)。
前提条件
- Claude Code 1.x 已安装 — Claude Code 配置
jq已安装(brew install jq/apt install jq/winget install jqlang.jq)- 有权编辑
settings.json(通常是~/.claude/settings.json)
下载基础配置
devAlice 推荐的五条 Hook 基础配置 + 权限拒绝/允许列表 — 下载后按需定制。
settings.json# 1. 下载
curl -fsSL https://devalice.jaceclub.com/assets/ai-agents/claude-code-hooks/settings.example.json -o settings.json
# 2. 验证 SHA-256
shasum -a 256 settings.json
# 期望值:30c1d4117a935cac03b601c71125c11b71733c8c59517ab129fd8913cfb8a69e
# 3. 检查(尤其是 PreToolUse hooks)
less settings.json
# 4. 复制到目标位置(macOS/Linux)
mkdir -p ~/.claude
cp settings.json ~/.claude/settings.json⚠️ 注意:Hooks 会运行任意 shell 命令。务必先检查他人的 Hooks 再应用。此示例是安全的五条 Hook 基础配置,但请根据自身环境审阅并调整。
1. Hook 结构 — 5 分钟
~/.claude/settings.json 中的 hooks 键:
{
"hooks": {
"EventName": [
{
"matcher": "正则或工具名", // 可选
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc '实际的 shell 命令'"
}
]
}
]
}
}匹配规则:
- 无
matcher→ 每次事件均触发 matcher= 工具名(Bash、Write、Edit、Read等)→ 仅匹配该工具matcher= 正则(^/(?:ej|euijinpro)\\b)→ 用于UserPromptSubmit,匹配 prompt 文本
通过 stdin 传入的 JSON:
{
"session_id": "...",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/Users/...",
"tool_name": "Bash",
"tool_input": { "command": "..." },
"tool_response": { /* 仅 PostToolUse */ }
}用 jq -r .tool_input.command 提取。
2. SessionStart — 加载上下文
最简单的 hook。会话开始时打印一行:
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'printf \"[session start] %s\\nmemory sync: latest (skip)\\n\" \"$(date \"+%Y-%m-%d %H:%M:%S\")\"'"
}
]
}
]stdout 会以系统消息的形式呈现。可用于:
- 触发内存文件自动加载
- 输出项目专属上下文
- 启动时输出 git status 摘要
# 更丰富的示例
command='/bin/bash -lc "echo [session start]; git -C \"$PWD\" status -s; git -C \"$PWD\" log --oneline -3"'3. PreToolUse — 阻止危险命令(价值最高的 Hook)
退出码为 2 即阻止。即使 AI 尝试执行危险的 shell 命令,也能在此拦截。
3.1 危险模式
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'cmd=$(jq -r .tool_input.command); case \"$cmd\" in *\"rm -rf /\"*|*\"sudo rm\"*|*\":(){\"*) echo \"blocked dangerous command: $cmd\" >&2; exit 2;; esac'"
}
]
}
]拦截的危险模式:
rm -rf /— 破坏系统sudo rm— 大范围删除(如有需要可加入 allow-list):(){— fork bomb(:(){ :|: & };:)
3.2 阻止对密钥文件的编辑
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'path=$(jq -r .tool_input.file_path); case \"$path\" in */.env|*/credentials.json|*/.aws/credentials) echo \"blocked secret-file edit: $path\" >&2; exit 2;; esac'"
}
]
}防止意外修改 .env、credentials.json、~/.aws/credentials 等敏感文件。
3.3 保护特定目录
command='/bin/bash -lc "path=\$(jq -r .tool_input.file_path); case \"\$path\" in /etc/*|/System/*) echo \"blocked system path: \$path\" >&2; exit 2;; esac"'4. PostToolUse — 自动格式化 / Lint / 日志记录
工具调用完成后的清理与后处理。
4.1 Prettier 自动格式化
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'path=$(jq -r .tool_input.file_path); case \"$path\" in *.ts|*.tsx|*.js|*.jsx) prettier --write \"$path\" 2>/dev/null || true ;; esac'"
}
]
}
]AI 执行 Write/Edit 后,prettier 自动整理格式。每次编辑约增加 0.5 秒延迟。
4.2 记录变更文件
command='/bin/bash -lc "path=\$(jq -r .tool_input.file_path); echo \"[changed] \$(date +%T) \$path\" >> ~/.claude/change.log"'会话中每次文件变更的时序记录。
4.3 类型检查 / Lint(慢速操作移至 Stop)
每次编辑都执行 tsc --noEmit 开销较大。建议放在 Stop Hook 中(§6)。
5. UserPromptSubmit — slash command 检测 / 后处理触发
"UserPromptSubmit": [
{
"matcher": "^/(?:ej|euijinpro)\\b",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'echo \"[ej skill] prompt earmarked for euijin.pro logging\" >&2'"
}
]
}
]- 检测 slash command → 触发外部系统(Slack、独立日志)
- 自动标记特定关键词(
urgent、bug) - token 预算阈值检查
6. Stop — 会话结束时的清理
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'printf \"\\n[session end] %s\\n\" \"$(date \"+%Y-%m-%d %H:%M:%S\")\"'"
}
]
}
]用途:
- 已变更文件的 git status 摘要
- 备份(
rsync或git stash) - 外部关闭通知
- 一轮类型检查 / 构建
command='/bin/bash -lc "if git diff --quiet; then echo \"[clean]\"; else echo \"[changes]\"; git diff --stat; fi"'7. permissions — 拒绝 / 允许
独立于 Hooks,但同样是 settings.json 中最重要的安全配置项。
"permissions": {
"deny": [
"Bash(rm -rf /:*)",
"Bash(sudo rm:*)",
"Read(.env)",
"Read(.env.local)",
"Read(.aws/credentials)",
"Read(*/credentials.json)"
],
"allow": [
"Bash(git status:*)",
"Bash(git log:*)",
"Bash(git diff:*)",
"Bash(pnpm:*)",
"Bash(npm:*)",
"Bash(node:*)"
]
}deny无条件优先 — 与 Hook 拦截形成纵深防御allow中的命令无需用户确认,自动批准(常用的安全命令)- 其余所有操作每次都会询问用户
本指南的 Hooks 与 deny 规则形成叠加保护 — 构成纵深防御。
8. settings.json 位置和优先级
| 位置 | 范围 | 说明 |
|---|---|---|
~/.claude/settings.json | 用户全局 | 所有项目 |
<project>/.claude/settings.json | 项目 | 进入项目时与用户配置合并 |
<project>/.claude/settings.local.json | 项目 + 用户本地 | 建议加入 .gitignore |
合并顺序:用户 → 项目 → 项目本地,后者覆盖前者。
Hooks 采用追加而非覆盖合并 — 5 个用户 Hook + 3 个项目 Hook = 全部 8 个均会运行。
9. 调试 — Hook 不触发时的排查
9.1 检查 stdin payload
临时调试 hook:
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'cat > /tmp/claude-hook-debug.json'"
}
]
}→ cat /tmp/claude-hook-debug.json | jq .
9.2 测试退出码
command='/bin/bash -lc "echo dbg >&2; exit 2"'→ 验证阻止(PreToolUse)是否有效。
9.3 确认 Hook 触发
command='/bin/bash -lc "echo \"[hook fired $$ $(date +%T)]\" >> ~/.claude/hook-trace.log"'会话后检查日志。
9.4 JSON 语法错误
settings.json 容错性低 — 尾随逗号和转义遗漏是最常见的问题。通过 jq . ~/.claude/settings.json 验证语法。
10. 故障排查
"command not found: jq"
brew install jq(macOS)/ sudo apt install jq(Linux)/ winget install jqlang.jq(Windows)。
Hook 触发但无任何效果
command必须是单行。多行逻辑请用\\n分隔,或调用独立脚本文件。- 注意 shell 转义 — JSON 内部
\"需要双重转义。
PreToolUse exit 2 未生效
- 确认
exit 2确实执行到 shell 命令的最后一行。 set -e环境下case ;;可能以 exit 0 退出 — 请在匹配分支内显式写exit 2。
Prettier 不运行
- 确认 prettier 已安装在项目根目录(
pnpm prettier --version)。 - Hook PATH 可能不包含
node_modules/.bin→ 使用npx prettier或绝对路径。
settings.json 变更未生效
- 需要重启会话,或使用
/config reload(如支持)。 - 可能因 JSON 语法错误而被静默忽略。用
jq . ~/.claude/settings.json验证。
后续步骤
- Claude Code 配置 — 前置条件
- Agent runner — 自主执行 + hooks 组合
- MCP servers — 与 hooks 的角色区别
- 多代理工作流 — 集成 subagents
参考资料
更新日志
- 2026-05-12 — 初稿(devAlice M2 种子扩展)