agentsclimarketplace

Cli tool architect

Skill ralvarezdev/ralvaskills/skills/tooling/cli-tool-architect

Cross-language CLI standards — subcommand structure, flag/env/config/default precedence, TOML in XDG, stdout-data/stderr-logs split, --output json|yaml, exit codes, NO_COLOR, completions. Go (cobra+pflag+viper) and Python (typer) recipes. Use when designing or reviewing a CLI.From its SKILL.md

Install
npx -y skills add ralvarezdev/ralvaskills --skill cli-tool-architect

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

11.4 KB, ~3.0k tokens by cl100k_base, as published. Nobody here has run it

CLI Tool Architecture

Language-agnostic conventions for command-line tools. Go-specific recipes use spf13/cobra + spf13/pflag + spf13/viper; Python-specific recipes use typer. Per-language stacks are canonical in go-architect and python-architect. See STACK.md for additional CLI-specific libraries (output styling, alternative loggers).

1. Command structure

  • Root + subcommands. tool <subcommand> [args] [flags]. Mirrors git, kubectl, docker, rsk. Top-level flags are global; subcommand flags are scoped to that subcommand.
  • One verb per subcommand. tool create user is fine. tool create-user-now is not.
  • Hierarchy when it reads naturally: tool resource action (e.g. kubectl get pods, aws s3 ls). Skip if your tool has fewer than ~5 commands — flatten.
  • tool alone (no subcommand) prints help. Never run "the default action" — implicit behavior is the source of surprise commits and accidental deploys.
  • Names: short, lowercase, single-word where possible. get, list, apply, delete. Two words if they're a phrase: get-config.

2. Arguments vs flags

  • Positional arguments for the required noun of the verb: tool delete <user-id>. Use sparingly — every extra positional reduces clarity.
  • Flags for everything else. Required flags exist; mark them with Required: true (cobra) / Option(...) (typer) and document explicitly.
  • No more than 2 positional args in most cases. Beyond that, switch to flags.
  • Short flags only for the top 5 most-used. -v, -h, -f, -o. Don't shortify every flag — tool deploy -e prod -r us-east-1 -p high -d becomes unreadable; long form is self-documenting.
  • Boolean flags are pure switches: --dry-run, not --dry-run=true. Negate with --no-foo if both states need an explicit form.
  • Plural for repeatable flags: --tag debug --tag perf becomes --tags debug,perf or --tag repeatable. Pick one and document.

3. Flag / env / config-file / defaults precedence

Configuration values can come from four places. The order is fixed and inviolable:

flag > env var > config file > built-in default
  • A --log-level=debug flag wins over TOOL_LOG_LEVEL=info env wins over log_level = "warn" in the config wins over the compiled-in default (info).
  • Env-var prefix is the tool name in upper-snake: TOOL_LOG_LEVEL, TOOL_API_URL. Document the prefix in --help.
  • Show effective config: every CLI should have a tool config show (or tool config --show) that prints the resolved values and where each came from (flag / env / file / default). This is the difference between "user can diagnose" and "user files a bug ticket".

4. Configuration file — TOML in XDG location

  • Default format: TOML — readable, supports comments, modern default (Rust/Cargo, Python pyproject.toml, uv.lock).
  • Default location: XDG Base Directory spec.
    • Config: $XDG_CONFIG_HOME/<tool>/config.toml, falling back to ~/.config/<tool>/config.toml
    • Cache: $XDG_CACHE_HOME/<tool>/, falling back to ~/.cache/<tool>/
    • Data: $XDG_DATA_HOME/<tool>/, falling back to ~/.local/share/<tool>/
    • Windows: %APPDATA%\<tool>\config.toml (per XDG-on-Windows convention translated to platform).
  • Discovery order for the config file:
    1. --config <path> flag (explicit override)
    2. TOOL_CONFIG env var
    3. ./<tool>.toml (project-local; useful for tools that have per-repo config)
    4. $XDG_CONFIG_HOME/<tool>/config.toml (user-global)
  • One canonical filename per tool. Don't accept .tool.toml, tool.config.toml, config.toml, .toolrc all at once. Pick one and stick to it.
  • tool init writes a sane default config to the canonical location; refuse to overwrite without --force.

Note: rsk itself uses ~/.config/rsk/config.json (JSON) for legacy / parsing reasons; TOML is the recommended default for new CLIs.

5. Help text discipline

Every command, subcommand, and flag has documented help. Help is the API surface.

  • tool --help lists subcommands with a one-line description each.
  • tool <subcommand> --help has: short description, usage line, flags grouped (Required, Common, Global), and at least one example at the bottom.
  • Examples drive understanding — newcomers read examples before they read flag tables. Include 2–3 realistic invocations per subcommand.
  • Line length 80 chars in help text — terminals are still ~80 cols by default.
  • No marketing copy. Short, factual, actionable.

6. Output discipline — stdout for data, stderr for logs

Strict separation. Always.

  • Data → stdout. The thing the user wants — JSON, the filename created, the resource ID, the table.
  • Logs, progress, errors → stderr. Anything the user doesn't want piped into the next command.
  • Errors that prevent producing data must exit non-zero (see §8). Don't print an error to stdout and exit 0.

