---
name: flmux-panes
description: Drive a running flmux terminal multiplexer — spawn panes, type commands into them, and read their screens — via the `flmux` control-plane CLI. Use when the user asks to control flmux panes, run a command in another pane, orchestrate work across terminals, watch/read a pane's output, or have an agent operate sibling terminals. Requires the flmux app to be running (its JSON-RPC Unix socket at $FLMUX_SOCKET_PATH, default /tmp/flmux.sock).
---

# Driving flmux panes with flmux

`flmux` (pronounced "flummox") is a control-plane client for a running
`flmux` instance. flmux is a Flutter terminal multiplexer; each pane is a
live terminal with a stable integer **surface id**. You drive panes by surface id.

Two interchangeable clients (same interface):
- `flmux <method> [key=value ...]` — compiled binary on PATH.
- `scripts/flmux.sh <method> [key=value ...]` — POSIX sh + python3, no Dart.

If neither is found, the app may not be running, or the socket path differs
(`FLMUX_SOCKET_PATH`, default `/tmp/flmux.sock`).

## Verbs

| Verb | Params | Result |
|------|--------|--------|
| `tree` | — | pane tree: surface ids, `focused`, titles |
| `new-split` | `[direction=row\|column] [surface_id=N] [kind=shell\|monty]` | `{surface_id: N}` of the new pane (`monty` = in-process REPL, no PTY) |
| `send` | `surface_id=N text=STR` | types STR into that pane's process |
| `read-screen` | `[surface_id=N]` | `{text: "..."}` rendered screen as plain text |
| `focus` | `surface_id=N` | makes that pane active |
| `status` | — | flat list: each pane's `activity`/`command`/`cpu`/`last_activity` (for a quick `flmux status` table) |
| `set-status` | `[surface_id] [status] [progress] [label]` | cooperative agent badge/progress/label |
| `set-title` | `[surface_id] title=STR` | sticky display label (empty clears) |
| `close` | `[surface_id]` | kill + remove a pane (e.g. an exited one) |
| `maximize` | `[surface_id]` | surface a pane |
| `subscribe` / `unsubscribe` | — | opt in/out of push notifications (`state`/`event`, no `id`) on a persistent connection |
| `play` | `file=PATH [surface_id=N]` | play an image/GIF as cell-art (GIFs loop); new pane if no surface_id. Needs `chafa`. |

`text=` escapes: `\n \r \t`, `\e` (ESC, for CSI), `\xHH` (raw byte; `\x03`=Ctrl-C,
`\x15`=Ctrl-U). **Submit (Enter) with `\r`** — that's what the Return key sends.
A shell takes `\n` too, but a TUI (e.g. `pi`) treats `\n` as a literal newline
and only submits on `\r`. Recall the last command with `\e[A`.

## Workflow for an agent

1. `flmux tree` — discover existing panes and the focused surface id.
2. `flmux new-split direction=row` — make a worker pane; note the returned `surface_id`.
3. `flmux send surface_id=<id> 'text=<command>\r'` — run a command there (`\r` submits).
4. Wait briefly (commands are async), then `flmux read-screen surface_id=<id>` and
   parse `result.text` to observe output / detect a prompt, error, or completion.
5. Repeat steps 3–4 to steer; use `focus` to surface a pane for the human.

Tips:
- `read-screen` returns the *visible* screen as text — poll it to track progress
  (look for the shell prompt returning, or known output markers).
- To interrupt a running command: `send surface_id=N text=\x03` (Ctrl-C).
- To drive an interactive program, send keystrokes/escape sequences via `send`.

## Example

```sh
flmux new-split direction=row              # -> {"surface_id": 2}
flmux send surface_id=2 'text=npm test\n'
sleep 2
flmux read-screen surface_id=2             # inspect result.text for pass/fail
```
