oss

tmux-ide is an open-source project by Prototyper.View source(opens in a new tab)

tmux-ide home

Agent status detection

tmux agent status: see if Claude Code, Codex or any agent in a pane is working, blocked, done or idle, from hooks, a self-report or a screen fallback.


Agent detection gives every tmux pane an agent status: tmux-ide knows whether each agent is working, blocked, done or idle. It has two layers. tmux-ide trusts an agent's own report first. When there is none, it reads the pane's process and screen, and shows you exactly how it decided.

The same states appear on Home, in the sidebar and pane headers, and in the tmux chrome.

How detection works

The four states are blocked, working, done, and idle (plus unknown when there's no signal at all). A fresh self-report decides the state; without one, tmux-ide infers it from the pane's process and screen.

Systems / Agent detection

Two layers, one status.

how pane %3 gets its status● working! blocked✓ done○ idle

  1. %3A tmux paneworking

  2. @agent_stateSelf-report · pane optionauthority

    a. Ground truth. A fresh @agent_state stamp from Claude Code hooks or any agent always wins and goes straight to the status.

  3. pane_pid → agentProcess treefallback↓ no fresh stamp

    b. Inference. With no stamp, or one older than 10 minutes, tmux-ide finds the real agent and reads its screen.

  4. rule set per agentScreen manifestsfallback

    Infers working, blocked or done

  5. working · blocked · done · idleAgent status

    c. Everywhere. The same status drives Home, pane headers, the chrome and team --json.

explain any panetmux-ide agent explain <pane>

Fig 1.A pane's status comes from its own fresh self-report when there is one; otherwise from its process tree and visible output, matched against per-agent manifests.

How agents report their own state

The first layer is the agent's own report. An agent stamps its pane with a tmux option, and tmux-ide trusts it while it is fresh.

The Claude Code integration

tmux-ide integration install claude

This command writes a small POSIX hook script and registers it in ~/.claude/settings.json for the Claude Code lifecycle events that map to agent states:

Claude eventReported state
UserPromptSubmit, PreToolUseworking
Notificationblocked
Stopdone
SessionEndidle

Each hook invocation stamps the current pane:

@agent_state       "<working|blocked|done|idle>:<unix epoch>"
@agent_session_id  the Claude session id

The session id lets tmux-ide restore --resume-agents bring the conversation back after a crash.

Install and uninstall touch only tmux-ide's entries: they are tagged by the hook-script path, and a one-time backup is written next to settings.json. Hooks are read at session start, so the integration takes effect for new Claude Code sessions.

The self-report contract

You don't need the integration to report state. Any agent or script can write the same pane option:

tmux set-option -p @agent_state "working:$(date +%s)"   # working | blocked | done | idle

The value is <state>:<unix epoch>. A working or blocked report older than 10 minutes is treated as stale, and detection falls back to reading the screen, so long-running agents should re-stamp periodically.

Two optional options add context while the state report is fresh:

tmux set-option -p @agent_display_name "reviewer"         # name shown for the agent
tmux set-option -p @agent_status_text "refactoring auth"  # one-line status

Both are cleaned of control characters and cut to 32 characters. The display name labels the agent in the app, and both appear in tmux-ide team --json.

Detection without a self-report

Without a fresh self-report, the detector works in two steps. First it finds the agent from the pane's process tree, the command actually running in the pane. Then it matches the last lines of the screen against that agent's manifest, a rule set for recognizing working, blocked and done.

This layer is deterministic, you can inspect it, and you can override it.

Which agents are detected?

tmux-ide ships a manifest for each of these agent commands. Tuned manifests were built from real captured screens or the agent's own source strings. Conservative manifests report a state only on strong evidence, such as an explicit prompt or a spinner; anything they miss shows as unknown.

Manifest idAgentFallback
claudeClaude Codetuned
codexCodextuned
aiderAidertuned
opencode, gemini, copilot, cursor, goose, amp, devin, kimi, pi, grok, kiro, cline, droid, kiloOther agent CLIsconservative

Any agent, listed or not, can report its own state, and you can add or replace a manifest. Claude Code also has the hooks integration above. tmux-ide integration install opencode captures opencode session ids so restore can resume those conversations; it does not report state.

Debug a wrong state with agent explain

To see exactly how a pane was classified, ask:

tmux-ide agent explain %3          # a pane id
tmux-ide agent explain %3 --json

It's read-only — it captures and inspects, never sends keys — and prints everything the detector reasoned over:

  • the pane's command and pid;
  • the @agent_state authority option (raw value, parsed verdict, staleness);
  • the @agent_hint override option;
  • which manifest resolved and by which path (hint / fast / tree);
  • each state's rule and whether it matched;
  • the winning classification;
  • the bottom screen lines it judged.

Override detection for a tool

If the fallback ever guesses wrong for a particular tool, pin the manifest with the @agent_hint pane option — agent explain reports whether a hint is applied and which manifest it forced:

tmux set-option -p @agent_hint claude

To tune detection for a tool, drop a manifest into ~/.tmux-ide/agent-detection/ — one JSON file per agent:

{
  "id": "my-agent",
  "commands": ["my-agent", "my-agent-cli"],
  "states": {
    "working": { "any": [{ "contains": "esc to interrupt", "caseInsensitive": true }] },
    "blocked": { "any": [{ "contains": "(y/n)", "caseInsensitive": true }] }
  }
}

A file whose id matches a bundled manifest replaces it; a new id adds one. Invalid files are skipped with a warning. Your files always win over the bundled set and over the optional manifest pack fetched by tmux-ide update --manifests. For authoritative control, prefer the self-report contract above: a fresh @agent_state always wins.

Read agent states as JSON

Whatever the detector resolves is exposed programmatically. tmux-ide team --json carries a per-pane agents[] array on each session — one entry per pane that classified to a real agent, flat across the session's windows:

FieldWhat it is
paneIdtmux pane id, e.g. %5
windowIndexthe window (tab) the pane lives in
sessionowning session name (repeated per entry so a flat list stays keyed)
kindresolved agent — the manifest id (claude, codex, …)
statefinal status (blocked / working / done / idle)
confidencethe manifest's evidence confidence
sinceauthority-state epoch stamp, or null for a scraped/tracked pane
titlepane_title
commandpane_current_command — the immediate process (often node/bun/sh)
dirpane_current_path — the pane's working directory

The app's Home roster, sidebar and pane headers show the same agent states. See App tour.

Common questions

Do I need the Claude Code integration?

No. Without it, tmux-ide still detects Claude Code from its process and screen. The integration makes the state exact and records the session id for restore.

Why does an agent still show working after it stopped?

A working or blocked report expires after 10 minutes; until then tmux-ide trusts it. If a hook did not fire, run tmux-ide agent explain <pane> to see the stamp and its age, and tmux-ide integration status to check the hooks.

Does detection send keys to my panes?

No. Detection only reads pane options, the process tree and the visible screen. agent explain is read-only too.

See also