burgee
Switching from…

Switching from commander

A commander alternative that is drop-in compatible: change one import, keep your code and tests, and gain --json, --schema, --mcp and shell completions. Graded by commander's own test suite.

burgee is a commander alternative you adopt by changing one import. It is not a new API to learn and not a wrapper around commander: burgee/commander implements commander's API itself, and commander's own test suite is the grade.

Migrate from commander in one import

- import { Command } from 'commander';
+ import { Command } from 'burgee/commander';

Your program and your tests stay as they are. On a larger codebase, npx burgee migrate --dry-run finds every commander import and prints the rewrite before it makes one; it refuses, by file and line, anything it cannot rewrite mechanically, and leaves that file untouched.

Is it really compatible with commander?

Compatibility is graded, not claimed. commander's own test suite — vendored, unmodified apart from the import specifier — runs against burgee/commander in CI, beside a control that runs the same suite against real commander:

passingrate
burgee/commander1360 / 1360100.0%
commander itself (control)1360 / 1360100.0%

From Compatibility, which npm run compat:page generates from the oracle's last run; this table is copied from it and that page is the authority. A façade is never called "compatible" until its host's own suite passes 100%.

What you gain over commander

The same program, after the import swap, answers four things commander cannot:

  • --json — every command's result in one stable envelope, { "ok": true, "data": … }, and every failure in the same place: { "ok": false, "error": { "code", "message" } } on stdout, alone, whether commander refused the command line or the action threw — with the exit code the error names (AuthError exits 5). Through 0.11.1 an action that threw under a plain parseAsync(process.argv) escaped as a stack trace instead. No stdout scraping. data is what the action returns, so an action that only prints gives data: null — commander actions return nothing; return the value (.action(() => ({ lines }))) and it is data.
  • --schema — the whole command tree as data: commands, arguments, options, choices, defaults and a JSON Schema per command. It runs no handler and reads no config, so it is safe for an agent to call first.
  • --mcp — the same commands served as MCP tools over stdio. Every command with an action is a tool; one that declared nothing is annotated effects: 'undeclared' with no hints, .effects('read_only') (or idempotent, non_idempotent) adds them, and .effects('withheld') keeps a command out. An action that prints is still a useful tool: its printed output becomes the tool result instead of corrupting the stream.
  • Completions — completion bash | zsh | fish | pwsh, generated from the same declaration; pressing TAB never runs your program.

And an exit-code contract an agent can act on: 2 means rewrite the command, any other non-zero means the command was fine and the world was not. Details on Your CLI is an agent tool.

Plugins, too: commander's plugin RFC (#2505) was closed for inactivity on 2026-10-09 without one, while a burgee plugin works on commander syntax, yargs syntax and native alike — see Plugins.

What commander still does better

Weight. From the root README's Measured table (2026-10-08, one CI runner, generated by npm run bench — see Benchmarks and burgee vs the alternatives):

burgeecommander
Runtime dependencies5, none outside the burgee family0
Full CLI run over bare node+20.3 ms+14.8 ms
Installed size1690 KB203 KB

Against commander alone, burgee/commander is the heavier bundle (1.417×). Lighter than commander alone is not reachable while the front end also carries a manifest, a schema and an MCP server, so the lighter-than-commander gate is a downward-only ratchet instead: it holds the ratio within 1.42× and may only be lowered (D-157). Against commander plus what a commander user installs to match burgee's config, shutdown and cursor handling, it is met (0.438×). Both rows are on the README and neither is hidden. And commander has users; burgee is new.

When to switch

  • You want your CLI usable by an AI agent without writing tool definitions by hand.
  • You want --json output and exit codes that mean something, on every command.
  • You want plugins shared across programs.

If none of that matters and bundle bytes do, commander is the lighter choice today.

On this page