DeutschGitHub
prototypepackages/cli/src

CLI

@ralphschuler/cli

Dispatches validated argv commands through awaited middleware.

argvcommandsmiddleware

01 · Problem

What is it for?

String splitting, conversion and command execution must be separate so invalid input never reaches a handler.

02 · Mental model

How should you think about it?

A command is a schema plus a handler; middleware wraps dispatch without changing parsing rules.

In this repository

Dispatches validated argv commands through awaited middleware. Pass argv arrays to preserve shell quoting. Interactive input and shell completion are separate adapters.

03 · Control flow

What happens in which order?

  1. Resolve the command by name.
  2. Parse positional arguments and --flag=value tokens.
  3. Convert values and validate required fields.
  4. Run middleware in order.
  5. Await the command handler.

04 · Building blocks

The important contracts

Cli / CommandHandlerRegister and dispatch commands explicitly.
ICommand / IArgument / IFlagDescribe a handler and validated inputs.
use(middleware)Await middleware around the command.

05 · Build it yourself

Implement a small core

Start with this small independent implementation, then use the steps below to make its contract explicit.

  1. Resolve the command by name.
  2. Parse positional arguments and --flag=value tokens.
  3. Convert values and validate required fields.
  4. Run middleware in order.
  5. Await the command handler.
Concept sketch · may reference omitted helpers
type Command = {
  name: string;
  parse(tokens: string[]): Record<string, unknown>;
  run(context: Record<string, unknown>): Promise<void>;
};

async function runCli(argv: string[], commands: Map<string, Command>) {
  const [name, ...tokens] = argv;
  const command = commands.get(name);
  if (!command) throw new Error("Unknown command: " + name);
  const context = command.parse(tokens);
  await command.run(context);
}

06 · Verify

What should you test?

  • Reject unknown commands, flags and extra arguments.
  • Preserve spaces in argv values.
  • Verify middleware order and reject repeated next calls.

Try it yourself

Turn the first verification claim into a failing test, implement the smallest fix, then add a boundary case.

Reject unknown commands, flags and extra arguments.

Compare your approach

Your check should fail when the stated invariant is deliberately broken, and pass again after the fix. Keep setup deterministic and assert an observable result.

07 · Limits

Trade-offs and failure modes

  • String input supports whitespace splitting only.
  • Shell completion and interactive prompts are separate extensions.
  • No CLI starts during import.
Important

Pass argv arrays to preserve shell quoting. Interactive input and shell completion are separate adapters.

08 · Keep exploring

Source code and related ideas

yarn tsx packages/cli/examples/basic.ts