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

a2done

v0.6.0

Published

Enforcement layer for AI coding agents: finish every item you asked for, stop generating slop, stop repeating corrected mistakes — proven by machine-checked evidence, not the agent's own word.

Readme

A2D — agent to done

No more skipped. No more slopped. No more forgotten rules. Exactly what you ask.

You asked for seven things. The agent did four, added two you never asked for, broke a rule you corrected last week, and announced "done". A2D makes that impossible to say.

It is not another way to run agents. It is an enforcement layer you put on top of the harness you already use — a checklist the agent cannot close on its own word, rules that survive its memory, and a completion verdict that is produced by running a proof, not by the model deciding it feels finished.

Why the agent's word is not evidence

An agent looking for the shortest path to "done" can edit the checker, rewrite the rule file, patch the hook config, git commit --no-verify, reinterpret a prose rule as "not applicable here", or simply mark its own item complete.

So the design rule is one sentence:

Completion must be a fact outside the agent, not its opinion of itself.

Concretely, in this package:

  • The checklist lives in a file the server writes, not the agent.
  • An item cannot be registered without its acceptance criterion. a2d_plan refuses a bare item and says what a good verifier for that item would look like.
  • An item can only be closed by re-running that same criterion. A verifier handed to a2d_complete is an additional proof, never a substitute. A fabricated proof is rejected and the item stays open.
  • A Stop hook refuses "I'm done" while anything is still open.
  • A per-item iteration limit force-skips an impossible item instead of looping forever, so the fuse never turns into a hang.

Remove any one and the enforcement leaks. The first two are a pair on purpose: verifying the proof achieves nothing if the agent chooses which proof after seeing its own work.

Sometimes the criterion really is wrong — the check was written against a file that has since moved. a2d_replan is the legal exit: it records the old criterion, the proposed one and your agent's reasoning into plan.json, and parks the item for you to decide. It does not close the item and does not install the new criterion. Changing the definition of done is allowed; changing it quietly is not.

Local enforcement protects you from a forgetful agent. It does not protect you from an agent that edits the rules — everything here is a file in its working directory. That is a real limit, and it is stated here rather than hidden.

And there is a second one, found by running it rather than by reading it: all of the above applies only to an agent that calls a2d_plan. With no plan there is no criterion to insist on and nothing for the Stop hook to hold open, so an agent that skips the tools finishes freely and says it is done. Three live runs on one six-item task produced two that did exactly that.

The package does not block that case — a quick edit legitimately needs no checklist, and a gate that fires on unrelated sessions gets uninstalled. What it does now is refuse to let it pass unnoticed: a turn that changed files and ended with no plan is recorded, and a2d status says so. Enforcement against an agent that declines to be enforced needs the registry to be the environment it runs in, which a local file cannot be.

Install

npm install -g a2done
cd your-repo
a2d init

a2d init writes project-local config only — it never touches your global ~/.claude/settings.json, and never a config outside the repo you ran it in. Wiring Claude Desktop or Codex is one explicit command each; see SETUP.md.

The first 60 seconds

$ a2d init
a2d init — repo: /Users/you/your-repo
  ✓ MCP · Claude Code: registered (claude mcp add).
  ✓ MCP · Cursor (project): added "a2d" to /Users/you/your-repo/.cursor/mcp.json
  ✓ hooks: /Users/you/your-repo/.claude/settings.json
      added: Stop, PostToolUse, UserPromptSubmit
  ✓ .a2d: /Users/you/your-repo/.a2d
  ✓ guardian: Installed git pre-commit guardian.

Also found on this machine, NOT wired (their config is global, outside this repo):
  – Claude Desktop — connect with: a2d connect claude-desktop
  – Codex — connect with: a2d connect codex

RESTART REQUIRED — if a session is already open in this repo, exit and start it again.
Hooks and MCP servers are read at startup; a live session will keep running unwired and
a2d will look broken.

Then: the agent has a2d_plan / a2d_complete / a2d_replan / a2d_status and cannot stop
while the checklist has open items. Run `a2d doctor` to verify wiring, `a2d uninstall` to remove it.
$ a2d doctor
a2d doctor — harness: Claude Code
  (running inside Claude Code (CLAUDECODE env); .claude/settings.json present)

  ✓ node — v22.14.0
  ✓ MCP · Claude Code — registered
  ✓ MCP · Cursor — registered in /Users/you/your-repo/.cursor/mcp.json
  ✗ MCP · Claude Desktop — installed but not registered — run `a2d connect claude-desktop`
  – MCP · Codex — not installed on this machine
  ✓ MCP server built — /usr/local/lib/node_modules/a2done/dist/src/mcp/server.js
  ✓ guardian built — /usr/local/lib/node_modules/a2done/dist/src/guardian/pre-commit.js
  ✓ Stop hook — wired
  ✓ PostToolUse hook — wired
  ✓ UserPromptSubmit hook — wired
  ✓ .a2d state dir — /Users/you/your-repo/.a2d
  ✗ plan.json — none yet (created by a2d_plan)
  ✓ git pre-commit guardian — installed