Non-negotiable. Tools that mix the streams break every shell pipeline. Concrete pipe examples in RECIPES § Output discipline.

7. Output formats — --output json|yaml mandatory on list/get

Human-readable text is the default. Every command returning structured data must also support --output json and --output yaml.

  • -o short form is standard (kubectl, gh, oc).
  • JSON output is stable and documented — clients depend on it. Schema changes are breaking.
  • JSONL for list operations — one line per record, so tool list -o json | grep works.
  • YAML for human eyeballing of nested data; rarely useful for pipes.

Invocation examples in RECIPES § Output format flag examples.

8. Exit codes

Standard semantics across the ecosystem — full table in RECIPES § Exit-code reference. Key rules:

  • 0 success, 1 generic failure, 2 misuse, 130 SIGINT. Anything custom is domain-specific and documented.
  • Document every non-standard exit code in --help or tool help exit-codes.
  • Don't reuse codes across categories within one tool.
  • Misuse vs failure: parsing errors are 2; tool worked but operation failed is 1 or a custom non-zero.

9. Color & TTY behavior

  • Auto-detect TTY: color on when stdout is a terminal, off when piped (tool list | less should not contain ANSI escapes).
  • Respect NO_COLOR environment variable (no-color.org). Set → no color, regardless of TTY detection.
  • Respect --no-color flag as an explicit override.
  • Respect FORCE_COLOR when set (CI logs in tools like GitHub Actions render ANSI).
  • --color=auto|always|never for full control (matches git, grep, ls).
  • Don't go wild with color. A status column (green: OK, red: FAILED, yellow: WARN) is great. Rainbow output is not.

10. Logging — structured to stderr

CLI logging is for the user's terminal, not log aggregation. Different style from server logging.

  • Default log level: info. -vdebug, -vvtrace. --quiet / -qwarn.
  • Log to stderr always. No exceptions.
  • Structured output even for human reading — key=value pairs are scannable. Use log/slog (Go) or structlog (Python). charmbracelet/log (Go) and rich (Python) add pretty-printing while preserving structure.
  • Errors include the operation that failed, the inputs that mattered, and any correlation id if cross-service. Format example in RECIPES § Error message format.
  • No stack traces in user-facing errors (see §15).

11. Progress feedback

For long-running operations only — anything that takes more than ~2 seconds.

  • Spinner for indeterminate work ("connecting to API...").
  • Progress bar when total is known ("uploading 1.2 GB / 4 GB").
  • Suppress when not a TTY — same auto-detection as color. Piping shouldn't fill the output with \r overwrites.
  • Don't conflict with log output. Pause/reposition the spinner when printing a log line, or write progress to a single line that's overwritten in place.
  • Cancellation: Ctrl-C should immediately stop work, clean up, and exit 130.

12. Shell completions

  • Ship completions for bash, zsh, fish, pwsh. Cobra and typer both generate them — there's no excuse not to.
  • Install command: tool completion <shell> writes the script to stdout; user can pipe to the right location or use the tool's tool completion install --shell <shell> if you provide that convenience.
  • Document the one-time install in --help or a tool help completion page.

13. Versioning

  • tool --version and tool version both work; both print the same thing.
  • Output format is tool <semver> (rev <sha>, built <date>, <runtime>) — version + git short SHA + build date + runtime version. The SHA is critical for dev builds. Example in RECIPES § Version output.
  • Semver always. Pre-1.0 if the API isn't stable; commit to backward compatibility once you ship 1.0.
  • tool version --output json for scripting.

14. Distribution & install

  • Static single binary as the default delivery (go build static; Python via pyinstaller or distributed as a uv tool install package).
  • Multi-arch: linux/amd64 + linux/arm64 + darwin/amd64 + darwin/arm64 + windows/amd64 + windows/arm64. Mirrors docker-architect §5.
  • Release artifacts: signed binaries + checksums + (optional) SBOM. Use goreleaser (Go) or equivalent for the release matrix.
  • Package managers, in order of effort/reach:
    1. Direct binary download from GitHub Releases (always; the universal fallback).
    2. Homebrew tap (macOS + Linux).
    3. Scoop / Winget (Windows).
    4. Linux package repos (apt, yum) — only when the user base genuinely justifies it.
  • tool upgrade is a nice-to-have — fetches the latest release and replaces the binary. Make it opt-in; never auto-upgrade.

15. Error messages

Tell the user what failed, why, and what to try next. Three-line format + structured example in RECIPES § Error message format. Key rules:

  • No stack traces in user-facing errors. Log them at debug level (visible with -vv) or write to a file with a correlation id.
  • Suggest the fix when the cause is unambiguous. Did you mean: tool depoly → deploy? (Levenshtein distance from known subcommands).
  • Distinguish user errors from tool bugs. "Invalid flag value" → user error, exit 2. "Internal: nil pointer in handler" → bug, exit 1 with a "please file an issue" hint.

16. Language-specific recipes

Implementation skeletons for Go (cobra + pflag + viper) and Python (typer + rich) live in RECIPES.md. Canonical per go-architect §11 and python-architect §10.

What ships with it: 2 files

7.9 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.