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-cliHow to use domain-cli
- 1.Define your CLI arguments as a struct with #[derive(Parser)] from clap
- 2.Use #[derive(Subcommand)] to organize related commands
- 3.Implement layered config loading: CLI args first, then env vars, then config file
- 4.Return Result<(), Error> from main() and use eprintln! for errors, println! for data
- 5.Add progress bars with indicatif for long operations
- 6.Test exit codes and help text for user clarity
Use cases
- 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
- 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
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.
CLI arguments take highest priority, followed by environment variables, then config files, and finally defaults. This matches user expectations for override capability.
Sending errors to stderr and data to stdout keeps output pipeable and scriptable. Tools downstream can process stdout without being interrupted by error messages.
Use clap with derive macros (#[derive(Parser)]) for type-safe, ergonomic argument parsing. It automatically generates help text and handles validation.
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 Rule | Design Constraint | Rust Implication |
|---|---|---|
| User ergonomics | Clear help, errors | clap derive macros |
| Config precedence | CLI > env > file | Layered config loading |
| Exit codes | Non-zero on error | Proper Result handling |
| Stdout/stderr | Data vs errors | eprintln! for errors |
| Interruptible | Handle Ctrl+C | Signal 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
| Purpose | Crate |
|---|---|
| Argument parsing | clap |
| Interactive prompts | dialoguer |
| Progress bars | indicatif |
| Colored output | colored |
| Terminal UI | ratatui |
| Terminal control | crossterm |
| Console utilities | console |
Design Patterns
| Pattern | Purpose | Implementation |
|---|---|---|
| Args struct | Type-safe args | #[derive(Parser)] |
| Subcommands | Command hierarchy | #[derive(Subcommand)] |
| Config layers | Override precedence | CLI > env > file |
| Progress | User feedback | ProgressBar::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
| Mistake | Domain Violation | Fix |
|---|---|---|
| Errors to stdout | Breaks piping | eprintln! |
| No help text | Poor UX | #[arg(help = "...")] |
| Panic on error | Bad exit code | Result + proper handling |
| No progress for long ops | User uncertainty | indicatif |
Trace to Layer 1
| Constraint | Layer 2 Pattern | Layer 1 Implementation |
|---|---|---|
| Type-safe args | Derive macros | clap Parser |
| Error handling | Result propagation | anyhow + exit codes |
| User feedback | Progress RAII | indicatif ProgressBar |
| Config precedence | Builder pattern | Layered sources |
Related Skills
| When | See |
|---|---|
| Error handling | m06-error-handling |
| Type-driven args | m05-type-driven |
| Progress lifecycle | m12-lifecycle |
| Async CLI | m07-concurrency |
Related skills
More from actionbook/rust-skills and the wider catalog.

domain-cloud-native
Build cloud-native Rust apps with stateless design, graceful shutdown, and observability patterns.

domain-embedded
Embedded Rust development constraints: no_std, no heap, interrupt safety, and hardware ownership patterns.

domain-fintech
Fintech domain constraints and patterns for Rust: precision, immutability, and audit trails.

domain-iot
Design IoT apps with offline-first, power-efficient, and secure patterns in Rust.

domain-ml
Build ML/AI applications in Rust with efficient tensor operations, GPU acceleration, and model inference.

domain-web
Build concurrent, type-safe web services with async handlers, middleware, and shared state patterns.