mcpField
I
iterative-development

iterative-development

github.com/alinaqi测试与质量711276 次安装2 周前更新
代码仓库

关于 iterative-development

一项 Claude Code 技能,通过 Stop hook 在每次响应后运行测试和检查,并将代码错误反馈给模型,形成迭代式 TDD 循环。适用于有明确通过或失败标准的开发任务。

主要功能

  1. 01Stop hook 测试与检查循环
  2. 02将失败反馈给 Claude
  3. 03配置迭代次数上限
  4. 04可选的写入前 lint hook
  5. 05会话启动时注入上下文

适用场景

  1. 01通过测试检查实现功能
  2. 02借助失败测试修复错误
  3. 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 循环:

  1. 从 .claude/settings.json 中移除或重命名 Stop 钩子
  2. 或在脚本中设置 MAX_ITERATIONS=1
  3. 或删除 scripts/tdd-loop-check.sh

只有在脚本存在且已配置时,钩子才会触发。


添加到 Gitignore

# TDD loop state
.claude/.tdd-iteration-count

评论

支持 Markdown