主题
Hooks 钩子
Hooks 在 Claude Code 生命周期事件中自动触发的脚本,跟 AI 智能无关 — 是确定性控制。
一句话
Hooks 保证执行;提示词只是建议。 用 hooks 跑必须每次都执行的检查(lint、security scan、备份)。
适用场景
| 场景 | 用 Hook | 别用 Prompt |
|---|---|---|
| 改完代码自动 format | ✅ | ❌(Claude 可能忘) |
阻止 Claude 修改 .env | ✅ | ❌(提示能绕过) |
| 写代码前先跑 lint | ✅ | ❌ |
| 提交前必跑测试 | ✅ | ❌ |
| 让 Claude 用某编码风格 | ❌ | ✅(Prompt 够了) |
Hook 事件类型
| 事件 | 何时触发 |
|---|---|
PreToolUse | 工具调用前 —— 可拦截/修改 |
PostToolUse | 工具调用后 —— 看结果 |
Stop | 会话结束时 |
UserPromptSubmit | 用户提交 prompt 时 |
Notification | Claude 发通知时 |
配置位置
json
// ~/.claude/settings.json(全局)
// 或 .claude/settings.json(项目级)
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo '🚨 Bash command: ' && echo \"$CLAUDE_TOOL_INPUT\""
}
]
}
]
}
}实战例子:3 个超有用的 hook
1. 自动 format(PostToolUse)
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "if [[ \"$CLAUDE_FILE_PATH\" == *.ts ]] || [[ \"$CLAUDE_FILE_PATH\" == *.tsx ]]; then npx prettier --write \"$CLAUDE_FILE_PATH\"; fi"
}
]
}
]
}
}Claude 改完 .ts / .tsx 文件自动跑 prettier。
2. 阻止改 .env(PreToolUse + 拦截)
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "if [[ \"$CLAUDE_FILE_PATH\" == *.env* ]]; then echo '🛑 BLOCKED: .env files cannot be modified' >&2; exit 1; fi"
}
]
}
]
}
}返回非 0 退出码会阻止该工具调用执行。
3. 提交前必跑测试(PreToolUse Bash)
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -q 'git commit'; then pnpm test || (echo '❌ Tests failed, commit blocked' >&2; exit 1); fi"
}
]
}
]
}
}Claude 想 git commit → 先跑测试,挂了拦下。
环境变量(hook 内可用)
| 变量 | 含义 |
|---|---|
$CLAUDE_TOOL_NAME | 工具名(Bash / Edit / Write / Read ...) |
$CLAUDE_TOOL_INPUT | 完整输入 JSON |
$CLAUDE_FILE_PATH | 操作的文件路径(如有) |
$CLAUDE_SESSION_ID | 当前会话 ID |
$CLAUDE_CWD | 工作目录 |
高级:用脚本而不是 inline command
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/pre-edit.sh"
}
]
}
]
}
}.claude/hooks/pre-edit.sh:
bash
#!/usr/bin/env bash
set -e
FILE="$CLAUDE_FILE_PATH"
# 多个规则
case "$FILE" in
*.env*|*.pem|*.key|*secrets*)
echo "🛑 BLOCKED: protected file $FILE" >&2
exit 1
;;
*.ts|*.tsx)
# 改 TS 文件前先 check 是否会破坏类型
npx tsc --noEmit --skipLibCheck && echo "✅ Type check passed"
;;
esacchmod +x .claude/hooks/pre-edit.sh 给执行权限。
Hook 跟 deny rule 的区别
settings.json deny | Hook | |
|---|---|---|
| 阻止机制 | Claude 看到规则不去碰 | 强制拦截,不靠 Claude 自觉 |
| 可绕过吗 | 提示词社工有可能 | 不能 |
| 编程逻辑 | 静态规则 | 动态 shell 脚本 |
| 推荐用于 | 一般约束 | 安全/合规关键文件 |
注意事项
Hook 是定时炸弹
- Hook 命令在你机器上跑,有完整文件系统访问权
- 写 hook 之前想清楚副作用
- 不要
rm -rf之类的破坏性操作 - 定期 review
~/.claude/settings.json防止被恶意 hook 注入
推荐安全 Hook(直接抄)
awesome-claude-code-toolkit 的 hooks 集合 已经预制了 20+ 个生产级 hook,包括:
- 阻止
.env/.pem/secrets/访问 - AWS / GCP credential 文件保护
- kubeconfig 写保护
- SSH key 保护
- git 危险命令(force push / reset hard)拦截
bash
# 一键拉
cd ~/.claude
git clone https://github.com/rohitg00/awesome-claude-code-toolkit
ln -s awesome-claude-code-toolkit/hooks .claude-hooks
# 然后把 hooks 引到 settings.json调试 hook
启动 claude 加 --debug:
bash
claude --debug每个 hook 触发都会打日志,方便调试。
Hook vs Skill vs Slash Command vs MCP
| 触发 | 用途 | |
|---|---|---|
| Hook | 自动(生命周期事件) | 强制规则 / 自动化 |
| Skill | 自动(任务匹配) | 复用工作流 |
| Slash Command | 手动 / | 一次性指令 |
| MCP | 自动(数据需要) | 外部数据/能力 |
四者互补,不冲突。
