tmux-scout

May 24, 2026 · View on GitHub

tmux-scout feature overview

A tmux plugin for monitoring and navigating Claude Code, Codex, Gemini CLI, Kimi CLI, GitHub Copilot CLI, OpenCode, Cursor Agent, Hermes, and Trae CLI sessions. Provides a real-time fzf picker to jump between agent panes, a status bar widget showing session counts, and crash detection for dead sessions.

中文文档

If you prefer a zero-dependency setup without needing a Node.js runtime, check out this awesome Golang port created by @ianchesal:

👉 tmux-scout-golang

Features

  • Session pickerprefix + O opens an fzf popup listing all active agent sessions with status tags (WAIT / BUSY / DONE / IDLE), tmux window names, project names, prompt titles, and live tool details
  • Pane preview — right-side preview panel shows the last 40 lines of each session's tmux pane
  • Status bar widget — displays session counts by status (e.g. 0|1|2) in tmux's status-right, refreshed every 2 seconds
  • Auto-refreshCtrl-T toggles automatic picker reload every 2 seconds
  • Crash detection — dead processes and stale Codex JSONL files are automatically detected and cleaned up

Requirements

  • tmux >= 3.2
  • Node.js >= 16
  • fzf >= 0.51 (with --listen and --tmux support)

Installation

With TPM

Add to ~/.tmux.conf:

set -g @plugin 'qeesung/tmux-scout'

Then press prefix + I to install.

Manual

git clone https://github.com/qeesung/tmux-scout.git ~/.tmux/plugins/tmux-scout

Add to ~/.tmux.conf:

run-shell ~/.tmux/plugins/tmux-scout/tmux-scout.tmux

Reload tmux: tmux source ~/.tmux.conf

Hook Setup

tmux-scout needs hooks installed in the agent CLIs you want to track. Run the setup command after installation:

# SCOUT_DIR is set automatically when the plugin loads — these commands can be copy-pasted directly
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" install

# Other operations
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" install --claude   # Claude Code only
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" install --codex    # Codex only
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" install --gemini   # Gemini CLI only
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" install --kimi     # Kimi CLI only
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" install --copilot-cli  # GitHub Copilot CLI only
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" install --opencode # OpenCode only
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" install --cursor   # Cursor Agent only
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" install --hermes   # Hermes only
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" install --trae     # Trae CLI only
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" uninstall          # Remove all hooks
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" status             # Check installation status
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" doctor             # Run environment diagnostics

The installer is idempotent — running it multiple times is safe. If you move the repository, re-running install will automatically update hook paths. Without an agent flag, install, uninstall, and status operate on all supported integrations. Use an agent flag to scope the operation.

What gets modified

  • Claude Code: Adds a hook entry to each of the 9 supported Claude hook event types in ~/.claude/settings.json
  • Codex: Adds event hooks for SessionStart, UserPromptSubmit, PreToolUse, PermissionRequest, PostToolUse, and Stop in ~/.codex/hooks.json, enables Codex hook features/trust state in ~/.codex/config.toml, and keeps the legacy notify hook as a fallback for older Codex builds (original notify command is backed up and chained)
  • Gemini CLI: Adds command hooks in ~/.gemini/settings.json
  • Kimi CLI: Appends managed [[hooks]] blocks to ~/.kimi/config.toml while preserving unrelated TOML content
  • GitHub Copilot CLI: Adds command hooks in ~/.copilot/settings.json
  • OpenCode: Writes ~/.config/opencode/plugins/tmux-scout-opencode-plugin.js and registers it in the OpenCode JSON config
  • Cursor Agent: Adds command hooks in ~/.cursor/hooks.json
  • Hermes: Adds command hooks in ~/.hermes/cli-config.yaml or an existing ~/.hermes/config.yaml
  • Trae CLI: Adds command hooks in ~/.trae/traecli.yaml or an existing legacy config file

Usage

Picker

Press prefix + O (default) to open the session picker.

