Skip to content

ctretyak/tmux-agents-inbox

Repository files navigation

tmux-agents-inbox

See which Claude Code agents are waiting on you — done or needing input — across every tmux session, window, and pane. A status-line summary gives you the count at a glance, one key opens a popup listing all your Claude sessions with live, auto-refreshing status, and Enter jumps you straight to that exact pane (even in another tmux session). One more key jumps to the next waiting agent without opening the popup.

prefix + g  → popup                                            status-right
┌─────────────────────────────────────────────────────────┐    ⚡2 ⏳1 ✢1 ✓3
│ agents>                                                 │    (working/waiting/background/done)
│ ── Needs input (1) ──                                   │
│  ✻ my-app       feature-x      power-up design     1m   │    prefix + N → next waiting
│ ── Completed (1) ──                                     │    ctrl-s    → regroup
│  ✻ my-app                      title screen        9m   │    (state · session · flat)
│ ── Background (1) ──                                    │
│  ✢ my-app                      data export         2m   │
│ ── Working (1) ──                                       │
│  ✽ my-app                      collision system    5s   │
│                                                         │
│ enter: jump   ctrl-s: regroup   esc: close              │
└─────────────────────────────────────────────────────────┘
 columns: status icon · project · subfolder · description · age

How it works

Which sessions show up: every tmux pane currently running an interactive claude process — detected by mapping live Claude processes to their pane (a process-tree walk). So a session never vanishes while it's still alive, and sessions started before the hooks were installed still appear. (Background claude --bg sessions, the claude agents TUI, and the supervisor are deliberately excluded — those are agent view's job; see Background sessions.)

Status is recorded by a small Claude Code hook that runs inside each pane and writes a one-line state file keyed by the pane id (~/.cache/tmux-agents-inbox/pane-<id>). State mapping: SessionStart → idle (but source: compact keeps it working since the session is still active), UserPromptSubmit / PreToolUse / PostToolUse → working, Notification → waiting (idle_prompt reminders are ignored so a finished session stays Completed), Stop → done (or background if a background_tasks entry is still running), SessionEnd → removed. PostToolUse matters for the needs-input → working transition: answering an AskUserQuestion returns the tool (firing PostToolUse) before Claude resumes thinking, so the row clears immediately instead of sitting on Needs input through a long think. A live pane with no state file yet shows as idle.

When a hook record goes stale (after /compact, or for sessions that predate the install), the popup reconciles with live transcript activity: if the session's transcript was written in the last ~12 s the row is shown as working; otherwise the most recent transcript mtime drives the age column.

Description is the session's ai-title (the same auto-generated title Claude shows in --resume), read from the tail of the transcript so the cost is constant regardless of session length. Falls back to the tmux window name when no title has been generated yet.

Everything is computed on demand (no daemon): the popup rebuilds every 1 s while open (@agents-inbox-refresh-interval), the status line on tmux's status-interval. Jumping uses switch-client + select-window + select-pane on the pane id.

Done vs needs-input is best-effort. Claude Code's hooks fire Stop both when a turn finishes and when the agent asks a plain-text question, and Notification covers permission prompts and idle reminders. The inbox treats waiting and done together as "needs me" — which is what you actually want to act on.

Requirements

  • tmux ≥ 3.2 (uses display-popup)
  • fzf
  • bash (3.2+; macOS system bash is fine)
  • jq (only for install-hooks.sh; the hook itself has a jq-free fallback)

Install

1. The plugin (TPM)

set -g @plugin 'ctretyak/tmux-agents-inbox'

Then prefix + I to install. Or clone manually and add run-shell ~/path/to/tmux-agents-inbox/tmux-agents-inbox.tmux to ~/.tmux.conf.

2. The Claude Code hooks

Run the installer (backs up settings.json, preserves your existing hooks, idempotent):

bash ~/.tmux/plugins/tmux-agents-inbox/install-hooks.sh

Restart any running Claude Code sessions afterward. If you prefer to edit settings.json by hand, see Manual hook setup below.

3. The status-line summary (optional)

