top, for coding agents. A live terminal dashboard for Claude Code sessions: see every session's spend, tool activity, and risk in real time — and pause or kill a runaway one.
- Observe — live sessions table (state · tools · tokens · $), per-session timeline, global events feed. Reads Claude Code's local transcripts; 100% local, no network calls.
- Detect — stalls (no activity), thrash (identical tool-call loops), risky
actions (
rm -rf, force-push,curl | sh, publish/deploy, secrets access, writes outside the project), and budget caps. - Act —
ppauses a session,Kkills the process (with confirmation). Pause and kill are two very different mechanisms — see Pause & kill before relying on either.
curl -fsSL https://raw.githubusercontent.com/adityachaudhary99/agentop/main/install.sh | shiwr -useb https://raw.githubusercontent.com/adityachaudhary99/agentop/main/install.ps1 | iexBoth scripts download the right prebuilt binary for your OS/arch from
Releases, verify its
checksum, and install it (~/.local/bin on Linux/macOS,
%LOCALAPPDATA%\Programs\agentop on Windows) — no admin/root needed by
default.
Prefer Go tooling, or want to build from source:
go install github.com/adityachaudhary99/agentop/cmd/agentop@latestOr grab a binary directly from Releases.
agentop # live dashboard
agentop <dir> # shorthand for --dir <dir> (must be an existing directory)
agentop --dir <path> # scoped to one repo's sessions (see Scoping)
agentop stats # one-shot summary, no TUI
agentop hooks install # opt-in: enable pause/budget enforcement
agentop config init # write a commented config template
agentop version # print version| Command | Does |
|---|---|
agentop |
live dashboard, global — every session under ~/.claude/projects |
agentop <dir> |
shorthand for --dir <dir> — <dir> must stat as an existing directory, or the CLI errors instead of silently ignoring it. An explicit --dir alongside a positional always wins. |
agentop --root <dir> |
dashboard against an alternate transcripts root (testing/demos) |
agentop --dir <path> |
dashboard scoped to one repo — see Scoping |
agentop stats [--root <dir>] [--dir <path>] |
one-shot summary, no TUI; same scoping flags as the dashboard — see stats |
agentop hooks install|uninstall|status |
manage the opt-in PreToolUse hook — see Enforcement |
agentop config init |
write a commented ~/.agentop/config.toml template — see Config |
agentop version |
print the build version |
agentop hook |
internal — invoked by Claude Code's PreToolUse hook, not by users directly |
Keys:
| Where | Keys |
|---|---|
| sessions | j/k move · enter detail · e events · p pause/resume · K kill · o sort · O invert sort · / filter · i hide idle |
| detail / events | j/k scroll · esc back |
| sessions / detail / events | g/G jump to top / bottom — there is no Home/End binding |
| global | ? help · q quit · ctrl+c quit |
q quits from the sessions/detail/events views, but while the help overlay
or the filter input is open, q closes the overlay or types the letter
instead — it never quits from there. ctrl+c is the one unconditional
escape hatch: it quits immediately regardless of view, overlay, or an
in-flight kill confirmation.
The detail view (enter from sessions) prints the session's full id on its
own line, separate from the state/spend summary — copy it straight into
claude --resume <id>. Timestamps (started/last, in both the sessions
context panel and detail view) show just a time (15:04) for a session from
today, and Jan 2 15:04 once it spans into a previous day.
Sessions idle more than 24h at startup aren't tailed by the dashboard (it
skips their full history for startup speed) — run agentop stats for those.
By default agentop is global: it watches every session under
~/.claude/projects, across all your repos. Pass --dir <path> (or the
agentop <dir> shorthand) to scope the dashboard to one repo — only
sessions whose working directory is under <path> are shown. A session's
membership is decided once, from its origin directory (its first
CWD-bearing event, which is always a main-chain event — see
Sessions table), so it can't drift in or out of scope
mid-run. agentop stats --dir <path> does the same for the headless
summary. --dir composes with --root (an alternate transcripts root,
mainly for testing/demos): agentop --root <transcripts-dir> --dir <path>.
Each row renders as project · title, right up to a fixed-width project
column so the · title separator lands at the same screen column on every
row — even when project names in the visible set differ in length, and even
when several sessions share the same project (the title is what tells them
apart).
The title comes from Claude Code's own AI-generated title if one exists;
otherwise it falls back to the session's first real user message, the same
heuristic Claude Code's own resume list uses. Injected meta text — slash-
command wrappers, <system-reminder> blocks, plain Caveat: … prose — is
recognized and skipped, so it never becomes a fake title.
A session's project/working-directory identity is fixed at its origin directory: the first event that carries a cwd from the session's main chain. Subagent (Task-tool) transcript entries never set or change a session's own directory or project, even if a subagent's events happen to arrive first.
When a session has spawned subagents, an indented └ subagents child row
appears under it with its own tool count and spend — see
Subagents for what that total does and doesn't include.
The line above the sessions table reads, in order:
N active · M idle · today $X · total $Y · burn $Z/hr · alerts A · hooks on|off · skipped S [· watch-fails W] [· sort MODE(↓|↑)]
active/idle— counts of currently visible sessions in each state (the idle threshold isidle_minutesin config).today— spend from events that landed on today's local calendar date only, summed across all sessions that touched today — not each visible session's whole lifetime.total— lifetime spend of every currently visible session (its full history, not just today's). For full historical totals beyond what's currently visible, useagentop stats.burn $Z/hr— aggregate burn rate across sessions, shown once it's ≥ $0.005/hr. Until the watcher's initial scan of existing transcripts has fully settled, this slot instead readsscanning…— rates are meaningless mid-replay, so agentop shows no number rather than a fake one.alerts— count of detector alerts raised this run (stall/thrash/risk/ budget); see them individually in the events view (e).hooks on|off— whetheragentop hooks installhas been run.skipped— malformed transcript lines encountered so far; counted, never hidden.watch-fails— only shown when greater than 0: directories fsnotify failed to watch (e.g. an OS watch-descriptor ceiling); those subtrees miss live updates.sort MODE↓|↑— only shown once sort isn't the default (activity, descending); appears after changing sort witho/O.
On a narrow terminal, the scanning…/burn slot is dropped entirely rather
than truncating anything after it.
Each session row shows a tok/m column (tokens/minute), and the summary
strip shows an aggregate burn rate in $/hr once there's measurable spend.
Both are derived from a 60-second sliding window of usage samples. Rates
render – — not a guess — when there isn't enough data yet (fewer than 2
samples, or samples spanning under 5 seconds): a fresh or quiet session, not
a bug.
Spend/burn are computed at Anthropic's API list rates, including prompt-cache write and read — cache traffic usually dominates an agentic session, and cache writes bill at 1.25x the input rate. If you're on a Claude subscription (Pro/Max), nothing here is a bill: it's the API-equivalent value of what the session consumed.
Transcripts don't record a cache entry's TTL, so agentop always assumes the default 5-minute cache (write = 1.25x input); sessions using the 1-hour cache option may undercount cache-write cost (its write rate is ~2x input, not 1.25x).
<synthetic> — Claude Code's own bookkeeping model name for entries it
generates itself (errors, continuations) — is priced at exactly $0 and
counted as known, so it never flips a session's spend into the honest-
unknown ~?. Bare alias model ids (sonnet/opus/haiku/fable, without
a claude-… prefix — how subagent spawns are recorded) are priced at their
current-generation rate; if a generation later diverges from an alias, this
undercounts or overcounts until the pricing table is revisited.
When a session spawns subagents (Claude Code's Task tool), agentop parses
the sidechain transcript entries and shows an indented └ subagents child
row under the parent in the sessions table, with its own tool count and
spend. The parent session's own totals already include subagent activity —
the child row is a breakdown, not an addition. The detail view shows the
same split.
Pause and kill are unrelated mechanisms — conflating them is the most common source of confusion, so read both fully before relying on either.
- Pause (
p) only sets a flag in~/.agentop/policy.json. By itself it does nothing to the running Claude Code process — it does not stop, block, or slow it down. It becomes an actual enforcement point only onceagentop hooks installhas been run: from then on, Claude Code's PreToolUse hook checks that flag before the paused session's next tool call and blocks it, with a message the agent itself can read ("session paused by agentop — resume it from the agentop dashboard"). Presspagain to resume. Without the hook installed, pausing is bookkeeping only. - Kill (
K) is unrelated to pause and needs no hook. It sends a termination signal to the OS process agentop has matched to that session (matched by project working directory, since transcripts carry no PID). The confirm modal names the exact PID it's about to act on — that modal is the safeguard, since matching is best-effort (see Limits). Kill only ever terminates a process: it never touches transcripts, session history, or anything else on disk.
Platform honesty on kill matching: transcripts carry no PID, so agentop can
only look for running processes whose command line contains "claude". On
Windows the process list exposes no working directory either, so multiple
concurrent sessions are indistinguishable — the modal names exactly one
candidate PID and you decide. Sessions hosted by embedded runners (a bare
node.exe worker, an IDE-managed process) don't match at all and report
"no matching claude process found". Treat kill as a convenience for the
common one-session case, not a guarantee.
agentop hooks install adds a PreToolUse hook to ~/.claude/settings.json
(backed up first). The hook reads ~/.agentop/policy.json — written by the
dashboard whenever you pause a session or a session crosses a configured
budget cap — and blocks that session's next tool call with a message the
agent can read. It fails open: any hook error (bad JSON, missing file, a
panic — anything) allows the call, so agentop can never break Claude Code.
agentop hooks uninstall removes only agentop's own entries;
agentop hooks status reports whether it's currently installed.
Known upstream limitation: headless runs (claude -p) don't fire
user-settings PreToolUse hooks (verified against Claude Code 2.1.207), so
pause enforcement applies to interactive sessions only.
agentop stats prints one table against the full local transcript history
(not the dashboard's 24h startup-tail window) and exits — no live updates,
no TUI. Columns: SESSION (id) · PROJECT · TITLE · STATE · TOOLS ·
TOKENS · COST, followed by a total: $X · skipped: N malformed lines
footer.
The SESSION column's width grows continuously with terminal width — from
an 8-character id up toward the full 36-character UUID — rather than
snapping between a short and a full id at some fixed threshold; any width
left over once SESSION reaches its full 36 chars goes to TITLE instead.
Because it's a one-shot print, it does not react to a live resize: rerun
agentop stats after resizing your terminal to get the new layout. All
truncation (TITLE, SESSION) cuts on whole characters, so a truncated
column lands at exactly its stated display width even with multi-byte text.
~/.agentop/config.toml — stall threshold, budgets (off by default), disabled
risk rules, pricing overrides. Unknown models show cost ~? — agentop never
invents numbers; malformed transcript lines are counted in the status bar, not
hidden.
- Claude Code only (the ingest is an adapter; more harnesses later).
- Kill is best-effort: transcripts carry no PID, so agentop matches running
claudeprocesses by project directory and asks before acting. On Windows, a process's working directory isn't cheaply accessible, so discovery can't rank candidates by cwd there — the confirm modal naming the exact PID is the safeguard, not cwd-ranking. - Live view + today's totals; no history database beyond what
agentop statscan recompute from transcripts on disk.
MIT © Aditya Chaudhary
