npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@towles-tool/comment-budget

v0.2.1

Published

A budget for comment volume: AI writes comments faster than anyone can review them, so cap how much commentary a reader wades through to reach the code.

Readme

comment-budget

A budget for comment volume, written for a codebase an AI writes most of.

A model can emit more commentary in one pass than a human could ever accurately review, and it keeps adding — narrating each step, restating the line below it, heading every block, layering the next pass over the last — until there is too much of it to read. Commentary nobody reads is not documentation; it is what the code is hiding behind.

So the budget is a cap on what review can actually absorb. One question: how much commentary must a reader wade through to reach the code?

comment_lines / (comment_lines + code_lines)      blank lines ignored

So 0.15 means "1 line in 7 is comment" — not comments-per-code-line. Two other signals sit beside the ratio: an over-long unbroken run of comment, and an over-long .md. It never reads what a comment says, so it can't tell you one is stale or wrong — that is not the check. Volume is, and nothing else measures it.

Install

cargo install comment-budget                   # or, for a prebuilt binary:
bun add -d @towles-tool/comment-budget         # npm/pnpm/yarn work too

Use

comment-budget                    # the gate: only the lines this branch adds over `main`
comment-budget --all              # the standing backlog, repo-wide
comment-budget --report           # thresholds in effect, surface table, worst files
comment-budget --format json      # findings as an array, for CI to consume
comment-budget --surface web      # one surface, for a session spent fixing it

comment-budget --new-from-merge-base release   # gate against a branch other than main
comment-budget --new-from-rev HEAD~3           # gate against a revision itself

Exit status is 0 when nothing errored, 1 when something did, 2 on a bad invocation. Warnings never fail the run — they are the standing hit list.

Why the default is a diff

Repo-wide, an established codebase reports hundreds of errors, and a gate that fails every run is one nobody reads. So the default judges only what a branch adds, and the ratio is the added lines' own — a branch can neither add commentary nor inherit the file's existing debt. An over-long run is the exception: it is measured whole and merely has to touch an added line to be reported, because a reader wades through all of it however much you wrote.

The comparison is against the working tree, so a local run judges what you are about to push, not only what you have committed. --whole-files opts back into judging every touched file whole. The --new-from-* flag names are golangci-lint's, which is where the idea is best known from.

Configure

Everything measured — and how hard, and why — lives in comment-budget.toml at the root of the tree, found by searching upward from the working directory.

  • kinds bind file extensions to a grammar — rust, typescript, tsx, hcl, or prose. exempt prefixes are invisible to every signal, neither comment nor code, so a Rust //! header can hold the decision it records — but only for a file's first exempt_free lines, past which they count normally. That cap is what stops exempt being a hiding place: without it the cheapest way to pass is to move prose from /// into //!, shortening nothing for a reader. counted is the bloat being measured; a comment matching neither list counts, so a new syntax can't slip through unmeasured.
  • surfaces claim paths by glob and set the thresholds. First match wins, and a readable file no surface claims is a hard error — under first-match-wins the failure mode of this design is a tree nobody noticed was exempt, and that reads exactly like passing.
  • surface.file fires only when ratio and line count both exceed a tier. Mass alone flags a big well-commented file; ratio alone flags a tiny stub whose three doc lines are 40% of nothing. Neither is the thing being hunted.

A file may opt out with a top-of-file comment-budget: allow(<reason>). The reason is required — an unexplained opt-out is the failure mode it exists to prevent. A minimal config:

skip = ["node_modules", "target", "dist"]

[kinds.rust]
grammar     = "rust"              # the tree-sitter grammar whose comment nodes are read
extensions  = ["rs"]
exempt      = ["//!"]             # module docs: where hard-won "why" lives
exempt_free = 12                  # ...but only this many lines of it are free
counted     = ["///", "//"]       # item docs and narration: the bloat being measured

[kinds.markdown]
grammar    = "prose"              # parses nothing; measures length instead
extensions = ["md"]

[[surface]]
name   = "crates"
paths  = ["crates/*/src/**/*.rs"]
goal   = "Document the module and the crossing points; not every pub item."
target = 0.15                     # reported per surface, not enforced
[surface.file]
warn  = { ratio = 0.20, lines = 50 }
error = { ratio = 0.30, lines = 100 }
[surface.run]
warn  = 8
error = 14

[[surface]]
name   = "docs"
paths  = ["**/*.md"]
goal   = "Prose has no code to sit against, so length is the only signal it offers."
target = { lines = 150 }
warn   = { lines = 150 }
error  = { lines = 250 }

[escape]
directive = "comment-budget: allow(<reason>)"

Library

The binary is a thin shell over the crate; Finding keeps its fields rather than only a rendered line, so a consumer can emit GitHub annotations or editor diagnostics without parsing text back out.

let (cfg, root) = Config::discover(&std::env::current_dir()?)?;
let diff = Diff::open(&root, &Since::MergeBase("main".into()), false)?;
let analysis = comment_budget::analyze(&root, &cfg, Some(&diff))?;
for finding in comment_budget::judge(&cfg, &analysis.stats) {
    println!("{finding}");
}

Fixing what it reports

Delete, don't reflow. Cut history — git already holds it — and keep only what looks forward: the why, and the how where the code leaves it unclear. Squeezing under a threshold just moves an error onto the warning list, and the budgets are not the thing to lower.

License

MIT OR Apache-2.0, at your option.