Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

tuic CLI

The tuic command line tool lets you control TUICommander from the terminal. It combines the best of VS Code’s code CLI, Zed’s editor integration, and tmux’s session management into a single binary.

Installation

From the app: Settings > General > TUIC CLI > Install TUIC CLI

First launch: TUICommander offers to install the CLI on first run.

From the CLI itself: tuic install-cli

The binary is installed to:

  • macOS: /usr/local/bin/tuic (requires admin password)
  • Linux: /usr/local/bin/tuic (requires sudo)
  • Windows: %LOCALAPPDATA%\Microsoft\WindowsApps\tuic.exe (no admin needed)

The CLI auto-updates silently when TUICommander starts — no manual update needed. Install and update stage the new executable beside the installed path, then rename it into place. A running tuic process keeps its original executable; installing over a symlink replaces the link without changing its target.

Opening Files and Repos

# Open a file (launches TUICommander if not running)
tuic file.rs

# Open at specific line and column
tuic file.rs:42
tuic file.rs:42:10
tuic open --goto file.rs:42

# Open the current directory as a repo (adds it to the sidebar and activates it)
tuic .
tuic /path/to/project

# Open with --wait (for use as $EDITOR)
tuic open --wait file.rs

# Diff two files
tuic diff old.rs new.rs

A directory lands in the sidebar and becomes the active repo. tuic <dir> and tuic open <dir> call the selected running server over local IPC through MCP repo action=add; development builds need no registered OS URL handler. Registration is explicit and takes effect immediately, without a confirmation dialog or a connected desktop window. Reopening preserves existing workspaces, terminals, names and groups. Registration does not create a terminal; use tuic new for a shell. Server errors produce a nonzero exit code.

Temporary directories are refused

tuic <dir> registers the directory permanently — the sidebar keeps it across restarts. A temporary directory therefore becomes a row that points at something the operating system later deletes, and nothing records who added it. tuic refuses such a path instead:

$ tuic "$TMPDIR/scratch"
Refusing to register "/var/folders/.../T/scratch" as a repository: it is inside a
temporary directory, which the OS deletes while the sidebar entry stays behind forever.
For a shell there instead: tuic new /var/folders/.../T/scratch

A path is refused when it is at or below the temp root of the shell you run tuic from (TMPDIR on macOS and Linux, TEMP/TMP on Windows) or below /tmp. Running the check in the CLI rather than in the app is what makes a custom TMPDIR — for example the ~/Gits/.tmp this repository’s Rust suites use — count as temporary; the app process has a different one and cannot see yours.

Only registration is refused. tuic new <dir> still opens a shell in a temporary directory, because a session is disposable in the same way the directory is.

Using as $EDITOR

export EDITOR="tuic open --wait"
git commit  # opens commit message in TUICommander

Session Management

These commands mirror tmux semantics:

# List all sessions (short IDs; --json for scripts)
tuic ls
tuic ls --json

# Create a new session
tuic new
tuic new -n "my-session"
tuic new -n "build" /path/to/repo

# Create a session and run something in it
tuic run pnpm dev
tuic run -n "tests" cargo nextest run

# Send input to a session
tuic send <id-or-name> "make test" Enter

# Capture session output
tuic capture <id-or-name>
tuic capture <id-or-name> -n 50          # last 50 lines
tuic capture <id-or-name> --format raw

`capture` supports `raw` and `text`; `--format log` is not supported by the
CLI and returns an error.

# Kill a session
tuic kill <id-or-name>

# Resize a session
tuic resize <id-or-name> 120x40

# Pause/resume output
tuic pause <id-or-name>
tuic resume <id-or-name>

Session targets are resolved by the server. Use the PTY ID, its stable tuic_session, the terminal alias (for example tu-33), a unique short PTY-ID prefix, or a unique display name; an ambiguous target is rejected rather than guessed.

Sending keys

Each argument is either a key name or literal text — matched whole, never as a substring, so tuic send build "Enter the room" types the sentence instead of pressing Return mid-word. Adjacent literals are joined with a single space.

Key names: Enter, Space, Tab, Escape, BSpace, Up, Down, Left, Right, Home, End, PageUp, PageDown, and any C-<letter> (C-c, C-d, C-u, …).

Agent Orchestration

Generic MCP calls

Use tuic mcp <tool> [<json>|-] to call a native MCP tool over the local socket. Omit the JSON argument for {}, or pass - to read a JSON object from stdin. The tool’s text payload is printed unchanged with a trailing newline, so it can be piped to jq:

