Skip to content

Process Manager

⌘⌥P opens the Process Manager — a full-screen overlay that aggregates every process in every descendant tree of every shell, across every workspace.

┌─ Process Manager ────────────────────────── 47 processes · 12.3% CPU · 1.2 G RSS ─┐
│ │
│ ▼ Workspace: build │
│ ▼ surface:1 ~/code/foo port chips: :3000 :8080 │
│ PID Command CPU% RSS [kill] │
│ 12345 *bun run dev 4.2% 180M [kill] │ ← * = foreground
│ 12346 esbuild 0.8% 42M [kill] │
│ ▶ surface:2 ~/code/bar │
│ │
│ ▶ Workspace: docs │
└────────────────────────────────────────────────────────────────────────────────────┘
ColumnWhat
PIDProcess id. Foreground row is highlighted with the accent color.
CommandFull argv — bun run dev, python3 -m http.server 8765, etc.
CPU %Instantaneous from ps %cpu; cell color heats up to red via color-mix.
MemoryRSS, formatted as K / M / G.
Actionkill button — SIGTERM by default. Shift+click for SIGKILL.

Above the workspace list: a summary (N processes · X.X% CPU · Y.Y M RSS).

The panel refreshes in place on every metadata change — no polling from the webview. Every emit from the SurfaceMetadataPoller (1 Hz when focused, ~3 Hz when hidden) flows through the same diff pipeline that drives the pane chips. If nothing changed, no re-render.

Each surface row collapses to show its cwd and a row of port chips. The chips are extracted from the lsof -iTCP -sTCP:LISTEN snapshot the poller runs once per tick — same source as the pane header chips. Click a chip to open http://localhost:<port> in a browser pane.

  • src/views/terminal/process-manager.ts — the overlay panel.
  • src/bun/surface-metadata.ts — the poller (used by the panel and CLI).
  • src/bun/rpc-handlers/surface.ts — surface.kill_pid, surface.kill_port.