I run Claude Code for almost everything now. On a typical day I have five or six iTerm2 tabs open — one refactoring a module, one writing tests, one scaffolding something new.
Here's the thing nobody warns you about: when one session finishes, you have no idea which tab it is.
So you click through them. One by one. Like scratching lottery tickets, except the prize is just finding which Claude is sitting there waiting for you. Meanwhile, the other sessions keep running, and you've just wasted five minutes of parallelism you'll never get back.
What I tried first
iTerm2's built-in idle notification. It tells you "a session went idle." Great — but which one? With six tabs open, you're still guessing.
terminal-notifier via Claude Code hooks. Better — you can show the project name in a macOS banner. But banners disappear in a few seconds. If you're heads-down in another tab, you miss it. Gone. Back to clicking through tabs.
What I actually wanted was dumb simple: make the tab itself tell me. Visually. In a way I literally cannot miss.
What I built
claude-code-iterm2-tab-status — a Claude Code plugin that puts a live status prefix on each iTerm2 tab.
Three states:
| State | Prefix | What happens |
|---|---|---|
| Running — Claude is working | ⚡ | Quiet prefix, that's it |
| Idle — Claude finished | 💤 | Quiet prefix |
| Attention — needs your permission | 🔴 | Tab flashes orange + shows a badge |
Only the attention state actually flashes. Running and idle are just quiet indicators. Glance at your tab bar and you know exactly what's happening everywhere.
When you click on a flashing tab, it stops. When you type your next prompt, it goes back to ⚡. Your original tab color, title, and badge are saved and restored.
How it works
Claude Code hooks → JSON signal file → iTerm2 adapter → tab status
Two things made this possible:
Claude Code has a hooks API.
Notificationhooks fire onidle_promptandpermission_prompt.UserPromptSubmitfires when you send a new message. They're official, documented, and run a shell command with event data on stdin.iTerm2 has a Python API. You can set tab colors, titles, badges — all programmatically, per-session. And you can match sessions by TTY.
The bridge is a signal file. When Claude's state changes, the hook writes a small JSON file to /tmp/claude-tab-status/ with the session's TTY and current state. On the iTerm2 side, a Python script runs as an AutoLaunch script, polls that directory every second, matches TTY → tab, and updates accordingly.
No screen scraping. No regex on terminal output. No fragile hacks.
Install
It's a Claude Code plugin. Two commands:
# Register the marketplace
/plugin marketplace add JasperSui/jaspersui-marketplace
# Install
/plugin install iterm2-tab-status@jaspersui-marketplace
On first session start, the plugin automatically sets up the iTerm2 Python runtime and deploys the adapter. Just restart iTerm2 once and you're done.
Configuration
Run /iterm2-tab-status:config inside Claude Code — it walks you through everything interactively. Flash color, prefixes, badge text, macOS notifications. Settings live in ~/.config/claude-tab-status/config.json and hot-reload within about a second.
If you prefer env vars:
export CLAUDE_ITERM2_TAB_STATUS_COLOR_G=0 # red flash instead of orange
export CLAUDE_ITERM2_TAB_STATUS_NOTIFY=true # also send macOS notifications
What I'd do differently
The signal file approach turned out to be a good call. It keeps the Claude Code side and the iTerm2 side completely decoupled. If someone wants to write an adapter for Kitty or WezTerm, they just need to read the same JSON files. The signal format is stable and documented.
What I underestimated was TTY matching edge cases. If you split panes or rearrange sessions, the TTY mapping can get stale. A restart fixes it, but I'd like to make that more robust.
Try it out
Repo: github.com/JasperSui/claude-code-iterm2-tab-status
If you're running multiple Claude Code sessions in iTerm2, give it a shot. I'd genuinely like to know if the TTY matching holds up on setups other than mine — I've only tested on macOS with iTerm2 3.6.x so far.
And if you use a different terminal with a scriptable API, PRs for new adapters are very welcome!
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.