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 EscTarget selector: --name <NAME> / --id <N> / --focused. When omitted the focused pane is used.
renga focus
Move keyboard focus.
renga focus --name secretaryrenga 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 workerTarget 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| Flag | Meaning |
|---|---|
--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 withrenga listif 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| Flag | Meaning |
|---|---|
--lines <N> | Return only the bottom N rows of the screen grid |
--cursor | Include 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.
| Code | Meaning |
|---|---|
shutting_down | renga is shutting down. Clients should stop their loops. |
app_timeout | The UI thread failed to reply in time. Usually transient; retry. |
parse | JSON parse failure (protocol bug). |
protocol | Protocol violation (e.g. duplicate hello). |
internal | Server-side invariant broken (parser lock poison, serialization error). |
pane_not_found | The target pane (id / name / Focused) does not exist. |
pane_vanished | Resolved on lookup but disappeared before the action — treat like pane_not_found. |
split_refused | renga split refused: MAX_PANES reached or pane already at minimum size. |
io_error | PTY 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" ;;
esacTreat 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.