Agent skill

CLI Design

by citypaul in citypaul/.dotfiles

Unix-composable CLI design patterns. An agent skill from citypaul/.dotfiles.

CC-BY-SA-4.0Auto-check: notesBackend & APIs

Install CLI Design

skills CLI
$ npx skills add citypaul/.dotfiles --skill cli-design -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install citypaul/.dotfiles cli-design --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/citypaul/.dotfiles.git skills-src && mkdir -p .claude/skills && cp -r skills-src/claude/.claude/skills/cli-design .claude/skills/cli-design && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
cli-design
GitHub stars
739
Token cost
~6.6k tokens
SKILL.md length
3,269 words
Files
7
Skills in repo
44
Repo updated
First seen
Licence
CC-BY-SA-4.0

At a glance

Unix-composable CLI design patterns. An agent skill from citypaul/.dotfiles.

  • Works in 5 steps: Flags — per-invocation overrides → Environment variables — MYCLI_* prefix,… → Project config — .myclirc,… → …
  • Building CLI tools
  • SKILL.md covers When to Use, Core Principle, The Unix Stream Contract and Keep Handlers Pure, plus 14 more sections
  • Calls jq, curl and git

What it does

CLI Design is an agent skill from citypaul/.dotfiles. Unix-composable CLI design patterns. Use when building CLI tools, designing command trees, implementing output layers, or testing CLI behavior. Covers stream separation (stdout/stderr), format flags (--json/--plain), exit codes, TTY detection, composability, and error design. Language-agnostic principles; TypeScript implementation patterns in resources/. For API design (REST, HTTP), see api-design.

Its SKILL.md is about 6.6k tokens, which your agent loads only when the skill is triggered. The skill folder holds 7 other files (for example `resources/composability.md`, `resources/output-architecture.md` and `resources/source-notes.md`).

It sits in Backend & APIs, covering API design and Design patterns. It works with TypeScript. The licence is CC-BY-SA-4.0.

When your agent uses it

  • Building CLI tools
  • Designing command trees
  • Implementing output layers
  • Testing CLI behavior

Example prompts

  • “/cli-design”

Requirements

  • Node.js
  • Docker

Workflow steps

5 steps, taken from the first numbered list in SKILL.md.

  1. Flags — per-invocation overrides
  2. Environment variables — MYCLI_* prefix, per-session
  3. Project config — .myclirc, mycli.config.ts, or in package.json
  4. User config — ~/.config/mycli/ (follow XDG spec)
  5. Defaults — sensible built-in values

What it can do on your machine

Read from SKILL.md and the folder at commit cd4028d. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • jq
    • curl
    • git

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • clig.dev
    • specifications.freedesktop.org

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

CLI Design loads about 6.6k tokens when it runs. Until then it costs about 103 tokens; SKILL.md has 3,269 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~103
When it runs · the whole SKILL.md, loaded when a task matches
~6.6k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check: notes

The automated check noted patterns worth knowing about, such as sudo or a known installer.

  • NoteMentions a .env fileSKILL.md:308
    - Read `.env` where appropriate, but don't use it as a substitute for proper config

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from citypaul/.dotfiles at commit cd4028d, republished under its CC-BY-SA-4.0 licence (© citypaul). 3,269 words, ~6,562 tokens.