KeyAction
EnterJump to selected session's pane
Ctrl-DShow selected session details
Ctrl-RRefresh session list
Ctrl-TToggle auto-refresh (every 2s)
EscClose picker

Each line shows:

* BUSY   claude app-window            my-project       "implement the login page"  Bash: npm test
  • * — current pane indicator
  • W:APP / W:ANS / W:PLAN — waiting for approval, answer, or plan confirmation
  • BUSY / DONE / IDLE — session status
  • INT / CRASH / STALE — recently interrupted, crashed, or stale sessions
  • Agent type (claude / codex / gemini / kimi / copilot-cli / opencode / cursor / hermes / trae)
  • tmux window name (- when no window is linked)
  • Project directory name
  • Session title (first prompt)
  • Current tool details (for working sessions)

Supported Agents and Colors

tmux-scout currently supports the agent CLIs below. The color sample is the actual foreground color used for the agent label in tmux/fzf, shown with the standard 256-color terminal palette. Your terminal theme may render the final color slightly differently. Run npm run agent-colors to preview the same colors in your terminal.

AgentPicker labelSetup flagUser-visible color
Claude Codeclaude--claude#d7875f warm orange (#d7875f)
Codexcodex--codex#00af87 green (#00af87)
OpenCodeopencode--opencode#ffaf87 peach (#ffaf87)
Gemini CLIgemini--gemini#5f87ff blue (#5f87ff)
GitHub Copilot CLIcopilot--copilot-cli#875fff purple (#875fff)
Cursor Agentcursor--cursor#eeeeee near-white (#eeeeee)
Kimi CLIkimi--kimi#949494 gray (#949494)
Hermeshermes--hermes#d7af87 sand (#d7af87)
Trae CLItrae--trae#5fff87 bright green (#5fff87)

Status Bar

The status widget is not automatically injected — you need to add it manually. The plugin sets a SCOUT_DIR environment variable at load time, so you can use $SCOUT_DIR to reference the widget script regardless of install location.

Without a theme plugin, add to ~/.tmux.conf:

set -g status-right '#($SCOUT_DIR/scripts/status-widget.sh) #S'
set -g status-interval 2

With a theme plugin (e.g. minimal-tmux-status), directly setting status-right won't work because the theme overrides it. Use the theme's own option instead:

# minimal-tmux-status
set -g @minimal-tmux-status-right '#($SCOUT_DIR/scripts/status-widget.sh) #S'

The widget shows:

W|B|D

Where W = waiting for attention (red), B = busy/working (yellow), D = done/completed (green). An optional I = idle (blue) appears when idle sessions exist.

When tmux mouse mode is enabled, clicking the tmux-scout status segment opens the same picker as prefix + O:

set -g mouse on

tmux-scout does not enable mouse mode for you. The clickable segment is subtly underlined by default. In the picker, single-click selects a row and double-click jumps to it.

Configuration

Keybinding

set -g @scout-key "O"    # default: O (prefix + O)

Status Bar

set -g @scout-status-format '{W}/{B}/{D}'         # custom separators
set -g @scout-status-format '{W} wait {B} busy'   # with labels

Placeholders: {W} wait, {B} busy, {D} done, {I} idle, {A} approval waits, {Q} question/answer waits, {P} plan waits, {T} total active sessions.

Status click behavior:

set -g @scout-status-click on      # default: clickable status segment
set -g @scout-status-click off     # plain text status segment
set -g @scout-status-click force   # override an existing MouseDown1Status binding

With the default on, tmux-scout only installs its click binding when MouseDown1Status is unset, still tmux's default, or already owned by tmux-scout.

Optional mouse UI tuning:

set -g @scout-status-click-style underscore   # default clickable hint
set -g @scout-status-click-style off          # disable the underline hint

Watchdog

By default, tmux-scout starts a tmux-managed watchdog so session state stays current even when the picker/status bar is not refreshing. To disable background reconciliation:

set -g @scout-watchdog off

