/tdd-rust
TDD workflow for RTK filter development. Red-Green-Refactor with Rust idioms. Real fixtures, token savings assertions, snapshot tests with insta. Auto-triggers on new filter implementation.
Claude Code
$ npx -y skills add rtk-ai/rtk --skill tdd-rust --agent claude-code
Codex
$ npx skills add rtk-ai/rtk --skill tdd-rust -a codex -y
Cursor
$ npx skills add rtk-ai/rtk --skill tdd-rust -a cursor -y
Gemini CLI
$ npx skills add rtk-ai/rtk --skill tdd-rust -a gemini-cli -y
GitHub Copilot
$ npx skills add rtk-ai/rtk --skill tdd-rust -a github-copilot -y
OpenCode
$ npx skills add rtk-ai/rtk --skill tdd-rust -a opencode -y
Windsurf
$ npx skills add rtk-ai/rtk --skill tdd-rust -a windsurf -y
SKILL.md
tdd-rust.SKILL.mdname: tdd-rust
description: TDD workflow for RTK filter development. Red-Green-Refactor with Rust idioms. Real fixtures, token savings assertions, snapshot tests with insta. Auto-triggers on new filter implementation.
triggers:
- "new filter"
- "implement filter"
- "add command"
- "write tests for"
- "test coverage"
- "fix failing test"
allowed-tools:
- Read
- Write
- Edit
- Bash
effort: medium
tags: [tdd, testing, rust, filters, snapshots, token-savings, rtk]
RTK TDD Workflow
Enforce Red-Green-Refactor for all RTK filter development.
The Loop
1. RED — Write failing test with real fixture
2. GREEN — Implement minimum code to pass
3. REFACTOR — Clean up, verify still passing
4. SAVINGS — Verify ≥20% token reduction
5. SNAPSHOT — Lock output format with insta
Step 1: Real Fixture First
Never write synthetic test data. Capture real command output:
# Capture real output from the actual command
git log -20 > tests/fixtures/git_log_raw.txt
cargo test 2>&1 > tests/fixtures/cargo_test_raw.txt
cargo clippy 2>&1 > tests/fixtures/cargo_clippy_raw.txt
gh pr view 42 > tests/fixtures/gh_pr_view_raw.txt
# For commands with ANSI codes — capture as-is
script -q /dev/null cargo test 2>&1 > tests/fixtures/cargo_test_ansi_raw.txt
Fixture naming: `tests/fixtures/<command>_raw.txt`
Step 2: Write the Test (Red)
#[cfg(test)]
mod tests {
use super::*;
use insta::assert_snapshot;
fn count_tokens(s: &str) -> usize {
s.split_whitespace().count()
}
// Test 1: Output format (snapshot)
#[test]
fn test_filter_output_format() {
let input = include_str!("../tests/fixtures/mycmd_raw.txt");
let output = filter_mycmd(input).expect("filter should not fail");
assert_snapshot!(output);
}
// Test 2: Token savings ≥20%
#[test]
fn test_token_savings() {
let input = include_str!("../tests/fixtures/mycmd_raw.txt");
let output = filter_mycmd(input).expect("filter should not fail");
let input_tokens = count_tokens(input);
let output_tokens = count_tokens(&output);
let savings = 100.0 * (1.0 - output_tokens as f64 / input_tokens as f64);
assert!(
savings >= 20.0,
"Expected ≥20% token savings, got {:.1}% ({} → {} tokens)",
savings, input_tokens, output_tokens
);
}
// Test 3: Edge cases
#[test]
fn test_empty_input() {
let result = filter_mycmd("");
assert!(result.is_ok());
// Empty input = empty output OR passthrough, never panic
}
#[test]
fn test_malformed_input() {
let result = filter_mycmd("not valid command output\nrandom text\n");
// Must not panic — either filter best-effort or return input unchanged
assert!(result.is_ok());
}
}Run: `cargo test` → should fail (function doesn't exist yet).
Step 3: Minimum Implementation (Green)
// src/mycmd_cmd.rs
use anyhow::{Context, Result};
use regex::Regex;
use std::sync::LazyLock;
static ERROR_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^error").unwrap());
pub fn filter_mycmd(input: &str) -> Result<String> {
if input.is_empty() {
return Ok(String::new());
}
let filtered: Vec<&str> = input.lines()
.filter(|line| ERROR_RE.is_match(line))
.collect();
Ok(filtered.join("\n"))
}Run: `cargo test` → green.
Step 4: Accept Snapshot
# First run creates the snapshot
cargo test test_filter_output_format
# Review what was captured
cargo insta review
# Press 'a' to accept
# Snapshot saved to src/snapshots/mycmd_cmd__tests__test_filter_output_format.snap
Step 5: Wire to main.rs (Integration)
// src/main.rs
mod mycmd_cmd;
#[derive(Subcommand)]
pub enum Commands {
// ... existing commands ...
Mycmd(MycmdArgs),
}
// In match:
Commands::Mycmd(args) => mycmd_cmd::run(args),// src/mycmd_cmd.rs — add run() function
pub fn run(args: MycmdArgs) -> Result<()> {
let output = execute_command("mycmd", &args.to_vec())
.context("Failed to execute mycmd")?;
let filtered = filter_mycmd(&output.stdout)
.unwrap_or_else(|e| {
eprintln!("rtk: filter warning: {}", e);
output.stdout.clone()
});
tracking::record("mycmd", &output.stdout, &filtered)?;
print!("{}", filtered);
if !output.status.success() {
std::process::exit(output.status.code().unwrap_or(1));
}
Ok(())
}Step 6: Quality Gate
cargo fmt --all && cargo clippy --all-targets && cargo test
All 3 must pass. Zero clippy warnings.
Arrange-Act-Assert Pattern
#[test]
fn test_filters_only_errors() {
// Arrange
let input = "info: starting build\nerror[E0001]: undefined\nwarning: unused\n";
// Act
let output = filter_mycmd(input).expect("should succeed");
// Assert
assert!(output.contains("error[E0001]"), "Should keep error lines");
assert!(!output.contains("info:"), "Should drop info lines");
assert!(!output.contains("warning:"), "Should drop warning lines");
}RTK-Specific Test Patterns
Test ANSI stripping
#[test]
fn test_strips_ansi_codes() {
let input = "\x1b[32mSuccess\x1b[0m\n\x1b[31merror: failed\x1b[0m\n";
let output = filter_mycmd(input).expect("should succeed");
assert!(!output.contains("\x1b["), "ANSI codes should be stripped");
assert!(output.contains("error: failed"), "Content should be preserved");
}Test fallback behavior
#[test]
fn test_filter_handles_unexpected_format() {
// Give it something completely unexpected
let input = "completely unexpected\x00binary\xff data";
// Should not panic — returns Ok() with either empty or passthrough
let result = filter_mycmd(input);
assert!(result.is_ok(), "Filter must not panic on unexpected input");
}Test savings at multiple s
Read more
name: tdd-rust description: TDD workflow for RTK filter development. Red-Green-Refactor with Rust idioms. Real fixtures, token savings assertions, snapshot tests with insta. Auto-triggers on new filter implementation. triggers: - "new filter" - "implement filter" - "add command" - "write tests for" - "test coverage" - "fix failing test" allowed-tools: - Read - Write - Edit - Bash effort: medium tags: [tdd, testing, rust, filters, snapshots, token-savings, rtk]
RTK TDD Workflow
Enforce Red-Green-Refactor for all RTK filter development.
The Loop
1. RED — Write failing test with real fixture 2. GREEN — Implement minimum code to pass 3. REFACTOR — Clean up, verify still passing 4. SAVINGS — Verify ≥20% token reduction 5. SNAPSHOT — Lock output format with insta
Step 1: Real Fixture First
Never write synthetic test data. Capture real command output:
# Capture real output from the actual command git log -20 > tests/fixtures/git_log_raw.txt cargo test 2>&1 > tests/fixtures/cargo_test_raw.txt cargo clippy 2>&1 > tests/fixtures/cargo_clippy_raw.txt gh pr view 42 > tests/fixtures/gh_pr_view_raw.txt # For commands with ANSI codes — capture as-is script -q /dev/null cargo test 2>&1 > tests/fixtures/cargo_test_ansi_raw.txt
Fixture naming: `tests/fixtures/<command>_raw.txt`
Step 2: Write the Test (Red)
#[cfg(test)]
mod tests {
use super::*;
use insta::assert_snapshot;
fn count_tokens(s: &str) -> usize {
s.split_whitespace().count()
}
// Test 1: Output format (snapshot)
#[test]
fn test_filter_output_format() {
let input = include_str!("../tests/fixtures/mycmd_raw.txt");
let output = filter_mycmd(input).expect("filter should not fail");
assert_snapshot!(output);
}
// Test 2: Token savings ≥20%
#[test]
fn test_token_savings() {
let input = include_str!("../tests/fixtures/mycmd_raw.txt");
let output = filter_mycmd(input).expect("filter should not fail");
let input_tokens = count_tokens(input);
let output_tokens = count_tokens(&output);
let savings = 100.0 * (1.0 - output_tokens as f64 / input_tokens as f64);
assert!(
savings >= 20.0,
"Expected ≥20% token savings, got {:.1}% ({} → {} tokens)",
savings, input_tokens, output_tokens
);
}
// Test 3: Edge cases
#[test]
fn test_empty_input() {
let result = filter_mycmd("");
assert!(result.is_ok());
// Empty input = empty output OR passthrough, never panic
}
#[test]
fn test_malformed_input() {
let result = filter_mycmd("not valid command output\nrandom text\n");
// Must not panic — either filter best-effort or return input unchanged
assert!(result.is_ok());
}
}Run: `cargo test` → should fail (function doesn't exist yet).
Step 3: Minimum Implementation (Green)
// src/mycmd_cmd.rs
use anyhow::{Context, Result};
use regex::Regex;
use std::sync::LazyLock;
static ERROR_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^error").unwrap());
pub fn filter_mycmd(input: &str) -> Result<String> {
if input.is_empty() {
return Ok(String::new());
}
let filtered: Vec<&str> = input.lines()
.filter(|line| ERROR_RE.is_match(line))
.collect();
Ok(filtered.join("\n"))
}Run: `cargo test` → green.
Step 4: Accept Snapshot
# First run creates the snapshot cargo test test_filter_output_format # Review what was captured cargo insta review # Press 'a' to accept # Snapshot saved to src/snapshots/mycmd_cmd__tests__test_filter_output_format.snap
Step 5: Wire to main.rs (Integration)
// src/main.rs
mod mycmd_cmd;
#[derive(Subcommand)]
pub enum Commands {
// ... existing commands ...
Mycmd(MycmdArgs),
}
// In match:
Commands::Mycmd(args) => mycmd_cmd::run(args),// src/mycmd_cmd.rs — add run() function
pub fn run(args: MycmdArgs) -> Result<()> {
let output = execute_command("mycmd", &args.to_vec())
.context("Failed to execute mycmd")?;
let filtered = filter_mycmd(&output.stdout)
.unwrap_or_else(|e| {
eprintln!("rtk: filter warning: {}", e);
output.stdout.clone()
});
tracking::record("mycmd", &output.stdout, &filtered)?;
print!("{}", filtered);
if !output.status.success() {
std::process::exit(output.status.code().unwrap_or(1));
}
Ok(())
}Step 6: Quality Gate
cargo fmt --all && cargo clippy --all-targets && cargo test
All 3 must pass. Zero clippy warnings.
Arrange-Act-Assert Pattern
#[test]
fn test_filters_only_errors() {
// Arrange
let input = "info: starting build\nerror[E0001]: undefined\nwarning: unused\n";
// Act
let output = filter_mycmd(input).expect("should succeed");
// Assert
assert!(output.contains("error[E0001]"), "Should keep error lines");
assert!(!output.contains("info:"), "Should drop info lines");
assert!(!output.contains("warning:"), "Should drop warning lines");
}RTK-Specific Test Patterns
Test ANSI stripping
#[test]
fn test_strips_ansi_codes() {
let input = "\x1b[32mSuccess\x1b[0m\n\x1b[31merror: failed\x1b[0m\n";
let output = filter_mycmd(input).expect("should succeed");
assert!(!output.contains("\x1b["), "ANSI codes should be stripped");
assert!(output.contains("error: failed"), "Content should be preserved");
}Test fallback behavior
#[test]
fn test_filter_handles_unexpected_format() {
// Give it something completely unexpected
let input = "completely unexpected\x00binary\xff data";
// Should not panic — returns Ok() with either empty or passthrough
let result = filter_mycmd(input);
assert!(result.is_ok(), "Filter must not panic on unexpected input");
}Test savings at multiple s
CLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies
Other skills on rtk.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about tdd-rust.
- Which coding agents can use tdd-rust?
- tdd-rust installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

