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.