At-a-glance status indicators for Claude Code and Codex sessions in your tmux tab bar.
Running multiple Claude Code sessions across tmux windows? claude-tmux-lights adds a small colored indicator next to each window name so you can see which sessions are working, which need your attention, and which are idle โ without switching windows.
1:backend ๐ข 2:frontend โ ๏ธ3m 3:tests โช 4:docs
โ โ โ โ
working needs input idle no session
| Indicator | Default | Meaning |
|---|---|---|
| Green | โ | Claude is actively working |
| Yellow + elapsed time | โ 3m | Claude finished, needs your attention (you haven't looked yet) |
| White | โ | Claude finished, you've seen it (acknowledged) |
| (none) | No Claude Code session in this window |
Yellow appears when Claude:
- Finishes a response and waits for your next prompt
- Needs you to approve a tool permission
- Asks you a question (elicitation dialog)
Switching to that tmux window auto-acknowledges it (yellow becomes white). Submitting a new prompt turns it green again.
Option A: Development / testing (session-only)
claude --plugin-dir /path/to/claude-tmux-lightsOption B: Permanent install via marketplace
# Inside Claude Code:
/plugin marketplace add mpercy/claude-tmux-lights
/plugin install claude-tmux-lights@claude-tmux-lightsThen configure tmux by running the setup command inside Claude Code:
/claude-tmux-lights:setup
The plugin will also nudge you to run setup if it detects tmux isn't configured.
Add to your ~/.tmux.conf:
set -g @plugin 'mpercy/claude-tmux-lights'Press prefix + I to install. The plugin auto-configures your window-status-format.
Note: TPM handles the tmux side only. You still need the Claude Code plugin installed for the hooks to fire.
If you prefer not to use TPM or the setup command, add the renderer call to your tmux config directly:
# In ~/.tmux.conf (adjust the path to where you cloned the repo)
setw -g window-status-format ' #I:#W#(python3 /path/to/claude-tmux-lights/scripts/tmux_render.py #{window_id} #{window_active}) '
setw -g window-status-current-format ' #I:#W#(python3 /path/to/claude-tmux-lights/scripts/tmux_render.py #{window_id} #{window_active}) 'Reload with tmux source-file ~/.tmux.conf or prefix + R.
Codex (0.145+) uses the same hook architecture as Claude Code, so the same plugin lights up Codex tabs. Install:
codex plugin marketplace add ~/src/claude-tmux-lights
codex plugin add claude-tmux-lights@claude-tmux-lightsThen run /hooks once inside a Codex session to trust the hook commands
(Codex records trust against each command's hash; re-approve after changing
the hook manifest). Lights behave identically to Claude sessions, with one
addition: Codex's PermissionRequest event turns the light yellow while a
command waits for approval. Codex has no idle-prompt event, so the
interrupt-recovery transition is Claude-only.
Unlike Claude Code, which runs plugins in place from wherever you cloned the
repo, Codex copies the plugin into its own cache
(~/.codex/plugins/cache/claude-tmux-lights/...) at plugin add time โ
codex plugin list's PATH column shows the marketplace source, not this
cache path, so it's easy to miss. That means repo edits (e.g. git pull,
local hook changes) are not picked up automatically on the Codex side;
after updating the repo, re-run
codex plugin add claude-tmux-lights@claude-tmux-lights to refresh the
cached copy.
The plugin has two components:
Hook handler (scripts/hook_handler.py) โ A Claude Code plugin hook that writes per-session JSON state files whenever Claude starts working, stops, needs permission, or ends a session. It listens to 8 hook events:
| Hook | Effect |
|---|---|
SessionStart |
Creates state file (white/idle) |
UserPromptSubmit |
Sets state to active (green) |
PostToolUse |
Restores active after permission approval |
Stop |
Sets state to waiting (yellow) |
Notification(permission_prompt) |
Sets state to waiting (yellow) |
Notification(elicitation_dialog) |
Sets state to waiting (yellow) |
Notification(idle_prompt) |
Interrupt recovery (white) |
PermissionRequest |
Codex-only; sets state to waiting (yellow) while a command awaits approval |
SessionEnd |
Deletes state file |
Renderer (scripts/tmux_render.py) โ Called by tmux every status-interval (typically 2s) for each window tab via #() interpolation. Reads state files, determines display, and outputs tmux-formatted indicator strings.
State files live in $XDG_RUNTIME_DIR/claude-tmux-lights/ or ~/.local/state/claude-tmux-lights/.
SessionStart โโโบ white โ (idle)
โ
UserPromptSubmit
โ
โผ
green โ (active) โโโโ PostToolUse (if was waiting)
โ โฒ
โผ โ
Stop / permission_prompt โโโบ yellow โ (waiting)
โ
[switch to window]
โ
โผ
white โ (viewed)
- Ghost cleanup: If Claude Code is killed without
SessionEndfiring, the nextSessionStartin the same pane evicts the stale state file. The renderer also deduplicates by pane as a safety net. - Orphan cleanup: Waiting sessions older than 30 minutes (configurable) are automatically removed by the active window's renderer.
- Interrupt recovery: When you press Esc to cancel Claude, the
Stophook doesn't fire. After ~60 seconds theidle_promptnotification fires, which transitions to white (auto-acknowledged, since you were present when you cancelled). - Atomic writes: All state file writes use
tempfile+os.rename()for POSIX atomicity โ no partial reads.
Create ~/.config/claude-tmux-lights/config.json to customize icons, colors, and behavior. All fields are optional โ only include what you want to override:
{
"active_icon": "๐ข",
"waiting_icon": "โ ๏ธ",
"viewed_icon": "โช",
"active_color": "colour46",
"waiting_color": "colour226",
"viewed_color": "colour252",
"show_elapsed_time": true,
"max_indicators_per_window": 5,
"orphan_threshold_seconds": 1800
}| Key | Default | Description |
|---|---|---|
active_icon |
โ |
Icon when Claude is working |
waiting_icon |
โ |
Icon when Claude needs attention |
viewed_icon |
โ |
Icon when idle/acknowledged |
active_color |
colour46 (green) |
tmux color for active icon |
waiting_color |
colour226 (yellow) |
tmux color for waiting icon |
viewed_color |
colour252 (light grey) |
tmux color for viewed icon |
show_elapsed_time |
true |
Show time since Claude stopped next to waiting icon |
max_indicators_per_window |
5 |
Max indicators per window (for multiple panes) |
orphan_threshold_seconds |
1800 |
Seconds before stale waiting sessions are cleaned up |
Config changes take effect on the next tmux status refresh (every status-interval seconds) โ no restart needed.
- tmux (any modern version)
- Python 3.6+
- Claude Code with plugin support
No pip packages. No node modules. Zero external dependencies.