Skip to content

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.

CapabilityCommanderYargsCittyCACCleyedreamcli
Type inference from definitionManual .opts<T>()GoodGoodBasicGoodFull — flags, args, context
Built-in value sourcesCLI, defaults, envCLI, env, configCLI, defaultsCLI, defaultsCLI, defaultsCLI, env, config, prompt, default
Schema-driven promptsNoNoNoNoNoIntegrated
Middleware / hooksLifecycle hooksMiddlewarePlugins / hooksEventsNoYes — typed middleware
Built-in test harness with output captureNoNoNoNoNorunCommand() + capture
Shell completions from command definitionsNoBuilt-in (bash/zsh)NoNoNoBuilt-in (bash/zsh)
Structured output primitivesDIYDIYDIYDIYDIYBuilt-in (--json, tables, spinners)
Config file supportDIYBuilt-in (.config())NoNoNoBuilt-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 us or -r eu
  • Type inferenceflags.region is "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, or answers in test harness

Design Principles

  1. Schema is the law. Everything derives from it: parsing, types, resolution, help, completions.
  2. Progressive disclosure. A 5-line script stays 5 lines. Complexity scales by composition.
  3. Deterministic behavior. Resolution rules are explicit and consistent.
  4. Portable by default. Core avoids runtime-specific APIs; adapters handle the edges.
  5. Ergonomic, not clever. Predictable DX over type-theory stunts.

Released under the MIT License.