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

@tablation/crew

v0.78.0

Published

A standing team of headless agents that picks work off a Tablation board, does it on a machine you control, and reports back.

Readme

crew

A standing team of headless agents that picks work off a Tablation board, does it on a machine you control, and reports back on the board.

Documentation: https://crew.tablation.dev — start with Getting started. The source is in docs/.

Each seat is a row in the board's Crew table that happens to be a robot, with a brief (prompts/<promptSet>/, see "Prompt sets" below) and a slice of the queue. A seat is named in that table and answers to that name, in the queue digest and in the comments it writes on tickets. Everything the crew knows about a run — what to build, what is blocked, what has shipped — is table data, and every step it takes is visible as a status change or a comment on the ticket. There is no hidden state and no queue but the board.

Every agent is handed a roster at the top of its prompt: who else is aboard, what each of them is called, and which rows are holds — the people using this ship, and the interactive sessions they work through. A ticket assigned to a hold is off limits to every seat, whatever its status, which is how a person takes something over without racing the crew for it.

Today's crew has four polled seats, described below under the default prompt set (the three-lane dev/design/QA policy this repo ships with — see "Prompt sets" below for others, and for writing your own):

| Seat | Owns | Brief | | --- | --- | --- | | dev | approved tickets that don't need design work | prompts/default/personas/lane-dev.md | | design | approved tickets flagged Needs design | prompts/default/personas/lane-design.md | | qa | everything at fixed or qa, whoever built it | prompts/default/personas/lane-qa.md | | triage | tickets assigned to the triage seat | prompts/default/personas/lane-triage.md |

