Skip to content
Development
Skill

/tdd-loop

Drive the red-green-refactor loop for any non-trivial code change. Auto-detect the project's test runner (pytest / vitest / jest / node --test), invoke it via the `tdd.py` helper, and act on the normalized exit code. Use when you write a test, TDD this feature, let me test-drive

From plugin
jig
620 skills3 agents5 hooks
Install
$ npx -y skills add ramboz/jig --skill tdd-loop --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/tdd-loop

Context preview

The summary Claude sees to decide when to auto-load this skill.

Drive the red-green-refactor loop for any non-trivial code change. Auto-detect the project's test runner (pytest / vitest / jest / node --test), invoke it via the `tdd.py` helper, and act on the normalized exit code. Use when you write a test, TDD this feature, let me test-drive

SKILL.md

tdd-loop.SKILL.md
name: tdd-loop
description: >
  Drive the red-green-refactor loop for any non-trivial code change. Auto-detect
  the project's test runner (pytest / vitest / jest / node --test), invoke it via the
  `tdd.py` helper, and act on the normalized exit code. Use when you write a
  test, TDD this feature, let me test-drive a behaviour, are about to
  implement [feature], want to run my tests, ask is my coverage complete, or
  ask are tests green. Do not use for one-off exploratory scripts, throwaway
  spikes, or pure-documentation edits that touch no executable code.
user-invocable: true

> Spec 006 promoted this skill from a dangling auto-install reference to an > active skill. The deterministic runner detection + subprocess invocation > live in `tdd.py`; this SKILL.md drives the judgment layer.

What this skill does

Codifies the red-green-refactor loop that the `implementer` subagent (`agents/implementer.md`) already encodes as "non-negotiable TDD discipline." The skill:

  • Detects the project's test runner from filesystem signals (one of `pytest`,

`vitest`, `jest`, or Node's built-in `node --test`).

  • Invokes the runner as a subprocess against a target, a focused

`--test-path`, or a single `--test` selector.

  • Streams the runner's stdout/stderr through to the caller — you see real

output, not a swallowed summary.

  • Normalizes the runner's native exit code so callers can branch

deterministically:

  • `0` — all green
  • `1` — at least one red test (runner started but reported failure)
  • `2` — could not detect a runner, OR the runner binary is missing

The red-green-refactor loop

1. **Red.** Write a failing test for one acceptance criterion (or one well-bounded behaviour). Run `tdd.py run <target>` and confirm the new test is the *only* new failure — exit 1. 2. **Green.** Write the minimum implementation that makes the test pass. Run `tdd.py run <target>` again; expect exit 0. 3. **Refactor.** Clean up only after green. Re-run after each meaningful refactor; exit 0 must hold.

Stay in this loop one AC at a time. Do not write three tests and then three implementations — the loop's value is the tight feedback cycle.

When a test you did NOT just write goes red

The Green step above is about the *new* failing test you wrote for this AC — making it pass is the work. A **pre-existing** test going red is the opposite signal: it is a guard reporting that your change touched something load-bearing. The reflex to "make it green" is wrong here, and an agent is very good at satisfying it — deleting the assertion, loosening the expectation, or marking the test skipped all turn the light green while removing the protection.

So: **when a test you did not just write fails, do not fix the test — first suspect that your change broke what the test guards.** Only after you have ruled that out (the test encodes an intent that has genuinely, deliberately changed) may you touch the test, and then update its assertion *and its rationale* together so the next reader sees why. Never weaken, skip, `xfail`, or quarantine a pre-existing test to reach green. (This mirrors the standing rule for driving a PR to green — a red guard is work now, not an obstacle to route around.)

Helper invocations

Two subcommands cover the loop: `tdd.py detect` figures out which runner the project uses, and `tdd.py run` invokes it.

Detect the runner

python3 "${CLAUDE_PLUGIN_ROOT}/skills/tdd-loop/tdd.py" detect [target]
  • `target` defaults to `.` when omitted.
  • Stdout: `pytest`, `vitest`, `jest`, or `node` (one line).
  • Exit 2 with stderr `no test runner detected at <target>` if no signal matches.

