Skill: CLI Guideline
Modern CLI design principles based on clig.dev — human-first design while maintaining composability.
Core Philosophy
- Human-first design — CLIs are for humans, not just scripts
- Simple parts that work together — Composable via pipes, stdin/stdout, exit codes
- Consistency — Follow established conventions (flags, env vars, behavior)
- Say just enough — Not too verbose, not too silent
- Ease of discovery — Help, examples, suggestions
- Conversation as norm — Trial-and-error is expected; guide the user
- Robustness — Handle errors gracefully, feel solid
- Empathy — Delight users, exceed expectations
Essential Rules
I/O Streams
| Stream | Purpose | Example |
|---|---|---|
stdout |
Primary output, data, machine-readable | Results, JSON, piped data |
stderr |
Messages, logs, progress, errors | Status, warnings, spinners |
Exit 0 |
Success | |
Exit ≠0 |
Failure (map codes to failure modes) |
Help
-hand--helpshow full help (exit 0, ignore other flags)- No args + required args → concise help (description + 1-2 examples + "use --help")
- Lead with examples, not option lists
- Include support path (URL, issue tracker)
- Suggest likely fixes on typos ("Did you mean...?")
- If expecting stdin and it's a TTY, show help immediately (don't hang)
Output
- Human-readable by default; detect TTY for formatting
--plainfor stable, line-based output (scripts, grep/awk)--jsonfor structured output- Brief success messages; verbose only when state changes
- Suggest next commands in workflows
- Color: intentional (not decorative); disable via
NO_COLOR,TERM=dumb,--no-color - No animations/spinners when stdout is not TTY
- No debug noise by default; use
--debugorDEBUG=1
Errors
- Rewrite for humans: what happened + how to fix
- High signal-to-noise; group similar errors
- Important info at the end (eye drawn there)
- Unexpected errors → debug path + bug report instructions
Arguments & Flags
- Prefer flags over positional args
- Full-length flags for everything (
--help, not just-h) - One-letter flags for common options only
- Standard names:
--help,--version,--dry-run,--verbose,--json,--force,--output,--quiet,--debug,--no-input - Sensible defaults (right thing for most users)
- Prompt for missing input (TTY only); never require prompts
- Confirm dangerous actions; support
--dry-run -for stdin/stdout when flag takes a file- Order-independent flags/subcommands when possible
- Never accept secrets via flags (use
--password-fileor stdin)
Interactivity
- Prompt only when stdin is TTY
- Support
--no-inputto disable all prompts - Password prompts: don't echo
- Ctrl-C always exits quickly
Subcommands
- Consistent flags across subcommands
- Consistent naming pattern (noun verb or verb noun)
- Avoid ambiguous names (update vs upgrade)
Robustness
- Validate input early; fail fast with clear message
- Responsive < 100ms; show progress for long ops
- Network calls timeout (configurable)
- Recoverable on rerun; crash-only design
- Handle misuse (scripts, bad networks, concurrent instances)
Future-Proofing
- Treat interfaces as contracts (flags, env vars, config, output)
- Additive changes preferred
- Warn before breaking changes; deprecate gracefully
- No catch-all subcommand (blocks future commands)
- No arbitrary abbreviations of subcommands
Signals
- Ctrl-C: exit immediately, acknowledge, timeout cleanup
- Second Ctrl-C: force stop (document behavior)
Configuration
- Precedence: flags > env > project config > user config > system config
- Follow XDG spec (
~/.config/...) - Never silently edit other programs' config
Environment Variables
- Names:
UPPERCASE_WITH_UNDERSCORES(no leading digit) - Single-line values preferred
- Respect:
NO_COLOR,FORCE_COLOR,DEBUG,EDITOR,HTTP_PROXY,PAGER,HOME,TMPDIR - Read
.envfor project context (not as full config) - Never read secrets from env vars (use files/stdin)
Naming
- Simple, memorable, lowercase
- Dashes only if needed; short but not cryptic
- Easy to type
Distribution
- Single binary when possible
- Easy uninstall (document it)
Analytics
- No telemetry without consent (opt-in preferred)
- Transparent collection; consider alternatives
Quick Reference
For detailed stress-testing checklist, see references/checklist.md.
For complete guidelines with examples, see references/full-guidelines.md.
Language-Specific Notes
Python
- Use
argparse,click, ortyper - Return exit codes from
main(); useraise SystemExit(main()) - No tracebacks for expected errors;
--debugfor unexpected - Handle Ctrl-C cleanly (exit 130, no traceback)
- Separate TTY detection for stdout vs stderr
TypeScript/Node
- Use
commander,yargs,oclif, ornode:util parseArgs - Separate stdout/stderr; handle async failures
--debugfor stack traces- Avoid premature
process.exit(); preferprocess.exitCode
Bash
- Shebang matches features (
#!/usr/bin/env bashfor Bash-only) - Robust flag parsing; handle quotes/whitespace
trapfor cleanup;mktempfor temp files- Lint with
shellcheck - use skill:
shellck