# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [3.0.1] - 2026-07-21

No library code changed; this release ships agent-facing material that was
being built but never published.

### Added

- **The `cli-creation` skill now ships in the package** — `skills/` was absent
  from npm's `files` and JSR's `publish.include`, so consumers installing from
  either registry never received it.
- **Documentation lookup paths in the `cli-creation` skill** — `deno doc`
  against JSR (works whether the consumer installed from npm or JSR, including
  subpaths, `--json`, and `--filter`), plus the site's `llms.txt`,
  `llms-full.txt`, and `/raw/` markdown endpoints, so API shapes are read from
  the published package rather than recalled.

## [3.0.0] - 2026-07-21

The stable 3.0.0 release. It ships the code of 3.0.0-rc.20 plus the fix and
documentation below; the complete v3 feature record lives in the rc.1 - rc.20
sections that follow. Upgrading from 2.x is covered by the new
[upgrade guide](https://dreamcli.kjanat.dev/guide/upgrading-v3).

### Fixed

- **`runCommand()` rejected `--quiet`/`-q`** — the testkit gained the root-flag
  layer for `--json` in rc.1, but `--quiet` (added in rc.17) was never taught to
  it, so copying a real `mycli --quiet …` invocation into a test failed with
  `Unknown flag` (exit 2). Both spellings are now detected before the `--`
  separator, set quiet verbosity, and are stripped before parsing; a literal
  post-separator `--quiet` still reaches the command.
- **Mobile documentation hovers** — the twoslash bottom sheet collapsed to a
  2px sliver (a transformed popper ancestor became the containing block for the
  fixed sheet), its backdrop stayed over the page after dismissal, and closing a
  popup disabled it permanently via an inline `display: none`.

### Added

- **Upgrade guide** — `docs/guide/upgrading-v3.md` documents every 2.5.0 →
  3.0.0 breaking and behavioral change with before/after code.
- **Five new examples** covering the v3 surface: `flag-types.ts` (url, path
  with `create`, date, duration, bytes, count, keyValue, constraints, array
  separator/unique), `parser-control.ts` (negatable, duplicates, spelling
  parity), `standard-schema.ts` (Standard Schema v1 interop),
  `help-config.ts` (themes, `flagOrder`, routable default, JSON help),
  `output-extras.ts` (`out.color`, hyperlink gating, `setExitCode`).

### Changed

- **Examples modernized** — `basic.ts` uses function-form `.example()`,
  `json-mode.ts` and `middleware.ts` use `out.status()` and document
  `--quiet`, `transport-launcher.ts` replaces its hand-rolled port check
  with declarative numeric constraints.
- **`skills/cli-creation` rewritten for v3** — corrected stale grounding paths
  (`examples/standalone/`, `apps/docs/` no longer exist), rebuilt the pattern
  cookbook around the v3 surface with every snippet type-checked against the
  published types, and modernized the scaffolder templates (function-form
  examples, declarative numeric constraints instead of hand-clamping,
  `out.status()`, a `--quiet` test).
- **README refreshed** — the flag-types block lists the full v3 family
  (dropping the `flag.custom` URL sample that `flag.url()` superseded), the
  output sample shows `out.status()` / `out.setExitCode()` / `--quiet`, the
  completions sample covers the eager-flag form and fixes a
  `completion`/`completions` typo.

## [3.0.0-rc.20] - 2026-07-21

### Changed

- **ansispeck `^0.4.1`** — bumped from `^0.2.0` (npm dependency and JSR
  import map).

## [3.0.0-rc.19] - 2026-07-21

### Added

- **Expanded config discovery** (#61) — the default search now covers three
  scopes, first match wins, no merging:
  1. **Project**: the base directory (default `cwd`) and every ancestor up
     to the filesystem root, nearest first, each probed for
     `.{appName}.{ext}`, `{appName}.config.{ext}`, and the new
     `.config/{appName}.{ext}` convention.
  2. **User**: XDG / AppData as before, plus
     `~/Library/Application Support/{appName}/config.{ext}` on macOS.
  3. **System**: `/etc/{appName}/config.{ext}` on Linux and macOS.
- **`baseDir` discovery option** — `discoverConfig()` accepts a base
  directory to anchor the project-scope walk somewhere other than `cwd`.
- **`RuntimeAdapter.userConfigDirs` / `systemConfigDirs`** — optional
  ordered config roots supplied by the runtime adapters; discovery falls
  back to `[configDir]` / `[]` when a custom adapter omits them.

### Changed

- **Breaking**: `buildConfigSearchPaths(appName, options)` replaces the
  positional `(appName, cwd, configDir, loaders?)` signature; options carry
  `baseDir`, `userConfigDirs`, `systemConfigDirs`, and `loaders`.

## [3.0.0-rc.18] - 2026-07-21

### Added

- **`flag.path({ type: 'directory', create: true })`** — creates the directory
  (recursively) at resolution time when nothing exists at the path. Only
  available with `type: 'directory'`, enforced at the type level. Creation
  runs through a new `mkdir` seam: `RuntimeAdapter.mkdir`, overridable via
  run options, noop in the test adapter, so `src/core` stays process-free.

### Fixed

- **`flag.path({ type, mustExist: false })` rejected missing paths** — the
  builder recorded the explicit `mustExist: false`, but resolution errored on
  any missing path whenever path checks were active, making an optional
  to-be-created path (e.g. an `--outdir` that the command itself creates)
  impossible to declare. A missing path now passes when `mustExist` is
  `false`; an existing path is still type-checked.

## [3.0.0-rc.17] - 2026-07-20

### Added

- **`out.status()`** — a success/status line channel that writes to **stderr**
  (stdout stays clean for piping) and is suppressed under quiet verbosity.
  `info` remains stdout-bound; `warn`/`error` remain always-emitted.
- **Global `--quiet`/`-q` flag** — sets quiet verbosity on every CLI, wired
  like `--json`: detected and stripped at the root before dispatch (command
  schemas never see it), honoring the `--` separator so a literal `-q`
  positional reaches the command unchanged. Listed under root help's
  `Global options:`.

### Fixed

- **`.run()` misread a post-separator `--json`** — runtime preflight used a
  naive `includes('--json')`, so `mycli cmd -- --json` entered JSON mode via
  the adapter path even though `.execute()` correctly treats it as a
  positional. Preflight now uses the `--`-aware detection.

## [3.0.0-rc.16] - 2026-07-20

### Added

- **`resolveRenderContext(argv, options?)`** — pre-`run()` probe returning the
  output decisions the framework will make for a given argv: `jsonMode`
  (`--`-aware `--json` detection), `isTTY`, the gated `color` palette, and
  `isHyperlinkSupported`. Computed with the same composition `.execute()`/
  `.run()` feed into the output channel, so content styled before `run()` —
  banners, hand-rendered help — matches the channel that will actually render,
  with zero argv re-parsing.
- **`includesBeforeSeparator` / `stripBeforeSeparator`** exported from the
  package root — the `--`-aware root-flag primitives, for consumers who only
  need correct flag detection.

## [3.0.0-rc.15] - 2026-07-20

### Added

- **Configurable flag order in help** — `.help({ flagOrder })` controls the
  `Flags:` table order: `'alphabetical'` (default, short-aliased flags first
  then alphabetical) or `'declaration'` (the order `.flag()` was called). For
  full control, `.help({ sortFlags: (a, b) => number })` supplies a custom
  comparator over flag names that wins over `flagOrder`. Both are also
  accepted at runtime via `execute`/`run` `help` options.

## [3.0.0-rc.14] - 2026-07-20

### Added

- **Function-form examples** — `.example()` now accepts a
  `(meta) => string` builder alongside the literal string. `meta` carries the
  invoked program `name` (`help.binName`, falling back to the command name) and
  `version`, resolved at render time, so examples reference the real program
  name instead of hardcoding it and stay truthful under symlinks, `inheritName`,
  and `npx x` vs a global install. The thunk resolves in both `--help` text
  (composing with example highlighting) and `--json`/definition-schema output.
- **`ExampleMeta`** and **`ExampleCommand`** public types for annotating
  function-form example builders.

## [3.0.0-rc.13] - 2026-07-20

### Changed

- **Syntax-highlighted example commands** — the `Examples:` help section now
  highlights each command per token: the leading binary via `usageBin` (bold)
  and flag tokens (`-x`, `--long`) via `flag` (cyan), with values left plain.
  Tokenizing is quote-aware so `--scope './a b'` stays one token, and the
  existing color gate is respected (identity formatters when `NO_COLOR` or
  non-TTY), so `stripAnsi(colored)` still equals the plain rendering.

## [3.0.0-rc.12] - 2026-07-20

### Added

- **`out.isHyperlinkSupported`** — resolved OSC 8 hyperlink gate on the output
  channel. Honors `NO_HYPERLINKS`/`FORCE_HYPERLINKS` and the
  `--no-hyperlinks`/`--hyperlinks` flags, falling back to `isTTY`. Consumers
  rendering their own `out.color.link(...)` output can gate on it to keep OSC 8
  escapes out of piped or opted-out contexts.
- **Standard Schema v1 validation** — `flag.custom(schema)` and
  `arg.custom(schema)` accept any conforming validator (including callable,
  sync, and async schemas) with inferred output types and no runtime
  dependency. Validation runs after source resolution, so argv, env, config,
  prompt, stdin, and default values behave consistently; array flags and
  variadic args validate each element.
- **`isMainModule(import.meta)`**: Compatibility helper for consumers whose
  ambient `ImportMeta` interface omits `main`. Projects with normal Node, Bun,
  or Deno runtime typings can keep using the conventional
  `if (import.meta.main)` guard directly.

### Changed

- **`.manifest({ from: import.meta })` compatibility form**: Anchored manifest
  discovery now also accepts the calling module's complete `import.meta`
  object and extracts its URL internally. The conventional
  `{ from: import.meta.url }` form remains supported and preferred when runtime
  typings expose `url`. Together with `isMainModule(import.meta)`, the new forms
  avoid global `ImportMeta` augmentation, allowing consumers with an empty
  ambient `ImportMeta` interface to pass both TypeScript/Deno checks and JSR
  publish validation.

### Removed

- **Internal schema DSL** — the private template-literal parser, validator, and
  JSON Schema converter were replaced by direct definition-schema objects,
  removing roughly 1,500 lines without changing the generated schema.

### Fixed

- **`NO_HYPERLINKS` ignored in the help header** — root-help header hyperlinks
  gated on raw TTY status, so `NO_HYPERLINKS=1` (and `--no-hyperlinks`) still
  emitted OSC 8 links in the header. The gate now consults
  `out.isHyperlinkSupported`, which respects the standard hyperlink overrides.

## [3.0.0-rc.11] - 2026-07-15

### Fixed

- **JSR publish** — the Deno import map pinned `ansispeck` below `0.2.0`,
  so the JSR build resolved an ansispeck whose `createColors` rejected
  `osc8`'s two-argument call and failed type checking. The map now tracks
  `^0.2.0` alongside the npm dependency. (rc.10 published to npm only.)

## [3.0.0-rc.10] - 2026-07-15

### Changed

- **`osc8()` link text is optional** — it now defaults to the link target, so
  `osc8('https://x.dev')` renders as a hyperlink displaying its own URL.
  Terminals without OSC 8 support still show a usable address. Passing an
  explicit label is unchanged.
- **`osc8()` now delegates to `ansispeck`'s hyperlink constructor** — it emits
  the **ST** (`ESC \`) terminator instead of **BEL** (`\x07`). Both are valid
  OSC 8 terminators supported by modern terminals; the rendered link is
  unchanged. This removes dreamcli's last hand-rolled escape-sequence code.

## [3.0.0-rc.9] - 2026-07-11

### Added

- **JSON help** — `--help` with `--json` emits the CLI's definition document
  (the `generateSchema()` shape, `$schema`-tagged) on stdout instead of help
  text, for root help, `help <command>`, and `<command> --help` alike. New
  `generateCommandSchema(schema)` export produces the per-command document.

### Changed

- **`generateSchema()` emits the default command's full definition** — the
  `defaultCommand` field was a name-only string reference while the default
  command's flags/args appeared nowhere in the document (it never lists in
  `commands`). It is now the complete serialized command.

- **`flag.array()` rejects non-element builders at compile time** — the
  element position now requires a builder carrying only value-shape settings
  (kind, constraints, enum values, `parseFn`). Flag-level modifiers
  (`.alias()`, `.env()`, `.default()`, `.prompt()`, `.describe()`, …) were
  silently ignored on elements before; passing them is now a type error, as
  is `flag.path()` / `flag.count()` / `flag.keyValue()` / nested arrays.
  Type-level only — no runtime behavior change.

## [3.0.0-rc.8] - 2026-07-11

### Added

- **Themed help output** — root and per-command help render with a semantic
  color theme (bold+underlined section titles, cyan literals, dimmed metadata,
  yellow deprecations) whenever color is enabled, using the same gate as
  `out.color` (TTY + color support, no `--json`, `NO_COLOR`/`FORCE_COLOR`
  honored). Piped and JSON output stays byte-clean. Customize via
  `.help({ theme: (c) => ({ sectionTitle: c.magenta }) })` — the factory
  receives the gated ansispeck palette and is never invoked when color is off,
  so custom themes cannot leak escapes. New public types: `HelpTheme`,
  `HelpThemeFactory`.
- **Negatable booleans** — `flag.boolean().negatable()` accepts `--no-<name>`
  as `false`. Both spellings are one logical flag (last CLI occurrence wins,
  shared duplicate policy); the negated spelling is presence-only
  (`--no-foo=x` errors) and renders as `--[no-]foo` in help. Custom spelling
  via `{ alias }`, unadvertised via `{ hidden }`. Env/config/prompt/default
  resolution is unchanged.
- **Duplicate policy** — `.duplicates('last' | 'first' | 'error')` on
  singleton flags controls repeated CLI occurrences. `'error'` throws
  `ParseError` code `DUPLICATE_FLAG` with `{ flag, count, values }` details;
  `'first'` keeps the first value while still consuming later value tokens.
  Counted per logical flag (aliases + negated + parity spellings); env/config
  precedence is never a duplicate.
- **`packageRepositoryUrl(pkg, { require: true })`** — narrows the return
  type to `string`, throwing a `CLIError` (code `INVALID_REPOSITORY`) at the
  call site when the `repository` field is missing or not a recognisable
  locator. Removes the `!` assertion for manifests known to carry a valid
  repository, with the guarantee enforced at runtime rather than assumed.
- **Flag spelling parity (kebab ↔ camel)** — `--doThis` matches a flag named
  `do-this` and vice versa, for names and long aliases, on by default.
  Handler keys, help, completions, and suggestions stay canonical.
  Automatically disabled per pair when both spellings are declared explicitly;
  globally via `cli('mycli', { flags: { caseParity: false } })` (new
  `cli(name, options)` overload) or `RunOptions.flags`.

### Fixed

- `execute(argv, { flags })` overrides now reach the planner's dispatch arity
  scanning, not just command parsing.
- Generated meta-schema descriptions render `@defaultValue` tags as a
  `Default: <value>` paragraph instead of silently dropping them.

## [3.0.0-rc.7] - 2026-07-10

### Added

- **`.derive()` handlers may omit the return** — a validation-only derive with
  no `return` statement (sync or async) now typechecks; previously `void` /
  `Promise<void>` failed the `Output` constraint and forced an explicit
  `return undefined`.
- **String constraints** — `flag.string({ nonEmpty, minLength, maxLength, pattern })`
  plus chained `.nonEmpty()` / `.minLength()` / `.maxLength()` / `.pattern()`,
  mirroring the numeric-constraint model. Checked in fixed order
  (nonEmpty → minLength → maxLength → pattern) at both the CLI parse boundary
  (`INVALID_VALUE`) and env/config/prompt resolution (`CONSTRAINT_VIOLATED`),
  and emitted into the exported JSON Schema as `minLength` / `maxLength` /
  `pattern`.
- **`.separator()` and `.unique()` on array flags** — `.separator(',')` splits
  each CLI occurrence before element coercion, so `--region us,eu --region ap`
  works alongside repetition and errors name the offending element. Env/config
  string values use the same separator (default remains `','`). `.unique()`
  deduplicates the final resolved array (first-seen order) regardless of
  source.
- **`flag.url()`** — parses into a `URL`, with optional
  `{ protocols: ['https'] }` allowlist. Help shows `<url>`.
- **`flag.path()`** — path string with opt-in filesystem checks
  (`{ mustExist: true }`, `{ type: 'file' | 'directory' }`) validated after
  resolution through the runtime adapter, so CLI/env/config values are checked
  identically. `RuntimeAdapter` gained a `stat()` primitive (Node/Bun via
  `node:fs/promises`, Deno via `Deno.stat`; test adapters default to "nothing
  exists"), and `RunOptions` / `ResolveOptions` accept a `stat` injection for
  process-free execution — without one, checks are skipped.
- **`flag.date()`** — strict ISO-8601 → `Date` with optional inclusive
  `min` / `max` bounds. Rejects lenient `Date.parse` inputs (`'0'`,
  `'March 5'`) and calendar-invalid dates (`2026-02-31`).
- **`flag.duration()`** — `'30s'`, `'5m'`, `'1.5h'`, `'250ms'`, `'2d'`,
  compounds (`'1h30m'`), or bare milliseconds → milliseconds.
- **`flag.bytes()`** — `'512mb'`, `'1.5gb'`, `'64kb'` or bare bytes → bytes
  (binary units, case-insensitive).
- **`flag.count()`** — occurrence counter (`-vvv` → `3`, absent → `0`,
  explicit `--verbose=2` supported). Takes no value token, not promptable,
  and joins boolean flags in help (no `<value>` placeholder) and shell
  completions.
- **`flag.keyValue()`** — repeated `KEY=VALUE` merges into
  `Record<string, string>` (split at the first `=`, later keys win, unset
  resolves to `{}`). Env accepts comma-separated pairs; config accepts a plain
  object. New `'empty-object'` optional fallback joins arrays' `[]` as the
  kinds that resolve to a value when unset.

## [3.0.0-rc.6] - 2026-07-09

### Added

- **Runtime terminal sizing hooks** — `RuntimeAdapter` now exposes
  `getTerminalSize()` and `onTerminalResize()`. Node/Bun read
  `process.stdout.getWindowSize()` (falling back to `columns` / `rows`) and
  subscribe to stdout `resize`; Deno uses native `Deno.consoleSize()` and
  `SIGWINCH` on non-Windows platforms.

### Changed

- **Help output now fits the live terminal in `.run()`** — root and command help
  use runtime terminal columns when no explicit `.help({ width })` or runtime
  `help.width` is provided. Non-TTY or unavailable sizes still fall back to 80,
  and `.execute()` remains deterministic for tests.

## [3.0.0-rc.5] - 2026-07-08

### Removed

- **`createBunAdapter` export** (from `@kjanat/dreamcli/runtime`). Bun exposes a
  Node-compatible `process`, so the Bun adapter had been a pure passthrough to
  the Node adapter since the runtime version hard-fail was dropped — it blessed
  a runtime rather than abstracting distinct I/O. Auto-detection now maps Bun to
  the Node adapter directly and `src/runtime/bun.ts` is gone. Runtime detection
  is unchanged: `detectRuntime()` still reports `'bun'` and `RUNTIMES` still
  lists it. Callers that selected the Bun adapter explicitly should use
  `createNodeAdapter` (identical behavior) or rely on `createAdapter()`
  auto-detection (#51).

## [3.0.0-rc.4] - 2026-07-06

### Added

- **`.default(cmd, { route: true })`** — expose a default command under its own
  name as a routable top-level command, in addition to the bare/flags-only root
  surface. `mycli` and `mycli <name>` then run the same command object, and it
  is listed in the root `Commands:` section (tagged `(default)`) beside its
  siblings. Lets CLIs keep both a short root shorthand and an explicit,
  documented named command without duplicating the builder (#55). Opt-in only
  — the surface-only default remains the default behavior. With `route: true`
  the name wins over positional interpretation, so a positional value equal to
  the command's own name must be passed after `--`.

## [3.0.0-rc.3] - 2026-07-06

### Added

- **`@kjanat/dreamcli/version` subpath** — exposes the framework's own build
  identity as `DREAMCLI_VERSION` and `DREAMCLI_REVISION` (git short SHA) for
  diagnostics and bug reports. Deliberately a subpath rather than root exports
  so the constants stay out of root-import IDE completions; both report
  `'dev'` when running unbundled source.

## [3.0.0-rc.2] - 2026-07-06

### Added

- **`out.color` — context-aware ANSI colors** — every handler's `out` now carries
  a color palette powered by [`ansispeck`](https://github.com/kjanat/ansispeck).
  Colors auto-enable only when stdout is a TTY, JSON mode is off, and the
  environment supports color (`NO_COLOR`, `FORCE_COLOR`, `--no-color`, `--color`,
  `CI` respected); otherwise every formatter is an identity function, so handlers
  can write `out.log(out.color.green('✔ done'))` unconditionally.
  `createOutput({ color })` forces the palette on/off (useful for asserting
  colored output in tests), and the `Colors` type is re-exported from the root
  entrypoint. First runtime dependency — kept external in `dist` (~1 kB gzipped
  install).

### Changed

- **npm package now resolves built `dist` on every runtime** — the `bun`/`deno`
  → `src/*.ts` export conditions are gone and `src` is no longer published to
  npm (JSR still ships source). Bun and Deno consumers previously loaded raw
  source where build-time defines never ran, so `version` reported `'dev'`;
  they now get the same tested output as Node, with the real version. The
  export map is generated by tsdown at build time instead of hand-maintained.

## [3.0.0-rc.1] - 2026-07-02

### Added

- **`.completions({ as: 'flag' })`** — expose shell completion as an eager
  `--completions <shell>` flag on the CLI root instead of a `completions`
  subcommand. The planner intercepts the flag before dispatch, prints the script,
  and exits; root help advertises `--completions <bash|zsh|fish|powershell>` in its
  `Flags:` section. `.completions()` still defaults to `{ as: 'command' }`. Because
  the eager flag is intercepted before dispatch, the `--completions` name is
  reserved in flag mode — declaring a command/default flag of that name throws at
  build time.
- **Shell auto-detection** — `--completions` with no value (flag form) resolves the
  target shell from the environment: `$SHELL` (parsed as an interpreter path) wins
  when it names a supported shell, otherwise the presence of `$PSModulePath` selects
  PowerShell — the reliable signal for `pwsh`, which never sets `$SHELL`.
- **`detectShell(env)` / `normalizeShell(raw)`** — exported helpers behind the
  detection above; `normalizeShell` accepts bare names, `$SHELL` paths, and the
  `pwsh`/`powershell` pair.
- **`.help(config)` builder + extended `HelpOptions`** — configure root-help
  rendering: `inlineDefault`, `showDefaultInCommands`, and `footer` (plus the
  existing `width` / `hyperlinks`). Builder config is merged under runtime
  `options.help` (runtime wins).
- **Numeric constraints for number flags and args** — `flag.number()` and
  `arg.number()` now accept `{ min?, max?, int?, finite? }` (bounds inclusive),
  and the same constraints compose via chained `.int()` / `.min(n)` / `.max(n)` /
  `.finite(allow?)` methods (a later call overrides an earlier value, including
  one set in the options object). The chained methods are compile-time guarded
  to number-kind builders only. Constraints are enforced identically across the
  parse path and the env/config/prompt resolution path (order: finite → int →
  min → max): parse-time (CLI) violations throw `INVALID_VALUE`, while
  env/config/prompt coercion reports `CONSTRAINT_VIOLATED` (both exit code `2`).
  They are also surfaced in the exported JSON Schema as `minimum` / `maximum` and
  `type: "integer"` when `int` is set. `min` / `max` must be finite — a
  non-finite bound (`Infinity` / `-Infinity` / `NaN`) throws at construction. The
  resolved TypeScript value type stays `number`.
- **Prompt-level defaults for `confirm` and `input` prompts** — `confirm`
  prompts accept `default?: boolean` (drives the `(Y/n)` vs `(y/N)` hint and the
  empty-line result, so a confirm can default to **No**), and `input` prompts
  accept `default?: string` (shown in the hint as `(default: <value>)` and used
  when the user presses Enter, skipping `validate`). Precedence among defaults is
  prompt-level `default` > flag `.default()`. Unset `confirm` default stays
  backward compatible (`true`).

### Changed

- **BREAKING — the default command is now the root _surface_, not a named
  subcommand.** `.default(cmd)` registers the command only as the default; it is no
  longer added to `schema.commands`. Consequences:
  - It can no longer be invoked by its own name (`mycli mycmd` does not route to it;
    the token is treated as input to the default command).
  - It is omitted from the root `Commands:` list by default (re-enable with
    `.help({ showDefaultInCommands: true })`).
  - Root `--help` renders the default command's `Arguments:`/`Flags:` inline even
    when sibling commands exist (previously only when it was the sole command), and a
    sole default with no subcommands shows its own usage line directly instead of a
    `[command]` placeholder.
- **`finite` defaults to `true` (behavior change)** — `flag.number()` /
  `arg.number()` now **reject `Infinity` and `-Infinity`**, which previously
  passed straight through to handlers (`Number("Infinity")` is not `NaN`). Pass
  `{ finite: false }` (or `.finite(false)`) to opt back into accepting non-finite
  values. `NaN` continues to be rejected as before.
- **BREAKING — the runtime no longer hard-fails the host CLI on its version.**
  Adapter construction previously threw (a bare, uncaught `Error`) when the
  detected Node/Bun/Deno version was below DreamCLI's declared minimum, crashing
  the consumer's CLI — including on `--help`. That check is removed: as a
  dependency the consumer's package manager already enforces the `engines` range
  at install time (and more softly), and when DreamCLI is bundled/inlined the
  consumer — not us — owns their CLI's supported-runtime policy. `SUPPORTED_RUNTIMES`
  and the generated `engines` minimums are unchanged and remain the source of
  truth for docs and packaging.

### Fixed

- **Empty `input`-prompt answer no longer clobbers a flag's `.default()`** — a
  flag declared with both `.default(...)` and an `input` prompt previously
  resolved to `""` when the user submitted an empty line, because empty input was
  treated as a real answer that short-circuited the default fallback. An empty or
  blank `input` answer with no prompt-level `default` is now treated as "no
  answer", so resolution falls through to the flag's `.default()` (or, for a
  required flag, the standard missing-flag error).

### Fixed

- **Root `--help` now matches consistently with `--version`** (#29) — previously
  `--help` / `-h` were intercepted at the root only when they were the very first
  token, while `--version` / `-V` matched anywhere before `--`. A help flag
  preceded only by flags (e.g. `mycli --verbose --help`) now shows root help,
  mirroring `mycli --verbose --version`. A help flag that follows a subcommand
  token still scopes to that subcommand (`mycli deploy --help` → `deploy` help);
  `--version` remains a global, position-independent flag by design (there is no
  per-command version). The bare `help` token now triggers root help from the same
  position as `--help`, and the `--` end-of-options separator is respected for both.
- **Generated `--help` now advertises the active built-in global flags** (#32) — root
  help renders a `Global options:` block listing `--help, -h` and `--json` (always),
  `--version, -V` (when a version is set), and `--config <path>` (when `.config()`
  enabled config discovery). Previously these framework-provided flags were invisible
  in help even though they worked. `--completions` is unchanged (still advertised via
  the inline surface when active).
- **Root usage no longer prints the default command's name** — the merged/sole-default
  usage line rendered `Usage: <bin> <default-name> …`, teaching an invocation that does
  not route (`.default()` is not a named command, so the token is consumed as the
  default's first positional). It now renders under the bin name only
  (`Usage: <bin> [flags] <args>`).
- **`inlineDefault: false` no longer hides a sole default command's interface** — when
  the default command is the only surface (no visible subcommands and no eager
  `--completions` flag), root help would collapse to a bare `Usage: <bin> [options]`
  with no discoverable args/flags. The default is now always rendered inline in that
  case, since suppressing it leaves no other path to its interface.

### Fixed

- **`runCommand()` now honors a CLI-level `--json` passed in `argv`** — the testkit
  gained the same root-flag layer as the real CLI: a `--json` before the `--`
  separator enables JSON mode and is stripped before parsing, instead of failing
  with `Unknown flag --json` (exit 2). A literal `--json` after `--` still reaches
  the command unchanged. The existing `{ jsonMode: true }` option keeps working —
  either source enables JSON mode. Copying a real `mycli --json …` invocation into a
  test now works verbatim.
- **`createTestPrompter()` now runs an `input` prompt's `validate`** — a queued
  string answer to an `input` prompt is validated exactly as the terminal prompter
  does; an answer that fails validation is rejected as a cancellation rather than
  injected verbatim as the resolved value, so prompt validation is integration-testable
  through `runCommand(cmd, argv, { answers })`. Non-string answers stay verbatim so
  downstream coercion paths remain testable.

## [2.5.0] - 2026-06-28

### Added

- **`.manifest()` — runtime-agnostic manifest discovery** — discover CLI metadata
  from any manifest file, not just `package.json`. Pass `files` to choose candidate
  filenames in priority order (e.g. `['deno.json', 'jsr.json']`); discovery walks up
  from `cwd` (or `from`) and the nearest manifest directory wins. Files are parsed as
  JSON with a JSONC fallback, so `package.json`, `deno.json`, `jsr.json`, and
  `deno.jsonc` all work — including manifests with `//` / block comments or trailing
  commas (a dependency-free, string-aware strip that leaves `//` inside string values
  such as URLs untouched).
- **`discoverManifest()`** — the generalized discovery helper behind `.manifest()`,
  accepting `{ startDir, files }`.
- **Optional scope retention in name inference** — `inferName` now accepts
  `{ scope: 'keep' | 'strip' }` (and `inferCliName(pkg, { stripScope })`) so a scoped
  `name` like `@scope/mycli` can be kept verbatim instead of always stripping to
  `mycli`. Relevant for `deno.json` / `jsr.json`, which have no `bin` field.
- **`.denoJson()`** — deprecated convenience preset for
  `.manifest({ files: ['deno.json', 'deno.jsonc', 'jsr.json'] })` (see Deprecated below).

### Changed

- **Manifest discovery now requires recognized metadata to count as a hit** — a
  parseable but metadata-less manifest (`{}`, or one carrying only non-metadata
  fields such as `dependencies` / `scripts` / `type`, or a config-only `deno.json`
  with just `tasks` / `imports`) is no longer treated as a match. Discovery now
  walks up to parent directories and tries the remaining candidate files instead
  of halting. This also changes the deprecated `discoverPackageJson()` /
  `.packageJson()`: where a metadata-less `package.json` previously halted the
  walk-up and resolved to `{}`, the walk-up now continues, so an ancestor's
  version can surface (relevant in monorepos). Pass pre-loaded `data` or an
  explicit `from` / `startDir` to pin discovery to one directory.

### Deprecated

- **`.packageJson()`, `.denoJson()`, and `discoverPackageJson()`** — superseded by
  `.manifest()` / `discoverManifest()`, whose defaults (`['package.json']`) match the
  old behavior. All still work, delegating to the generalized path.

## [2.4.1] - 2026-06-27

### Fixed

- **Destructuring `out` and activity handles** — `Out` methods and the spinner/progress activity
  handle methods are now bound to their instance, so pulling them off via destructuring (e.g.
  `const { done } = handle`) no longer breaks `this`-dependent behaviour and cleanup
  (https://github.com/kjanat/dreamcli/pull/40).

## [2.4.0] - 2026-06-23

### Added

- **`out.setExitCode(code)` for normal-output status exits** — command handlers can now request a
  process exit code without throwing or emitting error-shaped output, covering status/check CLIs that
  should print their normal report while still signalling degraded state to scripts
  (https://github.com/kjanat/dreamcli/issues/27).

### Fixed

- **Unknown root commands under no-arg defaults now report `UNKNOWN_COMMAND`** — when a CLI has a
  `.default()` command with no positional args, an unknown root token no longer falls through to the
  default parser as an unexpected positional. Defaults that declare positional args still receive
  those root tokens as before (https://github.com/kjanat/dreamcli/issues/26).

### Security

- **Hardened trailing-separator trimming against polynomial ReDoS** — the home/config path
  resolution, `package.json` repository-URL normalization, and runtime-binary `basename` helpers
  previously stripped trailing slashes with backtracking regexes (`/[\\/]+$/`, `/\/+$/`) that CodeQL
  flagged as polynomial regular expressions (`js/polynomial-redos`). They now route through a shared
  linear-time `stripTrailing` scan, so adversarial slash-heavy input can no longer trigger quadratic
  matching. No behavior change for valid inputs.

## [2.3.0] - 2026-06-20

### Fixed

- **Command dispatch now respects flag value-arity** — a space-separated value-flag value (e.g.
  `--source anthropic`) is no longer mistaken for a command name when it collides with a registered
  command, so the default-command fallback runs as intended
  (https://github.com/kjanat/dreamcli/issues/25). The command-name scan now skips the token a
  space-separated value-flag consumes (mirroring the parser's own value-consumption rules); the
  inline `--source=anthropic` form already worked, and both forms are now consistent.
- **Root `--version`/`--json` interception now respects the `--` end-of-options separator**
  (https://github.com/kjanat/dreamcli/issues/28). A `--version` or `--json` token after `--` is
  treated as a literal positional instead of triggering version output or JSON mode.

## [2.2.1] - 2026-06-10

### Added

- **`.links()` — OSC 8 hyperlinks in the root-help header** — the program name and version on the
  first line of root `--help` output can now carry
  [OSC 8 hyperlinks](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) in
  supporting terminals ([#20](https://github.com/kjanat/dreamcli/issues/20)). Pass explicit URLs
  (`.links({ name, version })`) or call `.links()` with no arguments to derive them from
  `package.json` metadata when `.packageJson()` is active: the name links to the normalized
  `repository` URL (falling back to `homepage`), and the version links to the forge release tag
  (`{repo}/releases/tag/v{version}` on GitHub, `{repo}/-/releases/v{version}` on GitLab). Escapes
  are emitted only when stdout is a TTY (overridable via the new `help.hyperlinks` option) and only
  on the header line — usage lines, the `--help` hint, the commands table, `--version` output, and
  completion scripts stay plain.
- **ANSI/OSC-aware help width helpers** — help padding and wrapping now measure _visible_ width:
  the shared `padEnd()`/`wrapText()` helpers strip ANSI CSI (colors) and OSC (hyperlink) escape
  sequences before counting columns, so escape-bearing text no longer mangles `--help` alignment.
  New public exports `osc8(url, text)` (wrap text in an OSC 8 hyperlink) and `visibleWidth(text)`
  (escape-aware width measurement) support custom help rendering.
- **`packageRepositoryUrl(pkg)`** — new public helper that normalizes a package's `repository`
  field to a browsable `https://` URL, handling the locator formats npm accepts (`{ type, url }`
  object form, `git+`/`.git` affixes, scp-style `git@host:u/r.git`, and the
  `github:`/`gitlab:`/`bitbucket:`/bare `u/r` shorthands). `PackageJsonData` now also parses the
  `homepage` and `repository` fields.

## [2.2.0] - 2026-06-09

### Added

- **`.packageJson(data)` — pre-loaded metadata** — `CLIBuilder.packageJson()` now accepts an
  already-imported `package.json` object (e.g. `import pkg from './package.json' with { type: 'json' }`),
  skipping filesystem discovery entirely. The data is detected via field shape (`name`/`version`/
  `description`/`bin`), and its `version`/`description` are merged into the CLI schema at builder time
  so the data form works in **both** `.run()` and `.execute()` (the filesystem-free path that
  previously couldn't consume `.packageJson()` at all). Explicit `.version()`/`.description()` still
  win; an empty `{}` or a settings-shaped object falls through to the settings overload, so this is a
  non-breaking addition.
- **`.packageJson({ from })` — anchored discovery** — discovery can now be anchored to an explicit
  location instead of the runtime cwd. Pass `{ from: import.meta.url }` (also accepts path strings,
  `file:` URL strings, or `URL` instances) so installable CLIs (`npm i -g`, `bunx`, `npx`) report
  their OWN version from any working directory. `discoverPackageJson(adapter, startDir?)` gained a
  matching optional anchor parameter.

## [2.1.0] - 2026-04-16

### Added

- **Prompt — flag kind compatibility validation** — `FlagBuilder.prompt()` now rejects incompatible
  prompt/flag combinations at compile time via `AllowedPromptConfig<C>` (e.g.,
  `flag.enum([…]).prompt({ kind: 'multiselect' })` is a TypeScript error). A runtime validation gate
  in `resolvePromptValueWithConfig()` catches mismatches before the prompter is invoked, throwing a
  `CONSTRAINT_VIOLATED` `ValidationError` with an actionable `suggest` message.
- **`flagKind` phantom discriminator** — `FlagConfig` now carries a `flagKind` field (phantom — never
  read at runtime) so the type system can distinguish all six flag kinds. `AllowedPromptConfig` uses
  an indexed-access map (`PromptConfigByFlagKind`) for union-safe resolution.
- **PowerShell completion playground** — added a Windows-friendly `pwsh-demo` workspace with a
  `.cmd` launcher, native `Register-ArgumentCompleter` registration, README install steps, and an
  end-to-end smoke test so PowerShell completions can be exercised outside unit tests.

### Changed

- **Meta-descriptions build** — `scripts/build-meta-descriptions.ts` pipes generated source through
  `dprint fmt --stdin ts` instead of writing/reading a temp file and formatting in-place.
- **PowerShell install guidance** — current-session docs and generated script headers now use
  `Out-String | Invoke-Expression`, matching the PowerShell flow that reliably evaluates multiline
  completion output.
- **Bun support floor** — relaxed the documented and enforced Bun minimum from `>= 1.3.11` to
  `>= 1.3` across package metadata, runtime support checks, examples, and docs.

### Fixed

- **PowerShell completions** — enum values now complete while the active token is still in progress,
  stop suggesting flags after `--`, and quote accepted values when spaces or quotes would otherwise
  produce invalid PowerShell input.
- **PowerShell playground launcher** — fixed the Windows `pwsh-demo.cmd` shim so the example runs
  reliably from PowerShell and Command Prompt.
- **Package metadata** — moved `vite` from `optionalDependencies` to `devDependencies` so consumer
  installs do not see it as a runtime dependency.
- **Docs deploy** — remove dead Workers runtime vars (`BUN_VERSION`, `NODE_OPTIONS`) that had no
  effect on the static assets Worker, and add `html_handling: "drop-trailing-slash"` so clean URLs
  resolve correctly instead of 404ing on trailing slashes.

## [2.0.1] - 2026-04-07

### Fixed

- **Schema URL resolution** — `$schema` in emitted definition documents now points at
  `dreamcli.schema.json` instead of the `/schema` subpath export, which doesn't resolve on the
  jsdelivr CDN.
- **GitHub Pages deploy** — use `env` import from `node:process` for env access and set VitePress
  `base` to `/dreamcli/` so assets and links resolve correctly on GitHub Pages.\
  This allows for the old github pages deploy to work as an alternative to the cf workers.
- **Publish pipeline** — split build and publish into separate jobs, run build before pack with
  `--ignore-scripts` to prevent prepack output from breaking `GITHUB_OUTPUT` parsing, hardcode the
  npm CDN schema URL instead of the unreliable jsr.io esm.sh path, replace `actions/setup-node`
  with bun's native registry auth, and switch internal imports to `#dreamcli/*` subpath imports.
- **Package exports map** — moved conditional exports from `publishConfig` into top-level `exports`
  so local resolution matches what consumers see after install.

## [2.0.0] - 2026-04-07

### Added

- **String-literal schema DSL** — added a single-source schema surface that parses definitions at
  compile time and runtime, then reuses the same model for JSON Schema generation.
- **Published definition schema export** — added `@kjanat/dreamcli/schema` so tooling and docs
  consumers can import the generated definition schema locally instead of relying on the CDN URL.
- **Fish and PowerShell shell completions** — expanded completion support beyond Bash and Zsh.
- **Source-backed docs surfaces** — added generated API inventory pages, per-entrypoint symbol
  routes, source-backed example pages with related symbol links, and reference guides for planner,
  resolver, output, schema, support, migration, troubleshooting, and semantic deltas.
- **`gh-project` workflow helper** — added a DreamCLI-powered project tool for syncing the
  re-foundation task board and PRD state.

### Changed

- **Package identity and build pipeline** — the package is now published as `@kjanat/dreamcli`,
  ships ESM-only, emits the definition schema during the tsdown prepare hook, tightens published
  exports for runtime-specific consumers, and hardens npm, JSR, and docs release checks.
- **Docs app architecture** — VitePress now builds reference and example pages from data loaders
  instead of static generated files, adds runtime/twoslash settings UI, improves mobile twoslash
  UX, and copies root artifacts into deployed docs output.
- **Examples and walkthroughs** — the `gh` example grew into a multi-file workspace canary, and the
  example set now doubles as richer docs source with broader JSDoc coverage and better walkthrough
  material.

### Fixed

- **Completion and alias handling** — hidden compatibility aliases now parse correctly, Bash and Zsh
  completion behavior is safer and more consistent, and shell completion edge cases were tightened.
- **Aggregate validation diagnostics** — mixed flag and arg validation failures now surface clearer
  per-issue labels plus value-source labels such as `env ...` and `stdin`.
- **Schema and docs integration** — schema URLs, generated meta descriptions, twoslash rendering,
  and source-backed docs pages now build reliably across local, CI, and Cloudflare deploys.
- **CLI and runtime edge cases** — unresolved schema references now fail closed, runtime/support
  checks were hardened, and several dispatch/output/runtime regressions were corrected.

## [1.0.0] - 2026-04-02

### Added

#### Release Automation

- **GitHub Actions npm publish workflow** (`.github/workflows/publish-npm.yml`) — publishes the
  package to npm on GitHub release with provenance enabled, bringing npm release automation in line
  with the existing JSR publish flow.

#### Canonical Semantics Guide

- **`docs/guide/semantics.md`** — centralized reference for parser and resolver behavior, including
  repeated flags, short-flag stacking, `--` separator rules, `--no-*` alias behavior, value-source
  precedence, non-interactive prompt skipping, propagated-flag masking, and default-command root
  help/completion semantics.

#### Plugin Lifecycle Hooks

- **`plugin(hooks, name?)`** and **`.plugin(definition)`** expose a typed extension surface around
  command execution.
- **Lifecycle phases** — `beforeParse`, `afterResolve`, `beforeAction`, and `afterAction` let
  plugins observe or instrument execution without reaching into CLI internals.

#### `derive()` Command Context

- **`command(...).derive(handler)`** adds typed command-scoped pre-action context derived from fully
  resolved flags and args.
- **Derived context merges into `ctx`** so commands can validate once and consume typed values in
  the action handler.

#### Schema Export and Validation

- **`generateInputSchema()`** exports JSON Schema from CLI definitions for machine validation and
  tooling.
- **Schema export docs and reference coverage** now document export formats, discriminator behavior,
  and default/hidden command handling.

#### Runtime Surface and Execution Options

- **Runtime support matrix and version guards** added around adapter creation.
- **`run()` accepts `jsonMode`** in options, letting callers force structured output without
  shell-level flags.
- **`out.table()` format and stream overrides** expose finer control over tabular output.

### Changed

- **Docs navigation and entrypoints** — guide pages now link to the canonical semantics guide, the
  API reference landing page includes quick import guidance and key factories per subpath export,
  and the VitePress sidebar surfaces the semantics page under the Advanced guide section.
- **Public API surface tightened** — runtime exports were pruned and guarded, explicit `require`
  conditions were added to package exports, and self-referencing package imports were hardened.
- **Docs and examples expanded** — JSDoc/reference coverage grew across exported symbols, schema
  export/testing/runtime docs were added, and the `gh` walkthrough became a multi-file example
  package.
- **CI and packaging hardened** — version-sync checks, supported Node pinning, preview publish
  verification, and Bun-pack package validation were added around the release surface.

### Fixed

- **Default-command UX** — single-command root help is merged correctly, root completions surface
  default-command flags, unknown root flags are rejected cleanly, and schema discriminator handling
  matches the actual default-command surface.
- **Stdin and runtime behavior** — stdin reads defer until dispatch needs them, empty pipes are
  distinguished from no pipe, and test adapters now match real runtime behavior more closely.
- **Parser/help/completion/output edge cases** — optional array flags resolve to `[]`, variadic help
  formatting is corrected, bash/zsh completion edge cases are hardened, non-finite JSON values are
  rejected, and table options are preserved correctly.

## [0.9.2] - 2026-03-30

### Added

#### Stdin-Backed Positional Arguments

- **`ArgBuilder.stdin()`** lets positional args consume piped stdin when no CLI token is provided.
- **`RuntimeAdapter.readStdin()`** adds full stdin reads to the runtime contract across Node, Bun,
  and Deno adapters.
- **`RunOptions.stdinData` and testkit plumbing** let in-process tests feed stdin-backed commands
  without touching real process state.
- **Comprehensive stdin coverage** added across schema, resolver, runtime, and testkit tests.

### Changed

- Positional-arg resolution for stdin-enabled args expanded from **CLI → env → default** to
  **CLI → stdin → env → default**.
- Scripts now separate **`lint`** from **`format`**, so linting no longer doubles as a rewrite step.

## [0.9.1] - 2026-03-30

### Added

#### Default Command Support

- **`CLIBuilder.default(command)`** lets a CLI run a fallback command when no subcommand is
  specified.
- **Root args and flags flow through the default command** while explicit subcommands still take
  precedence.

#### Package.json Auto-Discovery

- **`CLIBuilder.packageJson(settings?)`** — opt-in builder method that enables automatic
  `package.json` discovery at `.run()` time. Walks up from `cwd` to find the nearest `package.json`
  and merges `version` and `description` into the CLI schema. Explicit `.version()` and
  `.description()` calls always take precedence over discovered values.
- **`PackageJsonSettings.inferName`** — when `true`, infers the CLI binary name from the `bin` key
  (first key of the object) or the package `name` field (scope stripped). Defaults to `false`.
- **`discoverPackageJson(adapter)`** — pure function that walks up from `adapter.cwd` to find and
  parse the nearest `package.json`. Returns `PackageJsonData | null`. All I/O flows through the
  adapter — fully testable with virtual filesystems.
- **`inferCliName(pkg)`** — resolves CLI name from `PackageJsonData` with priority: bin key → scoped
  name (stripped) → `undefined`.
- **Silent error handling** — malformed JSON, non-object roots, and missing `package.json` all
  return `null` (not errors). Deno permission denials degrade gracefully via the adapter's existing
  `readFile` contract.
- **Completions skip** — package.json discovery is skipped for the `completions` subcommand,
  matching the existing config discovery skip pattern.
- **43 new tests** — `package-json.test.ts` (24 unit tests: walk-up resolution, field extraction,
  error resilience, Windows path termination) and `cli-package-json.test.ts` (19 integration tests:
  version/description fill, name inference, precedence, walk-up, completions skip, combined with
  config, error resilience).

#### `help` Virtual Subcommand

- **`BINARY help <command>`** produces the same output as `BINARY <command> --help`. Rewrites argv
  via recursive `execute()` — no dispatch duplication.
- **Nested support** — `help db migrate` works like `db migrate --help`.
- **Bare `help`** shows root help.
- **Defers to real commands** — if the user registers a command named `help` (or aliased as `help`),
  it takes priority over the virtual subcommand.
- **`--json` propagation** — `help --json <command>` correctly preserves json mode (help text routes
  to stderr, stdout reserved for data).
- **10 new tests** across `cli.test.ts` (7), `cli-nesting.test.ts` (2), `cli-json.test.ts` (1).

#### Arg Environment Variable Resolution

- **`ArgBuilder.env(varName)`** binds a positional argument to an environment variable. When the CLI
  value is absent, the resolver reads the env var and coerces the string to the arg's declared kind
  (passthrough for strings, `Number()` with NaN guard for numbers, `parseFn` invocation for custom
  args). Resolution order: **CLI → env → default**.
- **`ArgSchema.envVar`** field (`string | undefined`) stores the env var name on the runtime schema
  descriptor.
- **Env coercion for args** via `coerceArgEnvValue()` in the resolver. Handles `string`
  (passthrough), `number` (parse + NaN guard), and `custom` (delegates to `parseFn`, wraps thrown
  errors).
- **`[env: VAR]` annotation** in help output for args with env bindings, matching the existing flag
  annotation style.
- **Actionable required-arg error hints** — `buildRequiredArgSuggest()` generates suggestions
  including the env var when configured (e.g. "Provide a value for \<target\> or set
  DEPLOY_TARGET").
- **16 new tests** — `resolve-arg-env.test.ts` (15 tests covering string/number/custom coercion,
  CLI > env > default precedence, deprecation warnings, error cases) and 1 help output test for the
  `[env: VAR]` annotation.

#### Command Metadata

- **`CommandMeta`** added to action handlers and middleware, carrying the CLI name, invoked binary,
  version, and resolved leaf command name.

#### Documentation Site, README, and Examples

- **README** added with project pitch, usage, install guidance, and comparison table.
- **Examples directory** added with seven implementation examples.
- **VitePress documentation site** added with concepts, guide, and reference sections.
- **GitHub Pages deploy workflow and sitemap** added for hosted docs.
- **Walkthrough guide** added for a GitHub CLI-style example application.

### Changed

- Completion generation was reorganized into shell-specific generators and the package/tooling
  surface was refreshed for the `0.9.1` milestone.
- Comprehensive public-facing JSDoc examples were added to the builder APIs.

### Fixed

- Default commands no longer swallow nested unknown-command errors.

## [0.9.0] - 2026-02-11

### Added

#### Deno Adapter

- **`createDenoAdapter(ns?)`** — full `RuntimeAdapter` implementation for Deno. Reads argv from
  `Deno.args` (prepends synthetic `['deno', 'run']` for parity), env from `Deno.env.toObject()`, cwd
  from `Deno.cwd()`, stdout/stderr via `TextEncoder` → `Deno.stdout.write`/`Deno.stderr.write`,
  stdin via `Deno.stdin.readable` stream with line-buffered reader, TTY detection via
  `isTerminal()`, `readFile` via `Deno.readTextFile`, and homedir/configDir from env vars.
- **Permission-safe** — `PermissionDenied` errors gracefully degrade: env falls back to `{}`, cwd to
  `/`, readFile to `null`. Non-permission errors propagate.
- **`deno-builtins.d.ts`** — ambient type declarations for `TextEncoder`, `TextDecoder`, and
  `ReadableStream` (needed because `lib: ["ES2022"]` excludes web platform APIs).
- **`createAdapter()` auto-detection** now handles Deno runtime via `globalThis.Deno` feature
  probing.
- Re-exported `createDenoAdapter` and `DenoNamespace` from `@kjanat/dreamcli/runtime` subpath.

#### Cross-Runtime CI

- **GitHub Actions CI workflow** (`.github/workflows/ci.yml`) — lint+typecheck (Bun), test matrix
  (Node LTS + Bun stable), Deno smoke test, build with publint+attw.
- **Deno smoke test** (`scripts/deno-smoke-test.ts`) — runs on real Deno runtime after build,
  exercising `createDenoAdapter()` against actual Deno APIs.

#### JSR Publishing

- **`deno.json`** with JSR package config (`@kjanat/dreamcli`), three subpath exports, publish
  include/exclude rules.
- **GitHub Actions publish workflow** (`.github/workflows/publish-jsr.yml`) — publishes to JSR on
  GitHub release with OIDC provenance.

### Changed

- **`.ts` import extensions** — all import specifiers switched from `.js` to `.ts` via
  `allowImportingTsExtensions`. tsconfig updated: `noEmit: true` +
  `allowImportingTsExtensions: true` replace `declaration`/`declarationMap`/`sourceMap`/`outDir`
  (all handled by tsdown). Removes the need for Deno's `unstable: ["sloppy-imports"]`.
- `detectRuntime()` updated with Deno detection via `globalThis.Deno?.version?.deno`.
- `createAdapter()` switch now covers `'deno'` case alongside `'node'` and `'bun'`.
- Completion generator: `typeof` check on `globalThis` narrowed to avoid Deno type errors.
- `runtime/deno.ts` expanded from empty stub (~5 lines) to full implementation (~318 lines).
- Test count: 1695 tests across 47 test files (up from 1658 in v0.8.0).

## [0.8.0] - 2026-02-11

### Breaking

- **Subpath exports** — single `"."` entry split into `"."`, `"./testkit"`, `"./runtime"`. Test
  utilities (`runCommand`, `createCaptureOutput`, `createTestPrompter`, `createTestAdapter`,
  `PROMPT_CANCEL`) moved to `@kjanat/dreamcli/testkit`. Runtime adapters (`createAdapter`,
  `createNodeAdapter`, `createBunAdapter`, `detectRuntime`, `ExitError`, `RUNTIMES`,
  `RuntimeAdapter`) moved to `@kjanat/dreamcli/runtime`. `createTestAdapter`/`TestAdapterOptions` exported
  only from `@kjanat/dreamcli/testkit`.

### Added

#### Spinner & Progress Bar

- **`out.spinner(text, options?)`** creates a spinner handle for indeterminate progress feedback.
  Returns a `SpinnerHandle` with `update(text)`, `succeed(text?)`, `fail(text?)`, `stop()`, and
  `wrap(promise, options?)` for auto-succeed/fail on promise settlement.
- **`out.progress(options)`** creates a progress bar handle. Pass `total` for determinate mode
  (percentage bar); omit for indeterminate (pulsing animation). Returns a `ProgressHandle` with
  `increment(n?)`, `update(value)`, `done(text?)`, and `fail(text?)`.
- **Four rendering modes** with automatic dispatch:
  - **TTY** — animated braille spinner (80ms frames) and bar rendering with ANSI cursor control.
    Hides cursor during animation, restores on terminal methods.
  - **Static** (`fallback: 'static'`) — plain text at lifecycle boundaries (start, succeed, fail).
    No ANSI codes. For CI and piped output.
  - **Noop** (`fallback: 'silent'`, default) — all methods are no-ops. Silent in non-TTY.
  - **JSON mode** — always noop (structured output only).
- **Active handle tracking** — at most one spinner or progress may be active at a time. Creating a
  new one implicitly stops the previous to avoid garbled terminal output.
- **`ActivityEvent` discriminated union** — 10-variant DU capturing spinner and progress lifecycle
  events (`spinner:start`, `spinner:update`, `spinner:succeed`, `spinner:fail`, `spinner:stop`,
  `progress:start`, `progress:increment`, `progress:update`, `progress:done`, `progress:fail`).
- **Testkit capture handles** — `CaptureOutputChannel` subclass overrides `spinner()` and
  `progress()` to record `ActivityEvent[]` for assertion. `CapturedOutput.activity` array added.
- **New public types** exported from barrel: `ActivityEvent`, `Fallback`, `SpinnerHandle`,
  `SpinnerOptions`, `ProgressHandle`, `ProgressOptions`.
- **`out.stopActive()`** public method on `Out` to clean up active spinner/progress timers. Prevents
  process hangs when a handler throws before reaching a terminal method (`stop`, `succeed`, `fail`,
  `done`). `runCommand()` calls it automatically in a `finally` block; direct `createOutput()` users
  call it themselves.
- **`progress:increment` activity event** — 10th `ActivityEvent` variant
  `{ type: 'progress:increment', delta }`. `increment()` now emits this instead of reusing
  `progress:update`, making capture events unambiguous for testing.

### Changed

- `Out` interface extended with `spinner()` and `progress()` methods.
- `CapturedOutput` extended with `activity: ActivityEvent[]` field.
- `createCaptureOutput` now returns a `CaptureOutputChannel` that records activity events separately
  from stdout/stderr.
- `FlagParseFn<T>` widened from `(raw: string) => T` to `(raw: unknown) => T`. Config files carry
  structured JSON data — `parseFn` now receives the raw value directly and is responsible for
  narrowing. CLI/env still pass strings; config passes the JSON value as-is.
- `Out` interface extended with `stopActive()` method for explicit timer cleanup.
- `ActivityEvent` union widened from 9 to 10 variants (added `progress:increment`).
- `OutputChannel` refactored: activity handle implementations extracted to `activity.ts` (~581
  lines), `WriteFn` type and `writeLine` helper extracted to `writer.ts` (~30 lines). `index.ts`
  reduced from 1156 to 589 lines. No public API changes.
- All activity handle output (static and TTY) now routes to stderr. Previously static mode used a
  `StaticWriters` pair routing some output to stdout; the dual-writer abstraction is removed.
- `runCommand()` calls `out.stopActive()` in a `finally` block, ensuring timer cleanup on handler
  exceptions.
- **Resolve coercion unified** — three near-identical functions (`coerceEnvValue` ~105 lines,
  `coerceConfigValue` ~120 lines, `coercePromptValue` ~120 lines) replaced by single `coerceValue()`
  using `CoerceSource` discriminated union (`'env' | 'config' | 'prompt'`). Error messages
  parameterized via `sourceLabel()`/`sourceDetails()`/`coercionError()` helpers. `resolve/index.ts`
  reduced from ~1115 to ~940 lines.
- **Activity types extracted** — 7 activity/output types (`Fallback`, `SpinnerOptions`,
  `SpinnerHandle`, `ProgressOptions`, `ProgressHandle`, `ActivityEvent`, `TableColumn`) moved from
  `schema/command.ts` to `schema/activity.ts` (~150 lines). `command.ts` reduced from 898 to 784
  lines.
- **Root help extracted** — `formatRootHelp()` + `padEnd()` + `wrapText()` moved from `cli/index.ts`
  to `cli/root-help.ts` (~133 lines). Uses structural `CLISchemaLike` interface to avoid circular
  imports. `cli/index.ts` reduced from 901 to 793 lines.
- **`infer/` stub deleted** — removed empty `src/core/infer/index.ts`.
- **Source files in published package** — `"src"` added to `files` array in package.json.
- **Package manager migrated** — pnpm → bun. `packageManager` set to `bun@1.3.9`.
- **Build config** — `tsdown.config.ts` changed to multi-entry build with `minify: true`.
- Test count: 1658 tests across 46 test files (up from 1518 in v0.7.0).

### Fixed

- `--config=<path>` equals form now correctly parsed and stripped from argv before dispatch.
- Zsh completion: multi-alias flags use all short aliases in the exclusion group, not just the
  first.
- Bash completion: `escapeForSingleQuote()` sanitizes `compgen -W` words to prevent shell injection.
- Win32 `resolveConfigDir`: strip trailing separator from homedir to avoid doubled backslashes on
  drive roots (e.g. `C:\`).
- Win32 `resolveConfigDir`: treat empty `APPDATA` as unset, falling back to homedir-based path.
- Win32 `homedir`: add `HOMEDRIVE`+`HOMEPATH` fallback; never use `HOMEPATH` alone.
- Levenshtein distance: replace 2D array with `Uint16Array` rolling buffer, drop defensive
  `undefined` guards.
- Completions command detection via schema lookup instead of raw `argv[0]` string match.
- Child flag with `propagate: false` correctly masks ancestor's propagated flag of the same name.
- Dispatch: exhaustiveness guard on `subResult` switch in nested command resolution.
- Nested group help: `binName` built from full command path, not just root.
- Config loader: lowercase extensions in `buildExtensionList`/`buildLoaderMap` for case-insensitive
  matching.
- Empty-string env var fallbacks in runtime adapter treated as unset.
- `ProgressHandle.increment()` was emitting `progress:update` events indistinguishable from
  `update()` calls. Now emits `progress:increment` with `delta` field.
- **Prompt number coercion** — `coercePromptValue` was missing NaN guard for number flags; now
  handled by unified `coerceValue()`.

## [0.7.0] - 2026-02-10

### Added

#### Subcommand Nesting

- **`CommandBuilder.command(sub)`** registers nested subcommands, building recursive command trees
  of unlimited depth. Parent commands store children as type-erased `ErasedCommand` entries — the
  parent doesn't need the child's generic types.
- **`group(name)`** factory as a semantic alias for `command()`. Communicates intent: groups
  organise subcommands, leaf commands have actions. A group may also have its own `.action()` (e.g.
  `git remote` lists remotes, `git remote add` dispatches to a child).
- **`flag.propagate()`** modifier marks a flag for automatic inheritance by all descendant commands.
  Propagated flags are collected from the ancestor chain at dispatch time. Child commands override a
  propagated flag by redeclaring the same name — child definition wins completely.
- **Recursive dispatch** in `CLIBuilder.execute()`. Walks argv segments matching command names at
  each tree level. Handles hybrid commands (action + subcommands): subcommand match takes priority,
  else falls through to the parent handler. Groups without handlers show help.
- **Nested help** — `formatHelp()` renders a "Commands:" section listing available subcommands for
  group commands. Usage line adapts to show `<command>` placeholder when subcommands exist.
- **Nested completions** — bash and zsh generators traverse the full command tree depth. Propagated
  flags included at each nesting level. Bash uses path-keyed case statements; zsh generates
  per-group helper functions.
- **Scoped "did you mean?"** — typo suggestions search within the current command scope, not the
  global command list. Help hint shows scoped path (e.g. `Run 'myapp db --help'`).

### Changed

- `CommandSchema` extended with `commands: readonly CommandSchema[]` for nested subcommand schemas.
- `ErasedCommand` extended with `subcommands: ReadonlyMap<string, ErasedCommand>` for dispatch.
- `ErasedCommand` interface moved from `cli/index.ts` to `schema/command.ts` (shared location).
- `FlagSchema` extended with `propagate: boolean` (default `false`).
- `RunOptions` extended with `mergedSchema` internal field for propagated flag injection.
- Dispatch logic extracted to `cli/dispatch.ts` (~285 lines) and flag propagation to
  `cli/propagate.ts` (~87 lines), reducing `cli/index.ts` by ~220 lines.
- Test count: 1518 tests across 43 test files (up from 1300 in v0.6.0).

### Fixed

- Completion generator no longer recurses into hidden command subtrees.
- Dispatch respects `--` end-of-flags sentinel before command names.

## [0.6.0] - 2026-02-10

### Added

#### Config File Discovery

- **`CLIBuilder.config(appName)`** enables config file discovery with XDG-compliant search paths.
  Searches `.{app}.json` and `{app}.config.json` in cwd, then `{configDir}/{app}/config.json`. JSON
  loader built-in.
- **`--config <path>` global flag** overrides config file discovery path. Extracted from argv before
  command dispatch.
- **`CLIBuilder.configLoader(loader)`** plugin hook for registering custom config format loaders
  (YAML, TOML, etc.). `configFormat(extensions, parseFn)` convenience factory.

#### Schema Additions

- **`flag.custom(parseFn)`** — new flag kind accepting an arbitrary parse function with full
  return-type inference from `parseFn`. Wired through parser coercion, all three resolve coercions
  (env, config, prompt), and help formatter.
- **`.deprecated(message?)`** modifier on `FlagBuilder` and `ArgBuilder`. Emits structured
  `DeprecationWarning` during resolution when a deprecated flag/arg is explicitly provided (CLI,
  env, config, prompt — not for default fallthrough). Renders `[deprecated]` or
  `[deprecated: <reason>]` in help text.

#### RuntimeAdapter Extensions

- **`readFile`** — async file read returning `null` for ENOENT, throws on other errors. Uses lazy
  `import('node:fs/promises')`.
- **`homedir`** — computed from env vars (`HOME`/`USERPROFILE`) + `platform`; avoids `node:os`.
- **`configDir`** — `XDG_CONFIG_HOME` on Unix, `APPDATA` on Windows; falls back to `~/.config` or
  `~\AppData\Roaming`.

### Changed

- `RuntimeAdapter` interface extended with `readFile`, `homedir`, `configDir`.
- `NodeProcess` interface extended with `platform` field.
- `FlagSchema` extended with `parseFn: FlagParseFn<unknown> | undefined`.
- `FlagSchema` extended with `deprecated: string | true | undefined`.
- `ArgSchema` extended with `deprecated: string | true | undefined`.
- `ResolveResult` extended with `warnings: readonly DeprecationWarning[]`.
- `CLISchema` extended with `configSettings` for config file discovery.
- `FlagParseFn<T>`, `DeprecationWarning`, `ConfigResult`, `FormatLoader`, `configFormat` exported
  from public API.
- Test count: ~1300 tests (up from 1198 in v0.5.0).

## [0.5.0] - 2026-02-10

### Added

#### Shell Completions

- **Bash completion generator** — produces self-contained bash scripts with
  `COMP_WORDS`/`COMP_CWORD` scanning, per-command case branches with flag and enum value
  completions, and `complete -F` registration.
- **Zsh completion generator** — produces `#compdef` scripts with `_arguments` flag specs,
  `_describe` subcommand lists, and enum value completions via `->state` dispatch.
- **`CLIBuilder.completions()`** adds a built-in `completions --shell <bash|zsh>` subcommand that
  outputs a ready-to-eval completion script. In `--json` mode, emits `{ script }` JSON object.
  Fish/PowerShell accepted in the enum with descriptive "not yet supported" errors.

#### Runtime Portability

- **`detectRuntime()`** via `globalThis` feature detection — identifies Node, Bun, Deno, or unknown.
  Exported `Runtime` type and `RUNTIMES` constant.
- **`createAdapter()`** auto-detecting adapter factory — calls `detectRuntime()` and returns the
  appropriate `RuntimeAdapter`. `CLIBuilder.run()` uses it as default when no adapter is provided.
- **Bun adapter** implementing `RuntimeAdapter` by delegating to the Node adapter (Bun's
  Node-compatible APIs).

### Fixed

- Completion: cross-command enum collision — `collectEnumCases()` scoped per-command to prevent enum
  values from one command leaking into another's completions.
- Completion: shell injection safety — `quoteShellArg()` escapes `schema.name` and enum values in
  generated scripts.
- Completion: conditional `--version` — bash generator omits `--version` from completions when
  `schema.version` is undefined, matching zsh behavior.
- CLI: `--json` mode completions output `{ script }` JSON instead of raw script text.
- CLI: guard against double `.completions()` call.
- Runtime: `NodeProcess` type exported from main barrel.
- Runtime: `@internal` removed from `GlobalForDetect` (contradicted public export).
- Runtime: `createAdapter()` switch uses `default: never` exhaustiveness guard.

### Changed

- Shell completion types, generator stubs, and barrel exports added to `src/core/completion/`.
- Test count: 1198 tests across 35 test files (up from 1010 in v0.4.0).

## [0.4.0] - 2026-02-09

### Added

#### Typed Middleware

- **`middleware<Output>(handler)`** factory creating phantom-branded `Middleware<Output>` values.
  Handler receives `{ args, flags, ctx, out, next }` — call `next(additions)` to continue the chain
  with typed context, omit `next()` to short-circuit (auth guards), or await `next()` for
  wrap-around patterns (timing, try/catch).
- **`CommandBuilder.middleware(m)`** registers middleware in execution order. Each call widens the
  context type parameter `C` via `WidenContext<C, Output>` intersection — `Record<string, never>`
  (the default) is replaced entirely on the first call, preventing `never` collapse. Adding
  middleware drops the current handler (type signature changed).
- **Context type parameter `C`** on `CommandBuilder<F, A, C>`, `ActionParams<F, A, C>`, and
  `ActionHandler<F, A, C>`. `ctx` in the action handler is `Readonly<C>` — property access is a type
  error until middleware extends it.
- **Middleware chain execution** (`executeWithMiddleware`) in testkit. Builds a continuation chain
  from back to front; context accumulates via `{ ...ctx, ...additions }` at each step. Replaces the
  former `invokeHandler` bridge.

#### Structured Output

- **`out.json(value)`** emits `JSON.stringify(value)` to stdout. Always targets stdout regardless of
  JSON mode. Handlers should prefer this over `out.log(JSON.stringify(...))`.
- **`out.table(rows, columns?)`** renders tabular data. In JSON mode: emits rows as JSON array. In
  text mode: pretty-prints aligned columns with headers (auto-inferred from first row when `columns`
  omitted). `TableColumn<T>` descriptor type with `key` and optional `header`.
- **`out.jsonMode`** and **`out.isTTY`** readonly properties on `Out` interface. Handlers check
  these to skip decorative output (spinners, ANSI codes) when machine-readable output is expected or
  stdout is piped.
- **`--json` global flag** detection in `CLIBuilder.execute()`. Strips `--json` from argv before
  command dispatch. CLI-level dispatch errors (unknown command, no action) rendered as JSON when
  active.
- **`jsonMode`** and **`isTTY`** options on `RunOptions`, `CLIRunOptions`, and `OutputOptions`.
  `CLIBuilder.run()` auto-sources `isTTY` from `adapter.isTTY`.

### Changed

- `ActionParams<F, A>` → `ActionParams<F, A, C>` with `ctx: Readonly<C>` (was
  `Readonly<Record<string, unknown>>`).
- `CommandBuilder` carries third type parameter `C` (default `Record<string, never>`). All
  metadata/builder methods preserve `C` in return type.
- `CommandSchema.middleware` added as `readonly ErasedMiddlewareHandler[]`.
- `Out` interface extended with `json()`, `table()`, `jsonMode`, and `isTTY`.
- `OutputChannel` constructor accepts `isTTY` and `jsonMode` from resolved options. `log`/`info`
  redirect to stderr writer in JSON mode.
- `runCommand` and `CLIBuilder.execute` error paths render JSON when `jsonMode` active.
- `createCaptureOutput` accepts `jsonMode` and `isTTY` options.
- Test count: 1010 tests across 31 test files (up from 797 in v0.3.0).

## [0.3.0] - 2026-02-09

### Added

#### Interactive Prompting

- **Prompt type definitions** (`PromptConfig`) as a discriminated union with four kinds: `confirm`,
  `input`, `select`, `multiselect`. Each kind has specialized fields — `InputPromptConfig` supports
  `placeholder` and `validate`, select/multiselect support `SelectChoice` arrays with optional
  labels and descriptions.
- **`FlagBuilder.prompt(config)`** metadata modifier for declaring prompt configuration on flags,
  following the same immutable builder pattern as `.env()` and `.config()`.
- **Prompt engine interface** (`PromptEngine`) with `promptOne(config) → Promise<PromptResult>` as a
  pluggable renderer seam. `ResolvedPromptConfig` variant guarantees non-empty choices for
  select/multiselect after merging from `FlagSchema.enumValues`.
- **Built-in terminal prompter** (`createTerminalPrompter(read, write)`) with line-based I/O for
  confirm (y/n), input (with validation and placeholder), select (numbered list), and multiselect
  (comma-separated numbers with min/max). All prompts have a `MAX_RETRIES = 10` safety valve.
- **Test prompter** (`createTestPrompter(answers, options?)`) with queue-based answers for
  deterministic testing. `PROMPT_CANCEL` symbol sentinel for simulating cancellation.
  `onExhausted: 'throw' | 'cancel'` controls behavior when answer queue is empty.
- **Prompt resolution in the resolver**. Resolution chain expanded from CLI > env > config > default
  to CLI > env > config > **prompt** > default. Flags with prompt config and no value from prior
  sources trigger `prompter.promptOne()`. Cancelled prompts fall through to default/required.
  Non-interactive mode (no prompter) skips prompts entirely.
- **`ReadFn`** (`() => Promise<string | null>`) as the minimal stdin abstraction. `null` signals
  EOF/cancel. `RuntimeAdapter` extended with `stdin: ReadFn` and `stdinIsTTY: boolean`.
- **Node adapter stdin** wraps `process.stdin` via dynamic `import('node:readline')` with lazy
  per-call readline interfaces. Minimal `node:readline` type declarations in `node-builtins.d.ts`
  avoid `@types/node` dependency.
- **Automatic prompt gating** in `CLIBuilder.run()`: when `stdinIsTTY=true` and no explicit prompter
  provided, auto-creates `createTerminalPrompter(adapter.stdin, adapter.stderr)`. Prompt output
  routed to stderr to avoid interfering with piped stdout.
- **Command-level `.interactive(resolver)`** API on `CommandBuilder`. Resolver receives partially
  resolved flags (after CLI/env/config), returns `Record<string, PromptConfig | false | undefined>`
  controlling which flags get prompted. Truthy `PromptConfig` overrides per-flag prompt; `false`
  explicitly suppresses; absent falls back to per-flag `.prompt()` config.
- **Testkit `answers` convenience** on `RunOptions`. Accepts `Record<string, TestAnswer>` to
  auto-create a test prompter. `prompter` field also available for explicit engine injection.
  `CLIRunOptions` mirrors both fields.

### Changed

- `resolve()` is now **async** (`Promise<ResolveResult>`). All callers (`runCommand`,
  `CLIBuilder.execute`, `CLIBuilder.run`) updated to await.
- Resolution chain expanded from CLI > env > config > default to CLI > env > config > prompt >
  default.
- `ResolveOptions` extended with optional `prompter: PromptEngine` field.
- `RunOptions` extended with `prompter` and `answers` fields.
- `CLIRunOptions` extended with `prompter` and `answers` fields.
- `RuntimeAdapter` extended with `stdin: ReadFn` and `stdinIsTTY: boolean`.
- `createTestAdapter` defaults to EOF-returning stdin and `stdinIsTTY: false`.
- Test count: 797 tests across 21 test files (up from 599 in v0.2.0).

## [0.2.0] - 2026-02-09

### Added

#### Resolution Chain

- **Environment variable resolution** in the resolver. Flags with `.env('VAR')` now resolve from the
  `env` record after CLI and before default. String, number, boolean (lenient:
  `true/false/1/0/yes/no`), enum, and array (comma-separated) coercion. Invalid env values produce
  `ValidationError` with `TYPE_MISMATCH` or `INVALID_ENUM` codes.
- **Config object resolution** in the resolver. Flags with `.config('dotted.path')` resolve from a
  plain `Record<string, unknown>` after env and before default. `resolveConfigPath()` walks nested
  objects segment-by-segment. Config values may already be typed from JSON — coercion is lenient for
  matching types. Full chain: CLI > env > config > default.
- **Resolution source annotations** in help text. Flags with env or config declarations now display
  `[env: VAR]` and `[config: path]` in `formatHelp()` output, ordered between description text and
  presence indicators.
- **Actionable required-flag error hints**. When a required flag is missing after full resolution,
  `ValidationError.suggest` lists all configured sources (e.g. "Provide --region, set DEPLOY_REGION,
  or add deploy.region to config"). CI-friendly error messages with `envVar`/`configPath` in
  details.
- **Env/config wiring through testkit and CLI builder**. `RunOptions` and `CLIRunOptions` accept
  `env` and `config` fields. `runCommand()` threads them into `resolve()`. `CLIBuilder.run()`
  auto-sources `adapter.env` when no explicit env option is provided.

### Changed

- Resolution chain expanded from CLI > default (v0.1) to CLI > env > config > default.
- `resolve()` now accepts optional `ResolveOptions` parameter with `env` and `config` fields.
- `ResolveOptions` exported from public API surface.

## [0.1.0] - 2026-02-09

### Added

#### Core Framework

- **Structured errors** (`CLIError`, `ParseError`, `ValidationError`) with stable error codes,
  `toJSON()` serialization, type guard functions (`isCLIError`, `isParseError`,
  `isValidationError`), and actionable `suggest` hints.
- **Flag builder** (`flag`) with full type inference for boolean, string, number, enum, and array
  kinds. Supports `.alias()`, `.default()`, `.required()`, `.describe()`, `.hidden()`,
  `.deprecated()`, `.env()`, and `.config()` declarations.
- **Arg builder** (`arg`) with type inference for string, number, custom parse functions, and
  variadic args. Supports `.default()`, `.required()`, `.optional()`, and `.describe()`.
- **Command builder** (`command`) with `.flag()`, `.arg()`, `.description()`, `.example()`,
  `.hidden()`, `.alias()`, and `.action()`. Accumulates phantom types so handler receives fully
  inferred `flags` and `args`.
- **Argv parser** with tokenizer (`tokenize`) and schema-aware parser (`parse`). Handles long/short
  flags, `=` syntax, boolean negation (`--no-*`), flag stacking (`-abc`), `--` separator, and type
  coercion against the schema.
- **Resolution chain** (CLI parsed value → schema default). Validates all required flags/args,
  aggregates multiple errors into a single throw, and provides per-field suggestions.
- **Auto-generated help text** (`formatHelp`) from command schema, including usage line,
  description, positional args, flags with types/defaults/aliases, examples section, and subcommand
  listing.
- **Output channel** (`createOutput`) with `log`/`info`/`warn`/`error` methods, `WriteFn`
  abstraction, verbosity levels (normal/quiet), and TTY detection. Includes `createCaptureOutput()`
  test helper.
- **Test harness** (`runCommand`) for running commands as pure functions with injected argv, env,
  and captured output. Returns `RunResult` with `exitCode`, `stdout`, `stderr`, and `error`.
- **CLI builder** (`cli`) with `.command()` registration, `.version()`, subcommand dispatch,
  automatic `--help`/`--version` flag handling, and unknown-command error with suggestions.
- **RuntimeAdapter interface** defining the platform abstraction boundary (argv, env, cwd,
  stdout/stderr, isTTY, exit). Includes `createTestAdapter()` for injectable test stubs and
  `ExitError` for testable process exits.
- **Node.js adapter** (`createNodeAdapter`) wiring `process.argv`, `process.env`, `process.cwd()`,
  `process.stdout`/`stderr`, and TTY detection.
- Stub files for Bun adapter, Deno adapter, runtime auto-detection, and shell completion generation.

#### Project Infrastructure

- Project scaffold with `src/` structure, TypeScript strict config, and ESM + CJS dual build via
  tsdown.
- Vitest test framework with 464 passing tests across 12 test files.
- Biome linter and dprint formatter configuration.
- `@vitest/coverage-v8` for test coverage reporting.
- `@arethetypeswrong/cli` and `publint` for package quality checks.
- CI script (`pnpm run ci`) running typecheck, lint, test, and build in sequence.
- PRD.md with full product requirements document.
- MIT License.
- Markdownlint configuration.

[Unreleased]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.17...HEAD
[3.0.0-rc.17]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.16...v3.0.0-rc.17
[3.0.0-rc.16]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.15...v3.0.0-rc.16
[3.0.0-rc.15]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.14...v3.0.0-rc.15
[3.0.0-rc.14]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.13...v3.0.0-rc.14
[3.0.0-rc.13]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.12...v3.0.0-rc.13
[3.0.0-rc.12]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.11...v3.0.0-rc.12
[3.0.0-rc.11]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.10...v3.0.0-rc.11
[3.0.0-rc.10]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.9...v3.0.0-rc.10
[3.0.0-rc.9]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.8...v3.0.0-rc.9
[3.0.0-rc.8]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.7...v3.0.0-rc.8
[3.0.0-rc.7]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.6...v3.0.0-rc.7
[3.0.0-rc.6]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.5...v3.0.0-rc.6
[3.0.0-rc.5]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.4...v3.0.0-rc.5
[3.0.0-rc.4]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.3...v3.0.0-rc.4
[3.0.0-rc.3]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.2...v3.0.0-rc.3
[3.0.0-rc.2]: https://github.com/kjanat/dreamcli/compare/v3.0.0-rc.1...v3.0.0-rc.2
[3.0.0-rc.1]: https://github.com/kjanat/dreamcli/compare/v2.5.0...v3.0.0-rc.1
[2.5.0]: https://github.com/kjanat/dreamcli/compare/v2.4.1...v2.5.0
[2.4.1]: https://github.com/kjanat/dreamcli/compare/v2.4.0...v2.4.1
[2.4.0]: https://github.com/kjanat/dreamcli/compare/v2.3.0...v2.4.0
[2.3.0]: https://github.com/kjanat/dreamcli/compare/v2.2.1...v2.3.0
[2.2.1]: https://github.com/kjanat/dreamcli/compare/v2.2.0...v2.2.1
[2.2.0]: https://github.com/kjanat/dreamcli/compare/v2.1.0...v2.2.0
[2.1.0]: https://github.com/kjanat/dreamcli/compare/v2.0.1...v2.1.0
[2.0.1]: https://github.com/kjanat/dreamcli/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/kjanat/dreamcli/compare/v1.0.0...v2.0.0
[1.0.0]: https://github.com/kjanat/dreamcli/compare/5b86f72...v1.0.0
[0.9.2]: https://github.com/kjanat/dreamcli/compare/b26f2d8...5b86f72
[0.9.1]: https://github.com/kjanat/dreamcli/compare/v0.9.0...b26f2d8
[0.9.0]: https://github.com/kjanat/dreamcli/compare/v0.8.0...v0.9.0
[0.8.0]: https://github.com/kjanat/dreamcli/compare/v0.7.0...v0.8.0
[0.7.0]: https://github.com/kjanat/dreamcli/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/kjanat/dreamcli/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/kjanat/dreamcli/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/kjanat/dreamcli/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/kjanat/dreamcli/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/kjanat/dreamcli/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/kjanat/dreamcli/releases/tag/v0.1.0
