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:
| passing | rate | |
|---|---|---|
burgee/commander | 1360 / 1360 | 100.0% |
| commander itself (control) | 1360 / 1360 | 100.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 (AuthErrorexits 5). Through 0.11.1 an action that threw under a plainparseAsync(process.argv)escaped as a stack trace instead. No stdout scraping.datais what the action returns, so an action that only prints givesdata: null— commander actions return nothing; return the value (.action(() => ({ lines }))) and it isdata.--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 annotatedeffects: 'undeclared'with no hints,.effects('read_only')(oridempotent,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):
| burgee | commander | |
|---|---|---|
| Runtime dependencies | 5, none outside the burgee family | 0 |
| Full CLI run over bare node | +20.3 ms | +14.8 ms |
| Installed size | 1690 KB | 203 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
--jsonoutput 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.
Migrate
One command moves a project from commander, yargs, chalk, ora and a dozen more incumbents to the family's drop-ins — every import the compatibility oracle grades level, and nothing it does not.
Switching from yargs
A yargs alternative that is drop-in compatible: change one import, keep your code and tests, and gain --json, --schema, --mcp and shell completions — with no dependency outside the burgee family where yargs has six.