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

emendant

v0.1.2

Published

Watches API and SDK breaking changes, finds the affected code, and writes a patch it has proved against your own checks.

Readme

Emendant

Watches API and SDK breaking changes, finds the affected code in a repository, and writes a patch it has proved against that repository's own checks.

Dependabot bumps a version number in a manifest. It does not change the code that calls the changed API, so the bump lands, the build fails, and a human does the real work. The gap is the semantic change, not the version number.

Install

npx emendant scan

That is the whole of it. Node 20 or later, no account, no sign-in, and no configuration: the change feed ships inside the package, so a scan makes no network request unless you turn on automatic feed updates. npx fetches the package and runs it, so there is no install step to do first. Install it properly when you want it in CI:

npm install --save-dev emendant

The first run at a terminal asks three short questions before it scans, and never asks them again: which model provider writes the few patches no transform can, whether to keep the feed up to date by itself, and whether to go ahead. Between the last two it lists the supported SDKs your repository has, which costs no scanning. Only providers whose credential is already in your environment are offered, the key is read when a request is made and never stored, and what is recorded is the provider name. All of it needs a terminal, so a scan in CI goes straight to the report.

What it does

emendant scan reads your manifests and lockfiles, walks the source it is allowed to read, parses only the files that could possibly be relevant, and reports the places where a breaking change described in the feed genuinely applies to code you have. It follows a value across files to get there, so a call site that never mentions the package by name is still found.

emendant fix turns the deterministic findings into patches and proves each one before offering it. emendant explain <change-id> prints the feed entry behind a finding, with its primary source. emendant feed update fetches a newer signed feed, and emendant feed status says which feed a scan would use without making a request. emendant scan --sarif prints the findings as one SARIF 2.1.0 log, so a CI run can upload them to code scanning. emendant --version says which build you have.

Coverage today is the AI SDKs: ai, openai, @anthropic-ai/sdk, langchain, @langchain/core and @google/genai, for TypeScript and JavaScript. It grows without a new release of the tool, because the feed is delivered separately.

A finding says which version it is about

  openai (installed as legacy-openai)  installed 4.104.0 → 5.0.0
  2 changes affect this repository
  ────────────────────────────────────────────────────────────────

  !  deprecation  Public core modules moved under openai/core
     core-module-paths  deterministic fix
     src/legacy.ts:6:1
       6 │ import { fileFromPath } from 'legacy-openai/uploads';


  ×  breaking     fileFromPath helper removed
     filefrompath-removed  assisted fix
     src/legacy.ts:6:10
       6 │ import { fileFromPath } from 'legacy-openai/uploads';
         │          ^

     src/legacy.ts:9:10
       9 │ return fileFromPath(path);
         │        ^

2 findings · 3 call sites in 1 file
→  emendant fix  writes and proves a patch for 1 of these call sites
→  emendant explain openai-npm-5.0.0-core-module-paths  the sources and the migration guidance

Three placements, and the report never mixes them up. pending: the installed version is below the release, so the code works today and the upgrade is what breaks it. adopted: the project is already on the release and the old call site is still there; hidden unless you ask for it with --adopted. unknown: the manifest declares a range that sits either side of the release and there is no lockfile to settle it, so the finding is reported and a warning says we could not place it. Guessing would select the wrong entries, and staying silent would hand that repository a clean report it had not earned.

Exit codes are 0 nothing found, 1 findings present, 2 tool error. Exit 1 is not failure; it means there is something to read.

A patch is proved before you are offered it

emendant fix rewrites the findings a named AST transform can rewrite, and writes one diff per finding into .emendant/patches/. It is deliberately narrow: the feed entry must carry a deterministic fix, the change must be curated at high confidence, and the provenance chain behind the site must be complete (every step traced, nothing assumed). Everything else is reported unfixed, with the reason, because "not fixed because the chain has an untraced step" is a sentence a person can act on and a silently missing finding is not.

Then it proves them. Each patch is applied in a copy of your repository outside your working tree, moved first to the version the patch was written against, and put through your own typecheck and test commands. Those commands are found rather than configured: an ordinary repository with a typecheck or test script needs no emendant.json to get a proved patch, and explicit typecheckCommand and testCommand values still win over what was found. They run behind the strongest boundary your machine has: a temporary home, a reduced environment, no network, confined writes, and credential files masked by name.

A patch that does not pass is not written, and the finding says why. A patch that does pass names the commands that passed inside its own header, because the patch is the thing that gets mailed and pasted into a review.

There are four grades and only two of them are a green run. test verified and typechecked mean your commands passed. structurally checked means the edits were applied and the workspace matched again, with nothing compiled or run, which is what a repository owning no checks gets. unchecked means even that was not possible. A run that could not decide, because your suite was already red or the install did not work, says so rather than blaming the patch.

A candidate is not a finding

emendant prefilter walks a repository and reports which files survive a plain substring search. A survivor contains the right characters somewhere in its text, including inside a comment, which is how the file implementing the prefilter ends up as a candidate for a change to OpenAI's beta.chat namespace.

That is the stage working. It answers "could this file possibly be relevant?" and stops, so that the expensive stage only sees the 0.4 percent of files worth looking at.

emendant match is the expensive stage: it parses each survivor, resolves what every import binds and what every receiver is, and reports only the places where the change genuinely applies. It routinely discards half the candidate files it is given, and every discarded one is a comment or a string literal that mentions an API rather than code that uses it.

A match still is not a finding. emendant scan adds the question the other two commands do not ask: is the installed version one this change applies to? That is the difference between "this code calls a removed helper" and "this code will break when you upgrade".

Why the feed is the product

Large language models write code well already, so the agent is not the moat. The moat is the curated, machine-readable record of what each breaking change actually breaks and what the fix looks like. Nobody publishes this in a usable form: changelogs are prose, release notes are inconsistent, and semantic version numbers do not tell you which call sites break.

Every entry cites a real source URL, pinned to a tag. If a change cannot be cited, it does not go in the feed.

Precision over recall

A tool that reports 40 things is ignored. A tool that reports 2 things that are both real gets installed everywhere. So: if a matcher cannot resolve a binding with confidence, it reports nothing. A missed finding costs one user interaction; a false finding costs the user.

This is why every entry ships with negative fixtures as well as positive ones, and why the negatives that the cheap substring gate cannot reject are the ones that matter. A local function sharing a removed export's name, a property chain on an unrelated object literal, a relative import containing the package path: each contains a trigger string, and each must still produce nothing.

The matcher declines wherever it would have to guess. A receiver is attributed to a package only by an import, a constructor call, or an instanceof guard, never by inference. An identifier bound twice in one file is dropped entirely rather than resolved by assumption. The matching documentation has the whole contract and its limits.

Precision applies to detection too

A version we are not sure of is worse than no version, because it selects the wrong feed entries. So detection reports a manifest range as a range rather than resolving it, and when a lockfile installs two versions of one package it says so instead of picking one. Two managers' lockfiles in one directory are treated the same way: nothing in a repository proves which of them was written last, not a packageManager declaration and not a marker file, so both are read and a disagreement between them is reported rather than resolved by filename. Naming the manager yourself settles it, in emendant.json or with --package-manager. The detection documentation has the whole contract, including the limits.

It applies to what the tool says about itself, too. Every warning is tagged with whether it describes a gap in coverage (something the repository has that we did not check). Those warnings print by default, because a user who is not told reads a clean report as though everything was looked at. Warnings that merely explain a choice wait for --verbose, and are counted rather than silently dropped.

Security

These are hard constraints, and they are also why the tool is worth installing.

  • It runs entirely on your machine. scan makes no network request by default: the feed ships inside the package. The only feed request there is fetches one whole signed snapshot of static JSON, and it happens when you ask for it: emendant feed update, or a scan after you have said yes to automatic updates and the cache is more than 24 hours old. Emendant downloads the whole feed rather than the entries for your dependencies precisely so that a request cannot name a package you depend on. Everything it fetches is verified against a trust root inside the installed package, so an origin decides what bytes arrive and never whether they are accepted.
  • Emendant never receives or stores your source. Not one line of it, ever. scan is entirely local, and so are the transforms that write most patches: no network request at all.
  • A few changes need a model, and only if you say so. Some breaking changes have no mechanical remedy, and for those fix can ask a provider you name on the command line to write the patch. It sends the matched block plus about twenty lines of context and the file's imports, from your machine straight to that provider, and nothing else about the repository. Without --model-provider those findings are reported unfixed and nothing is sent anywhere. Emendant never picks a provider for you, never reads a key it was not pointed at, and never falls back from a subscription you have to a bill you did not agree to. We say it this precisely because "your source is never uploaded" stops being true the moment any hosted model sees an excerpt.
  • That provider can be your own. --model-provider azure posts to one Azure AI Foundry resource in your tenant, and --model-endpoint points openai or anthropic at a gateway you run, so a network that permits a fixed list of hosts can still use assisted fixes. The line printed before the first request names the host that receives the excerpt, not the flag it was spelled after.
  • Verification asks your package manager for a version, and nothing else. To prove a patch for an upgrade you have not made yet, the scratch copy has to be moved to the new release, so fix runs npm install (or pnpm, or yarn) in that copy. It reaches your registry for a package you were going to install anyway. It sends nothing about your repository anywhere. That step carries a short list of environment variables rather than your shell: package managers expand ${NAME} in a repository's own .npmrc, so a repository could otherwise name any variable you have and have its value sent, as a token, to a registry it chose. Registry credentials are passed, anything else is refused, and --allow-registry-credential <name> is how you say otherwise.
  • It never writes to your source files or your git history. scan opens files for reading and nothing else. fix writes diffs into .emendant/ and leaves applying them to you. It does not run git add or git commit, now or in the hosted phase, which opens pull requests rather than touching a working tree.
  • It never executes code from the repository it is scanning during a scan. Only fix runs anything, only in a copy outside your working tree, only your own typecheck and test commands, and only after saying what it selected and why. The copy leaves out .git, so nothing it runs can reach your history.
  • The postinstall script does nothing. People check. There is no postinstall.

Licence and support

Emendant is proprietary software, licensed and not sold. You may install and run it for any purpose, including a commercial one; you may not redistribute it or the change feed inside it. The full terms are in LICENSE, and the terms of use for the feed service are at https://emendant.com/terms/.

Include the output of emendant --version in a report.