Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
rtk-ai avatar

Design Patterns

  • 978 installs
  • 74.8k repo stars
  • Updated August 3, 2026
  • rtk-ai/rtk

design-patterns is a Rust agent skill that applies RTK CLI filter patterns—Newtype, Builder, RAII, Trait Objects, and State Machine—so developers ship type-safe modules instead of generic boilerplate.

About

design-patterns is a Rust-focused agent skill from rtk-ai/rtk that encodes five established patterns for RTK filter module architecture: Newtype, Builder, RAII, Trait Objects, and State Machine. It targets CLI tool internals—not web or service layers—and triggers on phrases like "design pattern", "how to structure", and "refactor to pattern". The skill uses Read, Grep, and Glob to inspect existing modules before recommending pattern-specific refactors. Developers reach for design-patterns when adding new RTK filter modules or cleaning up repetitive Rust that lacks type boundaries and lifecycle discipline.

  • Applies 12 core design patterns (Factory, Strategy, Observer, Decorator, Command, Adapter, Singleton, Builder, Prototype
  • Converts natural language feature requests into pattern-rich implementations
  • Prevents common anti-patterns and tightly-coupled code
  • Works across frontend, backend, and full-stack agent projects
  • Hard-gate: always run before committing to a new major feature implementation

Design Patterns by the numbers

  • 978 all-time installs (skills.sh)
  • +42 installs in the week ending Aug 4, 2026 (Skillselion tracking)
  • Ranked #1,124 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/rtk-ai/rtk --skill design-patterns

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs978
repo stars74.8k
Last updatedAugust 3, 2026
Repositoryrtk-ai/rtk

Which Rust design pattern fits RTK filter modules?

Make Claude, Cursor, or Codex produce code that follows established software design patterns instead of generic or repetitive implementations.

Who is it for?

Rust developers building or refactoring RTK CLI filter modules who want established patterns instead of ad-hoc structs and wrappers.

Skip if: Teams working outside RTK or Rust, or web/service architectures where these CLI-specific patterns do not apply.

When should I use this skill?

The user asks for a design pattern, module structure advice, or a refactor toward Newtype, Builder, RAII, trait objects, or state machines in RTK Rust code.

What you get

Pattern-aligned Rust module sketches, refactored filter code, and type-safe CLI boundaries following RTK conventions.

  • Pattern-selected module structure
  • Refactored Rust filter code

By the numbers

  • Covers 5 Rust design patterns: Newtype, Builder, RAII, Trait Objects, and State Machine
  • Declares 6 tag keywords including rust, design-patterns, architecture, newtype, builder, and rtk

Files

SKILL.mdMarkdownGitHub ↗

RTK Rust Design Patterns

Patterns that apply to RTK's filter module architecture. Focused on CLI tool patterns, not web/service patterns.

Pattern 1: Newtype (Type Safety)

Use when: wrapping primitive types to prevent misuse (command names, paths, token counts).

// Without Newtype — easy to mix up
fn track(input_tokens: usize, output_tokens: usize) { ... }
track(output_tokens, input_tokens);  // Silent bug!

// With Newtype — compile error on swap
pub struct InputTokens(pub usize);
pub struct OutputTokens(pub usize);
fn track(input: InputTokens, output: OutputTokens) { ... }
track(OutputTokens(100), InputTokens(400));  // Compile error ✅
// Practical RTK example: command name validation
pub struct CommandName(String);
impl CommandName {
    pub fn new(s: &str) -> Result<Self> {
        if s.contains(';') || s.contains('|') || s.contains('`') {
            anyhow::bail!("Invalid command name: shell metacharacters");
        }
        Ok(Self(s.to_string()))
    }
    pub fn as_str(&self) -> &str { &self.0 }
}

Pattern 2: Builder (Complex Configuration)

Use when: a struct has 4+ optional fields, many with defaults.

#[derive(Default)]
pub struct FilterConfig {
    max_lines: Option<usize>,
    strip_ansi: bool,
    show_warnings: bool,
    truncate_at: Option<usize>,
}

impl FilterConfig {
    pub fn new() -> Self { Self::default() }
    pub fn max_lines(mut self, n: usize) -> Self { self.max_lines = Some(n); self }
    pub fn strip_ansi(mut self, v: bool) -> Self { self.strip_ansi = v; self }
    pub fn show_warnings(mut self, v: bool) -> Self { self.show_warnings = v; self }
}

// Usage — readable, no positional arg confusion
let config = FilterConfig::new()
    .max_lines(50)
    .strip_ansi(true)
    .show_warnings(false);

When NOT to use Builder: if the struct has 1-3 fields with obvious meaning. Over-engineering for simple cases.

Pattern 3: State Machine (Parser/Filter Flows)

Use when: parsing multi-section output (test results, build output) where context changes behavior.

// RTK example: pytest output parsing
#[derive(Debug, PartialEq)]
enum ParseState {
    LookingForTests,
    InTestOutput,
    InFailureSummary,
    Done,
}

fn parse_pytest(input: &str) -> String {
    let mut state = ParseState::LookingForTests;
    let mut failures = Vec::new();

    for line in input.lines() {
        match state {
            ParseState::LookingForTests => {
                if line.contains("FAILED") || line.contains("ERROR") {
                    state = ParseState::InFailureSummary;
                    failures.push(line);
                }
            }
            ParseState::InFailureSummary => {
                if line.starts_with("=====") { state = ParseState::Done; }
                else { failures.push(line); }
            }
            ParseState::Done => break,
            _ => {}
        }
    }
    failures.join("\n")
}

Pattern 4: Trait Object (Command Dispatch)

Use when: different command families need the same interface. Avoids massive match arms.

// Define a common interface for filters
pub trait OutputFilter {
    fn filter(&self, input: &str) -> Result<String>;
    fn command_name(&self) -> &str;
}

pub struct GitFilter;
pub struct CargoFilter;

impl OutputFilter for GitFilter {
    fn filter(&self, input: &str) -> Result<String> { filter_git(input) }
    fn command_name(&self) -> &str { "git" }
}

// RTK currently uses match-based dispatch in main.rs (simpler, no dynamic dispatch overhead)
// Trait objects are useful if filter registry becomes dynamic (e.g., TOML-loaded plugins)

Note: RTK's current match dispatch in main.rs is intentional — static dispatch, zero overhead. Only move to trait objects if the match arm count exceeds ~20 commands.

Pattern 5: RAII (Resource Management)

Use when: managing resources that need cleanup (temp files, SQLite connections).

// RTK tee.rs — RAII for temp output files
pub struct TeeFile {
    path: PathBuf,
}

impl TeeFile {
    pub fn create(content: &str) -> Result<Self> {
        let path = tee_path()?;
        fs::write(&path, content)
            .with_context(|| format!("Failed to write tee file: {}", path.display()))?;
        Ok(Self { path })
    }

    pub fn path(&self) -> &Path { &self.path }
}

// No explicit cleanup needed — file persists intentionally (rotation handled separately)
// If cleanup were needed: impl Drop { fn drop(&mut self) { let _ = fs::remove_file(&self.path); } }

Pattern 6: Strategy (Swappable Filter Logic)

Use when: a command has multiple filtering modes (e.g., compact vs. verbose).

pub enum FilterMode {
    Compact,    // Show only failures/errors
    Summary,    // Show counts + top errors
    Full,       // Pass through unchanged
}

pub fn apply_filter(input: &str, mode: FilterMode) -> String {
    match mode {
        FilterMode::Compact => filter_compact(input),
        FilterMode::Summary => filter_summary(input),
        FilterMode::Full => input.to_string(),
    }
}

Pattern 7: Extension Trait (Add Methods to External Types)

Use when: you need to add methods to types you don't own (like &str for RTK-specific parsing).

pub trait RtkStrExt {
    fn is_error_line(&self) -> bool;
    fn is_warning_line(&self) -> bool;
    fn token_count(&self) -> usize;
}

impl RtkStrExt for str {
    fn is_error_line(&self) -> bool {
        self.starts_with("error") || self.contains("[E")
    }
    fn is_warning_line(&self) -> bool {
        self.starts_with("warning")
    }
    fn token_count(&self) -> usize {
        self.split_whitespace().count()
    }
}

// Usage
if line.is_error_line() { ... }
let tokens = output.token_count();

RTK Pattern Selection Guide

SituationPatternAvoid
New *_cmd.rs filter moduleStandard module pattern (see CLAUDE.md)Over-abstracting
4+ optional config fieldsBuilderStruct literal
Multi-phase output parsingState MachineNested if/else
Type-safe wrapper around stringNewtypeRaw String
Adding methods to &strExtension TraitFree functions
Resource with cleanupRAII / DropManual cleanup
Dynamic filter registryTrait ObjectMatch sprawl

Anti-Patterns in RTK Context

// ❌ Generic over-engineering for one command
pub trait Filterable<T: CommandArgs + Send + Sync + 'static> { ... }

// ✅ Just write the function
pub fn filter_git_log(input: &str) -> Result<String> { ... }

// ❌ Singleton registry with global state
static FILTER_REGISTRY: Mutex<HashMap<String, Box<dyn Filter>>> = ...;

// ✅ Match in main.rs — simple, zero overhead, easy to trace

// ❌ Async traits for "future-proofing"
#[async_trait]
pub trait Filter { async fn apply(&self, input: &str) -> Result<String>; }

// ✅ Synchronous — RTK is single-threaded by design
pub trait Filter { fn apply(&self, input: &str) -> Result<String>; }

Related skills

How it compares

Pick design-patterns over generic Rust architecture advice when work is specifically inside RTK CLI filter modules and needs pattern-matched refactors.

FAQ

Which design patterns does RTK design-patterns cover?

design-patterns covers five Rust patterns for RTK CLI filter modules: Newtype, Builder, RAII, Trait Objects, and State Machine. Each pattern includes when to use it and how it maps to filter-module responsibilities rather than generic web stacks.

When should I invoke design-patterns in an agent session?

Invoke design-patterns when designing new RTK filter modules or refactoring existing Rust that feels repetitive or loosely typed. Trigger phrases include "design pattern", "how to structure", "best pattern for", and "refactor to pattern".

AI & Agent Buildingagentsautomation

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.