Claude Code skills

Autonomous loops for Claude Code

Three skills that take a spec, a feature request or a bug report to a git branch. Every commit passed the project's own checks, every gate has a model review, and a run is marked done only when the final review passes. Unattended once started, defaulting to haiku for review and docs and sonnet for planning and code.

How it works

loopctl, a Python script, replays RED, verifies GREEN, runs the project's own lint, type and test commands and measures diff coverage when a coverage report is configured, with no model calls. Models plan, write, review and draft docs.

  • loopctl (no model calls)
  • model spawn
  • Cost units per spawn: haiku 0.2 sonnet 1 opus 5
  1. 1

    plan

    plan (feature, product): spec, acceptance criteria, slices and the project commands. bugfix-loop starts at reproduce.

    sonnet · 1 unit
  2. 2

    red

    The new tests must fail on an assertion, not on an import, compile or signature error (stubs allowed), before the implementation is written.

    loopctl · no model cost
  3. 3

    green

    Any change to a test-path file (tests, fixtures, test helpers) after RED is rejected; suite green, revert-check, commit.

    loopctl · no model cost
  4. 4

    check

    Lint, types, build, diff coverage (≥ 80 %, only when a coverage report is configured), weakened tests and secrets.

    loopctl · no model cost
  5. 5

    review

    One independent reviewer per gate; blocker and major findings must carry file:line and evidence.

    haiku · 0.2 units
  6. 6

    report

    report.md: commits, checks, reviews, open findings and spend.

    loopctl · no model cost

One builder per slice

A sonnet builder writes the tests, then is resumed to implement them. It does not commit; loopctl commits. Any change to a test-path file (tests, fixtures, test helpers) after RED is rejected when loopctl green runs.

One reviewer per gate

A haiku reviewer (sonnet at the final gate of product-loop and feature-loop; bugfix-loop uses haiku) runs the tests and the app. Blocker and major findings must carry file:line and evidence or loopctl rejects the review. A BLOCK means at most two fix rounds, then one escalation of the builder (a fresh sonnet spawn). Still blocked at a slice gate: the findings are recorded in the report and the run continues. Still blocked at the final gate: the run ends partial.

Opus is opt-in

Opus runs only if you set models: {builder: opus} or {reviewer: opus}. A run is done only with a green final check and a PASS from the final review of the current head (sonnet, or haiku in bugfix-loop); otherwise it ends partial.

Three loops, one for each kind of work

Each skill is self-contained and states its cost estimate before it starts. Press play on a terminal to see a run, or scroll and it plays on its own.

Illustrative examples, not recordings of real runs. Estimates from the skills' budget formulas, not measured runs.

product-loop

From a spec to a product, one thin vertical slice at a time, with a walking skeleton first.

InputA product spec (greenfield)
OutputA product on branch loop/<run-id> that passed the project's own checks, with a final reviewer's verdict
  • 23 spawns
  • ~13.4 units
  • haiku
  • sonnet
Estimated cost per run
product-loopsh
$ claude "Run the product-loop on spec/invoicing-mvp.md"
# lc <cmd> = python3 <skill_dir>/scripts/loopctl.py <cmd> --run <run-id>
estimate: 23 spawns, about 13.4 cost units (10 slices)
$ lc init --loop product --slug invoicing --max-spawns 40
planner sonnet hardened spec, 10 slices, commands.json
reviewer haiku plan review: PASS
slice 1 of 10 walking skeleton through every layer
planner resumed (same context, no new spawn): scaffold + skeleton test
$ lc red --slice SL01 --tests tests/skeleton.test.ts
RED tests/skeleton.test.ts 1 failed by assertion: GET /health -> 404
$ lc green --slice SL01 --message "build(skeleton): walking skeleton and test harness" --paths src/server.ts tests/skeleton.test.ts
GREEN 1 passed tests unchanged since RED committed
$ lc check
format / lint / types pass tests 9 passed build pass weakened tests none secrets none
runnable early: npm start -> http://localhost:3000 (skeleton)
reviewer haiku skeleton review
review: PASS slices 2..10 follow the same red/green/check path
docs haiku README, CHANGELOG, docs/spec.md
$ lc check --final
final reviewer sonnet fresh clone, install and use from the README
final review: PASS e2e green, coverage 86%
report.md written 5 of 23 spawns shown (slices 2..10 elided) about 13.4 units branch loop/product-20261016-1702-invoicing
  1. plan
  2. red
  3. green
  4. check
  5. review
  6. report

