Making tmux.expose agent-aware

Landed another feature in tmux.expose this week, it really has become a great way for me to manage my worktrees + agents. Below is how I added a more agent-aware workflow.

Gaps

My pane layout from the last post has a Claude Code pane in it, and that’s not unusual for me anymore, most of my sessions have at least one agent running in a pane or a window. Which is great until you have six or seven sessions going and it’s hard to know which agent is awaiting on you, which one is still working and which agent is done. Before my PR tmux.expose was showing all of them in the same flat, alphabetical grid regardless of what’s actually happening inside them. One agent might be three minutes into a task. Another finished and is sitting there waiting on me. Another hit a permission prompt and has been stuck for twenty minutes because I haven’t looked at that pane. The grid doesn’t know the difference, so I don’t either, until I manually click through everything.

That’s a bad way to find out an agent has been blocked on you for twenty minutes.

Here’s the fix running against a few demo sessions, watch the left card flip to needs you and the right one catch up behind it:

tmux.expose sessions re-sorting as agent status changes

The fix

I opened a PR that makes the grid agent-aware. The mechanism is two tmux pane options:

tmux pane option Meaning
@agent_status working, waiting, or attention
@agent_status_since Unix timestamp of when that status started

The moment something writes those on a pane, tmux.expose picks it up on the next refresh. No config required to make it display, it works on any agent that can run a shell command on a state change, not just Claude Code. A session’s card takes the worst status across all of its panes (attention > waiting > working), so a multi-agent session never hides a stuck pane behind a busy one, and by default sessions with a waiting or blocked agent sort to the top of the grid, oldest-waiting first. That last part matters more than it sounds like it should, the agent that’s been waiting the longest is usually the one I forgot about.

The binary ships a one-shot helper so nothing but tmux-expose itself needs to be on PATH:

tmux-expose agent-status working    # agent just started a turn
tmux-expose agent-status waiting    # agent's turn ended, idle on you
tmux-expose agent-status attention  # agent is blocked (e.g. needs a permission)
tmux-expose agent-status clear      # agent/session is done, remove the marker

Wiring it into Claude Code is just hooks in ~/.claude/settings.json:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          { "type": "command", "command": "tmux-expose agent-status working" }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "tmux-expose agent-status waiting" }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          { "type": "command", "command": "tmux-expose agent-status attention" }
        ]
      }
    ],
    "SessionEnd": [
      {
        "matcher": "",
        "hooks": [
          { "type": "command", "command": "tmux-expose agent-status clear" }
        ]
      }
    ]
  }
}

That’s it, no separate script to write or distribute, just four hooks pointed at a binary that’s already on PATH.

Multi-agent sessions

The gif above is two single-pane demo sessions, but a session with more than one agent pane spells out its worst status in words and shows the rest as compact counts, ‼ needs you · ⏳2 · ⚙1. A single tracked pane just gets the plain label, ⏳ awaiting reply. I went back and forth on using bare emoji for all of it, but landed on words for the status that actually matters, emoji rendering varies by terminal font, and a symbol that silently fails to render is a bad failure mode for the one status you actually need to see. Here’s the full grid:

tmux.expose agent status

It’s merged now, and I’ve stopped tabbing through sessions to find the one that’s stuck. That’s the whole point of contributing to a tool you actually use every day, you’re not guessing what would be useful, you already know because you were just annoyed by its absence an hour ago. Give it a go and let me know what you think!