Signals checked (priority order — first hit wins):

  • **pytest** — `pytest.ini` OR `conftest.py` OR `[tool.pytest` in

`pyproject.toml` OR a `test_*.py` / `*_test.py` file at root or in any direct subdirectory.

  • **vitest** — `vitest.config.{ts,js,mjs}` OR `vitest` in `package.json`'s

`dependencies` / `devDependencies`.

  • **jest** — `jest.config.{ts,js,json}` OR `jest` in `package.json`'s

`dependencies` / `devDependencies`.

  • **node** — `package.json` `scripts.test` invokes `node --test`, OR a shallow

JS/TS file imports from `node:test`.

When multiple runners are detected, priority is **pytest > vitest > jest > node**.

Run the suite

python3 "${CLAUDE_PLUGIN_ROOT}/skills/tdd-loop/tdd.py" run [target] [--test-path PATH]
python3 "${CLAUDE_PLUGIN_ROOT}/skills/tdd-loop/tdd.py" run [target] [--test SELECTOR]
  • Auto-detects the runner via the same logic as `detect`.
  • Default commands:
  • pytest → `python3 -m pytest <path>`
  • vitest → `npx vitest run <path>` (note the `run` — keeps watch mode off)
  • jest → `npx jest <path>`
  • node → `node --test` for the full suite; `node --test <path>` with

`--test-path`

  • Output streams through to the caller's terminal.
  • Exit code is normalized (0 / 1 / 2) per the table above.

Use `--test-path` when you want to run a focused subset (e.g. a single test file) while still letting the helper detect which runner to invoke. Use `--test` when you want one named test: pytest selectors are passed as node ids (`path::test_name`), vitest/jest selectors map to file plus `-t`, and node selectors map to file plus `--test-name-pattern` when shaped as `path::test name`.

When NOT to use

  • **One-off scripts** or **REPL exploration** — TDD overhead isn't worth it

for code that won't survive the session.

  • **Exploratory spikes.** Spikes are research artifacts; they answer "what

shape should this take?" — tests come *after* the spike has settled the shape.

  • **Pure-documentation edits** that touch no executable code.
  • **Auto-generated code** (codegen output, vendored deps). Test the

generator, not the generated artifact.

Sibling helper: `quality.py` (test-quality snapshot)

Alongside `tdd.py` the skill ships `quality.py`, a deterministic test-quality preflight. It reads a unified diff (`--diff-file PATH` or `--again

Read more
Ships withjig

A Claude Code and Codex plugin that scaffolds AI-native development practices into new projects. jig adds a repeatable spec, implementation, review, and memory workflow to AI-assisted software projects.

Get the whole plugin
Stats
6
Stars
0
Forks
Active
Maintenance
Python
Language
MIT
License
9d ago
Last commit
4mo ago
Created

Repo: ramboz/jig

Other skills on jig.

adr-workflow
Skill

adr-workflow

Scaffold, accept, index, and link Architectural Decision Records (ADRs). Use when the user says "write an ADR", "record this decision", "resolve [deferred…

@ramboz@rambozView Skill
analyze
Skill

analyze

Cross-artifact consistency report for jig specs — a non-destructive six-category audit at CRITICAL/HIGH/MEDIUM/LOW severity, covering duplication, ambiguity,…

@ramboz@rambozView Skill
arch-review
Skill

arch-review

Team baseline for architecture, design-doc, and RFC review — produces summary, strengths, concerns, and open questions. Auto-triggers when you say review this…

@ramboz@rambozView Skill
bug-fix
Skill

bug-fix

Drive the teeth-gated lifecycle for reported defects: diagnose root cause, prove it, and prevent regression through REPORTED → DIAGNOSING → ROOT_CAUSED →…

@ramboz@rambozView Skill
clarify
Skill

clarify

Lightweight spec clarification scan for jig projects — a six-category ambiguity audit that asks up to five prioritized questions and appends them to the spec's…

@ramboz@rambozView Skill
code-health
Skill

code-health

Run a static-analysis pass on a project — detect the ecosystem (Python or Node), drive its linter (ruff / eslint, plus advisory pyright/complexity/ prettier…

@ramboz@rambozView Skill