feature-loop

Adds a feature to your repo in small, reviewed, test-first commits.

InputA feature request + an existing repo
OutputA feature branch with small green commits and a PR description
  • 10 spawns
  • ~6 units
  • haiku
  • sonnet
Estimated cost per run
feature-loopsh
$ claude "Run the feature-loop: add per-workspace rate limits to the public API"
# lc <cmd> = python3 <skill_dir>/scripts/loopctl.py <cmd> --run <run-id>
estimate: 10 spawns, about 6 cost units (3 slices)
$ lc init --loop feature --slug rate-limits
planner sonnet spec, 3 slices, commands.json
reviewer haiku plan review: PASS
slice 1/3 token bucket per workspace
builder sonnet tests for AC-1..AC-3
$ lc red --slice SL01 --tests tests/ratelimit.test.ts
RED tests/ratelimit.test.ts 4 failed by assertion
builder resumed: implementing the minimal code
$ lc green --slice SL01 --message "feat(api): token bucket per workspace" --paths src/ratelimit.ts tests/ratelimit.test.ts
GREEN 4 passed tests unchanged since RED committed feat(api): token bucket
$ lc check
format / lint / types pass tests 112 passed diff coverage 88% (min 80%) weakened tests none secrets none
reviewer haiku slice review
review: PASS slice 1/3 done; slices 2 and 3 follow the same path
$ lc check --final
final reviewer sonnet whole-branch review
final review: PASS checks --final green
docs haiku pr.md: what, why, AC to commit table
report.md + pr.md 6 of 10 spawns shown (slices 2 and 3 elided) about 6 units branch loop/feature-20261016-1702-rate-limits
  1. plan
  2. red
  3. green
  4. check
  5. review
  6. report

bugfix-loop

Reproduce first, fix the root cause, then look for the same defect elsewhere.

InputA bug report + an existing repo
OutputA reproduction test, a root-cause fix with sibling check, a short postmortem
  • 3 spawns
  • ~1.4 units
  • haiku
  • sonnet
Estimated cost per run
bugfix-loopsh
$ claude "Run the bugfix-loop on issue #482: CSV export drops the last row"
# lc <cmd> = python3 <skill_dir>/scripts/loopctl.py <cmd> --run <run-id>
estimate: 3 spawns, 1.4 cost units (cap 8 spawns)
$ lc init --loop bugfix --slug csv-last-row --max-spawns 8
{"run": "bugfix-20261016-1702-csv-last-row", "branch": "loop/bugfix-20261016-1702-csv-last-row", ...}
builder sonnet reproduce: triage + failing test
$ lc red --slice repro --tests tests/export.test.js
RED tests/export.test.js 1 failed by assertion: expected 4 rows, got 3
root cause: loop stops at the last "\n"; a file without one loses its final row
builder resumed (same context, no new spawn): root-cause fix + sibling check
$ lc green --slice repro --message "fix(csv): keep the last row of a file without a trailing newline" --paths src/export.js src/import.js tests/export.test.js
GREEN 1 passed tests unchanged since RED revert-check ok committed
$ lc check --final
format / lint / types pass tests 48 passed diff coverage 93% (min 80%) weakened tests none secrets none
reviewer haiku independent review of the diff
review: PASS 0 blockers, 0 majors (same defect also fixed in src/import.js)
docs haiku short postmortem
report.md written 3 spawns 1.4 units branch loop/bugfix-20261016-1702-csv-last-row
  1. plan
  2. red
  3. green
  4. check
  5. review
  6. report

What you would actually ask for

Four everyday scenarios. Open a card to see the prompt you would type and what comes back.

Build a product from a spec