A fifth persona, pair, rides crew agents sync alongside these four but is not polled: it is the identity a live, interactive session (an IDE conversation, not a scheduled run) uses when it touches the tracker. It has no Crew-table seat and is never assigned a ticket by the poll loop. crew agents prompt pair prints its current persona text (a workspace admin's live edit if there is one, else the local default) for something like a SessionStart hook to feed into a session at start.

The building lanes' shared policy (worktrees, one stateless invocation per cycle, the dev/design/QA hand-off) ships as the Crew Lane Agent skill, and the dev, design and qa briefs include it with a {{skill: Crew Lane Agent}} marker on their first line. The Agents row keeps the marker; the include is expanded when a run is assembled, from the workspace's Agent Skills row if an admin edited it, else the local skills/*.md file. So an admin edit to a lane's own sentences no longer forks the shared policy. The marker is {{skill:, a skill name matched exactly after trimming, }}, on its own line or inline. It is deliberately not a [[table:key]] record link: personas and skills are not rows of a workspace table. An unknown name refuses the run; a skill that includes itself (or a cycle) is expanded once and the repeat left literal with a warning in the run summary; nesting is capped at 3 levels and 200 KB of included text. crew agents sync warns about a marker naming a skill the preset doesn't ship. triage and pair are self-contained (SELF_CONTAINED_ROLES in src/config.ts): neither cuts a worktree, builds or hands anything to QA, so the policy would only misdescribe them. A preset forked before this change may still ship personas/common.md, which keeps being prefixed to the building briefs. The prompts carry rules, not history; the incidents behind each rule are kept in docs/prompt-rationale.md.

Alongside personas, a prompt set can also ship skills — prompts/<promptSet>/skills/*.md, each one YAML frontmatter (name, description) plus a markdown prompt body. A skill isn't a seat: it's not polled and not concatenated into every run's prompt, just synced into the workspace's Agent Skills table so any session can fetch it by name on demand. crew agents sync [route] pushes both personas and skills in one run — "agents" here means every agent-shaped resource crew owns, not just the Agents table; crew skills sync [route] is the same create/update/ diverged sync narrowed to skills alone, for when you've only touched skills/. crew connect runs this same sync (then the seat key mint) at the end for an admin key, so a fresh workspace needs no second command; a member key links seats to the existing rows and is told an admin has to run it once. --no-agents-sync skips it, and --json adds agents_synced and seat_key events on stdout. Example use — a Pair session invoked via an Epic's grill_link looks up the Grill-Me skill through the Tablation MCP tool agent_skills_controller_get_skill and follows it. prompts/default/skills/ grill-me.md is one shipped example; the rest are the building lanes' situational procedures (Worktree Setup, Running the Suite, Question and Investigation Tickets, Filing a Ticket, Ticket Attachments, Workspace Change Authorization, Design Pass), which the lane policy names at the step that needs each one — every run keeps the rule and the minimum inline and fetches the full procedure only when it applies, so a dev run that never files a ticket never pays for the filing procedure. test/prompts.test.ts checks that every skill a persona names ships in the same preset.

One role runs per cycle, whichever holds the most urgent actionable ticket (src/priority.ts decides, and the same module sorts the digest that agent is handed, so the two can never disagree). QA outranks the building roles whenever it has anything to check. If the winning role turns out to have nothing it can actually do, the cycle falls through to the runner-up rather than idling.

Merging and deploying are not an agent's job. After the agent phase, a release phase takes whatever the remote has, squash-merges every QA-verified branch, bumps the version, writes the changelog and runs the repo's deploy hook. A headless session is never handed permission to push to production.

A branch that will not merge is handed back, not skipped. The release rewinds it, merges the base into the branch in its own worktree, and returns the ticket to the dev lane with the conflict left in place to look at — so the seat that resolves it is one with the ticket's context, and QA checks the resolution like any other change. A branch that was merely stale merges cleanly at that point and nobody is woken at all. Conflicting a second time stops at needs_info rather than looping.

Prompt sets

The seats above and their briefs are policy, not mechanism — a fact about how one particular workspace likes to work, not about what crew itself can do. prompts/ ships more than one of these as complete, ready-to-run policies:

| Directory | Policy | | --- | --- | | prompts/default/ | Three lanes — dev, design (gated by a Needs design flag), QA. | | prompts/dev-qa/ | Two lanes — dev builds everything, QA checks it. No design gate. |

A route picks one with promptSet: in crew.yaml (see crew.example.yaml) — a bare name resolves under this repo's own prompts/, defaulting to default when omitted. Different routes on the same ship can run different prompt sets, since which workflow a workspace wants is a fact about that workspace, not about the machine running it.

Writing your own is the expected path once a shipped preset doesn't fit: copy a preset directory (cp -r prompts/default ~/my-crew-policy), edit its prose, and point promptSet at the copy (~/my-crew-policy or a relative path) — no fork of this repo required. A prompt set is a complete, self-contained fork of one personas/lane-<role>.md per seat (and the shared-policy skill the building briefs include), plus whatever skills/*.md it wants, not a diff against another one; each persona file is read as plain prose ({{skill: Name}} includes expanded at run time) and concatenated after the per-run roster/environment sections and before the queue digest (src/agent.ts's assemblePrompt), so there's no templating layer to learn. Keep the prose generic: test/prompts.test.ts fails a preset that names a path, a command, an id, a ticket reference or a date. prompts/dev-qa/ is a worked example of trimming a lane out of default cleanly, including the unused lane-design.md stub every preset still needs today — see the next paragraph for why.

One current limit: the role set itself is fixed — src/config.ts's RoleName (dev, design, qa, triage, pair) — so even a prompt set with no real use for a polled seat (dev-qa's design) still needs a lane-<role>.md file for every one of them, pair included, or crew agents sync and planAgentRun throw looking for it. prompts/dev-qa/ lane-design.md handles this by being a brief that just says "do nothing, you shouldn't be staffed" — copy that pattern for any polled seat your own policy doesn't use (pair is never polled, so its own brief just needs to exist, not disclaim itself). Making the seat set itself policy-defined, so a prompt set could add or drop a role outright, is a larger change than prompt sets took and hasn't been done.

What a workspace has to provide

The crew reads its meaning from the board, not from its own source. Statuses, the priority order, and which column plays which role are per-workspace facts, because a ship can be connected to several boards that each made different choices. docs/CONTRACT.md documents the defaults and what a workspace has to override if it names things differently.

Install

git clone <this repo> crew
cd crew
cp crew.example.yaml ~/.config/crew/crew.yaml
chmod 600 ~/.config/crew/crew.yaml     # it holds an API key
$EDITOR ~/.config/crew/crew.yaml
bin/crew doctor                        # read-only preflight

The config lives in ~/.config/crew/ rather than in the checkout: it describes the machine, so reinstalling or replacing the checkout does not lose it. crew looks in $CREW_CONFIG, then $XDG_CONFIG_HOME/crew, then ~/.config/crew/crew.yaml, and only then beside the checkout.

crew connect resolves a workspace/project's ids into the state tree so you do not have to look them up by hand. doctor then checks the config against reality: the checkouts, the tools on PATH, the agent binary, the repo's hooks, and whether the board answers. Nothing writes, wakes an agent or deploys until a route has enabled: true.

Right after the project resolves, crew connect prints a capability checklist for the signed-in member (write the Crew and Ships tables, create Repos rows, mint agent keys, sync personas and skills): ok, or missing — ask a workspace admin for <capability>. With --json it is one capabilities event on stdout. Connect carries on for whatever is allowed; misses are stored in the route's resolved state and crew doctor repeats them until a re-connect finds the grant in place.

A secondary workspace is addressed as <primary>:<slug>: crew connect paradium:issues (or paradium:issues/<project>, or --workspace-id paradium:issues) connects to it, and the route, keychain entry, seat-key name and repos directory all carry that segment verbatim (paradium:issues/issues, ~/Crew/paradium:issues/<repo>). Finder shows the colon in that directory as a slash; --dir overrides the location.

Then install it as a real service:

bin/crew install           # writes and loads this platform's own units
bin/crew install --dry-run # see what it would do first
bin/crew uninstall         # unload and remove them

install writes three independent units — run, release and passengers — and picks the mechanism itself per host: launchd on macOS, a systemd user unit on Linux (falling back to a crontab line where systemctl is not usable), or an error on Windows (not built yet). It always uses an absolute interpreter path (process.execPath) and points each unit's own log at a different file than the crew's own — pointing both at one file was hit for real, and doubles every line.

run is a persistent service: crew daemon, the long-running loop that chains cycles immediately when there's work and backs off when there isn't (launchd KeepAlive/systemd Restart=on-failure restart it if it crashes). Installing it starts it, the same as installing any other service would. bin/crew daemon start|stop|status|restart control that same service directly — useful without a full reinstall — except on the plain-crontab fallback, which cannot supervise a persistent process and so keeps the old one-shot-per-fire crew run instead. release and passengers stay on their own fixed timers/crontab lines, one-shot per fire same as always: the first run of each happens one interval after install, and each fire is a few API calls — a full agent session only starts when that cheap check finds something worth waking for.

The daemon also updates itself: on an idle pass (never mid-cycle) it checks whether its own installed files (src/dist/bin, or the binary itself for a compiled install) have changed since it started, and if so exits cleanly — the same crash-recovery restart above then relaunches it against whatever is now on disk. crew daemon restart forces this immediately, for a manual git pull && crew daemon restart instead of waiting for the next idle tick.

launchd/com.tablation.crew.plist is kept only as a reference for what install generates — hand-editing and loading it directly still works, but crew install/crew uninstall is the supported path now.

Commands

crew poll [route]              decide a cycle and report it; writes nothing
crew run [route] [--role R]    run the winning role's session, then release
crew daemon [route]                run the persistent supervisor loop in the foreground
crew daemon start|stop|status      control the installed `run` service directly
crew daemon restart                stop then start it (force-picks-up an update)
crew release [route]           merge what QA verified, version it, ship it
crew merge [route]             merge verified branches and stop
crew deploy [route]            release now, even with nothing new to merge
crew watch [route]             live view of what the crew is doing
crew status [route]            paused/running state, led by what the operator owes (needs_info assigned to them, needs_planning, ships needing attention)
crew doctor [route] [--fix]    preflight; --fix enables clean routes, offers crew install
crew selftest [route] [--toolchain NAME] [--json]   launch each toolchain as a run would and prove it reaches the tracker
crew sample-ticket [route] [--remove] [--dry-run] [--json]   file one ticket on a throwaway repo that walks the dev, QA and release loop
crew ports [route]             which checkout owns which ports, and what is up
crew reap [route]              kill servers left behind by removed worktrees
crew drop [route] NNN          remove a merged ticket's worktree and branch
crew worktree [route] NNN      cut (or refresh) this run's worktree for a ticket from the remote; prints its path
crew sync [route]              fast-forward the checkout and its worktrees from the remote
crew pause|resume [route] [R|release]  pause everything, one role, or releases only (passengers keep running)
crew log [route]               tail the log
crew inbox [--member NAME]    your tickets across every workspace
crew connect WS[/PROJECT]     resolve a workspace/project's ids into the state tree
crew repos add [ROUTE] [NAME] [PATH]  attach a local checkout to a route in crew.yaml, creating its Repos row if missing (--dry-run previews)
crew repos list ROUTE         the checkouts a route has, and the tracker Repos row each matches
crew repos check [PATH]        validate a checkout's .crew.yaml (parse, release readiness, hook syntax) without attaching it (--route ROUTE, --strict, --json)
crew repos hooks [ROUTE] NAME  suggest test/build hooks for a checkout (--write route|repo [--replace] writes them, --clear route removes them, --recipes, --hook KEY=CMD, --agent, --json)
crew install [--replace]     write and load this platform's scheduler unit (macOS: clears a crew installed from another path)
crew uninstall [--all]        unload and remove it (--all: every crew launchd unit, whichever path it came from)

Every command that could change something takes --dry-run.

One ship, many boards

A ship connects to several workspaces at once, and ranks their queues against each other — a P0 on one board outranks a P2 on another. The ship is identified by ship.name matching a row in each board's Ships table; a crew member is identified by email, so the same person is recognised across workspaces.

An area of development spans several repositories, so a checkout is a property of the ticket, not of the route: repos: maps each name in the board's Repos table to a directory on this machine. Naming even one repo there makes it a closed list — several ships can divide a multi-repo area's work between them, and anything not named is deliberately another ship's, marked NO CHECKOUT in the digest rather than left blank (blank would read as "work it here", which is the wrong directory).

Leaving repos: out entirely means the opposite: this ship serves everything the board's Repos table lists, each checkout defaulting to <reposBasePath>/<workspace>/<repoName> — reposBasePath is ~/Crew unless a ship: or route-level reposBasePath: says otherwise. A repo that doesn't exist yet at its derived path is cloned there automatically the first time a ticket actually needs it (from the Repos table's own remote column, owner/repo over SSH) — not up front on crew connect, which only discovers names and remotes, never fetches anything itself.

What lives where

bin/crew          the entry point
src/              the implementation (Node, run directly via type stripping)
test/             its tests, including a fake board for integration runs
                  (the suite must pass with no crew.yaml, as on CI: a CLI-level test sets
                  CREW_CONFIG to a missing file instead of relying on the machine's own)
prompts/          prompt sets — prompts/<name>/personas/lane-<role>.md
                  + prompts/<name>/skills/*.md
docs/             CONTRACT.md, REPO_SPEC.md, MIGRATION.md
launchd/          timer templates (edit the paths before installing)
crew.example.yaml copy to ~/.config/crew/crew.yaml

The crew carries no ids, no absolute paths, no project commands and no device names. Anything specific to a machine belongs in crew.yaml; anything specific to a repository belongs in that repository's own .crew.yaml (docs/REPO_SPEC.md), which declares its platform, hooks, branch naming, versioning and release mode:

| hook | required | what it is for | | --- | --- | --- | | test | yes | run the suite before a release | | build | yes | build it | | deploy | when release.mode: local | ship it | | setup | no | prepare a fresh worktree | | ports | no | print this checkout's port assignments | | version | no | print the current version | | bump | no | produce and print the next one | | merged | no | ask the forge whether a branch actually landed | | released | no | confirm a commit is live (a sha, or an exact version) |

The crew carries no commands of its own: everything it runs to test, build, version or ship a repository comes from that repository's own file.

One hook is the exception and lives in crew.yaml instead: notify, which is handed a level (ok / warn / fail), a headline and a detail line whenever a release ships or is blocked. Where that should be shown — a desktop notification, a status widget, a webhook, a push — is a fact about the machine and the person watching it, not about the code. Define none and release state simply goes to the log.

A repo's own .crew.yaml wins over anything repeated in crew.yaml, and crew doctor reports the duplication as drift. The client can still configure a repo that has no .crew.yaml of its own.

Status

Live on macOS, driving this project's own board. Triage is a seat, not a second process — there is one timer to install, not two.

The bash implementation this replaces has been deleted. Where its behaviour was the only specification of something — the priority matrix, the QA digest, the crew labels — it was captured as a fixture under test/fixtures/ first, so the tests still hold the port to what the original did.

Outstanding: Linux has not been exercised end to end, and the API key sits in crew.yaml in plaintext until the device-code flow and keychain storage land.