On this machine (Claude Code), the enforcement that is LIVE is: MCP tools (plan-time
verifier gate + server-run completion); git pre-commit guardian; Claude Code hooks
(Stop / PostToolUse / UserPromptSubmit).

2 item(s) need attention (✗ above); 1 does not apply here (–).

Both are correct: there is no plan until the agent makes one, and Claude Desktop is installed but not connected — with the command to fix it right there. a2d doctor reports what is true, not what is comforting.

Run the same command under Codex and the three hook lines change, because Codex does not read the Claude Code hook protocol at all:

  – Stop hook — n/a — Claude Code only. Codex does not read the hook protocol
                      (the entry is in settings.json, and is inert here)

On this machine (Codex), the enforcement that is LIVE is: MCP tools (plan-time verifier
gate + server-run completion); git pre-commit guardian.
NOT live here: Claude Code hooks — Codex cannot run them at all.

An earlier version printed those three as . A green tick where there is no protection is worse than a red one: it stops you looking.

$ a2d status
No plan. Call a2d_plan to create a checklist.

Canon: no rules yet.

Now give your agent a task with more than one part. It calls a2d_plan to register the items, and cannot stop until each one is closed with a proof that passes.

Changed your mind? a2d uninstall is the exact inverse of a2d init — it removes only the entries it recognises as its own, leaves a foreign pre-commit hook untouched, and keeps your plan and rules unless you pass --purge.

Which harness

A2D deliberately has no exclusive relationship with any harness.

Full setup for every client — including doing it by hand, and how to check it worked — is in SETUP.md. The short version:

a2d init wires the clients whose config lives inside this repo:

  • Claude Code — via claude mcp add
  • Cursor.cursor/mcp.json in the project

Clients that only have a machine-wide config are one explicit command each, because init does not edit files outside the repo you ran it in:

a2d connect claude-desktop    # ~/Library/Application Support/Claude/claude_desktop_config.json
a2d connect codex             # ~/.codex/config.toml
a2d connect cursor            # ~/.cursor/mcp.json, if you want it for every project

Each makes a .a2d-backup copy before its first edit, refuses to touch a config it cannot parse, and has an exact inverse in a2d disconnect <client>.

What is actually enforced, per client

This is the part worth reading before you decide where to run your agent.

| | Claude Code | Cursor | Claude Desktop | Codex | |---|:---:|:---:|:---:|:---:| | item cannot be registered without a criterion | ✓ | ✓ | ✓ | ✓ | | item can only be closed by re-running that criterion | ✓ | ✓ | ✓ | ✓ | | a2d_replan leaves a trail and asks you | ✓ | ✓ | ✓ | ✓ | | Stop hook — cannot stop while items are open | ✓ | — | — | — | | PostToolUse — canon checked after every edit | ✓ | — | — | — | | UserPromptSubmit — canon injected each turn | ✓ | — | — | — | | git guardian — commit blocked on a hard rule | ✓ | ✓ | ✓ | ✓ |

The dashes are not missing features, they are a protocol boundary: Stop / PostToolUse / UserPromptSubmit are the Claude Code hook protocol, and no other client implements it.

So outside Claude Code you get the gate that matters most — the agent still cannot invent its own definition of done — but nothing stops it walking away with items open. a2d status and the guardian are what catch that.

a2d doctor prints exactly this for the machine you are on, per client, and never shows a tick for something that cannot run there.

Agent frameworks and workflow harnesses are all the same shape from here. If yours runs git and speaks MCP, the enforcement applies. Wiring adapters for further harnesses is free and always will be — that is distribution, not product.

Proof types

Every checklist item carries one of these from the moment it is registered, and the server runs it to close the item:

| type | passes when … | |---|---| | file_exists | the path exists (and, if given, contains a substring) | | command_exit | a command exits with the expected code | | grep_count | a regex matches a file/glob an exact number of times | | test_pass | a command exits 0 | | human | never machine-passes → the item becomes needs_human, not done |

