| name | write-cli |
| description | Design or review command-line interface shape — flags, subcommands, help, errors, and output contracts — against established CLI conventions (clig.dev, POSIX, GNU). Use when the agent is about to write a CLI tool, add a subcommand, design flag names, review --help output, or evaluate CLI UX. Do NOT use for TUI/full-screen terminal apps, GUI design, or web API design. |
write-cli
Apply systematic CLI design checks before writing or reviewing a command-line tool. Every rule is a concrete yes/no question — answer it, don't rationalize around it.
Signature Shape
- Flags over positional args. Max 2 positional args, and only when the action is primary and memorable (
cp <src> <dst>). Everything else gets --long-name.
- Full names for every flag.
-h always has --help. -v always has --verbose. Short forms are for muscle-memory flags only.
- No positional args with mixed meanings. If you have
cmd <file> <name>, add a third thing, or the second arg means two different things across subcommands — redesign to flags.
- Order-independent.
myapp --verbose subcmd and myapp subcmd --verbose must both work.
Naming
- Reuse flag names across subcommands.
--json means JSON output everywhere. --repo means a repository everywhere. Never use --json on one subcommand and --format json on another.
- Follow the standard flag table.
-a/--all, -d/--debug, -f/--force, -h/--help, --json, --no-input, -o/--output, -p/--port, -q/--quiet, -u/--user, --version. Deviate only with a documented reason.
- Verb consistency across resource subcommands. Use the same verb set everywhere:
list, get, create, update, delete. If one subcommand uses remove and another delete, pick one.
Help & Discovery
- Progressive disclosure, three levels.
cmd → terse usage + one example. cmd subcmd → subcommand-specific params. --help → everything. Never dump full docs on cmd alone.
- Lead with examples, not option catalogs. The first thing a user sees should be a working invocation they can copy.
- Suggest corrections on invalid input. If the user typed a command that doesn't exist but resembles one that does, say so. "Did you mean
ps?" not "unknown command."
Output
- stdout = data, stderr = messaging. Primary output, machine-readable output, and pipeable content go to stdout. Logs, errors, progress, and hints go to stderr.
--json for structured output. When a machine might consume the output, provide --json. Pair with a filtering flag (--jq or --filter) to let callers trim before the data leaves the tool.
--quiet suppresses non-essential output. Scripts should not redirect stderr to /dev/null — the tool should shut up when asked.
- Default to human-readable, provide
--plain for grep/awk. If terminal formatting (color, tables, dividers) would break line-based tooling, --plain disables it.
Errors
- Every error says what happened AND what to do next. Not "permission denied" — "permission denied: cannot write to config.yaml. Run with
--config /path/to/writable/file or use chmod +w config.yaml."
- Never print a raw stack trace to users. Catch expected errors and rewrite them. Unexpected errors get a debug log file path plus a bug report URL.
Safety & Scriptability
- No secrets in flags.
--password, --token, --api-key leak to ps and shell history. Use --password-file, environment variables, or stdin.
- Every prompt has a flag fallback. If the tool prompts for input interactively, provide
--name, --yes, or --confirm so scripts don't hang.
- Confirm before destructive actions. Require
--force or interactive confirmation for anything that can't be undone. For severe destruction (deleting a server), require typing the resource name.
- Exit 0 on success, non-zero on failure. Map distinct non-zero codes to distinct failure modes so scripts can branch on them.
Function vs Form
- Make the default the right thing for 90% of users. If users need a flag for the common case, the default is wrong.
- If an action spans multiple underlying operations, make it one command.
merge is one command, not create-merge-request + approve + apply. The user's intent is "merge this" — match it.