You've got Grok Build crunching through a refactor in one pane, Claude Code negotiating a design doc in another, and Hermes Agent off doing⦠whatever Hermes Agent does. Wouldn't it be nice to know who's actually working, who's stuck waiting for your approval, and who's just daydreaming?
tmon sits quietly in your tmux status bar and tells you exactly that. It
finds every running AI coding agent across your panes, tracks whether it's
working, idle, or blocked on you, and shows a compact
color-coded count. Need details? Hit prefix a a for an interactive
dashboard β or just click the status bar indicator.
- π¦ Status bar β the whole zoo on one compact, color-coded line: π€ your fleet, π¨ blocked ("waiting for you β it has opinions"), β‘ working ("in flow, do not disturb"), π€ idle ("napping between thoughts"). Click it to open the dashboard.
- πΌοΈ Pane highlighting β agent panes wear a status-colored border strip (π¨ blocked, β‘ working), so the needy ones stand out without reading a single line of output.
- π¨ Blocked? You'll know. β a red π¨ means an agent is waiting on you; no more silent standoffs with Claude.
- π Teleport β
Enter(or a double-click) in the dashboard drops you straight into the agent's pane; a single click opens its preview and details without moving you. No moretmux list-panesarchaeology. - π Three dashboard views β flat list, grouped by project, or grouped by status. Press
vto switch; the choice sticks. - π Fuzzy search β Telescope-style matching over names, directories, branches, PR numbers, and even pane content.
- π Context gauges β a live progress bar shows each agent's context window filling up, with a
β οΈ before it goes supernova. - β³ Quota monitoring β a background worker probes your Claude, Grok, Codex, and Hermes account quota (plan tier, % used, next reset; Hermes reports its configured provider's balance) once per 15 minutes and shows it in the dashboard's stats line. Auto-spawns from the first status poll; no setup.
- π¨ Live themes β preview Catppuccin, Nord, Dracula and friends right in the popup, apply with
Enter. - π΅οΈ Hide the noise β glob patterns drop agents you don't care about from the status bar and dashboard. The agent keeps running; you just stop seeing it.
- π©Ί
tmon doctorβ one command that checks everything and explains itself in plain text (or JSON, for the CI crowd). Inside the dashboard, pressdfor the same report as a popin β scrollable, withrto re-run. - π Fit-to-width preview β press
finside the dashboard to wrap long captured lines to the preview width instead of cutting them at the edge. The choice persists. - π¬ Message agents β select an agent and press
sto type it a message in the dashboard:Entersends it to the agent's pane,alt+enterstarts a new line,escdiscards the draft. On Kitty-capable terminals with extended keys enabled,shift+enteralso starts a new line. - π€ 13 agents and counting β Grok Build, Claude Code, Codex CLI, Cursor, Cline, Aider, Copilot, CodeBuddy, Windsurf, Hermes Agent, OpenClaw, Prime Agent, OpenCode.
curl -fsSL https://raw.githubusercontent.com/guillaumemeyer/tmon/main/install.sh | shAdds the TPM plugin line
set -g @plugin 'guillaumemeyer/tmon'to ~/.tmux.conf at the right place when it is not there yet (before the
TPM initializer, or grouped with your other plugins). It does not install
TPM and does not run tmux. If you do not have TPM yet, install it and press
prefix + I inside tmux to clone and load tmon. Safe to re-run.
See "Alternative installation modes" if necessary.
- Won't feed your agents
- Won't pair your socks or approve
rm -rf /for you - Won't start agents or stop agents
- Won't auto-merge PRs written by three agents at once (we have some standards)
tmon is a fleet manager, not a petting zoo.
tmon watches the whole zoo out of the box β 13 agents:
π€ Grok Build Β· β³οΈ Claude Code Β· π§© Codex CLI Β· π±οΈ Cursor Β· π§Ά Cline Β· π¦ Aider Β· β¨ GitHub Copilot Β· πΎ CodeBuddy Β· π Windsurf Β· π Hermes Agent Β· π¦ OpenClaw Β· π°οΈ Prime Agent Β· β‘ OpenCode
How closely tmon can track each one depends on the agent's own state surface. Agents that publish live state files (Grok, Hermes, Codex CLI, Cline, CodeBuddy, Aider, OpenClaw, Prime Agent) or accept lifecycle hooks (Claude Code, Cursor, Copilot, Windsurf, Grok, Hermes approvals, Prime Agent's extension, OpenCode's plugin) give tmon authoritative working / blocked / idle signals; everyone else falls back to the CPU/IO and pane-content heuristics. The matrix below shows which features each agent's connector provides:
| Agent | Connector | Status | Blocked | Detail | Title | Tokens |
|---|---|---|---|---|---|---|
| Grok Build | native (~/.grok) + hooks |
exact | β | phase Β· tool Β· permission Β· model | β | β + window % |
| Claude Code | hooks | exact | β | tool Β· permission | β | β + window % + quota |
| Codex CLI | native (~/.codex rollouts) |
exact | β (hooks) | phase Β· tool Β· permission | β | β + window % + quota |
| Hermes Agent | native (~/.hermes + profiles) |
CLI/TUI | β (hooks) | model Β· approval | β | β + window % + quota |
| GitHub Copilot | hooks, else native fallback | exact | β | tool Β· permission | β | β |
| Cursor | hooks, else native fallback | exact | β | tool | β | β |
| Windsurf | hooks | exact | β | tool | β | β |
| Cline | native (~/.cline) |
working | β | session id | β | β |
| CodeBuddy | native (~/.codebuddy) |
idle | β | session id | β | β |
| Aider | native (.aider.chat.history.md) |
working | β | editing | β | β |
| OpenClaw | native (~/.openclaw + gateway lock) |
gateway | β | gateway Β· N active sessions | β | β |
| Prime Agent | native (daemon list + session JSONL) + hooks (extension) | exact | β | needs:input Β· tool Β· model | β | β + window % |
| OpenCode | hooks (plugin) | exact | β | tool Β· permission | β | β + window % |
Status is how precisely tmon knows the working / blocked / idle state:
exact from the agent's own signals, a partial signal (working, idle,
gateway, CLI/TUI), or heuristic (CPU/IO inference). Blocked marks
connectors that detect a permission wait themselves; a β falls back to the
pane-pattern heuristic ([y/N], permission prompts, β¦). Detail is what
the dashboard shows under the agent's name, Title the session name
("Title (Agent)"), and Tokens the stats line (tokens + context-window %
when known). Quota is the account-level rate-limit windows β % used,
next reset, plan tier β probed in the background by the usage worker for
Claude, Grok, and Codex, plus the configured provider's balance for Hermes
(it is bring-your-own-key, so there is no central account to bill), and
shown in the dashboard popup at the top of the preview
pane: a π Usage: header with one row per window, a divider, then a
π¬ Context window: line with the token counts and the context-window bar on its
own line below it. Each quota row renders dollars when the provider
reports them, percent otherwise β a window with money data shows its
amounts without a bar ($18.56 used Β· $81.44 left Β· Extra usage (monthly)),
while a remaining account balance shares the π Usage: header line
(π Usage: $65.85 remaining Β· DeepSeek balance) instead of taking its own
row. A percent-only window shows its progress bar (ββββ 38% Β· Current session (reset at 19:39 PDT)). All bars share a four-space left margin so
they line up. Only an agent with no quota at all shows π Usage: ?.
Hermes lists only live CLI/TUI sessions (not the messaging gateway).
The dashboard name is Title (Hermes - <profile>) when a profile is known
(default home or ~/.hermes/profiles/<name>). Session title, model, and
token stats come from each home's state.db. Dangerous-command waits become
blocked when approval hooks are installed (tmon hooks install hermes);
Hermes may prompt once to allowlist the shell hook. Its usage line shows the
configured provider's balance (from model.provider / model.base_url in
~/.hermes/config.yaml and the key in ~/.hermes/.env) on the π Usage:
header line like π Usage: $66.09 remaining Β· DeepSeek balance, so the
account level is visible without a central Hermes account.
Prime Agent reads its daemon's own session list (prime-agent list --json, TTL-gated β the spawn costs ~380 ms) plus each session's JSONL for
token usage, so working/idle, the model, and the context bar are exact
without hooks. Prime is bring-your-own-key on DeepSeek, so its π Usage:
header carries the DeepSeek account balance (π Usage: $65.85 remaining Β· DeepSeek balance), probed by the worker via DEEPSEEK_API_KEY in the
worker environment β the same account Hermes shows when Hermes is on
DeepSeek too. The daemon's activity field is the working signal; its
taskState (needs_input / completed) is only defined for idle sessions
and distinguishes a finished turn awaiting your next message
(needs:input) from one marked completed (turn-complete) β tmon shows
both as idle. A session that has not sent its first message yet is a
draft; tmon keeps it as a row, so a just-opened session already shows its
model, context bar, and account quota instead of π¬ Context window: ?. One
session is a client + supervisor + catalog + worker set
of processes that all share the process title prime-agent; tmon keeps
only the tty-owning client for pane teleport and falls back to the worker
PID for detached sessions (pane "?"), which is prime-agent's headline
feature. Installing the extension (tmon hooks install prime, then
/reload in prime-agent) adds exact mid-turn tool names. Prime Agent has
no permission event and no native blocked signal, so a permission wait
still reads as working and falls back to the pane-pattern heuristic.
Hooks are off by default (@tmon-auto-hooks off). Install them with
tmon hooks auto or tmon hooks install <agent> when you want authoritative
status. Codex is read natively from its session rollouts without hooks;
installing them (and trusting them in-session via /hooks) adds tool and
permission detail on top. Prime Agent's hooks are a single hot-reloadable
extension (~/.prime/agent/extensions/tmon-status.ts; apply with /reload)
instead of shell hooks. OpenCode's hooks are likewise a single auto-loaded
plugin (~/.config/opencode/plugins/tmon-opencode.ts) β opencode calls its
hook mechanism plugins, and the plugin is a passive observer that writes
status, running tool, title, and the latest context-usage snapshot. Without
hooks, the Cursor/Copilot native fallback
only reports the agent as idle; other agents still use CPU/IO heuristics
across status refreshes.
Missing your favorite agent? The connector interface is about 15 lines β
Name(), Enabled(), Probe() β and community connectors are how the
fleet grows (same playbook that made TPM the default tmux plugin story).
See CONTRIBUTING.md for a copy-paste template, the detect-signature checklist, and how to land a PR.
The status poll must stay fast and never touch the network, so quota
probing runs in a small background worker that tmon status auto-spawns on
its first poll (one fork+exec, well under the poll budget; a flock keeps it
single-instance per state dir). The worker probes the Claude OAuth usage
endpoint, the Grok Build billing endpoint, the Codex app-server JSON-RPC
interface, and the configured provider's balance for Hermes and Prime (both
bring-your-own-key; Prime's probe reads DEEPSEEK_API_KEY from the worker
environment) at most once per 15
minutes, writes <state>/usage.json (quota blocks; the token ledger lands
here in a later phase), and exits after 30 minutes with no live agents and
no open dashboard. A crashed worker is detected via its heartbeat file and
respawned by the next poll.
<state>/usage.json is the worker's only output (schema v1): a quota
block per agent with a windows list β one entry per reported window
(session, weekly all-models, weekly per-model), each with the percent used,
a display label and the next reset time β plus the plan tier when the API
exposes it and a statusText/authHelpText pair explaining an absent
window (no credentials, rate limited, β¦). A window that carries money data
also stores balance/limit/spend/currency, so the dashboard can
render amounts instead of a bar. Claude's windows match its own
/usage view: "Current session", "Current week (all models)", and per-model
windows such as "Current week (Fable)". Each status poll reads it cheaply
and attaches the windows to every live record of that agent (quota is
account-level, so each session of an agent shows the same windows); the
dashboard renders one row per window under the π Usage: header at the
top of the preview pane β the dollar amounts when the window has them
($18.56 used Β· $81.44 left Β· Extra usage (monthly)), else the progress
bar (ββββ 38% Β· Current session (reset at 19:39 PDT)); π Usage: ?
only when nothing is known β with the
context-window bar on its own line under the π¬ Context window: token counts
below a divider. The ledger fields (today,
recentDays, modelUsage) are reserved for the next phase.
The probes are read-only and never prompt: they read credentials from the
agent's own local files (~/.claude/.credentials.json, ~/.grok/auth.json,
the codex binary)
and store only the quota numbers β never tokens β in usage.json.
tmon workerβ run the worker loop in the foreground (auto-spawn target).tmon worker stopβ stop the worker and disable auto-respawn.tmon daemonβ run the worker loop manually (headless setups, debugging).@tmon-worker off(orTMON_WORKER=off) β disable the worker entirely; the poll then runs the quota probes itself, TTL-gated to once per 15 minutes.tmon doctorreports worker state, usage.json validity, and the last quota probe results.
For status bars that aren't tmux (polybar, i3blocks, a shell prompt),
tmon status --json prints the full poll result: every agent with its
status, pane, working directory, phase detail and token usage.
tmon status --json{
"statuses": ["working", "idle"],
"agents": [
{
"pid": 12345,
"label": "Grok",
"status": "working",
"pane": "main:0.2",
"cwd": "code/tmon",
"detail": "tool:Bash",
"usage": { "tokensUsed": 52397, "windowTokens": 200000 }
}
]
}Pipe it through jq for exactly what you need:
# All agents currently blocked on you:
tmon status --json | jq '.agents[] | select(.status=="blocked")'
# Total blocked count for a polybar module:
tmon status --json | jq '[.agents[] | select(.status=="blocked")] | length'
# Working directories of everything in flow:
tmon status --json | jq -r '.agents[] | select(.status=="working") | .cwd'
# Account quota for an agent with a quota probe (Claude, Grok, Codex):
tmon status --json | jq '.agents[] | select(.usage.quotaWindows != null) | {label, quotaWindows}'When the worker has data, the usage block also carries the account quota:
quotaWindows lists every reported window (percent, label, reset time);
quotaPct/quotaReset mirror the first window for convenience.
- Linux or macOS (amd64 or arm64)
- tmux β₯ 3.2 (tested on 3.4)
- A downloader and checksum tool used once on first load:
- download: curl or wget
- verify: sha256sum (Linux) or shasum (macOS)
- Network on first load β the binary is downloaded from GitHub Releases; afterwards it lives in the plugin directory
Native Windows is not supported, and the Windows build has not been tested.
Use WSL2 with tmux and install tmon
inside the Linux environment (plugin under the Linux home, e.g.
~/.tmux/plugins/tmon β avoid /mnt/c/... when you can). Bootstrap fetches
the Linux binary; agents must also run as Linux processes in WSL for
detection to see them.
Paste this into your coding agent (Grok Build, Claude Code, Codex, β¦) and let it wire up your tmux config:
Install the tmux plugin guillaumemeyer/tmon for me.
1. Check whether TPM (Tmux Plugin Manager) is installed:
- Look for ~/.tmux/plugins/tpm (or whatever path run '~/.tmux/plugins/tpm/tpm'
would use), and whether ~/.tmux.conf already loads TPM via
`run '.../tpm/tpm'`.
2. If TPM is installed (or you can install it cleanly):
- Add this line to ~/.tmux.conf if it is not already present:
set -g @plugin 'guillaumemeyer/tmon'
- Ensure the TPM initializer is the **last** line of ~/.tmux.conf:
run '~/.tmux/plugins/tpm/tpm'
- If TPM itself is missing, clone it first:
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
- Then install plugins (TPM: prefix + I, or run the TPM install scripts).
3. If TPM is not available and should not be installed, do a manual install:
- git clone https://github.com/guillaumemeyer/tmon ~/.tmux/plugins/tmon
- Add to ~/.tmux.conf if missing:
run-shell ~/.tmux/plugins/tmon/tmon.tmux
4. Reload: tmux source-file ~/.tmux.conf
5. Confirm success: the plugin dir exists and, on first load, bootstrap
downloads the binary into ~/.tmux/plugins/tmon/bin/tmon (status message
like "tmon: installed vβ¦"). Report what you changed.
On first load, tmon downloads its binary into <plugin>/bin/ β you'll see a
one-line status-bar message such as tmon: installed v0.6.0 (linux/amd64).
# ~/.tmux.conf
set -g @plugin 'guillaumemeyer/tmon'Then prefix I to install.
If you're setting up TPM for the first time, clone it and keep the initializer
at the very bottom of ~/.tmux.conf:
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
run '~/.tmux/plugins/tpm/tpm'Reload: tmux source-file ~/.tmux.conf, then prefix I if plugins are not
installed yet.
git clone https://github.com/guillaumemeyer/tmon ~/.tmux/plugins/tmon# ~/.tmux.conf
run-shell ~/.tmux/plugins/tmon/tmon.tmuxReload: tmux source-file ~/.tmux.conf. On first load, tmon downloads its
binary into <plugin>/bin/ β you'll see a one-line
tmon: installed v0.6.0 (linux/amd64) (or darwin/arm64, etc.) status-bar
message.
TPM: hit prefix U (uppercase). tmon installs a git post-merge hook in the
plugin repo, so the pull itself re-downloads the binary matching the new
VERSION and re-applies the tmux wiring β no reload needed. If the hook
cannot run (e.g. the plugin is not a git clone), fall back to
prefix U, then tmux source-file ~/.tmux.conf.
Manual installs: git pull origin main (the same hook applies).
Set any of these in ~/.tmux.conf before the plugin line. All of them
are optional β the defaults are sensible for most people.
| Option | Default | What it does |
|---|---|---|
@tmon-status-position |
right |
Which side of the status bar carries the indicator |
@tmon-poll-interval |
3000 |
ms between agent scans; also sets tmux status-interval |
@tmon-activity-threshold |
500 |
CPU ms/s to call an agent "working" |
@tmon-io-threshold |
102400 |
min IO bytes/poll to call an agent "working" |
@tmon-dashboard-key |
a |
chord leader for the popup (prefix <key> <key>) |
@tmon-connectors |
auto |
which connectors to enable (auto or a comma list) |
@tmon-connector-freshness |
30 |
seconds a connector signal stays authoritative |
@tmon-auto-hooks |
off |
auto-install lifecycle hooks at plugin load |
@tmon-ascii-icons |
0 |
1 renders icons as ASCII ([@] B I; working agents keep the spinner) |
@tmon-bold-counts |
1 |
bold the per-status counts |
@tmon-context-warn |
85 |
context % at which a 0 disables) |
@tmon-pane-border |
on |
status-colored border strip on agent panes (blocked/working) |
@tmon-pane-border-position |
top |
where the strip sits (top or bottom) |
@tmon-hide |
β | comma-separated glob patterns of agents to hide from the status bar and dashboard (label, cwd, or session) |
@tmon-pr-lookup |
on |
resolve open GitHub PR numbers for agent branches in the dashboard via gh |
@tmon-worker |
on |
auto-spawn the background usage worker (quota probes) from status polls; off falls back to TTL-gated lazy quota probes |
@tmon-color-<slot> |
β | override one theme color slot |
@tmon-icon-<slot> |
β | override one status glyph |
How often tmon scans for agents and samples their activity. The plugin also sets tmux
status-intervalto this value in whole seconds (ms / 1000, minimum 1). That is how oftentmon statusruns in the status bar. It also feeds the CPU/IO threshold arithmetic. Note thatstatus-intervalis global for the tmux server (other widgets refresh at the same rate).
| Default | 3000 |
| Unit | milliseconds |
| Range | 1000β60000 (1s to 60s) |
set -g @tmon-poll-interval "5000" # every 5 secondsCPU sensitivity for calling an agent "working." Lower values are more sensitive to light activity.
| Default | 500 |
| Unit | CPU milliseconds per second |
| Range | 100β5000 |
set -g @tmon-activity-threshold "200" # more sensitiveIO sensitivity for calling an agent "working." Lower values pick up lighter file and stream activity.
| Default | 102400 |
| Unit | bytes per poll interval |
| Range | 1024β10485760 |
set -g @tmon-io-threshold "51200" # more sensitive to IOWhich side of your status bar gets the tmon indicator.
| Default | right |
| Options | right or left |
set -g @tmon-status-position "left"The chord leader key for opening the agent navigation popup. Opens with
prefix <key> <key>.
| Default | a |
| Example binding | prefix a a β navigation |
set -g @tmon-dashboard-key "b" # use prefix b b insteadWhich agent connectors to enable. Connectors use each agent's own status signals (when available) for more accurate working / blocked / idle state.
| Default | auto |
| Options | auto (every connector whose agent is installed) or a comma list, e.g. grok,claude |
set -g @tmon-connectors "grok,hermes" # only these two connectorsHow long a connector's status signal stays valid before tmon falls back to activity-based detection.
| Default | 30 |
| Unit | seconds |
set -g @tmon-connector-freshness "60" # keep connector state longerAuto-install lifecycle hooks at plugin load for agents that need them (Claude Code, Codex, Cursor, Copilot, Windsurf, Grok). Default is off so a status-bar plugin never rewrites other tools' configs without consent. Install is idempotent and backs up each config once (
.tmon.bak) before the first change. Setononly if you want that install on every plugin load.
| Default | off |
| Options | on or off |
set -g @tmon-auto-hooks on # install hooks at plugin loadManual hook install (recommended):
~/.tmux/plugins/tmon/bin/tmon hooks auto # every supported agent found
~/.tmux/plugins/tmon/bin/tmon hooks install claude
~/.tmux/plugins/tmon/bin/tmon hooks install codex # also run /hooks once inside Codex to trust them
~/.tmux/plugins/tmon/bin/tmon hooks install cursor
~/.tmux/plugins/tmon/bin/tmon hooks install copilot
~/.tmux/plugins/tmon/bin/tmon hooks install windsurf
~/.tmux/plugins/tmon/bin/tmon hooks install grok # ~/.grok/hooks/tmon-grok.json; reload hooks in-session (/hooks + r)tmon hooks remove <agent> undoes an install; tmon hooks status lists
what's installed. Hooks are optional β without them, agents still appear via
activity detection.
Grok installs into its global hooks directory (~/.grok/hooks/) with
two tmon-owned files, so it applies to every session including background
ones that never appear in active_sessions.json. Running Grok sessions pick
up the hooks after /hooks + r (or a restart). Codex trusts hooks by
config hash β after hooks install codex (or any config change), accept
them once in-session with /hooks.
Render status icons as plain ASCII instead of emoji.
| Default | 0 |
| Options | 0 (emoji) or 1 (ASCII) |
set -g @tmon-ascii-icons "1" # [@]-B2-|3-I1 instead of π€-π¨2-|3-π€1Render the per-status counts (the
2inπ¨2) in bold.
| Default | 1 |
| Options | 0 (normal weight) or 1 (bold) |
set -g @tmon-bold-counts "0"When any agent's context-window usage reaches this percent, the status bar appends a
β οΈ warning (in the theme'swarncolor) and the dashboard's usage bar turns yellow.0disables the warning.
| Default | 85 |
| Options | any percent, or 0 to disable |
set -g @tmon-context-warn "90"Draw a short status strip on each agent pane's border β themed icon and status word in the blocked or working color β so you can spot waiting or busy agents without reading the pane. Idle agents clear the strip so the border returns to its default (empty) appearance; when an agent exits, its strip is removed. This uses tmux's
pane-border-statusline (not the box-drawing edges, which tmux only styles as active vs inactive). While the feature is on, tmon ownspane-border-statusandpane-border-formatglobally β turn it off (or runtmon border off) to restore the default border chrome. Non-agent panes get an empty strip for as long as the feature is enabled.
| Default | on |
| Options | on or off |
set -g @tmon-pane-border "on" # default; set "off" to disable
set -g @tmon-pane-border-position "top" # or "bottom"Hide agents you do not care about from the status bar and the dashboard. The agent keeps running β tmon only stops showing it. Each comma-separated pattern is a glob matched against the agent label (case-insensitive), its working directory, or its tmux session name.
*matches any run of characters (including/),?matches exactly one character; a pattern with no wildcards matches the exact string.Hidden agents also get no pane-border strip, and they are dropped from
tmon status --json, so scripts and the status bar always agree with the dashboard.
| Default | empty (show everything) |
# Hide every Aider agent (label match, case-insensitive).
set -g @tmon-hide "aider"
# Hide agents working in scratch dirs and in tool-owned tmux sessions.
set -g @tmon-hide "*/scratch/*,tool-*"The dashboard resolves the open GitHub pull request number for each agent's branch and shows it as
(branch Β· #42)on the row and in the projects-view header. Lookup usesgh(must be installed and authenticated), runs at most once per branch per minute, and never blocks the dashboard for more than three seconds. Turn it off to skip theghsubprocess entirely.
| Default | on |
set -g @tmon-pr-lookup "off"Auto-spawn the background usage worker from status polls. The worker probes your Claude, Grok, and Codex account quota (plan tier, % used, next reset) at most once per 15 minutes and writes
<state>/usage.json; it sleeps between cycles, exits after 30 minutes with no live agents and no open dashboard, and is respawned by the next poll if its heartbeat goes stale (a crash). Setoffto disable it entirely β the poll then runs the quota probes itself, TTL-gated to once per 15 minutes.
| Default | on |
| Options | on or off |
set -g @tmon-worker "off" # disable the background workertmon ships with color themes for both the status bar and the dashboard:
default Β· catppuccin Β· nord Β· dracula Β· tokyonight Β· gruvbox Β· solarized Β· onedark
The theme is chosen live from the dashboard: press t inside the popup to
open the theme selector β a list of presets on the left with the selected
theme's color palette previewed on the right. Browsing the list applies the
highlighted theme to the whole popup as a live preview. Enter or Space
applies and persists the theme (it writes state/theme and updates
TMON_THEME in the global environment and every live session β session
env otherwise shadows a global-only update). On the next tmux start or
plugin reload, tmon restores from state/theme; the binary also reads
that file directly so a stale session env cannot wipe the choice); Esc
or q closes the selector and reverts to the theme that was active before.
Preview a theme's colors straight from the terminal (swatches plus a sample status line):
~/.tmux/plugins/tmon/bin/tmon theme preview nordtmon theme (no arguments) lists all presets.
Fine-tune any theme with per-slot overrides. @tmon-color-<slot> accepts a
tmux color β a name (red), an indexed color (colour208), or hex
(#ff5555) β for the slots bg (the dashboard popup's background panel),
app, blocked, working, idle, dim, accent (the popup's rounded
border and highlights), warn, selbg.
@tmon-icon-<slot> swaps a status glyph for app, blocked, idle, or
warn (working agents show the animated spinner in the theme's working
color instead of an icon):
set -g @tmon-color-bg "#1e1e2e"
set -g @tmon-color-blocked "#ff5555"
set -g @tmon-icon-idle "π΄"
set -g @tmon-icon-app "@" # ASCII-only crowdRuntime state (state.json, theme choice, dashboard prefs, hook session
crumbs) lives under the XDG state directory so choices survive rebuilds,
tmux reloads, and reboots:
$XDG_STATE_HOME/tmon/ # when XDG_STATE_HOME is set
~/.local/state/tmon/ # default
The plugin binary stays in the plugin tree (<plugin>/bin). Override the
state path with TMON_STATE_DIR if you need a custom location.
| Binding | Action |
|---|---|
prefix a a |
Open the agent navigation popup |
| Click status bar indicator | Open the agent navigation popup |
d (in dashboard) |
Open the doctor report popin (r re-runs the checks) |
f (in dashboard) |
Toggle fit-to-width: wrap long preview lines to the panel width instead of cutting them at the edge (the choice persists) |
G (in dashboard) |
Jump the preview to the newest lines (the tail) and keep following new output. C-e is an alias |
gg (in dashboard) |
Jump the preview to the oldest captured lines (the top) |
Something's off? Run tmon doctor first β it checks everything at once
(tmux β₯ 3.2, downloader + checksum tools, binary vs. VERSION, writable
state dir, running agents, the usage worker and its heartbeat, usage.json
validity, the last quota probe results, connector and hook status) and
prints a β/β report with a non-zero exit code when anything fails:
~/.tmux/plugins/tmon/bin/tmon doctor # text report
~/.tmux/plugins/tmon/bin/tmon doctor --json # machine-readable, for CIStatus bar is empty β tmon only renders when agents are detected. Fire up an agent and it should appear. Still nothing? Run:
~/.tmux/plugins/tmon/bin/tmon statusDashboard won't open β Check your keybinding: tmux list-keys -T a-table.
If a conflicts with another plugin, change @tmon-dashboard-key.
Agent shows as "?" instead of a pane path β The agent couldn't be mapped to a tmux pane (e.g. headless or outside tmux). Harmless β the status bar still tracks it.
High CPU from tmon β Increase @tmon-poll-interval (e.g. to 10000).
Stale agent counts after crash β delete the state file and let it rebuild:
rm "${XDG_STATE_HOME:-$HOME/.local/state}/tmon/state.json"Binary won't download β needs network access to GitHub Releases. Check
connectivity, then from the plugin directory run scripts/bootstrap.sh
manually β it prints the failure reason.
Will tmon write my code for me? No. It just watches the robots that do. It's a leash, not a robot arm.
Does tmon phone home?
No. Everything runs locally. The one-time binary download on first load is
the only network call the poll ever makes; the only other network use is the
background usage worker reading your Claude, Grok, and Codex account quota
(read-only usage endpoints, at most once per 15 minutes, opt out with
@tmon-worker off).
Why is there an emoji in my status bar?
Because your agents are watching too. Set @tmon-ascii-icons "1" if you
need dignity.
My agent is blocked but tmon doesn't know it.
If its prompt is an unusual permission flow, the pane-pattern heuristic may
miss it β a connector for that agent fixes it (tmon hooks install <agent>
or check the connector matrix). When in doubt,
tmon doctor.
Why do my robots keep asking for approval? Because they respect you. Cherish it.
Does it work with Claude Code? Codex? Cursor? Thirteen agents and counting β see Supported agents.
Can I use it without tmux?
No β but tmon status --json feeds any status bar that can run a command.
"I didn't know my agents were blocked until tmon told me. My agents still don't know." β Someone with 14 panes and one coffee
"Finally, a tool that judges my agent fleet without judging me." β A human who definitely approved that plan
"
prefix a ais muscle memory now. My left pinky is a stakeholder." β Terminal-native
"I used to
tmux list-panesand pray. Now I pray less and click more." β Reformed pane spelunker
"Supports 11 agents. I only run two. The other nine live rent-free in the README." β Minimalist with FOMO
"Zero-config means I configured nothing and it still found Claude waiting on
[y/N]. Rude. Accurate." β Permission-prompt survivor
Testimonials may be fictional. Agents cannot sue.
tmon is built on a short stack of excellent Go libraries:
- Bubble Tea β the dashboard TUI
- Lip Gloss β styles and layout
- charmbracelet/x/ansi β ANSI width and truncation
- golang.org/x/sys β process / OS bits
MIT β because the robots haven't taken over yet.
