modisa.Documentation
CLI & API

DOCUMENTATION / Automation

CLI & API

Everything the TUI can do, as shell commands any agent can run.

From inside a pane

Every pane gets MODISA_SOCKET and MODISA_PANE_ID, so anything that can run a command can drive the workspace — and targets default to the calling pane.

sh
modisa pane split --name tests "pnpm test"     # opens next to the caller
modisa wait tests --exited                     # prints "exited <code>"
modisa pane read tests --lines 80
modisa agent spawn codex --name reviewer --prompt "review src/auth"
modisa wait @reviewer --state idle
modisa events --follow

Commands

CommandDoes
pane list | split | run | read | keys | close | rename | focusPanes. Keys are text or names: Enter, C-c, M-x, Up, Escape…
pane move | swap | resize | zoom | focus --directionRearrange panes: move one, process and all, beside another, into a tab, or to a new tab or space; swap two, in a tab or across tabs; resize, zoom, or focus the pane on one side.
pane layout | neighbor | edges | process-infoWhere a pane is: every box in its tab, the pane on one side or on each; and what runs in it: its pid, the job in its foreground, and its shell's directory.
pane attach [target] [--observe] [--remote ssh://host]One pane full-screen in the terminal you run it in: take it over (its size and its typing) until the prefix then d, or only watch it.
agent spawn <harness> | agent listStart and list agents.
wait <target> --exited | --state s | --match reBlock until a process exits, an agent reaches a state, or output matches.
send | inbox | messages | pauseAgent messaging.
workspace create | list | rename | closeSpaces.
tab create | tab listA new tab, optionally running a command; every space's tabs.
snapshotThe whole session as JSON — every space's tabs with their split trees, focus and zoom, every pane, and the area they're drawn in — read without attaching or resizing anything.
events [--follow] [--output]The session's event stream.
notify <title> [--body b] [--tone t] [--system] [--sound]A toast in every attached TUI, titled with the pane that sent it.
reportState and session reports from integrations, and a pane's title (--title).
debug detect [target]What detection sees for a pane.

Targets are a pane id (p3), @name or a name. modisa help lists everything.

sh
modisa pane move tests --new-tab                                    # the same process, in a tab of its own
modisa pane move tests --target @reviewer --split down --ratio 0.3  # below it, with 30% of the room
modisa pane swap tests @reviewer                                    # trade places, even across tabs
modisa pane zoom @reviewer --on
modisa pane read tests --format ansi | less -R                      # with its colours
modisa pane split --ratio 0.3 --env PORT=4000 "bun dev"             # 30% of the room, PORT set in it
modisa notify "tests failed" --body "3 in src/auth" --tone blocked --sound
modisa report --title "Reviewing auth"                              # names this pane until --title ""
modisa snapshot | jq '.workspaces[].tabs[].tree'
modisa pane attach @reviewer --observe                              # watch it full-screen here (q stops)

Moving a pane closes a tab or space it leaves empty, and the view stays where it is unless --focus. pane split, agent spawn, tab create and workspace create print the new pane's id, or with --json the whole pane and where it is (workspaceId, tabId).

pane read prints the last --lines lines as the pane wraps them; --source recent-unwrapped joins the lines it soft-wrapped, --source visible (or --screen) is just the screen, and --format ansi keeps colours and styles. A full-screen app (vim, less, an agent's own UI) has only its screen.

pane attach draws one pane in the terminal you run it in, which is its emulator. By default it takes the pane over: the pane takes that terminal's size and follows it, and typing from anywhere else (the TUI too, whose border says [attached elsewhere]) is dropped, while pane run, pane keys and send still reach it. One terminal takes a pane over at a time; from inside a pane it asks like pane keys, and never attaches to its own. --observe only watches, at the pane's own size. The prefix then d detaches (the prefix twice types it once); it exits 0 then, with the pane's own status if its program exits, and 3 if the connection drops. --remote reaches a pane over ssh, as modisa --remote does.

notify shows in every attached TUI, titled with the sending pane's @name or id (notify from outside a pane). --system and --sound ask for a system notification and a sound, which each client gives only where its own [notify] has that kind on for some event. Each pane may send 3 every 10 seconds, and all senders together 6; past that it exits 1 with rate_limited. With no TUI attached it prints no client attached and exits 0. report --title names a pane over the title its program sets, under an @name, until --title ""; it isn't saved.

The creating commands take --env NAME=value, once per variable (never MODISA_*). It's saved with the session, in a database only you can read, so a restart keeps it. A --cwd of ~ or a relative path is taken from where you run modisa.

Exit status: 0 ok, 1 failed, 2 usage, 3 server unreachable, 124 wait timed out, and wait --exited exits with the pane's code, so a child's own 1, 2, 3 or 124 can't be told from modisa's by status alone: with --json, the child's result is on stdout and modisa's error on stderr. With --json, a failure prints {"error":{"code","message"}} to stderr, with a stable code such as no_such_pane, pane_gone or timeout. Under the hood it's JSON-RPC 2.0 over the session's unix socket.

Commands, keys, agents, settings…