Skip to Content

IPC (External Control)

renga exposes a local-only IPC so external processes can drive a running instance. Scripts, agents, or another Claude Code session can manipulate panes, snapshot their screens, and subscribe to lifecycle events.

The endpoint is a Unix socket (macOS / Linux) or a Named Pipe (Windows). Access control lives at the OS socket permission layer — only processes running as the same user can reach it (see “Security model” below). RENGA_SOCKET / RENGA_TOKEN environment variables are how clients locate the endpoint; any process spawned inside a renga pane (claude, a shell, a script) inherits them and can call renga subcommands transparently.

Basic subcommands

renga list

List every pane in the active tab:

$ renga list [ { "id": 1, "name": "secretary", "role": "leader", "focused": true, "x": 0, "y": 0, "width": 80, "height": 24 }, { "id": 2, "name": "worker-foo", "role": "worker", "focused": false, "x": 80, "y": 0, "width": 80, "height": 24 } ]

x / y / width / height describe the pane’s rectangle on the terminal grid (origin (0, 0), cells). The rect is refreshed after each layout computation and again after each draw, and it accounts for the file-tree / preview sidebars. Before the first layout pass (e.g. right after startup) all four fields are 0, so treat zeros as “not yet placed” rather than “zero-sized pane”. Clients that don’t know about these fields can safely ignore them.

renga send

Write text to the target pane’s PTY. Add --enter to append a newline so the shell executes the line.

renga send --name worker-foo --enter "git status" renga send --id 2 "Hello" renga send --focused $'\x1b' # send Esc

Target selector: --name <NAME> / --id <N> / --focused. When omitted the focused pane is used.

renga focus

Move keyboard focus.

renga focus --name secretary

renga split

Split a pane. You can seed the new pane with a startup command, a stable name (--id), or a free-form role (--role).

renga split --direction vertical --command "claude" --id worker-a --role worker

Target the pane to split with --target-name <NAME> / --target-id <N> / --target-focused. When omitted, the focused pane is split. --direction vertical | horizontal is required.

renga new-tab

Open a new tab with a fresh single pane.

renga new-tab --command "claude --permission-mode plan" --id worker-plan --role worker --label "plan-session"

Lifecycle monitoring

renga events

Subscribe to pane lifecycle events as JSON Lines.

renga events --timeout 5s --count 10
FlagMeaning
--timeout <DUR>Stop after the given duration (2s / 500ms / 1m, …)
--count <N>Stop after receiving N events. EventsDropped meta-events count toward the budget

If neither flag is set the stream runs until the server closes the connection — supply at least one when scripting, so the client can’t hang forever.

Event types

{ "type": "pane_started", "id": 3, "name": "worker-foo", "role": "worker", "ts_ms": 1700000000000 } { "type": "pane_exited", "id": 3, "name": "worker-foo", "role": "worker", "ts_ms": 1700000005000 } { "type": "events_dropped", "count": 17, "ts_ms": 1700000010000 } { "type": "heartbeat", "ts_ms": 1700000012000 }
  • pane_started / pane_exited: pane lifecycle transitions.
  • events_dropped: number of events the server had to drop for this subscriber (slow consumer signal; reconcile with renga list if you see these).
  • heartbeat: emitted every 30 s as a keep-alive. Consumers can safely ignore it.

Typical one-liner

Watch worker pane exits only:

renga events --timeout 5s \ | jq -c 'select(.type == "pane_exited" and .role == "worker")'

renga inspect

Snapshot the visible screen of a pane. Useful for agents that need to detect approval prompts, error banners, mode indicators, etc. without a human in the loop.

renga inspect --name worker-foo --lines 10 --cursor
FlagMeaning
--lines <N>Return only the bottom N rows of the screen grid
--cursorInclude the cursor position and visibility in the payload

Example response:

{ "status": "ok", "data": { "pane": { "id": 3, "name": "worker-foo" }, "screen": { "rows": 40, "cols": 120, "line_start": 30, "line_count": 10 }, "lines": [ { "row": 30, "text": "Running tests..." }, { "row": 39, "text": "Allow this tool use? (y/n)" } ], "text": "Running tests...\n...\nAllow this tool use? (y/n)", "cursor": { "visible": true, "row": 39, "col": 27 } } }

Blank rows are preserved with text: "" so callers can index lines positionally.

Error codes

IPC failures surface as Error: [<code>] <message> on stderr (renga 0.5.7+). The code is a stable wire ABI; prefer matching on the code over substring-matching the message.

CodeMeaning
shutting_downrenga is shutting down. Clients should stop their loops.
app_timeoutThe UI thread failed to reply in time. Usually transient; retry.
parseJSON parse failure (protocol bug).
protocolProtocol violation (e.g. duplicate hello).
internalServer-side invariant broken (parser lock poison, serialization error).
pane_not_foundThe target pane (id / name / Focused) does not exist.
pane_vanishedResolved on lookup but disappeared before the action — treat like pane_not_found.
split_refusedrenga split refused: MAX_PANES reached or pane already at minimum size.
io_errorPTY write / spawn / OS-level failure.

Shell-side dispatch example:

out=$(renga send --name worker-foo --enter "ping" 2>&1) case "$out" in *"[pane_not_found]"*|*"[pane_vanished]"*) echo "worker closed" ;; *"[shutting_down]"*) exit 0 ;; *"[io_error]"*|*"[app_timeout]"*|*"[internal]"*) echo "transient: $out" ;; esac

Treat unknown codes as non-fatal — new codes may be added in future renga releases, and clients should never crash on them.

Subscriber teardown

renga events subscribers are released promptly when the client half-closes the connection. The server emits a heartbeat every 30 seconds, so the next write into a dead socket fails and unsubscribes the slot — even on quiet workspaces that otherwise go minutes without an event.

Security model

This is a same-user local IPC, not a secrecy boundary. Any process running as the same OS user can read RENGA_TOKEN from /proc/<pid>/environ. OS-level user isolation is the trust boundary.

RENGA_TOKEN exists specifically to detect PID reuse — so a stale RENGA_SOCKET path inherited by a child shell can’t misroute commands to a different renga instance that happened to get the same PID.

Last updated on