Claude Code Hooks 完全指南——自动化你的开发工作流

Claude Code Hooks 完全指南——自动化你的开发工作流

Claude Code Hooks 是 Claude Code 提供的生命周期脚本机制,允许你在特定时机自动执行自定义脚本。通过 Hooks,你可以实现代码保存时自动格式化、运行前自动检查、任务完成后自动通知等场景,将重复性工作完全自动化。


目录

  1. 什么是 Hooks?
  2. Hooks 的类型与触发时机
  3. 环境配置与前置要求
  4. 创建你的第一个 Hook
  5. Hook 脚本编写指南
  6. 实战案例
  7. 调试与排查
  8. 安全注意事项
  9. 常见问题

一、什么是 Hooks?

Hooks 是 Claude Code 的生命周期回调系统。你可以为特定事件(如文件保存前、任务完成后)注册自定义脚本,Claude Code 会在对应时机自动调用这些脚本。

Hooks vs Skills vs Commands

概念 触发方式 用途
Hooks 自动触发(事件驱动) 自动化流程、格式化、检查
Skills 自动匹配(自然语言触发) 教会 Claude 怎么做某事
Commands 手动调用(斜杠命令) 用户主动执行的流程
Agents 手动调用(@引用) 专用子代理完成子任务

工作流程示意

1
2
3
4
5
6
用户编辑代码 → 文件保存 → [before_write Hook 触发]
↓ 自动格式化/检查
文件写入磁盘 → [after_write Hook 触发]
↓ 自动编译/lint
任务完成 → [after_task Hook 触发]
↓ 自动总结/通知

二、Hooks 的类型与触发时机

Claude Code 支持以下 7 种 Hook 类型:

Hook 名称 触发时机 典型用途
before_read 读取文件之前 文件解密、权限验证
after_read 读取文件之后 内容过滤、日志记录
before_write 写入文件之前(可修改内容) 格式化代码、注入版权头
after_write 写入文件之后 运行 linter、编译检查
before_command 执行 shell 命令之前 安全检查、环境验证
after_command 执行 shell 命令之后 结果分析、日志记录
after_task 整个任务完成之后 生成报告、发送通知

重要:Hook 的执行顺序

1
2
3
任务开始 → before_read → after_read → ... → before_write → 写入
→ after_write → ... → before_command → 执行命令
→ after_command → ... → after_task → 任务完成

三、环境配置与前置要求

系统要求

  • Claude Code CLI 已安装(npm install -g @anthropic-ai/claude-code
  • Node.js 18+(部分 Hook 需要)
  • Bash/Zsh shell 环境

配置位置

Hooks 配置在 Claude Code 的 settings.local.json 文件中:

  • 项目级<项目根目录>/.claude/settings.local.json
  • 全局级~/.claude/settings.local.json

⚠️ 优先级:项目级配置覆盖全局级配置。settings.local.json 不会被提交到 Git(已添加到 .gitignore),适合存放个人配置。

配置文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"hooks": {
"before_write": {
"command": "node scripts/pre-format.js",
"timeout": 5000,
"blocking": true,
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"]
},
"after_write": {
"command": "./scripts/lint-check.sh",
"timeout": 10000,
"blocking": false
},
"before_command": {
"command": "bash scripts/command-guard.sh",
"timeout": 3000,
"blocking": true,
"exclude_commands": ["git*", "npm*", "pnpm*"]
},
"after_task": {
"command": "node scripts/task-summary.js",
"timeout": 15000,
"blocking": false
}
}
}

配置字段说明

字段 类型 默认值 说明
command string 必填 要执行的命令或脚本路径
timeout number 10000 超时时间(毫秒),超过此时间 Hook 会被终止
blocking boolean true 是否阻塞主流程(false 表示异步执行,不等待结果)
files string[] 全部 文件匹配模式(仅对 before_write/after_write 有效)
exclude_commands string[] 排除的命令匹配模式(仅对 before_command 有效)

四、创建你的第一个 Hook

步骤 1:创建配置目录

1
2
# 在项目根目录下创建 .claude 目录
mkdir -p .claude/hooks

步骤 2:编写 Hook 脚本

创建一个通用的格式化 Hook 脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
cat > .claude/hooks/format-code.sh << 'SCRIPT'
#!/bin/bash
# Claude Code Hook: 在写入文件前自动格式化代码
# 用途:保证所有写入代码的格式一致性

FILE_PATH="$1"
FILE_EXT="${FILE_PATH##*.}"

case "$FILE_EXT" in
js|jsx|ts|tsx)
npx prettier --write "$FILE_PATH" 2>/dev/null || true
;;
py)
python -m black --quiet "$FILE_PATH" 2>/dev/null || true
;;
go)
gofmt -w "$FILE_PATH" 2>/dev/null || true
;;
rs)
rustfmt "$FILE_PATH" 2>/dev/null || true
;;
esac

echo "[Hook] 已格式化: $FILE_PATH"
SCRIPT

chmod +x .claude/hooks/format-code.sh

步骤 3:注册 Hook

创建或编辑 .claude/settings.local.json

1
2
3
4
5
6
7
8
9
10
{
"hooks": {
"before_write": {
"command": "bash .claude/hooks/format-code.sh \"$CLAUDE_HOOK_FILE\"",
"timeout": 5000,
"blocking": true,
"files": ["*.js", "*.ts", "*.tsx", "*.py", "*.go", "*.rs"]
}
}
}

步骤 4:验证 Hook 生效

在 Claude Code 中让 Claude 修改一个代码文件:

1
2
3
# Claude Code 会自动调用 before_write Hook
# 你应该在终端中看到类似输出:
# [Hook] 已格式化: src/app.ts

五、Hook 脚本编写指南

环境变量

Hook 脚本可以访问以下环境变量:

变量名 说明 适用 Hook
CLAUDE_HOOK_FILE 当前操作的文件路径 before_write, after_write, before_read, after_read
CLAUDE_HOOK_TYPE Hook 类型名称 全部
CLAUDE_HOOK_COMMAND 即将执行的命令 before_command, after_command
CLAUDE_PROJECT_ROOT 项目根目录路径 全部

脚本签名规范

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
# 对于 before_write / after_write:
# 第一个参数通常是文件路径,也可以通过 $CLAUDE_HOOK_FILE 获取

set -euo pipefail # 推荐:严格模式

# 日志输出(会显示在 Claude Code 终端中)
echo "[Hook:$CLAUDE_HOOK_TYPE] 开始处理 $CLAUDE_HOOK_FILE"

# 你的逻辑...

echo "[Hook:$CLAUDE_HOOK_TYPE] 处理完成"

返回值约定

  • 退出码 0:Hook 执行成功,主流程继续
  • 退出码非 0(仅在 blocking=true 时):Hook 执行失败,主流程停止并报错
1
2
3
4
5
6
7
# before_write Hook 返回非 0 会阻止文件写入!
# 适用于:检查到安全问题、格式校验失败等场景

if ! npx eslint "$CLAUDE_HOOK_FILE"; then
echo "[Hook] ESLint 检查失败,阻止写入"
exit 1 # ❌ 这会阻止文件保存
fi

超时处理

为所有 Hook 设置合理的超时时间:

Hook 类型 建议超时 原因
before_write 3-10 秒 格式化应该很快
after_write 5-15 秒 Lint/编译可能需要更多时间
before_command 3-5 秒 安全检查应迅速
after_task 10-30 秒 报告生成可能稍慢

六、实战案例

案例 1:Python 项目自动格式化 + 类型检查

.claude/hooks/python-check.sh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash
set -euo pipefail

FILE="$CLAUDE_HOOK_FILE"
if [[ ! "$FILE" =~ \.py$ ]]; then
exit 0
fi

echo "[Hook] Python 格式化检查: $FILE"

