PluginBench
Skill
Pass
Audit score 90

domain-cli

actionbook/rust-skills

Build ergonomic CLI tools with type-safe argument parsing, config layering, and user feedback.

What is domain-cli?

A domain-constraint skill for Rust CLI applications. Use when building command-line tools to ensure proper argument parsing, configuration precedence (CLI > env > file), error handling, and user communication patterns.

  • Type-safe argument parsing with clap derive macros (#[derive(Parser)])
  • Subcommand hierarchies for multi-command tools
  • Layered configuration with CLI args overriding environment variables and config files
  • Proper error/data separation (stderr for errors, stdout for data)
  • Progress bars and interactive prompts for user feedback
  • Exit code handling for script integration and automation

How to install domain-cli

npx skills add https://github.com/actionbook/rust-skills --skill domain-cli
Claude Code
Cursor
Windsurf
Cline

How to use domain-cli

  1. 1.Define your CLI arguments as a struct with #[derive(Parser)] from clap
  2. 2.Use #[derive(Subcommand)] to organize related commands
  3. 3.Implement layered config loading: CLI args first, then env vars, then config file
  4. 4.Return Result<(), Error> from main() and use eprintln! for errors, println! for data
  5. 5.Add progress bars with indicatif for long operations
  6. 6.Test exit codes and help text for user clarity

Use cases

Good for
  • Building a multi-command tool with init, run, and config subcommands
  • Creating a CLI that accepts config from multiple sources with clear precedence
  • Adding progress feedback to long-running operations
  • Ensuring CLI output is pipeable and scriptable by separating errors from data
  • Implementing shell-completion and environment-variable support
Who it's for
  • Rust developers building command-line tools
  • Teams needing consistent CLI patterns and user ergonomics
  • Developers integrating CLIs into scripts or automation workflows

domain-cli FAQ

When should I use this skill?

Use this skill when building any Rust CLI tool that needs argument parsing, configuration management, or user feedback. It's essential for tools that will be scripted or integrated into automation.

What's the configuration precedence order?

CLI arguments take highest priority, followed by environment variables, then config files, and finally defaults. This matches user expectations for override capability.

Why separate errors to stderr?

Sending errors to stderr and data to stdout keeps output pipeable and scriptable. Tools downstream can process stdout without being interrupted by error messages.

Which crate should I use for argument parsing?

Use clap with derive macros (#[derive(Parser)]) for type-safe, ergonomic argument parsing. It automatically generates help text and handles validation.

How do I handle long-running operations?

Use indicatif's ProgressBar for visual feedback. Wrap it in a struct for RAII-style cleanup, ensuring the progress bar is properly finalized even if an error occurs.

Full instructions (SKILL.md)

Source of truth, from actionbook/rust-skills.


name: domain-cli description: "Use when building CLI tools. Keywords: CLI, command line, terminal, clap, structopt, argument parsing, subcommand, interactive, TUI, ratatui, crossterm, indicatif, progress bar, colored output, shell completion, config file, environment variable, 命令行, 终端应用, 参数解析" globs: ["**/Cargo.toml"] user-invocable: false

CLI Domain

Layer 3: Domain Constraints

Domain Constraints → Design Implications

Domain RuleDesign ConstraintRust Implication
User ergonomicsClear help, errorsclap derive macros
Config precedenceCLI > env > fileLayered config loading
Exit codesNon-zero on errorProper Result handling
Stdout/stderrData vs errorseprintln! for errors
InterruptibleHandle Ctrl+CSignal handling

Critical Constraints

User Communication

RULE: Errors to stderr, data to stdout
WHY: Pipeable output, scriptability
RUST: eprintln! for errors, println! for data

Configuration Priority

RULE: CLI args > env vars > config file > defaults
WHY: User expectation, override capability
RUST: Layered config with clap + figment/config

Exit Codes

RULE: Return non-zero on any error
WHY: Script integration, automation
RUST: main() -> Result<(), Error> or explicit exit()

Trace Down ↓

From constraints to design (Layer 2):

"Need argument parsing"
    ↓ m05-type-driven: Derive structs for args
    ↓ clap: #[derive(Parser)]

"Need config layering"
    ↓ m09-domain: Config as domain object
    ↓ figment/config: Layer sources

"Need progress display"
    ↓ m12-lifecycle: Progress bar as RAII
    ↓ indicatif: ProgressBar

Key Crates

PurposeCrate
Argument parsingclap
Interactive promptsdialoguer
Progress barsindicatif
Colored outputcolored
Terminal UIratatui
Terminal controlcrossterm
Console utilitiesconsole

Design Patterns

PatternPurposeImplementation
Args structType-safe args#[derive(Parser)]
SubcommandsCommand hierarchy#[derive(Subcommand)]
Config layersOverride precedenceCLI > env > file
ProgressUser feedbackProgressBar::new(len)

Code Pattern: CLI Structure

use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(name = "myapp", about = "My CLI tool")]
struct Cli {
    /// Enable verbose output
    #[arg(short, long)]
    verbose: bool,

    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Initialize a new project
    Init { name: String },
    /// Run the application
    Run {
        #[arg(short, long)]
        port: Option<u16>,
    },
}

fn main() -> anyhow::Result<()> {
    let cli = Cli::parse();
    match cli.command {
        Commands::Init { name } => init_project(&name)?,
        Commands::Run { port } => run_server(port.unwrap_or(8080))?,
    }
    Ok(())
}

Common Mistakes

MistakeDomain ViolationFix
Errors to stdoutBreaks pipingeprintln!
No help textPoor UX#[arg(help = "...")]
Panic on errorBad exit codeResult + proper handling
No progress for long opsUser uncertaintyindicatif

Trace to Layer 1

ConstraintLayer 2 PatternLayer 1 Implementation
Type-safe argsDerive macrosclap Parser
Error handlingResult propagationanyhow + exit codes
User feedbackProgress RAIIindicatif ProgressBar
Config precedenceBuilder patternLayered sources

Related Skills

WhenSee
Error handlingm06-error-handling
Type-driven argsm05-type-driven
Progress lifecyclem12-lifecycle
Async CLIm07-concurrency