Skip to content

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?

Terminal window
amux # interactive picker: pick an agent, jump to its pane
amux list # table of every live agent
amux list --json # machine-readable
amux scan # refresh the cache

In 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.

Terminal window
amux list --repo proposalwriter
amux list --repo proposalwriter --dormant --sort oldest
amux --repo proposalwriter # interactive picker
amux list --dir ~/Git/proposalwriter # this directory and below
amux --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.

Every pane running an agent gets one of four states, sorted most urgent first:

statecolourmeaning
inputredthe agent stopped and is asking you something
busygreenmid-turn, actively working
bgcyanbackground monitors or subagents running
idledimat 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.

Terminal window
amux list --dormant --sort oldest
amux pick --dormant --sort oldest
amux list --dormant-hours 48 --sort oldest --json

Each 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 is busy or bg, 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.

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/--name argument, 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

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.

Bind the picker to a popup overlay so it is one keystroke from anywhere:

~/.tmux.conf
bind-key A display-popup -E -w 85% -h 75% amux

Then 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:

Terminal window
bind-key a switch-client -l # return to the session you came from

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:

Terminal window
# how many agents are blocked on me right now?
amux list --json | jq '[.[] | select(.state == "input")] | length'
# name every waiting agent and its pane
amux list --json | jq -r '.[] | select(.state=="input") | "\(.name)\t\(.pane)"'
  • 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-r re-scans); the preview pane is always live.