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

workagentwork

v0.1.0

Published

Merge governance for agent-written code: a disk-based task state machine with a recorded-approval merge gate.

Readme

Work, Agent, Work

Work, Agent, Work is a git-native task system for parallel coding agents: every task is a branch, every merge needs a recorded approval, and the audit trail lives in your repo.

Agents write code faster than humans can review it. In an EASE 2026 study of 33,596 agent-authored pull requests, 61% received no human review at all (arXiv:2605.02273). A rubber-stamp approval answers no question when something breaks, and nobody can say who let a change land.

Quickstart: the whole loop in 60 seconds

Prerequisites: git, Node >= 20.6 (full support matrix), a repo you own that already has commits.

npx workagentwork init          # inside your repo — sets up work/
npx workagentwork new fix-the-thing
# edit work/proposed/0001-fix-the-thing.md: goal, owns-files, gates
npx workagentwork ready 0001    # machine checks pass it to the queue
npx workagentwork claim 0001 --session me
# you get a worktree: ../<repo>-work/0001 — work there, commit there
npx workagentwork submit 0001   # gates run
npx workagentwork approve 0001 --session me   # there is no bypass
npx workagentwork merge 0001    # branch lands, ledger records it
npx workagentwork serve         # open the board — or just open
                                # work/board.html from disk

npx workagentwork stats reads back cycle times, decision latency, and when the board clears. docs/getting-started.md walks the same cycle with explanations.

What it is

  • A merge gate. merge refuses to run without a recorded approval — there is no bypass flag — and runs the gates again on main after the merge commit, resetting the merge away if they fail.
  • A task state machine. Tasks are markdown files in work/<state>/ directories (proposed, ready, in-progress, review, blocked, done). State lives in the path, not a status field; any session can pick up a task from its file alone.
  • Worktree isolation. Claiming a task creates a branch work/NNNN-slug and a worktree at ../<repo>-work/<id>. One task, one branch, one worktree — no shared checkout, no watchers trampling each other.
  • Disjoint file ownership. Two tasks may not own the same file without a recorded dependency edge; ready and claim refuse the overlap.
  • Dependency-aware merge refusal. A task whose declared dependencies are not done cannot merge, no matter how green its own gates are.
  • Recorded approvals, on your disk. Every approval, state move, and gate verdict lands in committed files: the task's Approved: field, the notes sidecars, and the work/events.jsonl ledger. git log is the audit trail.
  • A derived board. work/board.html (and board.json) regenerate on every state change; work serve serves the live board over http.

What it is not

  • Not an AI code reviewer. The machine never grants the approval; a human does, on the record. Gates you configure (typecheck, tests) decide what "verified" means; the gate only refuses to move without the recorded decision.
  • Not a hosted dashboard. It is a CLI, not a service. State is plain files in your repo, inspectable outside any agent harness. The record stays in the customer's git — there is no telemetry and no sync target.
  • Not a merge-conflict resolver. Conflicts are prevented by disjoint file ownership and one-worktree-per-task, not resolved after the fact.

Work, Agent, Work began as the work system of a production repository and was extracted to stand alone. This repo is still governed by its own gate — its board is the reference deployment.

Free, and staying free (open core)

The CLI is Apache-2.0 and stays free. A future paid Team tier adds hosted identity, policy, and review-dossier sync on top; nothing that exists today moves behind a paywall. The boundary and the evidence gates behind the paid tier are public: docs/roadmap.md.

Documentation

Install

No install step is required: npx workagentwork <command> fetches and runs the CLI on demand (Node >= 20.6). To install once and use it everywhere:

npm install -g workagentwork   # then: work <command>

Claude Code plugin

Work, Agent, Work ships as a Claude Code plugin: .claude-plugin/plugin.json is the manifest, and .claude-plugin/commands/work.md registers a /work command whose body is a thin pointer — it runs work --help and follows the printed usage, so the CLI stays the single source of truth. Add this repository as a plugin marketplace and install workagentwork from it; the exact commands follow the Claude Code plugin docs.

Commands

| Command | Effect | |---|---| | work init | bootstrap work/ in this repository and write the agent-instruction block to AGENTS.md/CLAUDE.md; refuses an existing work/ tree | | work new <slug> [--title T] [--parent P] | scaffold a task file into work/proposed/ (built-in) | | work ready <id> [--allow-overlap] | proposed\|blocked -> ready (human gate; refuses Owns-file overlaps) | | work unblock <id> | alias for ready | | work block <id> [reason…] | active state -> blocked, reason appended (built-in) | | work claim <id> [--session S] [--purpose P] [--force] | ready -> in-progress; writes claim block, creates branch + worktree, runs the configured install; refuses unmet deps; held under work/.claim.lock; --purpose updates the session's registry sidecar | | work unclaim <id> [reason…] | in-progress -> ready; release a stalled claim (branch kept; clean worktree removed, dirty kept) | | work note <id> <text…> | append a timestamped progress line to the task's notes sidecar (work/notes/<id>.md), no state change | | work tag add <id> <text…> / tag rm <id> <text> | add/remove a task tag (exact match, case-sensitive, no commas); no state change; editable states: proposed, ready, blocked | | work priority <id> <n> | set task priority (integer 0–4, 0 most urgent); no state change; same editable states as tag | | work flag <id> <text…> / flag rm <id> <n> | record/clear an open decision on a task in the sidecar work/flags/<id>.md; rendered as ⚑ chips on the board; works on review tasks (task file untouched); refuses done | | work submit <id> [--no-wait] [--wait S] | in-progress -> review; enqueues a durable record under work/queue/ and a detached executor (work gates-run, spawned automatically) runs the configured gates in the worktree — on drift it first replays the branch onto main (conflicts abort cleanly; gates re-run there). Waits within a bound (default 600s) unless --no-wait; a failed run records the failing gate and output tail, and the task stays in-progress. An orphaned executor's runs are reaped and re-enqueued automatically; a wedged one is reported, never killed (built-in) | | work approve <id> [--session S] | record reviewer approval in the notes sidecar of a review task; required before merge | | work session <name> [--purpose "line"] | register a session's purpose in work/sessions/<name>.md (create or update); without --purpose prints the record; no state change | | work sessions | list registered and ledger-seen sessions: name, derived last-seen age, purpose line; read-only | | work status | one screen of submit-queue state: queue depth, current run with duration, orphaned and wedged entries with ages, recent failures with failing gates; read-only | | work adopt <id> --session S | take over a claimed task: records the handoff (claim-field rewrite + ledger event) and prints the full brief — task file, state, claim history, latest queue record with failing gate and output tail, suggested next step | | work merge <id> [--dry-run] [--takeover\|--abort] | review -> done; requires approval; refuses unmet deps and an unclean main tree; merges branch, verifies the gates on main, removes worktree, deletes branch. The merge lease spans a conflicted merge's resolution window; --takeover adopts a foreign open merge and --abort backs one out (both recorded) | | work rebase <id> | replay an in-progress/review task branch onto main in its worktree, then re-run the gates there; conflicts abort cleanly; records a sidecar note; no auto-merge; held under work/.rebase.lock | | work list [--json] [--state a,b] | one line per task; --json emits a machine-readable array, --state filters | | work board | regenerate board files | | work serve [port] | serve work/ over http for the live board (default port 8080); regenerates the board on start |

Commands are discovered modules: a file exporting command with name, usage, run in the CLI's commands/ directory registers itself — no entry-file edit. A module colliding with a built-in dies at startup.

Trains: staged branches for units of work

work stage <parent-id> opens a cohesion stage: branch stage/<parent> plus a staging worktree. Children of the parent then merge into the stage (routing is derived — no flags, no config), each with its own recorded approval; when the subgraph is done, the parent's own submit/approve/merge promotes the stage to main with one operator decision for the unit. Trains are recorded in work/trains/ and shown by work list. A repo that never stages behaves exactly as before.

Tiered gates: local gates run per ticket and may be trimmed; the ci tier in work.json is the full, unselected suite, run by CI on the pushed train branch. The verdict comes back as a branch in your repo (verdict/stage-0003, tip subject verdict: green <sha>) — the CLI reads it with a plain fetch, no token, no gh. A green verdict moves the train's lastGreen.

Red trains restore first and attribute second: a red verdict rolls the train branch back to its last-green ref (the recorded --force-with-lease exception — main is never force-pushed) and the record lands as unattributed. A red cohesion train reopens its parent; a red economy train unbundles and retries its members individually. Closing the investigation is an agent's recorded act: work stage <train> --attribute <single-ticket|interaction|flake|infra>. Nothing blocks on attribution, and queued work proceeds.

Economy trains (work bundle <id> <id>...) batch independent, finished tickets so one CI run amortizes across them. Membership is imperative; the premise is independence, and the red policy unbundles when it fails. Each ticket keeps its own recorded approval (work approve takes several ids; each records its own verdict) and lands on main individually. Bundles are pointless on a single machine — they exist to save CI runs, so use them only when several approved tickets are waiting on the same CI.

Audit: the trust-on-pull ritual

work audit replays the event ledger against the work tree and reports drift: a task file with no event chain, an approval whose ticket never merged, a done file touched after its done event, a state event with no tree change behind it. Each finding carries a file pointer and an event pointer. Audit refuses nothing and repairs nothing — findings exit nonzero, a clean repository exits zero, and the tree is untouched. After a git fetch from a machine you do not fully trust, run work audit before you believe its work/; solo, it is the one-command integrity check.

Sync contract (multi-machine)

Etiquette in one place: pull before you decide, push after you change (the CLI does both when a remote exists); sweep orphaned trains and failed submits as part of normal work; UAT staged features before approving promotion; choose stage/bundle/direct-to-main by cohesion and cost. Liveness is per machine — a pid proves nothing across machines, so cross-machine orphan classification is age-based and takeover goes through the recorded paths only (lock break --force, merge --takeover, work adopt). The same rules ship in the init-written instructions and the public docs, word for word.

The CLI never runs git pull blindly and never force-pushes; the remote is the cross-machine serialization point:

  • Before deciding, every state-changing command fetches and fast-forwards the current branch (rebasing local commits when they diverge), so decisions are made against the remote's truth. An unreachable remote warns and the command continues local-only — being offline never stops solo work.
  • After committing, the same commands push. A rejected push means another machine moved the branch: fetch, pull-rebase, revalidate the decision precondition against the post-pull state, retry once.
  • A second loss reports the race by name — 0007 was claimed by session agent-2 on another machine — records a race event in the ledger (work/events.jsonl), and exits nonzero. The local commit stays; it lands with the next successful push.
  • The loser never faces a merge conflict: a conflicted pull-rebase aborts and reports instead of leaving one open.
  • Local locks (work/.*.lock) keep their same-machine duty; the push race takes the cross-machine duty. Two layers, one pattern.
  • A repository without a remote behaves exactly as before: no fetch, no push, no change.

Configuration

Gates and install (per repository)

Nothing about your build tooling is hardcoded. The gate commands that submit (in the worktree) and merge (on main) run, and the install command claim runs in a fresh worktree, are configured in work.json at the repo root — as argv arrays, so any runner works:

{
  "install": ["pnpm", "install", "--prefer-offline"],
  "gates": [
    ["pnpm", "run", "typecheck"],
    ["pnpm", "test"]
  ]
}

work.json is the only config file: the CLI never reads configuration from package.json. A repo without work.json simply runs no install and no gates.

  • Gates run in order; the first failing one is named in the refusal, in the task's notes sidecar, and in the merge-recovery messages.
  • A repo without work.json skips the install and gate steps gracefully (a notice is printed, nothing fails); a repo with neither work.json nor package.json gets the foreign-repo warning instead. Non-JS repos — Python, Go, Java, anything — can point the argv at make, cargo, just, a shell script, anything.
  • work merge --dry-run previews every pre-flight check, the commit range, diffstat, and the Result/notes tails without mutating anything — the review dossier before approving.
  • Gate evidence: every gate run appends a line to the task's notes sidecar — gate name, duration, exit status — so a gate verdict can be audited after the fact.
  • Gate environment: gate processes run with CI=1 and WORK_GATE=1 in their environment; suites that switch behavior under CI (log levels, watch mode) see the same environment in submit and merge.
  • No retry path: one bounded run per invocation; a red gate stays red. Fix the branch and re-run the command — that is a new, recorded run, not a retry of a failed one.

