# Build Ergonomic Clis

> Designs, reviews, and implements product-grade command-line interfaces. Covers command tree structure, flag conventions, auth and profile UX, config precedence, human-first output defaults with opt-in machine output (--json), confirmation and dry-run rules, exit codes, and non-interactive behavior. Use when designing a CLI, reviewing CLI UX/DX, planning command structure, adding argument parsing, or implementing a service CLI. Supports C#/.NET Spectre.Console.Cli and Rust clap.

- Skill: `jkamsker/build-ergonomic-clis` (Agent Skill, multi-file: 22 files)
- Install (CLI): `npx skillmds@latest add jkamsker/build-ergonomic-clis`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jkamsker/build-ergonomic-clis/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: JKamsker (https://skillmd.com/u/jkamsker)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/jkamsker/build-ergonomic-clis

---


# 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 json` if the CLI supports explicit output modes).
- A command must not emit JSON by default if `--json` exists; `--json` must 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](references/cli-patterns.md) (generic CLI + automation contract).
- Then load service guidance based on the classification:
  - **Local-only**: [references/cli-patterns.md](references/cli-patterns.md) only.
  - **Hybrid**: [references/cli-patterns.md](references/cli-patterns.md) + only the relevant sections of [references/service-cli-patterns.md](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](references/service-cli-patterns.md) fully.
  - **Multi-surface service**: read [references/service-cli-patterns.md](references/service-cli-patterns.md) fully and explicitly apply [Multiple Auth Surfaces](references/service-cli-patterns.md#multiple-auth-surfaces) and [Context / Profile / Account Vocabulary](references/service-cli-patterns.md#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](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](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](references/csharp.md) for .NET and Spectre.Console.Cli. Default to `.NET 10` unless the repo or user explicitly pins an older target.
- Use [references/rust.md](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](assets/examples/csharp/spectre) or [assets/examples/rust/clap](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](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.json` only when you are intentionally replaying that benchmark.

## Default Rules

- Prefer branches over flat command lists. `auth login` is better than `auth-login`.
- Do not read from stdin unless the user opted in with an explicit flag such as `--stdin` or `--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](tests/scenario-checklist.md) and [tests/regression-checks.md](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](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.
  - [ ] `--json` is opt-in machine output and produces a stable envelope/shape.
  - [ ] No command emits JSON by default when `--json` exists (i.e., `--json` must 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).
  - [ ] `--json` output 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).

