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

@brutalsystems/birddog

v2.0.0

Published

Watch live coding-agent sessions on this machine and report what they are doing. Observation only: it never prompts, approves, or starts and stops a session.

Readme

birddog

license

Know what the coding agents on your machine are doing — and which one is waiting on you — without ever touching them.

birddog watches live Claude Code, Codex and opencode sessions on one machine and tells an orchestrating agent what it observed: a session went idle, asked for input, exited, or went quiet. It never sends a prompt, never approves anything, and never starts, stops or restarts a session.

birddog monitors sessions; it does not manage their work. Stopping birddog leaves every watched agent running.

It is the observation third of a small family: muster launches instructed agents, tincan lets them message each other, birddog watches them.

Status: working, early. Instances run, observe all three runtimes, report what deserves attention, and deliver alerts to an orchestrator over either of two routes. What each runtime actually exposes — and does not — is in docs/providers.md.

What it's for

Running more than one agent and not sitting over them. A worker that finished and a worker blocked on a question look identical from outside — both idle, both silent — and the difference is the one that costs you an afternoon. birddog is the part that tells them apart and says so, so an orchestrator can act on it instead of polling terminals.

What makes it different

  • It reports what it saw, never what it assumes. "Looked and saw nothing" and "no way to look" are different answers, and collapsing them is what lets silence pass for evidence that a worker is unblocked. birddog keeps them apart in every field it fills.
  • It refuses to guess a state. A status outside the vocabulary it has established is reported as unreadable rather than mapped to the nearest familiar word.
  • Every answer says how it was reached. An observed input request carries whether it came from the runtime's own record, from birddog's hooks, or from reading the conversation — so a consumer can trust one and discount another.
  • It touches nothing. No adapter resumes a conversation, starts a turn, or injects a prompt to find out what is happening. Reading is the whole of it.
  • Claims are pinned to versions. Runtime behaviour is established by driving the real programs and recording what they did. Where a claim was later measured to be wrong, the correction is in the git history rather than quietly edited away.

Quick start

npm i -g @brutalsystems/birddog        # macOS, Apple silicon
birddog discover                       # what can be watched
birddog doctor                         # what can and cannot be observed

The package carries the compiled binary, so nothing is built at install time and no Go toolchain is needed. From a clone, or without npm:

go build -o birddog ./cmd/birddog

Then watch something. Take a session_id (and, for Claude Code, the pid and proc_start beside it) from discover into a config — see examples/ — and start an instance:

birddog start --config birddog.json
birddog status --instance <id>
birddog events --instance <id> --after <cursor> --wait 25
birddog stop --instance <id>

The instance keeps observing after the shell that started it is gone, and stopping it leaves every watched session running.

TARGET        PROVIDER  STATUS         ATTENTION
api-refactor  claude    running_tool   —
auth-thread   codex     unavailable    —
checkout-7f   claude    waiting_input  input_requested

Seeing more

Three things birddog cannot observe from outside a session. All are opt-in, and none changes what a watched session does.

birddog hooks install      # Claude Code: tool calls and permission requests
# opencode: nothing outside the process can see it at all
[plugins.birddog]
npm = "@brutalsystems/birddog-opencode"

Claude Code again, for a session blocked on a question asked in plain prose — named per target in the watch list, because it is work on every pass and one of its two signals is a heuristic an operator should choose rather than inherit:

{ "id": "worker-1", "provider": "claude",
  "observations": { "transcript": true } }

Sessions already running pick hooks up without restarting. Everything here preserves what is already configured, and can be removed.

Getting told

Alerts go to the orchestrator that configured birddog, never to a watched worker, and are queued for a turn boundary rather than interrupting one.

| Route | Reaches | Needs | |---|---|---| | claude-inbox | a Claude Code orchestrator | nothing | | tincan | Claude Code, Codex or opencode | tincan 2.0.0+ on PATH | | none | nothing; poll events instead | nothing |

Delivery failure never affects observation. Whatever happens to an alert, the event feed still has it.

What it will not tell you

Liveness is only ever reported on evidence. A Claude Code session is live when its socket answers and the process at its PID is still the one the registry recorded; a Codex thread is live when a running process holds its writer lock. When the evidence fails, the last observed status is preserved and marked (stale) rather than presented as current.

unavailable is a real answer, not a gap to be filled in. Codex reports its threads as notLoaded to anyone who does not own them, which describes the asking process rather than the session — so birddog says it cannot see the state instead of guessing one.

Nor will it tell you a worker is unblocked. A question reaches a human two ways and the runtime's own record sees only one of them: a Claude Code session blocked on an AskUserQuestion menu reports waiting, while one that asked in plain prose reports idle, indistinguishable from a turn that simply ended. Transcript reading is what separates those two, and it says which of them it found. An absence of observation is never reported as evidence of absence, and a turn ending is not work finishing.

Requirements

macOS on Apple silicon. The npm package ships one prebuilt arm64 binary, so a global install refuses anything else rather than building from source. Building from a clone needs Go 1.26+; the plugin suite needs Node.

Then whichever agents you mean to watch. Verified against Claude Code 2.1.267 and 2.1.274, Codex CLI 0.155.1, and opencode 1.18.31 — what that verification covers, and what it does not, is in docs/providers.md.

Documentation

| | | | --- | --- | | providers.md | what each runtime exposes to an outside observer, and what it does not — authoritative | | decisions.md | what was decided, and what it costs — binding | | cli.md | every command, its output, and the error codes | | architecture.md | how the pieces fit, and the decisions that shaped them | | troubleshooting.md | what the surprising answers mean | | acceptance.md | every acceptance criterion, and what is not done | | docs/README.md | the full index, and what supersedes what |

Keeping it up to date

Publishing a release does not touch an installed copy: birddog --version keeps reporting the old version until you update it.

npm update -g @brutalsystems/birddog

If which birddog resolves to a version-manager shim (for example ~/.asdf/shims/birddog), run that update under the Node the shim resolves to and reshim afterwards — asdf reshim nodejs — or the shim keeps pointing at the old binary.

The opencode plugin is fetched by opencode from its own npm specifier and is never installed by hand. opencode caches that specifier and does not revisit it, so a session can keep loading an older plugin after a newer one ships.

Build and run locally

make check is the local gate, and is exactly what CI runs: gofmt, go vet, go test ./... -race, the plugin typecheck and the plugin suite. Passing it locally and failing in CI should not be possible.

make check
go build -o birddog ./cmd/birddog
./birddog discover
./birddog doctor

Scope of the first version

Claude Code, Codex and opencode. Terminal and programmatically launched sessions. One machine — birddog observes what is running beside it and has no network listener. Desktop apps are out of scope.

License and releases

MIT © 2026 BrutalSystems. See LICENSE.

Publishing is tag-driven and runs in GitHub Actions over OIDC trusted publishing, with no stored npm token anywhere in this repository. A bare git push publishes nothing; a version tag is what triggers publish.yml, which publishes the CLI and the opencode plugin together from the same tag in the same version.

There is no RELEASE_NOTES.md here. The GitHub Release is created with --notes-from-tag, so the tag annotation is the public release record — which is why the message is not optional:

npm version <patch|minor|major> -m "%s — <what changed>"

Ordinary semver from 1.0: major for a change to what a consumer relies on — the shape of an event or status record, the CLI surface a script calls, or the meaning of a reported state — minor for a feature that leaves existing readers correct, patch for a fix. Releases before 1.0 were made under a different rule, where the minor carried that signal. RELEASING.md covers it in full.