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

@fluxomize/hunch

v0.2.0

Published

Playwright's smart hunch about which tests to run. Predictive test selection with ML to cut CI time and carbon footprint.

Downloads

668

Readme

Status: beta. All three pieces work: the reporter records history, hunch train builds a model from it, and hunch run uses the model to run a subset of your suite. It has not yet been used in anger on a large real-world suite, which is the next thing that has to happen.

The problem

Your Playwright suite takes twenty minutes. You changed one CSS file. You still pay for all twenty minutes, on every push, on every pull request, and so does the planet: every minute of runner time is energy burned and CO₂ emitted.

Playwright already ships two good answers to part of this. --last-failed reruns what broke last time. --only-changed runs what statically depends on the files you touched. Both are useful, and Hunch is built to sit next to them rather than replace them.

But static dependency analysis only knows what could break. It does not know what usually breaks. A change to styles/main.css might statically touch 40 UI tests while, historically, only 3 of those 40 ever fail because of it.

The idea

Hunch learns that difference from your own history.

  1. Collect. A Playwright reporter records every test run, with its result, its duration and the files that changed in that commit, into a plain .hunch/history.jsonl in your repo.
  2. Train. hunch train builds a baseline logistic regression model that predicts which tests are likely to fail for a given set of changed files.
  3. Run. hunch run scores your suite against the current diff and runs only the tests worth running.
  4. Report. After each run you get the time, energy (kWh) and carbon (gCO₂eq) you saved.

No Python. No server. No dashboard. No network calls. Everything runs locally, on your machine or on your runner, and the data stays in your repository where you can read it.

Quickstart

pnpm add -D @fluxomize/hunch

Add the reporter to your Playwright config so Hunch starts learning from your normal runs:

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  reporter: [['list'], ['@fluxomize/hunch/reporter']],
});

That is the whole setup. Run your suite the way you always do, and Hunch writes one line per test per run into .hunch/history.jsonl at the root of your repository:

{
  "testId": "tests/login.spec.ts > login > signs in",
  "file": "tests/login.spec.ts",
  "project": "chromium",
  "duration": 1243,
  "status": "passed",
  "timestamp": "2026-09-17T05:11:47.998Z",
  "commitSha": "390f1c2e66160f62d0a8d75d56da77be15d38dde",
  "changedFiles": ["src/login.ts"],
  "branch": "main",
  "retry": 0
}

Commit that file. It is the training data, it is meant to be shared with your team, and it is plain text you can read, grep and diff.

Once you have around 20 to 30 runs of history, train a model from it:

npx hunch train

It tells you what it learned and how well, measured on runs it never saw:

Trained on 40 runs, 195 examples.

Quality at the usual 50% cutoff, measured on the most recent runs the model never saw.
  precision  100.0%  of the tests it flags, this many really fail
  recall      53.3%  of the tests that fail, this many get flagged
  F1          69.6%

Weights, largest influence first:
  -5.751  recentFailureRate
  +5.032  coChangeFailureRate
  +2.739  changesetSize
  ...

The model lands in .hunch/model.json as a readable list of feature names and weights. If it ever makes a choice you disagree with, that file is where you go to find out why.

Then run only what the model thinks is worth running:

npx hunch run -- --project=chromium
Selected 47 of 210 tests, from 3 changed file(s).
  2 of them have no history yet and are run regardless, because nothing is known about them.

  ... playwright output ...

Skipped 163 test(s), historically 4m 12s of machine time.
Estimated saving: 4.55 Wh, 2.16 gCO2eq.
Estimates, from your own history and the documented default assumptions about runner
power draw and grid carbon intensity. Not measurements.

Anything after -- goes straight to playwright test. Use --dry-run to see the selection without running anything.

While you wait

Training needs weeks of history, and a tool that says nothing for weeks is indistinguishable from a broken one. hunch stats answers the two questions you actually have during that time:

npx hunch stats
History: 42 runs, 210 tests, 8820 records.
  Collected from 2026-08-04 to 2026-09-17.
  Enough history to train. Run `hunch train`.

Fails most often:
    31%  tests/checkout.spec.ts > checkout > applies a discount code
    12%  tests/auth.spec.ts > auth > signs in with SSO

Flakiest:
    18%  tests/search.spec.ts > search > shows suggestions

Slowest:
    14.2s  tests/checkout.spec.ts > checkout > completes an order

Model: trained 2026-09-17 on 6300 examples.
  precision 81.0%, recall 74.2%, F1 77.4%
  Strongest signals: coChangeFailureRate +4.21, failureRate +2.05, flakyRate -1.88

