Using Codex to orchestrate Claude Code through TUICommander
Cross-agent orchestration without screen-scraping. TUICommander's MCP bridge gives external agents structured terminal state — shell idle/busy, parsed questions, suggestion chips — instead of raw ANSI bytes. Here's how to set it up and why it beats tmux.
The problem: agents driving agents
You want one AI agent to supervise another. Maybe Codex reviews Claude Code's work. Maybe a coordinator agent dispatches tasks to multiple workers. The underlying mechanic is always the same: send a prompt to a terminal, wait for the agent to finish, read what it produced.
The traditional approach uses tmux: tmux send-keys to type, tmux capture-pane to read. It works. It also means your orchestrating agent sees raw terminal output — ANSI escape codes, box-drawing characters, Ink TUI redraws, cursor movement sequences. The agent spends tokens parsing visual noise instead of understanding results.
What TUICommander gives you instead
TUICommander runs an MCP server on localhost. Any MCP-compatible client (Claude Code, Codex with MCP config, custom agents) can connect and use structured tools for terminal interaction:
| Tool | What it does |
|---|---|
ai_terminal_send_input | Send a command to a session (with safety checks) |
ai_terminal_read_screen | Read visible terminal text — already stripped of ANSI escapes |
ai_terminal_wait_for | Block until a regex matches or output stabilizes |
ai_terminal_get_state | Structured JSON: shell state, agent type, intent, pending question, choice prompts |
ai_terminal_drive_agent | Atomic send→wait→read in one call (new in v1.1.0) |
The critical difference: get_state returns parsed events, not text. TUICommander's output parser runs on every PTY chunk and extracts structured data from the terminal stream — questions, choice prompts, rate limits, progress, error states, suggestion chips. The orchestrating agent gets JSON, not a screenshot.
Setup: connecting Codex to TUICommander
Codex CLI supports MCP servers as tool providers. TUICommander auto-installs itself into Codex's TOML config when you enable the MCP bridge in Settings > Services. The result in ~/.codex/config.toml:
# ~/.codex/config.toml
[mcp_servers.tuicommander]
command = "/path/to/tuic-bridge"
That's it. Codex now has access to all ai_terminal_* tools. Every write tool (send_input, send_key, drive_agent) prompts you for confirmation before executing — TUICommander never lets an external agent type into a terminal silently.
The bridge binary communicates with TUICommander over the local Unix socket — no port configuration needed.
Example: Codex reviews Claude Code's work
You have Claude Code running in TUICommander tab 1. The tab tooltip shows its alias: tc-1 (derived from the repo name "tuicommander"). You start Codex in tab 2 and tell it:
"Review what Claude Code produced in session tc-1. If there are
test failures, tell Claude to fix them."
Here's what Codex does behind the scenes:
// Step 1: Check what's happening (tc-1 resolves to the full UUID)
ai_terminal_get_state({ session_id: "tc-1" })
→ { shell_state: "idle", agent_type: "claude-code",
intent: "Refactored auth module", ... }
// Step 2: Read the output
ai_terminal_read_screen({ session_id: "tc-1", lines: 100 })
→ Clean text, no ANSI. Codex reads the last 100 lines.
// Step 3: If tests failed, send a fix instruction
ai_terminal_drive_agent({
session_id: "tc-1",
command: "The auth tests are failing because the mock doesn't match the new signature. Fix the mock in auth.test.ts.",
timeout_ms: 60000
})
→ { screen: "...", shell_state: "idle",
session_state: { agent_type: "claude-code", ... },
pattern_matched: false }
Three tool calls. The first two are read-only (no confirmation prompt). The third sends a command and waits for Claude Code to finish — one atomic operation instead of the send→poll→poll→poll→read dance.
Example: automated multi-agent pipeline
A more advanced pattern: Codex orchestrates a pipeline across two Claude Code sessions.
"Run the linter in session tc-1. If it finds issues,
send them to session tc-2 to resolve. Repeat until clean."
Codex's execution loop:
// 1. Run linter
ai_terminal_drive_agent({
session_id: "tc-1",
command: "npm run lint",
wait_pattern: "\\d+ problems?|no issues",
timeout_ms: 30000
})
// 2. Check result
// drive_agent returns screen + pattern_matched
// If "0 problems" or "no issues" matched → done
// 3. Otherwise, extract errors from screen and forward
ai_terminal_drive_agent({
session_id: "tc-2",
command: "Fix these lint errors: [extracted from step 1]",
timeout_ms: 120000
})
// 4. Loop back to step 1
The key insight: wait_pattern in drive_agent lets the orchestrator define a structured exit condition. Instead of polling "is it done yet?" over and over, the tool blocks server-side until the pattern matches. Zero wasted tokens on polling.
Why this beats tmux: a token cost comparison
With tmux, every "check what happened" cycle looks like this:
// tmux approach (5 shell commands, ~3000 tokens round-trip)
tmux send-keys -t 0 "fix the bug" Enter // send
sleep 30 // guess at timing
tmux capture-pane -t 0 -p -S -100 // capture raw output
// → 100 lines of ANSI-encoded terminal output
// → agent parses escape codes, box drawing, cursor moves
// → extracts actual content from visual noise
tmux capture-pane -t 0 -p -S -100 // poll again to check
// → same parsing overhead
The raw output from capture-pane includes Ink TUI redraws, progress spinners, ANSI color codes, and box-drawing characters. A typical Claude Code session dump is 60–70% visual noise that the orchestrating agent must tokenize and ignore.
With TUICommander's MCP tools:
// MCP approach (1 tool call, ~500 tokens round-trip)
ai_terminal_drive_agent({
session_id: "tc-1",
command: "fix the bug",
timeout_ms: 120000
})
// → Clean text (ANSI stripped server-side)
// → Structured state (shell_state, agent_type, intent)
// → No polling (blocks until idle)
// → ~500 tokens for the response
| tmux | TUICommander MCP | |
|---|---|---|
| Commands per cycle | 3–5 (send + sleep + capture + parse + retry) | 1 (drive_agent) |
| Output format | Raw ANSI with visual noise | Clean text + structured JSON |
| Tokens per read | ~2000–3000 (raw terminal dump) | ~300–500 (clean content only) |
| Idle detection | Sleep/guess or poll loop | Server-side shell state tracking |
| Error detection | Grep for "error" in raw output | Parsed events: tool-error, api-error |
| Question detection | Regex on raw text (fragile) | Parsed question event with confidence flag |
| Suggestion chips | Not available | Parsed suggest event with items array |
The token savings come from three places: no ANSI overhead in the output (~60% reduction), no polling loops (~50% fewer calls), and structured state that replaces regex parsing on the agent side (fewer reasoning tokens).
The parsed events advantage
TUICommander's output parser detects specific agent states in real time:
- question — the agent is waiting for user input (with a confidence flag that distinguishes explicit prompts from silence-based heuristics)
- choice-prompt — numbered menu (edit confirmation, bash approval, apply patch). Includes the options as a structured array.
- suggest — follow-up suggestion chips. The orchestrator can pick one and send it back.
- rate-limit — API rate limit hit, with retry timing
- usage-limit / usage-exhausted — approaching or at the usage cap, with reset time
- progress — progress bar state (0–100)
- intent — what the agent says it's currently working on
- active-subtasks — how many background workers the agent has running
None of this is available through tmux. With capture-pane, you get pixels. With TUICommander, you get semantics.
Security model
Every write operation from an external MCP client triggers a confirmation dialog in TUICommander. The user sees exactly what will be sent and to which session. This is non-negotiable — an external agent cannot silently type commands into your terminal.
Read operations (read_screen, get_state, get_context) don't require confirmation. Secrets are redacted server-side before the output reaches the MCP client — API keys, tokens, and passwords are replaced with [REDACTED].
Cross-session writes (one agent writing to another agent's terminal) are blocked by default. You must explicitly enable unrestricted mode for the orchestrating session. Read-only cross-session access (monitoring another session's state) is always allowed.
Session aliases: human-friendly names
Raw session IDs are UUIDs — great for machines, terrible for prompts. When you tell Codex "check session a1b2c3d4-e5f6-...", you're burning tokens on noise. TUICommander solves this with automatic session aliases.
Every terminal session gets an alias derived from the repo directory name. The algorithm takes the first letter of each segment (split on -, _, ., or camelCase boundaries) and appends a per-repo counter:
| Repo name | Alias |
|---|---|
| tuicommander | tu-1 |
| night-recovery | nr-1, nr-2, ... |
| my-app | ma-1 |
| myProject | mp-1 |
Aliases are visible in the tab tooltip and in the output of ai_terminal_list_sessions. Every tool that accepts a session_id also accepts an alias — TUICommander resolves it to the full UUID internally. Counters reset on app restart, keeping numbers low.
When two repo names collide on the same acronym (e.g. my-app and my-api), the alias extends with additional letters from the longest segment to disambiguate (ma-1 vs map-1).
Getting started
- Open TUICommander with at least two tabs — one for the agent to be orchestrated, one for the orchestrator
- Hover any tab to see its alias in the tooltip (e.g.
tc-1), or callai_terminal_list_sessionsto see all aliases - Configure your orchestrating agent (Codex, Claude Code, or custom) to connect to TUICommander's MCP server at
http://127.0.0.1:{port}/mcp - Use aliases in your prompts and tool calls — "check session
tc-1" instead of a UUID
The full tool reference is available via the MCP tools/list endpoint. Each tool includes an input schema that your agent can inspect at runtime.
What's next
The drive_agent tool is the beginning. We're exploring event-driven orchestration — instead of the orchestrator polling get_state, TUICommander would push parsed events via the MCP SSE stream when an agent's state changes. That would enable fully reactive pipelines where the orchestrator only wakes up when something interesting happens.
We're also looking at session templates: "spawn a Claude Code session on this repo with these CLAUDE.md instructions" as a single MCP tool call. Combined with drive_agent, you'd be able to stand up and drive a full agent pipeline from a single orchestrating prompt.