Skip to content

feat: shell completions, config discovery, custom flags, deprecation, and nested subcommand trees (v0.5–v0.7) - #1

Merged
kjanat merged 67 commits into
masterfrom
v0.7-subcommand-nesting
Feb 10, 2026
Merged

kjanat merged 67 commits into
masterfrom
v0.7-subcommand-nesting

Conversation

@kjanat

@kjanat kjanat commented Feb 10, 2026 •

Copy link
Copy Markdown
Owner

Summary

Three milestones of framework capability shipped in one branch — 62 files changed, ~11k lines added, 1518 tests passing across 43 test files.


v0.5.0 — Shell Completions & Runtime Adapters

  • Bash/zsh completion generators from CLI schema — enum-aware, alias-aware, with shell-safe escaping for names and values
  • Built-in .completions() subcommand with --json support, duplicate-call guard, and descriptive errors for unimplemented shells (fish/powershell)
  • Runtime auto-detection via globalThis feature probing — detectRuntime() + createAdapter() factory
  • Bun adapter delegating to Node; Deno throws UNSUPPORTED_RUNTIME
  • Exhaustiveness guards on runtime switch + completion shell dispatch

v0.6.0 — Config Discovery, Custom Flags & Deprecation

  • Adapter-driven config discovery — XDG search paths, --config / --config=<path> override (stripped from argv), JSON loader built-in
  • Plugin hook for custom format loaders via .configLoader() + configFormat() factory
  • flag.custom(parseFn) with inferred return type — coercion wired through CLI/env/config/prompt; parseFn receives unknown (raw JSON from config, string from CLI/env)
  • .deprecated() modifier on flags and args — structured DeprecationWarning objects in resolve result, [deprecated] annotations in help output, out.warn() emission
  • RuntimeAdapter extended with readFile, homedir, configDir (XDG/APPDATA-aware, HOMEDRIVE+HOMEPATH fallback on win32)

