From 2.0.2 to 3.11.1
Version 2.0.2 was a single 258-line index.js. It walked a directory, skipped
blank and comment lines, and printed one number: the count of everything left over. It
could write that number into a README and attach a size label to it. There were no tests
and no build step.
Everything below happened after that. The tool is now a typed, layered codebase with four commands, and the counting itself is verifiably accurate rather than approximately right.
3.0.0 — the rewrite
The whole tool was rewritten in TypeScript and split into layers:
cli → config → core → report. The core is pure functions, so the parts that
decide what a line is can be tested without touching a filesystem.
Test coverage is enforced at 100% for statements, branches, functions and lines. This is a CI gate, not an aspiration: a pull request that drops coverage to 99.7% fails. Publishing moved to GitHub Actions using npm trusted publishing over OIDC, with no long-lived token in the repository.
3.0.1 – 3.3.3 — counting got precise
2.0.2 discarded comments and blanks. 3.x classifies every line as code, comment or blank and reports all three per language, so the output is a table rather than a single number:
Language Files Lines Code Comments Blank Complexity %
JavaScript 3,954 673,654 546,899 62,317 64,438 37,405 65.5%
TypeScript 419 81,509 65,140 11,248 5,121 8,399 7.8%
Along the way:
- JSX and TSX became their own categories instead of being folded into JS and TS.
-
A trailing-blank-line overcount was fixed, and unknown extensions now fall into an
Otherbucket instead of being dropped. - The size scale was recalibrated against real repositories. The old labels topped out at 50,000 lines, which put most real projects in the same bucket. The new ladder runs from Meteoroid to Universe and spans 1,000 to 5,000,000 lines.
-
The CLI became subcommand-based.
clines countis read-only by default; the bare command prints a banner. Before this, runningclineswould modify your README without being asked.
3.3.3 added this site. Counting accuracy was checked against cloc: on the
react repository, over an identical 6,915-file set, the two agree to within 0.12% on code
lines and 0.02% on blanks.
3.4.0 – 3.8.0 — clines dup
The first new command finds duplicated code. Detection is maximal-block clone matching
over code lines, ignoring whitespace differences, so reformatted copies still match.
--html writes a self-contained report with each snippet, every location, and
a filter box.
The five releases in this range were mostly corrections found by running it on real
repositories: syntax highlighting and indentation in the snippets, merging overlapping
fragments, --min-copies for blocks duplicated more than twice, and a fix
where the headline statistics ignored the --min-lines and
--min-copies filters and so disagreed with the table underneath them.
3.9.0 – 3.10.0 — complexity
3.9.0 added a Complexity column to count: a decision-point count, following
the approach used by scc, of branch and loop keywords and logical operators,
with comments and string contents excluded. 3.10.0 promoted it to its own command,
clines complexity (alias cx), which ranks files rather than
aggregating by language.
Complexity: 51,082 total · 2,303 files with complexity
Most complex files
File Complexity Code
packages/react-devtools-shared/src/backend/fiber/renderer.js 1,292 6,487
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js 1,102 5,929
3.11.0 — clines context
The newest command estimates how many tokens a language model reads for the whole tree,
compares the total against a context window, and breaks it down per file and per top-level
directory. --max turns it into a CI check: the command exits with status 1
once the total exceeds the budget.
$ clines ctx --window 1m
Context: 8,776,571 tokens · 877.7% of a 1,000,000-token window · 11% comments
Adding a tokenizer dependency was not an option, so the estimator approximates one: sub-word splitting on camelCase and snake_case boundaries, symbol runs charged by length, and one token per newline. The constants were fitted against GPT-4o's tokenizer rather than guessed.
| Corpus | Total error | Per-file median |
|---|---|---|
| Mixed calibration set (2,943 files, 5.1M tokens) | +0.3% | 10.3% |
| facebook/react (6,915 files, 8.3M tokens) | +5.2% | — |
The estimate runs a few percent high on JavaScript and TypeScript and low on Markdown and JSON. The near-zero figure on the mixed set is those two biases cancelling out, not a general guarantee. Treat totals as accurate to within about ±10%.
Version summary
| Version | Change |
|---|---|
| 3.0.0 | TypeScript rewrite, layered architecture, 100% coverage gate, CI/CD |
| 3.0.1 | Exclude generated files; fold unknown extensions into Other |
| 3.1.0 | Per-language table with code, comment and blank columns |
| 3.1.1 | Split JSX and TSX into their own categories; fix trailing-blank overcount |
| 3.2.0 | Recalibrated project-size scale |
| 3.3.0 | clines count subcommand, banner, read-only by default |
| 3.3.1 – 3.3.2 | Fix clines help; new README marker comments |
| 3.3.3 | This site |
| 3.4.0 – 3.5.1 | clines dup, with an HTML report and syntax highlighting |
| 3.6.0 – 3.6.1 | Merge overlapping fragments; collapsible cards; snippet fixes |
| 3.7.0 – 3.7.2 | --min-copies; statistics now respect the filters |
| 3.8.0 | --html opens the report in the browser |
| 3.9.0 | Complexity column in count |
| 3.10.0 | clines complexity (cx) with a ranked HTML report |
| 3.11.0 |
clines context (ctx) with a --max budget
|
| 3.11.1 | Banner lists every command |
What did not change
clines.json still uses the same add and remove model,
.gitignore is still respected, and --readme still writes into
the same marker comments. A 2.x configuration file works unchanged.