Skip to content

Repository files navigation

agentop

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.
  • Actp pauses a session, K kills the process (with confirmation). Pause and kill are two very different mechanisms — see Pause & kill before relying on either.

agentop demo

Install

curl -fsSL https://raw.githubusercontent.com/adityachaudhary99/agentop/main/install.sh | sh
iwr -useb https://raw.githubusercontent.com/adityachaudhary99/agentop/main/install.ps1 | iex

Both 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@latest

Or grab a binary directly from Releases.

Use

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

Commands

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.

Scoping

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>.

Sessions table

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.

Summary strip

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 is idle_minutes in 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, use agentop 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 reads scanning… — 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 — whether agentop hooks install has 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 with o/O.

On a narrow terminal, the scanning…/burn slot is dropped entirely rather than truncating anything after it.

Rates

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.

Subagents

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 & kill

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 once agentop hooks install has 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"). Press p again 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.

Enforcement (opt-in)

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.

stats

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.

Config

~/.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.

Limits (v1, by design)

  • Claude Code only (the ingest is an adapter; more harnesses later).
  • Kill is best-effort: transcripts carry no PID, so agentop matches running claude processes 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 stats can recompute from transcripts on disk.

MIT © Aditya Chaudhary

About

top, for coding agents — a live terminal dashboard for Claude Code sessions: spend, tool activity, risk detectors, and opt-in pause/kill. 100% local.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages