← Blog

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:

ToolWhat it does
ai_terminal_send_inputSend a command to a session (with safety checks)
ai_terminal_read_screenRead visible terminal text — already stripped of ANSI escapes
ai_terminal_wait_forBlock until a regex matches or output stabilizes
ai_terminal_get_stateStructured JSON: shell state, agent type, intent, pending question, choice prompts
ai_terminal_drive_agentAtomic 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
tmuxTUICommander MCP
Commands per cycle3–5 (send + sleep + capture + parse + retry)1 (drive_agent)
Output formatRaw ANSI with visual noiseClean text + structured JSON
Tokens per read~2000–3000 (raw terminal dump)~300–500 (clean content only)
Idle detectionSleep/guess or poll loopServer-side shell state tracking
Error detectionGrep for "error" in raw outputParsed events: tool-error, api-error
Question detectionRegex on raw text (fragile)Parsed question event with confidence flag
Suggestion chipsNot availableParsed 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:

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 nameAlias
tuicommandertu-1
night-recoverynr-1, nr-2, ...
my-appma-1
myProjectmp-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

  1. Open TUICommander with at least two tabs — one for the agent to be orchestrated, one for the orchestrator
  2. Hover any tab to see its alias in the tooltip (e.g. tc-1), or call ai_terminal_list_sessions to see all aliases
  3. Configure your orchestrating agent (Codex, Claude Code, or custom) to connect to TUICommander's MCP server at http://127.0.0.1:{port}/mcp
  4. 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.