burgee

burgee vs the alternatives

Measured against commander, yargs, oclif, cac and citty — every capability row marked built is in the package today, with its tests.

This page is maintained by hand, and that is the reason to distrust it most. Four of its rows disagreed with the suite that now measures them — an installed size 5.4x under, an import cost that flattered us by 12 ms, a claim that burgee landed level with cac against a measured 1.45x, and a compatibility count of 1,361 that has been 1,360 since the oracle's baseline was set. Benchmarks is generated from npm run bench and is the authority; this page exists to put six frameworks in one table, which that one does not do, and every cell here now says where it comes from.

  • ⚙️ measured by npm run bench on this repo, all four packages in one run on one machine, at the date below.
  • 📖 surveyed in competitor-landscape.md §2 on a different machine at a different time. Never compare a ⚙️ against a 📖 — that is a comparison between two machines.

Measured

⚙️ rows: 2026-10-19 (66e4611), a GitHub Actions runner (AMD EPYC 7763, 4 cores), linux/x64, Node 24.13, 52 interleaved spawns per variant. Milliseconds are a property of that machine, so the deltas below are for reading, never for gating; the machine-independent figure is the ratio on Benchmarks.

burgeecommanderyargs@oclif/corecaccitty
Downloads / weeknew508.2M259.3M10.9M49.4M30.5M
Runtime dependencies5, none outside the burgee family061800
Installed size ⚙️1607 KB203 KB515 KB912 KB 📖40 KB52 KB 📖
Full CLI run over bare node ⚙️+19.1 ms+15.3 ms+77.1 ms+131 ms 📖+4.1 ms+6 ms 📖

Three things in that table got worse when they were measured rather than asserted, and they stay in it.

Installed size. burgee installs at 1607 KB. It is our largest number, it is our least flattering one, and it has now been wrong on this page twice in opposite directions.

First it said 104 KB, a guess at the tarball rather than a measurement. That was corrected to 561 KB on 2026-09-09 — the real npm pack unpacked size, which is what lands in a user's node_modules — and then the package grew to 2.3x that figure while the cell sat still, because the cell was read from a published measurement rather than from a fresh install. Every check on this page compared the page against that file and stayed green.

Fixed 2026-09-16: the row is now measured against the tree by CI on every run, so it cannot fall behind again. It is checked there and not on a developer's laptop for a reason worth stating, since it is the same class of mistake: the walk starts at a workspace link, so locally it counts a developer's dev dependencies too and reads several hundred KB more for the same commit. The cell is whatever npm i burgee puts on disk on the runner, and nothing else.

85 KB of it arrived on 2026-09-17 and is worth naming. burgee/commander now spawns sub-binaries through bellpull instead of node:child_process, so burgee installs bellpull's whole package — 86,113 bytes. What it buys is a Windows subcommand that actually works: upstream commander sends every Windows spawn through node, which is a workaround for spawn not searching PATHEXT, and it is simply wrong for a subcommand that is a .cmd, a .bat, or has a shebang that is not node. The check on this row is what surfaced the cost, which is the argument for the check.

24 KB more arrived on 2026-09-30: seniority/yaml (24,226 bytes), the YAML parser that seniority's cosmiconfig drop-in needs to read the YAML files cosmiconfig reads. burgee never loads it — no module on burgee's path imports it, and its bundled column did not move — but it is on disk.

About 90 KB left on 2026-10-11. Every family package published a declaration file for every module, and a TypeScript consumer can only open the ones an exports entry reaches. burgee alone shipped 35 that none does, 76,504 bytes of them (migrate.d.ts was the largest). The build now prunes them by the closure the TypeScript compiler computes from the entry points, and a check fails if any shipped declaration imports one that was not shipped (D-20261011).

It buys no dependency outside the burgee family and seven drop-in front ends. It is still 1607 KB, and the bundled column on Benchmarks — what an application actually grows by — is the number to judge us on. That one is over its own ceiling too, and says so.

Speed. This page previously ran two rows, an import cost and a full run, with burgee's cells in both unsourced. The suite times one thing, a full CLI run as a process, so there is one row. burgee does not land level with cac: it starts at 1.47x cac's cold start against a published target of at or below cac, and that target is recorded as not met. It read 1.32x on 2026-09-09 and 1.56x on 2026-10-08, and CI runs since 2026-10-06 have read 1.47x to 1.56x: burgee's start-up got slower in that month, and this page says so rather than keeping the older figure. Since D-157 the gate is what is reachable: a downward-only ratchet at 1.55x cac, set from CI's own spread and only ever lowered, which it still meets. The burgee/yargs drop-in starts at 0.64x yargs (0.82x on 2026-09-09). The burgee/commander drop-in starts at 1.07x commander (1.18x on 2026-10-08, 1.08x on 2026-09-09).

