Architecture¶
Pipeline¶
- Input — read from stdin via readline
- Tokenize — split into tokens (quoted strings, escapes)
- Resolve — walk command tree matching tokens to node names
- Parse flags — remaining tokens →
--name valuepairs - Execute — matched command receives
CommandArgumentswith typed accessors - Output — command writes to
ctx.stdout - Loop — prompt again
Command tree¶
Commands form a hierarchy. Each node extends Command; namespace nodes extend CommandContainer.
Resolution¶
Given ["config", "set", "--theme", "dark"]:
"config"→ found, has subcommands → descend-
"set"→ found, leaf → stop, execute with--theme = "dark" -
Unknown command →
CommandNotFoundErrorwith prefix-matched suggestions - Commands with subcommands call
execute()only when no subcommand matches - Remaining tokens are parsed via
parseFlags()
Tokenizer¶
tokenize() splits a raw line into tokens:
- Whitespace — tokens separated by 1+ spaces/tabs/newlines; leading/trailing ignored
- Quotes —
"and'produce a single token (quotes stripped). One type can appear inside the other:"it's fine"→["it's fine"] - Escapes — inside quotes,
\"→",\\→\ - Adjacent chars —
foo"bar"→ParseError: Unexpected characters before quote: "foo" - Empty quotes —
""→[""] - Unclosed quotes —
"hello→ParseError: Unclosed " quote
Argument parsing¶
parseFlags() converts unmatched tokens to Record<string, string>:
--theme dark → { theme: "dark" }
--verbose → { verbose: "true" }
--fields id, name → { fields: "id, name" }
Bare tokens that don't match a positional definition are grouped onto the most recently written argument with a space separator. This lets --fields id, name produce the value "id, name" without quoting. Tokens with no prior argument and no positional definition still throw.
Wrapped in CommandArguments with typed accessors.
Tab completion¶
The Completer walks the command tree for matching names. At leaf commands, completes --flag names from definitions().
Error model¶
Errors propagate to handleError. Registered onError hooks run first — any returning true suppresses the error. Hook throw errors are caught individually; remaining hooks still run. Unconsumed errors print Error: <message> to stdout. The terminal loop never crashes.
ParseError is thrown during tokenization for malformed input (unclosed quotes, adjacent chars).
Commands — defining commands and arguments
Arguments — typed accessors and prompting
Hooks — lifecycle event reference