I run a lot of Claude Code sessions at the same time. Backend refactor in one terminal, tests in another, docs in a third, frontend in a fourth. It works great until it doesn't.
The problems show up fast:
- One session is blocked waiting for permission approval. I don't know which tab.
- One's burning through tokens at $4/hr. I can't see that without switching to it.
- One's idle for 15 minutes
- I'm tab-hunting instead of coding.
Claude Code is great at execution. It's not built to manage all these parallel coding sessions. Built the missing layer
claudectl
A terminal dashboard that monitors every running Claude Code session from one pane.
It auto-discovers all running sessions and shows live status, cost, burn rate, CPU usage, context window percentage, and token counts. No config needed. Just run
claudectl.
The problems it solves
Which session is blocked?
Sessions needing approval show as "Needs Input" in magenta. Press y to approve without switching terminals.
Which session is burning money?
Every session shows a live $/hr burn rate. Set a budget with --budget 5 --kill-on-budget and it auto-kills sessions that exceed it.
Which session is stalled?
Status detection uses multiple signals — CPU usage, JSONL conversation events, and message timestamps — not just one indicator. If CPU is zero but the last event says "processing," claudectl knows something is wrong.
How do I intervene without tab-hunting?
Press Tab to jump to any session's terminal. Press i to type input. Press d to kill. All from the dashboard.
How it works under the hood
claudectl is read-only. It never modifies Claude Code's files or behavior. It reads:
-
~/.claude/sessions/*.jsonfor session metadata -
~/.claude/projects/{slug}/*.jsonlfor conversation logs and token usage -
psoutput for CPU and memory per process
Status inference combines multiple signals rather than relying on any single one:
waiting_for_task event → Needs Input
CPU > 5% → Processing (overrides everything)
stop_reason: end_turn → Waiting
Last message > 10 min ago → Idle
Process exited → Finished
JSONL parsing is incremental — it tracks file offsets so it never rereads the full log. The binary is ~1MB with sub-50ms startup because it uses native ps instead of heavier system info crates.
Beyond monitoring
A few features that turned out to be more useful than I expected:
Event hooks — Run shell commands on session events. I use this to get Slack alerts when a session needs input:
toml
[hooks.on_needs_input]
run = "curl -X POST $SLACK_WEBHOOK -d '{\"text\": \"{project} needs approval\"}'"
[hooks.on_budget_warning]
run = "say '{project} hit 80% budget'"
Task orchestration — Define multi-session workflows with dependencies:
{
"tasks": [
{ "name": "Add auth", "cwd": "./backend", "prompt": "Add JWT middleware" },
{ "name": "Update tests", "cwd": "./backend", "prompt": "Update API tests", "depends_on": ["Add auth"] }
]
}
Highlight reels — Press R on any session to record a supercut of what it did: file edits, commands run, errors hit. Idle time and noise are stripped. Output is a shareable GIF.
Install
brew install mercurialsolo/tap/claudectl
# or
cargo install claudectl
Works on macOS and Linux. Supports Ghostty, Kitty, tmux, WezTerm, Warp, iTerm2, and Terminal.app.
What's next
The tool is at v0.9.1 now. The immediate roadmap is Windows/WSL support and expanding the hooks ecosystem. Longer term, I'm thinking about multi-agent monitoring beyond just Claude Code.
If you run multiple Claude Code sessions, give it a try. If you have ideas or run into issues, the repo is open:
GitHub: https://github.com/mercurialsolo/claudectl
---
_Built in Rust with ratatui. MIT licensed. The entire project was built with Claude Code._
Top comments (0)