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

deadmans-switch

v0.1.6

Published

A dead man's switch for autonomous agent fleets. Finds the agents that went quiet, and the ones that lie about succeeding.

Readme

Deadman's Switch

A dead man's switch for autonomous agent fleets.

Your agents don't crash. They go quiet, or they return ok: true having done nothing, and you find out five weeks later.

npx deadmans-switch scan

No install, no account, no config, no database. It reads what's already on your machine and tells you which agents stopped — and, more usefully, which ones stopped for a reason you already know about.


What it looks like

Real output from a 29-job laptop fleet, run through --anonymize:

  DEADMAN'S SWITCH  29 jobs · 7 projects · workstation

  CHECK
  ● quarry · abandoned
    missed 71 runs (silent 4 days), and nothing else here explains why
      ✗ quarry-queue                     missed 71 runs (silent 4 days)

  ● ember · abandoned
    1 stopped, 1 never ran at all — no shared stopping point, so this was never one decision
      ✗ ember-job-1                      silent 29 days · not loaded in launchctl
      ∅ ember-snapshot                   has never produced output · Disabled=true in plist

  DELIBERATE — NO ACTION
  ● nimbus · paused
    all 9 jobs went quiet within 11 days of each other, with no errors — this looks deliberate
      5 of these have never run at all

  HEALTHY
  ● meridian · active
    all jobs producing on schedule

  4 projects to check. Nothing on fire.
  1 paused — not counted above.

Those nine nimbus jobs have 971 missed runs between them. A conventional monitor shows you 971 alarms, or nine red lights. This shows you one grey line, because a human deliberately shelved that project — and then puts the one job that actually stopped without explanation at the top.

The idea

The unit of judgment is the project, not the job.

A job that stopped is not news. A job that stopped while its siblings kept running is news — the living siblings prove the machine, the network and the scheduler all work, so the dead one has no excuse.

A whole project that stopped, all at once, with no errors, is usually a human deciding to work on something else. Reporting that as an incident is how a monitoring tool teaches you to ignore it.

| State | Meaning | Shown as | |---|---|---| | lying | Meeting its schedule and producing nothing. Green everywhere you'd think to look. | NEEDS YOU NOW | | failing | Whole project dark and left errors behind — one credential or dep broke for everyone at once. Masquerades as a pause and costs weeks. | NEEDS YOU NOW | | partial | Some jobs alive, some dead. Highest signal-to-noise finding there is. | NEEDS YOU NOW | | zombie | Mostly stopped, stragglers still firing into the void and burning spend on work nobody reads. | CHECK | | paused | Everything stopped together, quietly. A human decided this. Said once, collapsed, then quiet. | DELIBERATE | | completed | Bounded run finished as designed. Not dead. | DELIBERATE | | active | Running as promised. | HEALTHY |

What it detects

| Rule | Signature | |---|---| | Missed runs | Declared schedule vs. observed output — "declares every 3 h, has missed 452 runs", not "quiet for a while" | | Credential expiry | 401, 403, invalid_api_key, billing disabled in run output | | Green but empty | Runs on time, reports success, and stops producing. Needs a runner that records liveness and success separately, so today that means Hermes — an empty stdout is not evidence, because a careful script logs to its own file | | Zombie path | The job invokes a binary that is no longer on disk — it cannot have worked, whatever its logs say | | Never ran | Scheduled, but has never once produced a byte | | Configured but inert | Plist on disk with Disabled=true, or absent from launchctl list | | Zombie spend | Stragglers still firing into a project nobody is reading | | Bounded and done | RRULE COUNT exhausted — finished, not dead |

Thresholds are always relative to a job's own schedule. Seven days of silence is catastrophic for a 90-minute job and unremarkable for a weekly one. When no schedule can be parsed, the verdict falls back to elapsed time and is labelled inferred — never assumed healthy. Unmeasurable silence is the most dangerous kind, and defaulting it to green is how a 61-day-dead agent stays invisible.

Where it looks

Codex automations · Hermes cron and profiles · Claude scheduled tasks · launchd agents · crontab. Failures in one source never sink the scan.

macOS. A systemd adapter for Linux exists in the source and passes its tests, but it has never been run against a real systemd, so it isn't claimed as supported. node cli/systemd.test.js on a Linux box is what would change that.

Clustering, and why there's almost no config

Jobs are grouped by what their definitions say they operate on — repository checkouts, tracker keys, team names — not by how they're spelled. atlas-board-driver and northwind-security-scan land in the same project because both point at the same checkout, which no amount of name-matching would tell you.

That gets most of a fleet with nothing configured. It has a floor: one real job's entire body is Clear the linear triage queue, which identifies no project to anyone but its author. When that happens the scan says so rather than quietly filing each one as its own healthy project:

  4 claude jobs name no repo, tracker or team, so each stands alone:
    build, queue, triage, sweep
    If some are one project, group them in .deadmanrc — see --help.

Optional .deadmanrc, nearest one wins, searched from the working directory up:

{
  "projects": {
    "atlas": ["atlas-", "nightly-ingest", "warehouse-sync"]
  }
}

Patterns match the start of a job name. Grouping is all this file can do — it cannot set a severity, mark anything healthy, or silence a finding. A monitoring tool whose config can hide a dead job will eventually be used to hide a dead job.

Menu bar

A scan takes ~120 ms, which is fast enough to live in the menu bar.

npx deadmans-switch install-menubar

That finds SwiftBar or xbar, reads the plugin folder it's actually configured to watch, installs the plugin and refreshes it. One of those two apps has to be installed — the plugin is a script they run — and if neither is, the command tells you the one line to run and stops.

It is the only thing here that writes anything. It writes one file, to a plugin folder you just asked it to write to. Scanning still never touches a job, a plist or a crontab.

When nothing needs you it renders a single dim · and costs no attention. When something does, it becomes ✗ 1, and the dropdown carries the same reasoning the terminal gives you:

⚠ 1
─────────────────────────────
1 project needs you now
23 jobs · 4 projects · workstation
─────────────────────────────
NEEDS YOU NOW
● atlas · partial
    1 of 13 jobs stopped while 12 kept running — the
    siblings prove nothing is wrong with the environment
    ✗ dispatch     missed 71 runs (silent 4 days)
    ∅ reconcile    has never produced output
─────────────────────────────
9 paused · 13 healthy

The whole finding is on screen the moment the menu opens. Nesting the reasoning into a submenu is the obvious way to build this and the wrong one: it hides the only thing the tool has to say behind a hover.

A monitor you have to remember to open is one more thing that goes quiet, which would be a funny way for this particular tool to fail.

The plugin runs from $HOME, so put a .deadmanrc there if you want your groupings applied — the one in a project directory won't be found.

Options

--json              print the report as JSON instead of a dashboard
--out <path>        write the report to <path>
--all               show healthy projects too, not just what needs you
--anonymize         stable aliases, roles preserved, all free text dropped
--menubar           render for SwiftBar/xbar
--include-vendor    include Apple/Google/Adobe launchd agents
--strict            exit non-zero when anything critical is found

--strict makes it a CI gate. --out plus the viewer merges reports from several machines into one fleet view, which is why there is no SSH code in here — same outcome, none of the transport failure modes.

Read-only, always

It never writes to, disables, kills or restarts anything it inspects. Suggesting a fix command is fine; running it is not.

--anonymize exists because the scan output is genuinely useful to share and genuinely full of your infrastructure. It drops free text entirely rather than scrubbing it — redacting paths and hostnames out of prose with regexes is a game you lose eventually, and none of that text is load-bearing for the findings.

Requirements

Node ≥ 18. No dependencies.

License

MIT