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

pr-complexity

v1.2.0

Published

Score code changes by information content — K(diff | codebase)

Readme

pr-complexity

A GitHub Action that scores pull requests by information content instead of lines of code. Language-agnostic: JS/TS, Python, Go, Rust, Ruby, Java, C/C++, C#, PHP, Swift, Kotlin, SQL, shell, Dockerfiles — anything text-based.

It compresses the diff against the rest of your codebase (brotli). Code that repeats existing patterns — boilerplate, renames, house style — compresses to almost nothing. Genuinely new logic doesn't. Deletions earn symmetric credit. The raw byte count is then mapped onto a log-scale points score approximating the Kolmogorov complexity of the change given the codebase: K(diff | repo).

Lockfiles, generated code, and build output are ignored.

Usage

on:
  pull_request:

permissions:
  contents: read
  pull-requests: write

jobs:
  complexity:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: sebMellen/pr-complexity@v1

The action posts one comment per PR and updates it on every push.

CLI (for humans and agents)

Zero dependencies, runs anywhere with Node 20+ and git:

npx pr-complexity              # score uncommitted changes vs HEAD (incl. untracked files)
npx pr-complexity --base main  # score HEAD vs merge-base of main (PR mode)
npx pr-complexity --json       # machine-readable, per-file attribution
npx pr-complexity --budget 40  # exit 1 if over budget

Because the score is cheap, deterministic, and signed, an agent can optimize against it — implement, score, simplify, repeat. The cheapest way to lower the score is to reuse existing patterns and delete dead code, so gaming it mostly means doing the right thing:

while ! pr-complexity --budget 40 --json > score.json; do
  agent "Reduce complexity. Biggest contributors: $(jq -c '.files[:5]' score.json)"
done

Always constrain on behavior (tests must pass) — the score is a simplicity prior, not proof of correctness.

Reading the score

Points are a Fahrenheit-style 0–100 scale: 0 means the PR adds no new information, and 100 is the limit — roughly 16 KB of novel compressed logic, the largest change that should ever land in one PR. Scores are clamped to ±100.

Points = min(100, 5 · log2(1 + bytes/100)^1.5), signed — deletions push negative. The 1.5 exponent compresses the everyday range (most PRs land under 50) and stretches the top end, so genuinely huge changes still shoot toward the ceiling. On top of that, a file-scatter penalty charges for review context-switching: 0 for ≤10 files, 6·log2(files/10) beyond, capped at +30. Net-negative PRs are exempt — simplification is never taxed.

| Points | Band | Meaning | |---|---|---| | ≤ 0 | net simplifier | Removes more information than it adds. Reward these. | | < 10 | trivial | Typos, config tweaks, one-line fixes. | | < 30 | small | A focused fix or small feature slice. | | < 50 | moderate | Real new logic; review carefully. | | < 70 | large | Heavy new complexity — should earn its keep. | | 70–99 | split-worthy | Strongly consider splitting. | | 100 | the limit | Too big to review. Split it. |

Calibration data from a large production TypeScript API: focused bug fixes score 5–30, feature slices 42–58, major upgrades (Prisma v7, CI reworks) 70–82, and a whole new platform API prototype (~3.3K lines, dozens of files) scores 88. Because the scale is anchored to information content rather than repo size, a point means roughly the same thing across codebases.

Outputs

The action sets points and net_bytes as step outputs, so you can gate on them (e.g. require extra review above a threshold).

How it works

  1. Diff the PR against its merge base (git diff base...head).
  2. Drop lockfiles (*.lock, *.sum), generated code (*.gen.*, *.pb.*), minified files, and build output (dist/, node_modules/, target/, __pycache__/, …).
  3. Strip comments (//, #, --, /* */ — heuristic) and whitespace from the added/removed lines.
  4. Concatenate the repo's source files (capped at 256 KB, excluding the files being changed so new code can't compress against itself) as a compression context.
  5. Score = compress(context + added) − compress(context) minus the same for removed lines, pooled across all files — a pattern repeated in 20 files is one idea, not twenty. The file-scatter penalty separately captures the per-file review burden, and per-file byte estimates keep the score attributable.

Because compression is a novelty detector, unusual identifiers, magic numbers, and new abstractions cost full entropy, while the 50th return statement costs ~zero.

Zero dependencies — just Node's built-in zlib, git, and the GitHub REST API.