kitbash / benchmark

#The standing token tax, measured

Every agent that cannot lazy-load carries a skill's entire body in context every session, before the skill is ever invoked. No other skill format has a field for that cost. Kitbash measures it at compile time — these are the numbers, generated by the benchmark script from committed fixtures run through the real compile pipeline.

13×Standing cost of a manifested skill on an eager target vs a lazy one
45×Same comparison for an unmanifested SKILL.md-only skill
9Targets measured, every one through kitbash compile
0Hand-entered numbers — the table is generated and CI fails if it drifts

#What is measured

Two costs matter, and conflating them is how the expensive one stays invisible:

  • Loaded — tokens the agent reads when the skill is actually in play. Roughly the same everywhere; this is the number people assume they are paying.
  • Standing — tokens sitting in the context window every session, before the skill is invoked. Lazy targets keep only a short stub. Eager targets keep the whole body. This is the number nobody measures.

Token counts are estimates at roughly four characters per token — the same estimator the compiler enforces context.budget and context.standing with, so the benchmark and the build agree by construction. Loading modes are read from the adapters themselves, not restated here, so these tables cannot drift from what the compiler emits.

Kitbash compiles to the cheapest loading mode each target actually supports. Claude Code, Cursor, Copilot, Devin (ex-Windsurf), Gemini CLI and the vendor-neutral .agents/skills/ path all load on demand, so a skill there costs only its stub. What the tables below price is the rest: Cline, Aider and the AGENTS.md floor, whose only mode is eager — they carry the whole body, every session, before the skill is invoked.

#prereview — manifested, budget 1500, authored lazy

The first-party example skill, compiled to all nine targets.

TargetLoaded (tok)Standing (tok)Loading
claude-code56740lazy
cursor56840lazy
agents56740lazy
copilot56740lazy
cline539539eager
windsurf56940lazy
gemini56740lazy
aider560560eager
agentsmd560560eager

Standing tax: about 40 tokens on a lazy target against about 539 on an eager one — roughly 13× per session for the identical skill. A team running four agents pays that gap four times over, forever, unless something measures it.

#review-checklist — bare, no manifest

A plain SKILL.md folder following the skills.sh / Claude Skills convention. Longer body, no declared budget — the gap widens.

TargetLoaded (tok)Standing (tok)Loading
claude-code88019lazy
cursor87919lazy
agents88019lazy
copilot88019lazy
cline859859eager
windsurf88119lazy
gemini88019lazy
aider885885eager
agentsmd885885eager

Standing tax: about 19 tokens on a lazy target against about 859 on an eager one — roughly 45× per session. The longer the skill, the worse the ratio, because the lazy stub stays small while the eager copy grows with the body.

#What the compiler does with it

Measuring is only useful if something acts on the measurement. At compile time Kitbash:

  • warns when a skill authored lazy lands on an eager target, naming the standing cost and the declared limit;
  • fails the build under --strict, so the warning can gate CI instead of scrolling past;
  • enforces context.budget against the compiled body and context.standing against the stub, per target;
  • reports the whole repo's standing cost in kitbash doctor, so the total is visible before it is a problem.

The copy-per-agent status quo pays the eager cost everywhere and never sees the number.

#Reproduce it

The table above is generated, committed, and re-checked in CI — if a change to any adapter moves a number and the committed results are not regenerated, the build fails.

Run the benchmark No network, no API keys, no model calls — it compiles fixtures and counts.
git clone https://github.com/singhharsh1708/kitbash
cd kitbash/packages/cli && npm install
npm run bench
Raw results

#Honest caveats

  • Token counts are a four-characters-per-token estimate, not a model-specific tokenizer. Real counts vary by a few percent per model; the ratio between lazy and eager is what the argument rests on, and that ratio is robust.
  • Only two fixtures are measured. They differ in size and manifest status on purpose, but they are not a survey of every skill anyone will write.
  • This measures what the format costs, not what the model does with it. A cheap skill that gives bad instructions is still a bad skill — that is what kitbash test and kitbash lint are for.
  • The standing figure is a floor. It counts one skill. Install ten and an eager agent carries all ten bodies at once.

#Keep reading