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

@dcwt/drift

v0.1.0

Published

Semantic lint with calibrated floors: prose conventions judged by a model, trusted in CI because every rule is measured against fixtures.

Downloads

266

Readme

drift

Semantic lint for the conventions AST linters can't see. You write a rule as prose — the same sentence that already lives in your CONTRIBUTING.md or AGENTS.md — and a model judges every applicable file against it, returning the probability the file violates it. What makes that trustworthy enough to gate CI is not the model; it's the harness around it:

  • Calibrated floors. A finding only becomes an error or warning past a per-rule probability floor, and floors are never guessed: drift eval measures each rule against its own fixtures (real compliant files, real or corrupted violating ones) and reports precision/recall across a sweep of candidate floors. A rule whose error floor can't hold 0.8 precision fails the eval.
  • De-flicker. Models sample; a borderline score moves run to run. A score within ±0.1 of a floor is re-asked once and the lower of the two samples kept, so a shaky error demotes and CI doesn't flap red/green day to day.
  • Honest failure. A file the judge couldn't score is a reported skip, never a crash and never an exit-code hit. A judge that answers fewer questions than asked is a loud BAD_RESPONSE, never a silently unscored rule.

Linting is cheap: the default judge is a System One model (calibrated probabilities, no prose) at roughly two hundredths of a cent per file.

A rule

One directory per rule:

drift/rules/repository-no-logic/
├── rule.md
└── fixtures/
    ├── compliant/
    │   └── plain-select.ts
    └── violating/
        ├── dto-mapping.ts
        └── domain-branching.ts

rule.md is frontmatter, context prose, and the question:

---
title: Repository contains business logic
docRef: conventions.md §8
include:
  - "**/repository.ts"
  - "**/repository/**"
exclude:
  - "**/*.test.ts"
floors:
  error: 0.9
  warn: 0.7
canonicalPath: "src/labels/{stem}/repository.ts"
---

A repository contains queries only. No business logic: no DTO mapping, no
unit conversion, no domain branching.

## Question

Does the repository in file "{path}" contain business logic — DTO mapping,
unit or date conversion, domain branching — beyond plain query construction?
Answer true only if logic that belongs a layer up is present.

The question is the whole prompt: floors calibrate exactly this text against exactly these fixtures, so editing the question means re-running drift eval and re-reading the sweep. canonicalPath is the path a fixture pretends to live at during eval — the path is part of the state the model judges, so a fixture should be seen at the layer it claims. Keep the fixtures directory outside your include globs.

A rule that needs a sibling file for context declares gathers (templates over the linted path, or { find, replace } regex rewrites); at most three related files ride along, 8 000 characters each. In fixtures, name a gathered sibling <stem>-related.<ext>.

Running

drift                       # lint files changed vs origin/main
drift src/ lib/thing.ts     # lint explicit paths
drift eval                  # measure every rule's floors against its fixtures
drift rules                 # list loaded rules

Rules load from drift/rules (or .drift/rules, DRIFT_RULES, --rules). The key comes from DRIFT_API_KEY / OPENROUTER_API_KEY / AI_DEFAULT_API_KEY. Exit codes: 1 when any error-severity finding (or, for eval, any rule below the precision bar), 2 on setup problems, 0 otherwise.

Judges

  • system-one (default): POST /v1/systemone through OpenRouter — a model that answers typed questions with calibrated probabilities and no prose.
  • chat (--judge chat --model <id>): any OpenAI-compatible endpoint, asked for a JSON object of probabilities. Noisier scores — which is why floors are per-judge: switching judges means re-running drift eval, not rewriting rules.

Both are behind one seam (Judge), so the library embeds anywhere:

import { createSystemOneJudge, loadRules, runLint } from "drift";

const summary = await runLint({
  judge: createSystemOneJudge({ apiKey }),
  rules: await loadRules("drift/rules"),
  files,
});