Add the summary to your own status-right (recommended — it won't clobber your config):

set -g status-right '#(~/.tmux/plugins/tmux-agents-inbox/scripts/inbox-status.sh) %H:%M'

Or let the plugin append it for you:

set -g @agents-inbox-auto-status 'on'

Options

Option Default Meaning
@agents-inbox-popup-key g Key (after prefix) to open the inbox popup.
@agents-inbox-next-key N Key (after prefix) to jump to the next waiting agent.
@agents-inbox-popup-min-width 50% Floor for the auto-sized popup width. Accepts <N>% (percent of client) or <N> (cells).
@agents-inbox-popup-min-height 60% Floor for the auto-sized popup height. Same format.
@agents-inbox-popup-width (unset) Optional fixed width — if set, replaces auto-fit + min. Same format.
@agents-inbox-popup-height (unset) Optional fixed height — if set, replaces auto-fit + min. Same format.
@agents-inbox-refresh-interval 1 Seconds between auto-rebuilds of the open popup.
@agents-inbox-auto-status off If on, append the summary to status-right (idempotent; preserves your existing value).
@agents-inbox-preview off Show a preview pane in the popup with the agent's live tmux output + transcript-derived header. Toggling with ? rewrites this option globally for the current tmux server, so the choice persists across popup opens until you change it again.
@agents-inbox-preview-position right:55% Passed straight to fzf --preview-window. Accepts e.g. right:50%, bottom:40%, left:60%.
@agents-inbox-columns icon project subfolder description age Ordered, space-separated list of popup columns. List order is column order; omit a name to hide that column; unknown names are ignored (a header lists them). Catalog: icon (status symbol), project, subfolder, description (ai-title), age (relative), session, window (name), window-index, pane (%id), path (cwd — unbounded width, no truncation).

Columns example. Show the session name and drop the subfolder:

set -g @agents-inbox-columns 'icon session description age'

Leave the option unset to keep the default layout. path is available but unbounded — a long working directory will widen the popup.

In the popup: Enter jumps, Ctrl-X kills the agent in that pane, ? toggles the preview pane, Ctrl-S switches grouping (state → session → flat), Esc closes. Type to fuzzy-filter. The list auto-refreshes every 1 s by default (@agents-inbox-refresh-interval to change). Group headers (the ── Needs input (N) ── lines) are non-selectable — Enter on them is a no-op, and Up/Down skip past them.

Rows are grouped under headers with counts — Needs input / Completed / Background / Working / Idle — most-urgent first, newest-first within each group. (Background is a finished turn with a still-running background_tasks entry — monitors, watches, long-running shells.) By default each row shows: a colored status icon, project, subfolder (the worktree name or path within the repo, blank at the repo root), the session's ai-title description, and how long ago the session was last active — configurable via @agents-inbox-columns.

The popup is sized to fit its content, floored by @agents-inbox-popup-min-width / -min-height (defaults 50% / 60% of the client) and capped at client − 2. Set @agents-inbox-popup-width / -height for a fixed size that overrides the auto-fit entirely.

With @agents-inbox-preview set to on, the popup reserves additional width (or, on narrow clients, additional height at the bottom) for a preview pane. The preview shows for the currently-highlighted row: a header with project / subfolder / ai-title and the row's state / age / most-recent user prompt, then a live tail of the agent's tmux pane. Press ? inside the popup to toggle the preview on or off for that session.

Background sessions

This plugin only tracks interactive Claude sessions running in tmux panes. For background sessions (claude --bg, or anything you've sent to the background), use Claude Code's built-in agent view, which already groups them by state with live summaries:

claude agents          # full-screen TUI: peek, reply, attach
claude agents --json   # scriptable list of background sessions

Manual hook setup

Merge these into the hooks object of ~/.claude/settings.json (append to existing event arrays rather than replacing them). Replace the path with your actual plugin path — the install script writes the correct absolute path automatically, so the script is recommended.

{
  "hooks": {
    "SessionStart":     [ { "hooks": [ { "type": "command", "command": "bash ~/.tmux/plugins/tmux-agents-inbox/hooks/inbox-hook.sh SessionStart" } ] } ],
    "UserPromptSubmit": [ { "hooks": [ { "type": "command", "command": "bash ~/.tmux/plugins/tmux-agents-inbox/hooks/inbox-hook.sh UserPromptSubmit" } ] } ],
    "PreToolUse":       [ { "hooks": [ { "type": "command", "command": "bash ~/.tmux/plugins/tmux-agents-inbox/hooks/inbox-hook.sh PreToolUse" } ] } ],
    "PostToolUse":      [ { "hooks": [ { "type": "command", "command": "bash ~/.tmux/plugins/tmux-agents-inbox/hooks/inbox-hook.sh PostToolUse" } ] } ],
    "PreCompact":       [ { "hooks": [ { "type": "command", "command": "bash ~/.tmux/plugins/tmux-agents-inbox/hooks/inbox-hook.sh PreCompact" } ] } ],
    "Notification":     [ { "hooks": [ { "type": "command", "command": "bash ~/.tmux/plugins/tmux-agents-inbox/hooks/inbox-hook.sh Notification" } ] } ],
    "Stop":             [ { "hooks": [ { "type": "command", "command": "bash ~/.tmux/plugins/tmux-agents-inbox/hooks/inbox-hook.sh Stop" } ] } ],
    "SessionEnd":       [ { "hooks": [ { "type": "command", "command": "bash ~/.tmux/plugins/tmux-agents-inbox/hooks/inbox-hook.sh SessionEnd" } ] } ]
  }
}

Caveats

  • Only tracks Claude running inside a real tmux pane on the current tmux server. An agent in a display-popup, a detached non-tmux terminal, or over plain SSH is invisible and can't be jumped to.
  • Single tmux server. Pane ids can collide across separate tmux servers; the cache isn't namespaced per server yet.
  • A row drops as soon as its claude process exits (membership is process-based), so a crashed or quit agent disappears on the next refresh even if it never fired SessionEnd. A still-running agent whose hooks have gone silent (e.g. background-only work) reconciles against the transcript: if the transcript hasn't been touched, the row may show as Completed even though work is happening — the transcript is our ground truth and pure background shells don't write to it.
  • Reading the ai-title description tails the last ~256 KB of the session transcript on each refresh — constant time regardless of session length.
  • Hook-detection notice is user-scope only. When the popup can't find this plugin's hook path in your user settings ($CLAUDE_SETTINGS or ~/.claude/settings.json), it shows a yellow hooks not detected banner at the top. It's a cheap grep for the plugin's resolved hook path, so it won't see hooks wired into a project .claude/settings.json, and a partial install (path present but not all events) reads as detected. Run install-hooks.sh (the documented path) and it clears.

Uninstall

Remove the @plugin line, delete the inbox-hook.sh entries from ~/.claude/settings.json (or restore a settings.json.bak.* backup), and rm -rf ~/.cache/tmux-agents-inbox.

About

A tmux plugin that shows which Claude Code agents are waiting on you — finished or needing input — across all your sessions, and jumps you to the next one with a single key

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages