关于 iterative-development
一项 Claude Code 技能,通过 Stop hook 在每次响应后运行测试和检查,并将代码错误反馈给模型,形成迭代式 TDD 循环。适用于有明确通过或失败标准的开发任务。
主要功能
- 01Stop hook 测试与检查循环
- 02将失败反馈给 Claude
- 03配置迭代次数上限
- 04可选的写入前 lint hook
- 05会话启动时注入上下文
适用场景
- 01通过测试检查实现功能
- 02借助失败测试修复错误
- 03通过回归检查进行重构
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ alinaqi/ maggy --skill iterative-development安装后在 Claude Code 中输入/iterative-development确认已加载。
详细介绍
迭代开发技能(停止钩子 TDD 循环)
概念: Claude Code 的 Stop 钩子会在 Claude 完成响应前触发。退出代码 2 会将 stderr 返回给模型并继续对话。无需任何插件,即可实现真正的 TDD 循环。
实际工作原理
Claude Code 有一个 Stop 钩子,会在 Claude 即将结束响应时运行。如果钩子脚本以代码 2 退出,其 stderr 会显示给模型,对话也会自动继续。
┌─────────────────────────────────────────────────────────────┐
│ 1. User asks Claude to implement a feature │
├─────────────────────────────────────────────────────────────┤
│ 2. Claude writes tests + implementation │
├─────────────────────────────────────────────────────────────┤
│ 3. Claude finishes its response │
├─────────────────────────────────────────────────────────────┤
│ 4. Stop hook runs: executes tests, lint, typecheck │
├─────────────────────────────────────────────────────────────┤
│ 5a. All pass (exit 0) → Claude stops, work is done │
│ 5b. Failures (exit 2) → stderr fed back to Claude │
├─────────────────────────────────────────────────────────────┤
│ 6. Claude sees failures, fixes code, response ends │
├─────────────────────────────────────────────────────────────┤
│ 7. Stop hook runs again → repeat until green or max tries │
└─────────────────────────────────────────────────────────────┘
关键要点: 没有伪造的插件,也没有 /ralph-loop 命令。该钩子是真正的 Claude Code 基础设施,会自动运行。
设置:Stop 钩子配置
将以下内容添加到项目的 .claude/settings.json:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "scripts/tdd-loop-check.sh",
"timeout": 60,
"statusMessage": "Running tests..."
}
]
}
]
}
}
TDD 循环检查脚本
在项目中创建 scripts/tdd-loop-check.sh:
#!/bin/bash
# TDD Loop Check - runs after each Claude response
# Exit 0 = all good, Claude stops
# Exit 2 = failures, stderr fed back to Claude to fix
MAX_ITERATIONS=25
ITERATION_FILE=".claude/.tdd-iteration-count"
# Track iteration count
if [ -f "$ITERATION_FILE" ]; then
count=$(cat "$ITERATION_FILE")
count=$((count + 1))
else
count=1
fi
echo "$count" > "$ITERATION_FILE"
# Safety: stop after max iterations
if [ "$count" -ge "$MAX_ITERATIONS" ]; then
rm -f "$ITERATION_FILE"
echo "Max iterations ($MAX_ITERATIONS) reached. Stopping loop." >&2
exit 0
fi
# Skip if no test files exist yet
if ! find . -name "*.test.*" -o -name "*.spec.*" -o -name "test_*" 2>/dev/null | grep -q .; then
rm -f "$ITERATION_FILE"
exit 0
fi
# Run tests
TEST_OUTPUT=$(npm test 2>&1) || {
echo "ITERATION $count/$MAX_ITERATIONS - Tests failing:" >&2
echo "$TEST_OUTPUT" | tail -30 >&2
echo "" >&2
echo "Fix the failing tests and try again." >&2
exit 2
}
# Run lint (if configured)
if [ -f "package.json" ] && grep -q '"lint"' package.json; then
LINT_OUTPUT=$(npm run lint 2>&1) || {
echo "ITERATION $count/$MAX_ITERATIONS - Lint errors:" >&2
echo "$LINT_OUTPUT" | tail -20 >&2
echo "" >&2
echo "Fix lint errors and try again." >&2
exit 2
}
fi
# Run typecheck (if configured)
if [ -f "tsconfig.json" ]; then
TYPE_OUTPUT=$(npx tsc --noEmit 2>&1) || {
echo "ITERATION $count/$MAX_ITERATIONS - Type errors:" >&2
echo "$TYPE_OUTPUT" | tail -20 >&2
echo "" >&2
echo "Fix type errors and try again." >&2
exit 2
}
fi
# All green - reset counter and let Claude stop
rm -f "$ITERATION_FILE"
exit 0
Python 版本
#!/bin/bash
# Python TDD Loop Check
MAX_ITERATIONS=25
ITERATION_FILE=".claude/.tdd-iteration-count"
if [ -f "$ITERATION_FILE" ]; then
count=$(cat "$ITERATION_FILE")
count=$((count + 1))
else
count=1
fi
echo "$count" > "$ITERATION_FILE"
if [ "$count" -ge "$MAX_ITERATIONS" ]; then
rm -f "$ITERATION_FILE"
echo "Max iterations ($MAX_ITERATIONS) reached." >&2
exit 0
fi
if ! find . -name "test_*" -o -name "*_test.py" 2>/dev/null | grep -q .; then
rm -f "$ITERATION_FILE"
exit 0
fi
TEST_OUTPUT=$(pytest -v 2>&1) || {
echo "ITERATION $count/$MAX_ITERATIONS - Tests failing:" >&2
echo "$TEST_OUTPUT" | tail -30 >&2
exit 2
}
if command -v ruff &>/dev/null; then
LINT_OUTPUT=$(ruff check . 2>&1) || {
echo "ITERATION $count/$MAX_ITERATIONS - Lint errors:" >&2
echo "$LINT_OUTPUT" | tail -20 >&2
exit 2
}
fi
if command -v mypy &>/dev/null; then
TYPE_OUTPUT=$(mypy . 2>&1) || {
echo "ITERATION $count/$MAX_ITERATIONS - Type errors:" >&2
echo "$TYPE_OUTPUT" | tail -20 >&2
exit 2
}
fi
rm -f "$ITERATION_FILE"
exit 0
用于质量保证的其他钩子
PreToolUse 钩子:写入文件前运行 Lint
在任何 Write/Edit 操作生效前运行 Linter:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "scripts/pre-write-lint.sh",
"timeout": 10,
"statusMessage": "Checking code quality..."
}
]
}
]
}
}
SessionStart 钩子:自动注入上下文
在会话开始时运行,以注入项目信息:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo 'TDD loop active. Tests run automatically after each response. Fix failures to continue.'",
"statusMessage": "Loading project context..."
}
]
}
]
}
}
核心理念
┌─────────────────────────────────────────────────────────────┐
│ ITERATION > PERFECTION │
│ ───────────────────────────────────────────────────────── │
│ Don't aim for perfect on first try. │
│ Let the loop refine the work. Each iteration builds on │
│ previous attempts visible in files and git history. │
├─────────────────────────────────────────────────────────────┤
│ FAILURES ARE DATA │
│ ───────────────────────────────────────────────────────── │
│ Failed tests, lint errors, type mismatches are signals. │
│ The Stop hook feeds them directly to Claude as context. │
├─────────────────────────────────────────────────────────────┤
│ CLEAR COMPLETION CRITERIA │
│ ───────────────────────────────────────────────────────── │
│ The hook defines "done": tests pass, lint clean, types ok. │
│ No ambiguity about when to stop. │
└─────────────────────────────────────────────────────────────┘
错误分类
并非所有失败都应该循环处理。钩子脚本应区分:
| 类型 | 示例 | 操作 |
|---|---|---|
| 代码错误 | 逻辑错误、断言错误、类型不匹配 | 退出 2 → 继续循环 |
| 访问错误 | 缺少 API 密钥、数据库连接被拒绝 | 退出 0 → 停止并向用户报告 |
| 环境错误 | 缺少软件包、运行时版本错误 | 退出 0 → 停止并向用户报告 |
上面的示例脚本已处理此问题——它们仅在测试、Lint 或类型检查失败时退出 2,环境问题不会触发该操作。
何时使用 TDD 循环
适用场景
| 用例 | 原因 |
|---|---|
| 功能开发 | 测试能提供明确的通过/失败信号 |
| Bug 修复 | 编写失败的测试、修复,然后循环直到通过 |
| 重构 | 现有测试能捕获回归问题 |
| API 开发 | 每个端点都可独立测试 |
不适用场景
| 用例 | 原因 |
|---|---|
| UI/UX 工作 | 需要人工判断 |
| 一次性操作 | 无需迭代 |
| 需求不明确 | 没有明确的“完成”标准 |
| 主观设计 | 没有客观的成功指标 |
禁用循环
若要在某个会话中临时禁用 TDD 循环:
- 从
.claude/settings.json中移除或重命名 Stop 钩子 - 或在脚本中设置
MAX_ITERATIONS=1 - 或删除
scripts/tdd-loop-check.sh
只有在脚本存在且已配置时,钩子才会触发。
添加到 Gitignore
# TDD loop state
.claude/.tdd-iteration-count
评论
支持 Markdown