Skip to content

zotoio/zoto-cursor-top

v0.2.0MIT

Live updating htop-style CLI that shows all agents across every Cursor process on this machine, with expandable parent-to-subagent hierarchy and scrolling log lines. After install, run /zoto-cursor-top-init once to build the CLI and put cursor-top on PATH.

zoto-cursor-top

A live updating htop-style CLI that lists every Cursor agent on the current machine - IDE windows, cursor-agent CLI sessions, and Cloud Agent VMs - in one expandable tree. Parent agents are expandable to reveal their subagents, and every row shows the start time, model, repo (working directory), and the last 3 log lines scrolling live.

cursor-top  ·  5 processes · 3 roots · 5 subagents · refresh 1000ms

TYPE     PID  AGENT                                MODEL              REPO                     START (elapsed)  STATUS TOKENS
[CLD]   6712  ▼ Cloud Agent VM                     claude-sonnet-4.5  /workspace               11:30:14 (32m04s) running
              Design plugin live-updating htop CLI
              log: tool: write plugins/zoto-cursor-top/src/cli.ts
              log: spawning subagent: generalPurpose
              log: tool: glob 'plugins/**/.cursor-plugin/plugin.json'
   [SUB]   --  ▶ Task(explore)                     claude-sonnet-4.5  /workspace               11:32:01 (30m17s) done
   [SUB]   --  ▼ Task(generalPurpose)              claude-sonnet-4.5  /workspace               11:40:01 (22m17s) running
              Scaffold htop-style TUI and wire discovery layer
              log: vitest 18/18 passed
              log: tsup build dist/cli.js ... 142kb
              log: writing tests/discovery/processes.test.ts
   [SUB]   --  ▶ Task(debug)                       claude-sonnet-4.5  /workspace               11:58:01 (4m17s)  waiting
[IDE]   4231  ▶ Cursor IDE                         claude-opus-4.7    /Users/dev/work/app      12:01:11 (14m07s) running
[CLI]   5612  ▶ cursor-agent CLI                   gpt-5              /Users/dev/work/util     12:08:11 (7m07s)  waiting

[↑/↓] move  [→/enter] expand  [←] collapse  [e]xpand all  [c]ollapse all  [r]efresh  [p]ause  [t]heme  [y] density  [q]uit

