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

@alizain/doublecheck

v2.4.1

Published

Run self-authored LLM code-inspectors against a project — one sandboxed microVM agent per check, one markdown report each

Readme

doublecheck

Runs self-authored LLM code-inspectors ("checks") against a project — one sandboxed agent per check, in parallel — and writes one markdown report per check.

A check is a markdown file: a timeless standard — no frontmatter, no schema; the body is the inspector's instructions. Checks come from one or more --checks-dir directories (default: $TARGET/.agents/checks), so a shared check set can serve a whole folder of repos. Everything run-specific — the intent of the work under review, known nuances, sanctioned exceptions, scope ("these changed files vs main") — arrives per run via --context; the harness computes no diffs and knows nothing about git. A report is a markdown file: whatever the agent writes, no imposed structure — and the operator's agent reads every line of it, which the prompt tells the inspector.

Why this exists

Linters and type checkers catch what can be pattern-matched. The defects that survive them are judgment calls: a silent fallback that swallows a config error, an abstraction at the wrong layer, a "for now" that will outlive its author. doublecheck encodes your judgment about those — each check is a standard you actually hold, written as prose instructions to an inspector agent that can read the whole tree and reason about it.

The division of labor is deliberate: everything that requires judgment lives in the check body (what to flag, what to leave alone, where to look); everything mechanical lives in the harness (sandboxing, parallelism, report collection). The harness knows nothing about git, diffs, or languages, so it never needs to change when your standards do.

The loop

  1. doublecheck mine distills your Claude Code history into durable preference observations (~/.doublecheck/catalog) — evidence of standards you have actually enforced in past conversations.
  2. A human and/or agent authors checks from those observations. This step is deliberately unproductized: going from "observed preference" to "runnable standard" is judgment work.
  3. doublecheck check runs every check against a project, one sandboxed agent per check, and writes one report per check. Read the reports, fix what is real, tighten any check that cried wolf.

How it works