That ranking is useful on its own, before any prediction happens. The flakiest list in particular is usually a to-do list somebody has been meaning to write down.

| Option | Default | What it does | | --- | --- | --- | | --ratio <n> | 0.3 | Fraction of the suite to run | | --min-tests <n> | 5 | Never run fewer than this many | | --diff <range> | HEAD~1..HEAD | Git range used to work out what changed | | --dry-run | off | Print the selection and stop |

Two rules that keep it from being confidently wrong

A test with no history is always run. No history means no prediction, and skipping a test because nothing is known about it would silently drop everything you added this week, which is exactly the code most likely to be broken.

When it cannot do its job, it refuses instead of guessing. No model, no git repository, no tests found: it says which, and runs nothing. A selector that quietly falls back to a partial run is worse than one that stops, because you would believe you had been tested when you had not.

Hunch is also complementary to Playwright's own --last-failed. Running that first and Hunch second gives you better coverage than either alone.

Configuration

Optional. Put hunch.config.json at the top of your repository, next to playwright.config.ts:

{
  "selectionRatio": 0.3,
  "minTests": 5,
  "minRunsToTrain": 20,
  "sustainability": {
    "runnerWatts": 65,
    "gridIntensity": 475
  }
}

Everything is optional and anything you leave out keeps its default. A command line flag beats the file, and the file beats the defaults.

The sustainability numbers are the ones worth setting. They are assumptions about hardware Hunch has never seen. 65 W is a rough figure for a GitHub hosted runner, and 475 gCO₂eq/kWh is a global average, which is the right shape of guess when the runner's location is unknown. A self hosted runner in Brazil sits closer to 85 gCO₂eq/kWh because the grid is mostly hydroelectric; the same suite is genuinely cleaner there, and Hunch has no way to know that on its own. hunch run prints the values it used, so what you see is always what it assumed.

On CI

Two ready to copy workflows live in examples/github-actions: one that runs the full suite on main and collects history, and one that runs Hunch's selection on pull requests. The README there covers the two things people get wrong, both of which silently produce a useless model rather than an error.

What the model looks at

Twelve features, all of them plain numbers you can inspect:

| Feature | What it captures | | --- | --- | | failureRate, recentFailureRate | How often this test fails, overall and lately | | observationConfidence | How much history stands behind those rates | | failureRecency | How long ago it last failed | | flakyRate | How often it fails and then passes on retry | | durationWeight | How slow it is | | coChangeFailureRate, coChangeSupport | How often it failed when these exact files changed before | | ownFileChanged | Whether the test's own file is in the diff | | pathAffinity | How close the test sits to the changed files in the tree | | changesetSize | How big the change is |

The two in bold are the reason this project exists. They are what a dependency graph cannot tell you: not what could break, but what has broken, for this change, before.

Known limitations

The baseline model has no regularisation. With a dozen features and the twenty to thirty runs this is designed for, features that measure similar things can end up with large opposing weights. The model still ranks tests sensibly, but do not read a single weight as if it were an independent finding. Quality is always reported on held out runs, so the numbers you see are not the model grading its own homework.

Reporter options

| Option | Default | What it does | | --- | --- | --- | | outputDir | .hunch | Where to write, relative to the repository root | | verbose | false | Print what the reporter is doing. Off by default, so normal runs stay quiet | | diffRange | HEAD~1..HEAD | Git revision range used to work out which files changed |

On CI you usually want diffRange to cover the whole pull request rather than the last commit:

reporter: [['list'], ['@fluxomize/hunch/reporter', { diffRange: 'origin/main...HEAD' }]];

The reporter is built so it can never fail your suite. Outside a git repository, or if anything it touches goes wrong, it prints a warning and records nothing.

Why energy matters here

Test selection is usually sold as a speed feature. Speed is the side effect. The thing being saved is compute, and compute is electricity.

Hunch reports its savings in kWh and gCO₂eq using a deliberately simple and fully documented estimate, with configurable runner power draw and grid emission factor. It is an estimate, not a measurement, and the docs will always say so.

Design constraints

Things Hunch will deliberately not do in v1:

  • Support test frameworks other than Playwright.
  • Ship a dashboard, a web UI, a backend or an API.
  • Require Python, at runtime or at build time.
  • Use deep learning, code embeddings or anything you cannot inspect in a JSON file.

Contributing

Contributions are welcome, including from people who have never contributed to open source before. Start with CONTRIBUTING.md, and please read our Code of Conduct.

License

Apache 2.0 © Fluxomize