This is not a launchd/systemd daemon. It is a single tmux-owned Node.js process that exits when the option is turned off or tmux is gone. The watchdog uses a hybrid loop:

  • fast lifecycle/pane checks and incremental Codex JSONL reads every 2s
  • Codex JSONL discovery every 30s
  • full reconcile every 60s

When the watchdog is running, it also starts a local single-writer bridge at ~/.tmux-scout/run/bridge.sock. Agent hooks prefer sending updates to that Unix socket so one process serializes status writes; if the socket is unavailable, hooks fall back to direct atomic file writes.

Optional intervals, in seconds:

set -g @scout-watchdog-interval 2
set -g @scout-watchdog-discovery-interval 30
set -g @scout-watchdog-full-interval 60

Watchdog diagnostics:

eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" watcher status
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" watcher once --full
eval "$(tmux show-env -g SCOUT_DIR)" && "$SCOUT_DIR/scripts/setup.sh" watcher stop

watcher status includes the bridge state, latest tick mode, duration, reconcile change count, Codex JSONL files read, parsed event count, and JSONL parse errors when present.

Data Storage

Session data is stored in ~/.tmux-scout/:

~/.tmux-scout/
├── status.json                      # Aggregated session index
├── sessions/                        # Per-session JSON files
│   ├── {session-id}.json
│   └── ...
├── watcher.pid                      # Watchdog process lock
├── watcher-state.json               # Watchdog JSONL offsets/cache
├── watcher.log                      # Watchdog diagnostics
├── run/bridge.sock                  # Watchdog single-writer Unix socket
├── codex-hooks-manifest.json        # Codex event hook trust keys owned by tmux-scout
└── codex-original-notify.json       # Backup of original Codex notify command

tmux-scout keeps live and recently visible sessions in the registry. Hidden internal sessions and terminal STALE / CRASH rows are removed after their short display window, while snapshots with an explicit endedAt are retained for up to 24 hours before cleanup.

Agent Compatibility Notes

tmux-scout now prefers Codex's event hook mechanism, which gives near-real-time updates for session start, prompt submission, tool activity, approval waits, and turn completion. This is the same style of lifecycle tracking used by Flux Desktop App.

With the default watchdog path, tmux-scout keeps hooks as the primary state source and adds Flux-style reconciliation: process/pane lifecycle checks, tail-only Codex transcript reads with cached offsets, registry pruning, and periodic full reconcile. It does not repeatedly reread every transcript on the fast path.

Internally, hook, pane, transcript, PID, and stale-timeout observations are reduced through a shared session-state model. Higher-confidence hook/PID events win over lower-confidence pane/transcript observations for short races, while terminal crash/stale events still close dead sessions.

For older Codex versions that only support notify, tmux-scout still installs and chains the legacy notify hook. In that fallback mode, first-turn discovery may still depend on JSONL polling until Codex emits a completion notification.

Gemini CLI, Kimi CLI, GitHub Copilot CLI, OpenCode, Cursor Agent, Hermes, and Trae CLI are tracked through a generic hook adapter. It maps their hook/plugin events onto the same session lifecycle model, so support quality depends on the payloads those CLIs expose for prompts, tool calls, approvals, questions, subagents, and completion.

Development

Project internals:

npm run check   # Syntax-check project JavaScript files
npm test        # Run focused unit tests
npm run ci      # Run both checks

Flow Fixtures and Debugging

Agent lifecycle regressions can be captured as JSON flow fixtures under tests/fixtures/flow/<agent>/. Each fixture replays real hook entrypoints into an isolated HOME and asserts the final session snapshot, state contract, and expected evidence stream.

Useful debug commands:

node scripts/debug.js list
node scripts/debug.js show <session-id> --plain
node scripts/debug.js evidence <session-id>
node scripts/debug.js inject --session-id debug-wait --agent codex --phase waitingForApproval
node scripts/debug.js replay tests/fixtures/flow/claude/approval.json --show

The same commands are available through scripts/setup.sh debug ... when you want tmux-scout to restore the PATH captured from tmux.

License

MIT