I built a macOS menu bar app to monitor all my Claude Code sessions — here’s how it works

I was running 10+ Claude Code sessions across Zed and Ghostty — one terminal tab per session — and kept switching between them to check which one was waiting for permission and which was done. It got old fast.

I looked at existing monitoring tools but they all wanted me to launch sessions from inside their app. I didn’t want to change my workflow. I just wanted to keep using my own terminals and have something tell me what was going on.

So I built c9watch.

What it does

c9watch is a native macOS menu bar app that discovers every running Claude Code session on your machine automatically. No plugins, no setup — it scans processes at the OS level, so it works with whatever terminal or IDE you’re already using.

Dashboard

Sessions are grouped by status — Working, Needs Permission, Idle, Done — so permission requests surface to the top. You can also group by project (with git branch info), expand any session to read the full conversation, stop or rename sessions, and jump straight to the parent terminal.

A few other things it does:

  • Tray popover — click the menu bar icon for a quick overview without opening the full dashboard
  • Conversation viewer — formatted markdown, code blocks, inline images, and a sidebar to navigate
  • Session history — search across all past sessions by keyword
  • Cost tracker — daily, per-project, per-model spending breakdowns
  • Mobile client — scan a QR code to check sessions from your phone via WebSocket
  • macOS notifications — get alerted when an agent needs your attention

How it works under the hood

Process discovery

A background thread polls every 2 seconds using sysinfo, scanning for running claude processes. Each process is matched to its session file in ~/.claude/projects/ via path encoding and timestamp correlation.

The last N entries of each session’s JSONL file are parsed to determine status:

  • Working — Claude is generating a response or executing tools
  • Needs Permission — a tool call is pending user approval
  • Idle — waiting for your next prompt

Status updates are pushed to the Svelte frontend via Tauri events. The UI reactively updates and sorts sessions by priority.

Session history and search

c9watch reads ~/.claude/history.jsonl for the session index, then scans individual JSONL files across all project directories for deep content search. The text extraction logic carefully distinguishes between user prompts and tool results — only actual user-typed content gets indexed, preventing noisy matches on metadata.

Results link back to the conversation viewer with scroll-to-match highlighting.

Cost tracking

Assistant message metadata in JSONL files contains model usage and token counts. The cost calculator uses per-model pricing tables and caches results by file mtime, so unchanged sessions are never re-scanned.

One tricky part: Anthropic’s model IDs include date suffixes like claude-sonnet-4-5-20250929. The pricing lookup normalizes these by stripping the suffix before matching against the pricing table.

The tray popover (NSPanel)

The menu bar popover uses macOS NSPanel with specific collection behaviors — full_screen_auxiliary(), can_join_all_spaces(), stationary() — so it appears above fullscreen apps without stealing focus. Getting the drag region to coexist with clickable tab buttons in the title bar required setting -webkit-app-region: no-drag on individual buttons while keeping the surrounding area draggable.

Tech stack

Layer Technology
Desktop framework Tauri 2
Frontend SvelteKit + Svelte 5
Backend Rust
Process discovery sysinfo
Design system Vercel Noir (true black, Geist fonts)

Why Tauri over Electron

You’re already running a bunch of Claude Code agents eating up memory. The monitoring tool shouldn’t add to that. Tauri gives you a native binary with minimal overhead — Rust handles the heavy lifting (process scanning, JSONL parsing) at native speed, and Svelte compiles away the framework overhead on the frontend.

Development process

I built c9watch with Claude Code itself. Most of the Rust backend and Svelte frontend was AI-assisted — I’d describe what I wanted, review the output, test it, and iterate. The architecture decisions (Tauri over Electron, OS-level scanning, JSONL parsing strategy) were mine, but Claude Code did the heavy lifting on implementation.

Open source

MIT license, no telemetry, fully open source.

If you’re running multiple Claude Code sessions and losing track of what’s happening, give it a try. Feedback, issues, and PRs are all welcome.

Leave a Reply