教程概述
Hooks 是 Planning-with-files 自动化的核心机制。通过 Hooks,可以实现自动重读计划、提醒更新状态、验证任务完成等功能。
你将学到
- ✅ 四种 Hook 的作用时机
- ✅ 各平台 Hooks 配置方法
- ✅ 自定义 Hook 行为
- ✅ Hooks 故障排查
Hooks 机制概述
为什么需要 Hooks?
flowchart LR
subgraph 无 Hooks
A1[开始任务] --> B1[执行工作]
B1 --> C1[忘记更新状态]
C1 --> D1[目标漂移]
D1 --> E1[任务失败]
end
subgraph 有 Hooks
A2[开始任务] --> B2[执行工作]
B2 --> C2[Hook 自动重读计划]
C2 --> D2[Hook 提醒更新状态]
D2 --> E2[任务完成]
E2 --> F2[Hook 验证完成]
end
style E1 fill:#ffcdd2
style E2 fill:#c8e6c9
四种 Hook 类型
| Hook | 触发时机 | 核心作用 |
|---|---|---|
| SessionStart | 会话开始时 | 通知技能就绪、恢复会话 |
| PreToolUse | 工具执行前 | 重读 task_plan.md |
| PostToolUse | 工具执行后 | 提醒更新状态 |
| Stop | 尝试停止时 | 验证任务完成 |
Hooks 工作流程
sequenceDiagram
participant User
participant Claude
participant Hook
participant File as 规划文件
Note over User,File: SessionStart
User->>Claude: 开始会话
Claude->>Hook: SessionStart Hook
Hook->>File: 检查是否存在规划文件
Hook->>User: 通知技能就绪/恢复会话
Note over User,File: 工作循环
User->>Claude: 执行任务
Claude->>Hook: PreToolUse Hook
Hook->>File: 读取 task_plan.md
Hook->>Claude: 刷新目标到上下文
Claude->>Claude: 执行工具
Claude->>Hook: PostToolUse Hook
Hook->>User: 提醒更新状态(如需要)
Note over User,File: Stop
User->>Claude: 停止
Claude->>Hook: Stop Hook
Hook->>File: 检查所有阶段状态
alt 有未完成阶段
Hook->>User: 警告未完成,继续工作
else 全部完成
Hook->>User: 验证通过,可以停止
end
SessionStart Hook
触发时机
- Claude Code 会话启动时
/clear后重新开始时
默认行为
[planning-with-files] Ready.
Auto-activates for complex tasks, or invoke manually with /plan
Session Recovery: 检测到未完成的任务 "博客标签系统"
是否继续之前的任务?
配置示例
Claude Code (~/.claude/settings.json):
{
"hooks": {
"SessionStart": {
"command": "bash ~/.claude/hooks/session-start.sh"
}
}
}
Cursor (.cursor/hooks.json):
{
"hooks": {
"SessionStart": {
"command": "bash .cursor/hooks/session-start.sh"
}
}
}
SessionStart 脚本
#!/bin/bash
# session-start.sh
PLANNING_DIR=".planning"
# 检查是否存在规划文件
if [ -f "task_plan.md" ]; then
echo "[planning-with-files] 检测到未完成任务"
echo "任务: $(grep '^# Task Plan:' task_plan.md | sed 's/^# Task Plan: //')"
# 获取当前阶段
CURRENT_PHASE=$(grep -A1 '\*\*Status:\*\* in_progress' task_plan.md | head -1 | sed 's/^### //')
echo "当前阶段: $CURRENT_PHASE"
echo "输入 '继续' 恢复任务"
fi
# 通知技能就绪
echo "[planning-with-files] Ready. 使用 /plan 开始新任务"
Session Recovery 实现
#!/bin/bash
# session-recovery.sh
# 查找最近的规划文件
LATEST_PLAN=$(find . -name "task_plan.md" -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)
if [ -n "$LATEST_PLAN" ]; then
# 读取任务目标
GOAL=$(grep '^## Goal' "$LATEST_PLAN" -A1 | tail -1)
# 获取进度
COMPLETE=$(grep -c '\*\*Status:\*\* complete' "$LATEST_PLAN")
IN_PROGRESS=$(grep -c '\*\*Status:\*\* in_progress' "$LATEST_PLAN")
PENDING=$(grep -c '\*\*Status:\*\* pending' "$LATEST_PLAN")
echo "=== Session Recovery ==="
echo "任务: $GOAL"
echo "进度: $COMPLETE 完成, $IN_PROGRESS 进行中, $PENDING 待开始"
echo "========================"
fi
PreToolUse Hook
触发时机
在 Write、Edit、Bash 等工具执行之前触发。
核心作用
注意力操控原理:在长会话中,AI 容易"忘记"原始目标。强制重读 task_plan.md 确保目标始终在注意力中。
flowchart TD
A[准备执行工具] --> B{是 Write/Edit/Bash?}
B -->|是| C[PreToolUse Hook 触发]
B -->|否| D[直接执行]
C --> E[读取 task_plan.md]
E --> F[提取 Goal 和当前 Phase]
F --> G[注入到上下文]
G --> H[执行工具]
style C fill:#fff3e0
style E fill:#e1f5ff
配置示例
Claude Code:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit|Bash",
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/hooks/pre-tool-use.sh"
}
]
}
]
}
}
PreToolUse 脚本
#!/bin/bash
# pre-tool-use.sh
PLAN_FILE="task_plan.md"
if [ ! -f "$PLAN_FILE" ]; then
exit 0
fi
# 提取目标
GOAL=$(grep '^## Goal' "$PLAN_FILE" -A1 | tail -1 | sed 's/^//')
# 提取当前阶段
CURRENT_PHASE=$(grep -B1 '\*\*Status:\*\* in_progress' "$PLAN_FILE" | grep '^###' | head -1 | sed 's/^### //')
# 提取关键决策(最近3条)
DECISIONS=$(grep '^|' "$PLAN_FILE" | grep -v '^| Decision' | grep -v '^|---' | head -3)
# 输出提醒
echo "=== 任务上下文刷新 ==="
echo "目标: $GOAL"
echo "当前阶段: $CURRENT_PHASE"
if [ -n "$DECISIONS" ]; then
echo "最近决策:"
echo "$DECISIONS"
fi
echo "===================="
匹配器配置
| 匹配器 | 说明 |
|---|---|
Write |
写入文件时触发 |
Edit |
编辑文件时触发 |
Bash |
执行命令时触发 |
Write|Edit |
写入或编辑时触发 |
.* |
所有工具都触发 |
高级配置
条件触发(只对特定文件触发):
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/hooks/pre-write.sh"
}
]
}
]
}
}
pre-write.sh:
#!/bin/bash
# 只对 src/ 目录下的文件触发
FILE_PATH=$1 # Hook 会传入文件路径
if [[ "$FILE_PATH" == src/* ]]; then
echo "正在修改源代码,请确认符合设计..."
# 读取相关设计
cat findings.md | grep -A5 "API 设计"
fi
PostToolUse Hook
触发时机
在 Write、Edit 等工具执行之后触发。
核心作用
- 提醒更新状态:完成阶段后提醒更新 task_plan.md
- 检查 2-Action 规则:追踪查看操作次数
- 自动记录:自动更新 progress.md
flowchart TD
A[工具执行完成] --> B{PostToolUse Hook}
B --> C{检查操作类型}
C -->|Write/Edit| D[检查是否完成阶段]
D --> E{阶段完成?}
E -->|是| F[提醒更新 Status]
E -->|否| G[继续]
C -->|Read/Grep| H[追踪查看次数]
H --> I{达到 2 次?}
I -->|是| J[提醒更新 findings.md]
I -->|否| G
style F fill:#fff3e0
style J fill:#fff3e0
配置示例
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/post-tool-use.sh"
}
]
}
]
}
}
PostToolUse 脚本
#!/bin/bash
# post-tool-use.sh
PLAN_FILE="task_plan.md"
PROGRESS_FILE="progress.md"
# 检查是否完成阶段
# 通过检查 task_plan.md 中的子任务完成情况
CURRENT_PHASE=$(grep -B5 '\*\*Status:\*\* in_progress' "$PLAN_FILE" | grep '^###' | head -1 | sed 's/^### //')
TASKS_TOTAL=$(grep -A10 "$CURRENT_PHASE" "$PLAN_FILE" | grep -c '^\- \[ \]')
TASKS_DONE=$(grep -A10 "$CURRENT_PHASE" "$PLAN_FILE" | grep -c '^\- \[x\]')
if [ "$TASKS_TOTAL" -eq 0 ] && [ "$TASKS_DONE" -gt 0 ]; then
echo "=== 阶段完成提醒 ==="
echo "阶段 '$CURRENT_PHASE' 的所有任务已完成"
echo "请更新 task_plan.md 状态为 'complete'"
echo "==================="
fi
# 记录到 progress.md
TIMESTAMP=$(date '+%H:%M')
ACTION="执行了文件操作"
echo "| $TIMESTAMP | $ACTION | - |" >> "$PROGRESS_FILE"
Stop Hook
触发时机
当 AI 尝试停止任务时触发。
核心作用
验证优先:只有所有阶段都标记为 complete,任务才算完成。
flowchart TD
A[AI 尝试停止] --> B{Stop Hook}
B --> C[检查 task_plan.md]
C --> D{所有 Status: complete?}
D -->|是| E[允许停止]
D -->|否| F[阻止停止]
F --> G[列出未完成阶段]
G --> H[继续工作]
style F fill:#ffcdd2
style E fill:#c8e6c9
配置示例
{
"hooks": {
"Stop": {
"command": "bash .claude/hooks/check-complete.sh"
}
}
}
Stop Hook 脚本
#!/bin/bash
# check-complete.sh
PLAN_FILE="task_plan.md"
if [ ! -f "$PLAN_FILE" ]; then
echo "未找到 task_plan.md,允许停止"
exit 0
fi
# 检查是否有未完成的阶段
IN_PROGRESS=$(grep -c '\*\*Status:\*\* in_progress' "$PLAN_FILE")
PENDING=$(grep -c '\*\*Status:\*\* pending' "$PLAN_FILE")
if [ "$IN_PROGRESS" -gt 0 ] || [ "$PENDING" -gt 0 ]; then
echo "=== 任务未完成 ==="
echo "进行中阶段: $IN_PROGRESS"
echo "待开始阶段: $PENDING"
# 列出未完成阶段
echo ""
echo "未完成阶段:"
grep -B1 '\*\*Status:\*\* in_progress\|pending' "$PLAN_FILE" | grep '^###'
echo ""
echo "请完成所有阶段后再停止。"
echo "如果确定要停止,请手动更新 task_plan.md 状态。"
echo "================="
# 返回非零阻止停止
exit 1
fi
echo "=== 任务验证通过 ==="
echo "所有阶段已完成,任务可以停止。"
exit 0
强制停止
有时需要强制停止(如紧急情况),可以:
- 跳过 Hook:在配置中临时禁用
- 手动更新状态:将所有阶段标记为 complete
- 删除规划文件:删除 task_plan.md
各平台配置详解
Claude Code
配置文件:~/.claude/settings.json 或 .claude/settings.json
{
"hooks": {
"SessionStart": {
"command": "bash ~/.claude/hooks/session-start.sh"
},
"PreToolUse": [
{
"matcher": "Write|Edit|Bash",
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/hooks/pre-tool-use.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/hooks/post-tool-use.sh"
}
]
}
],
"Stop": {
"command": "bash ~/.claude/hooks/check-complete.sh"
}
}
}
Cursor
配置文件:.cursor/hooks.json
{
"hooks": {
"PreToolUse": {
"command": "bash .cursor/hooks/pre-tool-use.sh"
},
"PostToolUse": {
"command": "bash .cursor/hooks/post-tool-use.sh"
},
"Stop": {
"command": "bash .cursor/hooks/check-complete.sh"
}
}
}
GitHub Copilot
配置文件:.github/hooks/planning-with-files.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit|Bash",
"hooks": [
{
"type": "command",
"command": "bash .github/hooks/scripts/pre-tool-use.sh"
}
]
}
],
"errorOccurred": [
{
"type": "command",
"command": "bash .github/hooks/scripts/on-error.sh"
}
]
}
}
Gemini CLI
配置文件:.gemini/settings.json
{
"hooks": {
"SessionStart": {
"command": "bash .gemini/hooks/session-start.sh"
},
"PreToolUse": {
"command": "bash .gemini/hooks/pre-tool-use.sh"
},
"PostToolUse": {
"command": "bash .gemini/hooks/post-tool-use.sh"
},
"Stop": {
"command": "bash .gemini/hooks/check-complete.sh"
}
}
}
高级配置
条件触发
#!/bin/bash
# 只在特定条件下触发
# 获取当前时间
HOUR=$(date +%H)
# 只在工作时间提醒(9-18点)
if [ "$HOUR" -ge 9 ] && [ "$HOUR" -le 18 ]; then
echo "工作时间提醒:请更新进度"
fi
自定义脚本
集成 Git:
#!/bin/bash
# 在每次写入后检查是否需要提交
CHANGED_FILES=$(git status --porcelain | wc -l)
if [ "$CHANGED_FILES" -gt 5 ]; then
echo "已修改 $CHANGED_FILES 个文件,建议提交一次"
fi
集成测试:
#!/bin/bash
# 在完成阶段后自动运行测试
PHASE=$(grep -B1 '\*\*Status:\*\* in_progress' task_plan.md | grep '^###')
if [[ "$PHASE" == *"后端"* ]]; then
echo "后端阶段完成,运行测试..."
npm test
fi
故障排查
Hook 不触发
检查清单:
- 配置文件格式是否正确
- 脚本是否有执行权限
- 脚本路径是否正确
- 匹配器是否正确
# 检查权限
chmod +x .claude/hooks/*.sh
# 检查语法
bash -n .claude/hooks/pre-tool-use.sh
脚本执行错误
调试方法:
# 添加调试输出
#!/bin/bash
exec 2>&1
set -x
echo "Hook 执行开始"
# ... 脚本内容
echo "Hook 执行结束"
权限问题
# Windows 上可能需要
"command": "powershell -File .claude/hooks/pre-tool-use.ps1"
小结
Hooks 核心要点
- 四种 Hook:SessionStart、PreToolUse、PostToolUse、Stop
- 自动化工作流:重读计划、提醒更新、验证完成
- 平台适配:各平台配置略有不同
- 可定制:通过脚本自定义行为
检查清单
- SessionStart Hook 配置完成
- PreToolUse Hook 能重读 task_plan.md
- PostToolUse Hook 能提醒更新状态
- Stop Hook 能验证任务完成
- 脚本有执行权限
系列导航:
- ← 上一篇:教程 8:实战开发型任务
- → 下一篇:教程 10:与 Superpowers 协同
- 返回:教程系列索引