This skill should be used when implementing features with TDD, writing tests first, or refactoring with test coverage. Applies disciplined Red-Green-Refactor cycles with TypeScript/Bun and Rust tooling.
Guides developers through disciplined Red-Green-Refactor TDD cycles for TypeScript/Bun and Rust projects.
npx claudepluginhub outfitter-dev/outfitterThis skill inherits all available tools. When active, it can use any tool Claude has access to.
examples/bug-fix.mdexamples/feature-implementation.mdreferences/quality-metrics.mdreferences/test-patterns.mdWrite tests first, implement minimal code to pass, refactor systematically.
<when_to_use>
NOT for: exploratory coding, UI prototypes, static config, trivial glue code
</when_to_use>
<stages>Load the maintain-tasks skill for stage tracking. Advance through RED-GREEN-REFACTOR cycle.
| Stage | Trigger | activeForm |
|---|---|---|
| Red | Session start / cycle restart | "Writing failing test" |
| Green | Test written and failing | "Implementing code" |
| Refactor | Tests passing | "Refactoring code" |
| Verify | Refactor complete | "Verifying implementation" |
Task format:
- Write failing test for { feature }
- Implement { feature } to pass tests
- Refactor { aspect }
- Verify { what's being checked }
Workflow:
in_progresscompleted, add next in_progressEdge cases:
RED --> GREEN --> REFACTOR --> RED --> ...
| | |
Test Impl Improve
Fails Passes Quality
Each cycle: 5-15 min. Longer = step too large, decompose.
Philosophy:
<red_phase>
Write tests defining desired behavior before implementation exists.
Guidelines:
TypeScript:
import { describe, test, expect } from "bun:test";
describe("UserAuthentication", () => {
test("authenticates with valid credentials", async () => {
const result = await authenticate({
email: "user@example.com",
password: "SecurePass123!",
});
expect(result).toMatchObject({
type: "success",
user: expect.objectContaining({ email: "user@example.com" }),
});
});
test("rejects invalid credentials", async () => {
const result = await authenticate({
email: "wrong@example.com",
password: "wrong",
});
expect(result).toMatchObject({
type: "error",
code: "INVALID_CREDENTIALS",
});
});
test.todo("implements rate limiting after failed attempts");
});
Rust:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn authenticates_with_valid_credentials() {
let creds = Credentials { email: "user@example.com".into(), password: "SecurePass123!".into() };
assert!(matches!(authenticate(&creds), Ok(AuthResult::Success { .. })));
}
#[test]
fn rejects_invalid_credentials() {
let creds = Credentials { email: "wrong@example.com".into(), password: "wrong".into() };
assert!(matches!(authenticate(&creds), Err(AuthError::InvalidCredentials)));
}
}
Commit: test: add failing tests for [feature]
Transition: Mark "Red" completed, create "Green" in_progress
</red_phase>
<green_phase>
Implement minimum code to make tests pass.
Guidelines:
TypeScript:
type AuthResult =
| { type: "success"; user: User }
| { type: "error"; code: string };
async function authenticate(creds: {
email: string;
password: string;
}): Promise<AuthResult> {
if (!creds.password) return { type: "error", code: "MISSING_PASSWORD" };
const user = await findUserByEmail(creds.email);
if (!user) return { type: "error", code: "INVALID_CREDENTIALS" };
const match = await comparePassword(creds.password, user.passwordHash);
if (!match) return { type: "error", code: "INVALID_CREDENTIALS" };
return { type: "success", user };
}
Rust:
pub fn authenticate(creds: &Credentials) -> Result<AuthResult, AuthError> {
if creds.password.is_empty() { return Err(AuthError::MissingPassword); }
let user = find_user_by_email(&creds.email).ok_or(AuthError::InvalidCredentials)?;
if !compare_password(&creds.password, &user.password_hash) {
return Err(AuthError::InvalidCredentials);
}
Ok(AuthResult::Success { user })
}
Verify: bun test / cargo test
Commit: feat: implement [feature] to pass tests
Transition: Mark "Green" completed, create "Refactor" in_progress
</green_phase>
<refactor_phase>
Enhance code quality without changing behavior. Tests must continue passing.
Guidelines:
TypeScript:
// Extract validation
function validateCredentials(creds: {
email: string;
password: string;
}): AuthResult | null {
if (!creds.password) return { type: "error", code: "MISSING_PASSWORD" };
if (!isValidEmail(creds.email))
return { type: "error", code: "INVALID_EMAIL" };
return null;
}
// Branded types for safety
type Email = string & { readonly __brand: "Email" };
Rust:
// Extract validation
fn validate_credentials(creds: &Credentials) -> Result<(), AuthError> {
if creds.password.is_empty() { return Err(AuthError::MissingPassword); }
if !is_valid_email(&creds.email) { return Err(AuthError::InvalidEmail); }
Ok(())
}
// Newtype for safety
pub struct Email(String);
Verify: bun test / cargo test
Commit: refactor: [improvement description]
Transition: Mark "Refactor" completed, create "Verify" in_progress
Final: Run full suite. Mark "Verify" completed when all checks pass.
</refactor_phase>
<organization>Follow project conventions, defaulting to:
TypeScript/Bun:
src/{module}/{name}.ts # Implementation
src/{module}/{name}.test.ts # Unit tests colocated
src/{module}/__fixtures__/ # Test data
tests/integration/ # Integration tests
tests/e2e/ # End-to-end tests
Rust:
src/{module}/mod.rs # #[cfg(test)] mod tests { ... }
tests/integration/ # Integration tests
tests/fixtures/ # Test data
</organization>
<quality>
| Metric | Target |
|---|---|
| Line coverage | >=80% (90% critical paths) |
| Mutation score | >=75% |
| Unit test time | <5s |
Test characteristics:
Smells to avoid:
See quality-metrics.md for coverage and mutation testing details.
</quality><bug_fixes>
TDD workflow for bugs:
in_progress)fix: [bug description] with test coverageExample:
// 1. Failing test
test("handles division by zero gracefully", () => {
expect(divide(10, 0)).toMatchObject({
type: "error",
code: "DIVISION_BY_ZERO",
});
});
// 3. Fix
function divide(a: number, b: number): Result {
if (b === 0) return { type: "error", code: "DIVISION_BY_ZERO" };
return { type: "success", value: a / b };
}
</bug_fixes>
<rules>ALWAYS:
maintain-tasks skill)NEVER:
<quick_reference>
# TypeScript/Bun
bun test # Run all tests
bun test --watch # Watch mode
bun test --coverage # Coverage report
bun test --only # Run only .only tests
bun x stryker run # Mutation testing
# Rust
cargo test # Run all tests
cargo test --test NAME # Specific integration test
cargo tarpaulin # Coverage report
cargo mutants # Mutation testing
cargo test -- --nocapture # Show println! output
</quick_reference>
<references>Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.