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

@third-option/comment-budget

v0.1.0

Published

A gate on comment volume: how much prose must a reader wade through to reach the code.

Readme

comment-budget

A gate on comment volume. One question: how much prose 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 prose" — not comments-per-code-line. Two other signals sit beside the ratio: an over-long unbroken run of comment, and an over-long .md.

Linters flag what a comment says, not how much of it there is; comment volume goes unmeasured. This measures only that.

Install

cargo install comment-budget                    # or, for a prebuilt binary:
bun add -d @third-option/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           # surface table and worst files, no finding list
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 prose 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 of it 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. exempt comment prefixes are invisible to every signal, neither comment nor code, so a Rust //! header can be as long as the decision it records. counted is the bloat being measured. A comment matching neither 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 the directive 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, never capped
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.