tuic mcp session '{"action":"list"}' | jq length
tuic mcp agent '{"action":"wait","timeout_ms":8000}'
cat <<'JSON' | tuic mcp agent -
{"action":"send","to":"peer-id","message":"it's ready"}
JSON

Tool and protocol errors go to stderr with exit code 1. Invalid JSON or CLI arguments exit 2 before connecting. Managed callers send $TUIC_SESSION for peer binding; callers outside TUICommander register an external identity for that invocation.

Register an existing local directory directly through MCP:

tuic mcp repo '{"action":"add","path":"/absolute/path/to/project"}'

Both forms use the same backend registration operation. Git directories get a workspace for their checked-out branch; plain directories get a shell workspace. ui action=tab with a tuic://open-repo URL returns an error directing the caller to repo action=add.

Detached commands

Run tuic bg <log> -- <cmd> [args...] from a managed terminal to return at once while the command runs in a separate process group. The command’s stdout and stderr append to <log>, and its exit code is written to <log>.exit. When it finishes, tuic requests a BG DONE exit=<code> log=<log> cmd=… wake for the originating session. If that session is busy, the wake is queued until it becomes idle. <log>.wake records the request outcome as JSON: queued, mailed, retrying, or failed, with tuic_session and the number of queue attempts. A queue lookup or request failure falls back to MCP agent mail addressed to the same TUIC_SESSION; that mail includes the BG DONE text and the queue error. queued means the queue took the request, not that the agent later submitted it. mailed means the mail was surfaced to the caller; inbox-only mail remains a failure. If both paths fail after a transient socket error or server error, the runner retries up to six queue-then-mail attempts with exponential delays of 0.2–3.2 seconds (6.2 seconds of total retry waiting). A permanent queue rejection ends the retry immediately after mail fails. retrying records an attempt still in progress; failed records the last queue and mail errors. Each IPC read has its own three-second deadline, so a slow or unavailable server can extend the elapsed time beyond the retry waiting period. The command works on macOS, Linux, and Windows.

tuic bg "$HOME/Gits/.tmp/build.log" -- make check

On Windows, the detached runner does not inherit the launcher’s input or output pipes. Capturing the launcher output therefore returns before the background command completes.

Each background runner generates one completion key and reuses it on queue retries. The backend remembers the last 128 accepted keys per live terminal, so a lost reply does not enqueue the same completion again within that window. A recognized acceptance remains queued in the wake file even after the queue has drained. If every queue receipt is lost and mail also fails, the wake file reports uncertain, not a proven delivery failure; inspect backend state before retrying manually. uncertain also prevents automatic child closure.

TUIC_SESSION is required; without it, tuic bg exits 2 before starting a command. The launcher prints the wake-status path and removes stale .exit and .wake files before detaching. If no BG DONE arrives, inspect .exit for command completion and .wake for both queue and mail failures. Both errors are also appended to the log. Neither status file can start a new agent turn while TUICommander is unavailable.

The instance config directory also holds bg-wakes/<TUIC_SESSION>.json. It is marked retrying while a background command is active or its wake is being retried, then records the final result. Automatic managed-child idle closure reads this record and keeps a child open after a failed wake.

# Spawn an AI agent (the prompt is required — the agent starts on it)
tuic agent spawn claude "review the failing tests"
tuic agent spawn codex "add a changelog entry" --repo /path/to/repo
tuic agent spawn codex "review" --name reviewer --model gpt-5 --args exec --args --full-auto \
  --cwd /path/to/repo --rows 40 --cols 120 --json

# List running agents
tuic agent ls

# Deliver a message to a registered peer's INBOX (peer registry)
tuic agent send <peer-uuid> "fix the tests"

# Type a prompt into an agent's TERMINAL and submit it (no peer routing)
tuic agent type <id-or-name> "fix the tests"

# Wait for mail or inspect peers without polling
tuic agent wait --timeout-ms 60000 --json
tuic agent inbox --json
tuic agent list-peers --path /path/to/repo --json
tuic agent stats --json

# Server-owned session state and output
tuic session status <id-or-name> --json
tuic session wait <id-or-name> --until idle --timeout-ms 60000 --json
tuic session output <id-or-name> --limit 50 --json

# Server-owned worktree lifecycle
tuic repo worktree-list /path/to/repo --json
tuic repo worktree-create /path/to/repo --branch feature/task --spawn-session --json
tuic repo worktree-remove /path/to/repo <branch> --json

agent wait and session wait size their IPC read timeout from --timeout-ms (60 seconds by default) with a five-second transport margin. MCP worktree creation and removal allow 305 seconds on Unix, four seconds beyond the server’s 301-second request limit. Other Unix CLI requests retain a three-second read timeout, so a stalled app fails promptly. A timed-out MCP action may still complete on the server; inspect its state before retrying.

