burgee

Benchmarks

Every number this project claims in public, measured — and the ones that are not measured, saying so.

Generated by npm run bench:page from the run of 2026-10-27 at commit 440f18f. Do not edit by hand.

Published claims, settled

Every number this repository states in public, checked against the record that settles it. "Not met" and "unmeasured" are different outcomes and never collapse into each other: one says we measured and it is not true, the other says we have not measured.

ClaimTargetMeasuredOutcome
the engine's cold start stays within 1.55× of cac's, the lightest framework in the landscape (a downward-only ratchet; the ≤ 1 bar is not reachable, D-102)<= 1.551.46 ratio✅ met
the core entry point is under 52 KB bundled<= 5324824,460 bytes✅ met
burgee stays within 2.35× of cac alone in a user's bundle (a downward-only ratchet, lowered as it shrinks; ≤ 1 is not reachable, D-102)<= 2.352.34 ratio✅ met
burgee/commander stays within 1.42× of commander alone in a user's bundle (a downward-only ratchet, lowered as it shrinks; ≤ 1 is not reachable, D-102)<= 1.421.417 ratio✅ met
burgee/yargs is lighter in a user's bundle than yargs, the package it replaces<= 10.975 ratio✅ met
roundel/chalk is lighter in a user's bundle than chalk, the package it replaces<= 10.599 ratio✅ met
roundel/tokens is lighter in a user's bundle than picocolors, the package it replaces<= 10.15 ratio✅ met
roundel/colorette is lighter in a user's bundle than colorette, the package it replaces<= 10.909 ratio✅ met
roundel/kleur is lighter in a user's bundle than kleur, the package it replaces<= 10.561 ratio✅ met
roundel/kleur/colors is lighter in a user's bundle than kleur/colors, the package it replaces<= 10.903 ratio✅ met
roundel/supports-color is lighter in a user's bundle than supports-color, the package it replaces<= 10.829 ratio✅ met
roundel/yoctocolors is lighter in a user's bundle than yoctocolors, the package it replaces<= 10.951 ratio✅ met
flagstaff/ora is lighter in a user's bundle than ora, the package it replaces<= 10.938 ratio✅ met
flagstaff/boxen is lighter in a user's bundle than boxen, the package it replaces<= 10.698 ratio✅ met
flagstaff/cli-table3 is lighter in a user's bundle than cli-table3, the package it replaces<= 10.705 ratio✅ met
flagstaff/spinner is lighter in a user's bundle than ora, the package it replaces<= 10.121 ratio✅ met
flagstaff/box is lighter in a user's bundle than boxen, the package it replaces<= 10.503 ratio✅ met
flagstaff/table is lighter in a user's bundle than cli-table3, the package it replaces<= 10.554 ratio✅ met
flagstaff/log-update is lighter in a user's bundle than log-update, the package it replaces<= 10.517 ratio✅ met
linegauge is lighter in a user's bundle than string-width, the package it replaces<= 10.939 ratio✅ met
linegauge/wrap is lighter in a user's bundle than wrap-ansi, the package it replaces<= 10.795 ratio✅ met
linegauge/slice is lighter in a user's bundle than slice-ansi, the package it replaces<= 10.78 ratio✅ met
linegauge/strip is lighter in a user's bundle than strip-ansi, the package it replaces<= 10.595 ratio✅ met
paratext is lighter in a user's bundle than ansi-escapes, the package it replaces<= 11.632 ratio❌ not met
bellpull is lighter in a user's bundle than tinyexec, the package it replaces<= 10.986 ratio✅ met
seniority/lilconfig is lighter in a user's bundle than lilconfig, the package it replaces<= 10.612 ratio✅ met
seniority/dotenv is lighter in a user's bundle than dotenv, the package it replaces<= 10.516 ratio✅ met
seniority/cosmiconfig + seniority/find-up is lighter in a user's bundle than cosmiconfig + find-up, what it replaces<= 10.154 ratio✅ met
closeout/exit-hook is lighter in a user's bundle than exit-hook, the package it replaces<= 10.96 ratio✅ met
closeout/signal-exit is lighter in a user's bundle than signal-exit, the package it replaces<= 10.93 ratio✅ met
controlroom/ink + react + react-reconciler is lighter in a user's bundle than ink + react, what it replaces<= 10.79 ratio✅ met
controlroom is lighter in a user's bundle than ink, the package it replaces<= 10.065 ratio✅ met
examples/dashboard/dashboard.mjs is lighter in a user's bundle than examples/dashboard-ink/screen.mjs, what it replaces<= 10.066 ratio✅ met
seniority/cosmiconfig + seniority/find-up installs no more bytes than cosmiconfig + find-up<= 10.119 ratio✅ met
seniority/cosmiconfig + seniority/find-up installs no more packages than cosmiconfig + find-up<= 10.1 ratio✅ met
controlroom/ink + react + react-reconciler installs no more bytes than ink + react<= 10.432 ratio✅ met
controlroom/ink + react + react-reconciler installs no more packages than ink + react<= 10.275 ratio✅ met
importing controlroom/ink starts no slower than importing ink + react, as a same-run ratio<= 10.4 ratio✅ met
importing controlroom starts no slower than importing ink + react, as a same-run ratio<= 10.163 ratio✅ met
burgee is lighter in a user's bundle than cac plus the 3 packages a user of it installs to reach the same capability set<= 10.25 ratio✅ met
burgee/commander is lighter in a user's bundle than commander plus the 3 packages a user of it installs to reach the same capability set<= 10.438 ratio✅ met
burgee/yargs is lighter in a user's bundle than yargs plus the 3 packages a user of it installs to reach the same capability set<= 10.548 ratio✅ met
commander's own test suite passes 1360 of 1360 against our entry point>= 13601,360 tests✅ met
yargs's own test suite passes 816 of 816 against our entry point>= 816816 tests✅ met
chalk's own test suite passes 58 of 58 against our entry point>= 5859 tests✅ met
ora's own test suite passes 99 of 99 against our entry point>= 9999 tests✅ met
log-update's own test suite passes 99 of 99 against our entry point>= 9999 tests✅ met
boxen's own test suite passes 84 of 84 against our entry point>= 84213 tests✅ met
an agent spends at least 40% fewer tokens per task against a CLI that meets the floor<= 0.60.747 ratio❌ not met
an agent takes at least 30% fewer turns per task against a CLI that meets the floor<= 0.70.75 ratio❌ not met

The two agent rows changed measure on 2026-10-09 (D-20261009-b1-totals-and-explain). This run settled them on the ratio of pooled medians, which commander's bimodal median turned into a coin flip. They are now settled on the tokens and turns each build spends summed over every task-run. Summed from this run's per-task detail, burgee ÷ commander is 0.692 for tokens (claim ≤ 0.6) and 0.703 for turns (claim ≤ 0.7). The first published run that carries the total records settles both rows here.

B2 — cold start

Spawned processes, not an in-process micro-benchmark: process time is what a user and an agent actually pay, and excluding Node's own startup would flatter every row. bare node is the floor row and most of every other number.

These milliseconds are a property of the machine that ran them — AMD EPYC 7763 64-Core Processor, 4 cores, linux/x64, Node v24.13.0, on CI — and are not comparable across machines. Nothing gates on them. What is gated and banded is the ratio below, taken between two spawns interleaved in the same run, which cancels the machine out.

Variantp50 msp95 msspawns
bare node28.429.992
commander45.248.792
burgee/commander48.351.992
yargs107.0111.492
burgee/yargs68.772.592
cac32.234.292
burgee47.150.392
picocolors31.833.392
roundel/tokens32.034.192
roundel/chalk34.536.392
ink + react345.8358.992
controlroom/ink138.7159.392
controlroom56.361.092
lilconfig34.536.192
seniority/lilconfig32.734.592
dotenv51.458.892
seniority/dotenv35.437.692
string-width61.563.592
linegauge34.936.692
exit-hook31.834.192
closeout/exit-hook31.133.192
signal-exit31.132.892
closeout/signal-exit32.334.992
Ratiop50p95gate
burgee/commander ÷ commander1.077×1.162×≤ 1.4
burgee/yargs ÷ yargs0.643×0.680×≤ 0.95
burgee ÷ cac1.460×1.565×≤ 1.55
controlroom/ink ÷ ink + react0.400×0.457×≤ 1
controlroom ÷ ink + react0.163×0.174×≤ 1
Differencep50p95gate
roundel/tokens − picocolors+0.2 ms+2.3 ms≤ +10.0 ms
roundel/chalk − picocolors+2.6 ms+4.8 ms≤ +10.0 ms
seniority/lilconfig − lilconfig−1.8 ms−0.3 ms≤ +2.0 ms
seniority/dotenv − dotenv−16.1 ms+0.8 ms≤ +10.0 ms
linegauge − string-width−26.6 ms−24.7 ms≤ 0.0 ms
closeout/exit-hook − exit-hook−0.6 ms+0.8 ms≤ +2.0 ms
closeout/signal-exit − signal-exit+1.1 ms+3.7 ms≤ +2.0 ms

B1, the half that needs no model — what an agent can act on

Not B1, and not a stand-in for it: this measures nothing about tokens or turns. It measures whether the CLI's answer is legible to an agent, which needs no model at all. Ten tasks per variant, one spawn each, non-TTY with stdin closed — the only environment an agent gets. The same demo program, built on each engine.

Varianthangs/100exit code--jsonbytes
burgee0100.0%100.0%164
commander040.0%25.0%31
yargs040.0%25.0%458

Exit code is the one that decides an agent's next move: 2 means rewrite the command, any other non-zero means the command was fine and the world was not. Both incumbents answer 1 to a usage error, which tells an agent nothing — so it retries a malformed command until it gives up.

Bytes is reported against us and is not gated. commander reads fewer than we do, because our errors carry a hint naming the fix. That is a trade — bytes per failure against failed turns — and only B1 proper can settle it. It is on this page precisely so the trade is visible rather than quietly omitted.

N10 — the floor, graded by someone else's checklist

Every other number on this page comes from an instrument this repository wrote, and an instrument its authors wrote can only find the faults they thought of. This one hands the same demo program to two published, third-party checkers and records what they say: clispec 0.3.0, the scorer for The CLI Spec v0.3 (six principles, 24 points), and cli-agent-lint 0.3.5 (34 checks, graded A–F). Both are pinned by version and archive digest in benchmarks/floor-tools.ts, run offline, and are never re-scored here — a check they fail is listed as failed, even where we would argue with it.

Measured at commit 28843a5 on 2026-10-19.

Variantclispeccli-agent-lint
burgee24 / 24 (Excellent)100.0% (A)
commander2 / 24 (Needs Work)71.2% (B)
yargs2 / 24 (Needs Work)70.9% (B)

What clispec 0.3.0 failed on burgee's demo:

  • none

What cli-agent-lint 0.3.5 did not pass on burgee's demo (warn and info included; skip is not):

  • none

B5 — runtime against the incumbent

"Faster" is a claim like any other, so it is measured like one. One realistic workload per pair — the mixed text a CLI prints, a redrawn frame, a 20-row wrapped table, a prompt typed into and submitted — each in its own process, in front of a TTY with colour and hyperlinks forced, and each first proving that ours and the incumbent print the same bytes: a ratio between two different jobs is the flattering number this page exists to refuse. Eleven interleaved rounds; the number is the median of the per-round ratio, ours ÷ theirs, so a round's load lands on both. A reading over its ceiling is confirmed by two more fresh processes before it fails, so load on one process cannot fail a pull request and a regression in every process still does.

The target is ≤ 1.0 for every pair. Where a pair is above it, the gate is a ceiling just above its CI measurement that may only go down: the PR that makes the pair faster lowers it, and raising one takes a recorded decision (.sdlc/bands/runtime-ratchets.json).

Measured at commit 440f18f:

Pairours ÷ theirs, p50p95≤ 1.0gate
paratext ÷ ansi-escapes1.085×1.114×❌ not met≤ 1.15
paratext/terminal-link ÷ terminal-link0.507×0.791×✅ met≤ 0.95
linegauge ÷ string-width0.739×0.884×✅ met≤ 0.85
linegauge/strip ÷ strip-ansi1.036×1.049×❌ not met≤ 1.1
linegauge/wrap ÷ wrap-ansi0.732×0.784×✅ met≤ 0.85
linegauge/slice ÷ slice-ansi0.957×1.134×✅ met≤ 1.05
roundel/chalk ÷ chalk1.240×1.282×❌ not met≤ 1.35
bellpull/node-which ÷ which0.314×0.319×✅ met≤ 0.5
bellpull ÷ tinyexec0.986×1.008×✅ met≤ 1
flagstaff/ora ÷ ora0.949×1.037×✅ met≤ 1.05
flagstaff/log-update ÷ log-update0.762×1.435×✅ met≤ 1.05
flagstaff/boxen ÷ boxen0.370×0.393×✅ met≤ 0.45
flagstaff/cli-table3 ÷ cli-table30.390×0.467×✅ met≤ 0.45
caique/clack ÷ @clack/prompts1.102×1.167×❌ not met≤ 1.7
burgee/commander ÷ commander1.044×1.245×❌ not met≤ 1.2
burgee/yargs ÷ yargs1.081×1.123×❌ not met≤ 1.1

The ceilings every pull request is gated on today:

Entry pointReplacesTargetCeilingFirst ceilingSteps
paratextansi-escapes≤ 1≤ 1.1512.35 (2026-09-29)3
paratext/terminal-linkterminal-link≤ 1≤ 0.9529.7 (2026-09-29)3
linegaugestring-width≤ 1≤ 0.851.95 (2026-09-29)4
linegauge/stripstrip-ansi≤ 1≤ 1.15.6 (2026-09-29)4
linegauge/wrapwrap-ansi≤ 1≤ 0.852.6 (2026-09-29)4
linegauge/sliceslice-ansi≤ 1≤ 1.0511.65 (2026-09-29)4
roundel/chalkchalk≤ 1≤ 1.355.5 (2026-09-29)4
bellpull/node-whichwhich≤ 1≤ 0.51.8 (2026-09-29)3
bellpulltinyexec≤ 1≤ 11 (2026-09-30)1
flagstaff/oraora≤ 1≤ 1.051.8 (2026-09-29)5
flagstaff/log-updatelog-update≤ 1≤ 1.054.2 (2026-09-29)4
flagstaff/boxenboxen≤ 1≤ 0.451.3 (2026-09-29)5
flagstaff/cli-table3cli-table3≤ 1≤ 0.452.2 (2026-09-29)4
caique/clack@clack/prompts≤ 1≤ 1.73.35 (2026-09-29)4
burgee/commandercommander≤ 1≤ 1.21.6 (2026-09-29)3
burgee/yargsyargs≤ 1≤ 1.11.55 (2026-09-29)3

B3 — compatibility

Read from compat-oracle and never recomputed here: two implementations of a compatibility rate produce two rates. The full method, and the control column that proves the gate works, are on Compatibility.

HostPassingRate
ansi-escapes4100.0%
boxen213100.0%
chalk59100.0%
clack16100.0%
cli-cursor8100.0%
cli-table329100.0%
cli-truncate12100.0%
colorette67100.0%
commander1,360100.0%
cosmiconfig24299.6%
cross-spawn68100.0%
dotenv197100.0%
execa00.0%
exit-hook21100.0%
extra-typings369100.0%
find-up104100.0%
ink1,304100.0%
inkjs-ui102100.0%
inquirer-core45100.0%
inquirer-prompts221100.0%
kleur15100.0%
lilconfig77100.0%
log-update99100.0%
meow14698.6%
onetime6100.0%
ora99100.0%
picocolors1100.0%
rc1100.0%
restore-cursor6100.0%
signal-exit13499.3%
slice-ansi104100.0%
string-width233100.0%
strip-ansi8100.0%
supports-color55100.0%
term-img18100.0%
terminal-link8100.0%
which5100.0%
widest-line1100.0%
wrap-ansi85100.0%
yargs816100.0%
yoctocolors78100.0%

B4 — weight

Bundled is what your application grows by when you import that entry point: a fixture importing exactly it, one symbol used, esbuild-minified — the incumbent measured by the same command over the same fixture shape. A tarball size answers a different question.

Installed is what npm install puts on disk, including everything the package drags in. Ours drag nothing, which is where the two columns diverge most; but one of our packages serves every entry point it publishes, so the installed column is not per-entry-point and nothing gates on it.

Entry pointReplacesBundledTheirsRatioInstalledTheirs
burgeecac24,46010,4522.340×1,722,51241,198
burgee/commandercommander55,39339,0841.417×1,722,512207,368
burgee/yargsyargs108,897111,6860.975×1,722,512528,744
roundel/chalkchalk4,7908,0020.599×131,56156,180
roundel/tokenspicocolors3832,5570.150×131,5616,373
roundel/colorettecolorette1,7941,9730.909×131,56117,009
roundel/kleurkleur1,0361,8470.561×131,56120,250
roundel/kleur/colorskleur/colors6216880.903×131,56120,250
roundel/supports-colorsupports-color1,7642,1280.829×131,56111,618
roundel/yoctocolorsyoctocolors2,2472,3630.951×131,56113,519
flagstaff/oraora55,76059,4600.938×690,089285,079
flagstaff/boxenboxen28,67141,1050.698×690,089804,192
flagstaff/cli-table3cli-table324,40334,6200.705×690,089153,737
flagstaff/spinnerora7,20759,4600.121×690,089285,079
flagstaff/boxboxen20,69641,1050.503×690,089804,192
flagstaff/tablecli-table319,18434,6200.554×690,089153,737
flagstaff/log-updatelog-update18,75136,3000.517×690,089262,051
linegaugestring-width5,6466,0130.939×107,19837,194
linegauge/wrapwrap-ansi11,42714,3720.795×107,19880,578
linegauge/sliceslice-ansi11,41114,6340.780×107,19866,999
linegauge/stripstrip-ansi2724570.595×107,19810,484
paratextansi-escapes7,1024,3511.632×104,10930,912
bellpulltinyexec5,8835,9690.986×97,76626,969
seniority/lilconfiglilconfig3,2865,3650.612×225,52217,985
seniority/dotenvdotenv5,96611,5640.516×225,52246,091
seniority/cosmiconfig + seniority/find-upcosmiconfig + find-up12,87783,8700.154×225,5221,892,988
closeout/exit-hookexit-hook1,5791,6450.960×110,55213,200
closeout/signal-exitsignal-exit2,6572,8560.930×110,55276,966
controlroom/ink + react + react-reconcilerink + react531,706673,1370.790×3,529,6088,165,450
controlroomink41,982642,3300.065×1,417,4857,986,787
examples/dashboard/dashboard.mjsexamples/dashboard-ink/screen.mjs48,366730,2100.066×1,417,4858,470,778

A complete CLI — the dependency bill

What npm install puts on disk for a CLI that parses its arguments, colours its output, shows a spinner, prints a table and asks a question. Ours is the union of each package installed alone, offline, from its tarball (supply chain). Theirs is each incumbent's tree as package-lock.json resolves it, and each tree's size agrees with the one the upstream watch resolved from the registry for the same release. A package installed at two versions counts twice: both are on disk.

Paired by job: parse arguments (burgee for commander), colour (roundel for chalk), spinner (flagstaff for ora), table (flagstaff for cli-table3), prompts (caique for @inquirer/prompts).

npm installPackages installedBeyond the ones namedInstalled at two versionsFrom outside the family
burgee + roundel + flagstaff + caique9500
commander + chalk + ora + cli-table3 + @inquirer/prompts49443—

Ours installs bellpull, burgee, caique, closeout, flagstaff, linegauge, paratext, roundel, seniority, every one published from this repository. Theirs installs ansi-regex, string-width, strip-ansi twice, at two versions each; the incumbents are separate projects, so the last column has no meaning for them.

Reproduce

npm install
npx turbo run build
npm run bench -- --check

Results are written to benchmarks/results/<suite>/<date>.json and read by the Stage 6 control bands in .sdlc/bands/control-bands.json. The suite refuses to emit a band value it did not measure; see benchmarks/README.md.

On this page