Per check: a scratch workdir gets prompt.txt (environment preamble + operator run context, when given + check body + report contract). A microsandbox microVM boots from a locally built image with the project bind-mounted read-only at its real host path and the scratch dir mounted rw as the guest cwd. The selected agent CLI (--agent: headless claude -p, or codex exec) runs inside with its full toolkit and no permission gates — the microVM is the safety boundary, the ro mount is the "don't touch my repo" guarantee. The agent writes report.md; the host copies it to $OUTPUT/<run-timestamp>/<check>.md (with --save-jsonl, alongside <check>.stream.jsonl — the inspector's raw stream, kept so a run can be audited after the guest is gone).

The project is never copied — every guest shares the live host directory (dirty files included) through the ro mount, so per-check cost is one temp dir and ~2 GiB of guest memory.

Exit 0 only when every check produced a report. An agent failure (exit ≠ 0, no report) writes a failure-record report and flips the run's exit code; missing token / checks / image abort loudly up front.

Setup

npm install -g @alizain/doublecheck   # installs the `doublecheck` command; or, from a clone: pnpm install

Guests boot from an image with both agent CLIs baked in, and no install needs an image step. An installed release resolves ghcr.io/alizain/doublecheck-guest:<its own version>; a clone resolves the nearest release tag reachable from its checkout (git describe), so git pull bringing a new tag is also the image update. Either way the runner pulls the image on first use (~1 GB, once per version — the release workflow publishes it for amd64+arm64 alongside the npm package).

The locally built image is only for iterating on Dockerfile.guest itself (needs a running Docker daemon), opted into explicitly:

./scripts/build-guest-image.sh
DOUBLECHECK_GUEST_IMAGE=doublecheck-guest:latest pnpm doublecheck check …

DOUBLECHECK_GUEST_IMAGE=<ref> works for any install — the named ref must already be in the microsandbox cache (it is never pulled); useful offline or for custom guest images. A clone with no reachable release tag (e.g. a shallow clone) falls back to the locally built image.

Usage

CLAUDE_CODE_OAUTH_TOKEN=... doublecheck check \
  --target DIR       # tree under inspection, mounted read-only; default: cwd
  --checks-dir DIR   # repeatable; default: $TARGET/.agents/checks
  --context FILE     # run brief: intent, nuances, sanctioned exceptions, scope
  --agent NAME       # claude (default) or codex
  --model MODEL      # default per agent: claude haiku, codex gpt-5.6-sol
  --parallel N       # default: 4 concurrent guests
  --output DIR       # default: $TARGET/.doublecheck
  --check NAME       # repeatable; default: every check in the checks dirs
  --save-jsonl       # persist each inspector's raw stream beside its report

Credentials per agent, gathered on the host before any guest boots:

  • claude: CLAUDE_CODE_OAUTH_TOKEN is required in the environment and injected into each guest; where it comes from is the operator's business.
  • codex: no env var — each guest gets a fresh copy of the host's ~/.codex/auth.json (run codex login once). No staleness guard (removed 2026-07-13): accepted risk that a guest-side refresh of near-expiry ChatGPT tokens rotates the single-use token family out from under the host (codex self-refreshes at ~8 days; a mid-run 401 could do the same regardless). Recovery is codex login on the host. Guests never write auth state back.

Mining your Claude history into an observation catalog

doublecheck mine walks every Claude Code transcript on the machine and runs one sandboxed agent per real conversation (≥ --min-turns genuine human turns) to extract durable engineering preferences into ~/.doublecheck/catalog, mirroring the transcript tree — one <project>/<session>/observations.md per conversation, frontmatter recording the source hash so re-runs only mine new or grown sessions. Mining guests get egress to the selected agent's own API domains only (claude: *.anthropic.com; codex: *.chatgpt.com + *.openai.com) — the one reachable destination is the service the agent already sends its context to. Note what that means for --agent codex: your Claude Code transcript content flows to OpenAI. Design: docs/2026-07-05-mine-design.md.

CLAUDE_CODE_OAUTH_TOKEN=... doublecheck mine \
  --projects DIR     # default: ~/.claude/projects
  --catalog DIR      # default: ~/.doublecheck/catalog
  --agent NAME       # claude (default) or codex
  --model MODEL      # default per agent: claude opus, codex gpt-5.6-sol
                     # (a bad-model mine pollutes a durable asset)
  --parallel N       # default: 4
  --min-turns N      # default: 2
  --limit N          # mine at most N pending conversations
  --dry-run          # list what would be mined, boot nothing

fixtures/planted/ is a tiny target with two planted silent fallbacks (and two legitimate defaults that must not be flagged) for exercising the tool end-to-end:

CLAUDE_CODE_OAUTH_TOKEN=... doublecheck check --target fixtures/planted --model haiku
doublecheck check --target fixtures/planted --agent codex  # auth from ~/.codex/auth.json

What the inspector sees

The facts of the agent's world, so you can decide how to author checks:

  • The agent is one headless CLI process per check, running inside its own microVM with all approval/permission gates bypassed — the VM is the boundary. Check agents have unrestricted internet egress.
    • --agent claude (default): claude -p (model from --model, default haiku) with the pinned toolkit Task (it can spawn subagents), Bash, Read, Write, Edit, Glob, Grep, WebSearch, WebFetch.
    • --agent codex: codex exec (default model gpt-5.6-sol, reasoning effort pinned to xhigh in the staged guest config) with codex's own toolset: shell, apply_patch, plan/todo, subagent spawning (multi_agent), and server-side web search. The staged config disables codex's ambient inputs — no AGENTS.md pickup, no plugin/marketplace fetch — so the piped prompt is its only input, same as claude.
  • Its only input is the prompt: a short environment preamble (verbatim in src/contract.ts) + the operator's run context, when --context was given
    • the check body + the report contract. No session, no conversation history — the check body is the standard; the run context is everything about this particular run (intent, sanctioned exceptions, scope). The report contract also tells the inspector that every line of its report is read in full, findings must be verified, and "no findings" is a perfectly good report.
  • The filesystem: the project is bind-mounted read-only at its real host path — the live working tree, dirty files and .git included, so git log/git diff/git blame and rg work against the real repo; writes to it fail. The guest image also has node 24, curl, and wget. The cwd is a writable scratch dir private to the check; nothing in it survives except report.md.
  • The output: the prompt tells the agent its chat reply is discarded and only ./report.md is read back. The harness imposes no structure on the report — it contains whatever the check body asks for.
pnpm doublecheck # run the CLI from source (tsx)
pnpm test        # vitest — pure logic, plus real-guest integration tests with a FAKE agent (never real claude)
pnpm typecheck   # tsc --noEmit
pnpm check       # biome

Design records: docs/2026-07-05-doublecheck-design.md, docs/2026-07-05-run-context-and-decomposed-flags-design.md, docs/2026-07-06-codex-agent-design.md. For how a driving agent should use this tool — above all, what a good --context brief contains — see SKILL.md.

Releasing

Manual, via the release GitHub Actions workflow — semantic-release over Conventional Commits, ported from pggit. Nothing releases as a side effect of pushing to main.

gh workflow run release -f dry_run=true    # preview next version + notes, publish nothing
gh workflow run release -f dry_run=false   # ship: npm publish + GitHub Release + tag + guest image
gh workflow run release -f image_version=X.Y.Z  # no release: (re)build + push the guest image
                                                # for an existing version (recovery, or refreshing
                                                # the baked-in agent CLIs)

A real release also builds Dockerfile.guest for amd64+arm64 and pushes ghcr.io/alizain/doublecheck-guest:<version> (+ :latest) — the image installed CLIs pull at runtime. The ghcr package must be public for those unauthenticated pulls (one-time visibility flip in the package settings after the very first push). image_version mutates an existing tag in place; machines that already pulled it keep their cached copy (the runner pulls if-missing and never re-checks).

  • Only feat: / fix: / BREAKING CHANGE: commits trigger a release and decide the bump (minor / patch / major). Other commit styles ride along unreleased — use docs:/chore:/plain messages for work that shouldn't ship a version.
  • version in package.json stays 0.0.0-development forever — semantic-release derives the real version from git tags. Never hand-bump it.
  • The pre-publish gate is biome + tsc + unit tests only (test:unit excludes *.integration.test.ts, which boots real microsandbox guests CI doesn't have) + tsdown build. Run the full pnpm test locally before releasing.
  • Needs the NPM_TOKEN repo secret: a token that can publish @alizain/doublecheck without an OTP prompt — classic "Automation" type, or a granular token with read/write package access (and 2FA bypass if the account requires 2FA for writes).

Learned the hard way on the first release (2026-07-05):

  • A failed npm publish leaves a stale tag. semantic-release pushes vX.Y.Z before publishing to npm. If the publish step fails, delete the tag (git push origin :refs/tags/vX.Y.Z) before retrying — otherwise the retry sees the tag as the last release, finds no new conventional commits, and releases nothing.
  • The package is scoped because npm forbids unscoped doublecheck. The name-similarity rule (DoubleCheck and double-check exist) rejects it with a 403 at publish time only — registry lookups 404 as if the name were free. The installed bin is still doublecheck; publishConfig.access: public is what keeps a scoped package from defaulting to private.
  • npm provenance/OIDC is off on purpose — the presence of id-token: write makes npm 11.x prefer trusted publishing and 404 when none is configured (npm/cli#8976). Details in the comment block of .github/workflows/release.yml.