Commands run anchored to the repo root, with a hard timeout. Details in SPEC.md.

human matters: an item that genuinely needs your eyes is not quietly auto-approved and not silently dropped. It is parked in a state that is neither. And because every item now needs a criterion, human is the honest answer for the ones no machine can settle — it is accepted, and it can never come back as done.

The criterion that could never have failed

There is one thing the gate insists on and cannot judge. It can make your agent state an acceptance criterion before the work. It cannot tell whether that criterion means anything:

command_exit  `true` exits 0

Well-formed. Runs. Passes. Proves nothing — and closing an item on it is the agent grading itself, one level up.

So a2d_plan says so, out loud, in the response the agent reads:

🔴 CRITERIA THAT MAY NOT BE ABLE TO FAIL — 1 item(s).
The plan was created; nothing is blocked.

  #2 "make the build pass"
      🔴 `true` exits 0 on any machine, in any state, whether or not the work
         happened. This criterion cannot fail, so closing the item proves nothing.
         Name a command that is red before the work and green after it.

And you see the same thing:

a2d review
○ #2  make the build pass
    criterion: `true` exits 0
    🔴 the command cannot fail

○ #4  no emoji anywhere in the page
    criterion: `node -e "…/\p{Extended_Pictographic}/u…"` exits 0

One question per line: could this criterion have failed if the work were wrong?

It warns; it does not refuse. "Is there a criterion" is a fact, so the plan gate can refuse on it. "Is this criterion meaningful" is a judgement — and a refusal that is wrong one time in five teaches agents to write around the checker, which is worse than no checker at all. file_exists without contains is sloppy for "update the changelog" and exactly right for "create the file". The decision is yours; that is the whole argument of the product.

When the criterion itself is wrong

Your plan says grep_count "2026" in page.html. Then the footer moves into a component. The work is right and the check fails.

If there is no way out of that, the thing that gets deleted is the enforcement. So there is a way out, and it goes through you:

