5.0.0: auditing our own output
A tool that tells you where your codebase wastes effort should be able to survive the same question. So 4.4.0 through 5.0.0 came out of pointing that question at clines itself — first at what it spends, then at what it puts on your screen. Both audits were done by measuring rather than by reading the code and forming opinions, which turned out to matter.
4.4.0 — three kinds of repeated work
Profiling every command against facebook/react found three redundancies, none
of which were obvious from reading the source.
Glob patterns were recompiled per file, per pattern.
isIgnoredFile called globToRegExp inside a .some(),
so react's 44 .gitignore entries produced 304,216 RegExp constructions
on every run.
isIgnoredFile over 6,914 files: 171 ms
precompiled equivalent: 12 ms
Every file was classified twice. The collector worked out which lines were code,
comment or blank in order to summarise file roles — then tokenize,
measureContext and toDupFile each did the identical work again
on the same content.
comments spent a third of a second on data it never used. It ran the
full context analysis, estimating tokens for every line of every file, purely to rank
candidates by comment volume — then re-read and re-classified the fifty files it picked.
| command | before | after |
|---|---|---|
count |
1.13s | 0.84s |
dup |
1.23s | 0.90s |
cx |
1.20s | 0.82s |
ctx |
1.28s | 0.88s |
comments |
7.33s | 6.81s |
The check that mattered was not the stopwatch but the diff: output from
count, dup, cx and ctx is
byte-identical before and after.
4.5.0 — the output did not fit
Nothing in the codebase read process.stdout.columns, and four separate
renderers each hardcoded a 68-character truncation. On a standard 80-column terminal a
fifth of the output wrapped, which destroys the alignment the tables exist for.
| lines over 80 columns | before | after |
|---|---|---|
ctx |
24 | 0 |
cx |
22 | 0 |
comments |
21 | 0 |
dup |
12 | 0 |
Truncation also threw away the wrong end. In a monorepo the package name is the part that tells you where you are, and a leading ellipsis deleted exactly that:
before …plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts
after compiler/packages/babel-…/InferMutationAliasingEffects.ts
And a repository that dwarfs a context window now reads as a multiple:
16.5× a 200,000-token window rather than 1651.5% of one.
One finding in this pass was wrong. An early measurement reported a 219-character line in
count, which looked like a serious overflow. It wasawkcounting bytes: the box-drawing character─is three bytes in UTF-8, and the divider was 73 characters all along. Re-measuring by code point put every command under 100. Worth stating plainly, because a metrics tool has no business reporting a number it has not checked the units of.
5.0.0 — the rough edges
A walk through the actual journeys — piping, typos, empty directories, malformed config — turned up six problems.
The banner leaked escape codes when redirected.
clines > notes.txt wrote ^[[1m^[[38;5;38m… into the file.
Nothing checked for a terminal, and NO_COLOR was ignored. Both are now
honoured.
One mistyped config key produced ten lines of validator internals:
before Invalid config in …/clines.json: [
{ "code": "unrecognized_keys", "keys": [ "ignor" ], "path": [], … } ]
after Invalid config in …/clines.json:
Unknown key "ignor" — did you mean "ignore"?
--top did not do what it looked like it did.
clines cx --top 3 still printed twenty-one rows, because the flag only sized
the HTML report. It now sets how many files the terminal lists, on every command —
including dup, which had the most results and no limit at all.
--html opened a browser by itself. Writing a file is expected of a
command-line tool; launching a graphical application unasked is not. It now takes
--open.
Alongside those: comments says what it is doing before spending seven seconds
in git blame, an empty directory reports that no files were found instead of
drawing an empty table and calling the project a Meteoroid, and the
--html hint is dropped when the output is piped, because advice addressed to
a human does not belong in a pipeline.
Upgrading
npm went straight from 3.11.1 to 5.0.0, so an upgrade crosses two sets of breaking changes at once.
-
Numbers will drop: tests, fixtures, generated and vendored files are excluded by default
since 4.0.0.
--allreproduces the old output exactly. --htmlno longer opens a browser. Add--open.--topnow sets terminal rows, not HTML rows.comments --filesis now--scan.ctx --commentsis now thecommentscommand.clines.jsonis unchanged, and a 2.x config still works.
The intermediate versions were never published. There is nothing between 3.11.1 and 5.0.0 on the registry, and nothing missing from 5.0.0 because of it.