Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Drive an external bar

Your agent panes live in tmux but your status bar does not: sketchybar, waybar, polybar, starship, a Prometheus scrape, a tmux-less terminal. Poll tma status --format plain on whatever interval that bar already uses. It prints the same counts as the tmux driver with the color codes dropped, since those bars do their own styling:

$ tma status --format plain
⚑1 ●2 ✓1

The recipes

sketchybar, in a plugin script:

sketchybar --set agents label="$(tma status --format plain)"

waybar, a custom/tma module with "exec" and an "interval":

"custom/tma": { "exec": "tma status --format plain", "interval": 5 }

starship, a custom module in starship.toml:

[custom.tma]
command = "tma status --format plain"
when = true
format = "[$output]($style) "

For a bar that would rather have numbers than glyphs, --format json gives the same counts as a one-line schema-1 document. Scope any of these with the selector flags: tma status --format plain --repo app is one repo’s counts.

Why a poll is enough on its own

A poll from an external bar is a first-class ambient driver. Every tma status invocation, whatever its --format, runs the full poll cycle and stamps every agent pane on the server before it prints; the format only decides how the counts are rendered. A sketchybar item polling every 5 seconds keeps state as fresh as a 5-second status-interval would, so you do not also need #(tma status) in status-right for freshness. You may still want it for the inline glyphs.

That holds for a scoped poll too: the selector narrows what is printed, never what the cycle refreshes.

No attached client is needed. Unlike #(), which only runs while a client is drawing the status line, an external poll reaches a detached server perfectly well: tma connects to the socket, cycles, and exits. A tmux session you started with new-session -d and never attached to still gets refreshed state, which is exactly the case tma doctor warns about when nothing else is driving.

Export to Prometheus

tma status --format prom writes the Prometheus text exposition format, which is what a node_exporter textfile collector reads. Write it atomically (rename into place) so the collector never scrapes a half-written file:

* * * * * tma status --format prom > /var/lib/node_exporter/tma.prom.$$ && mv /var/lib/node_exporter/tma.prom.$$ /var/lib/node_exporter/tma.prom

The same caveat as any cron tma invocation applies: cron gives you no $TMUX, so pass --socket-name/--socket-path when your agents run on a named server, and make sure the crontab user is the one who owns the tmux socket.

Two families come out of it: tma_agents{state="…"} (the counts, all five classes always present) and tma_agent_state_seconds{pane,agent,state} (how long each pane has held its current state). The second is the one worth alerting on: a blocked agent whose age climbs past a few minutes is one nobody has answered.

- alert: AgentBlockedTooLong
  expr: tma_agent_state_seconds{state="blocked"} > 600
  annotations:
    summary: '{{ $labels.agent }} in {{ $labels.pane }} has been blocked 10 minutes'

Because the cron run is itself an ambient driver, a Prometheus export doubles as the polling floor on a server with no attached client.

Next