Agents

June 20, 2026 · View on GitHub

repomon runs every agent the same way — each in its own durable tmux window — but it learns each agent's status differently, because each CLI stores its session state differently.

How agents run

When you spawn an agent (New Lane, the e key, or agent.spawn), the daemon launches its CLI in a tmux window named lane-<id> inside the configured session (default repomon):

tmux new-window -t repomon -n lane-7 -c <worktree> '<agent-binary> [task]'

The daemon reads output with capture-pane, sends input with send-keys, and attach gives you the raw session. Because tmux owns the process, the agent survives the daemon and the TUI. The spawned kind is recorded on the lane so repomon can identify it later.

Several agents can run in the same worktree at once: a second spawn (or adopting an external session into an occupied lane) takes the next slot — lane-<id>-2, lane-<id>-3, … — and they run side by side. Fleet and the sidebar mark such a lane with an ×N badge, and Tab/⇧Tab cycle the cursor between a lane's agents in Split/Focus; input and attach route to the cursored one.

Choosing an agent

New Lane lists the auto-detected built-ins (claude-code / codex / aider, marked ✓ if on PATH) plus any custom agents you define — cycle them with Tab (Shift+Tab to go back). The default agent (marked ★) is preselected.

Multiple Claude accounts

Claude keeps each account's data in a config dir (~/.claude by default; a second account is typically run with CLAUDE_CONFIG_DIR=~/.claude-work). repomon scans for these — the default ~/.claude plus any ~/.claude-* holding a projects/ dir, and $CLAUDE_CONFIG_DIR — and offers one agent per account: claude-code (default) and e.g. claude-work (→ CLAUDE_CONFIG_DIR=~/.claude-work claude). No custom config needed. Detection and adopt are account-aware: a work-account session is read from ~/.claude-work/projects and adopting it resumes against that account. (A shell alias like claude-work isn't a real binary, so a custom agent pointing at claude-work won't launch — use the autodetected entry instead.)

Managing agents in-app

