From tdd
Strict t-wada style Red-Green-Refactor TDD for TypeScript + Vitest projects. Use when implementing features, fixing bugs, changing runtime behavior, or refactoring logic — any task that adds or modifies production code. Enforces test-first development with a verified Red, minimal Green, and disciplined Refactor, plus agent-safe Vitest execution rules.
How this skill is triggered — by the user, by Claude, or both
Slash command
/tdd:tddThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Practice strict Test-Driven Development in the style of t-wada / Kent Beck. The goal is **動作するきれいなコード — clean code that works**, grown one small verified step at a time.
Practice strict Test-Driven Development in the style of t-wada / Kent Beck. The goal is 動作するきれいなコード — clean code that works, grown one small verified step at a time.
No production code without a failing test you have seen fail. No exceptions.
if [ -f pnpm-lock.yaml ]; then echo pnpm; elif [ -f yarn.lock ]; then echo yarn; elif [ -f bun.lock ] || [ -f bun.lockb ]; then echo bun; elif [ -f package-lock.json ]; then echo npm; else echo "npm (no lockfile found)"; finode -p "const p = require('./package.json'); const v = { ...p.dependencies, ...p.devDependencies }.vitest; v ? v : 'NOT found in package.json'" 2>/dev/null || echo "package.json not found"found=$(ls vitest.config.* vitest.workspace.* vite.config.* 2>/dev/null); echo "${found:-none found — check package.json or ask the user}"grep -qsE "globals[\"']? *: *true" vitest.config.* vite.config.* 2>/dev/null && echo "enabled — use describe/it/expect/vi without imports" || echo "not detected — import { describe, it, expect, vi } from 'vitest'"node -p "Object.entries(require('./package.json').scripts ?? {}).filter(([k]) => k.includes('test')).map(([k, v]) => k + ': ' + v).join(' | ') || 'none'" 2>/dev/null || echo "package.json not found"If anything above is missing or contradicts the repository, read package.json and the Vitest config yourself before running tests. Never guess commands.
| Task | TDD required? |
|---|---|
| New feature / new behavior | Yes |
| Bug fix | Yes — failing regression test first |
| Refactoring logic | Yes — green tests before, during, after |
| Changing untested legacy code | Yes — characterization tests first (see below) |
| Comments, docs, formatting only | No |
| Pure type declarations with no runtime effect | No — consider type tests (expectTypeOf) |
| Config / CI / dependency bumps | No — but run the full suite afterwards |
If the user explicitly opts out of TDD, note the tradeoff in one sentence and follow their instruction. Otherwise this skill is binding.
vitest (or a test script without run) can block the session forever. Always use vitest run …..skip without a written reason. If a test is wrong, fix it and say why.it('returns 0 for an empty cart') — never after the implementation..only. Search and remove before finishing (CI rejects it via --allowOnly=false).探索 Explore → Plan (test list) → 🔴 Red → 🟢 Green → 🔵 Refactor → next item … → Done
Read the relevant code, types, and existing tests before planning. If an unfamiliar API needs investigation, a throwaway spike is allowed — but spike code must be deleted or rebuilt test-first before integration. Never promote untested spike code to production.
it.todo(…) placeholders in the test file and mirror it in your task tracker.describe('calculateTotal', () => {
it.todo('returns 0 for an empty cart');
it.todo('sums item prices');
it.todo('applies percentage discount');
it.todo('never returns a negative total');
});
expect line, then work backwards to the setup. Expected values are concrete literals — never computed with the logic you are about to implement.npx vitest run src/cart.test.tsnpx vitest run -t "applies percentage discount"Pick the smallest workable strategy:
| Strategy | When | How |
|---|---|---|
| 仮実装 (Fake It) | Unsure how to implement | Return a hard-coded constant; generalize later |
| 三角測量 (Triangulation) | The fake must become real | Add a second test with different values the constant cannot satisfy, then generalize |
| 明白な実装 (Obvious Implementation) | Implementation is trivial and certain | Just write it — if it does not go green immediately, fall back to smaller steps |
Run the affected tests. Gate: the new test passes and nothing else broke. Speed to green beats elegance — sins committed here are repaid in Refactor.
90 appearing in both is the signal to generalize a fake).Then return to the list and pick the next item.
it.todo (tell the user which)npx vitest run.only; every .skip carries a reason commenttsc --noEmit or the project's script); lint passes if configuredit.skip + a reason comment + a list item, and report it. Never delete it.Report each cycle compactly so the user can follow along:
🔴 applies percentage discount — fails as expected (expected 90, received 100)
🟢 pass (7 tests, 112ms) — fake-it with constant
🔵 generalized via triangulation; extracted applyDiscount() — all green
Before changing behavior in code that has no tests: pin the current behavior with characterization tests — assert what the code actually does today, even if it looks wrong, and report oddities to the user instead of silently "fixing" them. Once green, proceed with the normal cycle.
it.each, fixtures, assertion stylevi.* usage: mock boundaries, not your own logicCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub kongyo2/tdd --plugin tdd