import { type BindingId, type BindingResolution } from '../keybindings/index.js'; /** POSIX single-quote escaping for one shell word. */ export declare function shellQuote(s: string): string; export declare function inTmux(): boolean; /** The pane currently displaying `nodeId`, found by the authoritative `@crtr_node` * pane tag — set by EVERY viewer (a bare `surface attach`, a focus split, a * spawned viewer) on connect. Scans all panes (`list-panes -a`); the reverse of * `nodeInPane`. Unlike the focus TABLE (which only `registerViewerFocus` writes, * so it misses attach-viewed nodes), the tag is on any pane showing the node. * Returns the pane id, or null when no pane shows it. Works headless (no $TMUX). */ export declare function paneOfNode(nodeId: string): string | null; export interface TmuxLocation { session: string; window: string; pane: string; } /** Where the caller currently is, or null if not inside tmux. */ export declare function currentTmux(): TmuxLocation | null; export declare function sessionExists(name: string): boolean; /** Create a detached session rooted at `cwd` if it doesn't exist. The session * name is a root's tmux home; every node under that root is a window in it. */ export declare function ensureSession(name: string, cwd: string): void; export interface OpenWindowOpts { session: string; /** Window name (the node's display name). */ name: string; cwd: string; env: Record; /** The full command to run in the window (already a shell string). */ command: string; } /** Open a background window for a node and run `command` in it. `-d` keeps it * detached so it doesn't steal focus or become the current window. Returns the * new window id AND the pane id it created (the durable `%pane_id`, LOCATION's * anchor) — callers that only need the window destructure `.window`. * * Target is `${session}:` (trailing colon = the session, no window index) plus * `-a` (insert after the current window) so tmux allocates the next free index. * Passing a bare session name resolves to the session's *active window*, which * makes new-window try to create AT that occupied index and fail with * "create window failed: index N in use" whenever the active window is not the * last one (common when base-index is 0 but the live window sits at index 1). * `-a` also keeps node windows off index 0, which is reserved for the optional * dashboard. The explicit `-t ${session}:` target is the §2.2 HARD DRIVER * INVARIANT — never let new-window fall back to tmux's global current session. */ export declare function openNodeWindow(opts: OpenWindowOpts): { window: string; pane: string; } | null; export interface SplitWindowOpts { cwd: string; env: Record; /** The full command to run in the new pane (already a shell string). */ command: string; /** Stack the new pane below instead of beside (default: beside, `-h`). */ vertical?: boolean; /** Place the new pane BEFORE the target (left for `-h`, above for `-v`) via * `-b`, instead of after it (default: after — right/below). */ before?: boolean; /** Fixed size of the new pane in the split axis' cells (columns for `-h`, rows * for `-v`) via `-l`. Omit ⇒ tmux's default even split. */ size?: number; } /** Split `targetPane`'s window, opening a NEW pane beside it running `command`, * and return the new pane id (the durable `%id`). The ONLY new-pane-beside verb * (Q3: a focus opened side-by-side). `-d` keeps the caller's pane active; `-h` * makes the split side-by-side (left/right), the default for a focus viewport. * * §2.2 HARD DRIVER INVARIANT: `targetPane` is REQUIRED — a bare `split-window` * would split tmux's global current pane, which can leak a pane into an * unrelated user session (the exact bug this design kills). The explicit * `-t ` makes the destination structurally un-leakable. Returns * null if tmux fails. */ export declare function splitWindow(targetPane: string, opts: SplitWindowOpts): string | null; /** Bring a node's window forefront. Switches client across roots when needed. */ export declare function focusWindow(session: string, window: string): boolean; /** Close a node's window (drop it from the UI). */ export declare function closeWindow(window: string): boolean; /** Rename a window (`tmux rename-window -t `). Used by the * root relaunch to re-title the viewer window when its pane is re-pointed at * the freshly-minted node. Best-effort; false if tmux fails. */ export declare function renameWindow(window: string, name: string): boolean; /** Break a single PANE out into a BRAND-NEW window of its own (`tmux break-pane * -d`). `-d` keeps the new window in the background (does not switch the client * to it); `-s ` names the source pane — the §2.2 HARD DRIVER INVARIANT, so * the break never falls back to tmux's global current pane. The pane keeps its * durable `%id` across the move (only its window changes), so callers can keep * using the same pane handle. Returns the new `{window, pane}` location, or null * if tmux fails. Used by `surface tmux-spread` to lift the caller's viewer into a * fresh window before tiling sibling viewers beside it. */ export declare function breakPane(pane: string): { window: string; pane: string; } | null; /** Close a single PANE. Its window closes automatically once this was the last * pane, but sibling panes survive — so co-located nodes (several agents sharing * one window via swap-pane focus) are torn down one at a time instead of all * at once by a window kill. Pane ids are the stable vehicle handle; windows * shift under swap-pane focus, so pane-granular teardown is the correct unit. */ export declare function closePane(pane: string): boolean; /** Open a command in a tmux popup anchored to a pane. The command is a shell * string because tmux executes popup payloads through the user's shell. */ export declare function displayPopup(pane: string, cwd: string, command: string): boolean; /** The active pane id of a window. Node windows are single-pane, so this is the * node's pane. Returns null if the window is gone or tmux fails. */ export declare function paneOfWindow(session: string, window: string): string | null; /** The window a pane currently lives in. Used after a swap-pane to learn which * slot the caller's pane occupied — pane ids are stable across swaps, windows * are not, so the node→window mapping must be re-derived from the pane. Returns * null if the pane is gone or tmux fails. */ export declare function windowOfPane(pane: string): string | null; /** The session + window a pane currently lives in (`display-message -p -t %id`). * The §2.4 reconciliation read-back: resolve a node's/focus's CURRENT * window/session from its durable pane id before any act, so crtr follows a * manual `move-pane`/`join-pane`/`break-pane` instead of fighting it. Null if * the pane is gone or tmux fails. */ export declare function paneLocation(pane: string): { session: string; window: string; } | null; /** Does this pane id still exist? A `display-message` probe on the `%id` — the * v3 PRIMARY liveness probe (§1.2/§2.2), replacing window-existence so a user * moving a pane to another window/session never reads as "gone". True iff tmux * knows the pane. * * NOTE: `display-message -p -t ` EXITS 0 with EMPTY output (it does * not error on an unresolvable pane target) — so an `.ok` check alone would * report a dead pane as alive, defeating the whole point of pane-existence * liveness. We therefore require the echoed `#{pane_id}` to equal the requested * pane: a live pane echoes its own id, a gone/bogus one yields empty. */ export declare function paneExists(pane: string): boolean; /** Does this pane exist AND have its command still RUNNING (`#{pane_dead}` = 0)? * Distinguishes a pane genuinely hosting a live process from a remain-on-exit * corpse frozen after exit (`pane_dead` = 1). Node viewer panes run `crtr surface attach` * as the pane command, so pane-running ⟹ a live viewer occupies it. */ export declare function paneRunning(pane: string): boolean; /** Every live pane id on the server (`list-panes -a`), as a Set for membership * probes. Returns null when tmux is unreachable (no server / transient failure) * so callers can tell "no panes" apart from "can't tell" — a GC pass must skip, * never mass-delete, on a failed probe. One subprocess call total, so batch * liveness sweeps (e.g. the daemon's stale-focus GC) don't pay a per-pane * display-message each. */ export declare function listLivePanes(): Set | null; /** The working directory of a pane (`display-message -p -t * '#{pane_current_path}'`). Used to preserve a view monitor's cwd across a * view-cycle respawn so project-scoped views still resolve. Null if tmux fails. */ export declare function paneCurrentPath(pane: string): string | null; /** Flash a transient status-line toast on the client showing `pane` (or the * current client when `pane` is omitted). `ms` sets the display duration. Used * by `crtr canvas issue new` to confirm a capture from inside its popup. * Best-effort; never throws. */ export declare function displayMessage(message: string, pane?: string, ms?: number): boolean; /** Set a PANE-scoped tmux option (`tmux set-option -p -t `). * Used to tag a pane with the view id it currently hosts (`@crtr_view`) so the * view-nav cycle can read it back and switch to the next/prev view in place. * Best-effort; never throws. */ export declare function setPaneOption(pane: string, name: string, value: string): boolean; /** Read a PANE-scoped tmux option value (`tmux show-options -p -t -q -v * `): `-v` prints only the value, `-q` suppresses the unknown-option * error so an unset option yields an empty string. undefined if tmux fails. */ export declare function getPaneOption(pane: string, name: string): string | undefined; export interface RespawnPaneOpts { /** Target pane id (e.g. `%3`) — the pane to re-exec in place. */ pane: string; cwd: string; env: Record; /** The full command to run in the pane (already a shell string). */ command: string; } /** Re-exec a command in an EXISTING pane, in place — SYNCHRONOUS. Runs the * `respawn-pane` to completion and reports the real exit status. Used when the * caller is NOT the pane being respawned (e.g. the daemon resuming a frozen * focus pane), so it can confirm the respawn landed. Returns true on success. */ export declare function respawnPaneSync(opts: RespawnPaneOpts): boolean; /** The command line a pane was originally started with (`#{pane_start_command}`) * — the exact command `respawn-pane` with no argument re-runs. undefined when * tmux is unreachable or the pane is gone. */ export declare function paneStartCommand(pane: string): string | undefined; /** The pane's root process pid (`#{pane_pid}`) — the process tmux launched in * the pane (the viewer itself, or the login shell hosting it). undefined when * tmux is unreachable or the pane is gone. */ export declare function panePid(pane: string): number | undefined; /** Kill a pane's current process and re-run the SAME command line it was * started with (`respawn-pane -k` with no command reuses the pane's original * command). Used to hot-reload a viewer pane in place: the fresh process * re-resolves `crtr` and picks up the currently selected runtime generation, * while the pane id, geometry, and the node's broker are untouched. */ export declare function respawnPaneInPlace(pane: string): boolean; /** Turn a pi argv array into a single shell command string. * * The binary defaults to `CRTR_PI_BINARY` when that env var is set, else the * bundled pi CLI launched with the current Node binary. CRTR_PI_BINARY is a * TEST-ONLY substitution seam: the integration-test harness points it at a * deterministic fake-pi vehicle so a real `crtr node new` reaches the fake * instead of the LLM pi, without depending on tmux/shell PATH inheritance. An * explicit `binary` arg still overrides the env. The value may be a multi-word * launcher (e.g. `node --import tsx/esm host.ts`); only argv entries are * shell-quoted, so a multi-word binary is spliced verbatim ahead of them. */ export declare function piCommand(argv: string[], binary?: string): string; /** List all window ids present in `session`. Returns [] if the session does * not exist or tmux fails for any reason. Each entry is the raw window id * string reported by tmux (e.g. `@1`, `@2`, …). */ export declare function listWindowIds(session: string): string[]; /** True when both `session` and `window` are present (non-null/undefined) and * the window currently exists inside the session. False whenever either arg * is absent, the session is gone, or tmux does not know the window. */ export declare function windowAlive(session: string | null | undefined, window: string | null | undefined): boolean; /** Activate a window within its session (same-session navigation). Equivalent * to `tmux select-window -t :`. Best-effort; never throws. */ export declare function selectWindow(session: string, window: string): boolean; /** Apply a named tmux layout to a window (`tmux select-layout -t * `). `surface tmux-spread` calls it with `tiled` to evenly grid every * viewer pane in the spread window (and between splits, to redistribute space so * the next split has room). Best-effort; false if tmux fails. */ export declare function selectLayout(window: string, layout: string): boolean; /** Make a pane the ACTIVE pane in its window (`tmux select-pane -t `). A * `split-window -d` keeps the CALLER active, so a freshly-opened viewer pane is * not focused until this runs — `focus` calls it so picking a node (e.g. from the * alt+g graph) lands the keyboard on the new viewer. Best-effort; false if tmux * fails. */ export declare function selectPane(pane: string): boolean; /** Switch the tmux client to a different session (cross-session focus). Runs * `tmux switch-client -t `. Best-effort; never throws. The caller is * responsible for following up with selectWindow to land on the right window. */ export declare function switchClient(session: string): boolean; /** Type a literal (e.g. a `/graph` slash command) into a pane and press Enter * (`tmux send-keys -t '' Enter`). Requires the pane's editor be * empty, same limitation as the menu's `/promote` item. Best-effort. */ export declare function sendKeysEnter(pane: string, text: string): boolean; export interface TmuxInstalledPair { readonly table: string; readonly key: string; } export interface TmuxInstallDiagnostic { readonly code: 'occupied' | 'tmux-error'; readonly bindingId?: BindingId; readonly gesture?: string; readonly message: string; } export interface TmuxInstallResult { readonly ok: boolean; readonly installed: readonly TmuxInstalledPair[]; readonly diagnostics: readonly TmuxInstallDiagnostic[]; } export declare function legacyInboxKeyFile(): string; /** Reconcile the tmux server with one immutable crouter binding snapshot. */ export declare function installTmuxBindings(bindings?: BindingResolution): TmuxInstallResult;