Why dreamcli
Most TypeScript CLI frameworks treat the type system like decoration. You define flags in one place, then use parsed values somewhere else as a loosely typed blob. Env vars, config files, and interactive prompts live in separate universes. Testing means hacking process.argv.
dreamcli collapses all of that into a single typed schema.
Comparison
Approximate comparison of first-party, built-in support as documented by each project. Third-party plugins and custom glue can extend the other libraries.
| Capability | Commander | Yargs | Citty | CAC | Cleye | dreamcli |
|---|---|---|---|---|---|---|
| Type inference from definition | Manual .opts<T>() | Good | Good | Basic | Good | Full — flags, args, context |
| Built-in value sources | CLI, defaults, env | CLI, env, config | CLI, defaults | CLI, defaults | CLI, defaults | CLI, env, config, prompt, default |
| Schema-driven prompts | No | No | No | No | No | Integrated |
| Middleware / hooks | Lifecycle hooks | Middleware | Plugins / hooks | Events | No | Yes — typed middleware |
| Built-in test harness with output capture | No | No | No | No | No | runCommand() + capture |
| Shell completions from command definitions | No | Built-in (bash/zsh) | No | No | No | Built-in (bash/zsh) |
| Structured output primitives | DIY | DIY | DIY | DIY | DIY | Built-in (--json, tables, spinners) |
| Config file support | DIY | Built-in (.config()) | No | No | No | Built-in (XDG discovery, JSON) |
The Core Insight
The closest analog is what tRPC did to API routes — the individual pieces existed. The insight was wiring them so types flow end-to-end.
One flag declaration:
ts
import { } from '@kjanat/dreamcli';
.(['us', 'eu', 'ap'])
.('r')
.('DEPLOY_REGION')
.('deploy.region')
.({ : 'select', : 'Which region?' })
.('us');This single chain configures:
- Parsing — accepts
--region usor-r eu - Type inference —
flags.regionis"us" | "eu" | "ap"in the handler - Resolution — CLI → env var → config file → interactive prompt → default
- Help text — flag appears with description, alias, default, and choices
- Shell completions — tab-complete suggests
us,eu,ap - Testing — override via
env,config, oranswersin test harness
Design Principles
- Schema is the law. Everything derives from it: parsing, types, resolution, help, completions.
- Progressive disclosure. A 5-line script stays 5 lines. Complexity scales by composition.
- Deterministic behavior. Resolution rules are explicit and consistent.
- Portable by default. Core avoids runtime-specific APIs; adapters handle the edges.
- Ergonomic, not clever. Predictable DX over type-theory stunts.