amux — Agents Tmux Dashboard
amux finds the coding agents running across your tmux panes and lets you
jump to one. Its job is to answer the question that gets hard once you have
more than a handful of sessions: which agent is waiting on me?
What It Does
Section titled “What It Does”amux # interactive picker: pick an agent, jump to its paneamux list # table of every live agentamux list --json # machine-readableamux scan # refresh the cacheIn the picker, rows are sorted by urgency, the right-hand pane shows a live
preview of whatever is highlighted, enter jumps to that pane, and ctrl-r
refreshes.
Filter by repository or directory
Section titled “Filter by repository or directory”amux list --repo proposalwriteramux list --repo proposalwriter --dormant --sort oldestamux --repo proposalwriter # interactive pickeramux list --dir ~/Git/proposalwriter # this directory and belowamux --dir . # picker for the current directory--repo NAME (also --project NAME) matches the exact, case-sensitive repository
name shown by Amux, or the main repository directory name for linked Git
worktrees. Thus --repo proposalwriter also includes sessions in linked
worktrees with different directory names. This matches local repository names,
not GitHub owner/repo URLs.
--dir PATH matches a directory and its descendants, with ~, relative paths,
and symlinks resolved. It does not include sibling worktrees outside that
subtree. Combine both flags to require both matches. Filters work with table,
JSON, cached results, and the picker; automatic and Ctrl-r refreshes preserve
them. The shared scan cache always retains agents from all projects.
Agent states
Section titled “Agent states”Every pane running an agent gets one of four states, sorted most urgent first:
| state | colour | meaning |
|---|---|---|
input | red | the agent stopped and is asking you something |
busy | green | mid-turn, actively working |
bg | cyan | background monitors or subagents running |
idle | dim | at its prompt, nothing pending |
input is the reason the tool exists. An agent blocked on a permission
prompt or an AskUserQuestion is invisible among a hundred panes, and it
will sit there indefinitely until you notice.
Finding dormant sessions
Section titled “Finding dormant sessions”amux list --dormant --sort oldestamux pick --dormant --sort oldestamux list --dormant-hours 48 --sort oldest --jsonEach row shows INPUT AGE, the age of the last recorded submitted user input, and ACTIVITY, independently of the current state:
dormant: waiting at a prompt, with known input older than 72 hours.recent: waiting, with input within the threshold.working: the current state isbusyorbg, regardless of input age.unknown: no reliable last-input evidence is available.
Use --dormant-hours to change the threshold. --sort oldest puts known
oldest input first and unknown ages last. Both options work in the picker;
refresh preserves them. With the usual tmux shortcut, type dormant to
filter the displayed rows, or open the dedicated dormant picker above.
Input timestamps come from the harness’s submitted-input history, not file
modification times, terminal output, or the clock in a status footer. Hooks,
tool results, and monitor output do not count as submitted input. These are
session-level timestamps: two panes sharing a conversation share its
last-input time. JSON records identify this as input_scope: "shared-session".
Unsent drafts are not dated; check the pane before exiting its agent.
Claude’s live PID registry identifies its current conversation and name.
Codex matching requires an open rollout file associated with the running
agent; an old resume command or a matching working directory is insufficient.
Some Codex clients keep history remotely and therefore show unknown.
History reads are bounded, so sufficiently old input outside the retained
lookup window also shows unknown. Personal and alternate account homes are
checked separately. No sessions are exited or sent input by scanning.
What each row shows
Section titled “What each row shows”sasy:1.8 input codex 4.2d dormant certify-main observability@main└─ pane state harness age activity session name repo@branch- pane — the tmux target; jump to it manually with
tmux switch-client - name — the live Claude registry name when available, otherwise
--resume/--nameargument, the tmux pane title, or an on-screen banner - repo@branch — read directly from
.git/HEAD, so git worktrees resolve to the branch they are actually on - context — the model and status line lifted from the harness footer
How detection works
Section titled “How detection works”Two questions, answered from two different places.
Which harness is this? From the process, not the screen.
pane_current_command is useless here: Claude Code reports a bare version
string like 2.1.220 because it runs a versioned binary, and Codex reports
node. amux inspects the executable of each process beneath the pane’s
shell — including under wrappers like direnv exec — and matches on
argv[0], or on the script path when argv[0] is an interpreter.
What is it doing? From the screen and available live runtime metadata. Claude’s prompts are a structured choice list, so they are recognised directly; position disambiguates a pending prompt from one you already answered, because an answered prompt has the harness footer rendered below it.
Codex has no structured prompt UI, so “waiting for you” is a heuristic: the last content line ends in a question mark, and nothing indicates it is working. Expect occasional misses and false alarms there.
Pane captures run concurrently, and each account history is read once per scan. The additional history lookup is bounded to keep refresh work predictable.
The picker opens on cached rows and refreshes in the background, so it feels
instant regardless of how many panes you have. --max-age controls how long
a cached scan stays usable (default 30s); amux scan refreshes it explicitly,
which is useful from cron or a tmux hook.
tmux keybinding
Section titled “tmux keybinding”Bind the picker to a popup overlay so it is one keystroke from anywhere:
bind-key A display-popup -E -w 85% -h 75% amuxThen prefix + A opens the dashboard over whatever you are looking at,
enter jumps to the chosen agent, and esc closes without moving anything.
Pair it with a “jump back” binding, since jumping to an agent switches your current client to that session:
bind-key a switch-client -l # return to the session you came fromScripting
Section titled “Scripting”amux list --json emits one object per agent with every field — pane,
session, kind, state, name, cwd, repo, branch, model, info, pid, session_id,
last_input_at (Unix seconds), input_age_seconds, input_source,
input_scope, and inactivity — so you can
drive alerts or dashboards from it:
# how many agents are blocked on me right now?amux list --json | jq '[.[] | select(.state == "input")] | length'
# name every waiting agent and its paneamux list --json | jq -r '.[] | select(.state=="input") | "\(.name)\t\(.pane)"'Limitations
Section titled “Limitations”- Local tmux only. It reads the tmux server on this machine.
- Codex input detection is heuristic — Codex exposes no prompt state, so the question-mark rule will sometimes miss or over-report.
- States are sampled, not streamed. The list reflects the moment you
opened it (
ctrl-rre-scans); the preview pane is always live.