Your first run
Point it at a directory:
npx bonsai-lint --over 15 src/Three columns come back, worst first:
69 packages/billing/src/invoice-mapper.service.ts:47 InvoiceMapperService::mapLineItems 25 services/api/src/Controller/CheckoutController.php:207 CheckoutController::applyDiscounts 22 packages/web/src/parser/lexer.js:19 LexerScore, then the file and the line the declaration starts on, then the qualified name of the
unit. That is the whole format. A clean run prints nothing at all and exits 0.
The default threshold is 15
Section titled “The default threshold is 15”Run it with no arguments at all and it still reports, because 15 is the default for every language. That is a widely used default for a metric of this kind, and it is what this documentation uses in examples.
npx bonsai-lint src/ # same as --over 15Treat it as a starting point rather than an authority. A threshold is a claim about what your
team will tolerate, and 15 is a claim somebody else made about code they have not read.
--over changes it for one run; threshold in bonsai-lint.toml changes it for the project.
Look before you pick a number
Section titled “Look before you pick a number”The first run is more useful as a question, what is actually in here?, than as a verdict.
--all scores every unit and reports all of them, so you see the distribution rather than
guess at it:
npx bonsai-lint --all src/ | head -30If most of your code sits under 10 and a handful of functions are in the 40s, the number to argue about is obvious. Sort by the score and pick a threshold from what you find.
Reading a score
Section titled “Reading a score”Cognitive complexity measures how hard code is to read, not how hard it is to test. A
switch with twenty arms is cyclomatically awful and cognitively fine; three nested ifs are
the reverse. Increments come from breaks in linear flow, and each one costs more the deeper it
is nested, which is why a callback pyramid scores far above the sum of its parts.
The scoring rules page has the full increment table. The short version:
+1for each branch, loop,catch, or run of boolean operators+1more for every level of nesting the construct sits inside- nesting compounds through closures, so a callback inside a loop inside a condition is scored at the depth it genuinely sits at
What gets scanned
Section titled “What gets scanned”Every supported file under the paths you name, minus declaration files and minified output. You do not select a language. Point it at a PHP project and it behaves as a PHP linter, point it at a monorepo and it scores everything in one pass on one metric.
See languages and extensions for the mapping, and cross-language parity for why one threshold is meaningful across both.
Where to go next
Section titled “Where to go next”- The scores are high everywhere and you cannot fix them today → baselines
- Different teams need different limits → monorepos
- One function genuinely has to stay complex → suppressing findings
- You want it to fail a build → continuous integration