Gate concurrency (per repository)

Concurrent submit/merge runs in several sessions can oversubscribe the machine and make gates fail under load — a green gate should mean green regardless of who else is running suites. Set gateConcurrency to bound simultaneous gate runs across all sessions:

{
  "gateConcurrency": 2,
  "gates": [["npm", "test"]]
}
  • Work, Agent, Work budgets concurrency across sessions: runs beyond the limit queue on a semaphore under work/.gate-slots/ (never committed), printing queued behind N run(s). The wait is bounded; when no slot frees in time, the run refuses with no gate ran — it does not run the gate anyway.
  • The repo's gate argv budgets resources within one run (for example the test runner's worker cap). The two budgets are independent on purpose.
  • A crashed holder's slot (dead pid) is recovered automatically, like a stale lock. The key is absent by default: unlimited, and no semaphore directory is created. gateConcurrency must be a positive integer; anything else is ignored with a warning.

Conventions kept from the extracted system

  • Worktree root: ../<repo-basename>-work/<id> relative to the repo root; branch prefix: work/. Fixed, not configurable.
  • Session identity: commands that attribute actions (claim, approve) take --session S, otherwise WORK_SESSION_ID, otherwise ZCODE_SESSION_ID, otherwise manual-<ts>. A session can state its purpose in a registry sidecar (work session <name> --purpose "…", read back with work sessions); last-seen derives from the events ledger, never from the sidecar.
  • Specialist field: a free-text role label in the task file format. Work, Agent, Work attaches no semantics to it; claim merely prints it.

Task file format

work/<state>/NNNN-slug.md, fields as bold bullets. The normative specification of this format — fields, states, transitions, invariants — is docs/format.md:

# 0007-add-export-button

- **Title:** Add export button
- **Parent:** docs/prd/0001-mvp.md
- **Depends on:** 0005, 0006      (bare 4-digit task ids only)
- **Owns files:** src/ui/export.ts, src/ui/export.test.ts
- **Branch:** —                    (filled by claim)
- **Claim:** —                     (filled by claim: session @ timestamp)
- **Approved:** —                  (on done copies only, assembled by merge)
- **Specialist:** —                (free-text role)
- **Tags:** —                      (comma-separated labels; — means none)
- **Priority:** 2                  (integer 0–4, 0 most urgent)

## Goal
Self-contained description: what and why, pointers to docs and code.

## Acceptance criteria
- [ ] Verifiable condition

## Result
Filled in by the claiming session; submit renames the file into review/ on
main unchanged, and merge folds this section plus the notes sidecar into the
done copy.

Notes and approvals never edit the task file while a branch holds a copy of it: note and approve write to work/notes/<id>.md, and merge assembles the done copy (branch body wins, notes land under ## Notes, the approval becomes the Approved: header field). This keeps task-file merges conflict-free by construction.

Read freshness

Every board generation stamps board.json with generatedAt and rev (the main HEAD sha); text list prints the same stamp as an as of <generatedAt> @ <rev> header. The contract: a board older than your last action is a rumor — re-read it before claim or merge, and treat a decision command's refusal as authoritative over any earlier snapshot. Decision commands do not trust the snapshot regardless: claim re-validates the task's state and dependencies under work/.claim.lock, immediately before the claim write.

Invariants

  • State moves happen only through the CLI, never by moving files by hand.
  • board.json / board.html are derived; every state change regenerates and auto-commits work/.
  • claim serializes on work/.claim.lock; merge on work/.merge.lock; rebase on work/.rebase.lock.
  • The claim race guard uses link() + unlink(), so two sessions cannot both claim one task.
  • Merge has no bypass for the approval gate.
  • Board edits over serve go through the same tag/priority commands as the terminal (one allowlisted code path); the file:// board snapshot is read-only.

Development

npm install
npm run typecheck
npm test
npm run build      # tsc -> dist/ (bin: dist/work.js)

License

Apache-2.0 — see LICENSE.