Press A from Fleet (or Ctrl+A from New Lane) to open the agent manager:

  • n — add a custom agent: a name (what you pick in New Lane) and a command (the launch command line, run in the lane's worktree). Tab switches fields, saves.
  • e — edit the selected custom agent (built-ins are read-only). Renaming is handled transparently.
  • d — delete the selected custom agent.
  • * — set (or clear) the selected agent as the default; built-ins can be the default too.

Changes are written straight to ~/.config/repomon/config.toml. You can still hand-edit it:

# ~/.config/repomon/config.toml
default_agent = "claude-yolo"

[agents]
claude-yolo = "claude --dangerously-skip-permissions"
claude-resume = "claude --continue"

Note: editing agents in-app rewrites config.toml via the serializer, so hand-added comments in that file are not preserved.

agent.detect returns the combined list (with a default flag); agent.add / agent.remove / agent.set_default mutate the config and persist it; agent.spawn resolves a chosen name to its custom command (if any) or the built-in binary, appends an optional task, and runs it.

Interacting

There are two ways to drive an agent, and they trade off fidelity vs. staying in repomon's chrome.

Open it as a real terminal (the native way) — //a

Pressing (Split/Grid), or / / a (Focus), attaches to the agent's own tmux pane. This is a genuine terminal — there is no difference from running the agent in a plain terminal window: native wheel scrolling and scrollback, character-precise mouse selection, ⌘V image paste straight into Claude, full color, every key.

To come back to repomon, press F12 (single key) — or Ctrl-b d, or Ctrl-b q. A thin status bar along the bottom of the attached pane always shows this. Detaching leaves the agent running in the background; don't type exit or Ctrl-C unless you actually want to end it.

repomon configures its tmux server to feel native: mouse on (wheel scroll + drag-select), set-clipboard on (OSC-52 passthrough), a 50k-line scrollback, drag-select copies straight to the system clipboard via pbcopy, and a status bar showing the detach key. Because you're in the real process, anything the agent supports in a terminal — including image paste — works exactly as it would standalone.

Why attach rather than emulate? The in-app view is a capture-pane picture plus send-keys; it can't carry a nested terminal's scroll wheel or real clipboard-image paste. Attaching hands you the actual PTY, so the focused agent is indistinguishable from native.

Quick mediated type — i

For a fast one-liner without the attach context-switch, i enters insert mode and forwards each keystroke via send-keys — printable chars, Enter, Backspace, arrows, Shift+Tab (Claude's mode cycling), Ctrl-<key> (e.g. Ctrl-C), and Esc (the agent needs it to interrupt/clear). Because Esc is forwarded, leave insert with Ctrl-O. Option/Alt + Arrow (word jump) and Alt + Backspace (word delete) forward too — set Terminal.app → Profiles → Keyboard → "Use Option as Meta key". This view is a snapshot, so:

  • Scroll back with PgUp/PgDn (work in both modes; always reach repomon). Typing or /esc returns to the live tail.
  • Select & copy: drag over lines — copied to the clipboard on release (line-granular).
  • Paste an image: press v — repomon saves the clipboard image to a temp PNG and inserts its path (Claude reads images referenced by path).

For anything the snapshot can't do (precise selection, wheel scroll, ⌘V image paste), just open the real terminal with .

AgentKind::command() maps kinds to binaries:

KindBinary
claude-codeclaude
codexcodex
aideraider
cursorcursor-agent
otherthe kind string itself

Auto-continue on usage limits

When a Claude agent hits its usage limit it prints "limit reached · resets at

  • On by default for every repomon-managed agent. The transcript doesn't record limit info, so detection reads the tmux pane; the "approaching usage limit" warning never triggers it.
  • Per-lane off: press C on a lane to disable auto-continue for it this session (it then shows the normal ⏸ needs you when paused). Globally: set auto_continue = false in config.toml. Change the typed message with auto_continue_message (default "continue").
  • Give-up: after 6 attempts that don't take, it stops and flags the lane needs you so you can step in.
  • Only managed agents (with a tmux window) are touched — external sessions have no window to type into. The detection/parse and the state machine are pure and unit-tested (agent/limit.rs, auto_continue.rs).

Usage corner (usage probe)

With usage_probe = true (a Settings toggle, off by default), the TUI shows agent usage in the bottom-right corner — e.g. 5h 38% · wk 12% · 3:00 PM (limit windows + the soonest reset) — for the account the focused agent runs under. It's provider-aware and per-account: a Claude agent shows its account's /usage (~/.claude vs ~/.claude-work), a Codex agent shows its /status; switch focus and the corner follows.

Subscription usage has no CLI flag, file, or supported endpoint — the only source is an interactive command (Claude /usage, Codex /status). So a daemon watcher (usage_watch.rs), only while a TUI is attached, spawns a hidden throwaway session per account every ~5 minutes, sends the usage command, captures and parses the pane (agent/usage.rs, fixture-tested), then dismisses (Esc) and kills the window. It never sends a model prompt. Numbers are normalized to % used across agents (Codex reports "% left"); windows shown are whatever the tool reports — Claude's 5-hour + weekly, Codex's 5-hour/weekly or (Free plan) monthly. Caveats, by design:

  • It spawns a background agent process briefly per probe (hence opt-in). The probe window is named usage-probe-… (not lane-…) and runs in your home dir, so it never inflates a lane's ×N agent count. The first run accepts the one-time folder-trust prompt for that dir; each probe leaves a tiny (promptless) transcript/session behind. Codex is probed only when it's installed (~/.codex exists).
  • The /usage and /status layouts are undocumented and change between versions. The parsers anchor on labels (not positions) and return nothing rather than wrong numbers; when usage can't be read, the corner falls back to the focused lane's rate-limit countdown (⏳ resume 3:00 PM), or shows nothing. If a tool restyles its screen, recapture the fixture (crates/repomon-core/src/agent/fixtures/) and adjust the parser.

Expanded agent rows + rename

By default a lane running several agents shows as one sidebar row with an ×N badge. Turn on expand agent rows in Settings (,) to instead show the lane as a small tree: the lane header (keeping ×N) with one indented row per agent — ↳ <summary> <status>. The summary is auto-derived (the first 1–4 words of that agent's opening prompt), and each agent's own status glyph is shown, so you can see and select individual agents directly in the Fleet/Split sidebars. Up/down navigate the rows; selecting an agent row makes it the active agent (Enter/focus/attach/stop/keys target it).

Press R on a selected agent row to rename it inline (Enter saves, Esc cancels; an empty name clears the custom label). The label persists in the daemon keyed by the agent's transcript id, so it survives refreshes and daemon restarts, and never bleeds onto a different agent that later reuses the slot. (Sessions without a transcript id yet — a just-spawned placeholder — can't be renamed until their transcript appears.) See session.rename in docs/protocol.md.

External sessions (running in another terminal)

Because status comes from the transcript, a claude you start in any other terminal inside a registered repo's worktree is detected automatically — its status and "needs you" show up on that lane, tagged ·ext (external: repomon didn't spawn it, so it has no tmux window).

If you run several Claude sessions in one worktree, each (a distinct <session-id>.jsonl, active within the last few hours) shows as its own entry in the lane detail — Tab/⇧Tab move the cursor () between them.

repomon can't type into a plain terminal process, so to drive an external session press o to adopt the highlighted one (Fleet/Split/Focus): repomon resumes that exact session with claude --resume <id> (or --continue for the most recent) in a managed tmux lane, after which it's fully interactive here. The original terminal window is left as-is — close it once you've adopted. repomon can manage several agents in the same worktree, each in its own tmux window (lane-<id>, lane-<id>-2, …), so adopting an external session adds a managed agent alongside any already running — and you can observe every external session in the lane detail and choose which to adopt.

How status is detected

Each agent kind has an AgentMonitor (crates/repomon-core/src/agent/). Monitors are tried in priority order; the first to return a summary wins. If none does, the daemon falls back to "is the repomon-spawned tmux window alive?" and shows the recorded kind as Running.

Claude Code — rich status

Transcripts live at ~/.claude/projects/<encoded-cwd>/<session>.jsonl, where the directory name is the working directory with / and . replaced by -. repomon derives:

  • tool-call counttool_use blocks across assistant messages,
  • statusWaiting (the last entry is an assistant turn with no tool call → needs you), Running (mid tool-loop), or Idle (no activity for 10 min),
  • title — first user message or a summary entry.

The encoding scheme has changed before, so it's isolated in claude::encode_project_dir and fixture-tested; matching also falls back to the cwd recorded inside each transcript.

Aider — coarse status

Aider writes .aider.chat.history.md into the working directory. repomon uses that file's modification time: Running if it changed in the last two minutes, else Idle. (There's no reliable "needs you" signal yet.)

Codex — tmux-only for now

Codex's on-disk session format isn't stable enough to parse reliably, so CodexMonitor returns nothing and repomon relies on the tmux-alive fallback for Codex agents it spawned. When the format stabilizes, implement CodexMonitor::summary_for like the others.

Adding a new agent

  1. Add a variant (or use Other) and a binary in AgentKind (model.rs).
  2. Implement AgentMonitor for it in crates/repomon-core/src/agent/ and add it to default_monitors().
  3. Add it to AGENT_KINDS in the TUI so New Lane can spawn it (Tab to cycle).