These are the figures from the CI runner, which is where the suite now publishes from: the same commit measured eleven times there put burgee ÷ cac inside 2.3%, where a developer laptop under load spread the same ratio from 1.351 to 1.957 in a single day. The absolute milliseconds are a property of that machine and are not comparable with the two 📖 columns, which come from a survey on somebody else's. The ratios are what the gates and bands watch, because a ratio taken between two spawns in the same run cancels the machine out.

Moving to that machine did not uniformly flatter us. burgee ÷ cac improved from 1.45x to 1.32x and commander went from a small loss to a level result; burgee/yargs ÷ yargs moved against us, from 0.69x to 0.82x. The machine was chosen for its variance, before these numbers were read.

burgee's speed comes from node:util.parseArgs being in the standard library, not from a faster language: burgee is TypeScript, exactly like commander and yargs. On this run it buys a lead over yargs, and neither parity with commander nor a lead over cac.

@oclif/core's 131 ms is a surveyed number, not one this suite reproduces, and it is carried here rather than dropped because eighteen runtime dependencies against none outside the burgee family is the comparison this table exists for. Treat it as the survey's, not as ours.

Capabilities, with evidence

burgee against commander, yargs and meow, the three frameworks compat-oracle grades its drop-in paths against. Every mark links to its evidence — a test in this repository for burgee, and for the others the source file at the version the oracle grades, or that framework's own test suite — and scripts/capabilities-lock.test.ts fails the build when a cited test or quoted source stops saying what the cell claims. A row is here only when every cell of it can be proved.

✓ yes · ◐ partial, with what is missing · ✗ no · — does not apply. Every mark links to its evidence: our test or grade, or the incumbent’s source at the version compat-oracle grades.

Agents and automation

Shell and terminal

Configuration

Compatibility

Capabilities across six frameworks

This table is older and hand-maintained, and covers oclif, cac and citty, which the table above does not grade. Where the two disagree, the one above is the one with evidence: this table said commander dumps its help on a runtime error, and commander's own source says help after an error is off by default.

burgeecommanderyargsoclifcaccitty
One file, no build step✅ (enforced by test)✅✅❌ scaffold✅✅
Plugins that add commands✅ built❌ (RFC #2505, closed unlanded)❌✅❌❌
Plugins on commander syntax✅ built—————
--json envelope✅ built❌❌partial❌❌
Exit-code contract✅ built❌❌partial❌❌
No help dumped on a runtime error✅ built✅❌❌❌❌
Weight budget per entry point✅ built❌❌❌❌❌
commander compatibility✅ 1,360 / 1,360 of commander's own tests—❌❌❌❌
yargs compatibility✅ 816 / 816 of yargs' own tests❌—❌❌❌
--schema (the CLI as data)✅ built❌❌manifest❌❌
--mcp (an MCP server)✅ built❌❌❌❌❌
Shell completions✅ built❌partial✅❌partial
Types derived from one schema✅ builtpartialpartial✅partial✅

✅ built · 🔨 in progress

Plugins means a package that adds commands to a program it did not write, which is what oclif's plugins and burgee's do. citty 0.2.2 has a plugins option, but each one is a pair of setup and cleanup hooks run around a command and adds no command, so its cell stays ❌.

The three that matter

Plugins. commander has no plugin system — its RFC has been open for years — and yargs has none either. burgee has one, and it works on a program written in commander's syntax: change one import and you gain a capability commander cannot sell you at any price.

Nothing you rely on breaks. Compatibility is graded by commander's own 1,360 tests and yargs' 816, run in our CI, published as a pass rate that only goes up. Not the word "compatible" — a number you can check. (This paragraph said 1,215 and the table said 1,361; both were wrong, and the count the oracle's baseline actually holds is 1,360. It is on Benchmarks, where it is read from the oracle rather than retyped.)

One declaration, every surface. Help, --json, --schema, --mcp, completions and types are all projections of the same manifest, so they cannot drift, and adding the next one is an emitter rather than a feature.

What we do not have

Users. commander and yargs do 767M downloads a week between them; burgee is days old. Every number above is real and none of it matters until someone installs it. The compatibility burn-down is published from the first commit precisely so that gap is visible rather than glossed.

Reproducing

The ⚙️ rows come from the suite, not from a shell loop, because a shell loop measures whatever else the machine was doing:

git clone https://github.com/ofri-peretz/burgee && cd burgee
npm install && npx turbo run build
npm run bench -- --check

That writes benchmarks/results/cli-benchmarks/<date>.json, which carries every number on Benchmarks along with the resolved version and path of every package it measured. The 📖 rows are not reproduced by it; they are competitor-landscape.md §2's, taken on a different machine.

On this page