roost: top, but it can see your subagents

top shows you processes. A Claude Code subagent isn’t one — it runs as a sidechain inside its parent’s process, so anything that watches pids is blind to it by construction. That’s the gap roost was built to close: a dashboard for every live Claude Code session, the model it’s on, how much context it’s burned — and the subagents each one spawned, which nothing else surfaces.

It’s one file, stdlib-only Python 3.9+, no dependencies. Runs on macOS, Linux, and Windows.

  WORKER    MODEL    CTX  IDLE    TASK
NEAR LIMIT
  demo-a1   opus-5   85%  12s     refactor the parser
PARKED + COSTLY
  demo-b2   opus-5   61%  4h10m   audit the build scripts
WORKING NOW
  demo-c3   fable-5  22%  3s      add integration tests

SUBAGENTS
  STATE    AGENT       MODEL     CTX  IDLE   TASK
  working  a812aca59f  opus-5    33%  2s     survey the config loaders
  idle     adaffaba4b  sonnet-5  67%  1h22m  draft the migration notes

Sessions group by what it costs to ignore them, not by how big they are: NEAR LIMIT is about to stop working, PARKED + COSTLY bills its whole context on the next turn whether you look at it or not, and everything quiet collapses to one line so a fleet of two dozen sessions doesn’t scroll off the bottom of the terminal.

Where the subagent data actually comes from

A subagent’s short task description (“survey the config loaders”) lives only in the parent session’s transcript, keyed by agentId. The subagent’s own transcript sits one directory deeper: ~/.claude/projects/<slug>/<sessionId>/subagents/agent-<id>.jsonl. roost joins the two, falling back to the opening words of the subagent’s own first message if the parent record has already scrolled out of reach. Three local, read-only sources feed the whole dashboard — live session files, project transcripts, and a localhost probe of the Ollama/LiteLLM/OpenWebUI ports — and nothing goes over the network beyond that probe.

Acting on a session, carefully

Reading is the safe half. Press i to arm interactive mode and a cursor plus an EXPERIMENTAL marker show up — that’s the one key that draws the line, because x ends a real process. There’s no local control channel into a running Claude Code session, so nothing gentler is available from outside it: on Unix x sends SIGTERM and the session exits on its own terms; on Windows, where there’s no equivalent, it’s a hard TerminateProcess. y copies the session ID instead, for claude --resume <id>. Every stop gets logged to ~/.claude/logs/roost.jsonl — model, context, idle time — but the session’s task text is deliberately never recorded, on the theory that an audit log of what got stopped shouldn’t double as a copy of what was being worked on.

Install

Homebrew, apt/deb with a signed repo, pipx, npm, or just the raw file — no dependencies means curl -o roost .../roost.py && chmod +x roost works too. The bare name roost was already taken on both PyPI and npm by an unrelated, deleted project, so those two ship as roost-top; the command it installs is still plain roost. Windows gets a real roost command through npm, or you just save the script as roost.py and run it — .PY is already in PATHEXT.

What it doesn’t pretend to be

It reads an undocumented on-disk format that can change without warning — that’s the whole foundation, and breakage is expected, not exceptional. The context-window size for a session is inferred, not recorded anywhere on disk, so roost picks the smallest standard tier the usage fits and shows its work. The ADVICE panel’s thresholds are tuned to one person’s usage patterns. Daily-driven on macOS and Windows; CI runs the suite on Linux and it detects sessions there, but nobody’s actually living on it yet.

It’s one person’s tool with a public issue tracker, and the help wanted label is real asks, not decoration — Linux field reports chief among them.

MIT licensed.


From my swamp to yours.

github.com/gmhoward9289-ops/roost