Building CLIs
Build professional command-line interfaces across Python, Go, and Rust using modern frameworks with robust argument parsing, configuration management, and shell integration.
When to Use This Skill
Use this skill when:
- •Building developer tooling or automation CLIs
- •Creating infrastructure management tools (deployment, monitoring)
- •Implementing API client command-line tools
- •Adding CLI capabilities to existing projects
- •Packaging utilities for distribution (PyPI, Homebrew, binary releases)
Common triggers: "create a CLI tool", "build a command-line interface", "add CLI arguments", "parse command-line options", "generate shell completions"
Framework Selection
Quick Decision Guide
Python Projects:
- •Typer (recommended): Modern type-safe CLIs with minimal boilerplate
- •Click: Mature, flexible CLIs for complex command hierarchies
Go Projects:
- •Cobra (recommended): Industry standard for enterprise tools (Kubernetes, Docker, GitHub CLI)
- •urfave/cli: Lightweight alternative for simple CLIs
Rust Projects:
- •clap v4 (recommended): Type-safe with derive API or builder API for runtime flexibility
For detailed framework comparison and selection criteria, see references/framework-selection.md.
Core Patterns
Arguments vs. Options vs. Flags
Positional Arguments:
- •Primary input, identified by position
- •Use for required inputs (max 2-3 arguments)
- •Example:
convert input.jpg output.png
Options:
- •Named parameters with values
- •Use for configuration and optional inputs
- •Example:
--output file.txt,--config app.yaml
Flags:
- •Boolean options (presence = true)
- •Use for switches and toggles
- •Example:
--verbose,--dry-run,--force
Decision Matrix:
| Use Case | Type | Example |
|---|---|---|
| Primary required input | Positional Argument | git commit -m "message" |
| Optional configuration | Option | --config app.yaml |
| Boolean setting | Flag | --verbose, --force |
| Multiple values | Variadic Argument | files... |
See references/argument-patterns.md for comprehensive parsing patterns.
Subcommand Organization
Flat Structure (1 Level):
app command1 [args] app command2 [args]
Use for: Small CLIs with 5-10 operations
Grouped Structure (2 Levels):
app group subcommand [args]
Use for: Medium CLIs with logical groupings (10-30 commands)
Example: kubectl get pods, kubectl create deployment
Nested Structure (3+ Levels):
app group subgroup command [args]
Use for: Large CLIs with deep hierarchies (30+ commands)
Example: gcloud compute instances create
See references/subcommand-design.md for structuring strategies.
Configuration Management
Standard Precedence (Highest to Lowest):
- •CLI Arguments/Flags (explicit user input)
- •Environment Variables (session overrides)
- •Config File - Local (
./config.yaml) - •Config File - User (
~/.config/app/config.yaml) - •Config File - System (
/etc/app/config.yaml) - •Built-in Defaults (hardcoded)
Best Practices:
- •Document precedence in
--help - •Validate config files before execution
- •Provide
--print-configto show effective configuration - •Use XDG Base Directory (
~/.config/app/) for config files
See references/configuration-management.md for implementation patterns across languages.
Output Formatting
Format Selection:
| Use Case | Format | When |
|---|---|---|
| Human consumption | Colored text, tables | Default interactive mode |
| Machine consumption | JSON, YAML | --output json, piping |
| Logging/debugging | Plain text | --verbose, stderr |
| Progress tracking | Progress bars, spinners | Long operations |
Best Practices:
- •Default to human-readable output
- •Provide
--outputflag (json, yaml, table) - •Use stderr for logs, stdout for data
- •Auto-detect TTY (disable colors if not interactive)
- •Use exit codes: 0 = success, 1 = error, 2 = usage error
See references/output-formatting.md for formatting strategies.
Language-Specific Quick Starts
Python with Typer
Installation:
pip install "typer[all]" # Includes rich for colored output
Basic Example:
import typer
from typing import Annotated
app = typer.Typer()
@app.command()
def greet(
name: Annotated[str, typer.Argument(help="Name to greet")],
formal: Annotated[bool, typer.Option(help="Use formal greeting")] = False
):
"""Greet someone with a message."""
greeting = "Good day" if formal else "Hello"
typer.echo(f"{greeting}, {name}!")
if __name__ == "__main__":
app()
Key Features:
- •Type hints for automatic validation
- •Minimal boilerplate with decorators
- •Auto-generated help text
- •Rich integration for colored output
See examples/python/ for complete working examples including subcommands, config management, and interactive features.
Go with Cobra
Installation:
go get -u github.com/spf13/cobra@latest
Basic Example:
var rootCmd = &cobra.Command{
Use: "greet [name]",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Hello, %s!\n", args[0])
},
}
rootCmd.Flags().Bool("formal", false, "Use formal greeting")
rootCmd.Execute()
Key Features:
- •POSIX-compliant flags
- •Viper integration for configuration
- •Subcommand architecture
- •Shell completion generation
See examples/go/ for complete working examples including Viper config and multi-level subcommands.
Rust with clap
Installation (Cargo.toml):
[dependencies]
clap = { version = "4.5", features = ["derive"] }
Basic Example (Derive API):
use clap::Parser;
#[derive(Parser)]
#[command(about = "Greet someone")]
struct Cli {
/// Name to greet
name: String,
/// Use formal greeting
#[arg(long)]
formal: bool,
}
fn main() {
let cli = Cli::parse();
let greeting = if cli.formal { "Good day" } else { "Hello" };
println!("{}, {}!", greeting, cli.name);
}
Key Features:
- •Compile-time type safety
- •Derive API (declarative) or Builder API (programmatic)
- •Comprehensive validation
- •Performance optimized
See examples/rust/ for complete working examples including subcommands and builder API patterns.
Interactive Features
Progress Indicators
Python (rich):
from rich.progress import track
for _ in track(range(100), description="Processing..."):
time.sleep(0.01)
Go (progressbar):
import "github.com/schollz/progressbar/v3"
bar := progressbar.Default(100)
for i := 0; i < 100; i++ {
bar.Add(1)
}
Rust (indicatif):
use indicatif::ProgressBar;
let bar = ProgressBar::new(100);
for _ in 0..100 {
bar.inc(1);
}
Prompts and Confirmations
Python:
confirm = typer.confirm("Are you sure?")
if not confirm:
raise typer.Abort()
Go:
reader := bufio.NewReader(os.Stdin)
fmt.Print("Are you sure? (y/n): ")
response, _ := reader.ReadString('\n')
Rust:
use dialoguer::Confirm;
if Confirm::new().with_prompt("Are you sure?").interact()? {
// Proceed
}
Shell Completion
Generating Completions
Python (Typer):
_MYAPP_COMPLETE=bash_source myapp > ~/.myapp-complete.bash _MYAPP_COMPLETE=zsh_source myapp > ~/.myapp-complete.zsh
Go (Cobra):
rootCmd.AddCommand(&cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
switch args[0] {
case "bash":
rootCmd.GenBashCompletion(os.Stdout)
case "zsh":
rootCmd.GenZshCompletion(os.Stdout)
}
},
})
Rust (clap):
use clap_complete::{generate, shells::Bash};
generate(Bash, &mut Cli::command(), "myapp", &mut io::stdout())
See references/shell-completion.md for installation instructions.
Distribution and Packaging
Python (PyPI)
pyproject.toml:
[project]
name = "myapp"
version = "1.0.0"
scripts = { myapp = "myapp.cli:app" }
Publish:
pip install build twine python -m build twine upload dist/*
Go (Homebrew)
Formula:
class Myapp < Formula
desc "My CLI application"
url "https://github.com/user/myapp/archive/v1.0.0.tar.gz"
def install
system "go", "build", "-o", bin/"myapp"
end
end
Rust (Cargo)
Publish:
cargo login cargo publish
Installation:
cargo install myapp
See references/distribution.md for comprehensive packaging strategies including binary releases.
Best Practices
Universal CLI Conventions
Always Provide:
- •
--helpand-hfor usage information - •
--versionand-Vfor version display - •Clear error messages with actionable suggestions
Argument Handling:
- •Use
--separator for options vs. positional args - •Support both short (
-v) and long (--verbose) forms - •Validate and sanitize all user inputs
Error Handling:
- •Exit code 0 for success
- •Exit code 1 for general errors
- •Exit code 2 for usage errors
- •Write errors to stderr, data to stdout
Interactivity:
- •Detect TTY (interactive vs. piped input)
- •Provide
--yes/--forceto skip prompts for automation - •Show progress for operations longer than 2 seconds
Configuration Best Practices
File Formats:
- •Use YAML, TOML, or JSON consistently
- •Separate files per environment (dev, staging, prod)
- •Validate configuration in CI/CD with
--check-config
Secret Management:
- •Never commit secrets to config files
- •Use environment variables or secret managers
- •Document required environment variables
Precedence:
- •CLI args > env vars > config file > defaults
- •Document precedence in help text
- •Provide
--print-configto show effective configuration
Integration with Other Skills
testing-strategies:
- •CLI testing with mocks and fixtures
- •Integration tests for end-to-end workflows
- •See examples/python/test_cli.py
building-ci-pipelines:
- •Binary builds for multiple platforms
- •Automated releases via GitHub Actions
- •See references/distribution.md
api-patterns:
- •Building API client CLIs
- •Authentication and token management
- •Formatting API responses
secret-management:
- •Secure credential storage
- •Environment variable integration
- •Vault/secrets manager integration
Reference Files
Decision Frameworks:
- •framework-selection.md - Which framework to choose
- •argument-patterns.md - Arguments vs. options vs. flags
- •subcommand-design.md - Structuring command hierarchies
Implementation Guides:
- •configuration-management.md - Config files and precedence
- •output-formatting.md - Human vs. machine-readable output
- •shell-completion.md - Generating completions
- •distribution.md - Packaging and releasing CLIs
Code Examples:
- •examples/python/ - Typer examples (basic, subcommands, config, interactive)
- •examples/go/ - Cobra examples (basic, subcommands, Viper integration)
- •examples/rust/ - clap examples (derive, builder, subcommands)
Quick Reference
Framework Recommendations:
- •Python: Typer (modern) or Click (mature)
- •Go: Cobra (enterprise) or urfave/cli (simple)
- •Rust: clap v4 (derive or builder)
Common Patterns:
- •Arguments: Primary inputs (max 2-3)
- •Options: Named parameters with values
- •Flags: Boolean switches
- •Subcommands: Group related operations
- •Config: CLI args > env vars > files > defaults
Output Standards:
- •Default: Human-readable (colored, tables)
- •Machine: JSON/YAML via
--outputflag - •Errors: stderr, data: stdout
- •Exit: 0 = success, 1 = error, 2 = usage
Distribution:
- •Python: PyPI (
pip install) - •Go: Homebrew, binary releases
- •Rust: Cargo (
cargo install), binary releases