# 第一步:自动格式化
black --quiet "$FILE"

# 第二步:排序 import
isort --quiet "$FILE"

# 第三步:类型检查(非阻塞,仅提示)
mypy "$FILE" --show-error-codes 2>&1 | head -5 || true

echo "[Hook] Python 文件处理完成"

注册配置:

1
2
3
4
5
6
7
8
9
10
{
"hooks": {
"before_write": {
"command": "bash .claude/hooks/python-check.sh",
"timeout": 10000,
"blocking": true,
"files": ["*.py"]
}
}
}

案例 2:安全命令守卫

.claude/hooks/command-guard.sh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash
set -euo pipefail

COMMAND="$CLAUDE_HOOK_COMMAND"

# 危险命令列表
DANGEROUS_COMMANDS=(
"rm -rf /"
"sudo rm"
"dd if=/dev/zero"
"chmod -R 777"
">.*/dev/"
"DROP DATABASE"
"TRUNCATE TABLE"
"git push --force"
)

echo "[安全卫士] 检查命令: ${COMMAND:0:100}..."

for pattern in "${DANGEROUS_COMMANDS[@]}"; do
if echo "$COMMAND" | grep -iqE "$pattern"; then
echo "[安全卫士] ⚠️ 检测到危险命令: $pattern"
echo "[安全卫士] 操作已阻止!如需执行,请在 Claude Code 中手动确认。"
exit 1
fi
done

echo "[安全卫士] 命令安全检查通过 ✓"

案例 3:任务完成自动生成摘要

.claude/hooks/task-summary.sh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash
set -euo pipefail

SUMMARY_FILE="$CLAUDE_PROJECT_ROOT/.claude/task-summary.md"
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")

{
echo "## 任务完成报告 — $TIMESTAMP"
echo ""
echo "**项目**: $CLAUDE_PROJECT_ROOT"
echo "**Hook 类型**: $CLAUDE_HOOK_TYPE"
echo ""
echo "### 修改的文件"
git diff --name-only 2>/dev/null || echo "(未检测到 Git 仓库)"
echo ""
echo "---"
echo ""
} >> "$SUMMARY_FILE"

echo "[Hook] 任务摘要已追加到 $SUMMARY_FILE"

案例 4:TypeScript/React 项目全流程自动化

完整的 .claude/settings.local.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"hooks": {
"before_write": {
"command": "bash .claude/hooks/pre-write.sh",
"timeout": 8000,
"blocking": true,
"files": ["*.ts", "*.tsx", "*.js", "*.jsx", "*.css", "*.json"]
},
"after_write": {
"command": "bash .claude/hooks/post-write.sh",
"timeout": 15000,
"blocking": false,
"files": ["*.ts", "*.tsx"]
},
"before_command": {
"command": "bash .claude/hooks/command-guard.sh",
"timeout": 3000,
"blocking": true,
"exclude_commands": ["npm*", "pnpm*", "yarn*", "git status*", "ls*", "cat*"]
},
"after_task": {
"command": "bash .claude/hooks/task-summary.sh",
"timeout": 5000,
"blocking": false
}
}
}

七、调试与排查

检查 Hook 是否生效

1
2
3
4
5
# 查看当前 Claude Code 配置
claude config list

# 查看 settings.local.json 内容
cat .claude/settings.local.json

查看 Hook 执行日志

Claude Code 会在终端输出 Hook 的执行信息。如果 Hook 出错,你会看到类似:

1
2
[Hook] before_write: ❌ 退出码 1
[Hook] 错误: ESLint 检查失败

常用调试技巧

  1. 先跑脚本本身:在终端直接运行 Hook 脚本,确保它独立工作正常
1
2
# 模拟 Hook 环境变量
CLAUDE_HOOK_FILE="src/app.ts" bash .claude/hooks/format-code.sh
  1. 逐步减少复杂性:先用一个简单的 echo "Hello" 脚本确认 Hook 机制工作,再替换为真实逻辑

  2. 检查路径:Hook 脚本的工作目录是项目根目录,不是脚本所在目录。使用绝对路径或 $CLAUDE_PROJECT_ROOT

  3. 日志重定向:将日志写入文件而非仅输出到终端