Features

  • Cross-process visibility. Discovers Cursor IDE windows, every cursor-agent CLI session, and Cloud Agent VM exec daemons in one view.
  • Parent / subagent hierarchy. Subagents spawned via the Task tool are nested under their parent and the tree is collapsible.
  • Per-row context. Start time, elapsed duration, model, repo, status, and the last 3 log lines for every agent (configurable).
  • Live updates. Refresh interval is configurable (default 1 second, minimum 200ms). Log lines re-tail every tick.
  • Three output modes. Interactive TUI, --once for one-shot formatted output, and --json for machine-readable snapshots.
  • Colour themes. Thirteen built-in palettes — utilitarian (default, mono, high-contrast, ocean, forest, sunset, solarized, dracula) plus emoji/unicode decor themes (party, kawaii, cyber, retro, wizard). Switch via --theme or the t key. A non-empty NO_COLOR env var forces mono per no-color.org. Themes affect the TUI only — --once and --json output stay colour-free.
  • Layout density. Three levels (compact, cozy, comfortable) control how much detail each agent row shows: row only, row + title, or row + title + log tail (today's default). Switch via --density or the y key; applies to the TUI and --once.
  • Token usage column. Chat and subagent rows show current context usage from Cursor's composerData store (promptTokenBreakdown.totalUsedTokens), formatted in thousands with one decimal (1.2k, 94.6k). Process-only rows show - when no composer session exists.
  • Billed cost (analytics key). When CURSOR_ANALYTICS_API_KEY (Admin read:e) or CURSOR_API_KEY is set and the current-user email can be resolved, a COST column appears with the conversation's billed USD total. Conversations still in flight sum unique requests; if the API rewrites an in-flight request as tokens grow, that request's latest totals replace the previous ones instead of stacking. The header shows the lookback-window spend. Pass --no-usage-api to disable. Without a key the default layout is unchanged.
  • Usage tail. cursor-top tail live-tails billed requests for the current user (-n, -f/--follow, -p/--prompt, --json, --hours, --email). Same auth as the COST column.
  • Log tail display (TUI + --once). Log lines default to oldest-first (chronological). Press o to toggle newest-first. Both toggles persist in ~/.zoto/cursor-top.json.
  • Filter / search. Narrow the tree by repo, model, status, or free text via --filter "<query>" or the / key in the interactive TUI. Scoped tokens (repo:, model:, status:) AND-combine with bare terms matched across label, title, repo, model, and log lines. Matching nodes keep their ancestor chain; non-matching siblings are hidden. A repo: match on a parent does not auto-include non-matching children.
  • Lifecycle event strip (TUI only). On every refresh the CLI diffs the previous unfiltered snapshot against the new one and surfaces the most recent transitions in a one-line event strip above the footer: finished (✓), failed (✗), blocked / needs input (⏸), and newly appeared agents. The strip and collector ! diagnostic lines are hidden by default — press i to toggle them on. The keybind row stays pinned to the bottom of the terminal. Rows whose status just changed stay accent-highlighted for ~5 seconds. With the default --active-only prune, agents that finish vanish from the tree rather than transitioning to done — those disappearances are reported as finished events. Pass --bell to ring the terminal bell on finished / failed events (at most one bell per tick). Event data is not included in --json snapshots today — that remains a deliberate non-goal for a future additive extension.
  • Demo mode. --demo renders a populated synthetic fixture for screenshots and onboarding without needing any Cursor session running.
  • Detail pane (TUI only). Press d on a selected row to open a bottom split showing full metadata and a deep log tail (default 25 lines via --detail-lines). Loaded lazily for the selected node only — the main tree still uses --lines (default 3). Esc closes the pane before clearing an active filter. ↑/↓ moves selection while the pane is open and the pane follows.
  • Viewport windowing (interactive TUI only). Large agent trees scroll inside a terminal-height viewport: only rows that fit the visible window are rendered, with ↑ N more / ↓ N more overflow indicators when rows are clipped above or below. Selection-follow scrolling keeps the highlighted row visible as you move with ↑/↓. Windowing accounts for header, filter bar, event strip, detail pane, and multi-line rows at the current density. --once and --json still emit the full untruncated tree.
  • No native deps. Works on macOS, Linux, and Windows using only ps / PowerShell and the standard library.

Installation

After installing from the Cursor marketplace

The marketplace ships agents, skills, commands, and rules — not a built cursor-top binary. Run one-time setup before /zoto-cursor-top:

/zoto-cursor-top-init

That builds dist/cli.js, copies the plugin to ~/.cursor/plugins/zoto-cursor-top/, installs Ink/React runtime deps, and symlinks cursor-top onto PATH (usually ~/.local/bin). Restart Cursor after init, then verify with cursor-top --help or cursor-top --demo.

From the zoto-agents monorepo

From the repo root:

pnpm install
pnpm --filter @zoto-agents/zoto-cursor-top install-local

install-local auto-builds dist/ when missing. Use /zoto-cursor-top-init inside Cursor for the same flow, or pnpm install-local from plugins/zoto-cursor-top/.

Published npm package (optional)

To use the CLI without init, install the package globally (once published):

yarn global add @zoto-agents/zoto-cursor-top
# or
npm install -g @zoto-agents/zoto-cursor-top

The CLI is exposed as cursor-top.

Usage

cursor-top                # interactive live TUI (default 1s refresh)
cursor-top --demo         # synthetic fixture, no Cursor required
cursor-top --once         # render one frame to stdout and exit
cursor-top --json         # emit one snapshot as JSON and exit
cursor-top --interval 500 # refresh every 500ms (minimum 200ms)
cursor-top --lines 5      # tail 5 log lines per agent (default 3)
cursor-top --theme ocean  # blue/cyan accent palette (TUI only)
cursor-top --density compact  # agent row lines only (no title/log tail)
cursor-top --filter "status:running repo:app"  # narrow tree (TUI + --once/--json)
cursor-top --bell         # ring terminal bell on finished / failed events (TUI only)
cursor-top --detail-lines 40  # deep tail depth for the d-key detail pane (TUI only)
cursor-top tail -n 20     # last 20 billed requests (analytics API key)
cursor-top tail -f        # follow billed usage
cursor-top --help         # show all flags
FlagDefaultDescription
--theme <name>defaultTUI colour palette: default, mono, high-contrast, ocean, forest, sunset, solarized, dracula, party, kawaii, cyber, retro, wizard. The last five add emoji/unicode decor (TUI only). Unknown names warn and fall back to default. NO_COLOR forces mono. Does not affect --once / --json.
--density <level>comfortableLayout density: compact (row only), cozy (row + title), comfortable (row + title + log tail). Applies to the TUI and --once.
--filter "<query>"(none)Filter by scoped tokens (repo:, model:, status:) and/or free text (AND-combined, case-insensitive). Pre-seeds the interactive filter; applies to --once and --json before output.
--belloffRing the terminal bell on finished / failed lifecycle events in the interactive TUI only (at most one bell per refresh tick). Ignored for --once, --json, and non-TTY stdout.
--detail-lines <n>25Deep log tail depth for the d detail pane (minimum 1). Interactive TUI only; does not change per-row --lines tails.
--usage-api / --no-usage-apionJoin billed USD from /teams/filtered-usage-events when an analytics key is present. --no-usage-api hides the COST column.
tail [options]—Subcommand: live-tail billed usage events. See cursor-top tail --help.

Keyboard

KeyAction
↑ / ↓ or j / kmove selection
→ / Enter or lexpand selected node
← or hcollapse selected node
eexpand all
ccollapse all
rforce refresh
p or spacepause / resume auto-refresh
tcycle colour theme (13 palettes; party … wizard add emoji decor)
ycycle layout density (comfortable → compact → cozy)
/open filter bar (repo: / model: / status: tokens + free text; Enter apply, Esc clear)
dtoggle detail pane for selected row (shows full metadata + deep log tail; Esc closes pane first)
itoggle lifecycle event strip and ! diagnostic lines (default off)
otoggle log tail order (default oldest-first; status shows · logs newest when reversed)
?toggle in-app help (keyboard shortcuts + ! diagnostic glossary)
q or Ctrl-Cquit

Inside Cursor

This plugin also registers:

  • The slash command /zoto-cursor-top-init - one-time CLI build and PATH setup after marketplace install (run once before the TUI).
  • The slash command /zoto-cursor-top - launches the TUI in the active terminal with $ARGUMENTS passed through.
  • The skill zoto-cursor-top-monitor - guided workflow for launching and interpreting the TUI.
  • The agent zoto-cursor-top-troubleshooter - diagnoses missing rows or empty log tails when discovery comes up short.
  • The rule zoto-cursor-top.mdc - documents conventions and data sources for any agent that wants to invoke or describe the CLI.

Data sources

SourceUsed for
ps -axww (macOS / Linux) or Get-CimInstance Win32_Process (Windows)Cursor IDE, cursor-agent, Cloud Agent VM processes
~/Library/Application Support/Cursor/ (macOS), ~/.config/Cursor/ (Linux), %APPDATA%/Cursor/ (Windows)IDE session metadata
~/.cursor/cli/cursor-agent CLI session metadata
~/.cursor/projects/<workspace>/Cloud Agent VM in-flight agents
Log files referenced from each session recordLast 3 lines per agent
POST /teams/filtered-usage-events (when CURSOR_ANALYTICS_API_KEY or CURSOR_API_KEY is set)Billed COST per conversation and cursor-top tail

Every data source is optional: the CLI tolerates missing directories and emits diagnostics rather than crashing.

Caching and refresh cadence (interactive TUI)

The live TUI creates one collector before render() and reuses it on every refresh tick (including manual r). Each tick:

  • Fast lane — ps scan for liveness; stat every transcript for mtime-derived status; re-tail only log files whose (mtimeMs, size) changed.
  • Slow lane (tick 1 and every 5th tick) — re-walk session JSON, refresh workspace/agent-transcripts enumeration, retry unresolved composer-model ids, refresh slug maps.
  • Usage lane (tick 1, then every 15s when an analytics key is present) — poll billed usage events and restamp conversation totals. Fast ticks reuse the last successful join.

Session files, log tails, and composer lookups are cached between ticks; unchanged sources cost stat only (no readFile / readWindow). Fs work is capped at 24 concurrent operations. --once and --json still run a single cold collect in a fresh process — their default output is unchanged.

How the hierarchy is built

  1. Explicit parentId from session metadata wins when present.
  2. PID -> PPID from the OS process table is used when metadata is missing.
  3. Root fallback - any node with no resolvable parent is rendered at the top level.

This means subagents appearing at root level always indicate that the session metadata was missing a parentId for them - the troubleshooter agent walks the user through filing a Cursor-side report when this happens.

Development

cd plugins/zoto-cursor-top
pnpm install
pnpm test           # vitest
pnpm bench          # vitest bench (collector + render; see bench/BASELINE.md)
pnpm validate       # structural validation
pnpm build          # tsup -> dist/cli.js
pnpm start          # run via tsx
pnpm demo           # run --demo via tsx

Benchmarks: pnpm bench runs scale fixtures at tiers S/M/L and prints per-tier [fs-ops] tables during setup. Canonical before/after numbers live in bench/BASELINE.md. For a fast warm-tick sample without the full bench loop, use pnpm exec tsx bench/quick-warm-metrics.mjs. For windowed render timings, use pnpm exec tsx bench/quick-render-window.mjs.

Project layout:

plugins/zoto-cursor-top/
├── .cursor-plugin/plugin.json
├── agents/zoto-cursor-top-troubleshooter.md
├── bin/cursor-top.mjs                # global-install entry point
├── commands/zoto-cursor-top.md
├── commands/zoto-cursor-top-init.md
├── docs/
├── rules/zoto-cursor-top.mdc
├── scripts/
│   ├── install-local.ts
│   ├── uninstall-local.ts
│   └── validate-plugin.ts
├── skills/zoto-cursor-top-monitor/
│   ├── SKILL.md
│   └── evals/evals.json
├── src/
│   ├── cli.ts                        # entry point
│   ├── types.ts                      # shared types
│   ├── discovery/                    # process / session / log / hierarchy
│   └── ui/                           # Ink components
└── tests/                            # vitest

License

MIT.