v0.7.0 — Nested Subcommand Trees

  • CommandBuilder.command() + group() for recursive command tree definition
  • flag.propagate() for explicit flag inheritance — ancestor walk with deterministic shadowing (nearest wins, target's own flags override)
  • Recursive dispatch in CLIBuilder — walks argv to deepest match, -- handling, remaining-argv preservation
  • Scoped "did you mean?" suggestions via Levenshtein (Uint16Array rolling buffer) restricted to current command group
  • Nested help — Commands section in formatHelp(), group help with full binName path
  • Nested bash/zsh completions — recursive traversal with propagated flags at each level
  • Type-erased runtime shapes — ErasedCommand, AnyCommandBuilder, eraseBuilder helper

Housekeeping & Review Fixes

  • CHANGELOG entries for v0.5.0, v0.6.0, v0.7.0
  • package.json bumped to 0.7.0, git tags applied
  • GOALS.md release plan updated (v0.5–v0.7 marked done)
  • 3 rounds of review feedback addressed: 13 items in first pass, config/completion/dispatch hardening in follow-ups
  • Specs added: v0.7-subcommand-nesting.md, v0.8-spinner-progress.md

New Public API Exports

Category Symbols
Completion generateCompletion, generateBashCompletion, generateZshCompletion, CompletionOptions, Shell, SHELLS
Config discoverConfig, configFormat, buildConfigSearchPaths, ConfigAdapter, FormatLoader, ConfigResult
Schema flag.custom(), FlagParseFn, flag.propagate(), .deprecated(), group(), AnyCommandBuilder, ErasedCommand
Resolve DeprecationWarning
Runtime detectRuntime, RUNTIMES, createAdapter, createBunAdapter, NodeProcess, GlobalForDetect

Stats

  • 62 files changed
  • +11,466 / −831 lines
  • 1,518 tests across 43 test files
  • Zero runtime dependencies

feat: Shell completions, config discovery, custom flags, deprecation, and nested subcommand trees

Overview

This PR delivers three major release milestones (v0.5.0–v0.7.0) for the dreamcli TypeScript CLI framework, spanning 62 files with 11,466 additions and 831 deletions. The PR includes 1,518 tests across 43 test files and introduces significant new capabilities for building production-grade CLIs.

Key Features

v0.5.0: Shell Completions & Runtime Detection

  • Bash/Zsh Completion Generators: Automatic shell completion script generation from CLI schemas, with enum-aware value suggestions and shell-safe escaping
  • Built-in .completions() Subcommand: Generators include --json output mode and output guards
  • Runtime Auto-Detection: Runtime detection via globalThis with detectRuntime(), createAdapter(), and per-runtime adapters (Node, Bun, Deno)
  • Bun Adapter: Delegates to Node adapter; Deno explicitly throws UNSUPPORTED_RUNTIME

v0.6.0: Config Discovery, Custom Flags & Deprecation

  • Config File Discovery: Adapter-driven discovery (XDG/standard search paths), with --config flag override and custom format loaders
  • Custom Flag Parsing: flag.custom(parseFn) with type inference and coercion across CLI/env/config/prompt sources
  • Deprecation Warnings: .deprecated() modifier for flags/args with help annotations and DeprecationWarning emissions
  • Extended RuntimeAdapter: New methods readFile(), homedir(), configDir() (XDG/APPDATA-aware, with Windows fallbacks)

v0.7.0: Nested Subcommand Trees

  • Hierarchical Commands: CommandBuilder.command() and .group() for arbitrary nesting depth
  • Flag Propagation: .propagate() modifier for explicit flag inheritance with deterministic shadowing across command ancestry
  • Recursive Dispatch: Multi-level command resolution with remaining argv preservation and -- handling
  • Nested Help & Completions: Updated help output with Commands section; bash/zsh generators traverse full command trees with propagated flags
  • Scoped Suggestions: "Did you mean?" suggestions scoped to current command level with Levenshtein distance

Implementation Highlights

Config System

  • New src/core/config/ module with discoverConfig(), pluggable FormatLoader abstraction, and JSON as default
  • Integration into CLIBuilder.run() with automatic discovery and --config flag support
  • Custom format loaders via .configLoader() with precedence rules

Completion System

  • New src/core/completion/ with generateBashCompletion() and generateZshCompletion()
  • Nested command tree traversal, propagated flag inclusion, and enum value escaping
  • Unified generateCompletion() dispatcher; fish/powershell raise UNSUPPORTED_OPERATION

Command Dispatch

  • New src/core/cli/dispatch.ts implementing recursive matching with support for leaf/group commands
  • Supports aliases, unknown-command handling with suggestions, and needs-subcommand cases
  • New src/core/cli/propagate.ts for collecting inherited flags with shadowing rules

Runtime Adaptation

  • New src/runtime/ modules: auto.ts (auto-detection), detect.ts (runtime identification), bun.ts (Bun adapter)
  • Filesystem primitives for config discovery; adapter contract extended with readFile, homedir, configDir
  • Platform-aware path resolution with XDG and Windows (APPDATA, HOMEDRIVE+HOMEPATH) support

Type System

  • Type-erased commands: ErasedCommand, AnyCommandBuilder, eraseBuilder() for heterogeneous subcommand storage
  • New commands: readonly CommandSchema[] field for schema-level subcommand representation
  • Generic flag/arg parsing: FlagParseFn<T>, ArgParseFn<T> with type inference

Testing & Quality

  • 1,518 tests across 43 test files with comprehensive coverage of:

    • Config discovery, loading, and error handling
    • Bash/Zsh completion generation with nested commands and propagated flags
    • Custom flag and deprecation workflows
    • Nested command dispatch, propagation, and help rendering
    • Runtime detection and adapter behavior
    • Deprecation warnings across CLI/env/config/prompt sources
  • End-to-end tests validate nested dispatch, flag propagation shadowing, and multi-level integration

  • Test helpers updated for new schema fields (commands, propagate, deprecated, parseFn)

Public API Additions

Completion: generateBashCompletion, generateCompletion, generateZshCompletion, SHELLS, CompletionOptions, Shell

Config: buildConfigSearchPaths, configFormat, discoverConfig, ConfigAdapter, ConfigDiscoveryOptions, ConfigDiscoveryResult, ConfigFound, ConfigNotFound, FormatLoader

Runtime: createAdapter, createBunAdapter, detectRuntime, RUNTIMES, GlobalForDetect, Runtime

Schema: group, ErasedCommand, AnyCommandBuilder, FlagParseFn (with flag.custom<T>() method), .propagate() and .deprecated() modifiers on flags/args

Resolve: DeprecationWarning in ResolveResult with structured deprecation tracking

CLI: .config(), .configLoader(), .completions() builder methods; ConfigSettings type

Documentation & Housekeeping

  • CHANGELOG entries for v0.5–v0.7 with feature summaries
  • package.json version bumped to 0.7.0
  • Git tags applied for v0.5.0, v0.6.0, v0.7.0
  • GOALS.md updated with completion markers and v0.6/v0.7 details
  • Specs added: v0.7-subcommand-nesting.md (design, architecture, task breakdown), v0.8-spinner-progress.md (future direction)
  • AGENTS.md updated to reflect new active implementations
  • Linter configuration (biome.jsonc) updated; language server preferences reordered (.zed/settings.json)

Breaking Changes

None. All additions are backward-compatible; existing APIs remain stable while new optional features are layered on top.

Fixed Issues

  • Empty APPDATA handling: Fixed resolveConfigDir() to treat empty APPDATA environment variable as unset on Windows

Review & Merge

PR merged 2026-02-10T20:01:27Z with three review rounds addressing feedback and hardening across all subsystems.

kjanat added 30 commits February 9, 2026 17:21
…xport)

Removed @internal from GlobalForDetect in detect.ts:58. Type is exported from src/index.ts:125 and used in public function signatures (detectRuntime, createAdapter). Also audited all other @internal tags — found 2 more standalone contradictions (ErasedMiddlewareHandler, ErasedInteractiveResolver) but those are separate scope.
…itch

Added `default: { const _exhaustive: never = runtime; return _exhaustive; }` to createAdapter switch in auto.ts. Compile-time error if new Runtime variant added without handling. Added exhaustiveness test iterating RUNTIMES constant. All 1157 tests pass.
Accept fish/powershell in flag enum so generateCompletion() produces
descriptive 'not yet supported' CLIError instead of generic parse error.
…rror messages

Expanded .completions() --shell flag enum from ['bash','zsh'] to all Shell values ['bash','zsh','fish','powershell']. Users now get descriptive 'not yet supported' CLIError for unimplemented shells instead of generic parse error. Updated JSDoc, added powershell test, strengthened fish/powershell error assertions. 1159 tests pass.
Added NodeProcess to type exports in src/index.ts barrel. Was the only runtime type missing from public API surface. Used in createNodeAdapter() and createBunAdapter() parameter signatures. All 1159 tests pass, typecheck/lint/format clean.
Added guard at top of .completions() — checks `this.schema.commands.some(c => c.schema.name === 'completions')`, throws CLIError(DUPLICATE_COMMAND) on second call. 3 tests: CLIError type, error code, with other commands. 1162 tests pass, typecheck/lint/format clean.
In --json mode, out.log(script) sent completion script to stderr (log redirects there). Fixed: .completions() action checks out.jsonMode — uses out.json({ script }) for JSON mode, out.log(script) for normal. 4 tests added. 1166 tests pass, typecheck/lint/format clean.
Made bash completion --version conditional on schema.version !== undefined, matching zsh behavior. Fixed both with-commands path (rootFlags variable) and no-commands path (inline compgen string). 3 new unit tests + 3 E2E test updates. 1171 tests pass, typecheck/lint/format clean.
Moved enum case completion inside per-command `case "$subcmd"` branches. `collectEnumCases()` now takes single CommandSchema instead of array — no cross-command dedup. Each command gets its own `case "$prev"` block with only its enum values. 2 tests: cross-command collision (deploy prod/staging vs test unit/e2e), mixed enum/non-enum. Zsh unaffected (already per-command). 1173 tests pass, typecheck/lint/format clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant