Initial commit
This commit is contained in:
674
commands/cli-enhance.md
Normal file
674
commands/cli-enhance.md
Normal file
@@ -0,0 +1,674 @@
|
||||
---
|
||||
name: cli-enhance
|
||||
description: Add features to existing CLI applications like colors, progress bars, shell completions, and better error messages
|
||||
---
|
||||
|
||||
# CLI Enhance Command
|
||||
|
||||
Add modern CLI features to an existing Rust CLI application, including colors, progress bars, interactive prompts, shell completions, and beautiful error messages.
|
||||
|
||||
## Arguments
|
||||
|
||||
- `$1` - Feature to add: "colors", "progress", "prompts", "completions", "errors", "config", "logging", or "all" (required)
|
||||
- `$2` - Path to project directory (optional, defaults to current directory)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Add all enhancements
|
||||
/cli-enhance all
|
||||
|
||||
# Add specific feature
|
||||
/cli-enhance colors
|
||||
/cli-enhance progress
|
||||
/cli-enhance completions
|
||||
|
||||
# Enhance specific project
|
||||
/cli-enhance colors /path/to/my-cli
|
||||
```
|
||||
|
||||
## Available Enhancements
|
||||
|
||||
### 1. Colors and Styling
|
||||
|
||||
Add semantic colors to CLI output using owo-colors.
|
||||
|
||||
**What Gets Added:**
|
||||
|
||||
- Dependency: `owo-colors`
|
||||
- Dependency: `supports-color` (for detection)
|
||||
- Color module with semantic helpers
|
||||
- NO_COLOR environment variable support
|
||||
- Terminal capability detection
|
||||
|
||||
**Example Implementation:**
|
||||
|
||||
```rust
|
||||
// src/colors.rs
|
||||
use owo_colors::{OwoColorize, Stream};
|
||||
|
||||
pub fn success(message: &str) {
|
||||
println!(
|
||||
"{} {}",
|
||||
"✓".if_supports_color(Stream::Stdout, |text| text.green().bold()),
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
pub fn error(message: &str) {
|
||||
eprintln!(
|
||||
"{} {}",
|
||||
"✗".if_supports_color(Stream::Stderr, |text| text.red().bold()),
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
pub fn warning(message: &str) {
|
||||
println!(
|
||||
"{} {}",
|
||||
"⚠".if_supports_color(Stream::Stdout, |text| text.yellow().bold()),
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
pub fn info(message: &str) {
|
||||
println!(
|
||||
"{} {}",
|
||||
"ℹ".if_supports_color(Stream::Stdout, |text| text.blue().bold()),
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
pub fn supports_color() -> bool {
|
||||
use supports_color::Stream as ColorStream;
|
||||
supports_color::on(ColorStream::Stdout).is_some()
|
||||
}
|
||||
```
|
||||
|
||||
**Usage in Code:**
|
||||
|
||||
```rust
|
||||
use crate::colors;
|
||||
|
||||
colors::success("Build completed!");
|
||||
colors::error("Failed to read file");
|
||||
colors::warning("Configuration incomplete");
|
||||
colors::info("Processing 10 files");
|
||||
```
|
||||
|
||||
### 2. Progress Bars and Spinners
|
||||
|
||||
Add visual feedback for long-running operations using indicatif.
|
||||
|
||||
**What Gets Added:**
|
||||
|
||||
- Dependency: `indicatif`
|
||||
- Progress module with common patterns
|
||||
- Spinner for indeterminate operations
|
||||
- Progress bars with custom styling
|
||||
- Multi-progress for parallel tasks
|
||||
|
||||
**Example Implementation:**
|
||||
|
||||
```rust
|
||||
// src/progress.rs
|
||||
use indicatif::{ProgressBar, ProgressStyle, MultiProgress, HumanDuration};
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn create_progress_bar(total: u64) -> ProgressBar {
|
||||
let pb = ProgressBar::new(total);
|
||||
pb.set_style(
|
||||
ProgressStyle::default_bar()
|
||||
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})")
|
||||
.unwrap()
|
||||
.progress_chars("#>-")
|
||||
);
|
||||
pb
|
||||
}
|
||||
|
||||
pub fn create_spinner(message: &str) -> ProgressBar {
|
||||
let spinner = ProgressBar::new_spinner();
|
||||
spinner.set_style(
|
||||
ProgressStyle::default_spinner()
|
||||
.template("{spinner:.green} {msg}")
|
||||
.unwrap()
|
||||
.tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"])
|
||||
);
|
||||
spinner.set_message(message.to_string());
|
||||
spinner
|
||||
}
|
||||
|
||||
pub fn create_multi_progress() -> MultiProgress {
|
||||
MultiProgress::new()
|
||||
}
|
||||
```
|
||||
|
||||
**Usage in Code:**
|
||||
|
||||
```rust
|
||||
use crate::progress;
|
||||
|
||||
// Progress bar for known total
|
||||
let pb = progress::create_progress_bar(100);
|
||||
for i in 0..100 {
|
||||
// Do work
|
||||
pb.inc(1);
|
||||
}
|
||||
pb.finish_with_message("Complete!");
|
||||
|
||||
// Spinner for unknown duration
|
||||
let spinner = progress::create_spinner("Processing...");
|
||||
// Do work
|
||||
spinner.finish_with_message("Done!");
|
||||
```
|
||||
|
||||
### 3. Interactive Prompts
|
||||
|
||||
Add user-friendly interactive prompts using dialoguer.
|
||||
|
||||
**What Gets Added:**
|
||||
|
||||
- Dependency: `dialoguer`
|
||||
- Prompts module with common patterns
|
||||
- Confirmation prompts
|
||||
- Text input with validation
|
||||
- Selection menus
|
||||
- Multi-select options
|
||||
|
||||
**Example Implementation:**
|
||||
|
||||
```rust
|
||||
// src/prompts.rs
|
||||
use dialoguer::{
|
||||
Confirm, Input, Select, MultiSelect, Password,
|
||||
theme::ColorfulTheme
|
||||
};
|
||||
use anyhow::Result;
|
||||
|
||||
pub fn confirm(prompt: &str, default: bool) -> Result<bool> {
|
||||
Ok(Confirm::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt(prompt)
|
||||
.default(default)
|
||||
.interact()?)
|
||||
}
|
||||
|
||||
pub fn input(prompt: &str, default: Option<String>) -> Result<String> {
|
||||
let mut input = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt(prompt);
|
||||
|
||||
if let Some(d) = default {
|
||||
input = input.default(d);
|
||||
}
|
||||
|
||||
Ok(input.interact_text()?)
|
||||
}
|
||||
|
||||
pub fn select<T: ToString>(prompt: &str, items: &[T]) -> Result<usize> {
|
||||
Ok(Select::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt(prompt)
|
||||
.items(items)
|
||||
.interact()?)
|
||||
}
|
||||
|
||||
pub fn multi_select<T: ToString>(prompt: &str, items: &[T]) -> Result<Vec<usize>> {
|
||||
Ok(MultiSelect::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt(prompt)
|
||||
.items(items)
|
||||
.interact()?)
|
||||
}
|
||||
|
||||
pub fn password(prompt: &str, confirm: bool) -> Result<String> {
|
||||
if confirm {
|
||||
Ok(Password::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt(prompt)
|
||||
.with_confirmation("Confirm password", "Passwords don't match")
|
||||
.interact()?)
|
||||
} else {
|
||||
Ok(Password::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt(prompt)
|
||||
.interact()?)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage in Code:**
|
||||
|
||||
```rust
|
||||
use crate::prompts;
|
||||
|
||||
// Confirmation
|
||||
if prompts::confirm("Continue with deployment?", false)? {
|
||||
deploy()?;
|
||||
}
|
||||
|
||||
// Text input
|
||||
let name = prompts::input("Project name", Some("my-project".to_string()))?;
|
||||
|
||||
// Selection
|
||||
let envs = vec!["dev", "staging", "production"];
|
||||
let idx = prompts::select("Select environment", &envs)?;
|
||||
```
|
||||
|
||||
### 4. Shell Completions
|
||||
|
||||
Add shell completion generation support.
|
||||
|
||||
**What Gets Added:**
|
||||
|
||||
- Dependency: `clap_complete`
|
||||
- Completion generation command
|
||||
- Support for bash, zsh, fish, powershell
|
||||
- Installation instructions
|
||||
|
||||
**Example Implementation:**
|
||||
|
||||
```rust
|
||||
// src/completions.rs
|
||||
use clap::CommandFactory;
|
||||
use clap_complete::{generate, Generator, Shell};
|
||||
use std::io;
|
||||
|
||||
pub fn generate_completions<G: Generator>(gen: G) {
|
||||
let mut cmd = crate::cli::Cli::command();
|
||||
generate(gen, &mut cmd, cmd.get_name().to_string(), &mut io::stdout());
|
||||
}
|
||||
|
||||
pub fn print_install_instructions(shell: Shell) {
|
||||
match shell {
|
||||
Shell::Bash => {
|
||||
eprintln!("To install completions, add to ~/.bashrc:");
|
||||
eprintln!(" eval \"$(myapp --generate bash)\"");
|
||||
}
|
||||
Shell::Zsh => {
|
||||
eprintln!("To install completions, add to ~/.zshrc:");
|
||||
eprintln!(" eval \"$(myapp --generate zsh)\"");
|
||||
}
|
||||
Shell::Fish => {
|
||||
eprintln!("To install completions:");
|
||||
eprintln!(" myapp --generate fish | source");
|
||||
eprintln!(" Or save to: ~/.config/fish/completions/myapp.fish");
|
||||
}
|
||||
Shell::PowerShell => {
|
||||
eprintln!("To install completions, add to $PROFILE:");
|
||||
eprintln!(" Invoke-Expression (& myapp --generate powershell)");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Add to CLI:**
|
||||
|
||||
```rust
|
||||
// src/cli.rs
|
||||
use clap::{Parser, ValueEnum};
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct Cli {
|
||||
/// Generate shell completions
|
||||
#[arg(long = "generate", value_enum)]
|
||||
pub generate: Option<Shell>,
|
||||
|
||||
// ... other fields
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Clone)]
|
||||
pub enum Shell {
|
||||
Bash,
|
||||
Zsh,
|
||||
Fish,
|
||||
PowerShell,
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Beautiful Error Messages
|
||||
|
||||
Upgrade error handling with miette for rich diagnostics.
|
||||
|
||||
**What Gets Added:**
|
||||
|
||||
- Dependency: `miette` with `fancy` feature
|
||||
- Structured error types
|
||||
- Source code snippets in errors
|
||||
- Help text and suggestions
|
||||
- Error URLs
|
||||
|
||||
**Example Implementation:**
|
||||
|
||||
```rust
|
||||
// src/error.rs
|
||||
use miette::{Diagnostic, SourceSpan};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, Diagnostic)]
|
||||
#[error("Configuration error")]
|
||||
#[diagnostic(
|
||||
code(config::invalid),
|
||||
url("https://example.com/docs/config"),
|
||||
help("Check your configuration file syntax")
|
||||
)]
|
||||
pub struct ConfigError {
|
||||
#[source_code]
|
||||
pub src: String,
|
||||
|
||||
#[label("this field is invalid")]
|
||||
pub span: SourceSpan,
|
||||
|
||||
#[help]
|
||||
pub advice: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, Diagnostic)]
|
||||
pub enum AppError {
|
||||
#[error("File not found: {path}")]
|
||||
#[diagnostic(
|
||||
code(app::file_not_found),
|
||||
help("Check that the file exists and you have permission to read it")
|
||||
)]
|
||||
FileNotFound {
|
||||
path: String,
|
||||
},
|
||||
|
||||
#[error("Build failed")]
|
||||
#[diagnostic(
|
||||
code(app::build_failed),
|
||||
help("Run with -vv for detailed logs")
|
||||
)]
|
||||
BuildFailed {
|
||||
#[source]
|
||||
source: anyhow::Error,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Update main.rs:**
|
||||
|
||||
```rust
|
||||
fn main() -> miette::Result<()> {
|
||||
miette::set_panic_hook();
|
||||
|
||||
// Rest of application
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Configuration Management
|
||||
|
||||
Add comprehensive configuration system.
|
||||
|
||||
**What Gets Added:**
|
||||
|
||||
- Dependency: `config`
|
||||
- Dependency: `serde`
|
||||
- Dependency: `toml`
|
||||
- Dependency: `directories`
|
||||
- Config module with precedence handling
|
||||
- XDG directory support
|
||||
- Environment variable support
|
||||
|
||||
**Example Implementation:**
|
||||
|
||||
```rust
|
||||
// src/config.rs
|
||||
use config::{Config as ConfigBuilder, Environment, File};
|
||||
use directories::ProjectDirs;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use anyhow::Result;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
pub general: General,
|
||||
pub features: Features,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct General {
|
||||
pub log_level: String,
|
||||
pub timeout: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Features {
|
||||
pub colors: bool,
|
||||
pub progress: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load(cli_config: Option<PathBuf>) -> Result<Self> {
|
||||
let mut builder = ConfigBuilder::builder()
|
||||
.set_default("general.log_level", "info")?
|
||||
.set_default("general.timeout", 30)?
|
||||
.set_default("features.colors", true)?
|
||||
.set_default("features.progress", true)?;
|
||||
|
||||
// Load from standard locations
|
||||
if let Some(proj_dirs) = ProjectDirs::from("com", "example", "myapp") {
|
||||
let config_dir = proj_dirs.config_dir();
|
||||
builder = builder
|
||||
.add_source(File::from(config_dir.join("config.toml")).required(false));
|
||||
}
|
||||
|
||||
// Override with CLI-specified config
|
||||
if let Some(path) = cli_config {
|
||||
builder = builder.add_source(File::from(path));
|
||||
}
|
||||
|
||||
// Environment variables override everything
|
||||
builder = builder.add_source(
|
||||
Environment::with_prefix("MYAPP")
|
||||
.separator("_")
|
||||
.try_parsing(true)
|
||||
);
|
||||
|
||||
Ok(builder.build()?.try_deserialize()?)
|
||||
}
|
||||
|
||||
pub fn write_default(path: &PathBuf) -> Result<()> {
|
||||
let default = Config {
|
||||
general: General {
|
||||
log_level: "info".to_string(),
|
||||
timeout: 30,
|
||||
},
|
||||
features: Features {
|
||||
colors: true,
|
||||
progress: true,
|
||||
},
|
||||
};
|
||||
|
||||
let toml = toml::to_string_pretty(&default)?;
|
||||
std::fs::write(path, toml)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Structured Logging
|
||||
|
||||
Add tracing-based structured logging.
|
||||
|
||||
**What Gets Added:**
|
||||
|
||||
- Dependency: `tracing`
|
||||
- Dependency: `tracing-subscriber`
|
||||
- Logging module with verbosity support
|
||||
- Structured logging macros
|
||||
|
||||
**Example Implementation:**
|
||||
|
||||
```rust
|
||||
// src/logging.rs
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
use anyhow::Result;
|
||||
|
||||
pub fn setup(verbosity: u8) -> Result<()> {
|
||||
let level = match verbosity {
|
||||
0 => "error",
|
||||
1 => "warn",
|
||||
2 => "info",
|
||||
3 => "debug",
|
||||
_ => "trace",
|
||||
};
|
||||
|
||||
let env_filter = EnvFilter::try_from_default_env()
|
||||
.or_else(|_| EnvFilter::try_new(level))?;
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(fmt::layer().with_target(false).with_level(true))
|
||||
.with(env_filter)
|
||||
.init();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
|
||||
```rust
|
||||
use tracing::{info, warn, error, debug};
|
||||
|
||||
info!("Starting build process");
|
||||
debug!("Configuration: {:?}", config);
|
||||
warn!("Using default value for missing field");
|
||||
error!("Build failed: {}", error);
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
When you invoke this command:
|
||||
|
||||
1. **Analyze Current Project**
|
||||
- Detect existing dependencies
|
||||
- Identify CLI framework (Clap version)
|
||||
- Check for existing features
|
||||
- Find integration points
|
||||
|
||||
2. **Add Dependencies**
|
||||
- Update Cargo.toml with new dependencies
|
||||
- Add appropriate feature flags
|
||||
- Ensure version compatibility
|
||||
|
||||
3. **Generate Code**
|
||||
- Create new modules for features
|
||||
- Add helper functions and patterns
|
||||
- Integrate with existing code
|
||||
|
||||
4. **Update Existing Code**
|
||||
- Replace println! with colored output
|
||||
- Add progress bars to long operations
|
||||
- Upgrade error types
|
||||
- Add completion generation to CLI
|
||||
|
||||
5. **Add Documentation**
|
||||
- Document new features in README
|
||||
- Add inline code documentation
|
||||
- Provide usage examples
|
||||
|
||||
6. **Verify Integration**
|
||||
- Run cargo check
|
||||
- Run tests
|
||||
- Test new features
|
||||
|
||||
7. **Generate Report**
|
||||
- List added features
|
||||
- Show usage examples
|
||||
- Provide next steps
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
✓ Analyzed project structure
|
||||
✓ Added dependencies to Cargo.toml
|
||||
✓ Created colors module (src/colors.rs)
|
||||
✓ Created progress module (src/progress.rs)
|
||||
✓ Created prompts module (src/prompts.rs)
|
||||
✓ Updated CLI for completions
|
||||
✓ Upgraded error types with miette
|
||||
✓ Updated 15 call sites with new features
|
||||
✓ Added documentation
|
||||
|
||||
Enhancements Applied Successfully!
|
||||
|
||||
Added Features:
|
||||
• Colors and styling (owo-colors)
|
||||
• Progress bars and spinners (indicatif)
|
||||
• Interactive prompts (dialoguer)
|
||||
• Shell completions (bash, zsh, fish, powershell)
|
||||
• Beautiful error messages (miette)
|
||||
|
||||
New Dependencies:
|
||||
owo-colors = "4"
|
||||
indicatif = "0.17"
|
||||
dialoguer = "0.11"
|
||||
clap_complete = "4"
|
||||
miette = { version = "7", features = ["fancy"] }
|
||||
|
||||
Files Modified:
|
||||
• Cargo.toml (dependencies added)
|
||||
• src/lib.rs (modules exported)
|
||||
• src/cli.rs (completion flag added)
|
||||
• src/main.rs (error handler updated)
|
||||
|
||||
Files Created:
|
||||
• src/colors.rs
|
||||
• src/progress.rs
|
||||
• src/prompts.rs
|
||||
• src/completions.rs
|
||||
|
||||
Updated Code Locations:
|
||||
• src/commands/build.rs (added progress bar)
|
||||
• src/commands/init.rs (added prompts)
|
||||
• src/error.rs (upgraded to miette)
|
||||
|
||||
Usage Examples:
|
||||
|
||||
Colors:
|
||||
use crate::colors;
|
||||
colors::success("Build completed!");
|
||||
colors::error("Failed to read file");
|
||||
|
||||
Progress:
|
||||
use crate::progress;
|
||||
let pb = progress::create_progress_bar(100);
|
||||
pb.inc(1);
|
||||
pb.finish_with_message("Done!");
|
||||
|
||||
Prompts:
|
||||
use crate::prompts;
|
||||
if prompts::confirm("Continue?", true)? {
|
||||
// do something
|
||||
}
|
||||
|
||||
Completions:
|
||||
myapp --generate bash > /etc/bash_completion.d/myapp
|
||||
myapp --generate zsh > ~/.zfunc/_myapp
|
||||
|
||||
Next Steps:
|
||||
1. Review generated code
|
||||
2. Test new features: cargo run
|
||||
3. Update documentation if needed
|
||||
4. Commit changes: git add . && git commit
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
Use the appropriate **rust-cli-developer** agents:
|
||||
|
||||
```
|
||||
Use Task tool with subagent_type="rust-cli-developer:cli-ux-specialist"
|
||||
for colors, progress, and prompts
|
||||
|
||||
Use Task tool with subagent_type="rust-cli-developer:cli-architect"
|
||||
for configuration and logging
|
||||
|
||||
Use Task tool with subagent_type="rust-cli-developer:clap-expert"
|
||||
for shell completions integration
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Enhancements are additive and non-destructive
|
||||
- Existing code is updated carefully to maintain functionality
|
||||
- Dependencies are added with compatible versions
|
||||
- All changes are tested before completion
|
||||
- Documentation is updated to reflect new features
|
||||
- Backward compatibility is maintained where possible
|
||||
447
commands/cli-review.md
Normal file
447
commands/cli-review.md
Normal file
@@ -0,0 +1,447 @@
|
||||
---
|
||||
name: cli-review
|
||||
description: Review Rust CLI applications for UX, error handling, testing, and cross-platform compatibility
|
||||
---
|
||||
|
||||
# CLI Review Command
|
||||
|
||||
Comprehensively review a Rust CLI application for code quality, user experience, error handling, testing coverage, and cross-platform compatibility.
|
||||
|
||||
## Arguments
|
||||
|
||||
- `$1` - Path to project directory (optional, defaults to current directory)
|
||||
- `--focus` - Specific area to focus on: "ux", "errors", "tests", "config", "perf", or "all" (optional, default: "all")
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Review current directory
|
||||
/cli-review
|
||||
|
||||
# Review specific project
|
||||
/cli-review /path/to/my-cli
|
||||
|
||||
# Focus on specific area
|
||||
/cli-review --focus ux
|
||||
/cli-review --focus errors
|
||||
/cli-review --focus tests
|
||||
```
|
||||
|
||||
## Review Areas
|
||||
|
||||
### 1. Argument Design & CLI Interface
|
||||
|
||||
**Checks:**
|
||||
- [ ] Argument naming follows conventions (kebab-case)
|
||||
- [ ] Short and long forms provided where appropriate
|
||||
- [ ] Help text is clear and descriptive
|
||||
- [ ] Defaults are sensible and documented
|
||||
- [ ] Mutually exclusive args use proper groups
|
||||
- [ ] Required args are clearly marked
|
||||
- [ ] Value names are descriptive (FILE, PORT, URL)
|
||||
- [ ] Global options work with all subcommands
|
||||
- [ ] Version information is present
|
||||
|
||||
**Example Issues:**
|
||||
|
||||
```
|
||||
❌ Issue: Unclear argument name
|
||||
File: src/cli.rs:15
|
||||
Found: #[arg(short, long)]
|
||||
pub x: String,
|
||||
|
||||
Recommendation: Use descriptive names
|
||||
#[arg(short, long, value_name = "FILE")]
|
||||
pub input_file: PathBuf,
|
||||
```
|
||||
|
||||
### 2. Help Text Quality
|
||||
|
||||
**Checks:**
|
||||
- [ ] Command-level help is present
|
||||
- [ ] All arguments have descriptions
|
||||
- [ ] Long help provides examples
|
||||
- [ ] Help text uses active voice
|
||||
- [ ] Complex options have detailed explanations
|
||||
- [ ] Examples section shows common usage
|
||||
- [ ] After-help provides additional resources
|
||||
|
||||
**Example Issues:**
|
||||
|
||||
```
|
||||
❌ Issue: Missing help text
|
||||
File: src/cli.rs:23
|
||||
Found: #[arg(short, long)]
|
||||
pub verbose: bool,
|
||||
|
||||
Recommendation: Add descriptive help
|
||||
/// Enable verbose output with detailed logging
|
||||
#[arg(short, long)]
|
||||
pub verbose: bool,
|
||||
```
|
||||
|
||||
### 3. Error Messages
|
||||
|
||||
**Checks:**
|
||||
- [ ] Errors explain what went wrong
|
||||
- [ ] Errors suggest how to fix the problem
|
||||
- [ ] File paths are displayed in error messages
|
||||
- [ ] Using miette or similar for rich diagnostics
|
||||
- [ ] Error types are well-structured (thiserror)
|
||||
- [ ] Context is added at each error level
|
||||
- [ ] Exit codes are meaningful and documented
|
||||
- [ ] Errors go to stderr, not stdout
|
||||
|
||||
**Example Issues:**
|
||||
|
||||
```
|
||||
❌ Issue: Unhelpful error message
|
||||
File: src/commands/build.rs:42
|
||||
Found: bail!("Build failed");
|
||||
|
||||
Recommendation: Provide context and solutions
|
||||
bail!(
|
||||
"Build failed: {}\n\n\
|
||||
Possible causes:\n\
|
||||
- Missing dependencies\n\
|
||||
- Invalid configuration\n\
|
||||
Try: cargo check",
|
||||
source
|
||||
);
|
||||
```
|
||||
|
||||
### 4. User Experience
|
||||
|
||||
**Checks:**
|
||||
- [ ] Progress indicators for long operations
|
||||
- [ ] Colors used semantically (red=error, green=success)
|
||||
- [ ] NO_COLOR environment variable respected
|
||||
- [ ] Interactive prompts have --yes flag alternative
|
||||
- [ ] Destructive operations require confirmation
|
||||
- [ ] Output is well-formatted (tables, lists)
|
||||
- [ ] Supports both human and machine-readable output
|
||||
- [ ] Verbosity levels work correctly (-v, -vv, -vvv)
|
||||
|
||||
**Example Issues:**
|
||||
|
||||
```
|
||||
⚠ Warning: Missing progress indicator
|
||||
File: src/commands/download.rs:30
|
||||
Found: Long-running download operation without feedback
|
||||
|
||||
Recommendation: Add progress bar
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
|
||||
let pb = ProgressBar::new(total_size);
|
||||
pb.set_style(ProgressStyle::default_bar()...);
|
||||
```
|
||||
|
||||
### 5. Configuration Management
|
||||
|
||||
**Checks:**
|
||||
- [ ] Config file support implemented
|
||||
- [ ] Environment variables supported
|
||||
- [ ] Precedence is correct (defaults < file < env < CLI)
|
||||
- [ ] Config file locations follow XDG spec
|
||||
- [ ] Command to generate default config
|
||||
- [ ] Config validation on load
|
||||
- [ ] Sensitive data from env vars only
|
||||
- [ ] Config errors are helpful
|
||||
|
||||
**Example Issues:**
|
||||
|
||||
```
|
||||
❌ Issue: No environment variable support
|
||||
File: src/config.rs:15
|
||||
Found: Config only loaded from file
|
||||
|
||||
Recommendation: Support env vars
|
||||
#[arg(long, env = "MYAPP_DATABASE_URL")]
|
||||
pub database_url: String,
|
||||
```
|
||||
|
||||
### 6. Cross-Platform Compatibility
|
||||
|
||||
**Checks:**
|
||||
- [ ] Path handling uses std::path, not string concat
|
||||
- [ ] File permissions checked before use
|
||||
- [ ] Line endings handled correctly (CRLF vs LF)
|
||||
- [ ] Platform-specific code properly cfg-gated
|
||||
- [ ] Terminal width detection
|
||||
- [ ] Color support detection
|
||||
- [ ] Signal handling (Ctrl+C)
|
||||
- [ ] Tests run on all platforms in CI
|
||||
|
||||
**Example Issues:**
|
||||
|
||||
```
|
||||
❌ Issue: Hardcoded path separator
|
||||
File: src/utils.rs:10
|
||||
Found: let path = format!("{}/{}", dir, file);
|
||||
|
||||
Recommendation: Use Path::join
|
||||
let path = Path::new(dir).join(file);
|
||||
```
|
||||
|
||||
### 7. Testing Coverage
|
||||
|
||||
**Checks:**
|
||||
- [ ] Integration tests present (assert_cmd)
|
||||
- [ ] Help output tested
|
||||
- [ ] Error cases tested
|
||||
- [ ] Exit codes verified
|
||||
- [ ] Config loading tested
|
||||
- [ ] Environment variable handling tested
|
||||
- [ ] Snapshot tests for output (insta)
|
||||
- [ ] Cross-platform tests in CI
|
||||
|
||||
**Example Issues:**
|
||||
|
||||
```
|
||||
⚠ Warning: No integration tests found
|
||||
Expected: tests/integration.rs or tests/cli_tests.rs
|
||||
|
||||
Recommendation: Add integration tests
|
||||
See: https://rust-cli.github.io/book/tutorial/testing.html
|
||||
```
|
||||
|
||||
### 8. Performance
|
||||
|
||||
**Checks:**
|
||||
- [ ] Startup time is reasonable (< 100ms for --help)
|
||||
- [ ] Binary size is optimized
|
||||
- [ ] Lazy loading for heavy dependencies
|
||||
- [ ] Streaming for large files
|
||||
- [ ] Async runtime only when needed
|
||||
- [ ] Proper buffering for I/O
|
||||
|
||||
## Review Output Format
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
CLI Review Report: my-cli
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Overall Rating: B+ (Good)
|
||||
|
||||
Summary:
|
||||
✓ 23 checks passed
|
||||
⚠ 5 warnings
|
||||
❌ 3 issues found
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Issues Found
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
❌ CRITICAL: Missing error context
|
||||
File: src/commands/build.rs:42
|
||||
Line: return Err(e.into());
|
||||
|
||||
Problem: Errors are not wrapped with context
|
||||
Impact: Users won't understand what failed
|
||||
|
||||
Recommendation:
|
||||
return Err(e)
|
||||
.context("Failed to build project")
|
||||
.context("Check build configuration");
|
||||
|
||||
Priority: High
|
||||
Effort: Low
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
⚠ WARNING: No progress indicator
|
||||
File: src/commands/download.rs:55
|
||||
|
||||
Problem: Long operation without user feedback
|
||||
Impact: Poor user experience, appears frozen
|
||||
|
||||
Recommendation:
|
||||
Add indicatif progress bar for downloads
|
||||
|
||||
Priority: Medium
|
||||
Effort: Low
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Strengths
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
✓ Well-structured CLI with clear subcommands
|
||||
✓ Good use of Clap derive API
|
||||
✓ Proper error types with thiserror
|
||||
✓ Configuration management implemented
|
||||
✓ Cross-platform path handling
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Recommendations
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Priority: HIGH
|
||||
1. Add error context to all error paths
|
||||
2. Implement integration tests
|
||||
3. Add --help examples section
|
||||
|
||||
Priority: MEDIUM
|
||||
4. Add progress indicators for long operations
|
||||
5. Implement shell completion generation
|
||||
6. Add NO_COLOR support
|
||||
|
||||
Priority: LOW
|
||||
7. Optimize binary size with strip = true
|
||||
8. Add benchmarks for performance testing
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Detailed Metrics
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Code Quality: ████████░░ 80%
|
||||
Error Handling: ██████░░░░ 60%
|
||||
User Experience: ███████░░░ 70%
|
||||
Testing: ████░░░░░░ 40%
|
||||
Documentation: ████████░░ 80%
|
||||
Cross-Platform: █████████░ 90%
|
||||
|
||||
Binary Size: 2.1 MB (Good)
|
||||
Startup Time: 45ms (Excellent)
|
||||
Test Coverage: 45% (Needs Improvement)
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Next Steps
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. Address critical issues (3 found)
|
||||
2. Review and fix warnings (5 found)
|
||||
3. Improve test coverage to >70%
|
||||
4. Add missing documentation
|
||||
|
||||
Run with specific focus:
|
||||
/cli-review --focus errors
|
||||
/cli-review --focus ux
|
||||
/cli-review --focus tests
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
When you invoke this command:
|
||||
|
||||
1. **Analyze Project Structure**
|
||||
- Identify CLI framework (Clap, structopt, etc.)
|
||||
- Locate main entry point and command definitions
|
||||
- Map out module structure
|
||||
|
||||
2. **Review CLI Interface**
|
||||
- Parse CLI definitions
|
||||
- Check argument naming and documentation
|
||||
- Verify help text quality
|
||||
- Test help output
|
||||
|
||||
3. **Analyze Error Handling**
|
||||
- Review error types
|
||||
- Check error message quality
|
||||
- Verify proper context addition
|
||||
- Test error scenarios
|
||||
|
||||
4. **Check User Experience**
|
||||
- Look for progress indicators
|
||||
- Review color usage
|
||||
- Check interactive prompts
|
||||
- Verify output formatting
|
||||
|
||||
5. **Examine Configuration**
|
||||
- Review config loading
|
||||
- Check precedence implementation
|
||||
- Verify env var support
|
||||
- Test config validation
|
||||
|
||||
6. **Test Cross-Platform Support**
|
||||
- Review path handling
|
||||
- Check platform-specific code
|
||||
- Verify CI configuration
|
||||
- Test on different platforms
|
||||
|
||||
7. **Assess Testing**
|
||||
- Count integration tests
|
||||
- Check test coverage
|
||||
- Review test quality
|
||||
- Identify missing tests
|
||||
|
||||
8. **Generate Report**
|
||||
- Compile findings
|
||||
- Prioritize issues
|
||||
- Provide recommendations
|
||||
- Calculate metrics
|
||||
|
||||
## Implementation
|
||||
|
||||
Use the **rust-cli-developer** agents to perform the review:
|
||||
|
||||
```
|
||||
Use Task tool with subagent_type="rust-cli-developer:cli-ux-specialist"
|
||||
for UX and error message review
|
||||
|
||||
Use Task tool with subagent_type="rust-cli-developer:cli-testing-expert"
|
||||
for test coverage analysis
|
||||
|
||||
Use Task tool with subagent_type="rust-cli-developer:cli-architect"
|
||||
for architecture and cross-platform review
|
||||
|
||||
Use Task tool with subagent_type="rust-cli-developer:clap-expert"
|
||||
for CLI interface review
|
||||
```
|
||||
|
||||
## Focus Options
|
||||
|
||||
### UX Focus
|
||||
|
||||
Reviews only user experience aspects:
|
||||
- Color usage
|
||||
- Progress indicators
|
||||
- Interactive prompts
|
||||
- Output formatting
|
||||
- Error messages
|
||||
|
||||
### Errors Focus
|
||||
|
||||
Reviews only error handling:
|
||||
- Error types
|
||||
- Error messages
|
||||
- Context addition
|
||||
- Exit codes
|
||||
- Recovery strategies
|
||||
|
||||
### Tests Focus
|
||||
|
||||
Reviews only testing:
|
||||
- Integration tests
|
||||
- Test coverage
|
||||
- Test quality
|
||||
- Missing test scenarios
|
||||
- CI configuration
|
||||
|
||||
### Config Focus
|
||||
|
||||
Reviews only configuration:
|
||||
- Config loading
|
||||
- Precedence
|
||||
- Environment variables
|
||||
- Validation
|
||||
- Documentation
|
||||
|
||||
### Performance Focus
|
||||
|
||||
Reviews only performance:
|
||||
- Startup time
|
||||
- Binary size
|
||||
- Memory usage
|
||||
- I/O efficiency
|
||||
- Async usage
|
||||
|
||||
## Notes
|
||||
|
||||
- Review is non-destructive (read-only analysis)
|
||||
- Generates actionable recommendations
|
||||
- Prioritizes issues by impact and effort
|
||||
- Provides code examples for fixes
|
||||
- Can be run in CI for automated checks
|
||||
271
commands/cli-scaffold.md
Normal file
271
commands/cli-scaffold.md
Normal file
@@ -0,0 +1,271 @@
|
||||
---
|
||||
name: cli-scaffold
|
||||
description: Scaffold new Rust CLI projects with Clap, error handling, logging, and testing setup
|
||||
---
|
||||
|
||||
# CLI Scaffold Command
|
||||
|
||||
Scaffold a new Rust CLI application with best practices, proper structure, and all necessary dependencies configured.
|
||||
|
||||
## Arguments
|
||||
|
||||
- `$1` - Project name (required)
|
||||
- `$2` - Project type: "simple", "subcommands", or "plugin" (optional, default: "simple")
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Create a simple single-command CLI
|
||||
/cli-scaffold my-cli simple
|
||||
|
||||
# Create a CLI with subcommands
|
||||
/cli-scaffold my-cli subcommands
|
||||
|
||||
# Create a CLI with plugin architecture
|
||||
/cli-scaffold my-cli plugin
|
||||
```
|
||||
|
||||
## What Gets Created
|
||||
|
||||
The scaffold creates a complete Rust CLI project with:
|
||||
|
||||
### Dependencies
|
||||
|
||||
- **clap** (v4+) with derive feature for argument parsing
|
||||
- **anyhow** for error handling in application code
|
||||
- **thiserror** for library error types
|
||||
- **miette** for beautiful error messages with diagnostics
|
||||
- **tracing** + **tracing-subscriber** for structured logging
|
||||
- **config** for configuration management
|
||||
- **directories** for XDG directory support
|
||||
- **serde** for configuration serialization
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
my-cli/
|
||||
├── Cargo.toml
|
||||
├── src/
|
||||
│ ├── main.rs # Entry point
|
||||
│ ├── lib.rs # Library interface
|
||||
│ ├── cli.rs # CLI definitions
|
||||
│ ├── commands/ # Command implementations
|
||||
│ │ └── mod.rs
|
||||
│ ├── config.rs # Configuration management
|
||||
│ ├── error.rs # Error types
|
||||
│ └── logging.rs # Logging setup
|
||||
├── tests/
|
||||
│ └── integration.rs # Integration tests
|
||||
├── config/
|
||||
│ └── default.toml # Default configuration
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
1. **Clean architecture** - Library-first design, thin CLI wrapper
|
||||
2. **Error handling** - miette for beautiful diagnostics, structured errors
|
||||
3. **Logging** - Tracing with verbosity levels (-v, -vv, -vvv)
|
||||
4. **Configuration** - TOML config with precedence (defaults < file < env < CLI)
|
||||
5. **Testing** - Integration tests with assert_cmd pre-configured
|
||||
6. **Shell completions** - Built-in completion generation
|
||||
7. **Cross-platform** - Works on Windows, macOS, Linux
|
||||
|
||||
## Workflow
|
||||
|
||||
When you invoke this command:
|
||||
|
||||
1. **Gather Information**
|
||||
- Confirm project name
|
||||
- Select project type if not provided
|
||||
- Ask about optional features (async support, additional crates)
|
||||
|
||||
2. **Create Project Structure**
|
||||
- Run `cargo init` to create base project
|
||||
- Set up directory structure (src/, tests/, config/)
|
||||
- Create all necessary source files
|
||||
|
||||
3. **Configure Dependencies**
|
||||
- Add all required dependencies to Cargo.toml
|
||||
- Configure features appropriately
|
||||
- Set up dev-dependencies for testing
|
||||
|
||||
4. **Generate Source Files**
|
||||
- Create main.rs with proper error handling
|
||||
- Set up lib.rs with module exports
|
||||
- Create cli.rs with Clap definitions
|
||||
- Generate command modules based on project type
|
||||
- Set up error types with miette
|
||||
- Configure logging with tracing
|
||||
- Create configuration management code
|
||||
|
||||
5. **Add Testing Infrastructure**
|
||||
- Create integration test file
|
||||
- Add example tests for CLI commands
|
||||
- Configure assert_cmd and assert_fs
|
||||
|
||||
6. **Documentation**
|
||||
- Generate README.md with usage examples
|
||||
- Add inline documentation to code
|
||||
- Include configuration examples
|
||||
|
||||
7. **Finalize**
|
||||
- Run `cargo check` to verify setup
|
||||
- Run `cargo test` to ensure tests pass
|
||||
- Display next steps to user
|
||||
|
||||
## Project Type Details
|
||||
|
||||
### Simple CLI
|
||||
|
||||
Single command application with arguments and flags.
|
||||
|
||||
**Example:**
|
||||
|
||||
```rust
|
||||
// src/cli.rs
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "my-cli")]
|
||||
#[command(version, about, long_about = None)]
|
||||
pub struct Cli {
|
||||
/// Input file
|
||||
#[arg(short, long)]
|
||||
pub input: PathBuf,
|
||||
|
||||
/// Verbosity level
|
||||
#[arg(short, long, action = clap::ArgAction::Count)]
|
||||
pub verbose: u8,
|
||||
}
|
||||
```
|
||||
|
||||
### Subcommands CLI
|
||||
|
||||
Application with multiple subcommands (like git, cargo).
|
||||
|
||||
**Example:**
|
||||
|
||||
```rust
|
||||
// src/cli.rs
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct Cli {
|
||||
#[arg(short, long, global = true, action = clap::ArgAction::Count)]
|
||||
pub verbose: u8,
|
||||
|
||||
#[command(subcommand)]
|
||||
pub command: Command,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum Command {
|
||||
Init { name: String },
|
||||
Build { release: bool },
|
||||
Test { filter: Option<String> },
|
||||
}
|
||||
```
|
||||
|
||||
### Plugin-based CLI
|
||||
|
||||
Extensible architecture with plugin system.
|
||||
|
||||
**Features:**
|
||||
- Plugin trait definition
|
||||
- Plugin registry
|
||||
- Dynamic plugin loading
|
||||
- Plugin command routing
|
||||
|
||||
## Example Output
|
||||
|
||||
After running `/cli-scaffold my-cli subcommands`, you'll see:
|
||||
|
||||
```
|
||||
✓ Created project structure
|
||||
✓ Configured dependencies
|
||||
✓ Generated source files
|
||||
✓ Set up testing infrastructure
|
||||
✓ Created documentation
|
||||
|
||||
Successfully scaffolded 'my-cli'!
|
||||
|
||||
Project structure:
|
||||
my-cli/
|
||||
├── Cargo.toml
|
||||
├── src/
|
||||
│ ├── main.rs
|
||||
│ ├── lib.rs
|
||||
│ ├── cli.rs
|
||||
│ ├── commands/
|
||||
│ │ ├── mod.rs
|
||||
│ │ ├── init.rs
|
||||
│ │ ├── build.rs
|
||||
│ │ └── test.rs
|
||||
│ ├── config.rs
|
||||
│ ├── error.rs
|
||||
│ └── logging.rs
|
||||
├── tests/
|
||||
│ └── integration.rs
|
||||
└── README.md
|
||||
|
||||
Next steps:
|
||||
cd my-cli
|
||||
cargo build
|
||||
cargo test
|
||||
cargo run -- --help
|
||||
|
||||
Features included:
|
||||
• Clap v4+ for argument parsing
|
||||
• miette for beautiful error messages
|
||||
• tracing for structured logging
|
||||
• Configuration management (TOML)
|
||||
• Integration tests with assert_cmd
|
||||
• Shell completion generation
|
||||
|
||||
To add your logic:
|
||||
1. Edit src/commands/*.rs to implement commands
|
||||
2. Add tests in tests/integration.rs
|
||||
3. Update config/default.toml if needed
|
||||
|
||||
Documentation:
|
||||
• See README.md for usage examples
|
||||
• Run with --help to see all options
|
||||
• Use RUST_LOG=debug for detailed logs
|
||||
```
|
||||
|
||||
## Additional Options
|
||||
|
||||
You can customize the scaffold with these options:
|
||||
|
||||
- `--async` - Add tokio runtime for async operations
|
||||
- `--database` - Add sqlx for database support
|
||||
- `--http` - Add reqwest for HTTP client functionality
|
||||
- `--template <name>` - Use a custom template
|
||||
|
||||
## Implementation
|
||||
|
||||
Use the **rust-cli-developer** agent (any of the specialized agents as needed) to:
|
||||
|
||||
1. Validate inputs and gather requirements
|
||||
2. Generate the complete project structure
|
||||
3. Create all source files with proper implementations
|
||||
4. Set up testing and documentation
|
||||
5. Verify the project builds and tests pass
|
||||
|
||||
Invoke the agent with:
|
||||
|
||||
```
|
||||
Use Task tool with subagent_type="rust-cli-developer:cli-architect"
|
||||
```
|
||||
|
||||
The agent will handle all the implementation details and ensure the scaffolded project follows best practices for Rust CLI applications.
|
||||
|
||||
## Notes
|
||||
|
||||
- Projects are created in the current directory
|
||||
- Will fail if directory already exists (safety check)
|
||||
- Generated code includes inline documentation
|
||||
- All dependencies use latest stable versions
|
||||
- Cross-platform compatibility is ensured
|
||||
- Follows Rust API guidelines
|
||||
592
commands/cli-test.md
Normal file
592
commands/cli-test.md
Normal file
@@ -0,0 +1,592 @@
|
||||
---
|
||||
name: cli-test
|
||||
description: Generate comprehensive tests for Rust CLI applications including integration, snapshot, and property-based tests
|
||||
---
|
||||
|
||||
# CLI Test Command
|
||||
|
||||
Generate comprehensive test suites for Rust CLI applications, including integration tests, snapshot tests for output, and property-based tests for input validation.
|
||||
|
||||
## Arguments
|
||||
|
||||
- `$1` - Test type: "integration", "snapshot", "property", or "all" (required)
|
||||
- `$2` - Path to project directory (optional, defaults to current directory)
|
||||
- `--command <name>` - Specific command to test (optional)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Generate all test types
|
||||
/cli-test all
|
||||
|
||||
# Generate integration tests only
|
||||
/cli-test integration
|
||||
|
||||
# Generate snapshot tests for specific command
|
||||
/cli-test snapshot --command build
|
||||
|
||||
# Generate property-based tests
|
||||
/cli-test property
|
||||
|
||||
# Test specific project
|
||||
/cli-test all /path/to/my-cli
|
||||
```
|
||||
|
||||
## Test Types
|
||||
|
||||
### 1. Integration Tests
|
||||
|
||||
Tests that run the actual CLI binary with different arguments and verify output, exit codes, and side effects.
|
||||
|
||||
**Generated Tests:**
|
||||
|
||||
```rust
|
||||
// tests/integration_tests.rs
|
||||
use assert_cmd::Command;
|
||||
use assert_fs::prelude::*;
|
||||
use predicates::prelude::*;
|
||||
|
||||
fn cmd() -> Command {
|
||||
Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_help_flag() {
|
||||
cmd().arg("--help")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("Usage:"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_version_flag() {
|
||||
cmd().arg("--version")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains(env!("CARGO_PKG_VERSION")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_argument() {
|
||||
cmd().arg("--invalid")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("unexpected argument"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_missing_required_arg() {
|
||||
cmd().arg("build")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("required arguments"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_command_with_file_io() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let temp = assert_fs::TempDir::new()?;
|
||||
let input = temp.child("input.txt");
|
||||
input.write_str("test content")?;
|
||||
|
||||
let output = temp.child("output.txt");
|
||||
|
||||
cmd()
|
||||
.arg("process")
|
||||
.arg(input.path())
|
||||
.arg("--output")
|
||||
.arg(output.path())
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
output.assert(predicate::path::exists());
|
||||
output.assert(predicate::str::contains("TEST CONTENT"));
|
||||
|
||||
temp.close()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exit_code_config_error() {
|
||||
cmd()
|
||||
.arg("--config")
|
||||
.arg("/nonexistent/config.toml")
|
||||
.assert()
|
||||
.code(2)
|
||||
.failure();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_var_override() -> Result<(), Box<dyn std::error::Error>> {
|
||||
cmd()
|
||||
.env("MYAPP_PORT", "9000")
|
||||
.arg("config")
|
||||
.arg("show")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("9000"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Snapshot Tests
|
||||
|
||||
Tests that capture and compare command output to saved snapshots, useful for help text, formatted output, and error messages.
|
||||
|
||||
**Generated Tests:**
|
||||
|
||||
```rust
|
||||
// tests/snapshots.rs
|
||||
use assert_cmd::Command;
|
||||
use insta::{assert_snapshot, with_settings};
|
||||
|
||||
fn cmd() -> Command {
|
||||
Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_help_output() {
|
||||
let output = cmd()
|
||||
.arg("--help")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert_snapshot!(String::from_utf8_lossy(&output.stdout));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_command_help() {
|
||||
let output = cmd()
|
||||
.arg("build")
|
||||
.arg("--help")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert_snapshot!("build_help", String::from_utf8_lossy(&output.stdout));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_version_output() {
|
||||
let output = cmd()
|
||||
.arg("--version")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert_snapshot!(String::from_utf8_lossy(&output.stdout));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_message_format() {
|
||||
let output = cmd()
|
||||
.arg("build")
|
||||
.arg("--invalid-option")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert_snapshot!(String::from_utf8_lossy(&output.stderr));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_formatted_output_with_filters() {
|
||||
let output = cmd()
|
||||
.arg("status")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
|
||||
// Filter out timestamps and dynamic data
|
||||
with_settings!({
|
||||
filters => vec![
|
||||
(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}", "[TIMESTAMP]"),
|
||||
(r"Duration: \d+ms", "Duration: [TIME]"),
|
||||
(r"/[^\s]+/([^/\s]+)", "/path/to/$1"),
|
||||
]
|
||||
}, {
|
||||
assert_snapshot!(stdout);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_output() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let output = cmd()
|
||||
.arg("list")
|
||||
.output()?;
|
||||
|
||||
with_settings!({
|
||||
filters => vec![
|
||||
(r"\d{4}-\d{2}-\d{2}", "[DATE]"),
|
||||
]
|
||||
}, {
|
||||
assert_snapshot!(String::from_utf8_lossy(&output.stdout));
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Property-Based Tests
|
||||
|
||||
Tests that verify CLI behavior across a wide range of inputs using property-based testing.
|
||||
|
||||
**Generated Tests:**
|
||||
|
||||
```rust
|
||||
// tests/property_tests.rs
|
||||
use assert_cmd::Command;
|
||||
use proptest::prelude::*;
|
||||
|
||||
fn cmd() -> Command {
|
||||
Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap()
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_port_validation(port in 0u16..=65535) {
|
||||
let result = cmd()
|
||||
.arg("--port")
|
||||
.arg(port.to_string())
|
||||
.arg("validate")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
if (1024..=65535).contains(&port) {
|
||||
assert!(result.status.success(),
|
||||
"Port {} should be valid", port);
|
||||
} else {
|
||||
assert!(!result.status.success(),
|
||||
"Port {} should be invalid", port);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_input_handling(s in "\\PC{0,100}") {
|
||||
// CLI should handle any valid Unicode string without panicking
|
||||
let result = cmd()
|
||||
.arg("--name")
|
||||
.arg(&s)
|
||||
.arg("test")
|
||||
.output();
|
||||
|
||||
// Should not panic, even if it returns an error
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_path_handling(
|
||||
parts in prop::collection::vec("[a-zA-Z0-9_-]{1,10}", 1..5)
|
||||
) {
|
||||
let path = parts.join("/");
|
||||
|
||||
let _result = cmd()
|
||||
.arg("--path")
|
||||
.arg(&path)
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
// Should handle various path structures without panicking
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_numeric_range_validation(n in -1000i32..1000i32) {
|
||||
let result = cmd()
|
||||
.arg("--count")
|
||||
.arg(n.to_string())
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
if n >= 0 {
|
||||
assert!(result.status.success() ||
|
||||
String::from_utf8_lossy(&result.stderr).contains("out of range"),
|
||||
"Non-negative number should be handled");
|
||||
} else {
|
||||
assert!(!result.status.success(),
|
||||
"Negative number should be rejected");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_argument(items in prop::collection::vec("[a-z]{3,8}", 0..10)) {
|
||||
let result = cmd()
|
||||
.arg("process")
|
||||
.args(&items)
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
// Should handle 0 to many items
|
||||
assert!(result.status.success() || result.status.code() == Some(3));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Interactive Prompt Tests
|
||||
|
||||
Tests for interactive CLI features.
|
||||
|
||||
**Generated Tests:**
|
||||
|
||||
```rust
|
||||
// tests/interactive_tests.rs
|
||||
use assert_cmd::Command;
|
||||
|
||||
#[test]
|
||||
fn test_confirmation_prompt_yes() -> Result<(), Box<dyn std::error::Error>> {
|
||||
cmd()
|
||||
.arg("delete")
|
||||
.arg("resource")
|
||||
.write_stdin("yes\n")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("Deleted"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_confirmation_prompt_no() -> Result<(), Box<dyn std::error::Error>> {
|
||||
cmd()
|
||||
.arg("delete")
|
||||
.arg("resource")
|
||||
.write_stdin("no\n")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("Cancelled"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_yes_flag_skips_prompt() -> Result<(), Box<dyn std::error::Error>> {
|
||||
cmd()
|
||||
.arg("delete")
|
||||
.arg("resource")
|
||||
.arg("--yes")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("Deleted"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_interactive_mode() -> Result<(), Box<dyn std::error::Error>> {
|
||||
cmd()
|
||||
.arg("delete")
|
||||
.env("CI", "true")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("non-interactive"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Cross-Platform Tests
|
||||
|
||||
Platform-specific tests for compatibility.
|
||||
|
||||
**Generated Tests:**
|
||||
|
||||
```rust
|
||||
// tests/cross_platform_tests.rs
|
||||
use assert_cmd::Command;
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "windows")]
|
||||
fn test_windows_paths() -> Result<(), Box<dyn std::error::Error>> {
|
||||
cmd()
|
||||
.arg("--path")
|
||||
.arg(r"C:\Users\test\file.txt")
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn test_unix_paths() -> Result<(), Box<dyn std::error::Error>> {
|
||||
cmd()
|
||||
.arg("--path")
|
||||
.arg("/home/test/file.txt")
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cross_platform_path_handling() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let temp = assert_fs::TempDir::new()?;
|
||||
let file = temp.child("test.txt");
|
||||
file.write_str("content")?;
|
||||
|
||||
cmd()
|
||||
.arg("process")
|
||||
.arg(file.path())
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
temp.close()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "windows")]
|
||||
fn test_windows_line_endings() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let temp = assert_fs::TempDir::new()?;
|
||||
let input = temp.child("input.txt");
|
||||
input.write_str("line1\r\nline2\r\nline3")?;
|
||||
|
||||
cmd()
|
||||
.arg("process")
|
||||
.arg(input.path())
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
temp.close()?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Test Organization
|
||||
|
||||
Generated tests are organized into separate files:
|
||||
|
||||
```
|
||||
tests/
|
||||
├── integration_tests.rs # Basic integration tests
|
||||
├── snapshots.rs # Snapshot tests
|
||||
├── property_tests.rs # Property-based tests
|
||||
├── interactive_tests.rs # Interactive prompt tests
|
||||
├── cross_platform_tests.rs # Platform-specific tests
|
||||
└── snapshots/ # Saved snapshots (insta)
|
||||
├── snapshots__help_output.snap
|
||||
├── snapshots__build_help.snap
|
||||
└── ...
|
||||
```
|
||||
|
||||
## Dependencies Added
|
||||
|
||||
```toml
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2"
|
||||
assert_fs = "1"
|
||||
predicates = "3"
|
||||
insta = "1"
|
||||
proptest = "1"
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
When you invoke this command:
|
||||
|
||||
1. **Analyze CLI Structure**
|
||||
- Parse CLI definitions (Clap structure)
|
||||
- Identify commands and subcommands
|
||||
- Extract argument definitions
|
||||
- Find file I/O operations
|
||||
|
||||
2. **Generate Test Structure**
|
||||
- Create test directory if needed
|
||||
- Set up test modules
|
||||
- Add necessary dependencies
|
||||
|
||||
3. **Generate Tests Based on Type**
|
||||
- **Integration**: Tests for each command, success/failure paths
|
||||
- **Snapshot**: Capture help text, error messages, formatted output
|
||||
- **Property**: Input validation, edge cases
|
||||
- **Interactive**: Prompt handling, --yes flag
|
||||
- **Cross-platform**: Path handling, line endings
|
||||
|
||||
4. **Create Test Fixtures**
|
||||
- Sample input files
|
||||
- Config files for testing
|
||||
- Expected output files
|
||||
|
||||
5. **Generate Helper Functions**
|
||||
- Command builder helper
|
||||
- Common assertions
|
||||
- Fixture setup/teardown
|
||||
|
||||
6. **Verify Tests**
|
||||
- Run generated tests
|
||||
- Ensure they pass
|
||||
- Report any issues
|
||||
|
||||
7. **Generate Documentation**
|
||||
- Add comments explaining tests
|
||||
- Document test organization
|
||||
- Provide examples of adding more tests
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
✓ Analyzed CLI structure
|
||||
✓ Found 3 commands: init, build, test
|
||||
✓ Generated integration tests (12 tests)
|
||||
✓ Generated snapshot tests (8 tests)
|
||||
✓ Generated property-based tests (5 tests)
|
||||
✓ Generated interactive tests (4 tests)
|
||||
✓ Generated cross-platform tests (6 tests)
|
||||
✓ Added test dependencies to Cargo.toml
|
||||
✓ Created test fixtures
|
||||
|
||||
Test Suite Generated Successfully!
|
||||
|
||||
Files created:
|
||||
tests/integration_tests.rs (12 tests)
|
||||
tests/snapshots.rs (8 tests)
|
||||
tests/property_tests.rs (5 tests)
|
||||
tests/interactive_tests.rs (4 tests)
|
||||
tests/cross_platform_tests.rs (6 tests)
|
||||
|
||||
Total: 35 tests
|
||||
|
||||
Run tests:
|
||||
cargo test
|
||||
|
||||
Run specific test file:
|
||||
cargo test --test integration_tests
|
||||
|
||||
Update snapshots (if needed):
|
||||
cargo insta review
|
||||
|
||||
Coverage:
|
||||
• All CLI commands tested
|
||||
• Success and failure paths covered
|
||||
• Help text snapshots captured
|
||||
• Input validation tested
|
||||
• Cross-platform compatibility verified
|
||||
|
||||
Next steps:
|
||||
1. Review generated tests
|
||||
2. Run: cargo test
|
||||
3. Add custom test cases as needed
|
||||
4. Update snapshots: cargo insta review
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
Use the **rust-cli-developer:cli-testing-expert** agent to:
|
||||
|
||||
1. Analyze the CLI structure
|
||||
2. Generate appropriate tests
|
||||
3. Set up test infrastructure
|
||||
4. Create fixtures and helpers
|
||||
5. Verify tests run correctly
|
||||
|
||||
Invoke with:
|
||||
|
||||
```
|
||||
Use Task tool with subagent_type="rust-cli-developer:cli-testing-expert"
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Generated tests are starting points; customize as needed
|
||||
- Snapshot tests require manual review on first run
|
||||
- Property tests may need adjustment for specific domains
|
||||
- Interactive tests require stdin support
|
||||
- Cross-platform tests should run in CI on multiple platforms
|
||||
- Tests are non-destructive and use temporary directories
|
||||
Reference in New Issue
Block a user