Multiplexers
cyber-mux drives seven terminal multiplexers through one contract, so callers never write
host-specific code. But the multiplexers are not the same shape: they disagree on how
many nesting tiers they have, whether they track git worktrees, and whether they can name a pane or
tell you which one is focused. This page is the map of those differences — what each multiplexer
supports and what cyber-mux does when it falls short.
At a glance
Section titled “At a glance”| Capability | tmux | rmux | herdr | WezTerm (alpha) | Zellij (alpha) | cmux (alpha) | otty (alpha) |
|---|---|---|---|---|---|---|---|
| Workspace tier | ✗ (collapses to window) | ✗ (collapses to window) | ✓ | ✓ (a real Window/Workspace split) | ✗ (placement collapses to tab, but occupancy is reported) | ✓ (Window/Workspace) | ✓ (Window) |
| Worktree binding | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| Name a pane | — | — | ✓ | ✗ (throws / warns) | ✓ | ✓ (names its surface) | ✗ (throws / warns) |
| Report focused pane | ✓ (best-effort) | ✓ (best-effort) | ✓ (pane get) |
✓ (list-clients) |
✓ (list-clients) |
✓ (source-read) | ✓ (docs-read) |
| Knows the running harness | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| Size splits | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ (--size, 10–90%) |
| Floating pane | ✓ (tmux 3.7+, new-pane) |
✗ (refused by name) | ✗ (refused by name) | ✗ (refused by name) | ✓ (new-pane --floating) |
✗ (refused by name) | ✗ (refused by name) |
| Focus on open | preserved (-d) |
preserved (-d) |
preserved (--no-focus) |
restored | restored (0.45+) | restored | restored |
| Resize an open pane | ✓ | ✓ | ✓ | ✗ (refused by name) | ✗ (refused by name) | ✗ (refused by name) | ✗ (refused by name) |
| Zoom a pane | ✓ (resize-pane -Z) |
✓ (resize-pane -Z) |
✓ (pane zoom --on/--off) |
✓ (zoom-pane) |
✓ (toggle-fullscreen) |
✗ (refused by name) | ✗ (refused by name) |
| Move a pane beside another | ✓ (move-pane) |
✓ (move-pane) |
✓ (pane move --tab) |
✓ (split-pane --move-pane-id) |
✗ (refused by name) | ✗ (refused by name) | ✗ (refused by name) |
| Break a pane out | ✓ (break-pane) |
✓ (break-pane) |
✓ (pane move --new-tab) |
✓ (move-pane-to-new-tab) |
✗ (refused by name) | ✓ (break-pane, source-read) |
✗ (refused by name) |
Reading the two focus rows
Section titled “Reading the two focus rows”Report focused pane is isPaneFocused,
and it answers three ways, never two: focused, not focused, or unknown. Every backend can now
reach all three — a query that cannot be answered says so rather than returning a confident “no”, so
a caller can tell “the user is elsewhere” from “nobody can tell me”. Callers fail open on unknown.
✓ (best-effort) on tmux and rmux means the answer is derived rather than native: the pane must be
its window’s active pane, in the session’s current window, with a client attached. WezTerm and Zellij
read the attached client directly (list-clients), which is the only reading that survives more than
one tab — a per-tab “active pane” flag is true on several panes at once and cannot answer this.
cmux and otty are marked source-read and docs-read because neither backend can be driven on Linux
CI (#128); their row fields have not been observed
on a running binary.
Focus on open is focusOnOpen, and it has
three values:
- preserved — nothing moves, at any instant, on any route. The backend has a suppress-focus flag, every route passes it, and no route has to visit a pane to choose a split target.
- restored — a focus move happens and is deterministically undone before
open()returns. The caller ends on the pane they started on; a human watching may see a flicker. This is the honest answer for the four backends whose split has no target flag, so the anchor pane must be focused to choose it. - stolen — focus moves and stays moved. No backend declares this today.
The distinction matters because restored and preserved used to share one value. Zellij restores and tmux never moves, and a caller drawing a UI around “will the user’s eye jump” needs to tell those apart.
Drives tmux via its CLI (split-window, new-window, send-keys,
capture-pane, list-panes, …).
- No workspace tier. tmux calls the tab concept a window and has nothing above it, so both
workspaceandtabplacements collapse to a new window — the finest “own visible space” tmux offers. - Never steals focus. Every creating command passes
-d:new-windowalways did, andsplit-windowandnew-panenow do too. Without it tmux activates whatever it just created, so apane:rightopen used to move the attached client onto the new pane. It no longer does, on any placement. - No worktree binding. tmux has no workspace tier to bind a git worktree to, so it omits the
optional
worktreecapability; callers fall back to plain git plus a placement-appropriateopen(). - No harness awareness. tmux cannot say which agent runs in a pane, so
listPanesleavesharnessunset. - Can resize an open pane.
resize-pane -x/-ytakes a cell count, so cyber-mux converts the seam’s ratio against the pane’s own split region rather than sending-x <percent>, which tmux reads against the window — the same number only when the window holds a single split. focusresolves the pane’s session and window fromlist-panes -afirst, then beams in order:switch-client→select-window→select-pane. An unresolvable pane throws rather than issuing a false-success beam.- Floating panes need tmux 3.7 or newer.
--at pane:floatdrivesnew-pane, the command tmux added in 3.7 for a pane that sits above the tiled layout and so resizes none of the region’s other panes. cyber-mux does not probe the version: on an older tmux the command does not exist and tmux answers with its ownunknown command, which surfaces as a failed open naming the command. A float takes tmux’s default size — half the window’s width by a quarter its height — and--ratiois dropped, because a float takes no share of a split whose fraction it could be.
Drives rmux via its CLI (split-window, new-window,
send-keys, capture-pane, list-panes, …) — an async Rust reimplementation of the tmux command
language: rmux list-commands reports roughly 90 commands under tmux’s own names, flags, target
syntax, and #{...} format vocabulary.
rmux runs natively on Linux, macOS, and Windows — no other backend on this page runs natively on Windows, and that is why cyber-mux added it. Read that as a fact about rmux’s own portability: the adapter was built and verified against a live rmux 0.10.0 on Linux, and has not been exercised on Windows or macOS.
- No workspace tier, the same as tmux: both
workspaceandtabplacements collapse to a new window. - Never steals focus.
-don both creating commands,new-windowandsplit-window. Verified live on 0.10.0 rather than inherited from the tmux resemblance: a baresplit-windowmade the new pane active, and-dleft the original active while-P -Fstill reported the new id. - No worktree binding. Like tmux, it has no workspace tier to bind a git worktree to, so it
omits the optional
worktreecapability; callers fall back to plain git plus a placement-appropriateopen(). - Pane identity is tmux-shaped and stable (
%0,%1, …), addressable from outside the session:split-window -P -F '#{pane_id}'andnew-window -P -Freport the new pane’s id from the birth command itself. - Native cwd and env at birth.
-cand-eon bothsplit-windowandnew-windowset the new pane’s working directory and environment directly — unlike WezTerm and Zellij, no command-prefix env compensation is needed. - Can size a split.
-l N%sizes the new pane, the same direction as tmux’s-l, so cyber-mux inverts the caller’s ratio (1 - ratio). - Naming mirrors tmux exactly. A tab is a window, renamed with
rename-window; a pane has no rename verb, so its name is its title, set viaselect-pane -T— which moves no focus. - Workspace grouping uses tmux’s own mechanism. cyber-mux tags panes with
@-prefixed window user options (@cm_ws,@cm_tab) and lists a group with the server-side filterlist-windows -f '#{==:#{@cm_ws},<id>}'. - Reports region geometry.
list-panes -t <id> -F '#{pane_left}…'reports window-relative rects with the divider column excluded, so rmux implements the optionalregionscapability andtemplate saveworks on it. - Can resize an open pane.
resize-pane -x/-ytakes a cell count, so cyber-mux converts the seam’s ratio against the pane’s own split region rather than sending a percentage — rmux reads-x 60%against the window, exactly as tmux does, which is a different number at any nesting depth. Verified on a live rmux 0.10.0. - No harness awareness and no agent-state feed — herdr is the only backend with either.
focusresolves the pane’s session and window fromlist-panes -a, then beamsswitch-client→select-window→select-pane, the same sequence as tmux.- Floating panes: not supported.
new-pane— the tmux 3.7 command--at pane:floatdrives — does not exist on rmux (unknown command: new-pane), so--at pane:floatis refused by name, exactly as WezTerm, herdr, cmux, and otty refuse it — never substituted with a tiled split. - Detection runs before tmux’s. An rmux pane exports
$RMUX(tmux’s own<socket>,<pid>,<session>triple) and$RMUX_PANE(a%Npane id) — but it also sets$TMUX/$TMUX_PANEfor tmux compatibility, so cyber-mux checks the rmux variables first; otherwise an rmux session would resolve to the tmux adapter. The process-ancestry walk matches thermux-daemonprocess;CYBER_MUX=rmuxis the explicit override.
Driven via its CLI (pane split, tab create, workspace create, pane run, pane read, …).
herdr is agent-aware and returns rich JSON envelopes, which cyber-mux parses
defensively.
- Has a workspace tier, and binds git worktrees to it. herdr binds a git worktree to a workspace
as a first-class record, and that binding is what its UI groups a repo’s primary checkout and its
worktrees by — so herdr implements the optional
worktreecapability where tmux and WezTerm cannot. - Only the
worktreeroute binds.git worktree addfollowed byworkspace create --cwd <checkout>yields a workspace with no worktree record — herdr does not know it is a worktree at all, and leaves it out of the group. Onlyworktree create/worktree openproduce the binding. (herdr’sworktree liststill shows such a checkout with anopen_workspace_id, matching it by path after the fact — the list view is misleading here; the workspace record is the truth.) - Creating a worktree opens a workspace for the source checkout too when the repo has none — a group needs its parent.
- Knows the running harness.
listPanesreports each pane’s running harness, because herdr knows which agent is in each pane. - Never steals focus.
workspace create,tab create, andpane splitall pass--no-focus. On herdr 0.8.2 the split already left focus alone without the flag; it is passed anyway so the guarantee does not rest on a backend default that a later release could change.
WezTerm (alpha)
Section titled “WezTerm (alpha)”Driven via wezterm cli (spawn, split-pane, list --format json, send-text, activate-pane,
…) against WezTerm’s built-in multiplexer.
Verified against WezTerm 20240203-110809-5046fc22 — the newest stable release, and the build
CI’s live-backends job pins. WezTerm has shipped no stable release since 2024-02-03, so that pin
is the ceiling rather than a lag. On every PR that job runs mux.wezterm.integration.test.ts
against a real headless wezterm-mux-server: opening at pane:* and at workspace, reading the
pane and its cwd back out of list --format json, running a command in it and capturing the
output, tearing it down, and the two refusals this backend answers by name (--at pane:float, and
rename at the pane tier). Two things it does not cover, which therefore still rest on
wezterm cli --help and the CLI reference: the focus answer, and --percent split sizing. And note
what it drives — a headless mux server, not the GUI a real caller sits in. The gaps below are real,
spec’d limitations rather than forced parity:
- A genuine fourth placement level. WezTerm’s native tiers are Workspace › Window › Tab › Pane.
--at workspacemaps to a real WezTerm Window spawned into a fresh (or caller-named) Workspace, never a bare new tab;--at tabmaps to a real WezTerm Tab in the current window. tmux and herdr both collapse Workspace and Tab onto one level; WezTerm keeps them genuinely distinct. - Never binds a git worktree, despite having a real Workspace tier. Its CLI has no
worktreesubcommand or concept of one, so — like tmux, for the opposite reason — it falls back to plain git plus a placement-appropriateopen(). - No
--envflag on any space-creating command.spawnandsplit-panetake no such option, so every WezTerm open takes the command-prefix-or-warn fallback that herdr needs only for its one worktree route. - No way to title a pane, at birth or after.
rename(..., 'pane', …)throws rather than silently no-op’ing;open’s pane-tier--labeldegrades to a stderr warning instead. A new tab’s label has no birth flag either (unlike tmux-nor herdr--label) — every tab is named by a post-birthset-tab-title. A new workspace’s name, by contrast, is native at birth (it doubles as the--workspacevaluespawnalready takes).listPanes/describeRegionnever report a label at all —titleis always the ambient running-program name, never something an author chose. - A real focus-query primitive, in a place that is easy to miss.
list --format jsoncarries anis_activefield, and it is the wrong one: it is per-tab, so with two tabs open it readstrueon two panes at once, and a probe built on it would confidently name a pane the user is not looking at. The right one islist-clients --format json’sfocused_pane_id, which names the pane the attached client is actually on.isPaneFocusedreads that, and answersunknownwhen no client is attached — a headless mux server has nobody viewing anything, so “not focused” would be a fact read out of an absence. - No suppress-focus flag on any creating verb, so focus is restored rather than preserved.
Neither
spawnnorsplit-panehas a--no-focus, and both activate what they create (measured). So every open readslist-clientsfirst andactivate-panes the caller back afterwards — a visible detour that ends where it started, which is whatfocusOnOpen: 'restored'declares. --percentsizes the new pane — the same direction as tmux’s-l, not herdr’s pass-through — when sizing apane:*split.spawn/split-panereport only the bare pane id — unlike tmux/herdr, its tab (and, on ataborpane:*placement, its workspace) cost one follow-uplist --format jsoncall.
Zellij (alpha)
Section titled “Zellij (alpha)”Driven via zellij action (new-pane, new-tab, write-chars, send-keys, dump-screen,
focus-pane-id, list-panes --json, rename-pane, rename-tab-by-id, close-pane, …) against
Zellij’s built-in multiplexer. Requires Zellij ≥ 0.45.0. Two releases set that floor:
0.44 added per-pane CLI addressing, without which no faithful adapter is possible, and 0.45.0 added
--no-focus, which every open that has no split target to choose now passes. On an older binary
that flag is an unknown argument, so the open fails loudly rather than silently stealing focus.
Verified against Zellij 0.45.0 — the version CI’s live-backends job pins, which is the same
0.45.0 the adapter declares as its floor, so the suite runs at the floor rather than above it. On
every PR that job runs mux.zellij.integration.test.ts against a real headless Zellij: opens at
tab, workspace, pane:right and pane:float; both focus paths (the --no-focus open, and the
focus-and-restore round trip a named from forces); list-panes --json telling a float from a
tiled pane and reporting a pane’s id and cwd; rename; focus/isPaneFocused; run-and-capture and
its lines trim; and teardown. Not covered, because the adapter does not implement them: pane
geometry and resizePane, both refused by name below. The gaps below are real, spec’d limitations
rather than forced parity:
- Has a real session tier, but a placement can’t reach it. Zellij’s workspace-equivalent is a
session, and pane ids are session-scoped — cyber-mux’s pane target carries no session, so it can
only ever operate inside the ambient session. A
workspaceplacement therefore collapses to a new tab, the same way tmux collapsesworkspaceto a window, even though the underlying tier is real. It still reports the occupied session, though:OpenedPane.workspacecarries the ambient$ZELLIJ_SESSION_NAME— occupancy is answered even where placement can’t reach the tier. - Never binds a git worktree. Its CLI has no
worktreesubcommand or concept of one, so — like WezTerm — it falls back to plain git plus a placement-appropriateopen(). - No
--envflag onnew-pane/new-tab. Every Zellij open takes the same command-prefix-or-warn fallback as WezTerm: env rides in as anenv K=Vprefix on the launch command when there is one, or a stderr warning when there isn’t. - Can name a pane, at birth or after.
new-pane --namesets it at birth;rename-paneandrename-tab-by-idrename an already-open space. - Reports focused pane for real — off
list-clients, notis_focused.list-panes --jsoncarries anis_focusedfield per pane, and it istrueon more than one record at a time: a live session marks both the floating plugin pane and the tiled pane beneath it. It reports “focused within its layer”, so a probe reading it can name a pane the client was never on.isPaneFocusedreadslist-clientsinstead — the same authority the focus restore below already used — and answersunknownwhen no client is attached. - Restores focus on open, from Zellij 0.45.0. That release added
--no-focus, which is why the adapter’s floor moved from 0.44.1 to 0.45.0. Atab/workspaceopen, and apane:*open that names nofrom, pass the flag and move nothing. Apane:*open that does name afromcannot: under--no-focusZellij anchors the split on the pane the command was issued from rather than on the focused one, so passing it would split the wrong pane. That path instead focuses the target, splits it, and then focuses back to the pane that had focus before — a visible round trip, ending where it started. That round trip is why this backend declaresfocusOnOpen: 'restored'rather than'preserved': one of its two routes genuinely moves the user before putting them back, and the old boolean had no way to say so. On Zellij < 0.45.0 the open fails loudly with Zellij’s own unknown-argument error rather than silently stealing focus. Both mechanisms are asserted against a real Zellij 0.45.0 in CI’s live-backends job. - Cannot size a split. Zellij’s tiled splits are always even:
new-pane’s size flags (-x/-y/--width/--height) all require--floating, so a tiled split has no flag to honor a ratio with. cyber-mux does drive floats —--at pane:floatisnew-pane --floatingandcanFloatPanesistrue— but a float takes no share of a split whose fraction it could be. Soratiois dropped on both paths, and a tiled caller gets Zellij’s own even split, the same degrade path as a backend with nocanSizeSplits. - No region introspection yet. Pane geometry is deliberately not implemented (a follow-up), so
template saverefuses on Zellij by naming the backend, the same as WezTerm. Zellij does havezellij action resize, but it takes a direction and no amount — a nudge of a size it never states — and with no rects to measure against, nothing can converge that on a named fraction. SoresizePaneis refused on Zellij too.
The common shape
Section titled “The common shape”Each multiplexer answers its own liveness and focus probes, so a herdr pane id is never queried with a tmux command or vice versa. Anything a multiplexer cannot determine — a missing pane, an unreadable focus state — is reported as unknown, never a false negative.
cmux (alpha)
Section titled “cmux (alpha)”Driven via cmux CLI (new-pane, new-surface, workspace create, send, send-key,
read-screen, focus-panel, close-surface, list-panels, rename-tab, …) against
cmux, a Ghostty-based macOS terminal built for AI coding agents.
Not verified against a live binary as of 2026-09-08 — read off cmux’s own Swift source
(manaflow-ai/cmux at commit 71eb616d): the CLI’s argument parser, its own verb inventory, the
server-side payload builders, and its docs/cli-contract.md. cmux is a macOS GUI app, so CI’s Linux
live-backends runner has none to drive: there is no cmux row in that job and no
mux.cmux.integration.test.ts. Unlike the WezTerm and Zellij sections above, nothing here names a
version it was driven against. A source read is stronger than the docs read this section used to rest
on, and it is still not a verification:
- Has a real workspace tier. cmux’s hierarchy is Window → Workspace → Pane → Surface, where a
Surface is the terminal unit (a tab within a pane).
--at workspacemaps to a new workspace;--at tabmaps to a new surface in the current pane;--at pane:*maps to a new pane (a split). - Groups workspaces, unlike every other backend with a workspace tier. cmux has a tier above its
workspace —
workspace-group, a named collapsible sidebar section holding several top-level workspaces — so a--at workspaceopen carrying a workspace group id is really grouped rather than ignored the way herdr’s and WezTerm’s is. The opaque id rides cmux’s--idempotency-key, so re-using it finds the group instead of minting a second one. A--at tabor--at pane:*open is not grouped: it lands in the workspace the caller is already in. One caveat:workspace-group createalways mints a brand-new anchor workspace, so the first grouping call for a given id adds a workspace to the sidebar. It does not steal focus, and repeat calls for the same id open nothing. - Never binds a git worktree. Its CLI has no
worktreesubcommand or concept of one, so — like WezTerm and Zellij — it falls back to plain git plus a placement-appropriateopen(). - Env is native at the workspace tier, and not adopted yet.
workspace createtakes a repeatable--env KEY=VALUE(and--env-file) that later panes and surfaces in that workspace inherit;new-surfaceandnew-panetake none. Every open still takes the same command-prefix-or-warn fallback as WezTerm and Zellij — swapping a working compensation for a native flag would change behavior on source-only evidence, so it waits for a real boundary (#132). - Names a surface, never a pane.
rename-tab --surface <id> --title <text>is the only rename in reach: cmux has no pane-rename verb at any layer, because a pane is a geometric container with no name. A--labelon a split therefore names the split’s surface. A workspace is the exception and is named at birth, byworkspace create --name. - Reports focused surface.
list-panels --jsoncarries afocusedfield per surface, soisPaneFocusedanswerstrue/false— andunknownwhen the row carries no such field, rather than reading that silence as “not focused”. The row shape is a source read, never observed on a running binary, so an absent field is exactly the shape of being wrong here. - Restores focus on open.
new-panehas no split-target flag, so a--at pane:*open with afromhas to focus that surface before splitting it. Every open therefore reads which surface is focused first and focuses it back afterwards. Note that cmux’s creating verbs do take--focus <true|false>and document afalsedefault (#167) — cyber-mux does not pass it, because the restore is correct whether or not that documented default is what ships, and the flag itself is unverifiable without a Mac. - Cannot size a split.
new-panehas no size flag, andresize-paneis cell-based rather than fractional, so aratiodegrades to cmux’s own even split. - Cannot set a split’s directory natively.
new-surfaceandworkspace createboth take--cwd;new-panedoes not. Apane:*open carries the directory as acdon the command line instead — a shell-level cd, so it lands in that surface’s shell history and means nothing in a non-shell surface. - Reports no working directory in the listing. A surface row carries the directory it was
created with, not where its shell is, so
LivePane.cwdis absent on cmux rather than stale. - No region introspection. Pane geometry is not reported by the CLI, so
template saverefuses on cmux by naming the backend. - macOS only (cmux is a native Swift/AppKit app).
otty (alpha)
Section titled “otty (alpha)”Driven via otty CLI (pane split, pane send-keys, pane capture, pane focus, pane close,
tab new, open, panes, …) against otty, a native terminal-centric workspace
app built for AI coding agents.
Not verified against a live binary as of 2026-09-06 — built from the otty docs. Like cmux, otty
is a desktop GUI app (macOS/Windows), so CI’s Linux live-backends runner has none to drive: there
is no otty row in that job and no mux.otty.integration.test.ts, and nothing here names a version it
was driven against:
- Has a real workspace tier. otty’s hierarchy is Windows > Tabs > Splits > Panes.
--at workspacemaps to a new window (otty open, which always opens one — it takes no--new-window, and names the window at birth with--title);--at tabmaps to a new tab (otty tab new --title, which likewise names it at birth);--at pane:*maps to a split,otty pane split --direction right|down. - Never binds a git worktree. Its CLI has no
worktreesubcommand, so — like the other GUI-based backends — it falls back to plain git plusopen(). - No
--envflag on any space-creating command. Every otty open takes the command-prefix-or-warn fallback. - Every tier but
tabsets a directory natively.otty open [path]takes the directory as a positional argument andpane splittakes--cwd. Nothing otty publishes puts a working directory ontab new, so a--at tabopen carries it as acdon the command line instead — a shell-level cd, so it lands in that tab’s shell history and means nothing in a non-shell pane. otty ships no source, so the flag can be neither confirmed nor ruled out from the outside; thecdis the route that is correct either way, where a--cwdotty does not accept would fail every--at tabopen outright. Note thatpane split --cwdappears only in otty’s orchestration guide, not in its CLI reference, whose window/tab/pane section lists no flags at all. - Cannot name a pane. otty scopes
renameto windows and tabs, sotab renamenames an already-open tab and a pane-tierrenameis refused by name — a--labelon a--at pane:*open degrades to a warning instead, exactly as on WezTerm. - Reports focused pane.
panes --jsoncarries anis_focusedfield, soisPaneFocusedanswerstrue/false— andunknownwhen a row carries no such field. otty documents the command but never its row fields, so an absentis_focusedis the likeliest way this is wrong, and it must not read as a confident “not focused”. - Restores focus on open.
pane splithas no target flag, so afromis honored by focusing that pane first; every open reads which pane is focused and focuses it back afterwards. otty’s orchestration guide does showpane split --no-focus(#172), which cyber-mux does not pass: it would not suppress the anchor move, and the restore is correct either way. - Can size a split.
pane split --sizesizes the new pane, soratio— the fraction kept by the original — is inverted (same as tmux, WezTerm, and cmux). otty’s unit is a whole percent over a documented 10–90 range, so the fraction is scaled and rounded, and a ratio outside that range (say0.95, a 5% new pane) is clamped into it with a warning on stderr rather than sent as a size otty would reject. - Atomic send-keys.
pane send-keyscan mix literal text andkey:tokens in one call — cyber-mux composessendTextandsendKeysfrom this. - No region introspection. Pane geometry is not reported, so
template saverefuses on otty.otty pane resize --right Nexists but counts cells, and with no pane positions there is no split extent to take a fraction of, soresizePane— which takes a ratio — is refused as well. That is the contrast withpane split --sizeabove: a share needs no extent to be expressed in, a cell count does. - macOS/Windows desktop app.
GNU Screen — detected, not driven
Section titled “GNU Screen — detected, not driven”cyber-mux detects GNU Screen (a CYBER_MUX=screen override, or a screen ancestor) so it can
say so honestly, but it does not drive it: pinning CYBER_MUX=screen yields a named error, not a
backend. The blocker is identity, which is load-bearing across the whole contract
(MuxTarget.id, currentPane, LivePane.id). Screen addresses its split regions
positionally — there is no per-region id to send to or read from — and $WINDOW is left unset in
windows opened via screen -X, exactly the panes a driver creates, so a pane cannot even identify
itself. Every backend above ships a stable per-pane id (tmux $TMUX_PANE, rmux $RMUX_PANE, herdr
$HERDR_PANE_ID, WezTerm $WEZTERM_PANE, Zellij $ZELLIJ_PANE_ID, cmux $CMUX_SURFACE_ID, otty
$OTTY_PANE_ID); screen has no equivalent for
driven panes. Rather than ship a half-faithful adapter whose pane identity is unstable, cyber-mux
rejects the value with the reason — an honest “no” beats a backend that silently drives the wrong pane.