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

@prcompass/core

v0.2.0

Published

Deterministic engine for PR Compass: commit-mining, churn, cochange, risk-scoring. Zero network, zero LLM, zero DB. Fixture-in, JSON-out.

Readme

@prcompass/core

The deterministic engine for PR Compass: turns a list of git commits into per-file risk metrics. No network, no LLM, no database. Same input always produces the same output.

This package backs the OSS release of the PR Compass hosted product. The hosted product is the closed-source layer above; this engine is what computes "which file looks risky" given the repository's commit history.

Status

Pre-1.0. Public API is frozen at 1.0.0 per VERSIONING.md (planned). Until then, breaking changes only happen between minor versions and are listed in CHANGELOG.md.

Install

npm install @prcompass/core

ESM-only. Requires Node 20+.

Quick start

import {
  mineCommits,
  computeChurn,
  computeCochange,
  computeHotspots,
  computeRisk,
} from "@prcompass/core";

// 1. You produce CommitRecord[] yourself (e.g. via git log; see § "Producing CommitRecord[]" below).
const commits = await yourGitDriver.listCommits();

// 2. Mine: classify each commit as bug-fix or not.
const mined = mineCommits({ commits });

// 3. Per-file metrics.
const churn = computeChurn({ mined });
const cochange = computeCochange({ mined });
const hotspots = computeHotspots({ mined });

// 4. Combine into a risk report.
const risk = computeRisk({ mined, hotspots, churn, cochange });

// 5. risk.byFile[path] is your per-file FileRiskReport.
//    Every numeric value is grounded by real commit SHAs or is null.
console.log(JSON.stringify(risk, null, 2));

Invariants

These rules are non-negotiable. ESLint enforces import-side at the workspace root; the package-invariants.test.ts enforces package-side; the rest are upheld by code review and the determinism / no-fabrication tests.

  1. No network I/O. No fetch, no octokit, no node:http. Inputs come in as data; outputs go out as data.
  2. No LLM clients. No @anthropic-ai/sdk, no openai, no @prcompass/llm-client. Every claim is derived from real commit history, not generated.
  3. No DB clients. No @supabase/*, no @prcompass/db. The package neither reads nor writes persistent storage.
  4. No environment variables. Configuration is passed as function arguments. There is no process.env.* in business logic.
  5. No subprocesses. No child_process, no spawn. Reading the git history is the caller's job (see "Producing CommitRecord[]" below).
  6. Deterministic. Same input produces the same output, bit-for-bit. No Date.now(), no Math.random(), no unordered iteration in business code.
  7. No fabrication. A numeric claim either points to a real commit SHA in groundedIn or is null. The engine never defaults to 0 when it has no signal. The opposite is also true: a 0 is a real measurement (e.g. "this file appears in commits but none of them were bug-fix") and is grounded.

Public surface

| Module | Function | Purpose | | --------------- | ------------------------------------------------------- | ------------------------------------------------------------------------- | | commit-mining | mineCommits(opts) | Classify each commit as bug-fix vs not, attach a signal. | | commit-mining | parseCommitMetadata(stdout) | Pure parser for git log --format=.... | | commit-mining | parseCommitFiles(stdout) | Pure parser for git log --name-only --format=.... | | commit-mining | BUGFIX_REGEX, isBugFixCommit, filterBugFixCommits | Default bug-fix heuristic. | | churn | computeChurn(opts) | Per-file commit count, bug-fix count, defect density, first/last touched. | | cochange | computeCochange(opts) | File×file co-modification graph with Jaccard weights. | | hotspots | computeHotspots(opts) | Bayesian-smoothed bug-fix score per file. | | risk | computeRisk(opts) | Combine the above into a per-file risk report. |

All public types live alongside their function. See JSDoc for full details.

Producing CommitRecord[]

The engine consumes CommitRecord[]. To produce that from a git directory, run two git log passes and feed their outputs to the parsers:

import { execFile } from "node:child_process";
import { promisify } from "node:util";
import {
  parseCommitMetadata,
  parseCommitFiles,
  type CommitRecord,
} from "@prcompass/core";

const run = promisify(execFile);

async function listCommits(repoDir: string): Promise<CommitRecord[]> {
  const META = "%x1e%H%x1f%P%x1f%aN%x1f%aI%x1f%B";
  const { stdout: metaOut } = await run("git", ["log", `--format=${META}`], {
    cwd: repoDir,
  });
  const { stdout: fileOut } = await run(
    "git",
    ["log", "--name-only", "--format=\x1eCOMMIT %H"],
    { cwd: repoDir },
  );
  const meta = parseCommitMetadata(metaOut);
  const files = parseCommitFiles(fileOut);
  return meta.map((m) => ({ ...m, filesTouched: files.get(m.sha) ?? [] }));
}

The @prcompass/cli package (M12) ships a LocalAdapter that does exactly this. Use it directly if you don't want to write subprocess code.

License

Apache-2.0.