1
2
3
# 在 Hook 脚本中
exec >> "$CLAUDE_PROJECT_ROOT/.claude/hooks.log" 2>&1
echo "[$(date)] Hook 开始: $CLAUDE_HOOK_TYPE"

常见错误与解决方案

错误现象 可能原因 解决方案
Hook 不执行 配置文件语法错误 检查 JSON 格式,用 jsonlint 验证
Hook 超时 脚本执行时间过长 增大 timeout 值或优化脚本
文件写入被阻止 blocking=true 的 Hook 返回非 0 检查 Hook 脚本逻辑
环境变量为空 使用方式不正确 使用 $CLAUDE_HOOK_FILE 而非参数传递
脚本路径错误 相对路径问题 使用脚本所在目录的完整路径

八、安全注意事项

正确做法

  • ✅ 使用 files 字段限制 Hook 只作用于特定文件类型
  • ✅ 使用 exclude_commands 跳过已知安全的命令
  • ✅ 在开发和测试阶段使用 blocking: false
  • ✅ 为脚本设置合理的 timeout 值
  • ✅ 定期审查 Hook 脚本的内容

错误做法

  • ❌ 在 Hook 中执行 rm -rf 等破坏性操作
  • ❌ Hook 脚本读取敏感信息(密码、密钥)
  • ❌ 设置过长(> 60 秒)的 blocking 超时
  • ❌ 跳过错误处理(不使用 set -e
  • ❌ 在生产环境中调试未测试的 Hook

九、常见问题

Q1:Hook 脚本可以访问网络吗?

可以。Hook 脚本可以执行任何 shell 命令,包括网络请求。但建议将网络操作放在 blocking: false 的 Hook 中,避免阻塞主流程。

Q2:多个 Hook 可以同时注册吗?

是的。可以为同一个事件注册多个 Hook,但每个事件只能有一个 command 配置。如果需要执行多个操作,请在脚本内部串联:

1
2
3
4
5
#!/bin/bash
# 在单个 Hook 脚本中执行多个操作
npm run format
npm run lint
npm run test:quick

Q3:如何在不同项目间共享 Hook?

你可以将 Hook 脚本放在 Git 仓库中共享,或者创建一个模板项目:

1
2
3
4
5
6
# 创建 Hook 模板
mkdir -p ~/.claude-templates/hooks/
cp my-hooks/* ~/.claude-templates/hooks/

# 在新项目中引用
ln -s ~/.claude-templates/hooks/ .claude/hooks/

Q4:before_write Hook 修改了文件内容会怎样?

Claude Code 会将你修改后的内容写入磁盘。这意味着 Hook 可以修改即将写入的内容——这是格式化和代码注入的基础机制。

Q5:生产环境 vs 开发环境需要不同的 Hook 配置?

推荐使用 Git 分支或环境变量区分:

1
2
3
4
5
6
7
8
9
10
{
"hooks": {
"before_write": {
"command": "bash .claude/hooks/pre-write.sh --env ${NODE_ENV:-development}",
"timeout": 10000,
"blocking": true,
"files": ["*.ts", "*.tsx"]
}
}
}

然后在脚本内根据 $NODE_ENV 执行不同的逻辑。

Q6:Hook 执行失败会影响我正在编辑的代码吗?

  • blocking: true:如果 Hook 返回非 0 退出码,会阻止操作进行(如阻止文件写入)。你的代码不会被修改。
  • blocking: false:Hook 异步执行,即使失败也不影响主流程。

总结:Hooks 是 Claude Code 中极具威力的自动化工具。通过合理配置 before_write 实现格式化自动化、before_command 提供安全防护、after_task 生成报告总结,你可以大幅减少重复性工作,将精力集中在真正需要创造力的编码任务上。