# burgee vs 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.

Source: https://burgee.interlace.tools/docs/vs/commander

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

```diff
- 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](/docs/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": … }`,
  with errors in the same shape. No stdout scraping.
- **`--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. A command becomes a tool
  when it declares its `effects` (`.effects('read_only')` on commander syntax), so nothing
  is exposed to an agent by accident.
- **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](/docs/agent-surfaces).

Plugins, too: commander's plugin RFC
([#2505](https://github.com/tj/commander.js/issues/2505)) is unlanded, while a burgee
plugin works on commander syntax, yargs syntax and native alike — see
[Plugins](/docs/plugins).

## What commander still does better

Weight. From the root README's *Measured* table (2026-09-09, one machine, generated by
`npm run bench` — see [Benchmarks](/docs/benchmarks) and
[burgee vs the alternatives](/docs/comparison)):

| | burgee | commander |
| :-- | --: | --: |
| Runtime dependencies | 5, none outside the burgee family | 0 |
| Full CLI run over bare node | +14.0 ms | +15.3 ms |
| Installed size | 1154 KB | 203 KB |

The `lighter-than-commander` gate — `burgee/commander` lighter in a bundle than commander
alone — is **not met** (1.514×). Against commander *plus* what a commander user installs to
match burgee's config, shutdown and cursor handling, it is met (0.468×). 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.
