tmux-claude-status/

Know which Claude session needs you.

Run several Claude Code sessions across tmux windows and they all look identical from the tab bar. This puts one badge on each tab, so a glance tells you what's working, what's waiting on you, and what's ready to read.

A tmux status bar with five tabs. Badges appear and change as sessions work, become blocked, and finish.
Real tmux, real plugin — five windows changing state.

Four states

Each tab says one thing

The distinction that matters most is the third one. A turn ending isn't the same as the work being finished — Claude often ends a turn with a build or a test suite still running in the background, and a tab that says ready when it isn't will teach you to ignore the badges entirely.

·idle Nothing running, nothing unread
🔄running Prompt in flight — Claude is working
question Blocked on you: a permission prompt, a question, or an MCP dialog
shells Turn finished, but background shells it started are still running
done Turn finished, nothing running, ready to read

Only the checkmark ever clears

🔄, ❓ and ⏳ describe live state. Looking at a window doesn't answer a question or finish a build, so visiting a tab leaves them alone.

✅ clears when you arrive at the window, and also when you leave a window it appeared in — because that's the point at which you've actually had your chance to read it. Sitting in a window when a turn ends is not the same as having read the answer.

Install

Two halves, joined by one tmux option

The state half sets @claude_status on the window that owns the session. The render half turns that into a badge. They don't know about each other, which is why either can be swapped.

1. The renderer

With TPM, in ~/.tmux.conf:

set -g @plugin 'dop-amine/tmux-claude-status'

Or without a plugin manager:

run-shell ~/path/to/claude-status.tmux

2. The hooks

As a Claude Code plugin — no JSON editing, no absolute paths:

/plugin marketplace add \
  dop-amine/tmux-claude-status
/plugin install \
  tmux-claude-status@tmux-claude-status

Then restart your Claude sessions.

Restarting matters. Hooks are snapshotted when a session starts, and nothing reloads them — not /hooks, which is a read-only viewer, and not installing the plugin. claude --resume restarts the process while keeping the thread.

Using "Oh my tmux!"?

gpakosz rebuilds the status formats after sourcing your overrides, so it needs one extra step — the plugin publishes a fragment you splice in yourself. Both format variables need it, including the _current_ one for the selected tab. Setup guide →

Configuration

OptionDefaultPurpose
@claude_badge_running' 🔄 'The glyph for each state. Swap for single-width characters if emoji clip in your terminal.
@claude_badge_question' ❓ '
@claude_badge_shells' ⏳ '
@claude_badge_done' ✅ '
@claude_badge_auto_appendonAppend the badge to both window status formats automatically.
@claude_badge_clear_on_visit10 makes ✅ persist until your next prompt instead.
@claude_badge_toggle_keyunsetBind a prefix key to flip the ✅ lifecycle at runtime.

Requires tmux 3.1+ and Claude Code 2.1.x. No dependencies — POSIX shell and tmux.

Internals

Most of what this relies on isn't documented

The behaviour underneath was found by reading strings out of the Claude Code binary and running controlled experiments. It's written down in the repo, with the method for each claim, so it can be re-checked when a release changes something.

The tmux bug worth stealing

Clearing the badge when you visit a tab looks like a one-liner with after-select-window. It never fires. That's a command hook, and clicking a tab in the status bar runs switch-client, not select-window. The fix is the session-window-changed event hook.

It passed testing, too — the test drove select-window directly, which is the one path real usage never takes. Testing a UI behaviour by invoking the command you assume it runs only confirms your assumption back to you. The test suite now asserts on what a tab actually renders.