Files
skills/docs/devtools/tooling/electrobun/src/cli/cli.md
2026-05-29 08:20:56 +01:00

101 lines
3.9 KiB
Markdown

# CLI — `cli.ts`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Owns the five command surfaces — `createCommand`, `devCommand`,
`buildCommand`, `packageCommand`, `signCommand` — plus the
`runCli(argv, deps?)` dispatcher that the published binary delegates to.
Every command is a pure function: it validates its option struct against
the matching Zod schema, runs the orchestrated work, and returns a typed
report. `runCli` never calls `process.exit`; the only exit-call lives in
`src/bin/suaveplan-electrobun.ts`, so the entire CLI is unit-testable
without a child process.
`createCommand` is the scaffolder entry: it loads the layered config,
applies CLI overrides, optionally runs the prompt flow, then hands off
to `generate`. The other four commands are runner-only — they probe the
upstream `electrobun` binary, raise `ELECTROBUN.BINARY_UNAVAILABLE` when
missing, spawn the binary with the canonical argv, and return a
`CommandReport` carrying exit code, stdout, stderr, and duration.
## Features
- Five command functions, each with Zod-validated options and structured
reports.
- Pure `runCli(argv)` dispatcher with `--help` / `-h` short-circuit and
closed-set flag parsing.
- Long-flag `--key=value` and `--key value` forms supported uniformly.
- Unknown flags / subcommands raise `ELECTROBUN.UNKNOWN_FLAG`.
- Missing required values raise `ELECTROBUN.INVALID_OPTIONS`.
- `timeRunner` wrapper increments invocation + duration histograms per
call and increments failure counters on `ElectrobunError` paths.
## Usage: Basic
```ts
import { runCli } from "@suaveplan/electrobun/cli";
const { code } = await runCli(process.argv.slice(2));
process.exit(code);
```
## Usage: Advanced
```ts
import { buildCommand } from "@suaveplan/electrobun/cli";
import type { CommandRunner } from "@suaveplan/electrobun/types";
const customRunner: CommandRunner = async (input) => {
// ship the spawn to a remote CI worker
return await postToWorker(input);
};
const report = await buildCommand(
{ cwd: "/proj", target: "linux", update: true },
{ runner: customRunner }
);
```
## API Reference
- `createCommand(opts: CreateCommandOptions, deps: CommandDeps & {
prompter: Prompter }): Promise<GenerateReport>`.
- `devCommand(opts: DevCommandOptions, deps?: { runner? }):
Promise<CommandReport>`.
- `buildCommand(opts: BuildCommandOptions, deps?: { runner? }):
Promise<CommandReport>`.
- `packageCommand(opts: PackageCommandOptions, deps?: { runner? }):
Promise<CommandReport>`.
- `signCommand(opts: SignCommandOptions, deps?: { runner? }):
Promise<CommandReport>`.
- `runCli(argv: readonly string[], deps?: RunCliOptions): Promise<{
code: number }>`.
- `HELP_TEXT: string` — the full help banner.
## Implementation Notes
The arg parser uses a closed `ALL_KNOWN_FLAGS` set. New flags must be
added to the set before the parser will accept them; an unknown flag
fails the run with `ELECTROBUN.UNKNOWN_FLAG`, which keeps subtle typos
from being silently swallowed. The `--help` and `-h` short forms are
intercepted before subcommand dispatch so they work uniformly across
every subcommand.
`runCli` catches every `ElectrobunError` and folds it into `code: 1`
with a one-line stderr message. Any other thrown value re-propagates,
on the principle that the runtime should not eat unexpected exceptions
— if something other than a typed error escapes the call, the bin
wrapper surfaces it with `code: 2`.
The `timeRunner` helper increments `electrobun_runner_invocations_total`
on entry, records the duration histogram on every exit (success or
failure), and records `electrobun_runner_failures_total` when the thrown
`ElectrobunError` carries a numeric `details.exitCode`. The failure
counter labels the exit code with `-1` when no numeric exit-code is
available — for instance when the probe stage raises
`BINARY_UNAVAILABLE` — so dashboards can still attribute the failure
to the right command without inventing fake exit codes.