Skip to content

src/cli/index.ts — top-level command dispatcher

Argv → subcommand dispatch; the cli module’s contract. Hand-rolled (no commander / yargs / cac) to keep the dependency tree slim and behaviour trivially predictable. Each subcommand handler lives in a sibling src/cli/<name>.ts.

export async function run(argv: readonly string[]): Promise<number>
// Re-exports for tests + programmatic embedders:
export { detectFlow, parseRunArgs, type RunArgs } from './run.js'
export { parsePruneArgs, parseDuration, parseSize } from './cache.js'
export { parseLockArgs, type LockArgs } from './lock.js'
export { parseMigrateArgs, type MigrateArgs } from './migrate.js'
export { parseShowArgs, type ShowArgs } from './show.js'
export { parseMcpArgs, type McpArgs } from './mcp.js'
export { handleMcpRequest, listMcpTools, setMcpContext } from './mcp-rpc.js'
export { formatBytes } from './format.js'

run(argv) returns the exit code. bin.ts calls process.exit(await run(process.argv.slice(2))).

Argv first tokenHandler
runcli/run.ts:runCmd(rest)
watchcli/watch.ts:watchCmd(rest)
cachecli/cache.ts:cacheCmd(rest)
lockcli/lock.ts:lockCmd(rest)
migratecli/migrate.ts:migrateCmd(rest)
upgradecli/upgrade.ts:upgradeCmd(rest)
showcli/show.ts:showCmd(rest)
info / stats (deprecated alias)cli/info.ts:infoCmd(rest)
mcpcli/mcp.ts:mcpCmd(rest)
serve / dev / coordinator / workerredirect message: these live in the service package, not core; exit 1
help / --help / -h / (empty)cli/help.ts:printHelp()
version / --versionprocess.stdout.write('vx <VERSION>\n')
anything elseprint unknown command, then help, exit 1

Per-subcommand parsers / handlers carry their own argv-walk loops. See:

  • No global flags (no --debug, no --quiet, no --color). Color is gated by env (NO_COLOR / FORCE_COLOR / TTY).
  • No tab completion.
  • No subcommand aliases beyond the help / version sugar and the deprecated statsinfo.
  • No service commands — serve / dev / coordinator / worker belong to the separate service package’s binary (core has no service CLI).

tests/cli.test.ts covers the dispatcher table — help, version, unknown subcommand, and the service-command redirects (each moved command must NOT report “unknown command”). Per-subcommand parser tests live alongside.

To swap in a parser library, keep run(argv): Promise<number> and keep the per-subcommand re-exports stable (tests import them directly). Everything else can change.