Citty CLI
Quick Start
npm install citty `@bomb.sh/tab` `@clack/prompts`
Minimal command:
import { defineCommand, runMain } from 'citty';
export default defineCommand({
meta: { name: 'greet', description: 'Say hello' },
args: { name: { type: 'string', description: 'Your name', required: true } },
run({ args }) { console.log(`Hello, ${args.name}!`); },
});
Critical Rules
- Every command exports default
defineCommand() — no exceptions
- Lazy-load subcommands —
() => import('./cmd').then(m => m.default)
- Check
isCancel() after every @clack/prompts call — never skip
- Citty handles all arg parsing — no external parsers
- Architecture is opt-in — only suggest
cli/ structure when the user asks for project layout or scaffolding. Single-file CLIs are valid.
- Gate
tab() behind process.argv[2] === 'complete' — the adapter eagerly resolves lazy subcommands, defeating rule 2 on every startup
Architecture (only when user asks for structure)
See architecture for the full cli/ layout with commands, prompts, and lib directories. Do not impose this structure unless the user explicitly asks for scaffolding or project organization.
Workflow
- Define commands — commands
- Add prompts if interactive — prompts
- Add spinners/progress for long-running work — tty-ui
- Wire tab completion — tab-completion
- Scaffold
cli/ if multi-command — architecture
- Configure bin entry — sidecar setup
- Verify — run with
--help to confirm command registration
- Ship it — CI gates, versioning, npm or binary release — release
References
- Architecture — Structure and responsibilities
- Entrypoint — runMain + tab completion wiring
- Commands — defineCommand, args, subcommands
- Prompts —
@clack/prompts reusable modules
- Tab Completion —
@bomb.sh/tab citty adapter, completion protocol, per-shell install (compinit ordering, fish autoload), lazy-loading gate
- TTY UI — spinners and progress:
@clack/prompts spinner first, @bomb.sh/tty inline regions and layout for richer feedback
- Sidecar Setup — bin entry, build config
- Citty API — Resolvable, plugins, CLIError
- Release — CI gates, Changesets/release-please, npm + binary distribution
- Versioning — named schemes (SemVer/CalVer), 0.x vs 1.0, pre-releases, which bump, decision matrix, recording the policy
- Update Command — ask-first self-update: install-mode routing, streaming download (stall timeout, resumable retry), checksum-verified atomic swap, passive banner
1---2name: cli-building3description: Use when building TypeScript CLIs. Guides command structure, interactive prompts, tab completion, and terminal UI niceties (spinners, progress, inline regions) using citty, `@clack/prompts`, `@bomb.sh/tab`, and `@bomb.sh/tty` — and shipping them: CI gates, versioning (Changesets / release-please), and npm or single-binary release.4---56# Citty CLI78## Quick Start910 npm install citty `@bomb.sh/tab` `@clack/prompts`1112Minimal command:1314```ts15import { defineCommand, runMain } from 'citty';1617export default defineCommand({18 meta: { name: 'greet', description: 'Say hello' },19 args: { name: { type: 'string', description: 'Your name', required: true } },20 run({ args }) { console.log(`Hello, ${args.name}!`); },21});22```2324## Critical Rules25261. **Every command exports default `defineCommand()`** — no exceptions272. **Lazy-load subcommands** — `() => import('./cmd').then(m => m.default)`283. **Check `isCancel()` after every `@clack/prompts` call** — never skip294. **Citty handles all arg parsing** — no external parsers305. **Architecture is opt-in** — only suggest `cli/` structure when the user asks for project layout or scaffolding. Single-file CLIs are valid.316. **Gate `tab()` behind `process.argv[2] === 'complete'`** — the adapter eagerly resolves lazy subcommands, defeating rule 2 on every startup3233## Architecture (only when user asks for structure)3435See [architecture](references/architecture.md) for the full `cli/` layout with commands, prompts, and lib directories. Do not impose this structure unless the user explicitly asks for scaffolding or project organization.3637## Workflow38391. Define commands — [commands](references/commands.md)402. Add prompts if interactive — [prompts](references/prompts.md)413. Add spinners/progress for long-running work — [tty-ui](references/tty-ui.md)424. Wire tab completion — [tab-completion](references/tab-completion.md)435. Scaffold `cli/` if multi-command — [architecture](references/architecture.md)446. Configure bin entry — [sidecar setup](references/sidecar-setup.md)457. **Verify** — run with `--help` to confirm command registration468. Ship it — CI gates, versioning, npm or binary release — [release](references/release.md)4748## References4950- [Architecture](references/architecture.md) — Structure and responsibilities51- [Entrypoint](references/entrypoint.md) — runMain + tab completion wiring52- [Commands](references/commands.md) — defineCommand, args, subcommands53- [Prompts](references/prompts.md) — `@clack/prompts` reusable modules54- [Tab Completion](references/tab-completion.md) — `@bomb.sh/tab` citty adapter, completion protocol, per-shell install (compinit ordering, fish autoload), lazy-loading gate55- [TTY UI](references/tty-ui.md) — spinners and progress: `@clack/prompts` spinner first, `@bomb.sh/tty` inline regions and layout for richer feedback56- [Sidecar Setup](references/sidecar-setup.md) — bin entry, build config57- [Citty API](references/citty-api.md) — Resolvable, plugins, CLIError58- [Release](references/release.md) — CI gates, Changesets/release-please, npm + binary distribution59- [Versioning](references/versioning.md) — named schemes (SemVer/CalVer), 0.x vs 1.0, pre-releases, which bump, decision matrix, recording the policy60- [Update Command](references/update-command.md) — ask-first self-update: install-mode routing, streaming download (stall timeout, resumable retry), checksum-verified atomic swap, passive banner