
在 Agent 循环外运行的脚本,不占上下文,自动触发。
Hooks 是 Claude Code 最被低估的功能之一。
它能在特定时机自动运行脚本:文件保存后自动格式化、危险命令执行前拦截、任务完成时发送通知。更重要的是——它不占用上下文窗口。
Hooks 是事件驱动的脚本,在 Claude Code 生命周期的特定时刻自动触发。
Claude 执行流程:
用户输入 → Claude 推理 → PreToolUse → 工具执行 → PostToolUse → Claude 响应 → Stop
↑ ↑
拦截点 后处理点
核心优势:作为外部脚本运行,不占用 Claude 的上下文窗口。你在 Hooks 里做的任何事,都不会消耗那宝贵的 200K token。
事件 | 触发时机 | 典型用途 |
|---|---|---|
PreToolUse | 工具执行前 | 拦截危险命令、修改参数 |
PostToolUse | 工具执行后 | 自动格式化、通知 |
Notification | Claude 等待输入时 | 桌面通知 |
SessionStart | 会话开始时 | 加载环境变量 |
SessionEnd | 会话结束时 | 清理临时文件 |
Stop | Claude 完成响应时 | 验证任务完成 |
PreCompact | 上下文压缩前 | 保存关键信息 |
PostCompact | 上下文压缩后 | 重新注入上下文 |
ConfigChange | 配置文件变化时 | 审计日志 |
Hooks 配置在 settings.json 中:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "prettier --write \"$CLAUDE_FILE_PATH\""
}
]
}
]
}
}
配置位置:
位置 | 作用域 | 可共享 |
|---|---|---|
~/.claude/settings.json | 所有项目 | 否 |
.claude/settings.json | 单个项目 | 是,可提交 Git |
.claude/settings.local.json | 单个项目 | 否,gitignored |
Claude 完成任务后自动通知你,不用一直盯着终端。
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
macOS 使用 osascript,Linux 使用 notify-send,Windows 使用 PowerShell。
每次文件编辑后自动运行 Prettier。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
阻止 Claude 修改 .env、package-lock.json 等敏感文件。
#!/bin/bash
INPUT=$(cat)
FILE_PATH=(echo "
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "
echo "Blocked:
exit 2 # exit 2 = 阻止操作
fi
done
exit 0 # exit 0 = 放行
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
上下文压缩后,重新注入项目规则。
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo 'Reminder: use Bun, not npm. Run bun test before committing.'"
}
]
}
]
}
}
跳过 ExitPlanMode 的审批对话框。
{
"hooks": {
"PermissionRequest": [
{
"matcher": "ExitPlanMode",
"hooks": [
{
"type": "command",
"command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
}
]
}
]
}
}
配合 direnv,当目录变化时自动加载环境变量。
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
}
]
}
],
"CwdChanged": [
{
"hooks": [
{
"type": "command",
"command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
}
]
}
]
}
}
Hooks 通过 stdin 接收事件数据,通过 stdout/stderr 和 exit code 返回结果。
{
"session_id": "abc123",
"cwd": "/Users/sarah/myproject",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}
Exit Code | 行为 |
|---|---|
0 | 放行,继续执行 |
2 | 阻止,stderr 作为反馈给 Claude |
其他 | 放行,但显示警告 |
更精细的控制,返回 JSON:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Use rg instead of grep for better performance"
}
}
按工具名过滤:
{
"matcher": "Edit|Write" // 匹配 Edit 或 Write
}
{
"matcher": "Bash" // 只匹配 Bash
}
{
"matcher": "mcp__github__.*" // 匹配所有 GitHub MCP 工具
}
按工具参数过滤:
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git *)",
"command": "echo 'Git command detected'"
}
]
}
把事件数据 POST 到外部服务:
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/tool-use",
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}
]
}
]
}
}
用 Claude 模型做决策:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains\"}."
}
]
}
]
}
}
启动子 Agent 验证:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "Verify that all unit tests pass. Run the test suite and check results.",
"timeout": 120
}
]
}
]
}
}
1. 运行 /hooks 确认配置存在
2. 检查 matcher 是否正确(区分大小写)
3. 确认事件类型对不对
测试脚本:
echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
echo $? # 查看退出码
常见问题:
- 命令找不到 → 用绝对路径
- jq 找不到 → 安装 jq
- 脚本不执行 → chmod +x ./my-hook.sh
检查 stop_hook_active 字段:
#!/bin/bash
INPUT=$(cat)
if [ "
exit 0 # 已经触发过,允许停止
fi
1. 优先用 PostToolUse 而非 PreToolUse — PreToolUse 可能被拒绝,PostToolUse 更可靠
2. 脚本放项目目录 — .claude/hooks/ 可以提交 Git,团队共享
3. 用 jq 解析 JSON — 比 awk/sed 更可靠
4. stderr 给 Claude,stdout 返回 JSON — 各司其职
5. 超时设置合理 — 默认 10 分钟,复杂任务可调长
维度 | Hooks | Skills | CLAUDE.md |
|---|---|---|---|
加载时机 | 事件触发 | 按需加载 | 每次会话 |
上下文成本 | 零 | 描述常驻 | 每次请求都带 |
能力 | 运行脚本 | 提供知识 | 提供规则 |
适用场景 | 自动化 | 工作流 | 项目约定 |
结论:Hooks 是"自动化",Skills 是"知识库",CLAUDE.md 是"项目宪法"。三者配合,效率最高。