Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
alinaqi avatar

Iterative Development

  • 3 installs
  • 706 repo stars
  • Updated July 14, 2026
  • alinaqi/maggy

iterative-development is a Claude Code skill that configures a Stop-hook TDD loop which reruns tests, lint, and typecheck after each response and feeds failures back until they pass.

About

iterative-development configures a test-driven development loop inside Claude Code using the Stop hook. After each response it runs tests, lint, and typecheck, and if any fail it exits with code 2 so the failures are fed back and the model keeps fixing until green. A developer uses it to set up automated TDD iteration without plugins. It ships bash check scripts for both Node.js (npm test) and Python (pytest) with a max-iteration safety cap.

  • Sets up a TDD loop using Claude Code's Stop hook (exit code 2 feeds failures back)
  • Ships tdd-loop-check.sh scripts for both Node (npm test) and Python (pytest) with a 25-iteration cap
  • Adds PreToolUse lint-before-write and SessionStart context hooks

Iterative Development by the numbers

  • 3 all-time installs (skills.sh)
  • Ranked #1,648 of 2,153 Testing & QA skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
At a glance

iterative-development capabilities & compatibility

Capabilities
test automation · tdd loop · lint check · typecheck
Use cases
testing · ci cd
From the docs

What iterative-development says it does

TDD iteration loops using Claude Code Stop hooks - runs tests after each response, feeds failures back automatically
SKILL.md
Claude Code has a **Stop hook** that runs when Claude is about to conclude its response. If the hook script exits with code 2, its stderr is shown to the model and the conversation continues automatic
SKILL.md
MAX_ITERATIONS=25
SKILL.md
npx skills add https://github.com/alinaqi/maggy --skill iterative-development

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs3
repo stars706
Last updatedJuly 14, 2026
Repositoryalinaqi/maggy

What it does

Set up an automated TDD loop in Claude Code that reruns tests, lint, and typecheck after each response and feeds failures back until the suite is green.

Who is it for?

Node.js or Python projects that already have a test suite and want automatic red-green iteration in Claude Code.

Skip if: Projects with no test files, since the loop skips when it finds none.

When should I use this skill?

When setting up or configuring TDD loops via Stop hooks.

What you get

Tests, lint, and typecheck run automatically after each response and failures are fed back so the model keeps fixing until green.

  • scripts/tdd-loop-check.sh (Node and Python variants)
  • .claude/settings.json Stop/PreToolUse/SessionStart hook config

By the numbers

  • 25-iteration safety cap (MAX_ITERATIONS=25)
  • Stop-hook exit code 2 feeds stderr back

Files

SKILL.mdMarkdownGitHub ↗

Iterative Development Skill (Stop Hook TDD Loops)

Concept: Claude Code's Stop hook fires right before Claude finishes a response. Exit code 2 feeds stderr back to the model and continues the conversation. This creates a real TDD loop without any plugins.

---

How It Actually Works

Claude Code has a Stop hook that runs when Claude is about to conclude its response. If the hook script exits with code 2, its stderr is shown to the model and the conversation continues automatically.

┌─────────────────────────────────────────────────────────────┐
│  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  │
└─────────────────────────────────────────────────────────────┘

Key insight: No fake plugins, no /ralph-loop command. The hook is real Claude Code infrastructure that runs automatically.

---

Setup: Stop Hook Configuration

Add this to your project's .claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "scripts/tdd-loop-check.sh",
            "timeout": 60,
            "statusMessage": "Running tests..."
          }
        ]
      }
    ]
  }
}

The TDD Loop Check Script

Create scripts/tdd-loop-check.sh in your project:

#!/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 Variant

#!/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

---

Additional Hooks for Quality Enforcement

PreToolUse Hook: Lint Before File Writes

Runs a linter before any Write/Edit lands:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "scripts/pre-write-lint.sh",
            "timeout": 10,
            "statusMessage": "Checking code quality..."
          }
        ]
      }
    ]
  }
}

SessionStart Hook: Auto-Inject Context

Runs at session start to inject project info:

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo 'TDD loop active. Tests run automatically after each response. Fix failures to continue.'",
            "statusMessage": "Loading project context..."
          }
        ]
      }
    ]
  }
}

---

Core Philosophy

┌─────────────────────────────────────────────────────────────┐
│  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.                           │
└─────────────────────────────────────────────────────────────┘

---

Error Classification

Not all failures should loop. The hook script should distinguish:

TypeExamplesAction
Code ErrorLogic bug, wrong assertion, type mismatchExit 2 → loop continues
Access ErrorMissing API key, DB connection refusedExit 0 → stop, report to user
Environment ErrorMissing package, wrong runtime versionExit 0 → stop, report to user

The sample scripts above handle this — they only exit 2 for test/lint/type failures, not for environment issues.

---

When to Use TDD Loops

Good For

Use CaseWhy
Feature developmentTests provide clear pass/fail signal
Bug fixesWrite failing test, fix, loop until green
RefactoringExisting tests catch regressions
API developmentEach endpoint independently testable

Not Good For

Use CaseWhy
UI/UX workRequires human judgment
One-shot operationsNo iteration needed
Unclear requirementsNo clear "done" criteria
Subjective designNo objective success metric

---

Disabling the Loop

To temporarily disable the TDD loop for a session:

1. Remove or rename the Stop hook in .claude/settings.json 2. Or set MAX_ITERATIONS=1 in the script 3. Or delete scripts/tdd-loop-check.sh

The hook only fires if the script exists and is configured.

---

Gitignore Additions

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

Related skills

FAQ

Does this need a Claude Code plugin?

No. The skill states it uses Claude Code's real Stop hook, no fake plugins or /ralph-loop command.

What happens if there are no tests yet?

The check script skips and lets Claude stop if it finds no test files matching *.test.*, *.spec.*, test_*, or *_test.py.

Testing & QAtestingdevops

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.