a2d_replan { item_id: "3",
             new_verifier: { type: "grep_count", pattern: "<Year", path: "page.html", expect: 1 },
             reason: "the year in the footer: hard-coded in the markup, or a variable/component?
                      the acceptance criterion depends on which" }

The item becomes needs_human with that question attached. The new criterion is recorded, not applied — the registered one stays in force. Both, plus the reason, are written into .a2d/plan.json, which is committed, so the change shows up in git diff rather than in nobody's memory. One replan per item; a second is refused.

a2d status puts these in their own block, because they are the ones waiting on you:

⚠ 1 item(s) NEED A DECISION FROM YOU — they are not done and will not become done on their own:
  ☝ #3 footer shows the year
      Q: the year in the footer: hard-coded in the markup, or a variable/component? …
      criterion registered: grep_count /2026/ in page.html == 1
      criterion proposed:   grep_count /<Year/ in page.html == 1  ← NOT applied; your call

needs_human does not block a stop, so the agent finishes the rest of the plan instead of grinding against an impossible item.

Rules that outlive the conversation

When you correct the agent, record the correction once. It becomes canon and applies from then on, in every session:

a2d_rule_record { text: "No emoji in headings", scope: "*.html", class: "hard",
                 verifier: { type: "grep_count", pattern: "🚀|✨", path: "*.html", expect: 0 } }

Rules come in two classes, and the difference is whether a machine can decide them:

Hard — carries a verifier, so it is decidable.

  • Injected into context at the start of each turn.
  • Checked after every Edit/Write on a file in scope; a violation is reported back to the agent immediately.
  • Checked again by the git guardian on staged files: a violation exits 1 and the commit does not happen. Bypass is git commit --no-verify — this is your machine, not a jail.

Soft — a description with no checker, for taste that no regex can settle.

  • Injected into context the same way, so it is present rather than remembered.
  • Not enforced, and never pretends to be. It carries a hits counter instead, so a soft rule that keeps getting violated is visible as a candidate for a real checker.

Rules live in .a2d/canon/*.yaml, one per file, human-readable, meant to be committed and reviewed like code.

What's on disk

.a2d/plan.json           the checklist — human-readable, commit it
.a2d/canon/*.yaml        your rules — human-readable, commit them
.a2d/state-runtime.json  per-item iteration counters — gitignored

Plain files, plain git. No telemetry, no account, no phone-home. The only processes this package spawns are git, the claude CLI during init/uninstall/doctor, and the commands you put in your own verifiers.

Your code does not leave your machine

That is the promise, and it is the one to hold us to. It has two halves, because they are guaranteed in different ways.

Your files are never uploaded. Not the repo, not a snippet, not a diff. There is no code in this package that reads a file and sends it anywhere — because there is no code in this package that sends anything, full stop. Verifiers run where you are.

There is no network in the enforcement path. The Stop hook, PostToolUse, UserPromptSubmit, the git guardian and the MCP server contain no network code and cannot reach any — a verdict must never have a remote failure mode. That one is not a promise at all, it is a test: test/no-network.test.ts walks the real import graph of each of those entry points on every run and fails the build if any of them can reach fetch, node:http(s) or a URL literal.

Exactly one place in this package talks to the network — the once-a-day version check below, which sends nothing.

If a hosted mode is ever added, this promise is the constraint it has to satisfy, and it is written down now rather than after the fact. A server-side canon would exist so that an agent cannot weaken a rule it can no longer see — and even then your code would not leave your machine: verifiers run locally, and only the checklist, a hash of the criterion and whether it passed would cross the wire. Command output stays in the local registry unless you explicitly opt in. If you ever see this package send more than that, it has broken its word.

The one request this package makes

a2d init, a2d doctor and a2d status check once a day whether a newer version exists. Three commands you type. Nothing that runs automatically.

What goes over the wire:

GET https://raw.githubusercontent.com/0leg-design/a2done/main/version.json

That is the whole request. No query string, no custom headers, no body, no cookies, no redirects, no identifier of any kind — there is no code here that generates one. What the server can learn is that somebody fetched a public file, which is what any crawler produces.

What comes back is a static file: { latest, notes_url, message }. If latest is newer than what you have, you see something like:

a2d: 0.4.0 is out (you have 0.3.0).
  a2d_replan lands: change a criterion without hiding it
  notes: https://github.com/0leg-design/a2done/blob/main/CHANGELOG.md
  update: npm install -g a2done   ·   silence this check: A2D_NO_UPDATE_CHECK=1

Otherwise it says nothing at all. Timeout is 2 seconds and the answer is cached for 24 hours in .a2d/state-runtime.json, which is already gitignored. Every failure — offline, DNS, proxy, 404, malformed JSON — is silent: the CLI behaves exactly as if the channel did not exist.

To turn it off entirely:

export A2D_NO_UPDATE_CHECK=1

With that set, no request is made at all — the check returns before it reaches the network.

Reusable checklists

A loop manifest is a checklist with its verifiers already attached:

a2d loop loops/sdd-page.yaml

Where this stops working

Stated plainly, because you will find these out anyway:

  • Local enforcement does not survive an agent that edits the rules. The plan, the canon, the hook config and the checkers are all files in the agent's working directory. It can rewrite any of them. What is enforced here is that it cannot do so by accident or quietly — not that it cannot do so at all.
  • A gate only catches what somebody managed to state. A defect nobody named is a defect nothing checks. human verifiers and soft rules are where that honesty lives.
  • On a short, well-specified task with a strong model, this adds very little. The value is in long checklists, context that outgrows the window, taste that repeats, and work spread over several sessions.
  • There is no attaching to a running session. Hooks and MCP servers are read at startup, which is why a2d init tells you to restart.

What is free, and stays free

Everything in this package, under Apache-2.0, with no account and nothing to sign in to:

  • the Stop hook and the anti-stop logic
  • the plan-time verifier gate and evidence-gated completion
  • a2d_replan — the visible way to change a criterion
  • all five verifiers, run server-side
  • the local canon — no limit on the number of rules
  • the git guardian
  • a2d doctor and a2d uninstall
  • adapters into other harnesses
  • an open, git-able file format — your plan and your rules are yours, in plain text

This is not a trial and not a crippled tier. The line elsewhere is not "more rules"; it is about rules the agent cannot reach to weaken. This package is the half that is useful on its own.

Development

npm install
npm run typecheck
npm test                   # unit tests
npm run test:integration   # end-to-end on a clean repo: real MCP server, hook, guardian
npm run test:scenario      # the enforcement transcript, printed verbatim

test:scenario is the one to read if you want to know whether any of the above is true. It drives the compiled MCP server over stdio through the whole argument — a plan with no verifiers, a verifier brought to the closing, real work, a replan, a second replan — and prints every request and response.

Status

v0.3.0. The gate moved to plan time and completion stopped accepting substitutes; doctor stopped reporting hooks as live on harnesses that cannot run them. See CHANGELOG.md.

License

Apache-2.0. See LICENSE and NOTICE.

Copyright 2026 Oleg Kukharuk — oleg.design