The orchestration commands above call the same MCP tools as an agent. They use the local mcp.sock transport and send $TUIC_SESSION as x-tuic-session, so a child spawned from a managed terminal records that terminal as its parent. Outside TUICommander, tuic prints one notice and registers a headerless external MCP caller. That caller is the parent of a child it spawns, but its identity lasts only for that tuic invocation, so a later tuic call cannot read the mail the child sends back. Use --json for the unmodified server payload. A server error is printed to stderr and makes tuic exit non-zero.

Two delivery channels, chosen explicitly

tuic agent send and tuic agent type are not interchangeable, and neither guesses which one you meant.

CommandRouteTargetUse when
tuic agent sendpeer registry → recipient inboxa registered peer’s tuic_session UUIDthe recipient is an orchestrator or any peer, including one with no terminal of its own
tuic agent typePTY writea session ID or nameyou want the text to appear in a terminal and be submitted
tuic sendPTY writea session ID or nameraw keys, no agent framing (see Sending keys)

tuic agent send is the CLI counterpart of the MCP agent action=send tool and uses the same delivery path, so both report the same delivery_path and both land the payload exactly once. It exits non-zero — with the registry’s own message — when the recipient is not registered or the message is empty.

Acceptance is not delivery, and the output says which one you got:

Delivered to <peer> (sse_channel_and_inbox)

means something surfaced the message — a waiter, the SSE channel, or the recipient’s terminal. Whereas:

Buffered for <peer> (inbox_only) — unread until the recipient polls its inbox
warning: Recipient has NO terminal and no active wait: nothing will wake it. …

means the registry took the message but nothing will wake the recipient: it sits unread until that peer calls agent action=wait/inbox. Both exit 0, because the registry accepted the message in both cases — do not block on an answer after a Buffered line. The CLI validates this current report contract through message_id, delivered, and delivery_path; it does not require the removed accepted compatibility field.

tuic agent type keeps the agent-safe framing: the text and the Enter are sent as separate PTY writes, because a raw-mode Ink TUI treats a combined text\r as a prefill and leaves it unsent. tuic send does not do this.

tuic agent send uses $TUIC_SESSION when it runs inside a TUICommander session. Outside one, it registers a headerless external caller for the MCP connection and prints a notice on stderr. It has no terminal, automatic wake-up, or managed parent relationship; a spawned child cannot reply to it.

tmux Compatibility

tuic can act as a drop-in replacement for tmux. When invoked as tmux (via symlink), it translates tmux commands to TUICommander equivalents.

Setting Up the Alias

# Create tmux -> tuic symlink
tuic alias

# Remove the alias (restores original tmux if installed)
tuic alias --remove

Supported tmux Commands

When invoked as tmux, the following commands are supported:

tmux CommandBehavior
tmuxCreate new session in cwd
tmux new-session -s nameCreate named session
tmux list-sessionsList sessions
tmux kill-session -t targetKill session
tmux kill-serverKill all sessions
tmux send-keys -t target "cmd" EnterSend input
tmux capture-pane -t targetCapture output
tmux resize-pane -t target -x 120 -y 40Resize
tmux attach-sessionFocus TUICommander window
tmux has-session -t targetCheck if session exists (exit code)

Key names are translated: Enter, Space, Tab, Escape, C-c, C-d, C-z, etc.

System Commands

# Check TUICommander status — version, session/agent counts, and which
# sessions are waiting on you right now
tuic status

# Install CLI to system PATH
tuic install-cli
tuic install-cli --path /custom/path

# Create/remove tmux alias
tuic alias
tuic alias --remove

IPC Architecture

The CLI communicates with TUICommander via IPC:

  • macOS/Linux: Unix domain socket at <platform config dir>/com.tuic.commander/mcp.sock for the default instance.
  • Windows: Named pipe at \\.\pipe\tuicommander-mcp

Use tuic --instance <id> <command> or TUIC_APP_INSTANCE=<id> tuic <command> to select the same namespace as tuic-remote --instance <id>. An explicit --instance takes precedence over the environment. Invalid ids fail before any connection. Named Unix sockets use the server’s short SHA-256-based name in the OS temp directory. The bridge accepts tuic-bridge --instance <id> and the same environment variable; it searches only that instance’s alternative sockets.

On Unix, TUIC_SOCKET overrides the selected socket path. Windows retains the existing named-pipe endpoint. The clients and server share instance validation, config paths and IPC names through tuic-ipc; CLI and bridge also share HTTP request and response framing.

If TUICommander is not running, tuic open and tuic new will launch it automatically.