Prompt
Promptplain
Run the product-loop on spec/invoicing-mvp.md. ship: pr
Outcome
A product on branch loop/<run-id> that passed the project's own checks, with a final reviewer's verdict, pushed as a PR for you to review (ship: pr). If the review still blocks after the fix rounds, the run ends partial with the open findings listed first. A runnable walking skeleton is shown after the first slice.
Cost
Estimate: 23 spawns, about 13.4 units for 10 slices

Add rate limits to an API

Prompt
Promptplain
Run the feature-loop: add per-workspace rate limits to the public API (see docs/rate-limits.md).
Outcome
A feature branch with small green commits and a PR description.
Cost
Estimate: 10 spawns, about 6 units for 3 slices

Fix a CSV export bug

Prompt
Promptplain
Run the bugfix-loop on issue #482: "CSV export drops the last row when the file has no trailing newline".
Outcome
A reproduction test, a root-cause fix with sibling check, a short postmortem.
Cost
Estimate: 3 spawns, 1.4 units

Resume an interrupted run

Prompt
Promptplain
Run the product-loop on spec/invoicing-mvp.md
Outcome
Re-invoke the skill in the same repo after an interruption. State lives in .loop/<run-id>/, so the run continues from its next recorded step instead of starting over.
Cost
Estimate: units for spawns already recorded stay counted. A builder whose context is gone is spawned again and counted again. The conductor session is extra.

What a run costs

Every spawn is weighted by its model. Runs default to haiku and sonnet. Opus runs only if you set models: {builder: opus} or {reviewer: opus}.

Estimated cost per scenario, in cost units. Estimates from the skills' budget formulas, not measured runs
ScenarioSpawnsModelsCost units
Bug fix 3 haikusonnet 1.4
Feature, 3 slices 10 haikusonnet 6
Product, 10 slices 23 haikusonnet 13.4

Cost per spawn

  • haiku0.2
  • sonnet1
  • opus (opt-in only)5

Estimates from the skills' budget formulas, not measured runs. Units are spawns weighted by model (fixed ratios in loopctl, not prices or token counts); bars are scaled against the largest scenario (13.4 units). Fix rounds and escalation add cost. Excludes the conductor session.

Local by default

ship defaults to none and the loops work in a separate worktree. These are instructions to the agents plus the loopctl checks listed below.

No push unless you ask

No branch is pushed to a remote unless you pass ship: pr. Then the loop pushes its branch and opens a PR. The skills instruct the agent never to merge or force-push; loopctl has no push code. Your prompts and code are still sent to the model API, and the repo code and installers the loops run may use the network.

Separate worktree

Code changes happen in a separate git worktree on branch loop/<run-id>. The loop also adds a git-excluded .loop/ directory and a branch and worktree entry to your repository. product-loop runs git init and an initial empty commit if the target directory has none. loopctl green refuses ".", directories, globs, .loop/ and git-ignored paths.

Inputs are untrusted

Specs, bug reports, repo files and tool output are untrusted data. The agent templates tell models to treat them as data and to report embedded instructions as findings. This is a prompt-level mitigation, not a guarantee.

Use a sandbox

The loops execute your repo's code and installers. Run them in a devcontainer or VM.

Know the limits

loopctl catches common shortcuts: test-path files edited after RED, skip markers, removed assertions, changed assertions in existing tests (a reviewer must accept them), code that only works with git-ignored files created or changed after RED, commits made outside loopctl green, stale reviews. The checks are pattern-based and do not stop a malicious agent with shell access, so review the final diff.

Install

Installs into ~/.claude/skills. Add --project to install into ./.claude/skills instead. Needs git and python3.

One-line installsh
curl -fsSL https://loops.21no.de/install.sh | sh
Or by handsh
curl -fsSLO https://loops.21no.de/agent-loops.tar.gz
curl -fsSL https://loops.21no.de/agent-loops.tar.gz.sha256 | shasum -a 256 -c -
tar -xzf agent-loops.tar.gz
mkdir -p ~/.claude/skills
cp -r agent-loops/skills/* ~/.claude/skills/
Then ask Claude Codeplain
Run the product-loop on spec/invoicing-mvp.md. ship: pr
Run the feature-loop: add per-workspace rate limits to the public API (see docs/rate-limits.md).
Run the bugfix-loop on issue #482: "CSV export drops the last row when the file has no trailing newline".