Agent pane discovery¶
Find — and jump to — coding-agent sessions (claude, cursor-agent, codex,
opencode) that are currently running in any tmux pane on this host.
The pain this solves: you start claude in pane A on session chezmoi, hop
into pane B in another window for a quick test, drift over to a vibe/foo
session for a Codex task, and three windows later you can't remember which
pane has the agent you wanted to nudge. CodeIsland (macOS) tells you which
terminal app is running an agent, but not the specific tmux pane —
useless once you have more than one agent inside one terminal.
Two complementary surfaces ship in this repo, both backed by tmux:
| Tool | Scope | Best at |
|---|---|---|
tv agent-panes |
All four agents (Claude / Codex / OpenCode / Cursor) | "Where am I running stuff?" cross-agent overview, jump-to-pane |
tv agent-wakeup |
Same live panes, plus pueue wakeup tasks | "Which agent is quota-blocked, when should I send continue?" |
recon (optional) |
Claude-only, with live status (Working / Idle / Waiting) parsed from pane capture | "Which Claude needs my attention?" — recon's live status detection is richer than what we replicate in tv |
tv agent-panes (primary)¶
Open the picker with prefix + a inside tmux, or via the popup menu
(prefix + Space → → Agents → Live agent panes (tv)), or from any shell
with tv agent-panes.
Each row is one running agent pane. The status column shows ● (busy) or
○ (idle) for Claude (read from ~/.claude/sessions/{pid}.json); blank for
others.
[cc] ● chezmoi:1.1 │ discover-coding-agent-sessions │ chezmoi
[cc] ○ chezmoi:2.1 │ fix-nvm-lazy-loader-permission-request │ chezmoi
[cc] ● vibe/foo:4.1 │ refactor auth module │ foo
[oc] vibe/bar:0.0 │ Generate migration script │ bar
Keybindings (in the picker)¶
| Key | Action |
|---|---|
Enter / Alt+T |
Switch tmux focus to that session/window/pane |
Alt+K |
Kill the pane (with [y/N] confirm) |
Ctrl+Y |
Copy the pane target (session:win.pane) to clipboard |
Alt+P |
Copy directory path |
Alt+R |
Copy session ID |
Alt+S |
Copy matched .specstory/history/*.md path |
Ctrl+F |
Cycle preview: live tmux capture-pane snapshot ↔ stored transcript |
Esc |
Close the picker |
How resolution works¶
- Claude — Claude Code writes a per-process file at
~/.claude/sessions/{claude_pid}.jsoncontainingsessionId,cwd,status, and (when set) a friendlyname. The discovery walker reads these directly — zero heuristics for Claude. It then walks eachclaude_pid's parent chain viaps -o ppid=until it finds a tmuxpane_pid, which gives the pane target. - Codex / OpenCode / Cursor —
pgrep -x <bin>finds running agent PIDs, parent-chain walked the same way. Session ID is best-effort: the walker queries each agent's storage (opencode.db,~/.codex/sessions,~/.cursor/chats) for the most-recent matching session whosecwdequals the pane'spane_current_path. If multiple sessions share a cwd, the most recently updated wins. The pane is still listed even if no session ID matches — the row is useful for jump-to-pane regardless.
tv agent-wakeup (quota waits + scheduled continue)¶
Open with tmux prefix + M-a, Herdr prefix + Alt+A, via the popup menu
(prefix + Space → → Agents → Quota wakeup (tv)), or from a shell with
tv agent-wakeup.
The channel aggregates tmux and all running Herdr sessions from an ordinary
shell. Inside a multiplexer, auto stays ambient (Herdr inside Herdr, tmux
inside tmux). It captures the last part of each pane and conservatively marks panes as quota
waiting only when it sees explicit limit wording such as session limit,
rate limit, limit reached, or quota. It parses reset phrases like
resets 1:50am, resets in 70m, or resets in 5h 20m, then shows any
queued pueue wakeup task for the pane.
Claude HUD usage lines such as Context ... | Usage Limit reached are
ignored because claude-hud renders from cached/API usage data and is not an
authoritative live indicator that the pane is currently blocked.
WAIT_MENU is the Claude /rate-limit-options screen where option 1
(Stop and wait for limit to reset) is already selected. For that state the
recommended action is enter, not continue.
| Key | Action |
|---|---|
Enter |
Focus that tmux or ambient-Herdr pane |
Alt+C |
Schedule a smart wakeup at the detected reset time + 2 minutes |
Alt+T |
Prompt for a custom time/delay (01:52am, 70m, 5h, etc.) |
Alt+N |
Smart send now: Enter for WAIT_MENU, otherwise continue + Enter |
Alt+X |
Cancel queued wakeup tasks for that pane |
Ctrl+F |
Cycle preview; live tmux capture-pane or herdr agent read --source visible output |
The shell interface is unchanged:
agent-wakeup status
agent-wakeup status --backend all
agent-wakeup status --backend herdr --herdr-session default
agent-continue-at --pane %12 --at 01:52am
agent-continue-at --backend herdr --current --delay 70m
agent-wakeup send-now --pane %44 --auto # Enter for /rate-limit-options, continue otherwise
agent-wakeup send-now --pane %44 --enter-only # force selecting Stop-and-wait menu item
agent-continue-at --current --delay 70m
agent-wakeup cancel --pane %12
Scheduled wakeups are pueue tasks in group agent-wakeup. Tmux labels remain
agent-wakeup:%pane_id; Herdr labels are
agent-wakeup:herdr:<session>:<agent-session-id> when a stable ID is available.
That ID is preferred and re-resolved at execution to follow pane moves; a
missing identity aborts the send. Without an ID, the label is
agent-wakeup:herdr:<session>:pane:<pane-id>. Scheduling snapshots a SHA256 of
the terminal ID, agent name, and matching foreground agent PID/start time/argv
(start time from local ps), refusing if unavailable. Execution rejects changed
or unverifiable processes. This is process identity, not conversation
identity: switching conversations within the same live process is undetectable,
and pane moves are not followed without a stable ID.
Scheduled Herdr sends require idle/done, except for a recognized blocked
quota Enter menu. The quota guard is enabled only when detected at scheduling,
unless --no-expect-quota is used; text sends recheck the active tail, while
Enter menus retain the full capture. Blocked sends additionally require the
Stop-and-wait option in the active tail. A missing marker aborts. --force bypasses only quota,
not identity or readiness. With --auto, a fresh capture selects Enter for a
rate-limit menu or continue for a normal prompt. Text uses agent prompt --wait
--until working --until blocked --timeout 15000: Herdr submits text and Enter
together and verifies startup, without waiting for the whole turn to finish.
Only menu Enter uses agent send-keys. A stalled/timeout result is not retried:
inspect the agent first because the prompt may already have been delivered.
For ordinary shell panes use herdr pane run <pane-id> "ls", not agent prompt.
That submits a shell command plus Enter; inspect output with pane read or
pane wait-output to verify execution. These are Herdr terminal-control APIs,
not direct calls to the agent provider.
Wakeups are one-shot: the host must be awake with pueued, Herdr and the target agent running. They do not reopen closed sessions or agents, and exact execution timing is not guaranteed.
The fully automatic "detect quota, schedule, continue, repeat until non-quota
exit" supervisor is deliberately deferred; see
backlog/agent-quota-auto-loop.md.
Scheduled work beyond quota waits¶
For non-quota delayed work, prefer native agent scheduling when it fits:
- Claude Code has session-scoped
/loop/ cron tasks, Desktop scheduled tasks, and cloud Routines. - Codex App has Automations, including thread automations that wake up the same thread on a schedule.
- OpenCode is scriptable through
opencode run --continue/--session, and its GitHub integration supports cron schedules in GitHub Actions.
Those native surfaces are better for agent-owned scheduled work. The local
agent-wakeup / pueue path is still useful when the target is a specific tmux
pane, a one-shot future continuation, or a cross-agent workflow that should
show up in the same dashboard. The broader local wrapper idea is tracked in
backlog/agent-delayed-run-scheduler.md.
recon (Claude-only fast popup, optional)¶
gavraz/recon is a Rust TUI dashboard
that focuses on Claude Code only but pays attention to live agent status by
parsing the Claude TUI status bar via tmux capture-pane. Worth keeping
around when you're juggling several Claude sessions and want
"who's blocked on me?" at a glance.
Install¶
Installed automatically via the rust_cargo_tools ansible role on
chezmoi apply:
Upgrades flow through the standard cargo path:
Use¶
Open it from the popup menu: prefix + Space → → Agents → Claude
dashboard (recon). There's no top-level keybind by default — prefix + g
(recon's upstream default) is taken by the sesh picker on this repo.
Promote it to a top-level binding in dot_config/tmux/keybindings.conf if
you find yourself reaching for it often.
The → Agents submenu only shows the recon row when recon is on PATH,
so it disappears cleanly on hosts that haven't installed it.
Troubleshooting¶
"I don't see my running Claude"¶
Stale ~/.claude/sessions/{pid}.json files are skipped automatically — if
the PID isn't a descendant of any tmux pane, the row is omitted. If you
expect to see a session that isn't there:
~/.config/television/agent-sessions.py panes # raw TSV, see what it finds
ls -lt ~/.claude/sessions/ # what files exist
ps -p <pid_from_filename> # is the process alive?
tmux list-panes -aF '#{pane_id} #{pane_pid} #{pane_current_command}'
If the claude PID is alive but _find_pane_for_pid doesn't reach a tmux
pane (e.g., claude was launched outside tmux), the row is correctly
omitted — tv agent-panes only shows panes you can switch-client to.
"Codex/OpenCode/Cursor row has no session ID"¶
The cwd-based reverse lookup couldn't find a stored session whose
directory matches the pane's pane_current_path. Common causes: agent
was started with --cd to a path that doesn't match storage; storage
was migrated. The pane still shows up — Enter still jumps to it; you just
won't get an automatic "resume this exact session" handle.
"I jumped to a pane and tmux says it doesn't exist"¶
The picker is a snapshot. If a pane closes between snapshot and switch,
the action falls back to a tmux display-message warning and stays put.
Re-open the picker (prefix + a) to refresh.
"Recon doesn't appear in the Agents submenu"¶
menu-agents.sh probes command -v recon and only adds the row when the
binary is on PATH. If you've just installed it, your tmux server may
need a fresh shell environment — tmux source ~/.tmux.conf is enough; no
restart required.
Related¶
- tmux keybindings — popup-menu surface
(
prefix + Space) where→ Agentslives, plus the fullprefix + ?binding map. - Agent sessions browser — sister channel
tv agent-sessionsthat browses stored session history (resume an old session vs. jump to a running one). - SpecStory internals — explains the
<!-- Provider Session UUID -->reverse-lookup that lets the picker surface the matching.specstory/history/*.mdfor each row. - Sibling deferred work: per-pane working/idle status glyph in the tmux
window list itself (Option C of
backlog/tmux-window-status-indicators.md).