Show the live status of your Claude Code
sessions in tmux: every window gets a badge (status bar and choose-tree
window picker) telling you whether Claude is working, waiting for your
decision, idle, or stopped on an error — plus a global summary and a
shortcut to jump straight to the session that needs you.
| Status | Badge (default) | Meaning |
|---|---|---|
| working | ✻ animated, orange |
Claude is processing a prompt / running tools |
| need input | ? 3m bold orange |
Claude is waiting for a permission — with waiting time |
| waiting | ● orange |
Turn finished, Claude waits for a prompt |
| error | ✗ red |
Claude stopped on an error (StopFailure) |
Windows without a Claude session show nothing. The waiting badge ages:
● shrinks to • after 5 minutes and to a dimmed · after 30 minutes,
so sessions you have not checked in a while fade out visually.
Global summary pill and session flavor in the status bar:
Per-window badges in the choose-tree window picker:
Claude Code hooks (settings.json) call scripts/claude-status-hook.sh
on every event. The script sets tmux user options:
@claude_pane_status(pane) — pane status@claude_pane_status_since(pane) — epoch of the last change@claude_pane_pid(pane) — claude process PID (for liveness)@claude_pane_flavor(pane) — config dir name (claude,claude-work, …), handy because the Claude Code binary is named after its version number@claude_win_status(window) — aggregate, priorityneed_input>error>working>waiting
Companion daemon (scripts/claude-status-daemon.sh) — started on demand
by the hook, child of the tmux server (spawned via run-shell -b, dies
with it), single instance (mkdir lock), exits by itself when no Claude
session remains. It handles everything that needs time or computation:
- animation:
@claude_spinner_frameadvances at ~2.5 fps while ≥1 window is working AND ≥1 client is attached - liveness: one batched
psper sweep (~5 s) checks that each@claude_pane_pidis alive and on its pane's tty (robust against kill -9 and PID reuse); otherwise clears the status - durations:
@claude_win_wait(“3m”) for need_input windows - global summary:
@claude_status_summary(“?1 ✗1 ✱3 ●2”), counted per window, zeros omitted
Badges are composed by claude-status.tmux; reference them with
#{E:...}: @claude_badge_win, @claude_badge_pane,
@claude_badge_summary.
No phantom working: only UserPromptSubmit (a genuine turn start) moves
a session from waiting to working. Tool events
(PreToolUse/PostToolUse/PreCompact) set a “soft” working that never
overrides a waiting — essential because hooks run in parallel and these
events can arrive after the end-of-turn Stop (background agents run tools
too). SubagentStop is intentionally not wired: a subagent finishing says
nothing about the main loop state.
No phantom need_input: need_input comes from Notification
(notification_type=permission_prompt), which only fires when the question
actually reaches the user — NOT from PermissionRequest, which fires for
every request including ones auto-approved by another hook (a long tool
would keep a phantom ? for its whole duration).
set -g @plugin 'farnots/tmux-claude-status'Press prefix + I to install. Load it after your theme (e.g. catppuccin)
so the plugin inherits its colors.
git clone https://github.com/farnots/tmux-claude-status ~/.tmux/plugins/tmux-claude-status# in ~/.tmux.conf, ideally AFTER catppuccin (inherits @thm_red for errors)
run-shell ~/.tmux/plugins/tmux-claude-status/claude-status.tmuxMerge the blocks from
examples/claude-settings-hooks.json
into the hooks section of ~/.claude/settings.json (append at the end of
each event array, without touching your existing hooks — hooks of the same
event run in parallel).
# Window list (catppuccin example):
set -g @catppuccin_window_text "#{E:@claude_badge_win}<your existing format>"
set -g @catppuccin_window_current_text "#{E:@claude_badge_win}<your existing format>"
# Vanilla window-status-format:
set -g window-status-format " #I: #{E:@claude_badge_win}#W "
# Global summary at the head of status-right (auto-renders as a catppuccin
# "pill" when the theme is loaded, plain orange text otherwise):
set -g status-right "#{E:@claude_badge_summary}<your existing status-right>"
# choose-tree (prefix+w) — window and pane branches:
bind w choose-tree -Zw -F "#{?pane_format,#{E:@claude_badge_pane}...,#{?window_format,#{E:@claude_badge_win}...,...}}"Set these before the plugin's run-shell:
set -g @claude_status_color "#D97757" # Claude orange (default)
set -g @claude_status_error_color "red" # default: catppuccin @thm_red, else ANSI red
set -g @claude_status_icon_working "✱" # static fallback when no animation
set -g @claude_status_icon_need_input "?"
set -g @claude_status_icon_waiting "●"
set -g @claude_status_icon_waiting_old "•" # waiting for > 5 min
set -g @claude_status_icon_waiting_stale "·" # waiting for > 30 min (dimmed)
set -g @claude_status_waiting_old_secs 300 # aging thresholds (seconds)
set -g @claude_status_waiting_stale_secs 1800
set -g @claude_status_icon_error "✗"
set -g @claude_status_summary_icon "..." # default: nerd-font robot
set -g @claude_spinner_frames "✻ ✳ ✶ ✱" # animation frames, space-separated
set -g @claude_status_jump_key "C" # prefix+C → next need_input window
# (then error), cycling; empty = no binding- An already-open
choose-treedoes not refresh (no animation nor status changes); it is up to date each time it opens. - Animation pauses when no client is attached (nothing to animate).
- After a
kill -9of claude, the badge disappears at the next daemon sweep (≤ ~5 s), not instantly. - The
? 3mduration refreshes at sweep pace (~5 s). - If you answer a permission prompt and the following tool runs for a long
time, the
?can persist until that tool'sPostToolUsefires.
- tmux ≥ 3.4 (pane-scoped user options,
pane-exited/after-kill-panehooks) - Claude Code with hooks support
- No dependencies (POSIX sh, sed, awk, ps)
- A Nerd Font for the default summary icon and powerline separators
Apache 2.0 — attribution required (see NOTICE)

