Build Ergonomic CLIs
Use this skill to design a CLI as a product surface instead of a thin dump of API endpoints or internal functions.
Invoke with a task mode and a description: [design|review|implementation] <description>. If no mode is given, infer it from the request.
Core Principle: Human-First, Machine-Second
- Default output is for humans (tables for lists, concise summaries for actions, readable key/value for objects).
- Machine-readable output is opt-in via
--json(or--output jsonif the CLI supports explicit output modes). - A command must not emit JSON by default if
--jsonexists;--jsonmust materially change the output contract. - Avoid the common anti-pattern where “human mode” is just pretty-printed JSON; if JSON exists, it must live behind
--json.
When Not to Use This
- Do not use this skill for shell one-liners, grep or awk pipelines, or other ad hoc shell usage.
- Do not use this skill when the task is only about operating an existing third-party CLI.
- Do not use this skill for TUI or terminal-dashboard work, packaging and distribution work, or raw OpenAPI client generation.
Task Modes
Design
- First classify the CLI (required):
- Local-only: operates on local files/processes/project state only.
- Hybrid: local-first, but some commands optionally connect to remotes.
- Service-native: primarily interacts with one remote service/API surface.
- Multi-surface service: multiple independent target/auth surfaces (contexts, daemons + registries, certs + accounts, etc.).
- Always start with references/cli-patterns.md (generic CLI + automation contract).
- Then load service guidance based on the classification:
- Local-only: references/cli-patterns.md only.
- Hybrid: references/cli-patterns.md + only the relevant sections of references/service-cli-patterns.md for the remote-facing command subtree (typically: Auth Design, Targets, Profiles/Contexts, Defaults, Target Identity Modes, Resolution Algorithm, Aliases, Inference, Validation, Migration, and protocol-specific Protocol-Level Error Handling/Protocol-Level Diagnostic Logging).
- Service-native: read references/service-cli-patterns.md fully.
- Multi-surface service: read references/service-cli-patterns.md fully and explicitly apply Multiple Auth Surfaces and Context / Profile / Account Vocabulary.
- Define the command tree, help contract, env/config precedence, target/auth/context resolution, automation contract, confirmation rules, machine output, and non-interactive behavior before talking about code structure.
- Generic references are the source of truth. Worked examples are illustrative; if an example conflicts with a reference, the reference wins.
- Use assets/design/jf-cli-design.md only as a worked benchmark after the first design pass.
- For the detailed target/profile system asset, load assets/design/jf-cli-profile-system.md when the CLI needs saved remote targets and identities (profiles/contexts/accounts), defaults or aliases for target selection, and switching behavior — not merely “more than one server”.
Review
- Extract the current command tree, precedence rules, auth flow, and output contract before proposing changes.
- Compare the current behavior against the default rules and definition of done in this skill.
- Report concrete UX/DX regressions first, then list implementation fixes or missing tests.
Implementation
- Load exactly one implementation reference after the UX contract is settled.
- Use references/csharp.md for .NET and Spectre.Console.Cli. Default to
.NET 10unless the repo or user explicitly pins an older target. - Use references/rust.md for Rust and clap.
- If you need a small teaching sketch instead of mining a full repository, prefer the canonical examples under assets/examples/csharp/spectre or assets/examples/rust/clap.
- If the language is not C#/.NET or Rust, stop after the UX/DX design and translate it into framework-agnostic implementation guidance. Do not invent library-specific patterns.
- The Jellyfin OpenAPI document used by the worked benchmark in assets/design/jf-cli-design.md is not bundled, because it is large and rarely needed. Fetch it from a running Jellyfin server at
/api-docs/openapi.jsononly when you are intentionally replaying that benchmark.
Default Rules
- Prefer branches over flat command lists.
auth loginis better thanauth-login. - Do not read from stdin unless the user opted in with an explicit flag such as
--stdinor--password-stdin, or the command is explicitly interactive and a TTY is present. - Human-first output by default; machine-readable output only when explicitly requested (typically
--json). - For “list” commands, prefer a table with stable columns over free-form text blobs; truncate long fields (URLs/paths) and put warnings on stderr.
- Commands without the required arguments should print help or raise a validation error, not guess an implicit target such as "latest".
- Define and document a single precedence order for flags, environment variables, config, and defaults.
- Command implementation files must contain only one command class. The usual shape is one command plus its dedicated settings type in the same file; do not group multiple commands into one source file just because they are small or belong to the same branch.
Additional rules for service CLIs
- Fail fast when auth or target resolution is missing. Do not start an interactive login flow from an unrelated command.
- Keep credentials separate from general config, and bind stored credentials to the chosen target identity key (hostname/origin/base-URL mode as documented for the CLI).
- If multiple credential-storage schemes would be suitable for the CLI (for example OS secret store vs separate secret file vs sidecar key-file model), ask the user which scheme to use instead of silently choosing one. If the user does not choose, proceed with a clearly stated default assumption.
Definition of Done
- Produce a top-level command tree and justify the grouping in user-facing terms.
- Make global flags, reserved flags, environment variables, and config/default precedence explicit.
- Define human output (default) and machine output (opt-in, typically
--json), plus stdout vs stderr rules, confirmation rules, and exit codes. - Include language-specific implementation notes only when implementation is in scope. For other languages, provide framework-agnostic guidance.
- Include three to five validation checks or tests covering help, target resolution, non-interactive behavior, destructive flows, or machine-readable output. Use tests/scenario-checklist.md and tests/regression-checks.md as starting points.
Additional items for service CLIs
- Define auth, host, profile, credential-storage, and fallback-host behavior.
- Define target resolution validation checks.
Deliverables
When implementing or redesigning a CLI, produce these artifacts unless the user asks for less:
- CLI classification (local-only / hybrid / service-native / multi-surface service).
- A top-level command tree and a short explanation of the grouping.
- Target/auth/context resolution and precedence (flags → env → config/profile/context → defaults).
- Automation contract (machine output style + versioning + stdout/stderr rules).
- TTY / non-interactive behavior (stdin, prompts,
--quiet,--yes,--dry-run). - Destructive action and confirmation rules.
- Output modes and exit codes.
- Error message strategy, diagnostic logging, and verbosity levels.
- Implementation notes only when implementation is in scope; otherwise keep it framework-agnostic.
- Three to five validation checks or tests (see tests/scenario-checklist.md).
Additional deliverables for service CLIs
- Auth, host, profile, and fallback-host behavior.
- Auth storage model and the chosen target identity mode + normalization rules.
Pre-Implementation Extraction Checklist
Before you redesign a CLI, write down:
- Top-level branches and any expert-only or privileged labels that should survive.
- Exact environment variable names already in use.
- Stdout vs stderr rules for prompts, banners, streamed logs, and machine output.
- Current output default per command branch: which commands are “human-first” (tables/summaries) and which are already machine-first (and must be migrated behind
--json). - Domain-specific verbs or diagnostic commands that are part of the CLI's value, even if they do not fit a tiny generic verb set.
Additional extraction for service CLIs
- Exact auth modes and credential stores.
- Target-resolution order, fallback heuristics, and any git or directory inference.
Completion Checklist (Must Be Fully Satisfied Before Finishing)
Before you finish a response using this skill, you must ensure every checkbox below is satisfied and you must reflect it in your final response (for example as a short "Checklist" section with [x] items). If any item cannot be satisfied, stop and explicitly call out what is missing and why (and ask the user for a decision or provide a concrete next step).
- CLI classification is stated (local-only / hybrid / service-native / multi-surface service).
- Command tree is extracted/proposed and justified in user-facing terms.
- Flag/env/config/default precedence is explicit (including target/profile/context resolution for service CLIs).
- Output contract is explicit and consistent:
- Human-first output is the default.
-
--jsonis opt-in machine output and produces a stable envelope/shape. - No command emits JSON by default when
--jsonexists (i.e.,--jsonmust materially change output). - “List” commands render as tables (or an equally scannable columnar format) in human mode.
- Stdout vs stderr rules are defined for both human and JSON modes.
- Output contract has at least one regression check:
- Default output for a representative list command is not JSON (human-mode table/lines).
-
--jsonoutput for the same command is valid JSON and matches the documented envelope/shape.
- Non-interactive behavior is defined (TTY detection, stdin opt-in, prompts,
--quiet,--yes,--dry-run). - Destructive action policy is defined (confirmation, previews, and refusal behavior without
--yes/ in non-interactive). - Exit codes and error message strategy are defined (including machine-mode error shapes).
- 3–5 validation checks/tests are listed (help, resolution, non-interactive, destructive flows, JSON contract).