Download SKILL.mdSave it as .claude/skills/cli-design/SKILL.md (or your agent's skills folder). This skill also uses 6 other files; get the full folder from GitHub.
name
cli-design
description
Unix-composable CLI design patterns. Use when building CLI tools, designing command trees, implementing output layers, or testing CLI behavior. Covers stream separation (stdout/stderr), format flags (--json/--plain), exit codes, TTY detection, composability, and error design. Language-agnostic principles; TypeScript implementation patterns in resources/. For API design (REST, HTTP), see api-design.

CLI Design: Unix-Composable Command-Line Interfaces

This skill covers language-agnostic CLI design principles. The rules about stream separation, exit codes, format flags, and composability apply regardless of implementation language.

This bundle adapts the CC BY-SA 4.0 Command Line Interface Guidelines. Pinned provenance, modification notes, and license scope are recorded in resources/source-notes.md and LICENSE.

For API contract stability and Hyrum's Law, see the api-design skill. For config, env vars, and graceful shutdown, see the twelve-factor skill.

TypeScript implementation patterns are in the resources/ directory. Load them on demand when building a CLI in TypeScript:

ResourceLoad when...
output-architecture.mdImplementing Result types, entry point wiring, formatters, logger, JSON envelope schemas
testing-cli.mdWriting Vitest tests for CLI behavior (streams, exit codes, pipes, contract tests)
stream-contracts.mdUnderstanding Node.js buffering, NDJSON, signal handling, crash-only design
composability.mdDesigning or testing pipe behavior — worked shell examples (jq filtering, NDJSON streaming, stdin, chaining, --fields, parallel xargs)

When to Use

  • Building any command-line tool (any language)
  • Designing command tree, flags, and I/O contracts
  • Implementing the output layer (format detection, stream routing)
  • Testing CLI behavior (stdout/stderr separation, exit codes)
  • Reviewing a CLI for Unix composability

Core Principle

stdout is for DATA only — the product the user asked for. stderr is for EVERYTHING ELSE — diagnostics, progress, spinners, warnings, errors.

This separation is what makes mycli --json | jq ... work. One spinner character on stdout breaks every downstream pipe.

"Whatever software you're building, you can be absolutely certain that people will use it in ways you didn't anticipate. Your software will become a part in a larger system — your only choice is over whether it will be a well-behaved part." — clig.dev


The Unix Stream Contract

ContentStreamWhy
Primary output (data, results, JSON)stdoutPipeable, buffered for throughput
Progress bars, spinners, statusstderr, and only when stderr is a TTY (process.stderr.isTTY) — a piped stderr carries warnings and errors and nothing elseNot data — must not corrupt pipes, and a pipe consumer never wants a spinner
Warnings, errors, diagnosticsstderrVisible to user even when stdout is piped
Debug/verbose outputstderrDiagnostic, never data

Stream behavior:

  • Check stdout and stderr independently; stdout being piped does not mean stderr is piped
  • Do not assume C stdio's line/block/unbuffered rules describe every language runtime
  • In Node.js, sync/async process-stream behavior varies by operating system and destination; always respect .write() backpressure for high-volume output

When stdout is piped, the user doesn't want your status messages in their data. All non-data output must go to stderr.

For a deep dive on buffering behavior and performance implications, see resources/stream-contracts.md.


Keep Handlers Pure

The practical rule: functions that do the work should return data, not write to stdout. The CLI entry point handles all I/O.

Entry point (CLI main)              Your logic (handlers)
─────────────────────               ─────────────────────
parse args                          (input) → structured result
detect format (json/plain/human)    no printing to stdout
call handler                        no writing to stderr
format the result                   no calling exit
write to correct stream             just returns data
set exit code

This isn't an architecture mandate — it's just clean function design with one hard edge: the entry-point file is the only file that writes to a stream or ends the process. When you extend or edit a module that already prints or exits, move that writing into the entry point as part of the same change — do not leave it there, and never add more printing alongside it because "that function already did it". The benefits are concrete:

  • Testable without subprocess spawning — call the handler, assert on the returned value
  • Format flexibility for free — same data renders as JSON, plain text, or coloured tables by swapping one function
  • Reusable — the same handler works from a CLI, MCP server, HTTP API, or programmatic import

For simple CLIs where the "handler" is just calling a library, this separation already exists naturally — your library returns data, your CLI formats it. No extra layers needed.

If your project uses hexagonal architecture, the mapping is direct: the CLI entry point is a driving adapter, and the handler is a use case that returns a result through a port. See the hexagonal-architecture skill — the patterns reinforce each other, but hex arch is not required to benefit from keeping handlers pure.

For TypeScript implementation patterns (Result types, entry point wiring, formatters, logger interfaces), see resources/output-architecture.md.


Selecting a TypeScript Stack

Prefer the command and prompt libraries the repository already owns when they can satisfy the contracts in this skill. For a new material dependency, inspect current versions and project constraints and use evaluate-existing-solutions; do not select from remembered popularity or stale package characteristics.

Stricli can fit typed, injected command handlers, and @clack/prompts can fit optional TTY-gated interactivity. Treat them as candidates, not defaults. Compare them with the existing stack and alternatives on handler testability, type safety, startup cost measured in the target runtime, maintenance, dependency weight, non-interactive behavior, and migration cost.


Format Flag Contract

Three-tier output hierarchy:

Default: Human-Readable
  • Colors, tables, formatted text
  • Progress bars and spinners on stderr
  • Output tailored for terminal width
  • May change between versions — this is not a contract
--plain: Grep/Awk-Friendly
  • One record per line, no formatting, no colors
  • Stable between minor versions — this is a contract
  • Flat table rows, no borders, no grouped sections
  • Enables: mycli list --plain | grep error | wc -l

"Encourage your users to use --plain or --json in scripts to keep output stable." — clig.dev

--json: Structured Data
  • On success, stdout contains ONLY valid JSON — no spinners, no color, no progress
  • On any non-zero exit, stdout stays empty and the final non-empty stderr line is the structured JSON error envelope
  • That includes a domain failure (exit 1) where the run completed and the numbers exist — an unmet threshold, gate, or budget is a failure, not a success envelope carrying a false flag inside it. If the caller still needs the figures, put them inside error (a details field), never on stdout
  • Other diagnostics may precede that final stderr line and never contaminate stdout; consumers parse the final non-empty line rather than the entire diagnostic stream
  • Schema is versioned — breaking changes to JSON output are breaking changes to the CLI
  • --json implies non-interactive regardless of TTY

Consistent envelopes:

Success on stdout:

json
{ "ok": true, "data": { ... } }

The envelope is not optional and renames nothing: field names a consumer asked for live unchanged inside data, and a list of records goes in data rather than replacing the envelope with a bare array.

Failure on stderr:

json
{ "ok": false, "error": { "code": "CONFIG_MISSING", "message": "...", "fix": "..." } }
NDJSON for Streaming

For large datasets, use NDJSON (one JSON text per record, terminated by \n):

  • Each line is independently parseable
  • Any JSON value is valid NDJSON syntax; the application's decoder owns its schema
  • The examples in this skill use an optional local event profile with a type discriminator and an optional final summary record; neither is required by NDJSON
  • Document whether readers ignore or reject empty lines
  • Enables: mycli run --format ndjson | while read -r line; do ...; done

For NDJSON specification details, see resources/stream-contracts.md.


Exit Codes

CodeMeaningWhen
0SuccessOperation completed as expected
1Domain failureTool-specific failure (e.g. quality threshold not met)
2Invalid usageBad flags, missing required args, validation error
78Configuration errorInvalid config file, missing required config
75Temporary, retry-safe failureFailure occurred before dispatch, or the operation is documented and demonstrably safe to retry
130SIGINTUser pressed Ctrl-C (128 + 2)
143SIGTERM-style statusOptional documented status when preserving signal termination (128 + 15); a handled graceful shutdown may instead return 0

Rules:

  • Non-zero exit code MUST have a stderr explanation
  • Document exit codes in --help
  • Never use codes above 125 for application errors (reserved for signals: 128 + signal number)
  • Use exit 75 only when no mutation was dispatched or retry safety is established through idempotency/reconciliation; a merely transient cause is not enough
  • If a mutating request may have reached its destination, report an ambiguous outcome and direct the user to status/reconcile instead of inviting a blind retry
  • A handled SIGTERM may finish with 0 or a documented application status such as 143; Docker and Kubernetes do not require 143 for graceful shutdown
  • Map non-zero codes to the most important failure modes for your tool
  • A flag the caller simply did not pass is not invalid usage. Exit 2 is for input the tool cannot act on; an added gate, threshold, or filter flag is opt-in — its absence means the check is off and the command still succeeds with 0. Make a new flag required only when the command has no meaning without it, or every plain invocation becomes a usage error and hides the failures the codes exist to separate
  • Order the checks so each failure reaches the code that names it: a later validation must not intercept a config or data error and report it as bad usage

TTY Detection

Check priority order (first match wins):

PriorityConditionEffect
1--format json or --json flagNon-interactive, no color, no animation
2--no-color flagDisable color (output may still be interactive)
3FORCE_COLOR envEmpty, 1, 2, 3, or true enables color; every other value (including 0) disables it. Supported values override NO_COLOR and NODE_DISABLE_COLORS
4NO_COLOR (non-empty) or NODE_DISABLE_COLORS (defined), with FORCE_COLOR unsetDisable color
5TERM=dumbDisable color and animations
6CI=trueNo interactive prompts
7stdout is not a TTY (!isatty(stdout))Plain output, no animations on stdout
8DefaultFull interactive with colors

Resolve the output mode once, in the entry point, before anything is written. Walk the table above — format flags, --no-color, FORCE_COLOR, NO_COLOR, TERM, CI, and each stream's TTY status (isatty/isTTY) — into a single mode value and pass it to the formatters. Write that check even when today's output has no color and no animation yet: it is what keeps the decision in one place the day either is added.

Check stdout and stderr independently. When stdout is piped but stderr is a TTY, you can still show spinners on stderr while keeping stdout clean for the pipe consumer. Status lines, spinners and progress are written only when the stream carrying them is a TTY; when stderr is a pipe it carries warnings and errors and nothing else.

Gate prompts on stdin independently. Prompt only when stdin and the prompt's output stream are TTYs; stdout TTY status controls data formatting, not whether input is safe to request.

Optionally support MYCLI_NO_COLOR for app-specific color override.


Input Design

Flags Over Arguments
  • 1 positional arg: acceptable (the "main thing")
  • 2 positional args: suspicious — consider flags instead
  • 3+ positional args: prefer named flags unless a familiar command grammar makes the positions obvious
  • Exception: variadic args of the same type are fine (rm a.txt b.txt c.txt), as are universal idioms (cp source dest)

Flags are self-documenting, order-independent, and future-proof.

bash
# Bad — which is source, which is destination?
mycli copy myapp backup

# Good — explicit
mycli copy --from myapp --to backup
Standard Flags

Always provide long forms. Short flags only for the most common operations.

FlagMeaning
-h, --helpShow help (this should only mean help)
--versionPrint version to stdout
-q, --quietSuppress non-essential output
-v, --verboseMore detail in human output
-d, --debugDiagnostic output to stderr
-f, --forceSkip confirmation prompts
-n, --dry-runShow what would happen without doing it
--jsonStructured JSON output
--plainStable, grep-friendly plain text
--no-colorDisable color output
--no-inputDisable all prompts/interactivity
-o, --outputOutput file
--fieldsSelect output columns
Prompts and Interactivity
  • All prompts MUST be bypassable via flags for scriptability
  • Confirmation → --yes or --force
  • Selection → --type=value
  • Text input → --name=value
  • Passwords → --password-file=path or stdin pipe
  • If stdin is not a TTY, never prompt — fail with a clear error or use defaults
  • Secrets: never via flag values (leak to ps output and shell history). Prefer, in order: OS keychain or a 0600 credential file (~/.config/mycli/credentials), then stdin (mycli login --with-token < token.txt), then env vars only where the platform injects them (CI secret stores) — env leaks to child processes and crash reports, so never make it the primary documented path
  • Scale confirmation to severity: mild → y/N prompt with --yes bypass; moderate → prompt plus suggest --dry-run first; severe/irreversible (delete a database, overwrite production) → require typing the resource name to confirm
Conventions
  • Support -- to stop flag parsing: mycli run -- --flag-for-child-process
  • Support - for stdin/stdout file arguments: curl ... | mycli process -
  • Accept both --flag=value and --flag value
  • If stdin is expected but is an interactive terminal, display help immediately (don't hang like cat)

Show full SKILL.md (1,299 more words)Show less

Config Precedence

Highest to lowest priority:

  1. Flags — per-invocation overrides
  2. Environment variables — MYCLI_* prefix, per-session
  3. Project config — .myclirc, mycli.config.ts, or in package.json
  4. User config — ~/.config/mycli/ (follow XDG spec)
  5. Defaults — sensible built-in values

Rules:

  • Follow the XDG Base Directory Specification for config file locations
  • Env var naming: MYCLI_* prefix, uppercase letters + digits + underscores; keep values single-line; don't commandeer POSIX names
  • Respect the general-purpose env vars where relevant: NO_COLOR, FORCE_COLOR, DEBUG, EDITOR, PAGER, HTTP_PROXY/HTTPS_PROXY/NO_PROXY, TMPDIR, TERM, LINES/COLUMNS
  • Never accept secrets via flags; prefer keychain/credential files or stdin, env vars only when platform-injected (CI) — see "Prompts and Interactivity"
  • Read .env where appropriate, but don't use it as a substitute for proper config
  • If you modify configuration that belongs to another program, ask consent first

Error Design

Every error needs:

  1. Machine-readable code — UPPER_SNAKE_CASE (e.g. CONFIG_MISSING, AUTH_EXPIRED)
  2. What went wrong — context: which resource, operation, input
  3. How to fix it — exact command or action the user should take
  4. Reference — docs URL or mycli help <topic> (optional)
Human Mode
Error: CONFIG_MISSING — Configuration file not found
No configuration file found at ./mycli.config.ts or ~/.config/mycli/config.ts

Fix: Run `mycli init` to create a default configuration file
Docs: https://mycli.dev/docs/configuration
  • Put the most important information last (the eye is drawn to the end)
  • Use red sparingly and intentionally
  • Suggest corrections for typos ("Did you mean 'deploy'?")
  • Group similar errors under one header — don't repeat 50 similar-looking lines
  • Write debug logs to a file, not the terminal (unless --debug)
JSON Mode

Errors are structured too — not just success responses:

json
{
  "ok": false,
  "error": {
    "code": "CONFIG_MISSING",
    "message": "No configuration file found at ./mycli.config.ts",
    "fix": "Run `mycli init` to create a default configuration file",
    "transient": false
  }
}

The transient boolean says the cause may clear. It does not by itself authorize a retry: callers also need exit code 75 or an explicit retry-safe contract.


State Changes and Transparency

  • Confirm state changes — say what changed and show (or point to) the resulting state; traditionally-silent commands look broken to humans
  • Make current state easy to see — a status-style command for anything with complex state (the git status pattern)
  • Make hidden actions explicit — if you read/write files not passed as arguments or talk to remote servers, say so (stderr in human mode)
  • Resolve ambiguous mutations — if transport fails after dispatch, tell the user how to inspect or reconcile state; do not label the result retry-safe
  • Page long output through $PAGER only when stdout is a TTY, never when piped; do not execute the value through a shell, and document whether it is an executable token or parsed by a reviewed shell-word parser

Robustness

  • Validate input early — fail before any side effects, with a clear message
  • Responsiveness for ongoing human work — in interactive human mode, unless --quiet, show progress on stderr if an operation is still running after roughly 100ms; keep machine modes quiet unless their protocol explicitly defines progress, and include time estimates when interactive progress can stall
  • Timeouts on all network operations — configurable; use exit 75 only before dispatch or when retry safety/idempotency is established
  • Recoverable operations — re-running may resume only when safe; ambiguous mutation outcomes need an explicit status or reconciliation path
  • Crash-only design — exit fast on failure, defer cleanup to the next run; distinguish visibility-atomic replacement from crash-durable persistence, and keep bounded-cleanup timers referenced so Ctrl-C cannot bypass the bound — see resources/stream-contracts.md
  • Expect misuse — script wrapping, bad connections, concurrent instances, environments you never tested

Composability Patterns

Design for real-world pipes: filtering with jq, streaming NDJSON line-by-line, feeding stdin, chaining commands through emitted identifiers, selecting columns with --fields, and fanning out with xargs -P.

See resources/composability.md for the worked shell examples covering each of these patterns.

Key patterns:

  • Create commands output identifiers so subsequent commands can chain
  • List commands support --fields for column selection (reduces output size, critical for agent efficiency)
  • --quiet for CI scripts that only care about the exit code
  • NDJSON for streaming large datasets without buffering everything in memory
  • --dry-run with --json outputs planned changes as structured data

Subcommand Design

  • noun verb pattern is most common: mycli config set, mycli report generate
  • Be consistent across all subcommands — same flag names for same things
  • No ambiguous pairs (update vs upgrade is confusing)
  • No catch-all subcommands (you can never add subcommands with conflicting names)
  • No arbitrary abbreviations — aliases must be explicit and stable
  • With no args: list subcommands (multi-command CLI) or show help (single-command CLI)
Help
  • mycli --help — top-level help
  • mycli help <subcommand> — subcommand help
  • mycli <subcommand> --help — same as above
  • If run with missing required args, show concise help + 1-2 examples + "use --help for more"
  • Examples are the most-read section — lead with them
  • Include flag types, defaults, and allowed values for finite sets
  • Include a support/bug-report link in top-level help; pre-populate issue URLs with diagnostics where possible
  • Suggest the likely command on obvious typos ("Did you mean 'deploy'?") — ask, never auto-execute

Output Stability Contract

Stdout is a public API. Breaking changes to stdout format are breaking changes to the CLI.

ChangeImpact
Adding new optional JSON fieldsSafe (additive)
Adding new subcommandsSafe
Adding new flags with preserving defaultsSafe
Removing or renaming flagsBreaking
Removing or renaming JSON fieldsBreaking
Changing exit codesBreaking
Changing default behaviorBreaking
Changing human-readable outputUsually OK (not a contract)

When in doubt, add alongside — don't modify. Deprecate with stderr warnings before removing — and once you can detect that users have migrated, stop warning.


Naming, Distribution, Telemetry

  • Name: short, memorable, lowercase, easy to type; not so generic it collides with existing commands
  • Distribution: prefer a single binary; language-ecosystem tools (npm, pip) may reasonably assume their interpreter. Make uninstalling easy and documented
  • Telemetry: never collect usage/crash data without explicit consent — opt-in, stating what, why, and retention. Instrumented web docs or download counts are usually enough

Anti-Patterns

#Anti-PatternWhy It's Wrong
1Mixing data and diagnostics on stdoutBreaks every pipe: mycli list | jq . fails if warnings are on stdout
2Colors/ANSI in piped outputANSI sequences corrupt downstream parsing. Check isatty(stdout) + NO_COLOR
3Interactive prompts with no flag bypassAgents can't type 'y'. Every prompt needs --yes/--force. Non-TTY without bypass = hang
4Printing nothing on successSilence is ambiguous — show brief confirmation. Offer -q for scripts that want silence
5Designing for humans OR machines, not bothDetect context (TTY vs pipe), adapt automatically
6Output that doesn't guide the next actionEvery output is a signpost: success = next command, failure = fix command
7Breaking existing CLI contractsFlag names, exit codes, output shape are contracts. Add alongside, never modify
8console.log anywhere except the CLI adapterHandlers must return data; only the presentation layer writes to streams
9Handlers that exit the process directlyLet the entry point decide. Handlers return errors as data
10Non-zero exit without stderr explanationScripts need both the code and the reason
11Verbose default outputA single test run can generate 419KB. Support --fields, --quiet, --json

Verification Checklist

After designing or reviewing a CLI:

  • stdout has ONLY data; stderr has everything else
  • Every command supports --json with success data on stdout and structured failures on stderr
  • Exit codes are semantic and documented in --help
  • Every prompt has a --yes/--force/--flag bypass
  • Errors include: code, message, fix suggestion
  • --dry-run available for mutating commands
  • Progress/spinners go to stderr, never stdout
  • NO_COLOR, TERM=dumb, and --no-color respected
  • Piped output contains zero ANSI escape codes
  • Success output includes next-action guidance
  • Existing flags, exit codes, output fields never removed or renamed
  • JSON schema is versioned (additions safe, removals breaking)
  • Config follows flags > env > project > user > defaults
  • Secrets never via flags; keychain/credential-file/stdin preferred, env only when platform-injected (CI)
  • Severe destructive actions require typed confirmation (resource name), not just y/N
  • Network operations have configurable timeouts; exit 75 is reserved for pre-dispatch or demonstrably retry-safe failures
  • Interactive human operations still running after roughly 100ms show progress on stderr unless --quiet; machine-mode protocols stay clean
  • Ctrl-C exits fast with bounded cleanup
  • --help includes 2-3 realistic examples
  • Human output is grep-parseable (flat rows, no table borders)

Quick Reference

Stream routing, exit codes, and standard flags are tabled in the body — see "The Unix Stream Contract", "Exit Codes", and "Standard Flags" above.

Format Hierarchy
Default (TTY)     → colors, tables, formatted text
--plain           → one record per line, stable, grep-friendly
--json            → structured JSON, versioned schema
--format ndjson   → streaming, one JSON object per line
Config Precedence
flags > env vars > project config > user config > defaults

© citypaul, CC-BY-SA-4.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 6 other files in claude/.claude/skills/cli-design of citypaul/.dotfiles.

  • SKILL.md
  • LICENSE
  • resources/composability.md
  • resources/output-architecture.md
  • resources/source-notes.md
  • resources/stream-contracts.md
  • resources/testing-cli.md

Open the folder on GitHubat commit cd4028d

Compare with similar skills

CLI Design next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

CLI Design compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
CLI Design this skillcitypaul/.dotfiles739—~6.6kAutomated safety check: NotesCC-BY-SA-4.0
Interface Designccashwell/evm-cortex131—~1.7kAutomated safety check: PassMIT
Node Backend Development Guidelinesdiet103/claude-code-infrastructure-showcase10k2 repos~2kAutomated safety check: PassMIT
API Design Principlesjh941213/my-cc-harness12620 repos~3.4kAutomated safety check: PassNone
API ContractChenyCHENYU/Robot_Admin1k—~1.9kAutomated safety check: PassMIT
Server TRPC Router Guidelobehub/lobehub83k—~904Automated safety check: PassCustom licence

Similar skills

  • Interface Design

    ccashwell/evm-cortex

    Interface and abstract contract design patterns for Solidity protocols.

    131 GitHub stars~1.7k tokensUpdated 9 days ago
    Backend & APIsAuto-check passed
  • Node Backend Development Guidelines

    diet103/claude-code-infrastructure-showcase

    Sets layered architecture and coding rules for Node.js, Express and TypeScript microservices, covering routes, controllers, services, repositories, Prisma, Sentry and Zod.

    10k GitHub starsUsed in 2 repos~2k tokens
    Backend & APIsAuto-check passed
  • API Design Principles

    jh941213/my-cc-harness

    REST 및 GraphQL API 설계 원칙 가이드. An agent skill from jh941213/my-cc-harness.

    126 GitHub starsUsed in 20 repos~3.4k tokens
    Backend & APIsAuto-check passed
  • API Contract

    ChenyCHENYU/Robot_Admin

    A skill your agent uses when: generating TypeScript API layer (type definitions + request functions) from page-spec JSON or Swagger/OpenAPI docs.

    1k GitHub stars~1.9k tokensUpdated yesterday
    Backend & APIsAuto-check passed
  • Conventions for LobeHub's server tRPC routers: file locations, middleware that injects models into ctx, procedure patterns, aggregated detail endpoints and return shapes.

    83k GitHub stars~904 tokensUpdated today
    Backend & APIsAuto-check passed
  • defineRoute Route Builder

    bighadj22/codflow

    Creates new API endpoints, and converts older ones, with the defineRoute() pattern used in cod-server, including auth strategies, scopes and OpenAPI output.

    346 GitHub stars~924 tokensUpdated 2 days ago
    Backend & APIsAuto-check passed

More from citypaul/.dotfiles

All 44 skills in this repo
  • Find Skills

    citypaul/.dotfiles

    Discover and, with authorization, install agent skills from the open skills ecosystem.

    739 GitHub stars~2.5k tokensUpdated 6 days ago
    Auto-check passed
  • Render Code Shape

    citypaul/.dotfiles

    Render the shape of code — module boundaries, the types that cross them, signatures, and a cited call graph — for code that already exists or a change about to be built.

    739 GitHub stars~2.5k tokensUpdated 6 days ago
    Auto-check passed
  • Structure Codebase

    citypaul/.dotfiles

    Design, audit, and evolve physical source and package structures that expose real architectural boundaries while keeping related behavior together.

    739 GitHub stars~4.4k tokensUpdated 6 days ago
    Auto-check passed
  • Test Design Reviewer

    citypaul/.dotfiles

    Review test quality using Dave Farley's eight properties of good tests.

    739 GitHub stars~1k tokensUpdated 6 days ago
    Auto-check passed
  • Characterisation Tests

    citypaul/.dotfiles

    A skill your agent uses when modifying existing code that lacks tests and you need to document its actual current behavior before making changes -- the legacy code dilemma where you need tests to…

    739 GitHub stars~3.6k tokensUpdated 6 days ago
    Auto-check passed
  • CI Debugging

    citypaul/.dotfiles

    Systematic CI/CD failure diagnosis using hypothesis-first investigation, local reproduction, and environment delta analysis.

    739 GitHub stars~1.5k tokensUpdated 6 days ago
    Auto-check: notes

Works with

Questions about CLI Design

What does CLI Design do?

Unix-composable CLI design patterns. An agent skill from citypaul/.dotfiles. dotfiles. Unix-composable CLI design patterns.

When should I use CLI Design?

CLI Design fits situations like: building CLI tools; designing command trees; implementing output layers; testing CLI behavior.

How do I install CLI Design in Claude Code?

Run `npx skills add citypaul/.dotfiles --skill cli-design -a claude-code`. Or copy the skill folder (claude/.claude/skills/cli-design in citypaul/.dotfiles) into .claude/skills/cli-design in your project. Claude Code loads it when a task matches its description.

How do I install CLI Design in Codex?

Run `npx skills add citypaul/.dotfiles --skill cli-design -a codex`. Or copy the skill folder (claude/.claude/skills/cli-design in citypaul/.dotfiles) into .agents/skills/cli-design in your project. Codex loads it when a task matches its description.

Can I use CLI Design in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add citypaul/.dotfiles --skill cli-design -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/cli-design, .gemini/skills/cli-design, .github/skills/cli-design and .opencode/skills/cli-design in your project.

What does CLI Design need to run?

Going by SKILL.md and its folder, CLI Design needs the command-line tools its instructions call (jq, curl and git). Our summary lists: Node.js; Docker.

Does CLI Design access the network?

SKILL.md names 2 domains. As links in the text: clig.dev and specifications.freedesktop.org. This is read from the text; nothing was executed.

Is CLI Design safe to install?

Our automated static check of SKILL.md found notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.

What licence does CLI Design use?

CLI Design is published under the CC-BY-SA-4.0 licence (from the LICENSE file in the skill folder). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does CLI Design use?

About 6.6k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to CLI Design?

Skills that share tags, products or a category with CLI Design: Interface Design (ccashwell/evm-cortex, 131 stars), Node Backend Development Guidelines (diet103/claude-code-infrastructure-showcase, 10k stars), API Design Principles (jh941213/my-cc-harness, 126 stars) and API Contract (ChenyCHENYU/Robot_Admin, 1k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains CLI Design?

citypaul (a GitHub user) maintains it in citypaul/.dotfiles, which has 739 GitHub stars. The repository holds 44 skills in this directory. The repository was last updated on October 2, 2026.

Source: citypaul/.dotfiles on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.