# libtmux for Swift > The Swift port of libtmux (libtmux-swift). Every code sample below is Swift; the same pages exist for the other nine ports under their own prefix. - [Swift API reference](https://libtmux.org/en/swift/latest/reference/): every public symbol, generated from the source. Hosted on libtmux.org. --- # tmuxp load Source: https://libtmux.org/en/swift/latest/workspace/cli/load/ > Load a workspace file, saved workspace name, or project directory. Multiple inputs build in order; without `-d`, the final session is attached or selected through the current-client flow. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. The local Swift `tmux-workspace load` attaches from a foreground terminal and offers switch, detached, append or cancel choices inside tmux. `-y` skips the mode prompt but refuses an ambiguous client selection. Redirected and machine calls require `-d` or `--append`. Inputs are validated before prompting, and detaching or interrupting the client preserves the loaded workspaces. Clients using independent `active-pane` focus on the invoking pane's physical window prevent native switching. The `n` (detached) and `a` (append) choices remain available; such clients on other windows do not block switching. If the selected client gains `active-pane` before handoff, switching fails and the loaded workspace is preserved. Load a workspace file, saved workspace name, or project directory. Multiple inputs build in order; without `-d`, the final session is attached or selected through the current-client flow. ## Loading and attachment Create [`workspace.yaml`](../../guides/installation/#create-the-input) using the [installation walkthrough](../../guides/installation/), then load it on that walkthrough's dedicated socket: ```console $ tmuxp load \ -S "$WORKSPACE_TMP/tmux.sock" \ -d \ "$WORKSPACE_TMP/workspace.yaml" ``` `-d` avoids attachment. Inside an existing tmux client, the normal interactive flow can switch to the new session, append windows, or stay detached. `--append` explicitly selects the append flow and needs a current target session. An existing session is handled through tmuxp's load policy; loading is not a declarative reconciliation operation that removes surplus windows. Put flags before the complete group of filenames. The reference accepts flags before or after that group, but a flag between two filenames can cause an argument error. `-s` overrides the final input's session name when several files are loaded. tmuxp parses `-2` and `-8` as mutually exclusive flags, separate from the CLI text's `--color` setting. Legacy `-8` is unsupported; [tmux removed 88-color support](https://raw.githubusercontent.com/tmux/tmux/3.2a/CHANGES). ## Progress and script output `--progress-format` accepts `default`, `minimal`, `"window"`, `"pane"`, `verbose`, or a custom format. Available tokens include `{session}`, `{window}`, `{window_index}`, `{window_total}`, `{window_progress}`, `{window_progress_rel}`, `{windows_done}`, `{windows_remaining}`, `{pane_index}`, `{pane_total}`, `{pane_progress}`, `{progress}`, `{session_pane_progress}`, `{overall_percent}`, `{bar}`, `{pane_bar}`, `{window_bar}`, and `{status_icon}`. The Python reference panel defaults to three lines. `--progress-lines 0` hides the panel and sends script output to stdout; `-1` permits all available lines up to terminal height. `--no-progress` disables animation. See [environment settings](../../configuration/environment/) for environment bindings and [command ordering](../../configuration/commands/) for what is executed. Native Swift also defaults to three panel lines. Hiding its panel preserves each bootstrap stream's original destination. The panel updates while the script runs, retains at most 65,536 UTF-8 bytes and uses the initial terminal size without tracking resize events. JSON and NDJSON disable the panel and send live bootstrap chunks as structured stderr warnings. Cancellation clears the panel and attempts bounded final output; see the [child output contract](../../reference/output/#native-swift-child-output). ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `"workspace_files"` | one or more | filepath to session or filename of session in tmuxp workspace directory | | `-L` | value; None | passthru to tmux(1) -L | | `-S` | value; None | passthru to tmux(1) -S | | `-f` | value; None | passthru to tmux(1) -f | | `-s` | value; None | start new session with new session name | | `--yes`, `-y` | flag; False | always answer yes | | `-d` | flag; False | load the session without attaching it | | `-a`, `--append` | flag; False | load workspace, appending windows to the current session | | `-2` | flag; None | force tmux to assume the terminal supports 256 colours. | | `-8` | flag; None | legacy 88-colour flag; unsupported by tmux 3.2a+ | | `--log-file` | value; None | file to log errors/output to | | `--progress-format` | value; None | Spinner line format: preset name (default, minimal, window, pane, verbose) or a format string with tokens {session}, {window}, {progress}, {window_progress}, {pane_progress}, etc. Env: TMUXP_PROGRESS_FORMAT | | `--progress-lines` | value; None | Number of script-output lines shown in the spinner panel (default: 3). 0 hides the panel entirely (script output goes to stdout). -1 shows unlimited lines (capped to terminal height). Env: TMUXP_PROGRESS_LINES | | `--no-progress` | flag; False | Disable the animated progress spinner. Env: TMUXP_PROGRESS=0 | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/load.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp freeze Source: https://libtmux.org/en/swift/latest/workspace/cli/freeze/ > Capture a live session as a starting workspace file. A capture records observable session state; it cannot recover the original scripts, plugin intent, comments, or every application state. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Capture a live session as a starting workspace file. A capture records observable session state; it cannot recover the original scripts, plugin intent, comments, or every application state. ## Export a named session After the [installation walkthrough](../../guides/installation/) creates `workspace-guide`, export it to a new destination: ```console $ tmuxp freeze \ -L workspace-guide \ --workspace-format yaml \ --save-to captured-workspace.yaml \ --yes \ workspace-guide ``` Without a session argument, tmuxp resolves or asks for a live session. Without a format or destination, it can ask for those choices. `--yes` answers yes/no questions; it does not supply every missing selection. `--quiet` suppresses explanatory status, but prompts can still occur. Successful export writes the workspace document to a file, not stdout. Declining a confirmation can return without saving. In this reference, an explicit `--save-to` path bypasses the overwrite confirmation used by the prompted path. Select a new path deliberately. The native contract applies the same protection to explicit and prompted destinations and keeps `--force` distinct from `--yes`. Inspect the capture before reloading. See the [export and reload workflow](../../guides/export-session/) and the [native machine output](../../reference/output/) for the distinction between a saved file's format and a CLI result stream. ## What the captured document holds The seven native ports agree on three things a capture records, and on one it does not. **Window options are written under `options_after`.** tmux applies them after the panes exist, and options like `automatic-rename: off` do not hold if they are applied before. `load` accepts either spelling. **A pane sitting at a shell gets no `shell_command`.** Naming the shell would start a shell inside a shell when the document is reloaded. The shell is recognised by name as well as by the session's `default-shell`, because macOS runs bash for `/bin/sh` and the pane reports `bash`. **Nothing about the machine it was captured on.** No session environment, so a document does not carry the capturing host's `SSH_AUTH_SOCK` or `DISPLAY`, and no `default-size`, which would otherwise pin a reloaded session to the size of the terminal that captured it. ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `"session_name"` | optional | | | `-S` | value; None | pass-through for tmux -S | | `-L` | value; None | pass-through for tmux -L | | `-f`, `--workspace-format` | value; None | yaml, json | | `-o`, `--save-to` | value; None | file to save to | | `--yes`, `-y` | flag; False | always answer yes | | `--quiet`, `-q` | flag; False | suppress explanatory/status text; prompts still occur despite the parser help claiming otherwise | | `--force` | flag; False | overwrite the workspace file | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/freeze.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp convert Source: https://libtmux.org/en/swift/latest/workspace/cli/convert/ > Convert a workspace document between YAML and JSON while retaining its mapping keys. Conversion does not prove that a native builder supports every retained field. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Convert a workspace document between YAML and JSON while retaining its mapping keys. Conversion does not prove that a native builder supports every retained field. ## Change the file format Given the [`workspace.yaml`](../../guides/installation/#create-the-input) from the [installation walkthrough](../../guides/installation/), convert it to [`workspace.json`](./): ```console $ tmuxp convert \ --yes \ workspace.yaml ``` The destination has the same stem and opposite extension. In the reference, `--yes` permits replacement of an existing destination without an additional existence check. The original input remains. YAML comments and textual formatting do not round-trip through JSON. Conversion uses the complete document mapping. A serializer for a reduced native workspace struct can silently discard extension keys and is insufficient for this command. The native machine mode returns a document without writing a guessed destination; its extra save and overwrite controls are described in [output](../../reference/output/). ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `workspace_file` | required | checks tmuxp and current directory for workspace files. | | `--yes`, `-y` | flag; False | always answer yes | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/convert.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp edit Source: https://libtmux.org/en/swift/latest/workspace/cli/edit/ > Resolve a saved workspace or file and open it in the configured editor. The lookup rules are shared with loading. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Resolve a saved workspace or file and open it in the configured editor. The lookup rules are shared with loading. ## Open a workspace With [`workspace.yaml`](../../guides/installation/#create-the-input) already saved and `$EDITOR` set to a single executable: ```console $ tmuxp edit workspace.yaml ``` The reference passes the entire `$EDITOR` value as one executable string followed by the file path. An editor command containing arguments is not tokenized. It waits for the child process but does not propagate that child's exit status. Use an editor wrapper executable when arguments are needed. The native contract defines argument parsing and propagates child failure. These are deliberate behavior changes, not existing Python guarantees. See [discovery](../../guides/discovery/) and [environment](../../configuration/environment/). ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `workspace_file` | required | checks current tmuxp and current directory for workspace files. | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/edit.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp debug-info Source: https://libtmux.org/en/swift/latest/workspace/cli/debug-info/ > Collect tmuxp, Python, tmux, configuration, and environment diagnostics for troubleshooting. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Collect tmuxp, Python, tmux, configuration, and environment diagnostics for troubleshooting. ## Inspect structured diagnostics ```console $ tmuxp debug-info --json ``` The command writes one JSON object. Named path fields mask the home directory, but raw tmux output arrays are preserved. Review diagnostics before sharing them because names, commands, and raw tmux values may identify your environment. `--ndjson` is a native extension. The native result should name the port and runtime and define redaction for raw values. See [troubleshooting](../../guides/troubleshooting/) and [output](../../reference/output/). ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `--json` | flag; False | output as JSON | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/debug_info.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp ls Source: https://libtmux.org/en/swift/latest/workspace/cli/ls/ > List discovered project and saved workspace files, with optional grouping and configuration content. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. List discovered project and saved workspace files, with optional grouping and configuration content. ## List workspace records ```console $ tmuxp ls --json ``` JSON output is an object with `workspaces` and `global_workspace_dirs`; no workspaces still produces the object with an empty array. `--ndjson` emits one workspace record per line and emits zero lines for no records. When both format flags are supplied, NDJSON wins. `--full` includes full configuration content. `--tree` groups the human display by directory. Discovery includes nearest project configurations and configured global directories. It is not a recursive scan of every descendant directory. See [discovery rules](../../guides/discovery/) for precedence and candidate names. ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `--tree` | flag; False | display workspaces grouped by directory | | `--json` | flag; False | output as JSON | | `--ndjson` | flag; False | output as NDJSON (one JSON per line) | | `--full` | flag; False | include full config content in output | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/ls.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp search Source: https://libtmux.org/en/swift/latest/workspace/cli/search/ > Search discovered workspace fields using regular expressions or literal strings. Queries combine with AND unless `--any` selects OR. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Search discovered workspace fields using regular expressions or literal strings. Queries combine with AND unless `--any` selects OR. ## Find workspaces by session name ```console $ tmuxp search \ --json \ --fixed-strings \ --field session \ workspace ``` Field prefixes in query terms and repeated `--field` restrictions select name, session (`s`), path (`p`), window (`w`), or pane data. `--ignore-case` ignores case; `--smart-case` does so only when a pattern has no uppercase. `--word-regexp` requires whole words and `--invert-match` selects nonmatches. JSON results contain `"name"`, `"path"`, `"session_name"`, `"source"`, `matched_fields`, and `"matches"`. The pinned reference emits an empty byte stream for no matches, even with `--json`. With no query, machine search can print human help and return normally. An invalid regular expression can also yield no machine output. The native contract instead uses `[]` for an empty JSON result and usage status 2 for a missing or invalid pattern. Python regular-expression behavior is part of compatibility. Native regex libraries differ in lookaround, backreferences, Unicode, flags, and word boundaries. Matching the options alone does not establish expression equivalence. See [compatibility](../../reference/compatibility/) and [output](../../reference/output/). ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `"query_terms"` | zero or more | search patterns (prefix with field: for field-scoped search) | | `-f`, `--field` | value; None | restrict search to field(s): name, session/s, path/p, window/w, pane | | `-i`, `--ignore-case` | flag; False | case-insensitive matching | | `-S`, `--smart-case` | flag; False | case-insensitive unless pattern has uppercase | | `-F`, `--fixed-strings` | flag; False | treat patterns as literal strings, not regex | | `-w`, `--word-regexp` | flag; False | match whole words only | | `-v`, `--invert-match` | flag; False | show workspaces that do NOT match | | `--any` | flag; False | match ANY pattern (OR logic); default is ALL (AND logic) | | `--json` | flag; False | output as JSON | | `--ndjson` | flag; False | output as NDJSON (one JSON per line) | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/search.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp shell Source: https://libtmux.org/en/swift/latest/workspace/cli/shell/ > Open a Python shell with tmux objects available, or evaluate Python using `-c`. This command remains Python-specific even when reached through a native command. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Open a Python shell with tmux objects available, or evaluate Python using `-c`. This command remains Python-specific even when reached through a native command. ## Evaluate with a selected server Continue the [installation walkthrough](../../guides/installation/) through its detached load, leaving `workspace-guide` running in the same shell: ```console $ tmuxp shell \ -S "$WORKSPACE_TMP/tmux.sock" \ -c 'print(server.sessions)' \ workspace-guide editor ``` Use `-c`; the reference does not define `--command`. Optional session and window arguments select context. `--best` chooses the best available shell backend; the explicit selectors require their corresponding Python packages. The `--pdb` path uses a debugger rather than a normal REPL. The paired `--use-pythonrc` / `--no-startup` and `--use-vi-mode` / `--no-vi-mode` options share destinations. The last occurrence wins. The negative options disable the corresponding setting. See [environment](../../configuration/environment/) for startup and backend settings. A native REPL is not an equivalent implementation of Python `-c`, IPython, or PTPython. Native ports that support this command use an optional version-checked Python bridge and report an unsupported-runtime error if it is absent. Check [port coverage](../../reference/compatibility/) before relying on that bridge. Interactive machine output needs a separate terminal; an interactive transcript cannot share JSON stdout. Backend selectors are mutually exclusive. ## Native Swift shell The Swift command uses `TMUX_WORKSPACE_PYTHON` (default `python3`) and checks for tmuxp 1.74.0 plus libtmux `Server` support for selecting `tmux_bin` before evaluation. It passes the selected native tmux executable and socket to that runtime. Run the example above with `tmux-workspace` after installing this compatible Python environment. Captured `-c` calls stream output while Python runs. Machine calls require `-c`; live chunks use structured stderr warnings and the final stdout result contains both captured streams and the child exit status. See the [child output contract](../../reference/output/#native-swift-child-output) for filtering, UTF-8 handling and the one MiB limit per stream. Interactive backends require a terminal; optional backend packages need broader verification. ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `"session_name"` | optional | | | `"window_name"` | optional | | | `-S` | value; None | pass-through for tmux -S | | `-L` | value; None | pass-through for tmux -L | | `-c` | value; None | instead of opening shell, execute python code in libtmux and exit | | `--best` | flag; best | use best shell available in site packages | | `--pdb` | flag; None | use plain pdb | | `--code` | flag; None | use stdlib's code.interact() | | `--ptipython` | flag; None | use ptpython + ipython | | `--ptpython` | flag; None | use ptpython | | `--ipython` | flag; None | use ipython | | `--bpython` | flag; None | use bpython | | `--use-pythonrc` | flag; False | load PYTHONSTARTUP env var and ~/.pythonrc.py script in --code | | `--no-startup` | flag; False | disable Python startup loading; shares a destination with `--use-pythonrc`, last occurrence wins | | `--use-vi-mode` | flag; False | use vi-mode in ptpython/ptipython | | `--no-vi-mode` | flag; False | disable vi mode; shares a destination with `--use-vi-mode`, last occurrence wins | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/shell.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp import Source: https://libtmux.org/en/swift/latest/workspace/cli/import/ > Import a workspace from a supported external configuration format. Select one of the two child commands. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Import a workspace from a supported external configuration format. Select one of the two child commands. ## Choose the source format - [Teamocil](../import-teamocil/) converts a Teamocil workspace. - [tmuxinator](../import-tmuxinator/) converts a tmuxinator workspace. The parent command groups importers and has no workspace file argument of its own. Each child requires a source at the parser boundary, despite optional-looking help. Conversion is schema translation; it does not make tmuxinator Ruby or ERB evaluation available in a native YAML reader. ## Arguments and flags The parent accepts `-h` / `--help` and selects a child command. All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/import_config.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp import teamocil Source: https://libtmux.org/en/swift/latest/workspace/cli/import-teamocil/ > Translate a Teamocil workspace into tmuxp configuration, review the result, and select its saved representation. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Translate a Teamocil workspace into tmuxp configuration, review the result, and select its saved representation. ## Select an existing source With [`project.yml`](../../guides/discovery/) already present in the Teamocil configuration directory: ```console $ tmuxp import teamocil project ``` The source lookup uses [`~/.teamocil`](./). A source argument is effectively required: although its positional action has optional arity, it belongs to a required exclusive group, and omission exits 2. The reference previews and saves the transformed document interactively. Native automation needs explicit destination, encoding, and overwrite policy instead of implicit prompts. See [output](../../reference/output/). Inspect translated commands and directories before loading the result. An extensionless name searches the configured source directory. A filename with an extension is resolved relative to the current directory unless you provide an explicit path. ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `workspace_file` | effectively required | `nargs="?"` belongs to a required exclusive group; omission exits 2. Source lookup uses [`~/.teamocil`](./). | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/import_config.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp import tmuxinator Source: https://libtmux.org/en/swift/latest/workspace/cli/import-tmuxinator/ > Translate a tmuxinator workspace into tmuxp configuration, preserving an explicit boundary around dynamic Ruby configuration. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Translate a tmuxinator workspace into tmuxp configuration, preserving an explicit boundary around dynamic Ruby configuration. ## Select an existing source With [`project.yml`](../../guides/discovery/) already present in the configured tmuxinator directory: ```console $ tmuxp import tmuxinator project ``` `TMUXINATOR_CONFIG` overrides the source directory and expands a leading tilde. A source argument is effectively required, and omission exits 2 despite optional-looking help. The reference previews and saves the transformed document interactively. Do not interpret successful YAML parsing as support for ERB templates or arbitrary Ruby execution. Native importers must either implement a documented bridge or reject unsupported dynamic input. Inspect the resulting commands and directories before loading. See [environment](../../configuration/environment/) and [output](../../reference/output/). An extensionless name searches the configured source directory. A filename with an extension is resolved relative to the current directory unless you provide an explicit path. ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `workspace_file` | effectively required | `nargs="?"` belongs to a required exclusive group; omission exits 2. Source lookup honors `TMUXINATOR_CONFIG`. | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/import_config.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Workspace command reference Source: https://libtmux.org/en/swift/latest/workspace/cli/ > The tmuxp command tree, root options, and native compatibility scope. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../reference/compatibility/) describes this port's implemented coverage. The installed Python command is `tmuxp`. Native command names and installation artifacts are not established by this documentation prototype. The pages below document the Python reference grammar and identify native extensions. ## Commands - [load](./load/) builds sessions from files or saved workspace names. - [freeze](./freeze/) captures a live session to a workspace file. - [convert](./convert/) changes YAML and JSON representation. - [edit](./edit/) opens a resolved workspace in an editor. - [ls](./ls/) lists discovered configurations. - [search](./search/) searches their names and content. - [debug-info](./debug-info/) reports runtime and tmux diagnostics. - [shell](./shell/) evaluates Python with tmux context. - [import](./import/) selects [Teamocil](./import-teamocil/) or [tmuxinator](./import-tmuxinator/) conversion. ## Root options `-h` / `--help` shows help; `-V` / `--version` prints the version. `--log-level` selects `"debug"`, `"info"`, `warning`, `"error"`, or `critical`, with parser default `warning`. `--color` accepts `auto`, `always`, or `never`, defaulting to `auto`. Place root flags before the subcommand: ```console $ tmuxp --color never ls ``` Short flags belong to their command: `search -S` is smart case, while `load -S` selects a tmux socket path. Similarly, `-f` selects a tmux configuration for load, a field for search, and an output format for freeze. ## Machine mode and automation Current Python machine output exists on `ls`, `search`, and `debug-info` as described by their own flags. The native command tree adds `--json` and `--ndjson` to every leaf and allows these long options before or after the command. These additions are not installed Python features. Read [output and color](../reference/output/), [exit behavior](../reference/exit-codes/), [completion](./completion/), and [automation](../guides/automation/) before building a script around a command. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Shell completion Source: https://libtmux.org/en/swift/latest/workspace/cli/completion/ > Generate completion from command definitions and check native availability. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. tmuxp uses the separately installed `shtab` package for experimental completion. The parser entry point is [`tmuxp.cli.create_parser`](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). Generate a script in an environment that can import both packages, then use the shell-specific installation procedure from the [upstream completion guide](https://tmuxp.git-pull.com/cli/completion/). ## Native completion ArgumentParser defines the native command graph and supplies `--generate-completion-script` for shell completion. Manual, DocC, and site metadata integration remain separate work. Use supported parser APIs for those exports; private parser reflection is not a stable contract. Completion is derived from actual command metadata. It must include nested import commands, local short flags, positional arity, mutually exclusive choices, and all-command machine options. Verify generated scripts with the installed executable and target shell. See the [command tree](../) and [compatibility reference](../../reference/compatibility/). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Concepts Source: https://libtmux.org/en/swift/latest/concepts/ > tmux objects, command transports, queries, and workspaces across the libtmux language ports. libtmux lets you create sessions, arrange windows and panes, send commands, and read output from tmux. Start with the object hierarchy, then read about the transport, query, or workspace behavior your program needs. These pages explain shared concepts and differences between language ports: - **[Server, session, window, pane](server-session-window-pane/)**: tmux's object hierarchy and attached clients. - **[Control mode vs one-shot](transports/)**: subprocess commands, persistent connections, and batching. - **[Filtering and queries](queries/)**: find objects and handle absent or ambiguous matches. - **[Workspaces](workspaces/)**: build pane layouts from code or configuration files. Use the header's port links to open documentation for your language, including its API reference. --- # Server, session, window, pane Source: https://libtmux.org/en/swift/latest/concepts/server-session-window-pane/ > The object hierarchy every libtmux port mirrors from tmux itself, and the client that sits outside it. libtmux models tmux's server, session, window, and pane objects: ``` Server ├── Session │ └── Window │ └── Pane └── Client (attached view) ``` A `Server` contains sessions. Each `Session` contains links to windows, and each `Window` contains panes. Commands run inside a `Pane`, where you send input and capture output. A window can be linked to more than one session. ## Stable identity, not name or index tmux assigns a unique ID to each session, window, and pane at creation. The ID remains stable for that object's lifetime even if its name or index changes: | Object | ID prefix | Example | |--------|-----------|---------| | Session | `$` | `$13` | | Window | `@` | `@3243` | | Pane | `%` | `%5433` | | Server | - | identified by socket name or path instead | Python handles use the object ID to refresh their fields. Immutable snapshots, such as those in TypeScript, Swift, and Go, use IDs to identify the same tmux object across reads. Walking the whole tree, in each port: ```swift // A snapshot reads sessions, windows, and panes in one pass; the relations // below resolve against it without another tmux command. let snapshot = try await server.snapshot() for session in snapshot.sessions { print(session.name) for window in snapshot.windows(of: session) { print(" ", window.name) for pane in snapshot.panes(of: window) { print(" ", pane.id, pane.currentCommand) } } } ``` ## Client: a view, not a child A `Client` represents a terminal attached to a session. Several clients can view the same server, and each can switch sessions or windows independently. Client fields describe the view at the time of the read. A [control-mode connection](../transports/) is also a client. It appears in `list-clients`, counts toward `session_attached`, and affects attachment-dependent behavior such as `destroy-unattached`. Closing the last attached client can therefore destroy a session configured with that option. ## What differs between ports Ports differ in how they read state and report failures: - **Refreshing state.** Python objects reflect their last read until you call `.refresh()`. TypeScript, Swift, and Go also provide snapshots whose relationships can be queried without another tmux command. - **Blocking and async calls.** Python and Java use blocking calls. Rust, TypeScript, .NET, and Swift provide async APIs. Go uses ordinary calls with contexts for cancellation and deadlines. - **Failure handling.** Python and .NET raise exceptions. C++ returns `expected`. Some commands have an expected negative answer, such as `has-session` when a session is absent; check the method's result contract before treating that answer as a failure. See [Control mode vs one-shot](../transports/) for command costs and connection behavior. --- # Control mode vs one-shot Source: https://libtmux.org/en/swift/latest/concepts/transports/ > How a call in your program actually reaches the tmux server, and why a port might give you a choice. libtmux sends commands to tmux through subprocesses or persistent control-mode connections. Some ports also batch commands into one invocation: 1. **One-shot subprocess.** Each command spawns a fresh `tmux` process, which parses argv, executes the command, prints its output, and exits. This is the default in every port, and the *only* lane in Python: every `.cmd()` call underneath the object API is a `subprocess.Popen` around a `tmux` invocation. 2. **A persistent control-mode client.** `tmux -C attach-session` starts one long-lived tmux process that stays attached and speaks a line-oriented protocol over its stdout: commands go in, replies and asynchronous notifications (`%window-add`, `%output`, ...) come out, without starting a process per call. 3. **One invocation, several commands.** tmux accepts more than one command per invocation (`;`-joined, or one `-F`-tagged `list-*` per line). A port can fold several logical operations into a single process start without opening a control-mode connection at all. ## Where each port draws the line | Port | One-shot | Folded invocation | Persistent control client | |------|----------|--------------------|-----------------------------| | Python | every call | - | test-only (`ControlMode`, `libtmux._internal`) | | TypeScript | default | `pipeline()`, `batch()` | `connect()` / `watch()`: notifications only, commands stay per-process | | Go | `process` path | `Plan.Run` | `connection` (`Session.OpenControl`), `streaming` (`OpenNotifications`) | | Rust | `plan` feature, sequential | `plan`, folded | `control-mode` feature | | C# | "One-shot" mode | "Chained" mode (`server.Chain()`) | "Control" mode (`EnterControlModeAsync`) | | C++ | bounded subprocess (default) | `Chain` | `Server::control()` → `Connection` | | Java | every call | `Batch` | `ControlClient` (`attach`, `send`, `subscribeEvents`) | | Swift | default | - | `server.connect()` / `.watch()` (notifications; see below) | Choose based on whether you need command results, notifications, or a batch of changes. ## Notifications and commands are separable TypeScript's `connect()` adds an event observer while commands such as `session.newWindow(...)` and `pane.sendKeys(...)` continue to run as separate tmux processes. A dedicated process provides a completion boundary for output from alias-expanded or waiting commands. Go's `Session.OpenControl`, .NET's `EnterControlModeAsync`, Java's `ControlClient.send`, and Rust's `control-mode` feature can send commands through the persistent connection. Check your port's transport API before assuming that subscribing to events also changes how commands run. ## A control client is a real client A persistent control connection attaches a tmux client. It appears in `list-clients`, increments `session_attached`, and affects `destroy-unattached`, client hooks, and idle-client accounting. Each connection counts separately. Python's internal `ControlMode` test helper uses this behavior for commands that require an attached client, such as `display-popup` and `detach-client`. ## Why fold several commands into one invocation Creating an object can require a second command to read its resulting state. Batching reduces those repeated reads and process starts. TypeScript's `batch()` resolves planned mutations from one final snapshot. Go's `Plan`, .NET's `Chain`, and C++'s `Chain` also group operations without attaching a control client. ## What this costs in practice The Rust `matrix` example and .NET README compare process counts and timings. Their results describe specific workloads and environments: - **Rust's** `matrix` example runs the same create-and-query workload five ways. Blocking sequential and async sequential both cost 6 processes for 6 dispatches; folding the same 6 into async batches costs 3 processes; routing them over a control-mode connection costs exactly 1. - **C#'s** README reports the *marginal* cost of one more command in each mode, as medians against tmux 3.7b: roughly 2.3 ms for another one-shot process, roughly 0.2 ms for another command over an already-open control client, and roughly 0.02 ms for another command folded into one chained invocation. A persistent connection avoids starting a client for each command. A chain groups a known sequence into one invocation. For occasional commands, use the default subprocess transport; measure your workload before changing transports for performance. Use a notification stream when your program needs tmux events. ## Choosing a lane These examples use each port's subprocess API. See the port reference for batching and control-mode setup. ```swift import LibTmux // One-shot: every call underneath this handle spawns a `tmux` process. let server = try Server(socketName: "default") let session = try await server.newSession(named: "work", windowName: "editor") let window = try await server.newWindow(in: session, named: "logs").window let pane = try await server.splitWindow(window, direction: .right) try await server.run("echo hello", in: pane) ``` To inspect tmux's control protocol, attach a control client: ```console $ tmux -C attach-session -t work ``` --- # Filtering and queries Source: https://libtmux.org/en/swift/latest/concepts/queries/ > How you get from every session on the server to the one pane you mean, and what happens when zero or several match. Use a collection filter to find matching sessions, windows, or panes. Use an exactly-one lookup when your next operation requires a single target. - **Filtering returns a collection; exactly-one lookup checks the result count.** A `.filter()` or `.where()` call returns zero or more matches. Methods such as `.get()`, `.one()`, and `Selections.exactlyOne()` return one object or report a missing or ambiguous match. - **Choose where to filter.** Filter a snapshot in your program when you need several queries over the same data. A tmux format filter can reduce the rows returned by a live read. The cost depends on the data and queries you need. ## Python: `.filter()` and `.get()`, Django-style `server.sessions`, `session.windows`, and `window.panes` are `QueryList` collections. Call `.filter()` with field names and optional lookup suffixes: Lookups include `exact`, `contains`, `startswith`, `endswith`, `regex`, and their case-insensitive `i`-prefixed variants. Multiple keywords and chained `.filter()` calls combine with AND. `.get()` requires exactly one match. Its `default` argument handles an absent result; multiple matches still raise `MultipleObjectsReturned`. Server-wide collections (`server.windows`, `server.panes`) enumerate window links. A window linked to two sessions appears once per session, so a lookup can be ambiguous even when the window ID is unique. Use `Window.linked_sessions` to find its sessions. For a known ID, use `Pane.from_pane_id()` or `Window.from_window_id()` to resolve the object directly. For servers with hundreds or thousands of panes, `.filter()` still builds every object before you discard the ones that don't match. `search_sessions`, `search_windows`, and `search_panes` push a tmux `-f` filter expression down to the server instead, so libtmux builds objects only for the matches: Python-side lookups work with the library's supported tmux versions. The tmux filter grammar requires tmux 3.2 or newer. An unknown format token expands to an empty value, so a malformed filter can look like a valid filter with no matches. If `search_*()` unexpectedly returns no results, try `#{m:*,#{session_name}}` to check that the session data is available. ## TypeScript: criteria as data TypeScript's `Selection.where()` accepts structured, serializable criteria that can be stored in a configuration file or sent through MCP: `some`, `every`, and `none` test related objects. `{ mode: "insensitive" }` enables case-insensitive comparison. Use `.where()` for criteria that can be encoded with `encodeWhereDocument` and decoded with `decodeWhereDocument`; use `.filter()` for a predicate function. `.one()` throws `NoMatchError` or `MultipleMatchesError`. `.oneOrUndefined()` permits an absent result. ## Go, Rust, Java, C++: typed fields that fail queries at compile time These ports use typed fields to reject invalid comparisons at compile time: - **Go** offers both `tmux.PaneFilter{Active: tmux.Ptr(true), ...}` structs that push down into `SearchPanes` (one tmux command, only matches returned), and a `snapshot()` read followed by `tmuxq.Where(panes, predicate)` when you want several answers from one read. - **Rust** uses typed fields: `fields.pane_active.eq(true)` is valid, but `.gt(...)` on that boolean field is not. Expressions compose with `.and()`. With the `serde` feature, a query can be encoded as a versioned JSON document for configuration or MCP. - **Java** exposes each field as a typed accessor (`Pane_.index()`, `Session_.name()`) that plugs straight into an ordinary `Stream.filter()`; `Pane_.index().startsWith("2")` doesn't compile because the index is a number, not a string. `Selections.exactlyOne(...)` is the `.get()`-shaped call, throwing `NoMatchException` or `MultipleMatchesException`. - **C++** composes `FilterExpr` values with `&&`, `||`, and `!`, as in `pane::command.starts_with("nv") && pane::active`. Invalid field operations such as `pane::active.starts_with("x")` fail to compile. Examples of typed and local filters: ```swift // Filter locally with the standard library: let editors = try await server.panes().filter { $0.currentCommand == "nvim" } // Or build a filter that travels: stored, sent, replayed elsewhere: let expression = try FilterExpr.where(\.currentCommand, .isIn(["nvim", "vim"])) let matching = try await server.panes().filter(expression) ``` ## The cardinality contract, side by side | Port | Collection filter | Exactly-one | Empty | Several | |------|--------------------|--------------|-------|---------| | Python | `.filter()` | `.get()` | `ObjectDoesNotExist` (or `default=`) | `MultipleObjectsReturned` | | TypeScript | `.where()` / `.filter()` | `.one()` | `NoMatchError` (or `.oneOrUndefined()`) | `MultipleMatchesError` | | Java | `Stream.filter()` | `Selections.exactlyOne()` | `NoMatchException` | `MultipleMatchesException` | See the Go, Rust, C++, .NET, and Swift references for their exactly-one result types and failure handling. --- # Workspaces Source: https://libtmux.org/en/swift/latest/concepts/workspaces/ > Build pane layouts with the object API or a workspace configuration file. A workspace arranges windows and panes for a task, such as editing code, running a development server, and following logs. Build it with the object API when the layout depends on program logic. Use a declarative builder when you want to store the layout in a configuration file, such as [tmuxp](https://tmuxp.git-pull.com/) YAML or JSON. ## Building one imperatively Create a window, split it into panes, apply a layout, and send each pane its command: ```swift let session = try await server.newSession(named: "work") let window = try await server.newWindow(in: session, named: "dev").window let terminal = try await server.splitWindow(window, size: .percentage(30)) let logs = try await server.split(terminal, direction: .right) try await server.selectLayout(window, "main-vertical") ``` `Window.split()` or `Pane.split()` adds a pane. Direction and size control its placement. `select_layout()` rearranges the panes while their processes continue running. tmux provides `even-horizontal`, `even-vertical`, `main-horizontal`, `main-vertical`, and `tiled` layouts. Python's `attach=False` and C++'s detached creation keep new windows in the background. Check the creation defaults for your port if focus matters. Splits and resizes require tmux commands; [Control mode vs one-shot](../transports/) covers their transport costs and batching. ## Building one declaratively These packages read or build workspace configurations based on tmuxp: | Port | Package | Shape | |------|---------|-------| | Python | tmuxp itself | the format this whole idea is named after | | TypeScript | `@libtmux/workspace` | `applyWorkspace(server, { session_name, windows: [...] })` | | Go | `workspace` | tmuxp-shaped, per the port's own module layout | | Rust | `tmux-workspace` | tmuxp-shaped | | Java | `libtmux-workspace` | "enough of tmuxp's format to describe a workspace" | | C# | `LibTmux.Workspace` | reads tmuxp YAML directly | | Swift | `TmuxWorkspace` | Swift, JSON, or YAML (YAML needs the `YAMLWorkspaces` trait) | TypeScript's `applyWorkspace` applies a desired configuration. Applying the same configuration again reuses its existing objects: ```swift let workspace = Workspace( sessionName: "work", windows: [WindowPlan(windowName: "editor", panes: [PanePlan(), PanePlan()])] ) let session = try await WorkspaceBuilder.build(workspace, on: server) ``` C++ provides a consumer example in `examples/workspace/` that reads tmuxp configuration. The workspace builder is part of that example, rather than a library package: ## Cleaning up For temporary workspaces, Python's `Window` and `Session` context managers kill their objects on block exit, including when the block raises: See [Context managers](/topics/context-managers/) for cleanup support in each port. Use explicit kill methods when the handle does not provide scope-based cleanup. --- # Workspace configuration Source: https://libtmux.org/en/swift/latest/workspace/configuration/ > Tmuxp workspace configuration and current Swift builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../reference/compatibility/) describes this port's implemented coverage. A workspace file describes one tmux session, its windows and panes, and the commands sent to them. YAML and JSON carry the same field names. Save this complete example as [`workspace.yaml`](../guides/installation/#create-the-input): ```yaml session_name: workspace-example start_directory: ./ windows: - window_name: editor layout: even-horizontal panes: - echo ready - blank ``` The equivalent JSON is: ```json { "session_name": "workspace-example", "start_directory": "./", "windows": [ { "window_name": "editor", "layout": "even-horizontal", "panes": ["echo ready", "blank"] } ] } ``` With Python tmuxp installed, load this file detached on a socket reserved for the example: ```console $ tmuxp load \ -L configuration-example \ -d \ workspace.yaml ``` Inspect the resulting panes: ```console $ tmux -L configuration-example list-panes -t '=workspace-example' ``` Remove the example session when finished: ```console $ tmux -L configuration-example kill-session -t '=workspace-example' ``` ## How configuration becomes a session The tmuxp loader reads a document, expands command shorthand and shell variables, and applies inherited defaults before selecting a workspace builder. The classic builder then creates tmux objects and sends commands. Completion means construction and command delivery finished; it does not establish that a server launched in a pane is ready. `"session_name"` and `"windows"` are needed by normal loading. A window can omit its name and let tmux choose one; an omitted `"panes"` list defaults to one blank pane. Supplying explicit names and pane lists makes a portable example clearer. An empty pane list is not the same input as an omitted list. The internal `validate_schema` helper requires each window_name, but the current CLI/classic builder path does not call it. It is not a complete JSON Schema or an exact description of what load accepts. A YAML parser accepting a key also does not mean a builder implements it. ## Configuration reference - [Session](./session/) covers identity, options, environment, and root keys. - [Windows](./windows/) covers names, indexes, option timing, and focus. - [Panes](./panes/) covers shorthand, blank forms, shell, and overrides. - [Commands](./commands/) covers before commands, Enter, delays, and history. - [Environment](./environment/) separates process settings from pane values. - [Directories](./directories/) explains file discovery and path resolution. - [Layouts](./layouts/) explains pane arrangement and terminal size. - [Hooks and builders](./hooks/) covers scripts and Python extensions. Use the [configuration gallery](../examples/gallery/) for more complete files. Configuration conversion should preserve the source mapping, including extension keys; loading that mapping requires separate support for every execution feature. ## Current Swift builder `TmuxWorkspace` supports JSON decoding and optional YAML decoding through the `YAMLWorkspaces` trait. Unknown keys can be ignored. The model covers names, directories, layouts, and commands, with narrower shapes than tmuxp. See the [native builder behavior](../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-swift/blob/94b9e4cc436dda8e18e064179ae7d26e55bbbd73/Sources/TmuxWorkspace/Workspace.swift) before using these fields through application code. ## Reference source [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [validation.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/validation.py); [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py). --- # Session configuration Source: https://libtmux.org/en/swift/latest/workspace/configuration/session/ > Tmuxp session configuration and current Swift builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. The root mapping names the session and supplies defaults inherited by its windows and panes. The file's name is independent of `"session_name"`: loading [`project.yaml`](../../guides/discovery/) can create a session named `development`. ```yaml session_name: development start_directory: ./ suppress_history: true shell_command_before: - echo preparing pane environment: WORKSPACE_ROLE: development options: default-shell: /bin/sh global_options: status: true windows: - window_name: main panes: - echo ready ``` `"global_options"` changes the shared tmux server. Use a dedicated socket while learning these options. The example assumes `/bin/sh` exists. ## Root keys | Key | Meaning | | --- | --- | | `"session_name"` | Session identity; expanded before building | | `"windows"` | Ordered list of window mappings | | `start_directory` | Starting directory and base for inherited window directories | | `"options"` | tmux session options applied during construction | | `"global_options"` | tmux options applied with global scope on the selected server | | `"environment"` | Values placed in the tmux session environment | | `shell_command_before` | Commands prepended to commands in every pane | | `"suppress_history"` | Default history suppression inherited by windows and panes | | `"before_script"` | Process run after initial session creation, before configured windows | | `"plugins"` | List of Python plugin class references | | `workspace_builder` | Classic builder, registered builder name, or Python class reference | | `workspace_builder_paths` | Trusted directories used for Python builder imports | | `workspace_builder_options` | Builder behavior settings such as pane_readiness | Options use tmux option names and values. A workspace key such as `start_directory` is not a tmux option and does not belong in `"options"`. Some tmux settings are window options even when tmux permits them through a session target; consult tmux's option scope when choosing the catalog. ## Construction order The classic builder creates the initial session, runs its initial plugin hook and workspace before_script, then applies root options, global options, and session environment before creating configured windows. The temporary initial window is replaced. Hooks can observe those tmux operations; loading is not an invisible transaction. The same-named session is handled by the [load command](../../cli/load/) and its attach, switch, append, and detached choices. Changing a YAML file does not automatically reconcile an existing running session. An explicit `load -s` value overrides the configured name for that invocation. ## Inherited defaults A window can override its start directory and history policy, and a pane can override them again. Before commands accumulate in session, window, then pane order. Session environment remains distinct from window/pane launch environments. Read [directories](../directories/), [commands](../commands/), and [environment](../environment/) before relying on inheritance. Python scripts, plugins, and custom builder references execute code. Their exact timing and runtime requirements are in [hooks and builders](../hooks/). ## Current Swift builder The model includes session name, directory, and windows. Session environment/options, plugins, and custom builder fields are outside that model and can be ignored by decoding. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-swift/blob/94b9e4cc436dda8e18e064179ae7d26e55bbbd73/Sources/TmuxWorkspace/Workspace.swift) before using these fields through application code. ## Reference source [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [load.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/load.py). --- # Window configuration Source: https://libtmux.org/en/swift/latest/workspace/configuration/windows/ > Tmuxp window configuration and current Swift builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Each item in `"windows"` describes one window and its ordered panes. Set `"window_name"` when the name matters; an omitted name lets tmux choose it. `"window_index"` selects a tmux numeric index independently of the item's position in the list. ```yaml session_name: window-example start_directory: ./ windows: - window_name: tools window_index: 1 start_directory: ./ layout: even-horizontal focus: true options: automatic-rename: false options_after: synchronize-panes: true panes: - echo left - echo right ``` This configuration sends each pane its own initial command before enabling synchronized input. Later typing in one synchronized pane can affect the other pane. ## Window keys | Key | Meaning | | --- | --- | | `"window_name"` | Label used by tmux; shell variables are expanded | | `"window_index"` | Explicit numeric position, otherwise use tmux's available index | | `"panes"` | Ordered pane list; omission supplies one blank pane | | `"layout"` | Named tmux layout or explicit layout description | | `start_directory` | Directory inherited by panes unless a pane overrides it | | `"window_shell"` | Initial shell/application for panes, subject to pane shell override | | `focus` | Select the window after building | | `"options"` | Window options applied during creation | | `"options_after"` | Window options applied after panes and their initial commands | | `"environment"` | Launch environment used when a pane lacks its own map | | `shell_command_before` | Commands prepended after session before commands | | `"suppress_history"` | Window default overriding the session history policy | ## First pane and later panes A new window already has its initial pane. Tmuxp uses the first configured pane's start_directory, shell, and environment when launching that window, then creates splits for later panes. A first-pane override therefore matters during new-window, not only while creating splits. A pane shell overrides window_shell. Window environment is used when the pane has no environment map; providing a pane map selects that map instead. These rules have native-port differences, so use the current builder note on this page. ## Index, layout, and focus The classic builder moves the temporary initial window before creating the configured first window. This permits an explicit first index without reusing the temporary window's content. Do not assume window list position and tmux numeric index are identical. Window options precede layout application. `"options_after"` exists for settings such as synchronize-panes that should take effect after individual setup commands. [Layouts](../layouts/) explains named layouts, dimensions, and focus; [panes](../panes/) describes the pane forms accepted inside a window. ## Current Swift builder Windows have names, directories, layout, and panes. Indexes, environment, options, and focus settings from the tmuxp reference are not all represented. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-swift/blob/94b9e4cc436dda8e18e064179ae7d26e55bbbd73/Sources/TmuxWorkspace/Workspace.swift) before using these fields through application code. ## Reference source [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [2-pane-synchronized.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/2-pane-synchronized.yaml). --- # Pane configuration Source: https://libtmux.org/en/swift/latest/workspace/configuration/panes/ > Tmuxp pane configuration and current Swift builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. A pane can be a command string, a list of commands, or a mapping of settings. Each item in the window's `"panes"` list creates one pane; a command list inside that item describes several commands in that same pane. ```yaml session_name: pane-example start_directory: ./ windows: - window_name: main start_directory: ./ panes: - echo one command - [echo first command, echo second command] - shell_command: - echo configured pane start_directory: ./ focus: true - blank ``` ## Blank forms | Form inside `"panes"` | Reference interpretation | | --- | --- | | null, omitted YAML value, `blank`, or `"pane"` | Pane without its own commands | | Empty mapping or empty list | Expands to a pane without its own commands | | Mapping with no shell_command | Keeps the pane's other settings and uses no own commands | | `shell_command: null` or a single null command | No own commands | | Empty string `""` | Sends an empty command, normally pressing Enter | A blank pane can still receive inherited [before commands](../commands/). Blank forms do not disable session/window/pane setup. An omitted window panes key is defaulted to one blank pane; an explicitly empty panes list is a different shape and should not be used as a portable way to request that default. ## Pane keys | Key | Meaning | | --- | --- | | `shell_command` | String, ordered command list, or supported command dictionaries | | `shell_command_before` | Setup prepended after session/window before commands | | `start_directory` | Pane directory override | | `"shell"` | Shell/application launched for this pane | | `focus` | Select this pane in its window | | `"environment"` | Environment map selected for this pane's launch | | `"suppress_history"` | Pane history policy override | | `"enter"` | Default for whether to submit each command | | `sleep_before`, `sleep_after` | Default delays in seconds around each command | ## Launch a shell or type commands `"shell"` chooses the process tmux starts in the pane. `shell_command` sends text into the process already running there. Launching an application through shell can work with tmux's remain-on-exit behavior, while typing that application's name into a shell has different process semantics. A pane shell overrides window_shell, including on the first pane. The first pane also supplies its directory and environment during window creation. Use an installed shell/application path rather than assuming the same executable exists on every host. A successful build confirms command delivery, not application readiness or command exit status. See [commands](../commands/) for Enter, timing, and history, and [environment](../environment/) for launch-map selection. ## Current Swift builder The first pane is created with its window directory; split panes can supply a pane directory. This differs from tmuxp, which also lets the first pane override its directory. Command decoding accepts fewer forms. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-swift/blob/94b9e4cc436dda8e18e064179ae7d26e55bbbd73/Sources/TmuxWorkspace/Workspace.swift) before using these fields through application code. ## Reference source [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [blank-panes.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/blank-panes.yaml); [pane-shell.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/pane-shell.yaml). --- # Workspace commands Source: https://libtmux.org/en/swift/latest/workspace/configuration/commands/ > Tmuxp workspace commands and current Swift builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Tmuxp sends workspace commands into panes in order. A command can be a string or a mapping with `"cmd"` and execution controls. `shell_command_before` adds shared setup without copying it into every pane. ```yaml session_name: command-example shell_command_before: - echo session setup windows: - window_name: main shell_command_before: - echo window setup panes: - shell_command_before: - echo pane setup shell_command: - cmd: echo ready sleep_after: 0.2 - cmd: echo typed but not submitted enter: false sleep_after: 0 ``` This sends the three setup commands, submits echo ready, waits 0.2 seconds, and leaves the final command at the prompt. The delay pauses construction; it does not ask the application whether it is ready. ## Forms and order `shell_command` and `shell_command_before` accept a scalar command or a list. A command dictionary requires `"cmd"` for the text. The loader expands shell variables and tilde expressions in command strings before the builder sends them. Variables already known to the process running tmuxp can therefore be substituted before a pane shell sees the command. For each pane, the loader concatenates session before commands, window before commands, pane before commands, then the pane's own commands. A blank pane still receives inherited setup. Quoting affects the pane shell too; YAML quoting alone does not bypass tmuxp's expansion step. ## Enter and delays | Setting | Default and scope | | --- | --- | | `"enter"` | True; pane default, then command override | | `sleep_before` | No delay; pane default, then command override in seconds | | `sleep_after` | No delay; pane default, then command override in seconds | In the classic builder, a command's enter/delay override carries forward to subsequent commands in that pane. Set `enter: true` or an explicit zero delay to restore that behavior for a later command. Absence means keep the current value; zero is an intentional delay override. Pauses run synchronously during construction. They can let a startup command settle but do not monitor its success. A failed pane command is not necessarily a failed tmux send operation, and starting a web server is not proof it is accepting connections. ## History and prompt readiness History suppression defaults to true. A session value trickles to a window, and a pane can override it. Suppression prefixes command text with a space. Bash needs HISTCONTROL containing ignorespace or ignoreboth; zsh needs HIST_IGNORE_SPACE. Without shell support, the command can still enter history. Builder pane_readiness is separate from command delays. Auto waits for the configured zsh session shell, while custom pane/window launch commands skip prompt waiting. See [hooks and builders](../hooks/) for the policy and its limits. A workspace [before_script](../hooks/) runs as a process outside the panes and checks its exit status. Use it for bootstrap work that must succeed before configured windows are built, rather than treating pane command delivery as a checked process result. ## Current Swift builder Swift commands carry command text and Enter behavior. The reference delay dictionaries, inherited before commands, history suppression, and variable expansion are not supplied by that model. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-swift/blob/94b9e4cc436dda8e18e064179ae7d26e55bbbd73/Sources/TmuxWorkspace/Workspace.swift) before using these fields through application code. ## Reference source [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [sleep.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/sleep.yaml); [skip-send.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/skip-send.yaml). --- # Workspace environment Source: https://libtmux.org/en/swift/latest/workspace/configuration/environment/ > Tmuxp workspace environment and current Swift builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. The environment of the process running tmuxp controls discovery, expansion, and presentation. A workspace's `"environment"` mapping controls the tmux session or the environment passed when launching a pane. These are separate settings. ```yaml session_name: environment-example environment: WORKSPACE_ROLE: shared windows: - window_name: main environment: WINDOW_ROLE: tools panes: - echo window environment - environment: PANE_ROLE: isolated shell_command: env ``` The first pane receives the window launch map. The second selects its own pane map instead of merging it with the window map. It still inherits applicable tmux session/process environment. A pane map does not mean a completely empty environment plus that map. ## Expansion before launch Tmuxp expands tilde and environment-variable expressions in session/window names, paths, before_script, command strings, environment values, and string option values. It uses the environment of the process invoking tmuxp. It does not first populate that process environment from the workspace's environment mapping. For example, a command using an already-set process variable can be substituted before pane creation. Inspect the expanded intent when a variable should instead be read dynamically by a pane shell. Unknown variables and shell-specific expressions follow the loader's expansion and the eventual shell's rules, not a general template language. ## CLI and runtime variables | Variables | Effect | | --- | --- | | `TMUXP_CONFIGDIR`, `XDG_CONFIG_HOME`, `HOME` | Global workspace directory selection and home expansion | | `TMUXINATOR_CONFIG` | Tmuxinator import source directory | | `$EDITOR` | Editor executable; reference default is vim | | `$TMUX`, `$TMUX_PANE` | Current tmux connection and shell object context | | `TMUXP_PROGRESS` | Value 0 disables animated load progress | | `TMUXP_PROGRESS_FORMAT` | Default/minimal/window/pane/verbose preset or custom tokens | | `TMUXP_PROGRESS_LINES` | Script panel lines: default 3, 0 hides, -1 caps to terminal height | | `TMUXP_DETECT_TERMINAL_SIZE` | Value 1 enables size detection; default 1 | | `TMUXP_DEFAULT_COLUMNS`, `TMUXP_DEFAULT_ROWS` | Fallback session dimensions | | `COLUMNS`, `LINES`, `ROWS` | Terminal helper overrides and fallback sizing inputs | | `NO_COLOR`, `FORCE_COLOR` | Color policy; nonempty values are significant | | `PYTHONSTARTUP` | Startup file used by supported shell startup behavior | | `IPYTHON_ARGUMENTS` | Whitespace-split arguments for the IPython backend | | `PYTHONBREAKPOINT` | Can affect debugger selection in tmuxp shell | | `SHELL` | Shell diagnostics and readiness fallback | | `DISABLE_AUTO_TITLE` | Oh My Zsh automatic-title warning | | `PATH` | Executable lookup and diagnostics | | `LIBTMUX_TMUX_FORMAT_SEPARATOR` | Python libtmux format collection override | Explicit progress flags take precedence over their corresponding defaults. Nonempty NO_COLOR disables color even with always; otherwise explicit never/always precede FORCE_COLOR and automatic TTY detection. Machine-output extensions must disable ANSI regardless of forced color. `CLICOLOR` and `CLICOLOR_FORCE` are proposed cross-port presentation extensions, not variables read by this tmuxp reference. Native format codecs also need separate evidence before claiming the Python separator override works. See [directories](../directories/) for existing-directory precedence, [layouts](../layouts/) for size resolution, and [shell](../../cli/shell/) for Python-specific environment effects. ## Current Swift builder Tmuxp environment expansion and session/window/pane environment maps are outside the current Swift model. YAML support itself requires opting in to the YAMLWorkspaces trait. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-swift/blob/94b9e4cc436dda8e18e064179ae7d26e55bbbd73/Sources/TmuxWorkspace/Workspace.swift) before using these fields through application code. ## Reference source [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [finders.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/finders.py); [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [load.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/load.py); [shell.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/shell.py); [shell.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/shell.py); [colors.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/_internal/colors.py); [util.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/util.py). --- # Workspace files and directories Source: https://libtmux.org/en/swift/latest/workspace/configuration/directories/ > Tmuxp workspace files and directories and current Swift builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Tmuxp accepts an explicit workspace file, a saved workspace name, or a project directory. The location of the selected file determines how config-relative paths expand. Use an explicit file while debugging a discovery problem. Load a file on a dedicated server: ```console $ tmuxp load \ -L directory-example \ -d \ ./workspace.yaml ``` Load the current project's workspace: ```console $ tmuxp load . ``` These commands use Python tmuxp and assume the corresponding workspace file already exists. The first command leaves its session detached; use the session name from the file when inspecting or cleaning it up. ## Global and local discovery For the preferred global workspace directory, tmuxp tries TMUXP_CONFIGDIR, XDG_CONFIG_HOME/tmuxp (or the XDG default), then the legacy [`~/.tmuxp`](../../guides/discovery/) directory. It chooses the first existing directory. If none exists, it returns the legacy location. Setting TMUXP_CONFIGDIR to a nonexistent path does not automatically select that path over an existing fallback. Project discovery walks the current directory and its parents, choosing at most one workspace per directory in `.tmuxp.yaml`, [`.tmuxp.yml`](../../guides/discovery/), [`.tmuxp.json`](../../guides/discovery/) order. It stops at home or filesystem root. `ls` can report global directory candidates and locally discovered workspaces; that inventory is not identical to resolving one explicit load argument. The importers use their own source roots: Teamocil uses [`~/.teamocil`](../../cli/import-teamocil/); tmuxinator uses TMUXINATOR_CONFIG, with tilde expansion, or [`~/.tmuxinator`](../../cli/import-tmuxinator/). Their source argument is effectively required, even though the parser's positional arity looks optional. ## Start directories ```yaml session_name: directory-example start_directory: ./ windows: - window_name: root panes: - pwd - window_name: child start_directory: ./src panes: - pwd - start_directory: ./ shell_command: pwd ``` This example assumes the project has a `"src"` directory. The first window inherits the session directory. The child window resolves ./src against the explicit session directory, and its second pane resolves ./ against that window directory. Both child panes start in src. The root start_directory resolves a dot-relative path from the configuration file's directory. At child levels, the pinned loader resolves dot-relative paths against the immediate parent's start_directory before inherited defaults are filled. Define that parent value explicitly when using ./ or ../ in a child. A missing parent start_directory can raise KeyError during expansion. A plain relative window directory such as src is joined to the session directory later, during trickle. A dot-relative pane under that still-relative window can resolve against the invoking process's current directory first. Use an explicit ./src window value as above, or an absolute path, to avoid that inconsistency. Native normalization that resolves every child consistently would correct this reference behavior. When a document names no `start_directory` at any level, panes start in the directory the command was run from, not the directory the workspace file lives in. That is what tmuxp does, and all eight implementations agree on it. An explicit relative value such as `./src` is the other case: it always resolves against the workspace file's directory, at every level, so a workspace stays portable no matter where it is loaded from. Absolute and expanded-home paths retain their explicit location. Quote `~` in YAML to avoid its null spelling. A pane override also applies to the first pane in the Python classic builder. Inspect resolved values and the resulting pane directory when loading a workspace from a different working directory. ## Missing directories and bootstrap A nonexistent path can make tmux start somewhere unexpected, including a home directory, rather than producing a useful configuration error. Inspect the actual pane directory when verifying a workspace. Native ports may reject, retain, report, or pass through the value differently. A relative before_script path is resolved from the workspace file. Its process working directory uses the session start_directory when supplied. A bootstrap process can create required project files, but it runs after the initial tmux session exists. See [hooks](../hooks/) for failure handling and [environment](../environment/) for variable expansion. ## Current Swift builder A window directory overrides the workspace directory; a split pane can override both. The first pane uses the window directory. Encoding a Workspace preserves model values and does not resolve or discover configuration files. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-swift/blob/94b9e4cc436dda8e18e064179ae7d26e55bbbd73/Sources/TmuxWorkspace/Workspace.swift) before using these fields through application code. ## Reference source [finders.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/finders.py); [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [import_config.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/import_config.py); [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [start-directory.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/start-directory.yaml). --- # Workspace layouts and focus Source: https://libtmux.org/en/swift/latest/workspace/configuration/layouts/ > Tmuxp workspace layouts and focus and current Swift builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. A window's `"layout"` chooses how tmux arranges its panes. Named layouts adapt to the window size; explicit layout strings describe geometry more directly. The selected tmux version and terminal dimensions affect the final result. ```yaml session_name: layout-example windows: - window_name: main layout: main-horizontal focus: true options: main-pane-height: 60% panes: - shell_command: echo main pane focus: true - echo first lower pane - echo second lower pane ``` ## Layout names and options Common tmux layout names are even-horizontal, even-vertical, main-horizontal, main-vertical, and tiled. Layout availability belongs to the target tmux version. Options such as main-pane-height and main-pane-width shape applicable main-pane layouts. A row/column count and a percentage are different values; preserve that distinction in YAML/JSON. The classic builder applies window options before selecting a layout and applies options_after after panes and their setup commands. This lets a workspace size its main pane while delaying synchronize-panes until individual setup is complete. An explicit layout string can depend on the current number of panes and window size. A layout captured from one terminal is a starting point to inspect on another, not a portable pixel diagram. ## Terminal dimensions When TMUXP_DETECT_TERMINAL_SIZE is 1 (the default), the classic builder asks Python's terminal-size helper for initial session dimensions. COLUMNS and LINES can influence that helper. Fallback width uses TMUXP_DEFAULT_COLUMNS, then COLUMNS, then 80. Fallback height uses TMUXP_DEFAULT_ROWS, then ROWS, with nominal default 24. The helper can choose terminal dimensions instead of its fallback, so a TMUXP_DEFAULT value alone is not an unconditional size override. Detached sessions also need dimensions for reproducible layouts. Invalid numeric environment values can fail before useful construction. ## Focus and indexes Set window focus to select that window after construction, and pane focus to select the active pane within its window. Prefer one focused window and one focused pane per window; multiple true values depend on build order and are harder to reason about. A window_index selects its numeric tmux position. Pane IDs such as `%3` are runtime identities, not YAML pane list positions. Base-index and pane-base-index settings can make visible indexes differ from zero-based list positions. Inspect a running workspace's geometry and selection: ```console $ tmux -L layout-example list-windows -t '=layout-example' ``` This command assumes the sample workspace was loaded on the dedicated layout-example socket. Use [load](../../cli/load/) to select that socket, then inspect panes as needed. A successful load does not prove every native port honors the same focus/index/options policy. ## Current Swift builder Layout descriptions are passed through the builder. Returned Session values are snapshots; refresh after construction to inspect all created windows and panes. Do not infer focus/index support from layout support. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-swift/blob/94b9e4cc436dda8e18e064179ae7d26e55bbbd73/Sources/TmuxWorkspace/Workspace.swift) before using these fields through application code. ## Reference source [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [main-pane-height.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/main-pane-height.yaml); [main-pane-height-percentage.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/main-pane-height-percentage.yaml); [focus-window-and-panes.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/focus-window-and-panes.yaml); [window-index.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/window-index.yaml). --- # Workspace hooks and builders Source: https://libtmux.org/en/swift/latest/workspace/configuration/hooks/ > Tmuxp workspace hooks and builders and current Swift builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Workspace scripts, plugins, and custom builders extend how Python tmuxp creates a session. They are execution features, not passive configuration metadata. A workspace naming Python code needs that code installed or importable in tmuxp's environment. ## Bootstrap with before_script ```yaml session_name: bootstrap-example start_directory: ./ before_script: ./bootstrap.sh windows: - window_name: main panes: - echo bootstrap completed ``` This complete configuration assumes bootstrap.sh exists and is executable. Tmuxp resolves the script relative to the workspace file and uses the session start_directory as the process working directory when supplied. A zero exit status permits configured-window construction to continue; a failing script raises an error. The classic builder has already created the initial session when before_script runs. It kills that session when the bootstrap process fails. That does not undo files or other external effects created by the script. Pane shell_command is different: successful text delivery does not check the command's exit status. ## Python plugins `"plugins"` is a list of Python class references, conventionally a class in a package's plugin module. Install that package into the same Python environment as tmuxp. Plugins can declare tmux, libtmux, and tmuxp version requirements. The lifecycle includes these distinct hooks: | Hook | When it applies | | --- | --- | | `before_workspace_builder` | Initial session exists, before configured windows | | `on_window_create` | A window has been created, before its panes finish | | `after_window_finished` | That window's panes and setup have finished | | `"before_script"` | Plugin callback after session construction | | `reattach` | Reattachment to a session that already exists | The plugin callback named before_script is not the workspace before_script process. Their timing and execution mechanism differ. Plugin methods can change live tmux state; choose a plugin only when its behavior is intended for the workspace. ## Select a workspace builder The default is the built-in classic builder. `workspace_builder` can name a registered entry point in the [`tmuxp.workspace_builders`](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/registry.py) group, a `module:attribute` reference, or a dotted Python path. Custom builders receive expanded configuration and a libtmux server. `workspace_builder_paths` lists trusted directories temporarily added to Python's import path. Tilde and environment variables expand, relative entries resolve against the workspace file, and entries must exist as directories. Tmuxp does not use site.addsitedir for these paths. Adding an import path is not permission to treat arbitrary workspace files as inert data. A builder implements the synchronous build/session interface and cooperates with plugin, progress, before-script, script-output, and build-event callbacks. The configuration alone cannot establish that an arbitrary custom builder honors those callbacks or the classic builder's behavior. ## Pane readiness ```yaml session_name: readiness-example workspace_builder: classic workspace_builder_options: pane_readiness: auto windows: - window_name: main panes: - echo ready ``` Auto, the default, waits for a prompt when the configured session shell is zsh. Always requests the wait for default-shell panes; never skips it. Accepted aliases include true/on/yes/1 for always and false/off/no/0 for never, with strings normalized for case and surrounding whitespace. Unknown values are rejected. Custom pane/window launch commands skip prompt waiting. Readiness checks concern a shell prompt, not the eventual application's health, and do not acknowledge that every later command was consumed. Use [commands](../commands/) for explicit delays and Enter behavior. ## Current Swift builder The Swift model does not load Python plugins, custom builders, or before_script. Its cleanup policy is a native builder concern: it attempts scoped rollback and reports original and cleanup failures when both occur. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-swift/blob/94b9e4cc436dda8e18e064179ae7d26e55bbbd73/Sources/TmuxWorkspace/Workspace.swift) before using these fields through application code. ## Reference source [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [registry.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/registry.py); [protocol.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/protocol.py); [options.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/options.py); [plugins.md](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/docs/topics/plugins.md). --- # Examples Source: https://libtmux.org/en/swift/latest/examples/ > Programs for sending input, capturing output, and building workspaces in each language. These examples show how to complete a tmux task in each language. Use the tabs to select your port. Each page includes the source file and the checks run by that port's test suite; see those details before adapting an excerpt into a standalone program. Examples marked with `file=` are read from the port source during the build. Other blocks are copied excerpts. The source details identify which mechanism each block uses. - **[Attach and send keys](attach-and-send-keys/)**: get a session, send a command, and read output. Uses the hierarchy described in [Server, session, window, pane](/concepts/server-session-window-pane/). - **[Capture pane output](capture-pane-output/)**: read back what a pane is showing, and wait for output to appear instead of guessing a delay. The companion to [Capturing output](/guides/capturing-output/). - **[Build a workspace from a file](workspace-from-file/)**: the tmuxp-shaped job of describing a multi-window session as data and building it in one call, in each port that has one. ## What "verified" means, per port Port repositories use the following checks for their source examples. This site reads or copies those examples; a successful site build alone does not execute them: | Port | Mechanism | What it checks | |------|-----------|-----------------| | Python | `pytest` `testpaths` includes `README.md` and `src/libtmux` | `>>>` doctest blocks run against a real, isolated tmux session on every test run | | TypeScript | `scripts/check-doc-runnable.ts` | A block tagged `` must appear, line for line, in that file, which the integration suite executes | | Go | `go generate ./tmux` (`internal/generate/docs`) | A `` region in `README.md` is rewritten from the matching `// docs:name` … `// docs:end` region in `examples/`; CI fails on drift | | Rust | `#![doc = include_str!("../README.md")]` | The entire README is a doc comment, so `cargo test --doc` compiles and runs every fenced Rust block in it | | Java | `docs-tests` (`./gradlew :docs-tests:test`) | Every Java fence in READMEs and guides is compiled against the real artifacts, then run against real tmux via `libtmux-junit5` | | .NET | `sync_snippets.py --check` + `ReadmeExampleTests` | A `` region is quoted from a tested `[Example]` method; every `csharp run` block is additionally compiled and executed | | C++ | `tools/docs/check_readme.py` | Each ` ```cpp ` block in `README.md` must appear verbatim as a `#region` in `examples/05-readme.cpp`, which CTest builds and runs | | Swift | `Scripts/check_examples.py` | Each ` ```swift ` block in `README.md` and product READMEs must appear in `Examples/Sources/`, which `swift test --package-path Examples` compiles through its public products | See [Testing with libtmux](/guides/testing-with-libtmux/) for the fixture each of those test suites runs against, and each example page below for the exact file a given snippet was quoted from. --- # Attach and send keys Source: https://libtmux.org/en/swift/latest/examples/attach-and-send-keys/ > Get a session handle, send a command to a pane, and capture output. Get a session handle, send a command to a pane, and capture output. These examples use libtmux from your program; to attach your terminal interactively, see [Attaching to tmux](/guides/attaching-to-tmux/). Select your port below. The examples retain their source's setup, error handling, and cleanup, so the operations shown vary by port. [Where this comes from](#where-this-comes-from) identifies each source and its test coverage. ```swift file="Examples/Sources/ExampleCode/Changing.swift" // The examples in the README's "Change what is there" section. import LibTmux public func buildASessionByHand(_ server: Server) async throws -> Pane { let session = try await server.newSession(named: "work", windowName: "editor") _ = try await server.setOption("@purpose", to: "development", scope: .session(session)) let logs = try await server.newWindow(in: session, named: "logs").window let pane = try await server.splitWindow(logs, direction: .right) try await server.run("tail -f /tmp/build.log", in: pane) return pane } public func readBackWhatAPanePrinted(_ server: Server, _ pane: Pane) async throws -> [String] { let lines = try await server.capture(pane) print(lines.suffix(5).joined(separator: "\n")) return lines } public func spendOneProcessOnAllOfIt(_ server: Server) async throws { var plan = TmuxCommandList() for name in ["edit", "test", "logs"] { plan = plan.then("new-window", ["-d", "-n", name]) } _ = try await server.run(plan) } ``` ## Finding an existing session instead For a script that runs repeatedly, look up a session before creating it. [Attaching to tmux](/guides/attaching-to-tmux/#finding-a-session-instead-of-always-creating-one) shows that pattern, and [Filtering and querying, in practice](/guides/querying-and-filtering/) covers absent and ambiguous matches. ## Where this comes from ### Python **Source:** `src/libtmux/server.py`, `session.py`, `pane.py` docstrings **In this page:** hand-quoted, composed from three separate docstrings **Checked by:** `pytest` runs every `>>>` doctest (`testpaths` includes `src/libtmux`) against a real, isolated tmux session on every test run ### TypeScript **Source:** `examples/quickstart/quickstart.ts` **In this page:** read whole from the file **Checked by:** run against real tmux by `bun test examples`; its first half is also mirrored into README.md under a `` marker, checked line-for-line by `scripts/check-doc-runnable.ts` ### Rust **Source:** `crates/libtmux/examples/scratch.rs` **In this page:** read whole from the file **Checked by:** run to completion against a throwaway tmux by `scripts/run-examples.sh` (`just examples`), which CI runs and which also asserts the example leaves no session behind ### Go **Source:** `examples/quickstart/main.go` **In this page:** read whole from the file **Checked by:** the whole file runs against a real tmux server as `TestQuickstart`; the `docs:quickstart` region inside it is additionally mirrored into README.md by `go generate ./tmux`, and CI fails if the two drift ### Java **Source:** `examples/src/main/java/io/github/libtmux/examples/BuildAWorkspace.java` **In this page:** read whole from the file **Checked by:** run against real tmux by the `examples` module's own `ExamplesRunTest` ### .NET **Source:** `examples/LibTmux.Examples/Snippets/OneShot.cs` **In this page:** read whole from the file **Checked by:** its `ConnectAndBuild` region is mirrored into README.md and checked by `sync_snippets.py --check`; the mirrored `csharp run` block is additionally compiled and run by `ReadmeExampleTests` ### C++ **Source:** `examples/05-readme.cpp`, the `connect` and `build` regions **In this page:** Copied excerpts from the `connect` and `build` regions. **Checked by:** quoted verbatim into README.md, checked for drift by `tools/docs/check_readme.py`, and the whole file is built and run by CTest ### Swift **Source:** `Examples/Sources/ExampleCode/Changing.swift` **In this page:** read whole from the file **Checked by:** matched against the README's "Change what is there" section by `Scripts/check_examples.py`; compiled and run through the package's public products by `swift test --package-path Examples` ### Source inclusion A `file="..."` fence reads the named source during the site build. Hand-quoted excerpts are copies; their source files and regions are listed above. --- # Capture pane output Source: https://libtmux.org/en/swift/latest/examples/capture-pane-output/ > Capture a pane's screen and wait for expected output or a completion signal. Read a pane after [sending input](../attach-and-send-keys/). [Sending keys](/guides/sending-keys/#the-race-you-cant-see-from-the-call-site) explains why an immediate capture can miss output. These examples show screen capture and waiting; [Capturing output](/guides/capturing-output/) explains the choices. See [source details](#where-this-comes-from) for each example's source and validation. ## Read what's on screen The .NET example under "Wait for text instead of guessing a delay" uses `Pane.CaptureAsync` within a wait. Its separate Psmux transport also provides a capture API, shown in `examples/LibTmux.Examples/Snippets/Psmux.cs`. ```swift // From Examples/Sources/ExampleCode/Changing.swift, readBackWhatAPanePrinted: shown in full on Attach and send keys. let lines = try await server.capture(pane) ``` ## Wait for text instead of guessing a delay The Python example uses `wait_for`, tmux's signal channel. It waits for a signal from the command rather than matching pane text. `Session.OpenNotifications` receives tmux events as a stream. For tests that need to wait for screen text, use `tmuxtest.WaitForText`; see [Testing with libtmux](/guides/testing-with-libtmux/). Rust's `wait_for_text` looks before it sleeps, joins wrapped lines so a needle spanning a wrap still matches, and returns `PaneWait::Dead` rather than hanging forever if the pane's process ends first. Attach the `ControlClient` to receive `%output` notifications. An unattached client receives command replies only. `TmuxWait.UntilAsync` polls a read against a predicate rather than sleeping a fixed amount. C++ has no checked snippet that waits on pane *text*. `Server::wait_for(channel, timeout)`, in `include/libtmux/server.hpp`, uses tmux's own `wait-for` signal instead of scraping output, and its doc comment explains why that is the safer choice when the command you are waiting on can be made to announce itself: "a server that dies under a waiter makes tmux exit zero, which is indistinguishable from being signalled ... this reports that as a failure instead." ```swift file="Examples/Sources/ExampleCode/Waiting.swift" // The examples in , and the waiting section of the README. import LibTmux public func waitingOnAChannel(_ server: Server, pane: Pane) async throws { // region: guides-capturing-output-176 try await server.run( "make; \(server.shellInvocation) wait-for -S built", in: pane ) try await server.wait(for: "built") // endregion } public func watchingAFormat(_ server: Server, pane: Pane) async throws -> String? { try await server.connected(attachingTo: "work") { server, control in try await control.watch( FormatSubscription( name: "cmd", scope: .pane(pane.id), format: "#{pane_current_command}" ) ) for try await change in control.changes(named: "cmd") { return change.value } return nil } } public func waitingOnOutput( _ server: Server, pane: Pane ) async throws -> OutputWait { let ready = try RegexPattern("Listening on") let failed = try RegexPattern("EADDRINUSE|error", options: [.caseInsensitive]) let waited = try await server.waitForOutput( in: pane, matching: [ready], stoppingAt: [failed] ) return waited } public func watchingForChanges( _ server: Server, pane: Pane, building: Bool ) async throws { var mark = try await server.capture(pane, since: nil).cursor while building { let update = try await server.capture(pane, since: mark) for line in update.lines { print(line) } mark = update.cursor } } ``` `waitForOutput` takes patterns for both success and failure, so a process that fails fast is discovered immediately rather than by timing out. ## Where this comes from ### Python **Source:** `src/libtmux/pane.py` (`capture_pane`), `src/libtmux/server.py` (`wait_for`) docstrings **In this page:** hand-quoted **Checked by:** `pytest` runs every `>>>` doctest against a real, isolated tmux session on every test run ### TypeScript **Source:** `examples/capture/capture.ts` (read), `examples/agent/agent.ts` (wait) **In this page:** read whole from each file **Checked by:** both run against real tmux by `bun test examples`; `agent.ts` is additionally mirrored into README.md under a `` marker checked by `scripts/check-doc-runnable.ts` ### Go **Source:** `examples/quickstart/main.go` (read, already shown whole on the previous page), `examples/control-mode-subscribe/main.go` (wait) **In this page:** read: hand-quoted; wait: read whole from the file **Checked by:** both run against real tmux as `TestQuickstart` / `TestControlModeSubscribe`; the wait file's `docs:watching` region is additionally mirrored into README.md by `go generate ./tmux` ### Rust **Source:** `crates/libtmux/examples/scratch.rs`, already shown whole on the previous page **In this page:** hand-quoted excerpts of the same file **Checked by:** run to completion against a throwaway tmux by `scripts/run-examples.sh`, which CI runs ### Java **Source:** root `README.md` Quickstart (read), `examples/src/main/java/io/github/libtmux/examples/WatchPaneOutput.java` (wait) **In this page:** read: hand-quoted; wait: read whole from the file **Checked by:** every README fence is compiled and run against real tmux by `docs-tests`; `WatchPaneOutput` is additionally run by the `examples` module's `ExamplesRunTest` ### .NET **Source:** root `README.md`, "Running something, and reading it back" **In this page:** hand-quoted **Checked by:** one of the `csharp run` blocks compiled and run against real tmux by `ReadmeExampleTests` ### C++ **Source:** `examples/05-readme.cpp` `capture` region (read); `include/libtmux/server.hpp` doc comment (wait, no fence) **In this page:** hand-quoted **Checked by:** the `capture` region is quoted verbatim into README.md and checked by `tools/docs/check_readme.py`; the whole file is built and run by CTest ### Swift **Source:** `Examples/Sources/ExampleCode/Changing.swift` (read, already shown whole on the previous page), `Waiting.swift` (wait) **In this page:** read: hand-quoted excerpt; wait: read whole from the file **Checked by:** both matched against the README by `Scripts/check_examples.py` and run by `swift test --package-path Examples` ### Source inclusion Go, Rust, and Swift each reuse a file already shown in full on [Attach and send keys](../attach-and-send-keys/#where-this-comes-from): rather than dump the same file a second time, this page quotes just the relevant lines by hand, with a comment naming the source, and points back at the full listing there. --- # Build a workspace from a file Source: https://libtmux.org/en/swift/latest/examples/workspace-from-file/ > The tmuxp-shaped job of describing a multi-window session as data and building it in one call, in each port that has a checked way to do it. [tmuxp](https://tmuxp.git-pull.com/) describes sessions, windows, panes, and shell commands in configuration files. Several libtmux ports provide builders for this format. [Source details](#where-this-comes-from) identify the example files and their checks. For Python, use [tmuxp](https://tmuxp.git-pull.com/), a separate application built on libtmux's `Server`, `Session`, `Window`, and `Pane` APIs. TypeScript's `applyWorkspace` reuses existing objects when the same configuration is applied again. Go's `Example()`, in `workspace/example_test.go`, is a Go `Example` function: `go test` runs it and checks its output against the `// Output:` comment at the end, so this is executed on every test run rather than merely present in a README. `Parse` rejects a field it doesn't recognize rather than dropping it silently, and reports every problem it finds at once with the line it's on. `Build` is not atomic: tmux has no transaction, so a failure partway through leaves whatever was already created in place, identified by the session `Build` still returns. Rust's `freeze(&session).await?` exports an existing session to the workspace format. It recovers windows, panes, and working directories, but cannot recover the shell command originally typed to start a process. Java's `read` and `parse` validate the configuration and return a `Workspace` value. Only `build` changes tmux state. The .NET builder can wait for shell readiness before sending commands; [Sending keys](/guides/sending-keys/#the-race-you-cant-see-from-the-call-site) explains the startup race. It polls `pane_current_command`, `cursor_x`, and `cursor_y` for up to ten seconds by default. `PaneReadiness.Auto` waits for zsh, `Always` waits for every pane running the session's default shell, and `Never` sends immediately. If `BuildAsync` fails partway through, `WorkspaceBuildException.PartialResult` identifies what was created. C++'s `examples/workspace/` implements a consumer of the core API with its own `workspace.hpp` and `tmuxp.hpp` types. Those types are part of the example, not the library package. See [the example's README](https://github.com/libtmux/libtmux-cxx/tree/main/examples/workspace) to adapt it. ```swift file="Examples/Sources/ExampleCode/Workspaces.swift" // The examples in the README's `WorkspaceBuilder` section. import Foundation import LibTmux import TmuxWorkspace public func describeAWorkspaceInSwift() -> Workspace { let workspace = Workspace( sessionName: "work", windows: [ WindowPlan( windowName: "editor", layout: "even-horizontal", panes: [PanePlan(), PanePlan()] ), WindowPlan( windowName: "logs", panes: [PanePlan(shellCommands: ["tail -f /tmp/build.log"])] ), ] ) return workspace } public func buildItOnAServer(_ server: Server, _ workspace: Workspace) async throws -> Session { let session = try await WorkspaceBuilder.build(workspace, on: server) print(session.name, session.windowCount) return session } public func readAWorkspaceWrittenAsJSON(_ json: Data) throws -> Workspace { try Workspace.decode(json: json) } // Deliberately NOT wrapped in `#if YAMLWorkspaces`. A trait defines its // compilation condition only inside the package that declares it, so that guard // in a consumer package is always false and deletes the example — loudly if a // test calls it, silently if nothing does. The symbol is nonetheless here, // because the dependency was resolved with the trait on. public func readATmuxpFile(_ text: String) throws -> Workspace { try Workspace.decode(yaml: text) } ``` Swift's `WorkspaceBuilder.build` rejects an existing session with the requested name. `Workspace.decode(yaml:)` reads tmuxp YAML when the `YAMLWorkspaces` trait is enabled. `Workspace.decode(json:)` needs no additional trait. ## Where this comes from ### Python **Source:** Not listed. **In this page:** no fence; the README says tmuxp is a separate project by design **Checked by:** n/a ### TypeScript **Source:** `examples/workspace/workspace.ts` (`@libtmux/workspace`) **In this page:** read whole from the file **Checked by:** run against real tmux by `bun test examples/workspace` ### Go **Source:** `workspace/example_test.go` (`workspace.Parse` / `workspace.Build`) **In this page:** read whole from the file **Checked by:** `Example()` and its siblings run under `go test` and are checked against their own `// Output:` comments ### Rust **Source:** `crates/tmux-workspace/README.md`, "Build it" **In this page:** hand-quoted **Checked by:** the crate's own `crates/tmux-workspace/src/lib.rs` includes the README as a doc comment (`#![doc = include_str!("../README.md")]`), so `cargo test --doc` runs this exact block ### Java **Source:** `libtmux-workspace/README.md`, "What you get back" **In this page:** hand-quoted **Checked by:** every Java fence in the module's README is compiled and run against real tmux by `docs-tests` ### .NET **Source:** `src/LibTmux.Workspace/README.md` **In this page:** hand-quoted **Checked by:** one of the READMEs and docs `ReadmeExampleTests` compiles and runs against real tmux ### C++ **Source:** `examples/workspace/` (a consumer, not a library API) **In this page:** prose only **Checked by:** `examples/workspace/tests/` runs the consumer suite against real tmux; `ctest -R consumer.workspace` selects it. It exercises the example's own types, not a published `libtmux` API ### Swift **Source:** `Examples/Sources/ExampleCode/Workspaces.swift` **In this page:** read whole from the file **Checked by:** matched against the README's "Workspaces, from a file or from Swift" section by `Scripts/check_examples.py`; compiled and run by `swift test --package-path Examples` ### Source inclusion Rust, Java, and .NET use copied excerpts from their README examples. The source details above identify those files and their checks. --- # Workspace example gallery Source: https://libtmux.org/en/swift/latest/workspace/examples/gallery/ > Pinned YAML examples, JSON counterparts, and execution prerequisites. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. The examples below reproduce the pinned tmuxp YAML fixture corpus. Each record links to its source and, where present, its JSON twin. They illustrate configuration features; they are not all self-contained runnable projects. The `minimal` fixture and several other files omit window names despite the validator requiring them. Treat these as normalization and compatibility probes, not a promise that every fixture passes every load path. Where this port's native parser accepts a fixture below, that establishes only that its document parses, not that it fully executes. The [installation walkthrough](../../guides/installation/) supplies a complete runnable starting file. ## 2-pane-synchronized [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/2-pane-synchronized.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/2-pane-synchronized.json). ```yaml session_name: 2-pane-synchronized windows: - window_name: Two synchronized panes panes: - ssh server1 - ssh server2 options_after: synchronize-panes: on ``` ## 2-pane-vertical [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/2-pane-vertical.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/2-pane-vertical.json). ```yaml session_name: 2-pane-vertical windows: - window_name: my test window panes: - echo hello - echo hello ``` ## 3-pane [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/3-pane.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/3-pane.json). ```yaml session_name: 3-panes windows: - window_name: dev window layout: main-vertical shell_command_before: - cd ~/ panes: - shell_command: - cd /var/log - ls -al | grep \.log - echo hello - echo hello ``` ## 4-pane [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/4-pane.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/4-pane.json). ```yaml session_name: 4-pane-split windows: - window_name: dev window layout: tiled shell_command_before: - cd ~/ panes: - shell_command: - cd /var/log - ls -al | grep \.log - echo hello - echo hello - echo hello ``` ## blank-panes [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/blank-panes.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/blank-panes.json). ```yaml session_name: Blank pane test windows: # Emptiness will simply open a blank pane, if no shell_command_before. # All these are equivalent - window_name: Blank pane test panes: - - pane - blank - window_name: More blank panes panes: - null - shell_command: - shell_command: - # an empty string will be treated as a carriage return - window_name: Empty string (return) panes: - "" - shell_command: "" - shell_command: - "" # a pane can have other options but still be blank - window_name: Blank with options panes: - focus: true - start_directory: /tmp ``` ## env-variables [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/env-variables.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/env-variables.json). ```yaml start_directory: "${PWD}/test" shell_command_before: "echo ${PWD}" before_script: "${MY_ENV_VAR}/test3.sh" session_name: session - ${USER} (${MY_ENV_VAR}) windows: - window_name: editor panes: - shell_command: - tail -F /var/log/syslog start_directory: /var/log - window_name: logging for ${USER} options: automatic-rename: true panes: - shell_command: - htop - ls $PWD ``` ## focus-window-and-panes [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/focus-window-and-panes.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/focus-window-and-panes.json). ```yaml session_name: focus windows: - window_name: attached window focus: true panes: - shell_command: - echo hello - echo 'this pane should be selected on load' focus: true - shell_command: - cd /var/log - echo hello - window_name: second window shell_command_before: cd /var/log panes: - pane - shell_command: - echo 'this pane should be focused, when window switched to first time' focus: true - pane ``` ## main-pane-height-percentage [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/main-pane-height-percentage.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/main-pane-height-percentage.json). ```yaml session_name: main-pane-height start_directory: "~" windows: - layout: main-horizontal options: main-pane-height: 67% panes: - shell_command: - top start_directory: "~" - shell_command: - echo "hey" - shell_command: - echo "moo" window_name: my window name ``` ## main-pane-height [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/main-pane-height.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/main-pane-height.json). ```yaml session_name: main-pane-height start_directory: "~" windows: - layout: main-horizontal options: main-pane-height: 30 panes: - shell_command: - top start_directory: "~" - shell_command: - echo "hey" - shell_command: - echo "moo" window_name: my window name ``` ## minimal [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/minimal.yaml). ```yaml session_name: My tmux session windows: - panes: - ``` ## options [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/options.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/options.json). ```yaml session_name: test window options start_directory: "~" global_options: default-shell: /bin/sh default-command: /bin/sh options: main-pane-height: ${MAIN_PANE_HEIGHT} # works with env variables windows: - layout: main-horizontal options: automatic-rename: on panes: - shell_command: - man echo start_directory: "~" - shell_command: - echo "hey" - shell_command: - echo "moo" ``` ## pane-shell [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/pane-shell.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/pane-shell.json). ```yaml session_name: Pane shell example windows: - window_name: first window_shell: /usr/bin/python2 layout: even-vertical suppress_history: false options: remain-on-exit: true panes: - shell: /usr/bin/python3 shell_command: - print('This is python 3') - shell: /usr/bin/vim -u none shell_command: - iAll panes have the `remain-on-exit` setting on. - When you exit out of the shell or application, the panes will remain. - Use tmux command `:kill-pane` to remove the pane. - Use tmux command `:respawn-pane` to restart the shell in the pane. - Use and then `:q!` to get out of this vim window. :-) - shell_command: - print('Hello World 2') - shell: /usr/bin/top ``` ## plugin-system [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/plugin-system.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/plugin-system.json). ```yaml session_name: plugin-system plugins: - "tmuxp_plugin_extended_build.plugin.PluginExtendedBuild" windows: - window_name: editor layout: tiled shell_command_before: - cd ~/ panes: - shell_command: - cd /var/log - ls -al | grep *.log - echo "hello world" ``` ## session-environment [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/session-environment.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/session-environment.json). ```yaml session_name: Environment variables test environment: EDITOR: /usr/bin/vim DJANGO_SETTINGS_MODULE: my_app.settings.local SERVER_PORT: "8009" windows: - window_name: Django project panes: - ./manage.py runserver 0.0.0.0:${SERVER_PORT} - window_name: Another Django project environment: DJANGO_SETTINGS_MODULE: my_app.settings.local SERVER_PORT: "8010" panes: - ./manage.py runserver 0.0.0.0:${SERVER_PORT} - environment: DJANGO_SETTINGS_MODULE: my_app.settings.local-testing SERVER_PORT: "8011" shell_command: ./manage.py runserver 0.0.0.0:${SERVER_PORT} ``` ## shorthands [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/shorthands.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/shorthands.json). ```yaml session_name: shorthands windows: - window_name: long form panes: - shell_command: - echo 'did you know' - echo 'you can inline' - shell_command: echo 'single commands' - echo 'for panes' ``` ## skip-send-pane-level [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/skip-send-pane-level.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/skip-send-pane-level.json). ```yaml session_name: Skip command execution (pane-level) windows: - panes: - shell_command: echo "___$((1 + 3))___" enter: false - shell_command: - echo "___$((1 + 3))___"\; - echo "___$((1 + 3))___" enter: false ``` ## skip-send [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/skip-send.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/skip-send.json). ```yaml session_name: Skip command execution (command-level) windows: - panes: - shell_command: # You can see this - echo "___$((11 + 1))___" # This is skipped - cmd: echo "___$((1 + 3))___" enter: false ``` ## sleep-pane-level [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/sleep-pane-level.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/sleep-pane-level.json). ```yaml session_name: Pause / skip command execution (pane-level) windows: - panes: - # Wait 2 seconds before sending all commands in this pane sleep_before: 2 shell_command: - echo "___$((11 + 1))___" - cmd: echo "___$((1 + 3))___" - cmd: echo "___$((1 + 3))___" - cmd: echo "Stuff rendering here!" - cmd: echo "2 seconds later" ``` ## sleep-virtualenv [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/sleep-virtualenv.yaml). ```yaml session_name: virtualenv shell_command_before: # - cmd: source $(poetry env info --path)/bin/activate # - cmd: source `pipenv --venv`/bin/activate - cmd: source .venv/bin/activate sleep_before: 1 sleep_after: 1 windows: - panes: - shell_command: - ./manage.py runserver ``` ## sleep [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/sleep.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/sleep.json). ```yaml session_name: Pause / skip command execution (command-level) windows: - panes: - shell_command: # Executes immediately - echo "___$((11 + 1))___" # Delays before sending 2 seconds - cmd: echo "___$((1 + 3))___" sleep_before: 2 # Executes immediately - cmd: echo "___$((1 + 3))___" # Pauses 2 seconds after - cmd: echo "Stuff rendering here!" sleep_after: 2 # Executes after earlier commands (after 2 sec) - cmd: echo "2 seconds later" ``` ## start-directory [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/start-directory.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/start-directory.json). ```yaml session_name: start directory start_directory: /var/ windows: - window_name: should be /var/ panes: - shell_command: - echo "\033c - it trickles down from session-level" - echo hello - window_name: should be /var/log start_directory: log panes: - shell_command: - echo '\033c - window start_directory concatenates to session start_directory - if it is not absolute' - echo hello - window_name: should be ~ start_directory: "~" panes: - shell_command: - 'echo \\033c ~ has precedence. note: remember to quote ~ in YAML' - echo hello - window_name: should be /bin start_directory: /bin panes: - echo '\033c absolute paths also have precedence.' - echo hello - window_name: should be workspace file's dir start_directory: ./ panes: - shell_command: - echo '\033c - ./ is relative to workspace file location - ../ will be parent of workspace file - ./test will be \"test\" dir inside dir of workspace file' - shell_command: - echo '\033c - This way you can load up workspaces from projects and maintain - relative paths.' ``` ## suppress-history [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/suppress-history.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/suppress-history.json). ```yaml session_name: suppress suppress_history: false windows: - window_name: appended focus: true suppress_history: false panes: - echo "window in the history!" - window_name: suppressed suppress_history: true panes: - echo "window not in the history!" - window_name: default panes: - echo "session in the history!" - window_name: mixed suppress_history: false panes: - shell_command: - echo "command in the history!" suppress_history: false - shell_command: - echo "command not in the history!" suppress_history: true - shell_command: - echo "window in the history!" ``` ## window-index [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/window-index.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/window-index.json). ```yaml session_name: Window index example windows: - window_name: zero panes: - echo "this window's index will be zero" - window_name: five panes: - echo "this window's index will be five" window_index: 5 - window_name: one panes: - echo "this window's index will be one" ``` ## Prerequisites and portability SSH examples need the named hosts. Django and virtualenv examples need their project and environment. The plugin example needs its named Python plugin. Shell paths, log directories, top, `htop`, and editors are host-specific. The pane-shell fixture includes an obsolete Python 2 path; it is retained as upstream evidence, not recommended installation guidance. Review [configuration](../../configuration/), [commands](../../configuration/commands/), and [compatibility](../../reference/compatibility/) before adapting these fixtures. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Guides Source: https://libtmux.org/en/swift/latest/guides/ > Task-oriented walkthroughs that sit between the concepts and each port's own API reference. Use these guides to connect to tmux, send input, capture output, query objects, and test your program. [Concepts](/concepts/) explains the object model and transport choices. - **[Getting started](getting-started/)**: install tmux, choose a port, and run an example. - **[Attaching to tmux](attaching-to-tmux/)**: select a socket and find or create a session. - **[Sending keys](sending-keys/)**: send literal text, named keys, and Enter. - **[Capturing output](capturing-output/)**: read the screen or scrollback and wait for a result. - **[Filtering and querying, in practice](querying-and-filtering/)**: apply the lookup contracts from [Filtering and queries](/concepts/queries/). - **[Testing with libtmux](testing-with-libtmux/)**: use isolated tmux servers and manage test cleanup. [Examples](/examples/) provides source-backed programs for the same tasks, with source and validation details on each page. --- # Getting started Source: https://libtmux.org/en/swift/latest/guides/getting-started/ > Install tmux, pick a port, and run the smallest thing that proves your setup works. ## Install tmux The common tmux baseline documented here is 3.2a. Individual features can require a newer release; check your port's compatibility notes. Confirm your installed version: ```console $ tmux -V ``` If `tmux` is missing or older than 3.2a, install a supported version with your platform's package manager. libtmux uses an installed tmux executable. ## Pick a port Choose the port for your project's language: [Python](/py/), [TypeScript](/ts/), [Rust](/rs/), [Go](/go/), [Java and Kotlin](/java/), [.NET](/dotnet/), [C++](/cxx/), or [Swift](/swift/). [Server, session, window, pane](/concepts/server-session-window-pane/) explains the shared model, and [Control mode vs one-shot](/concepts/transports/) covers transport differences. For a prerelease package, pin an exact version and check its release notes before upgrading. API availability and defaults can differ between ports. ## Run the smallest thing that proves it works Start a tmux session to connect to: in one terminal: ```console $ tmux new-session -s foo -n bar ``` In a second terminal, install your port's package and run its example. The Python example uses the `foo` session above; the other examples create their own sessions. Installation commands appear in comments at the start of each block. [Attach and send keys](/examples/attach-and-send-keys/) provides the full examples, their source files, and their validation details. ```swift // .package(url: "https://github.com/libtmux/libtmux-swift", from: "0.1.0") let session = try await server.newSession(named: "work", windowName: "editor") let logs = try await server.newWindow(in: session, named: "logs").window let pane = try await server.splitWindow(logs, direction: .right) try await server.run("tail -f /tmp/build.log", in: pane) let lines = try await server.capture(pane) ``` ## What just happened A server handle targets tmux without taking over your terminal. Creating a session starts the server if needed. Sending keys writes input to a pane; the method's Enter and literal-text options control how tmux interprets it. [Sending keys](../sending-keys/) explains those defaults. Capture methods read the pane's screen or a requested scrollback range. ## Where to go next - [Concepts](/concepts/) for the mental model behind what you just did: the object hierarchy, how commands actually reach tmux, and how filtering works once you have more than one session to choose from. - [Attaching to tmux](../attaching-to-tmux/), [Sending keys](../sending-keys/), and [Capturing output](../capturing-output/) go one level deeper into each half of the round trip you just ran. - [Attach and send keys](/examples/attach-and-send-keys/) for the fully checked version of every block above, and how each one is verified. - Your port's own API reference (via the port switcher) once you're ready to look up method details. --- # Attaching to tmux Source: https://libtmux.org/en/swift/latest/guides/attaching-to-tmux/ > What a plain constructor call actually connects to, and how to find a session that might already exist instead of always creating a new one. Obtain a server and session handle to control tmux from your program. Your process keeps its own stdin and stdout, and tmux continues running independently. [Attach and send keys](/examples/attach-and-send-keys/) demonstrates this workflow. Attaching your terminal is a separate operation. Python's `Session.attach()` runs `tmux attach-session` and hands the terminal to tmux. [tmuxp](https://tmuxp.git-pull.com/) uses it after building a workspace. Check your port's reference if your program needs to hand over the terminal. ## Which socket a bare constructor reaches Use an explicit socket when several tmux servers may be running. The examples below show each constructor's defaults and environment-aware alternatives. To locate the server from inside a pane, use the port's environment lookup API for `TMUX` and `TMUX_PANE`. ```swift // The literal quickstart: there is no bare no-argument constructor, so // naming the socket is not optional the way it is elsewhere. let server = try Server(socketName: "default") ``` Sources: Python's `Server.from_env()` is doctested in `src/libtmux/server.py` (`pyproject.toml` `testpaths`). .NET's resolution order and `FromEnvironment` are from `src/LibTmux/README.md` ("Where a bare connect lands"), one of the nine documents `ReadmeExampleTests` compiles and runs. Go's is `tmux/server_options.go`'s doc comments. Rust's fallback is `crates/libtmux/examples/find.rs`, run via `cargo run --example find`. C++'s four constructors are the README's own description of `Server`, at the top of "What is libtmux?". Swift's is `README.md`'s top-level quickstart, matched against `Examples/Sources/QuickStart/main.swift` by `Scripts/check_examples.py`. Java's is `README.md`, run by [`docs-tests`](../testing-with-libtmux/#java-docs-tests). ## Finding a session instead of always creating one A script that runs more than once usually wants "attach if a session by this name already exists, create it otherwise," not a fresh session every time. ```swift // hasSession answers the question directly as a Bool, no exception needed // either way. if try await server.hasSession("work") == false { _ = try await server.newSession(named: "work", windowName: "start") } ``` Sources: Go uses `examples/filter-query/main.go`, region `docs:query-in-tmux`. Java uses `examples/.../BuildAWorkspace.java`, run by the examples module's tests. For exactly-one lookup semantics, see [Filtering and querying, in practice](../querying-and-filtering/). Swift uses `Examples/Sources/ExampleCode/Querying.swift`, checked against the README by `Scripts/check_examples.py` and exercised by `Examples/Tests/ExampleTests/ModeTests.swift`. .NET exposes `Server.HasSessionAsync(name)` in `src/LibTmux/Server.Lifecycle.cs`. Its `CreateSessionAsync` supports `ReplaceExisting`, which kills and recreates the session. Rust and C++ provide query APIs for session lookup. See [Filtering and querying, in practice](../querying-and-filtering/) and the port references for the call signatures; this page has no tested excerpt for those combinations. ## Where to go next - [Sending keys](../sending-keys/) and [Capturing output](../capturing-output/) pick up once you have a pane handle. - [Attach and send keys](/examples/attach-and-send-keys/) has the full, sourced code for the round trip this guide assumes. - [Testing with libtmux](../testing-with-libtmux/) if the server you want to attach to is one your own test suite should own and tear down. --- # Sending keys Source: https://libtmux.org/en/swift/latest/guides/sending-keys/ > Literal text versus tmux key names, whether Enter is pressed for you, and why a command can outrun the shell about to run it. Send literal text to type characters into a pane, or send tmux key names such as `C-c`, `Enter`, and `Up` to press those keys. Check the method's literal-text and Enter defaults: typing the word `Enter` and pressing Enter are different operations. ## Literal text, key names, and whether Enter follows These examples show each port's text, named-key, and Enter behavior. [Attach and send keys](/examples/attach-and-send-keys/) provides the full source examples and validation details. ```swift // One call: types the command line, then presses Enter. No literal switch // and no separate "type, don't submit" step is exposed at this level. try await server.run("echo hey", in: pane) ``` Check each method's input contract before sending text that could be a key name. Some ports separate text and key-name methods; others use a literal-text flag. [Concepts](/concepts/) introduces the shared tmux model. ## The race you can't see from the call site Completing `send-keys` means tmux accepted the input. The shell may still be starting, and the command may still be running. The port examples provide different ways to wait: - **Rust** uses a `retry_until` loop in the README's query example to wait for the shell. - **Go** provides `tmuxtest.WaitForShellReady` for tests that need a ready shell. - **.NET** demonstrates waiting for command output in the README's "Running something, and reading it back" section. Wait for shell readiness before sending input when startup matters. Then wait for the command's expected output or a completion signal before reading its result. The next guide covers those waiting APIs. ## Where to go next - [Capturing output](../capturing-output/): reading back what you just sent, and waiting for it correctly instead of guessing a delay. - [Attach and send keys](/examples/attach-and-send-keys/): the full sourced round trip this guide picks apart piece by piece. --- # Capturing output Source: https://libtmux.org/en/swift/latest/guides/capturing-output/ > Read a pane's screen or scrollback and wait for output or a completion signal. Capture a pane to read its visible screen or scrollback. After [Sending keys](../sending-keys/), wait for the expected output or a completion signal before reading the result. ## Visible pane vs. scrollback `tmux capture-pane` distinguishes the currently visible screen from the scrollback history above it, and every port exposes that split rather than flattening it: ```swift // The streaming form (below) additionally tracks a cursor, so a caller can // ask for only what's new since the last read. let lines = try await server.capture(pane) ``` Java's `pane.capture()` and .NET's `pane.CaptureAsync()` return the visible pane as a list of lines; neither's own README shows a scrollback option as of this page, so check the port's reference before assuming one exists. Sources: TypeScript's is `examples/capture/capture.ts`, run by `bun test examples/capture`. Go's is `examples/quickstart/main.go`. Rust's is `crates/libtmux/README.md`'s capability table, doctested via `#![doc = include_str!("../README.md")]`. C++'s is `README.md`'s "Read a pane" section, quoted verbatim from `examples/05-readme.cpp`'s `capture` region and checked by `tools/docs/check_readme.py`. Swift's is `README.md`, "Change what is there." ## Wait for the expected text An immediate capture can race the shell, as [Sending keys](../sending-keys/#the-race-you-cant-see-from-the-call-site) explains. Wait for the expected text with a timeout so your program stops promptly when the output arrives and reports a failure if it never does: ```swift // Takes patterns for both success and failure, so a process that fails // fast doesn't have to be discovered by timeout. try await server.waitForOutput(in: pane, matching: [ready], stoppingAt: [failed]) ``` Python's pytest plugin supplies isolated test servers; see [Testing with libtmux](../testing-with-libtmux/). For Python and C++ signal-based waiting, use the `wait-for` APIs below. This page does not include a wait-for-text helper for those ports. Sources: Go's is `tmux/tmuxtest/screen.go`, quoted in `README.md`'s "Testing your own code" section. Rust's is `crates/libtmux/README.md`, doctested. TypeScript's is `examples/agent/agent.ts`, run by the integration suite and quoted in `packages/libtmux/README.md` (``). Java's is `examples/.../WatchPaneOutput.java`. .NET's is `README.md`, one of the `csharp run` blocks `ReadmeExampleTests` runs. Swift's is `Examples/Sources/ExampleCode/Waiting.swift`, matched against `` and the README by `Scripts/check_examples.py`; the same file's `server.capture(pane, since: mark)`, called in a loop with the cursor it returns, is the "watch as it prints" shape for output too large or too open-ended to wait on a single pattern. ## When the pane can announce itself: `wait-for`, not scraping If you control the command, have it signal completion with `tmux wait-for -S done`. Wait on the same channel to avoid matching screen text: The Python example is a doctest in `src/libtmux/server.py`. C++'s `Server::wait_for(channel, timeout)`, declared in `include/libtmux/server.hpp`, also detects a server that dies during the wait. Swift's `server.wait(for:)` example waits for a build to signal completion: ```swift file="Examples/Sources/ExampleCode/Waiting.swift" region="guides-capturing-output-176" try await server.run( "make; \(server.shellInvocation) wait-for -S built", in: pane ) try await server.wait(for: "built") ``` Source: `Examples/Sources/ExampleCode/Waiting.swift`. Rust's `crates/libtmux/README.md` documents the same pattern under "tmux keeps a signal nobody is waiting on, so the job finishing first does not lose the race, and nothing polls," runnable as `examples/orchestrate.rs`. ## Where to go next - [Filtering and querying, in practice](../querying-and-filtering/): once you're reading more than one pane, finding the right one to capture. - [Testing with libtmux](../testing-with-libtmux/): the isolated-server fixtures that make waiting on real tmux practical inside a test suite. - [Capture pane output](/examples/capture-pane-output/): the full sourced code for the patterns above. --- # Filtering and querying, in practice Source: https://libtmux.org/en/swift/latest/guides/querying-and-filtering/ > Filter tmux objects, require one match, and choose where a query runs. Find sessions, windows, or panes with collection filters and exactly-one lookups. [Filtering and queries](/concepts/queries/) explains the result-count contracts and the choice between local and tmux-side filtering. This guide adds examples for common queries. ## Filling in the rest of the cardinality table | Port | Collection filter | Exactly-one | Empty | Several | |------|--------------------|--------------|-------|---------| | Go | `tmuxq.Where(values, predicate)` | `tmuxq.ExactlyOne(values, predicate)` | `tmuxq.ErrNoMatch` | `tmuxq.ErrMultipleMatches` | | Rust | `.iter().matching(&expr)` | `.exactly_one()` | prints via the error's `Display` | same, one error type covers both | | C++ | pipe a range into [`libtmux::matching(expr)`](/cxx/latest/reference/libtmux-matching/) | `libtmux::exactly_one(range)` | `.error()` says which way it went wrong | same call, same error type | Go's `ExampleExactlyOne` in `tmuxq/example_test.go` checks the result with `go test` and `// Output:` assertions: Rust's is `examples/find.rs`, run via `cargo run --example find`: C++'s is quoted straight from `examples/05-readme.cpp`'s `cardinality` region into `README.md`, and `tools/docs/check_readme.py` fails the build if the two ever disagree: For .NET and Swift result-count handling, consult the port reference. The examples here demonstrate .NET's `IEnumerable.Matching(expression)` returning an `IReadOnlyList` and Swift's `hasSession(_:)` returning a `Bool`. The latter checks existence; see [Attaching to tmux](../attaching-to-tmux/#finding-a-session-instead-of-always-creating-one). ## Declarative filters that travel, beyond Python and TypeScript [Filtering and queries](/concepts/queries/) covers Python's `.filter()` lookups and TypeScript's `.where()` documents. Two more ports build the same "a query is data, not code" idea, verified against their own README: ```swift // Built from key paths, so a text operator on a number is a compile error, // and it holds no closures, so it encodes for an MCP tool call. let expression = FilterExpr.where(\.currentCommand, .isIn(["nvim", "vim"])) ``` Sources: .NET's is `src/LibTmux/README.md`, "Filtering." Swift's is `Examples/Sources/ExampleCode/Filtering.swift`, matched against the README by `Scripts/check_examples.py`. ## Case-insensitive matching ```swift // Passed to the regex pattern itself rather than to the filter. let editors = try RegexPattern("^(n?vim|hx)$", options: [.caseInsensitive]) let expression = FilterExpr.where(\.currentCommand, .matches(editors)) ``` For case-insensitive matching in Java, .NET, Go, Rust, and C++, consult the port reference. Sources for the examples above: Python's lookup is covered in [Filtering and queries](../../concepts/queries/); TypeScript's is in `README.md`, "What querying looks like"; Swift's is in `Examples/Sources/ExampleCode/Filtering.swift`. ## Push the filter into tmux, or read once and filter locally Use a tmux-side filter to reduce the rows returned, or query a snapshot when you need several answers from one read. [Filtering and queries](/concepts/queries/) compares Python's `search_sessions()` with `.filter()`, and Go's `SearchPanes` with a snapshot plus `tmuxq.Where`. Check the required tmux version. Unknown format tokens expand to empty values, so validate an unexpectedly empty search before concluding that no objects match. ## Where to go next - [Attach and send keys](/examples/attach-and-send-keys/): its "Finding an existing session instead" section is this guide's recipes applied to one concrete lookup. - [Testing with libtmux](../testing-with-libtmux/): most of the fixtures there hand you a server with exactly one thing on it, which is precisely when an exactly-one query is the right tool instead of a filter you then index into. --- # Testing with libtmux Source: https://libtmux.org/en/swift/latest/guides/testing-with-libtmux/ > Every port ships a way to give your own tests a real, disposable tmux server. What each one hands you, and what it guarantees about cleanup. Use a private tmux server to test code that creates sessions, sends input, or captures output. The fixtures below allocate a separate socket and manage normal test cleanup. Cleanup after abrupt process termination depends on the fixture; Java's stale-server cleanup is described below. Request the fixture to obtain its server. Python's `session` fixture depends on `server`, so requesting a session also creates an isolated server: That exact block is a doctest in `src/libtmux/pytest_plugin.py`, checked by running it as a nested pytest run and asserting it passes. `session_params` overrides how the fixture builds a session (window size, for instance) without forking it; a temporary `HOME` and tmux config keep window and pane indices stable across machines, so an assertion like `window_name == "test"` doesn't depend on whatever `.tmux.conf` the test runner happens to have. `tmuxtest.NewServer(ctx, t)` captures the environment and working directory, resolves the tmux executable, and creates a server on its own socket. Construction can return an error. Test cleanup kills the server, and wait failures include the last captured screen. Source: `README.md`, "Testing your own code," backed by `tmux/tmuxtest/`. Enable the `test-support` feature in a dev-dependency. The crate README uses these guards in doctests through `#![doc = include_str!("../README.md")]`. `libtmux::test::retry_until(deadline, condition)` polls an arbitrary async condition; `Pane::wait_for_text` waits specifically for pane text. See [Capturing output](../capturing-output/). `libtmux-junit5` supplies each test with a running `Server` containing a session named `libtmux`. Request `TmuxSocketPath` when your code takes a socket path. Fixtures live in JUnit's per-test extension store. A shutdown hook kills servers owned by that JVM, and startup cleanup removes servers left by JVMs that have exited. Source: `libtmux-junit5/README.md`. The port's `docs-tests` module compiles Java fences from READMEs and guides, then runs them against `libtmux-junit5` servers. A `` directive can instead require a named exception, a compile failure, or an explicit skip reason. Source: `docs-tests/README.md`. `LibTmux.Testing` ships as a separate package, under `src/LibTmux.Testing/`. `await using` disposes the scope and kills its server when the block exits. Use `TmuxWait.UntilAsync` to wait for expected state; see [Capturing output](../capturing-output/). Source: `README.md`, "Testing your own code," exercised by `ReadmeExampleTests`. The example comes from `README.md`, "Testing your own tmux tools," and is checked against the `fixture` region in `examples/05-readme.cpp` by `tools/docs/check_readme.py`. Enable the `testing` CMake component with `find_package(libtmux COMPONENTS testing)`. It creates a private socket and temporary directory, sets `TMUX_TMPDIR`, and removes `TMUX` and `TMUX_PANE` from the child environment. `SocketNamespace::consumer(...)` labels sockets with the consumer suite's name. `examples/tests/README.md` shows use from outside the library's build tree. ```swift import Testing import TmuxFixture try await withTmuxServer { server in let sessions = try await server.sessions() #expect(sessions.map(\.name) == ["bootstrap"]) } ``` `TmuxFixture` is a separate package product. It starts a server with a bootstrap session and limits concurrent fixtures to reduce process and pseudo-terminal exhaustion. `LIBTMUX_TMUX_BIN` selects the executable; otherwise it checks installed locations. Source: `Tests/TmuxFixture/README.md`. TypeScript's harness at `packages/libtmux/src/_internal/test/testkit.ts` is internal and unpublished. For external tests, create an isolated `Server` and manage its cleanup in your test framework. ## Where to go next - [Capturing output](../capturing-output/): the wait helpers most of these fixtures are meant to be used alongside, instead of a fixed `sleep` in a test. - [Attach and send keys](/examples/attach-and-send-keys/) and [Capture pane output](/examples/capture-pane-output/): the same operations these fixtures give you a server to run, shown as tested examples in their own right. --- # Install and load a workspace Source: https://libtmux.org/en/swift/latest/workspace/guides/installation/ > Build the local Swift workspace CLI and load a session on a private tmux socket. Build and run the native Swift `tmux-workspace` command from the local `workspace-cli` checkout. **This is a partial, unreleased implementation.** These commands require that local source; they are not registry installation instructions or a claim that the CLI is available on the published branch. ## Build from the local checkout Run these commands from the native repository root. Use a Unix environment with tmux 3.2a or newer on `PATH` for this walkthrough. Use Swift 6.2 or newer on Linux. On macOS, use Xcode's Swift 6.3 or newer toolchain; the subprocess dependency requires it. The package targets macOS 13 or newer. Enable the YAML build trait for this walkthrough; JSON-only builds can omit it. ```console $ swift build \ --jobs 2 \ --traits YAMLWorkspaces \ --force-resolved-versions \ --product tmux-workspace ``` Outside tmux, this local loader requires an explicit endpoint. The walkthrough supplies one with `-S`. A copied Linux executable still needs the Swift runtime libraries supplied by its toolchain; it is not a standalone distribution. Inspect the built command: ```console $ .build/debug/tmux-workspace --help ``` ## Create the input Keep this shell open for the walkthrough. Create a temporary directory for its configuration and private tmux socket: ```console $ WORKSPACE_TMP="$(mktemp -d)" ``` Write a minimal configuration with two blank shell panes to [`workspace.yaml`](./#create-the-input) inside that directory: ```console $ cat > "$WORKSPACE_TMP/workspace.yaml" <<'YAML' session_name: workspace-guide windows: - window_name: editor layout: even-horizontal panes: [null, null] YAML ``` ## Load and inspect Load detached on the temporary socket. The JSON result describes the load; `-d` prevents terminal attachment: ```console $ .build/debug/tmux-workspace load \ -S "$WORKSPACE_TMP/tmux.sock" \ -d \ --json \ "$WORKSPACE_TMP/workspace.yaml" ``` Inspect the two panes through the same endpoint: ```console $ tmux \ -S "$WORKSPACE_TMP/tmux.sock" \ list-panes \ -t '=workspace-guide:editor' ``` Attach with tmux when ready: ```console $ tmux \ -S "$WORKSPACE_TMP/tmux.sock" \ attach-session \ -t '=workspace-guide' ``` Detach with your configured tmux detach binding. Capture the live session without choosing a file destination: ```console $ .build/debug/tmux-workspace freeze \ -S "$WORKSPACE_TMP/tmux.sock" \ --json \ workspace-guide ``` Capture reports recoverable live state. It cannot reconstruct the original command history, script or plugin definitions. Remove the walkthrough session when finished: ```console $ tmux \ -S "$WORKSPACE_TMP/tmux.sock" \ kill-session \ -t '=workspace-guide' ``` The configuration remains in the temporary directory until you remove it. Every tmux command above addresses that private socket. ## Current limits `load -2` forces 256-color handling in native tmux clients. Legacy `-8` is recognized but rejected before document lookup because supported tmux versions do not implement 88-color mode. Without `-2`, tmux detects color support. `--log-level` filters advisory diagnostics; fatal errors remain visible. `load --log-file` appends structured lifecycle and diagnostic records to a regular file. A write failure reports a secondary diagnostic and preserves the load result. Human load displays progress on terminal stderr, with presets, custom counters and a bounded recent-output panel. It uses the initial terminal size and conservative Unicode clipping. Bootstrap output keeps its original stdout or stderr destination, but is collected before display. Machine output disables the panel and emits structured window/pane events. Interruption clears the panel; SIGINT and SIGTERM return status 130 and stop captured children. Terminal attachment, plugins/custom builders, further pane/window execution settings, fuller capture, generated manuals and portable distribution remain unfinished. The complete tmuxp flag surface is not available. Python-specific shell behavior requires an interpreter with tmuxp 1.74.0 installed. Select it with `TMUX_WORKSPACE_PYTHON`. Ordinary native loading of this example does not require Python. ## Python alternative For the separate released tmuxp application, install its isolated Python tool environment with uv: ```console $ uv tool install tmuxp ``` Follow the [Python installation guide](/py/latest/workspace/guides/installation/) for that workflow. Installing tmuxp does not install the native command. ## Continue [Discovery](../discovery/), [configuration](../../configuration/) and the [load reference](../../cli/load/) explain the tmuxp compatibility model. Compare those references with the local command's help and the limits above. [Export and reload](../export-session/) explains the capture workflow, and the [compatibility reference](../../reference/compatibility/) records builder gaps. Use [Internals](../../internals/) for the library and consumer APIs. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Find saved workspaces Source: https://libtmux.org/en/swift/latest/workspace/guides/discovery/ > Resolve project files, explicit paths, and global workspace names. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Use an explicit YAML or JSON file path when you need an unambiguous input. A directory resolves its project configuration, and a saved name resolves through the tmuxp configuration roots. ## Global directories An existing `TMUXP_CONFIGDIR` takes precedence, followed by the XDG configuration directory and then the legacy [`~/.tmuxp`](./) directory. A nonexistent explicit directory does not automatically win discovery. See [environment](../../configuration/environment/) for the relevant variables. ## Project files Project discovery walks from the current directory toward its ancestors, stopping at home or the filesystem root. It selects at most one candidate per directory, preferring `.tmuxp.yaml`, then [`.tmuxp.yml`](./), then [`.tmuxp.json`](./). Nearer directories come first. It does not recursively enumerate child projects. Load the current project's configuration: ```console $ tmuxp load . ``` The normal command can attach or prompt. Use `-d` when you want detached execution and select `-L` or `-S` for an isolated server. [ls](../../cli/ls/), [search](../../cli/search/), and [edit](../../cli/edit/) use the same discovery concepts with their own result and error behavior. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Automate workspace operations Source: https://libtmux.org/en/swift/latest/workspace/guides/automation/ > Python automation and the local native CLI machine protocol. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. For the current Python loader, supply an explicit file, dedicated socket, and `-d` to avoid attachment. `--yes` answers yes/no questions; it is not a universal replacement for missing session, format, or destination choices. ## Read current records ```console $ tmuxp ls --json ``` Validate the JSON object before consuming its `workspaces` array. Python search has different empty-result behavior: no matches can produce no bytes rather than `[]`. See [search](../../cli/search/) before relying on a pipeline. ## Proposed native machine protocol Local native CLIs provide `--json` and `--ndjson`. Implemented services and terminal behavior vary by port. The shared machine contract resolves choices from arguments, avoid implicit stdin prompts, keep diagnostics on stderr, and prevent child output or attachment from corrupting stdout. The operation result must report partial completion instead of claiming rollback. NDJSON load events must be observable while work is still running, followed by exactly one terminal record. A buffered array split into lines after completion does not meet this requirement. Both output flags together select NDJSON. Read [output](../../reference/output/) for stream shapes and [exit behavior](../../reference/exit-codes/) for errors and interruption. [Export and reload](../export-session/) distinguishes document encoding from CLI result encoding. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Export and reload a session Source: https://libtmux.org/en/swift/latest/workspace/guides/export-session/ > Capture, inspect, and replay a workspace without assuming lossless recovery. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Use the session created by the [installation walkthrough](../installation/). Capture it to a new YAML destination: ```console $ tmuxp freeze \ -L workspace-guide \ --workspace-format yaml \ --save-to captured-workspace.yaml \ --yes \ workspace-guide ``` Review the output file. Capture cannot reconstruct original scripts, shell history, plugin decisions, comments, or every application's state. Compare window and pane topology, directories, layouts, environment, and options explicitly. Replay under a new name on the same dedicated server: ```console $ tmuxp load \ -L workspace-guide \ -d \ -s workspace-replay \ captured-workspace.yaml ``` Inspect the replay: ```console $ tmux -L workspace-guide list-panes -t '=workspace-replay' ``` Remove the replay when finished: ```console $ tmux -L workspace-guide kill-session -t '=workspace-replay' ``` The [freeze reference](../../cli/freeze/) explains prompts and overwrite behavior. [convert](../../cli/convert/) changes document representation without validating native execution support. The [native compatibility page](../../reference/compatibility/) records which ports lack capture or lose fields. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Troubleshoot workspace loading Source: https://libtmux.org/en/swift/latest/workspace/guides/troubleshooting/ > Diagnose argument, discovery, configuration, shell, and partial-build failures. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Start by identifying the failing stage. A parser error happens before a workspace is loaded. A missing file is a discovery problem. An accepted document can still fail during tmux creation, shell startup, command dispatch, or a plugin callback. ## Arguments and files Use the [command reference](../../cli/) for local flag meanings. Place root `--color` and `--log-level` before the command. Keep multiple load filenames together. Both importer children require a source argument. Confirm an explicit file works before investigating saved-name discovery. ## Configuration and commands Check the [configuration reference](../../configuration/) and the [example gallery](../../examples/gallery/). YAML parsing alone does not validate keys or prove builder support. A missing shell executable, directory, plugin package, SSH target, or application can prevent an otherwise valid workspace from behaving as intended. Commands are sent into panes and can require shell readiness. `enter: false` intentionally leaves text unsubmitted. Delays are measured in seconds; explicit zero and omission differ. See [commands](../../configuration/commands/) and [hooks](../../configuration/hooks/). ## Diagnostics and remaining state ```console $ tmuxp debug-info --json ``` Inspect raw tmux values before sharing diagnostics. Use the same `-L` or `-S` endpoint when inspecting a failed load. A partial build can leave sessions, windows, and panes behind; inspect them before cleanup. Do not assume rollback or kill an unrelated default server. Native failures must be evaluated against [port compatibility](../../reference/compatibility/), not inferred from a Python example. The native machine contract reports completed and failed stages explicitly. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Inspect a workspace through MCP Source: https://libtmux.org/en/swift/latest/workspace/guides/inspect-with-mcp/ > Connect the development Swift MCP server to a session loaded by its native workspace CLI. Inspect the session you loaded with the Swift workspace CLI by pointing its MCP server at the same tmux socket. The loaded windows and panes are ordinary tmux objects; discovery returns their existing IDs. **This guide uses development `workspace-cli` source.** Continue the [installation walkthrough](../installation/#load-and-inspect) through its detached load, keeping that shell and `WORKSPACE_TMP` available. Leave the `workspace-guide` session running. Build both executables from the same native repository checkout; released package instructions may describe different MCP contracts. ## Build the MCP server Run from the native repository root with the installation walkthrough's toolchain and dependencies. ```console $ swift build \ --configuration release \ --jobs 2 \ --force-resolved-versions \ --product libtmux-mcp ``` Use the Swift toolchain from the installation walkthrough. On Linux, the executable also needs that toolchain's runtime libraries. ## Select the same socket Configure an MCP client to launch the following command with the shown environment. The client owns the process's standard input and output for JSON-RPC messages. ```console $ LIBTMUX_TOOLSETS=inspect \ LIBTMUX_SOCKET_PATH="$WORKSPACE_TMP/tmux.sock" \ .build/release/libtmux-mcp ``` In a client's configuration file, use absolute executable or script paths and expand `WORKSPACE_TMP` to its actual value. Configuration files do not perform shell variable expansion. Retain the environment used to build and run the native executable. For a workspace loaded with `-L NAME`, set `LIBTMUX_SOCKET=NAME` instead of `LIBTMUX_SOCKET_PATH`. Do not set both. If you selected a tmux executable with `LIBTMUX_TMUX_BIN`, supply that same setting to the MCP process. ## Inspect and wait 1. Discover tools with `tools/list` and read the `tmux://capabilities` resource. Confirm that its resolved endpoint matches the loaded socket. 2. Call [list_sessions][mcp-source], then [list_windows][mcp-source] with `session: "workspace-guide"`, and [list_panes][mcp-source]. Retain the returned session, window and pane IDs. 3. Select one returned pane ID for capture or a bounded text wait. Use the argument names below; discover the full schema before adding options. | Tool | Arguments | | --- | --- | | [capture_pane][mcp-source] | `"paneId"`, `"maxLines"` | | [wait_for_text][mcp-source] | `"paneId"`, `"patterns"`, `"timeoutMs"` in milliseconds | Set `"timeoutMs"` to `10000` for a ten-second wait. A pending wait permits other inspection calls on the same connection. To check that behavior, start a wait for text absent from the pane, then request [list_panes][mcp-source] before its deadline. Client cancellation uses `notifications/cancelled` with the outstanding request ID; the connection remains usable for inspection. Use [snapshot_pane][mcp-source] for structured pane state and [capture_since][mcp-source] for incremental observation. Bound capture with `"maxLines"`; read the discovered schemas for cursor and history options. If discovery does not show `workspace-guide`, compare the resolved socket in capabilities with the CLI's `-S` path. A different socket selects a different daemon even when session names match. ## Close the connection Close the MCP connection's standard input to stop the server and release pending work. This separately loaded workspace remains running. When you finish the walkthrough, remove only its session on the same socket: ```console $ tmux \ -S "$WORKSPACE_TMP/tmux.sock" \ kill-session \ -t '=workspace-guide' ``` The configuration remains in the temporary directory until you remove it. See the verified [native workspace workflow][workspace-source] and [development MCP reference][mcp-source] for this source contract. The site's released MCP pages retain their version-pinned contracts. [workspace-source]: https://github.com/libtmux/libtmux-swift/blob/d08c1f527cb6c04ab755b54d37c2b4882be15b6e/Sources/TmuxWorkspaceCLI/README.md [mcp-source]: https://github.com/libtmux/libtmux-swift/blob/d08c1f527cb6c04ab755b54d37c2b4882be15b6e/Sources/libtmux-mcp/README.md --- # Swift workspace internals Source: https://libtmux.org/en/swift/latest/workspace/internals/ > Architecture and development interfaces of the Swift workspace builder. These pages document the in-development workspace builder for contributors and applications that call its APIs. For workspace loading from a terminal, see [tmuxp](https://tmuxp.git-pull.com/). ## Builder pipeline `Workspace` holds typed configuration or decodes JSON and optional YAML. `WorkspaceBuilder.build` applies that description through an async core `Server`. The caller supplies file reading, server startup, command-line handling, and attachment. ## Read the implementation - [Guides](./guides/) show builder setup and application code. - [Topics](./topics/) explain configuration, behavior, and failures. - [Examples](./examples/) exercise the builder through the language API. - [API](../reference/) links the configuration and construction interfaces. ## Implementation scope `TmuxWorkspace` builds a tmux session from Swift values or a [tmuxp](https://tmuxp.git-pull.com)-style configuration. It is a SwiftPM library product beside the core [`LibTmux`](../../reference/) product. Swift and JSON descriptions work without a YAML dependency. Enable the `YAMLWorkspaces` package trait to add YAML decoding. Building uses the same async core server API under either format. ## Existing sessions and versions The builder refuses an existing session with the requested name. On a later failure, it attempts to remove the exact session it created and reports both errors if cleanup also fails. These pages describe the current source API. The port distinguishes its unreleased source examples from the released alpha package. Use a matching source revision for these examples, or consult the release's own README when pinning a published version. [Product and version guidance](https://github.com/libtmux/libtmux-swift/blob/f02a4668570e1cc5198c941413750e021f42c214/README.md) --- # Swift workspace builder behavior Source: https://libtmux.org/en/swift/latest/workspace/internals/topics/ > Internal configuration, application, and failure contracts of the Swift workspace builder. `Workspace`, `WindowPlan`, and `PanePlan` are Swift values that describe the objects to create. Their Codable keys use tmuxp's field spelling. ## Supported format JSON decoding is always available. YAML decoding is compiled only when the package enables `YAMLWorkspaces`, which brings in Yams. Unknown keys are ignored by decoding; successful decoding does not establish full tmuxp compatibility. The model covers names, directories, layouts, and pane commands. Python plugins, hooks, and tmuxp's environment runtime are outside that model. A window directory overrides the workspace directory. A split pane can provide its own directory; the initial pane is created with its window's directory. Commands can request Enter or leave literal text unsubmitted. A successful build means the builder delivered its operations, not that a program launched in a pane is ready or has finished. ## Values and observation The returned `Session` is a captured value, not a live object that refreshes its properties. The builder creates additional windows after obtaining the initial session value. Ask the server for a fresh snapshot when inspecting the completed window and pane membership. Encoding a `Workspace` serializes the description you already hold. It is not a live-session freeze operation and does not export the current state of tmux. ## Existing names and failure cleanup The builder refuses an existing session name. If a later operation fails, it attempts cleanup using the exact created session, with an independent bounded cleanup task. Cancellation of the build does not itself cancel that cleanup. `WorkspaceBuilderError.rollbackFailed` carries the original error and the cleanup error. Inspect both before deciding whether a partial session remains. Removing a session cannot undo external effects already caused by its commands. [Configuration model](https://github.com/libtmux/libtmux-swift/blob/f02a4668570e1cc5198c941413750e021f42c214/Sources/TmuxWorkspace/Workspace.swift); [Builder and rollback](https://github.com/libtmux/libtmux-swift/blob/f02a4668570e1cc5198c941413750e021f42c214/Sources/TmuxWorkspace/WorkspaceBuilder.swift). --- # Use the Swift workspace builder Source: https://libtmux.org/en/swift/latest/workspace/internals/guides/ > Use the in-development Swift workspace builder from application code. Add `TmuxWorkspace` and [`LibTmux`](../../../reference/) to a SwiftPM target. This isolated example also uses the public `TmuxFixture` product for server startup and cleanup. The following dependency selects the source revision used by these examples: ```swift .package( url: "https://github.com/libtmux/libtmux-swift.git", revision: "f02a4668570e1cc5198c941413750e021f42c214" ) ``` Add the products to the target's dependencies: ```swift .product(name: "LibTmux", package: "libtmux-swift"), .product(name: "TmuxWorkspace", package: "libtmux-swift"), .product(name: "TmuxFixture", package: "libtmux-swift") ``` Use the toolchain specified by the port's package manifest and install tmux. For YAML input, enable `traits: ["YAMLWorkspaces"]` on the package dependency. Swift values and JSON need no trait. ## Build an isolated session Place this entry point in your executable target. `withTmuxServer` starts a private server and removes it after the closure, including when building throws. The workspace builder needs a running server for its initial session lookup. ```swift import LibTmux import TmuxFixture import TmuxWorkspace @main struct WorkspaceGuide { static func main() async throws { try await withTmuxServer { @Sendable server in let workspace = Workspace( sessionName: "guide", windows: [WindowPlan( windowName: "editor", panes: [PanePlan(), PanePlan()] )] ) let session = try await WorkspaceBuilder.build(workspace, on: server) print(session.name) } } } ``` Run the executable through SwiftPM: ```console $ swift run ``` ## Read configuration Use `Workspace.decode(json:)` with [`Data`](https://developer.apple.com/documentation/foundation/data), or `Workspace.decode(yaml:)` with a string when the YAML trait is enabled. Review unsupported fields before moving a Python workspace to this structural subset. The fixture removes the server after inspecting the session name. In an application, pass an already running server and retain the result instead. A build failure triggers the builder's own cleanup attempt; inspect `rollbackFailed` because it reports that the cleanup also failed. [Package products and toolchain](https://github.com/libtmux/libtmux-swift/blob/f02a4668570e1cc5198c941413750e021f42c214/Package.swift); [Build contract](https://github.com/libtmux/libtmux-swift/blob/f02a4668570e1cc5198c941413750e021f42c214/Sources/TmuxWorkspace/WorkspaceBuilder.swift). [Isolated server fixture](https://github.com/libtmux/libtmux-swift/blob/f02a4668570e1cc5198c941413750e021f42c214/Tests/TmuxFixture/TmuxFixture.swift). --- # Swift workspace builder examples Source: https://libtmux.org/en/swift/latest/workspace/internals/examples/ > Internal examples for building and inspecting workspaces through the Swift API. The port's [`ExampleCode`](https://github.com/libtmux/libtmux-swift/blob/f02a4668570e1cc5198c941413750e021f42c214/Examples/Package.swift) package contains functions for describing a workspace in Swift, building it, and reading JSON or YAML. Its tests compile and call those functions. ## Describe and build The example's YAML reader requires the dependency's `YAMLWorkspaces` trait. That trait enables the API inside the dependency; it does not define the same compilation condition in a consumer package. ```swift file="Examples/Sources/ExampleCode/Workspaces.swift" // The examples in the README's `WorkspaceBuilder` section. import Foundation import LibTmux import TmuxWorkspace public func describeAWorkspaceInSwift() -> Workspace { let workspace = Workspace( sessionName: "work", windows: [ WindowPlan( windowName: "editor", layout: "even-horizontal", panes: [PanePlan(), PanePlan()] ), WindowPlan( windowName: "logs", panes: [PanePlan(shellCommands: ["tail -f /tmp/build.log"])] ), ] ) return workspace } public func buildItOnAServer(_ server: Server, _ workspace: Workspace) async throws -> Session { let session = try await WorkspaceBuilder.build(workspace, on: server) print(session.name, session.windowCount) return session } public func readAWorkspaceWrittenAsJSON(_ json: Data) throws -> Workspace { try Workspace.decode(json: json) } // Deliberately NOT wrapped in `#if YAMLWorkspaces`. A trait defines its // compilation condition only inside the package that declares it, so that guard // in a consumer package is always false and deletes the example — loudly if a // test calls it, silently if nothing does. The symbol is nonetheless here, // because the dependency was resolved with the trait on. public func readATmuxpFile(_ text: String) throws -> Workspace { try Workspace.decode(yaml: text) } ``` The log pane expects a log file at the configured path. Change that command to match the application before using the description as a launcher. The returned session is a captured value; request a fresh server snapshot to inspect membership after all windows have been created. The [guide](../guides/) provides an isolated executable and cleanup. These helper functions accept a caller-owned server and keep the workspace running for that caller to use. ## Verification From a prepared source checkout, run the example package: ```console $ swift test --package-path Examples ``` The port also checks correspondence between these source functions and its README examples. Source inclusion in this page keeps the excerpt current; it does not run the Swift tests during site rendering. [Example source](https://github.com/libtmux/libtmux-swift/blob/f02a4668570e1cc5198c941413750e021f42c214/Examples/Sources/ExampleCode/Workspaces.swift) --- # Swift workspace builder API Source: https://libtmux.org/en/swift/latest/workspace/reference/ > Internal reference for the Swift workspace builder and configuration APIs. Import `TmuxWorkspace` for the configuration and builder, and [`LibTmux`](../../reference/) for the server and returned session value. ## Configuration values [`Workspace`](./workspace/) contains the session name, optional working directory, and ordered windows. `WindowPlan` contains its name, directory, layout, and panes. `PanePlan` contains its directory and commands. The values conform to `Sendable`, `Hashable`, and `Codable`. `Workspace.decode(json:)` reads JSON data. `Workspace.decode(yaml:)` reads a string only when `YAMLWorkspaces` is enabled. Encoding these values writes the existing description; it does not query tmux for a live export. ## Builder [`WorkspaceBuilder`](./workspacebuilder/) exposes the async `build(_:on:)` operation. It creates a new session on the supplied server and returns a `Session`. Keep using the server for live observation; session properties do not refresh themselves. ## Typed errors [`WorkspaceBuilderError`](./workspacebuildererror/) distinguishes an empty window list, an existing session name, a vanished session, underlying tmux errors, and failure of rollback. `rollbackFailed(original:cleanup:)` preserves both causes. The caller can report the initiating problem and separately inspect whether cleanup left objects behind. The supplied server remains owned by the caller. [Value and decoding contracts](https://github.com/libtmux/libtmux-swift/blob/f02a4668570e1cc5198c941413750e021f42c214/Sources/TmuxWorkspace/Workspace.swift); [Builder contract](https://github.com/libtmux/libtmux-swift/blob/f02a4668570e1cc5198c941413750e021f42c214/Sources/TmuxWorkspace/WorkspaceBuilder.swift). ## API declarations - [PanePlan](https://libtmux.org/en/swift/latest/workspace/reference/paneplan/) - [TmuxShellCommand](https://libtmux.org/en/swift/latest/workspace/reference/tmuxshellcommand/) - [WindowPlan](https://libtmux.org/en/swift/latest/workspace/reference/windowplan/) - [Workspace](https://libtmux.org/en/swift/latest/workspace/reference/workspace/) - [WorkspaceBuilder](https://libtmux.org/en/swift/latest/workspace/reference/workspacebuilder/) - [WorkspaceBuilderError](https://libtmux.org/en/swift/latest/workspace/reference/workspacebuildererror/) --- # Workspace reference generation Source: https://libtmux.org/en/swift/latest/workspace/internals/documentation/ > Keep help, completion, and site references aligned with command metadata. The native Swift CLI derives help and exports from its parser definitions. [Compatibility](../../reference/compatibility/) records current coverage. ## Command metadata ArgumentParser defines the native command graph and supplies `--generate-completion-script` for shell completion. Manual, DocC, and site metadata integration remain separate work. Use supported parser APIs for those exports; private parser reflection is not a stable contract. The [command reference](../../cli/) covers the Python grammar. Native metadata exports need to record command paths, aliases, positional arity, option spellings, types, defaults, choices, required and exclusive groups, store-constant values, repeat and ordering behavior, environment bindings, and child commands. Output schemas and availability belong alongside that metadata. Generate help, completion, and static reference from the same definitions. Compare generated results in CI and test actual installed help separately. A rendered example or successful parser-only probe does not establish service execution, distribution, or tmux compatibility. ## Site integration Keep equivalent task pages at the same workspace path in every port. The existing page switcher offers authored counterparts and leaves absent pages unavailable. A CLI guide and a builder API guide are different counterparts; preserve [builder reference](../../reference/) as its own surface. Write executable shell examples per port. Shared console, YAML, and JSON blocks survive language filtering. Installing a native library must not be presented as installing a workspace executable when it has no such artifact. The site version and workspace package version can differ. Record the package and source revision independently, and do not use an older version-shaped URL to imply that today's command existed in an older release. The installation guide identifies the available source builds and published artifacts. ## Verification Check [configuration](../../configuration/), [machine output](../../reference/output/), and [compatibility](../../reference/compatibility/) together. Exercise equivalent page paths, API backlinks, fragments, sidebars, metadata exports, search, and preview prefixes in assembled output. Verify command examples against isolated tmux servers, with explicit cleanup and known limitations. --- # Third-party notices Source: https://libtmux.org/en/swift/latest/third-party-notices/ > Licences and attribution for the tools that build libtmux.org and the software libtmux depends on. libtmux and this site are built with open-source software. Several of those licences ask that their notice text travel with the work, so it is reproduced here. ## Documentation toolchain | Tool | Licence | Role | |---|---|---| | [Astro](https://astro.build) | MIT | The site shell | | [Tailwind CSS](https://tailwindcss.com) | MIT | Styling | | [Pagefind](https://pagefind.app) | MIT | Site-wide search | | [Expressive Code](https://expressive-code.com) | MIT | Code blocks | | [Sphinx](https://www.sphinx-doc.org) | BSD-2-Clause | Python and C++ reference | | [Furo](https://github.com/pradyunsg/furo) | MIT | Sphinx theme | | [Breathe](https://github.com/breathe-doc/breathe) | BSD-3-Clause | Doxygen XML into Sphinx | | [Doxygen](https://www.doxygen.nl) | GPL-2.0-only | Parses C++ headers to XML | | [API Extractor](https://api-extractor.com) | MIT | TypeScript API model | | [IBM Plex](https://github.com/IBM/plex) | OFL-1.1 | Typeface | ### A note on Doxygen Doxygen is licensed GPL-2.0-only. It runs as a build step that reads libtmux's own headers and emits XML; that XML is rendered by Breathe and Sphinx, and no Doxygen-generated HTML is published. Running a GPL program over your own input does not place its licence on the output, and libtmux does not distribute Doxygen or any modified version of it. ## Reference hosting Three ports deep-link to the canonical host their ecosystem already uses, rather than duplicating it here: - Rust: [docs.rs](https://docs.rs/libtmux) - Go: [pkg.go.dev](https://pkg.go.dev/github.com/libtmux/libtmux-go/tmux) - Java and Kotlin: [javadoc.io](https://javadoc.io/doc/io.github.libtmux/libtmux) Those sites are operated independently of this project and carry their own terms. ## libtmux itself Each port is MIT licensed. See the `LICENSE` file in that port's repository for the authoritative text. --- # Exit codes and errors Source: https://libtmux.org/en/swift/latest/workspace/reference/exit-codes/ > Observed Python exit behavior and the proposed native error contract. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../compatibility/) describes this port's implemented coverage. Python tmuxp uses 0 for success, 1 for general failures, and 2 for argument usage errors. Those categories do not guarantee every current command propagates every underlying error. ## Current exceptions `edit` ignores the editor child's status. Machine `search` can return normally with no output for an invalid regular expression, and with human help for a missing query. Both import children exit 2 when their source argument is missing. See their command pages before treating status alone as proof of success. The root entry point checks for a supported tmux executable before parsing arguments, including help. Missing or unsupported tmux can print a diagnostic and exit with status 0. `tmuxp freeze` also catches a missing-session error, prints it, and returns normally. These are current implementation quirks. ## Proposed native behavior Parse errors return 2 before backend work. Input validation or execution failures return 1. A partial operation includes completed work and the failed stage in its result and returns 1; it must not claim rollback unless rollback occurred. An interruption returns 130 after owned streams and handles are cleaned up, without killing unrelated tmux sessions. Machine errors on stderr are compact JSON objects, one per line. Parse or validation failures before work leave stdout empty. Human errors stay readable text. Stable error codes complement readable messages; native exception objects and stack traces are not the public JSON schema. ## Machine error codes In `--json` and `--ndjson` mode an error record on stderr is `{"schema_version": 1, "code": ..., "message": ...}`, and every entry of a load summary's `errors` array carries the same `code`. The seven native ports report the same code for the same condition: | Condition | Code | | --- | --- | | Workspace file or name not found | `workspace_not_found` | | Malformed document, wrong type, or invalid value | `invalid_workspace` | | An unknown or unsupported key refused | `unsupported_key` | | tmux missing, or its server unreachable | `tmux_unavailable` | | A tmux command failed while building | `tmux_failed` | | `before_script` exited nonzero, or could not start | `script_failed` | | A target session does not exist | `session_not_found` | | A destination exists without `--force` | `destination_exists` | | A confirmation is needed but impossible | `confirmation_required` | | Interrupted by a signal | `interrupted` | | Arguments or mode misused | `usage`, with status 2 | Other conditions keep port-specific codes, in lower snake_case. Match on the code, not the message. See [output](../output/), [automation](../../guides/automation/), and [troubleshooting](../../guides/troubleshooting/). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # JSON, NDJSON, and semantic color Source: https://libtmux.org/en/swift/latest/workspace/reference/output/ > Python output and the native CLI stream and color contract. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../compatibility/) describes this port's implemented coverage. **Native CLI development contract.** The local `workspace-cli` worktrees provide JSON and NDJSON output. The schema below is the shared target; implemented command coverage remains port-specific. Python tmuxp provides JSON and NDJSON on `ls` and `search`, and JSON on `debug-info`. ## Format selection The shared contract accepts `--json` and `--ndjson` before or after every leaf command. NDJSON wins when both are present. A saved workspace's encoding is separate: `freeze -f yaml` describes the file, while `--json` describes the CLI result stream. Machine output has no ANSI styling, prompts, spinner frames, or raw child output. ## Native Swift child output The native Swift command streams `before_script` and captured `shell -c` output while the child runs. Human bootstrap output keeps its original stdout or stderr destination. Human shell output also preserves line breaks and tabs, escaping other terminal controls. In JSON and NDJSON modes, child chunks are warning records on **stderr** with codes `bootstrap_stdout`, `bootstrap_stderr`, `shell_stdout` or `shell_stderr`. `--log-level error` and `critical` hide those warnings. Load events and results stay on stdout; the final shell result retains complete captured stdout/stderr and the child exit status even when warnings are hidden. Each child stream is limited to one MiB. UTF-8 characters split across reads are retained until complete; invalid bytes use replacement characters. Output follows sink backpressure without replaying previous chunks. Overflow, failed writes and cancellation terminate the captured process group; a closed reader is detected on the next pending write. On cancellation, load attempts one terminal failure result and fatal diagnostic with bounded writes. Writable stdout receives the terminal result; blocked or closed output cannot guarantee delivery. A diagnostic interrupted on blocked stderr may end mid-record. SIGINT and SIGTERM return status 130, and append failures preserve the borrowed session. ## Machine output The document-to-stdout behaviors below are new machine-mode extensions. In the pinned reference, freeze always saves a file and quiet only suppresses status text. Separate the workspace file's encoding from the CLI stream's encoding. `freeze -f json` selects the document format. `--json` selects structured CLI output. Never append a status line to raw YAML or JSON document output. | Command | JSON stdout | NDJSON stdout | | --- | --- | --- | | `ls` | Object containing `workspaces` and `global_workspace_dirs`, retaining tmuxp's existing record fields. Empty discovery yields the same object with an empty array. | One workspace record per line, matching tmuxp. Empty discovery emits zero records. Directory diagnostics stay on stderr. | | `search` | Array of result records with `"name"`, `"path"`, `"session_name"`, `"source"`, `matched_fields`, `"matches"`. Empty results yield `[]`. | One result record per line. Empty results emit zero records. | | `debug-info` | One diagnostics object. Retain home masking for named path fields, define redaction for raw tmux values, and add port/runtime details under named fields. | One compact diagnostics object plus newline. | | `tmuxp load` | Versioned operation summary: command, status, results, errors and completed/failed stages. | Ordered operation events followed by exactly one terminal result. | | `tmuxp freeze` | The workspace document as a JSON object when writing stdout; if saving to a file, a versioned save result containing destination, format and recoverability warnings. | One versioned capture/save result per line, with a `"workspace"` object when returning the document. | | `convert` and importer leaves | The converted document as JSON when writing stdout; a versioned save result when an explicit destination is supplied. | One versioned conversion/save result with a nested document when returning it. | | `edit` | One versioned result after the editor exits, including selected file and child status. | One terminal edit result; interactive editor display uses the terminal rather than machine stdout. | | `"shell"` | For `-c`, one result containing captured Python stdout/stderr and child status. Native Swift machine calls require `-c`. | Native Swift writes one terminal result to stdout; live child warnings use stderr as described above. | Keep the established read-command JSON shapes rather than forcing a new universal envelope around existing pipelines. New operation envelopes use integer `schema_version: 1`. Search's empty array is a deliberate correction to tmuxp 1.74.0's empty byte stream. No-pattern machine search must return usage status 2, empty stdout and a structured stderr diagnostic. Explicit `--help` remains a documented human-help request. Invalid patterns must return usage status 2 and a diagnostic; tmuxp's current JSON search can silently return no output for an invalid regex. A new load summary has `schema_version`, `"command"`, `"status"` (`ok`, `partial` or `"error"`), `results` and `errors`. Each result identifies its workspace input and created/reused session; IDs are strings because tmux uses prefixes such as `$`, `@` and `%`. Errors include a stable `code`, a readable `message`, the input index and any completed/failed stage. Do not serialize native exception objects, language-specific field capitalization or unserializable handles. Every result names `input`, `input_index`, `session_id`, `session_name` and `reused`; ports add their own fields around those. An input that failed keeps its result record beside its `errors` entry, so a reader can tell which input failed and what became of its session. `status` is `ok` when every input completed, `partial` when some input completed or a failed one left effects behind, such as a borrowed or appended session, and `error` when nothing completed and nothing was retained. NDJSON operation events have `schema_version`, `"command"`, `event`, a monotonically increasing `"sequence"`, and event-specific data. The initial vocabulary is `started`, `workspace-started`, `session-created`, `window-created`, `pane-created`, `script-output`, `warning`, `workspace-completed`, `"failed"`, `"completed"`. Emit `"completed"` or `"failed"` once per invocation. Include operation/input identifiers where several files are involved. `workspace-started` names the `input` and `input_index` it belongs to, and `session-created` follows it directly, before any `window-created`, so a reader knows the session a window belongs to as soon as the windows arrive. Flush records as events arrive; buffering the whole run and splitting a JSON array into lines is not streaming. Drain child stdout and stderr concurrently to avoid pipe deadlocks. In machine mode, script text belongs inside escaped JSON strings; it must never be written directly to stdout. Apply backpressure, cap retained output and expose truncation explicitly. Line breaks, tabs, ANSI bytes, Unicode and arbitrary workspace names must remain valid encoded data. Binary output needs an explicit byte encoding or a documented replacement policy. Machine stderr contains one compact diagnostic JSON object per line. Parse/validation failures before work leave stdout empty and return 2 or 1 respectively. A partial load emits a partial/failure result describing completed work and returns 1; do not claim rollback unless it occurred. Human-mode diagnostics remain readable text. Interruptions should stop scheduling new work, drain or close owned streams, release owned process handles, and return 130 without killing unrelated tmux sessions. ## Prompts and file writes Machine mode resolves choices from arguments and never treats missing input as yes. Detached loading avoids mixing terminal attachment with JSON. Interactive editor and REPL display require a separate controlling terminal; otherwise the command rejects the request before execution. The native contract defines explicit save, format, and overwrite controls for conversion/import automation. With no destination, machine conversion returns the document and writes no guessed file. Existing files require explicit overwrite authorization, including explicit freeze destinations. These rules differ from the Python overwrite behavior documented by [freeze](../../cli/freeze/) and [convert](../../cli/convert/). ## Semantic color Use roles at the point that a domain value is rendered. Formatting an entire line with one success color loses the structure the requested style should communicate. Compose a status, subject, identifiers, paths, counts and hints separately, and reset styling after every token. | Role | Default tmuxp-aligned style | Typical values | | --- | --- | --- | | Heading | Bold bright cyan | Command and section headings | | Primary subject | Bold magenta | Workspace/session name, selected window | | Information | Cyan | Paths, targets, useful values | | Success | Green | Created, loaded, saved | | Warning | Yellow | Partial support, lossy capture, retained objects | | Error | Red | Failed operation, invalid field | | Secondary text | Blue or dim text, verified against terminal contrast | Sizes, timestamps, source labels, hints | | Command syntax | Distinct option/argument roles from the same theme | Flags, metavariables, examples | Copy tmuxp's policy explicitly: nonempty `NO_COLOR` disables; explicit never disables; explicit always enables; nonempty `FORCE_COLOR` enables auto; otherwise use the destination stream's terminal capability. To support the supplied reports' extra variables, add `CLICOLOR_FORCE` and `CLICOLOR` below those explicit/reference choices. `CLICOLOR_FORCE=0` does not force. Machine mode takes precedence over all color choices, including forced color. Measure layout using visible terminal width, not byte length or ANSI-bearing string length. Exercise narrow terminals, wrapped paths, Unicode and redirected output. Keep status words and labels even when color is enabled, so meaning survives monochrome output. Progress updates belong on stderr, animate only on a terminal, and become discrete records in NDJSON mode. Honor the reference progress presets, custom tokens and panel-line rules. ## Related reference See [exit codes](../exit-codes/), [command flags](../../cli/), and [environment](../../configuration/environment/). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp compatibility and port status Source: https://libtmux.org/en/swift/latest/workspace/reference/compatibility/ > Current local CLI capabilities, remaining gaps, and the historical builder audit. **Local implementation, unpublished.** The Swift `tmux-workspace` CLI is available in the `workspace-cli` source worktree. Its command and configuration coverage remains partial. See [installation](../../guides/installation/) for local setup; installing a published library does not establish availability of this CLI. Compatibility targets useful tmuxp 1.74.0 commands and workspace workflows, with native validation, execution and output conventions. Matching command names does not promise identical runtime behavior or Python semantics. ## This port Native services cover load, append, capture, discovery, search, conversion, both importers, editor execution and diagnostics. Loading creates sessions or reuses exact names. Append authenticates the inherited server PID, selected endpoint and current pane; failure preserves the borrowed session and reports new windows and potentially changed settings. A failed new build removes only its created session and reports earlier successful workspaces. Loading supports pane shorthand, inherited commands, sequential enter settings, history suppression, directories, layouts, indexes, focus, session environment and session/window options. Before-scripts run direct argv after session creation, from the expanded session directory or invocation directory. All input documents are validated before creation, including expanded session names and NUL rejection. Unsupported execution keys fail explicitly. Outside tmux, load currently requires an explicit `-S` or `-L` endpoint. Append uses new slots and ignores configured indexes. Loading checks layouts across all inputs before setup scripts or topology changes. Named layouts accept unique abbreviations supported by the running daemon; only a cold endpoint falls back to the selected client's version. Serialized layouts have bounded checksum, tree and pane-count validation. Tmux remains responsible for geometry correction and pruning. Human load with a foreground terminal attaches to the final workspace. Inside tmux, a prompt offers switching, detached loading, appending or cancellation. `-y` skips that mode prompt but refuses an ambiguous client selection. All inputs are validated before prompting. Redirected and machine calls require `-d` or `--append` and never prompt. Detaching or interrupting the attached client preserves loaded workspaces and restores terminal settings. Switching checks the selected server, pane terminal and client's current PID, session and globally active pane. A client with independent `active-pane` focus on the invoking pane's physical window prevents switching; detached and append choices remain available. Independent clients on other windows do not block switching. If the selected client gains the flag before handoff, the switch fails and the loaded workspace remains. A tmux client name can be reused after that check; outside attachment likewise checks the server before starting the terminal client. These checks do not make the separate attachment process atomic with a server restart or client replacement. `load -2` forces 256-color handling in direct and control clients, together with any explicit tmux configuration file. Legacy `-8` is recognized but rejected before document lookup or backend access; supplying both flags is a usage error. Without `-2`, tmux detects color support. YAML requires the `YAMLWorkspaces` build trait. Conversion preserves extension fields, and both importers require an explicit source. Import warns about untranslated fields, and rejects unexpanded ERB in a Tmuxinator source, which Ruby would have expanded before parsing. Teamocil evaluates no templates, so the same text in a Teamocil source is preserved literally. Optional saves publish files atomically; `--force` permits replacement. Capture records current commands, directories, window names, indexes, focus, layouts, local session/window options and session environment values. It cannot recover original arguments, scripts or plugin intent. Removed environment entries and global options are omitted. Values are captured literally; loading a capture applies normal tmuxp-compatible variable expansion. Search uses native ICU regular expressions without Python. Capture selects an explicit session name or ID, then authenticated tmux context, then the only live session. Ambiguous human calls offer a choice; machine calls require an explicit target. A `.json` destination selects JSON unless `-f` overrides it; other destinations default to YAML. `--quiet` suppresses human status and warnings while preserving machine results and required errors. Every command accepts `--json` and `--ndjson`; NDJSON takes precedence. Listing and search decode documents on demand; load emits sequenced events and one terminal result while its output stream remains writable. Human listing uses semantic colors and escapes terminal controls. Editor argv is passed directly; machine editor calls return bounded captures in their result. Bootstrap and captured shell calls also stream decoded text as structured stderr warnings. The [child output contract](../output/#native-swift-child-output) describes stream limits, filtering and cancellation. `--log-level` filters advisory diagnostics at debug, info, warning, error or critical severity. Fatal operation errors remain visible. `load --log-file` opens a regular append file before backend work and writes structured lifecycle and diagnostic records at the selected level. New files use owner-only permissions. A later log-write error reports a secondary diagnostic without replacing the operation result. Human load displays event-driven progress on terminal stderr. `--progress-format` accepts five presets or a custom counter template; `TMUXP_PROGRESS_FORMAT` supplies the default. `--progress-lines` sets the recent bootstrap-output panel: 3 rows by default, 0 hides it and -1 uses the initial terminal height. `TMUXP_PROGRESS_LINES` supplies its default. The panel retains at most 65,536 UTF-8 bytes and clips Unicode conservatively. It samples terminal size once and does not track resizing. Bootstrap output streams while the script runs, preserving its original stdout or stderr destination. `--no-progress`, `TMUXP_PROGRESS=0`, `TERM=dumb`, redirected stderr and machine output disable the display. NDJSON receives discrete window/pane events. Completion and interruption clear the panel. SIGINT and SIGTERM return status 130 and terminate captured children. Python shell execution checks tmuxp 1.74.0 through `TMUX_WORKSPACE_PYTHON`, defaulting to `python3`. Machine shell calls require `-c`; interactive backends require a terminal. Shell completion can be generated from ArgumentParser. The locally installed Linux executable still requires Swift runtime libraries. The [native source reference](https://github.com/libtmux/libtmux-swift/blob/528c5a8f5a4a699b7742a0c38e5075f386e11138/Sources/TmuxWorkspaceCLI/README.md) describes this implementation. Use the native executable's `--help` for the options implemented in that checkout. ### Remaining gaps - Window/pane environment, shell overrides, command timing/readiness, plugins and custom builders are not implemented in native loading. - Optional interactive Python backends, editor job control and broader descendant cleanup need further testing. - Full capture/configuration coverage, generated manuals, Darwin validation and portable distribution remain open. Building without the YAML trait does not provide YAML commands. ## Historical builder audit Audit date: 2026-09-09. [Native source snapshot](https://github.com/libtmux/libtmux-swift/tree/94b9e4cc436dda8e18e064179ae7d26e55bbbd73). The following results describe that original library revision, before the local CLI implementation. They are historical evidence, not its current capability list or a support guarantee for a published artifact. With optional YAML decoding enabled, the library parsed 23 upstream examples in the original audit but could silently omit unsupported keys. The first-pane explicit shell override was missing. YAML was absent from the default build, and the source snapshot had no native CLI. At that baseline, `TmuxWorkspace` supported JSON and optional YAML through the `YAMLWorkspaces` trait. Its model covered names, directories, layouts and commands with narrower shapes than tmuxp and could ignore unknown keys. Those library decoder results do not describe current CLI execution preflight. Read the port's [builder topics](../../internals/topics/) and [API](../) for its library interface. Use the language switcher to compare the same topic across ports; each port has its own coverage limits. ## Shared gaps Parser acceptance does not prove execution support. Native regex engines and Python plugin runtimes have different contracts; identical flags alone do not establish compatibility. See [shell](../../cli/shell/), [search](../../cli/search/) and [hooks](../../configuration/hooks/), and apply this port's current limitations above when reading those reference pages. ## Optional format separator Python libtmux exposes `LIBTMUX_TMUX_FORMAT_SEPARATOR` in its format collector. This native CLI does not claim that setting as a supported codec control. Its framing and decoding need their own compatible seam and collision, empty value, Unicode and line-break checks before accepting such a setting. ## Reading examples The [gallery](../../examples/gallery/) contains the upstream fixture corpus. Parsing a fixture and executing its applications are separate checks. Several require external programs, remote hosts, project directories or plugin packages. A successful YAML read does not establish those dependencies or the complete workspace behavior. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # MCP for Swift Source: https://libtmux.org/en/swift/latest/mcp/ > Run the Swift MCP executable or embed its typed tool service with explicit authority. `libtmux-mcp` is the Swift stdio executable. [`LibTmuxMCP`](https://github.com/libtmux/libtmux-swift/blob/254f8b2be7eb60cacc3ffcb3ea8e456784f582df/Package.swift) is the SwiftPM library product for embedding the same tools. They expose tmux discovery, captures, waits, input, configuration, and process control. The package requires Swift 6.2 or newer and supports Linux and macOS. The documented Darwin dependency build uses Xcode's Swift 6.3 toolchain. tmux is required at runtime. ## Start here - [Install](#install) points an MCP client at this server. - [Tools](./tools/) lists the MCP operations, arguments, and results. - [Guides](./guides/) build the executable and configure its environment. - [Topics](./topics/) explain toolsets, command waits, and capability discovery. - [Examples](./examples/) call a tool, then explore server internals. - [Language API](./reference/) documents embedding and implementation types. The executable defaults to inspection, management, and execution; a verified dedicated daemon can also receive teardown tools. The embedding initializer defaults to inspection alone. Read `tmux://capabilities` for the startup selection. [Workspace Manager](../workspace/) is a separate library product. The current MCP catalog does not include a workspace-file operation. [Library contract](https://github.com/libtmux/libtmux-swift/blob/254f8b2be7eb60cacc3ffcb3ea8e456784f582df/Sources/LibTmuxMCP/README.md) and [platform requirements](https://github.com/libtmux/libtmux-swift/blob/254f8b2be7eb60cacc3ffcb3ea8e456784f582df/README.md#requirements). --- # Swift MCP topics Source: https://libtmux.org/en/swift/latest/mcp/topics/ > Select toolsets, inspect the pinned endpoint, and observe bounded commands. The server selects one tmux endpoint and freezes its offered tools at startup. Read `tmux://capabilities` to inspect that endpoint's provenance and the effective tool selection. ## Select tools `LIBTMUX_TOOLSETS` selects any combination of `inspect`, `manage`, `execute`, and `teardown`. `LIBTMUX_TOOLS` adds exact names; `LIBTMUX_EXCLUDE_TOOLS` removes names last. Unknown names and malformed lists fail startup. Use `inspect` for discovery and terminal reads. Add `manage` for topology changes and `execute` for input and process creation. Select `teardown` explicitly when removal is needed on an existing or explicitly selected server. A default dedicated daemon can receive teardown tools when the launcher verifies its own minimal-configuration provenance. Tool selection shapes the callable interface. Execute tools act with the tmux user's authority; selecting a socket does not confine shell effects. ## Observe a command Use [`run_shell_command`](../tools/run_shell_command/) for a bounded command and its exit status. A deadline ends the wait; the pane command may still be running. Inspect it before submitting another command. Use [`capture_since`](../tools/capture_since/) to collect subsequent output and [`wait_for_text`](../tools/wait_for_text/) for an expected terminal condition. Their schemas and result limits are in the [tool reference](../tools/). The current catalog has no detached job-handle API. ## Resources and prompts The server exposes the static `tmux://capabilities` resource. Read live hierarchy and terminal state through tools. The current surface has no workflow prompts or dynamic resource templates. [Configuration and lifecycle contract](https://github.com/libtmux/libtmux-swift/blob/254f8b2be7eb60cacc3ffcb3ea8e456784f582df/Sources/libtmux-mcp/README.md). --- # Connect a Swift MCP client Source: https://libtmux.org/en/swift/latest/mcp/guides/ > Build libtmux-mcp and select its tmux endpoint and tool authority through environment variables. Build the Swift executable and have the MCP client launch it. Use the package's supported Swift toolchain and make tmux available to the client process. ## Build and launch From the Swift repository: ```console $ swift build --product libtmux-mcp ``` The debug executable is written under `.build/debug`. Run an inspection surface on a named socket: ```console $ LIBTMUX_SOCKET=docs-agent LIBTMUX_TOOLSETS=inspect \ .build/debug/libtmux-mcp ``` It waits for MCP messages. The executable accepts no flags; configure its endpoint through the environment. ## Connect a client If the executable is on the client's `PATH`, use this `mcpServers` entry. Otherwise set `command` to its actual built location. ```json { "mcpServers": { "tmux-swift": { "command": "libtmux-mcp", "env": { "LIBTMUX_SOCKET": "docs-agent", "LIBTMUX_TOOLSETS": "inspect" } } } } ``` `LIBTMUX_SOCKET_PATH` and `LIBTMUX_SOCKET` are mutually exclusive. `LIBTMUX_TMUX_BIN` selects the tmux executable. ## Verify and narrow the surface Ask the client to list sessions and read `tmux://capabilities`. Retain opaque references from listings for follow-up calls. To allow only listing and window creation, set `LIBTMUX_TOOLSETS` to an empty string and `LIBTMUX_TOOLS=list_sessions,create_window`. Reconnect after changing environment variables. Invalid tool selections fail startup; inspect stderr diagnostics. A stale pane reference needs a fresh listing, not another spelling of the same raw ID. [Executable contract](https://github.com/libtmux/libtmux-swift/blob/254f8b2be7eb60cacc3ffcb3ea8e456784f582df/Sources/libtmux-mcp/README.md). --- # Swift MCP examples Source: https://libtmux.org/en/swift/latest/mcp/examples/ > List sessions through the Swift MCP server and inspect implementation examples. Connect the server using the [setup guide](../guides/), then call [`list_sessions`](../tools/list_sessions/) from your MCP client. ## List sessions This is the `params` object for an MCP `tools/call` request. Send it through the connected client: ```json { "name": "list_sessions", "arguments": {} } ``` Use the returned session IDs when choosing a window or pane. The [tool reference](../tools/list_sessions/) describes this port's result and optional arguments. ## Internals The following examples are for applications that embed or extend the server. Installing and connecting an MCP client does not require this code. The Swift example package uses the public [`LibTmuxMCP`](https://github.com/libtmux/libtmux-swift/blob/254f8b2be7eb60cacc3ffcb3ea8e456784f582df/Package.swift) product directly. It demonstrates the default readonly surface and an exact typed selection without launching a stdio subprocess. ### Embed the tool surface ```swift file="Examples/Sources/ExampleCode/MCPEmbedding.swift" import LibTmux import LibTmuxMCP public func useEmbeddedTools(on server: Server) async throws(ToolError) -> Int { let tools = TmuxTools(server: server) for definition in tools.visibleDefinitions { print(definition.name, definition.summary) } let result = try await tools.call(ToolCall(name: "list_panes")) return result.structured["panes"]?.arrayValue?.count ?? 0 } public func useExactEmbeddedTools(on server: Server) -> TmuxTools { let authority = ToolAuthority( toolsets: [], includedTools: ["create_window", "list_sessions"] ) let tools = TmuxTools(server: server, authority: authority) return tools } ``` The first function lists visible definitions and calls `list_panes`. The caller supplies the existing `Server` and retains its lifetime. The second function starts with no toolsets and includes only its named operations. It does not include future tools automatically. ### Run its tests From the Swift repository with its toolchain and tmux installed: ```console $ swift test \ --package-path Examples \ --filter MCPEmbeddingTests ``` The [example tests](https://github.com/libtmux/libtmux-swift/blob/254f8b2be7eb60cacc3ffcb3ea8e456784f582df/Examples/Tests/ExampleTests/MCPEmbeddingTests.swift) use a tmux fixture to verify the pane count and exact offered names. Rendering the source does not run that test. The [example source](https://github.com/libtmux/libtmux-swift/blob/254f8b2be7eb60cacc3ffcb3ea8e456784f582df/Examples/Sources/ExampleCode/MCPEmbedding.swift) is also the basis for the library's embedding documentation. Use [Workspace builder examples](../../workspace/internals/examples/) for direct workspace construction; the MCP catalog has no workspace-file operation. --- # Swift MCP API Source: https://libtmux.org/en/swift/latest/mcp/reference/ > Find Swift embedding types, typed tool authority, and the separate protocol catalog. For MCP client requests, use the [tool reference](../tools/). This page covers language APIs for embedding or extending the server. [`LibTmuxMCP`](https://github.com/libtmux/libtmux-swift/blob/254f8b2be7eb60cacc3ffcb3ea8e456784f582df/Package.swift) is a SwiftPM library product. `libtmux-mcp` wraps it in a stdio executable. Public Swift types and MCP wire operations have separate names and responsibilities. ## Swift embedding API `TmuxTools(server:)` constructs a readonly tool surface. `ToolAuthority` selects `Toolset` groups and named tool inclusions or exclusions. `visibleDefinitions` reports the selection; `call(ToolCall)` invokes an operation and returns structured data or throws `ToolError`. `MCPRequestHandler` and `MCPService` provide protocol composition. `ServerConfiguration` parses the executable's environment and builds its libtmux endpoint. [Embedding contract](https://github.com/libtmux/libtmux-swift/blob/254f8b2be7eb60cacc3ffcb3ea8e456784f582df/Sources/LibTmuxMCP/README.md) and [configuration source](https://github.com/libtmux/libtmux-swift/blob/254f8b2be7eb60cacc3ffcb3ea8e456784f582df/Sources/LibTmuxMCP/Configuration.swift). ## MCP contract The [tool reference](../tools/) describes the wire catalog and schemas. Follow-up calls use opaque references returned by hierarchy discovery. The static `tmux://capabilities` resource reports the startup-frozen surface. Live state comes from inspect tools. The current surface has no workflow prompts or dynamic resource templates. The [Workspace builder API](../../workspace/reference/) owns workspace decoding and construction; those operations are separate from the MCP catalog. ## API declarations - [CallerIdentity](https://libtmux.org/en/swift/latest/mcp/reference/calleridentity/) - [InputSink](https://libtmux.org/en/swift/latest/mcp/reference/inputsink/) - [JSONValue](https://libtmux.org/en/swift/latest/mcp/reference/jsonvalue/) - [MCPRequestHandler](https://libtmux.org/en/swift/latest/mcp/reference/mcprequesthandler/) - [MCPService](https://libtmux.org/en/swift/latest/mcp/reference/mcpservice/) - [OutputClass](https://libtmux.org/en/swift/latest/mcp/reference/outputclass/) - [ProcessReach](https://libtmux.org/en/swift/latest/mcp/reference/processreach/) - [ProgressReporter](https://libtmux.org/en/swift/latest/mcp/reference/progressreporter/) - [ServerConfiguration](https://libtmux.org/en/swift/latest/mcp/reference/serverconfiguration/) - [ServerProvenance](https://libtmux.org/en/swift/latest/mcp/reference/serverprovenance/) - [TmuxEffect](https://libtmux.org/en/swift/latest/mcp/reference/tmuxeffect/) - [TmuxFormatControl](https://libtmux.org/en/swift/latest/mcp/reference/tmuxformatcontrol/) - [TmuxTools](https://libtmux.org/en/swift/latest/mcp/reference/tmuxtools/) - [ToolAnnotations](https://libtmux.org/en/swift/latest/mcp/reference/toolannotations/) - [ToolArgument](https://libtmux.org/en/swift/latest/mcp/reference/toolargument/) - [ToolAuthority](https://libtmux.org/en/swift/latest/mcp/reference/toolauthority/) - [ToolCall](https://libtmux.org/en/swift/latest/mcp/reference/toolcall/) - [ToolDefinition](https://libtmux.org/en/swift/latest/mcp/reference/tooldefinition/) - [ToolError](https://libtmux.org/en/swift/latest/mcp/reference/toolerror/) - [ToolOperation](https://libtmux.org/en/swift/latest/mcp/reference/tooloperation/) - [ToolOutcome](https://libtmux.org/en/swift/latest/mcp/reference/tooloutcome/) - [Toolset](https://libtmux.org/en/swift/latest/mcp/reference/toolset/) [Protocol catalog](https://libtmux.org/en/swift/latest/mcp/tools.json) --- # Workspace Manager for Swift (in development) Source: https://libtmux.org/en/swift/latest/workspace/ > Build the local Swift tmux-workspace CLI; implementation coverage remains partial and unreleased. **Workspace Manager for Swift is in development.** The local `workspace-cli` checkout contains a `tmux-workspace` CLI with native services. This implementation is partial and unreleased; published library packages do not provide this local CLI checkpoint. Native services discover, search, convert, import, edit, load, append and capture workspaces. Common directories, command inheritance, indexes, focus, session environment/options and before scripts are supported. Python shell evaluation uses a checked tmuxp bridge. `load -2` forces 256-color handling in native tmux clients. Legacy `-8` is recognized but rejected before document lookup because supported tmux versions do not implement 88-color mode. Without `-2`, tmux detects color support. ## Load a workspace from the terminal Follow the [local installation walkthrough](./guides/installation/) from a `workspace-cli` checkout of the [Swift repository](https://github.com/libtmux/libtmux-swift). It builds the native command and loads a small workspace on a private socket. After building, inspect the command without starting tmux: ```console $ .build/debug/tmux-workspace --help ``` Use detached load for the walkthrough. JSON and NDJSON output are available; choose the mode explicitly when scripting. The command/configuration reference below also documents tmuxp behavior and compatibility targets, so it is not a claim that every referenced feature works in this local implementation. ## Current coverage `--log-level` filters advisory diagnostics; fatal errors remain visible. `load --log-file` appends structured lifecycle and diagnostic records to a regular file. A write failure reports a secondary diagnostic and preserves the load result. Human load displays progress on terminal stderr, with presets, custom counters and a bounded recent-output panel. It uses the initial terminal size and conservative Unicode clipping. Bootstrap output streams to its original stdout or stderr destination and updates the panel while the script runs. Machine output disables the panel and sends child text as structured stderr warnings; load events remain on stdout. See the [child output contract](./reference/output/#native-swift-child-output) for limits and cancellation behavior. Human load with a foreground terminal attaches to the final workspace. Inside tmux, choose switch, detached load, append or cancel. `-y` skips the mode prompt and refuses an ambiguous client selection. Redirected and machine calls require `-d` or `--append`. Detaching or interrupting the client preserves loaded sessions. Plugins/custom builders, further pane/window execution settings, fuller capture, generated manuals and portable distribution remain unfinished. Parser coverage does not establish support for every configuration field or execution path. For the released Python workflow, use [tmuxp](https://tmuxp.git-pull.com/) and its [Python workspace guide](/py/latest/workspace/guides/). It is a separate application and remains useful when a required native feature is incomplete. ## Start here The `TmuxWorkspace` SwiftPM library product remains available for applications that build sessions through code. [Internals](./internals/) documents that API: - [Guides](./internals/guides/) show application setup and builder calls. - [Topics](./internals/topics/) explain supported configuration and behavior. - [Examples](./internals/examples/) exercise the library or source consumer. - [API](./reference/) covers the builder and configuration interfaces. ## tmuxp command and configuration reference Use the local CLI's help and the limits above when applying these compatibility references to native execution. - [Installation walkthrough](./guides/installation/) builds and runs the local native CLI. - [Inspect through MCP](./guides/inspect-with-mcp/) connects to the loaded session. - [Command reference](./cli/) lists tmuxp commands, flags and compatibility targets. - [Configuration](./configuration/) covers fields, normalization and execution. - [Example gallery](./examples/gallery/) includes upstream fixtures and prerequisites. - [Compatibility status](./reference/compatibility/) records builder/reference gaps. - [JSON, NDJSON, and color](./reference/output/) describes the shared output design. --- # Topics Source: https://libtmux.org/en/swift/latest/topics/ > Object traversal, cleanup, pane I/O, configuration, and failure handling. Use these pages for object traversal, cleanup, pane I/O, configuration, and failure handling. [Concepts](/concepts/) introduces the shared object model. The concept guides also cover [control mode vs one-shot](/concepts/transports/), [filtering and queries](/concepts/queries/), and [workspaces](/concepts/workspaces/). Choose a topic below for more detailed behavior: - **[Architecture](architecture/)**: locate operations and field definitions in each port's source. - **[Traversal](traversal/)**: navigate related objects, test membership, and compare identity. - **[Context managers](context-managers/)**: manage cleanup on block exit and identify objects that need an explicit kill. - **[Pane interaction](pane-interaction/)**: choose input modes, capture ranges, and completion waits. - **[Options and hooks](options-and-hooks/)**: configure tmux and register event commands at a supported scope. - **[Format-token fields](format-tokens/)**: read typed state and handle fields absent from a scope, version, or capture. - **[Waiting and retrying](waiting-and-retry/)**: wait on a condition or a named tmux signal. - **[Environment](environment/)**: locate objects from process variables and configure values inherited by new panes. - **[Socket and servers](socket-and-servers/)**: select a server, check liveness, and detect a replacement daemon. - **[Errors and exceptions](errors-and-exceptions/)**: handle command failures and determine whether a mutation can be retried. Use your port's API reference for signatures and defaults. These pages call out differences that affect how you use the APIs. --- # Architecture Source: https://libtmux.org/en/swift/latest/topics/architecture/ > The object hierarchy underneath the API you call, and who actually holds the behavior in each port. This page describes how the language ports organize their code and where operations live. For the object hierarchy and stable IDs, start with [Server, session, window, pane](/concepts/server-session-window-pane/). ## Where behavior lives: on the object, or through the server Python, TypeScript, Go, Rust, Java, .NET, and C++ provide operations on session, window, and pane objects. Each object carries its ID and server context. These examples send input, set an option, and kill a pane: Swift's `Session`, `Window`, and `Pane` are `Sendable` value types holding IDs and state fields. [Format-token fields](../format-tokens/) lists their fields. Perform operations through `Server`, passing the target value: ```swift try await server.sendKeys(["echo hi", "Enter"], to: pane) try await server.setOption("automatic-rename", to: "off", scope: .window(window)) try await server.kill(pane) ``` The practical effect is that `Server` is the one thing you hold onto in a Swift program; a `Session` or `Pane` you got back from a `snapshot()` is inert data you hand back to the server that produced it, not a handle you call things on. Every other port's `Server` is also where you start, but `Session`/`Window`/`Pane` stay live actors once you have one. ## A generated data table under a hand-written surface Ports translate object IDs into tmux targets (`-t`) and read state through tmux's `FORMATS` variables (`#{...}`). Their field definitions use generated catalogs, fixed field sets, or captured dictionaries: | Port | Generated table | Hand-written surface | |------|-------------------|----------------------| | Python | `libtmux.constants` (`FORMATS`, gated by scope and tmux version) | dataclass fields on `Obj` (`libtmux.neo`), `None` when a gate excludes a token | | TypeScript | `packages/libtmux/src/_generated/format_fields.ts` (`{ scope, since, token }` per row) | camelCase aliases on `Pane`/`Session`/`Window` (`packages/libtmux/src/_generated/field_aliases.ts`) | | Go | `format_generated.go`, `option_generated.go` (built by `internal/generate/formats`) | `(value, bool)` accessor methods: Go's own "comma ok" idiom for a gate | | Rust | `formats.rs`'s per-token macro rows (`token, wire name, scope, kind, since version, absent-handling`) | typed methods returning `Option` | | Java | (typed field accessors generated for the query layer: see `Pane_`/`Session_` in [Filtering and queries](/concepts/queries/)) | `Optional` for fields introduced after a port's tmux floor | | C++, Swift | fixed field sets; see below | a fixed, curated set of non-optional struct/class fields | | .NET | a snapshot dictionary read at capture time | typed properties that throw `IncompleteSnapshotException` for a field the capture didn't request, rather than gating on tmux version per field | Swift and C++ expose fixed sets of state fields. Swift includes indices, dimensions, active state, command, path, and edge flags. C++ declares its fields in `kFields` arrays and uses `pane->expand("#{...}")` for other tokens. See [Format-token fields](../format-tokens/) for optional fields and tokens outside the fixed sets. ## Module layout, by port Each port's own top-level organization, to orient yourself before opening its source: - **Python**: one module per tier (`libtmux.server`, `.session`, `.window`, `.pane`, `.client`), plus `libtmux.common` for shared plumbing, `libtmux.neo` for the dataclass query layer, `libtmux.options` / `libtmux.hooks` as mixins every tier includes, and `libtmux.exc` for the exception hierarchy. - **TypeScript**: `packages/libtmux/src/{server,session,window,pane,client}.ts` hold the public classes; nearly everything they call into lives under `_internal/operations/` (one file per concern: `pane_io.ts`, `hooks.ts`, `options.ts`, `topology.ts`) and `_generated/` (the format/option/hook catalogs above). Separate packages in the same monorepo cover workspaces (`@libtmux/workspace`) and an MCP server. - **Go**: a single `tmux` package, split by concern into many files rather than many packages (`model.go` for the core structs, `lifecycle_kill.go`, `pane_capture.go`, `pane_geometry.go`, `hierarchy.go`, `plan_server.go` for folded invocations); `tmuxq` is a separate package for predicate queries over an already-read snapshot ([Filtering and queries](/concepts/queries/)), and `workspace` a separate one again. - **Rust**: `crates/libtmux/src/{server,session,window,pane}/` directories, each split into files by concern (a `settings.rs` per tier holding that tier's options-and-hooks methods, matching the pattern in [Options and hooks](../options-and-hooks/)); `hooks.rs`, `options.rs`, and `formats.rs` hold the shared, scope-generic machinery those call into. Workspaces and the MCP server are separate crates in the same workspace. - **Java**: `io.github.libtmux` holds `Server`, `Session`, `Window`, and `Pane` as `final` classes; each exposes its option and hook tables through `.options()` / `.hooks()` accessor methods returning a separate `Options` / `Hooks` view scoped to that object, rather than mixing those methods directly into the entity class the way Python and Go do. `Session_`, `Window_`, and `Pane_` are a parallel set of typed-field classes that exist only for the query layer. - **.NET**: `src/LibTmux/` gives every entity its own name (`Pane.cs`, `Session.cs`, ...) but splits each into several `partial class` files by concern rather than by inheritance: `Pane.Capture.cs`, `Pane.Input.cs`, `Pane.Relations.cs`, `Pane.Scopes.cs`, `Pane.Topology.cs`, and so on all contribute to one `Pane` type. `Options`/`Hooks` are reached through `.Options` / `.Hooks` properties, structurally the same idea as Java's accessor methods. - **C++**: `include/libtmux/entities.hpp` declares `Session`, `Window`, and `Pane` together as value types (`private Row` bases), with their method bodies in `src/` rather than the header; `server.hpp`, `options.hpp`, and `capabilities.hpp` are separate headers. A private `testing` component (`include/libtmux/testing/`) ships separately from the library proper: see [Context managers](../context-managers/) for what it's for. - **Swift**: `Sources/LibTmux/Server.swift` is the hub every operation extends; `Session.swift` and `Pane.swift` declare the thin value types, `Snapshot.swift` holds the relationship queries ([Traversal](../traversal/)), and `Options.swift`, `PaneInteraction.swift`, and `Mutations.swift` are `extension Server` files grouping options/hooks, send/capture, and kill respectively: all reachable only through `Server`, per the section above. ## Naming conventions Method names follow language conventions: Python, Rust, and C++ use `snake_case`; TypeScript, Java, and Swift use `camelCase`; Go and .NET use `PascalCase`. Option and hook names remain tmux's dash-separated strings, such as `automatic-rename`, regardless of the method's spelling. --- # Traversal Source: https://libtmux.org/en/swift/latest/topics/traversal/ > Moving up and down the server/session/window/pane tree, and the two questions that come up once you have more than one object. Use relationships to move between sessions, windows, and panes. [Server, session, window, pane](/concepts/server-session-window-pane/) explains the hierarchy and snapshot model. This page covers relationship calls, collection membership, and object identity. ## Down the hierarchy List children through the parent object or a captured snapshot. Whether a read issues another tmux command depends on the API, independently of whether the call is async; see [Server, session, window, pane](/concepts/server-session-window-pane/). | Port | Server → sessions | Session → windows | Window → panes | |------|--------------------|--------------------|-----------------| | Python | `server.sessions` | `session.windows` | `window.panes` | | TypeScript | `await server.sessions()` | `session.windows` | `window.panes` | | Go | `server.Sessions(ctx)` | `session.Windows()` | `window.Panes()` | | Rust | `await server.sessions()` | `session.windows()` | `window.panes()` | | Java | `server.sessions()` | `session.windows()` | `window.panes()` | | .NET | `server.GetSessionsAsync()` | `session.GetWindowsAsync()` | `window.GetPanesAsync()` | | C++ | `server->sessions()` | `session->windows()` | `window->panes()` | | Swift | `server.sessions()`, or `snapshot.windows(of: session)` for windows/panes once you have a `Snapshot` | see previous column | see previous column | TypeScript's `session.windows` and `window.panes` read the graph loaded by `await server.sessions()` without additional tmux commands. Rust also provides `server.attached_sessions()` to list only sessions with an attached client. ## All panes in a session Use a session-wide pane collection when the task spans several windows, such as finding a command or capturing output from every pane. A window linked to multiple sessions still refers to the same tmux panes. Check whether the API reads live state or traverses a captured graph before reusing its result. ### Python `libtmux.Session.panes` runs a session-scoped `list-panes -s` read. Use it to list panes across the session's windows without manually listing each window. ### TypeScript `session.Session.panes` returns a `Selection` from the session's captured graph. Reading it does not issue another tmux command; refresh the snapshot when you need newer state. ### Rust `session.Session.panes` performs a live listing and returns a `Result` from the async call. Handle a command failure before using the returned panes. ### Go `tmux.Session.Panes` reads captured relations without another tmux command. The relation must have been included in the read that produced the session; an uncaptured relation does not establish that the session has no panes. ### .NET `LibTmux.Session.Panes` reads the session's captured relations. It does not issue a tmux command; an incomplete capture may lack the required relation. ### C++ `libtmux::Session::panes` runs a session-scoped `list-panes` command. Inspect the returned result before traversing the panes. ### Swift `Snapshot.panes(of:)` accepts a session or a window. The session overload traverses the captured graph and deduplicates pane IDs without a tmux read. ### Java Java has no direct session-wide pane member. Traverse `Session.windows()` and then `Window.panes()` in the captured relations. Deduplicate pane IDs when combining results from sessions that may share linked windows. ## Up the hierarchy Parent lookups may read captured data or query tmux again. Check the method's read and failure semantics; [Server, session, window, pane](/concepts/server-session-window-pane/) introduces that distinction: | Port | Pane → window | Window → session | |------|----------------|--------------------| | Python | `pane.window` | `window.session` | | TypeScript | `pane.window` (getter, from the loaded graph) | `window.session` (getter) | | Go | `pane.Window()` → `(Window, bool)` | `window.Session()` → `(Session, bool)` | | Rust | `await pane.window()` → `Result, Error>` | `await window.session()` → `Result, Error>` | | Java | `pane.window()` | `window.session()` | | .NET | `pane.Window` (property) | `window.Session` (property) | | C++ | `pane->window()` | `window->session()` | | Swift | `pane.windowID`, then look it up via `Snapshot` | not a per-window field: join through the snapshot instead | Go and Rust return optional relationship results. .NET's `.Window`, `.Session`, `.ActiveWindow`, and `.ActivePane` read captured state synchronously and throw `IncompleteSnapshotException` if that capture lacks the required context. ## One walk, down and back up Start with a session, traverse to a window and pane, then look up the parent and compare its identity with the starting object: ```swift let sessions = try await server.sessions() let session = sessions[0] let snapshot = try await server.snapshot() let window = snapshot.windows(of: session)[0] let pane = snapshot.panes(of: window)[0] // An array, not one Session: link-window can put a window in more than one. snapshot.sessions(of: window).contains(session) ``` ## The active child "Which window is in front right now" and "which pane would a command actually reach" are common enough questions that most ports expose the active child directly rather than making you filter a list: | Port | Session's active window | Window's active pane | |------|---------------------------|------------------------| | Python | `session.active_window` | `window.active_pane` | | TypeScript | `session.activeWindow` (getter) | `window.activePane` (getter) | | Go | `session.ActiveWindow()` → `(Window, bool)` | `window.ActivePane()` → `(Pane, bool)` | | Rust | `await session.active_window()` → `Result, Error>` | `await window.active_pane()` → `Result, Error>` | | Java | `session.activeWindow()` → `Optional` | `window.activePane()` → `Optional` | | .NET | `session.ActiveWindow` (property) | `window.ActivePane` (property) | | C++ | `session->active_window()` | `window->active_pane()` | | Swift | filter for `isActive` on `snapshot.windows(of: session)`: `Window` carries its own `window_active` flag rather than the session exposing an accessor | same pattern, on the pane's own active flag | In Swift, filter snapshot children by `isActive`. Other ports expose an active-child method or property on the parent. [Format-token fields](../format-tokens/) describes the underlying `window_active` and `pane_active` fields. ## Is it in that collection? Checking membership generally goes through whatever your language uses for collection membership, since most of these calls already return an ordinary array, slice, or list: - **Python** overloads `in` directly on its `QueryList`: `window in session.windows`, `pane in window.panes`. - **Java**, **C++**, and **.NET** return standard collections. Use their standard membership operations with the identity comparison appropriate to the port. - **TypeScript** returns iterable `Selection` objects. Iterate over the selection and compare IDs, or spread it into an array for standard array operations. - **Go** and **Rust** return slices or vectors. Iterate and compare object IDs when testing membership by tmux identity. ## Is this the same object? Compare IDs to determine whether two handles refer to the same tmux object on the same server. Equality operators vary by port: | Port | How you check | |------|----------------| | Python | `window.window_id == other.window_id` (or `pane.pane_id == ...`) | | TypeScript | compare `.id` | | Go | `pane.ID() == other.ID()`: `PaneID` is a plain, `==`-comparable `string` | | Rust | `pane.id() == other.id()`: verified from the port's own doctests, not struct equality | | Java | `pane.equals(other)`: overridden to compare server identity plus pane ID | | .NET | `pane.Equals(other)`: overridden to compare a generation counter plus ID | | C++ | `pane == other`: `operator==` is defined directly on `Session`/`Window`/`Pane` | | Swift | compare `.id` for identity; see the equality note below | **Swift's equality compares captured state.** Compiler-synthesized equality for `Session`, `Window`, and `Pane` compares every stored property, including dimensions and current command. Two reads can compare unequal even when they describe the same tmux object. Compare `.id` when checking identity on the same server. --- # Context managers Source: https://libtmux.org/en/swift/latest/topics/context-managers/ > Scope-based cleanup for tmux objects, and when your program must kill them explicitly. A tmux session, window, or pane normally remains until you kill it. Scope-based cleanup can kill it when your code leaves a block, including after an exception. See [Workspaces](/concepts/workspaces/) for a temporary layout example. Python provides context managers for tmux objects. .NET provides ownership scopes for servers, sessions, and windows. Other ports require explicit cleanup or offer guards for test servers: | Port | Server | Session | Window | Pane | |------|:------:|:-------:|:------:|:----:| | Python | yes | yes | yes | yes | | .NET | yes | yes | yes | - | | Java | closes conn. | - | - | - | | Rust | test-only | - | - | - | | C++ | test-only | - | - | - | | TypeScript | - | - | - | - | | Go | - | - | - | - | | Swift | - | - | - | - | "test-only" means a guard owns an entire disposable test server. Java's `AutoCloseable` server releases its transport but leaves tmux running. A dash means no built-in cleanup scope is listed for that object; use an explicit kill call with the cleanup mechanism appropriate to your language. ## Python: every level, including nested Python's `Server`, `Session`, `Window`, and `Pane` support context managers. Entry returns the existing object; exit kills it, including when the block raises: Nested scopes exit in reverse order: pane, window, session, then server. ## .NET: an explicit ownership type, stopping at Window .NET's `OwnedSessionScope` and `OwnedWindowScope` wrap the created object and implement `IAsyncDisposable`. The `Session` and `Window` handles themselves are not disposable: There is no `OwnedPaneScope`. For tests, `TmuxTestFactory.CreateHierarchyAsync()` returns a `TemporaryHierarchyScope` containing a private server, session, window, and pane. Disposing it kills the server. ## Java: `Server` is closeable, but closing one doesn't kill it Java's `Server` implements `AutoCloseable`. Exiting `try (Server server = Server.open(config))` releases the owned transport while tmux and its sessions remain running. Kill sessions, windows, panes, or the server explicitly when your program owns their cleanup. ## Rust: no async `Drop`, so cleanup is explicit or best-effort Rust's `Drop::drop` is synchronous and cannot await an async tmux kill. Use explicit shutdown when you need to observe cleanup failures: - **`kill(self)` consumes the handle.** Session, window, and pane kill methods take `self` by value, preventing subsequent use of that handle. - **`libtmux::test::TestServer` provides a test guard.** Call `guard.shutdown().await?` to handle cleanup errors. Its `Drop` implementation falls back to synchronous, best-effort `force_cleanup()`. ## C++: RAII exists, but only for a private test server C++'s `Session`, `Window`, and `Pane` are non-owning values; destroying a handle does not kill its tmux object. `libtmux::test::ScopedTmuxServer`, in the separate `testing` CMake component, owns a private test server and its temporary socket directory: ## TypeScript, Go, Swift: no built-in scoping at all TypeScript, Go, and Swift require explicit cleanup of sessions, windows, and panes. Connection or notification handles may have separate disposal APIs: - **TypeScript** implements `[Symbol.asyncDispose]` on control connections and notification streams. `await using` releases those handles; it does not kill the watched session or pane. See [Control mode vs one-shot](/concepts/transports/). Use `finally` for a session your program owns: ```typescript const session = await server.newSession({ name: "work" }); try { const window = await session.newWindow({ name: "editor" }); await window.panes.at(0)?.sendKeys("echo hi"); } finally { await session.kill(); } ``` - **Go** implements `io.Closer` on `ControlClient`, `PaneObservation`, and `NotificationStream`. Use `defer conn.Close()` for those resources and an explicit `Kill(ctx)` for tmux objects: ```go session, err := server.NewSession(ctx, tmux.NewSessionRequest{Name: "work"}) if err != nil { return err } defer session.Kill(ctx) // idiomatic Go: not a library-provided guarantee ``` - **Swift** uses non-owning session, window, and pane values. Call `try await server.kill(session)` or the corresponding window or pane overload when cleanup is required. ## What this means in practice Use explicit cleanup for objects whose handles have no disposal hook. For an entire disposable test server, prefer your port's test fixture or server guard; see [Testing with libtmux](/guides/testing-with-libtmux/). --- # Pane interaction Source: https://libtmux.org/en/swift/latest/topics/pane-interaction/ > Input defaults, screen capture, and waiting for a command to finish. Send input to a pane and capture its screen to interact with a running program. [Attach and send keys](/examples/attach-and-send-keys/) provides examples. [Sending keys](/guides/sending-keys/) and [Capturing output](/guides/capturing-output/) are task guides; this page compares input defaults, capture ranges, and completion handling. ## Typing into a pane Two questions come up every time you send something to a pane: should tmux press Enter afterward, and should tmux interpret what you sent as key names (`Enter`, `C-c`) rather than literal characters? Ports answer both, but disagree on whether that's one method with flags or two separate methods: ### Python **Type without Enter:** `pane.send_keys(text, enter=False)` **Type + Enter (default):** `pane.send_keys(text)` **How "literal" is chosen:** `literal=True` flag on the same method ### TypeScript **Type without Enter:** `pane.sendKeys(text, { enter: false })` **Type + Enter (default):** `pane.sendKeys(text)` **How "literal" is chosen:** `{ literal: true }` option ### Go **Type without Enter:** `pane.SendKeys(ctx, SendKeysRequest{Command: &text, SkipEnter: true})` **Type + Enter (default):** `pane.SendKeys(ctx, SendKeysRequest{Command: &text})` **How "literal" is chosen:** `Literal: true` field ### Rust **Type without Enter:** `pane.send_keys(keys)`: **always literal**, key names typed as text **Type + Enter (default):** `pane.send_line(text)` **How "literal" is chosen:** `send_keys` sends literal text; `send_key_names` interprets tmux key names. ### Java **Type without Enter:** `pane.send(keys)` **Type + Enter (default):** `pane.sendLine(command)` **How "literal" is chosen:** Separate text and key-sending methods. ### .NET **Type without Enter:** `SendKeysAsync(new SendKeysRequest(text, enter: false))` **Type + Enter (default):** `SendTextAsync(text)` (defaults `enter: true`) **How "literal" is chosen:** `Literal` field on `SendKeysRequest`; `SendTextAsync` hardcodes it ### C++ **Type without Enter:** `pane->send_text(text)` **Type + Enter (default):** `send_text(text)` then `send_key("Enter")` separately: no combined convenience exists **How "literal" is chosen:** `send_text` is always literal; `send_key` is always a key name ### Swift **Type without Enter:** `server.sendKeys([text], to: pane)` **Type + Enter (default):** `server.run(text, in: pane)` (sugar for `sendKeys([text, "Enter"], to: pane)`) **How "literal" is chosen:** `literally: true` option on `sendKeys` ### Examples **Rust's `send_keys` always sends literal text.** Use `send_key_names` for tmux key names. Passing `"Enter"` to `send_keys` types those characters; it does not press the key. **Text and Enter can be separate commands.** Python, TypeScript, Go, and .NET normally send Enter after the text. If the second operation fails, the text may already be in the pane; retrying the entire request can duplicate it. C++'s separate `send_text` and `send_key("Enter")` calls have the same risk. Rust's `send_line` and Java's `sendLine` append a literal `\r` to the text and send it in one `send-keys -l` command. They avoid a separate Enter dispatch. Send a command line and press Enter: ```swift try await server.sendKeys(["echo hi"], to: pane) // no Enter try await server.run("echo hi", in: pane) // sugar for sendKeys([text, "Enter"]) ``` ## Reading a pane back Capture methods return lines from the pane's visible screen by default: `pane.capture_pane()` in Python, `pane.capture()` in TypeScript, Rust, Java, and C++, `pane.Capture(ctx, ...)` in Go, `CaptureAsync(...)` in .NET, and `server.capture(pane)` in Swift. Request scrollback explicitly, such as with Python's `start` and `end` or Swift's `includingHistory`. ```swift try await server.capture(pane) ``` ## Waiting for something to finish A send call completes when input reaches tmux. It does not wait for the shell command to finish. Wait for expected output or a completion signal: - **Python** can poll capture output for a marker. Its test-support module also provides `libtmux.test.retry_until(condition, ...)` for arbitrary conditions. [Waiting and retrying](../waiting-and-retry/) covers polling helpers across ports. - **Swift** ships a real primitive for exactly this: `server.waitForOutput(...)` returns an `OutputWait` once a pattern shows up in the pane, rather than leaving you to write the loop. - **Go** and **.NET** expose tmux's `wait-for` signal channel through `server.WaitFor(ctx, WaitForRequest{...})` and `TmuxWaitChannel`. Use a named signal when you control the command and can make it announce completion. [Capture pane output](/examples/capture-pane-output/) has the checked, per-port code for the polling-with-a-marker version of this; reach for a port's native wait primitive above it where one exists. --- # Options and hooks Source: https://libtmux.org/en/swift/latest/topics/options-and-hooks/ > Reading and writing tmux's own configuration knobs, and binding commands to its events, at whichever scope you're holding. Use options to change tmux behavior, such as `automatic-rename` or the status-line format. Use hooks to run commands on events such as `session-renamed` or `after-split-window`. Choose the scope supported by the option or hook. ## Reading and writing options Read an option, set it, or unset it at a chosen scope. Ports differ in how they distinguish values set locally from effective values inherited from another scope: ### Python **Read all (this scope):** `pane.show_options()` **Read effective/inherited:** `pane.show_option(name, global_=True)` reaches the global fallback explicitly; no separate "resolved" call **Set:** `pane.set_option(name, value)` **Unset:** `pane.unset_option(name)` ### TypeScript **Read all (this scope):** `pane.showOptions()` **Read effective/inherited:** `pane.showResolvedOptions()` **Set:** `pane.setOption(name, value)` **Unset:** `pane.unsetOption(name)` ### Go **Read all (this scope):** `pane.Options(ctx)`: a typed struct with one accessor method per option **Read effective/inherited:** Not listed. **Set:** `pane.SetOption(ctx, ...)` **Unset:** `pane.UnsetOption(ctx, ...)` ### Rust **Read all (this scope):** `pane.options()` (typed `BTreeMap`), `pane.option_names()` **Read effective/inherited:** `pane.typed_option(name)` decodes one value by its declared kind **Set:** `pane.set_option(name, value)`, `pane.append_option(name, value)` **Unset:** `pane.unset_option(name)` ### Java **Read all (this scope):** `pane.options().all()` **Read effective/inherited:** `pane.options().get(name)` reads `show-options -A -v`: inherited, not just local **Set:** `pane.options().set(name, value)` **Unset:** `pane.options().unset(name)` ### .NET **Read all (this scope):** `pane.Options.GetAllAsync()` **Read effective/inherited:** `pane.Options.GetAsync(new GetOptionRequest(name, includeInherited: true))`: an explicit opt-in flag, mapped straight to tmux's own `-A` **Set:** `pane.Options.SetAsync(new SetOptionRequest(name, value))` **Unset:** `pane.Options.UnsetAsync(...)` ### C++ **Read all (this scope):** `pane->options()` **Read effective/inherited:** Not listed. **Set:** `pane->set_option(name, value)` **Unset:** `pane->unset_option(name)` ### Swift **Read all (this scope):** `server.options(.pane(pane))` **Read effective/inherited:** `server.option(name, scope: .pane(pane))` reads presence from the listing, then the value with `-v` **Set:** `server.setOption(name, to: value, scope: .pane(pane))` **Unset:** `server.unsetOption(name, scope: .pane(pane))` ### Examples After a successful write completes, read the option to obtain its updated value. These examples set, read, and unset a pane option: ```swift try await server.setOption("automatic-rename", to: "off", scope: .pane(pane)) try await server.options(.pane(pane)) try await server.unsetOption("automatic-rename", scope: .pane(pane)) ``` ## Hooks ### Python **Set:** `pane.set_hook(name, command)` **Unset:** `pane.unset_hook(name)` **List:** `pane.show_hook(name)`, `pane.show_hooks()` (all) **Run now, without the event:** Not listed. ### TypeScript **Set:** `pane.setHook(name, command, { append })` **Unset:** `pane.unsetHook(name)` **List:** `pane.showHooks()` (all; no singular `showHook`) **Run now, without the event:** Not listed. ### Go **Set:** `pane.SetHook(ctx, name, command)`, `pane.SetHooks(ctx, ...)` (bulk) **Unset:** `pane.UnsetHook(ctx, name)` **List:** `pane.Hooks(ctx)`: typed struct **Run now, without the event:** Not listed. ### Rust **Set:** `pane.set_hook(name, command)` **Unset:** `pane.unset_hook(name)` **List:** `pane.hook(name)`: one name only; **no listing at pane/window scope**, by design (see below) **Run now, without the event:** Not listed. ### Java **Set:** `pane.hooks().set(event, command)`, `.append(event, command)` **Unset:** `pane.hooks().unset(event)` **List:** `pane.hooks().all()` **Run now, without the event:** `pane.hooks().run(event)`: tmux's `set-hook -R` ### .NET **Set:** `pane.Hooks.SetAsync(new SetHookRequest(event, command))` **Unset:** `pane.Hooks.UnsetAsync(...)` **List:** `pane.Hooks.GetAllAsync()` **Run now, without the event:** `pane.Hooks.RunAsync(...)` ### C++ **Set:** `session.set_hook(name, command)`: no `Window`/`Pane` overload exists at all **Unset:** Not listed. **List:** `session.hooks()`, `server.global_hooks()` **Run now, without the event:** Not listed. ### Swift **Set:** `server.setHook(name, to: command, at: index, in: scope)` **Unset:** `server.unsetHook(name, in: scope)` **List:** `server.hooks(scope)` **Run now, without the event:** `server.runHook(name, in: scope)` ### Examples tmux stores hook commands in indexed arrays, such as `after-new-window[0]`. Python and TypeScript can include the index in the name. Go's `SetHooks` and Swift's `at:` parameter take it separately. Java's `.append()` and TypeScript's `{ append: true }` append without requiring the next index. Set and list a session hook. The next section explains window and pane scope limitations: ```swift try await server.setHook("session-renamed", to: "display-message 'renamed'", in: .session(session.id.rawValue)) try await server.hooks(.session(session.id.rawValue)) ``` ## Supported hook scopes tmux stores hooks globally or per session. Accepted `set-hook -w` or `-p` flags do not imply a separate window or pane hook table, and `show-hooks` does not provide a corresponding listing. Check the event's supported scope if a hook is accepted but never fires. Ports handle unsupported hook scopes differently: - **Java's `Hooks.java`** documents that tmux can accept a hook at an unsupported scope without an effective registration. Check `.all()` when diagnosing a hook that does not fire. - **Rust** validates scope in `Pane::set_hook` and `Window::set_hook`, returning `Error::OptionScopeMismatch` for an unsupported scope. - **Swift** restricts `HookScope` to `.global` and `.session`. **C++** exposes `set_hook` on `Session` and `global_hooks()` on `Server`, with no window or pane hook methods. Options have window and pane tables of their own. The hook-scope limitation does not apply to ordinary options. ## tmux version compatibility Python's compatibility notes list these tmux requirements: | Feature | Minimum tmux | |---------|-------------| | All options/hooks features | 3.2+ | | Window/pane hook *scope flags* (`-w`, `-p`) accepted | 3.2+; see the supported-scope caveat above | | `client-active`, `window-resized` hooks | 3.3+ | | `pane-title-changed` hook | 3.5+ | Check your port's compatibility notes before relying on a particular tmux release. --- # Format-token fields Source: https://libtmux.org/en/swift/latest/topics/format-tokens/ > The typed fields every object exposes, mirroring tmux's own format tokens, and why a field is sometimes absent. Object fields expose values from tmux's [FORMATS](https://man.openbsd.org/tmux.1#FORMATS), such as `pane_id`, `window_zoomed_flag`, and `session_name`. The available fields depend on the port, object scope, tmux version, and data requested by the read. A token needs the right **scope** and **tmux version**. For example, a pane token needs a pane context, and a token added after your tmux release may be absent. Ports represent absence with optional values, flags, or errors, as described below. ## The absence idiom, per port | Port | What an excluded field looks like | |------|--------------------------------------| | Python | the attribute is `None` | | TypeScript | the property is `undefined` | | Go | a two-return-value accessor: `pane.DeadSignal()` returns `(string, bool)`: Go's own "comma ok" idiom | | Rust | `Option`; consult the reference for the accessor name | | Java | `Optional`: `pane.floating()` returns `Optional`, empty when the field isn't populated | | .NET | nullable values or `IncompleteSnapshotException`, depending on whether the value or captured field is absent | | C++, Swift | fixed, non-optional fields; see below for access to other tokens | These examples read optional fields, including `pane_dead_signal` on tmux 3.3 or newer: Rust's `formats.rs` marks this token as optional. Consult its generated reference for the accessor name. .NET also distinguishes missing values from incomplete captures. `Pane.Title` is nullable because tmux may report no title. `Pane.Height`, `.Width`, and `.Index` throw `IncompleteSnapshotException` when the read that produced the handle did not request those fields. A handle resolved by ID alone may therefore lack enough data to answer: ## A generated table under the accessor Several ports generate scope- and version-tagged field catalogs from tmux source or documentation. [Architecture](../architecture/) describes the layouts. Examples include: - **TypeScript** uses `_generated/format_fields.ts` rows with `scope`, `since`, and `token`. For example, `pane_zoomed_flag` has pane scope and requires tmux 3.7. `_generated/field_aliases.ts` supplies the camelCase alias `pane.zoomedFlag`. - **Rust** uses a macro row in `formats.rs` for each token's wire name, scope, tmux version, and type. `pane_dead_signal` has `Pane` scope, requires `V3_3`, and is decoded as `Text`. - **Go** generates `format_generated.go` with `internal/generate/formats`. Some accessors decode richer values: `pane.DeadTime()` returns `(time.Time, bool)` and performs timestamp parsing for the caller. Two per-token facts survive across every one of these catalogs, because they're facts about tmux, not about any one port's generator: `pane_dead_signal` and `pane_dead_time` arrived in tmux 3.3, and a cluster of pane-geometry and floating-pane tokens (`pane_floating_flag`, `pane_pb_progress`, `pane_x`, `pane_y`, `pane_z`, `pane_zoomed_flag`, `bracket_paste_flag`, `synchronized_output_flag`, among others) arrived together in 3.7. ## The two ports that didn't generate the full catalog Swift and C++ expose fixed, non-optional fields on `Session`, `Window`, and `Pane`: - **Swift** carries `index`, `width`, `height`, `isActive`, `currentCommand`, `currentPath`, and the four edge flags. - **C++** declares fields in `kFields` arrays. Pane fields include `id`, `command`, `active`, `index`, `title`, `pid`, `tty`, `path`, `width`, `height`, `dead`, `in_mode`, edge flags, and `piping`. Accessors return `std::string_view`, `bool`, or `long long`. ```swift pane.isActive // Bool, not Bool?: always populated, never gated pane.currentCommand // String, likewise ``` For a token outside the fixed fields, C++ provides one-shot expansion with `pane->expand("#{pane_dead_signal}")`. Swift uses `FormatSubscription` on a control connection, delivering `SubscriptionChange` when tmux re-evaluates the token. That API observes changes over time. [Architecture](../architecture/) describes the fixed-field model. ## Fields promoted from the active child Python exposes fields promoted from an active child. For example, `session.pane_id` identifies the active pane of the session's active window: tmux's format engine includes active-child fields when listing a parent. A `list-sessions -F` row can include `window_id` and `pane_id` for the active window and pane. Check the port reference for typed access to those fields, or use the explicit relationships described in [Traversal](../traversal/). A pane context can include parent window and session fields. A session cannot identify one attached client when several clients may be attached, so client tokens such as `client_name` require a client context. --- # Waiting and retrying Source: https://libtmux.org/en/swift/latest/topics/waiting-and-retry/ > Polling a condition instead of guessing a sleep, and tmux's own wait-for signal channel as the alternative to polling. After sending input or starting a process, wait for the state your next step requires. [Pane interaction](../pane-interaction/#waiting-for-something-to-finish) covers waiting for screen text. This page covers arbitrary conditions and tmux's named `wait-for` signal channels. ## Polling a condition Polling checks a condition repeatedly until it succeeds or a deadline expires. The helpers below expose an interval and timeout; several live in test-support packages: ### Python **Helper:** `libtmux.test.retry_until(fn, seconds=, interval=)` **Where it lives:** The `libtmux.test` module in the main package; raises `WaitTimeout`. ### TypeScript **Helper:** `connectedServer.waitFor(matches, options)` **Where it lives:** The public control-connection API. Tests a predicate over `ServerSnapshot`; see [Control mode vs one-shot](/concepts/transports/). ### Go **Helper:** `tmuxtest.WaitFor(ctx, interval, condition)` **Where it lives:** `tmuxtest`, a separate test-support package from `tmux` ### Rust **Helper:** `libtmux::test::retry_until(within, condition)` **Where it lives:** `libtmux::test`, enabled with the `test-support` Cargo feature. ### Java **Helper:** Not listed. **Where it lives:** not found in the shipped library; a package-private `Await.until(...)` exists only inside the `integration-tests` module, which downstream code cannot depend on ### .NET **Helper:** `LibTmux.Testing.TmuxWait.UntilAsync(probe, timeout, interval)` **Where it lives:** `LibTmux.Testing`, part of the same shipped `LibTmux` package ### C++ **Helper:** Not listed. **Where it lives:** no generic condition-poll helper found in the public library; a `wait_until` exists only in the private `testing` component, for waiting on a spawned child process, not on tmux state ### Swift **Helper:** Not listed. **Where it lives:** a `waitUntil` helper exists only inside the test target's own support code, not shipped ### Examples Python, Rust, Go, and .NET provide general polling helpers in their test-support APIs. TypeScript's public `waitFor` instead waits on a server-snapshot predicate through a control connection. It subscribes before reading so it does not miss a change between those steps. For Java, C++, and Swift, this page lists no public arbitrary-condition polling helper. Use a loop with a deadline and interval if a more specific wait API does not fit; [Pane interaction](../pane-interaction/#waiting-for-something-to-finish) covers output waits. ## tmux's own wait-for channel Use `tmux wait-for -S ` to signal and `tmux wait-for ` to block until signalled. This avoids repeated screen captures when the command can announce its own completion: | Port | Signal | Wait | |------|--------|------| | Python | `server.wait_for(channel, set_flag=True)` | `server.wait_for(channel)` | | TypeScript | not exposed as public API: used only inside the test-server's own startup handshake | - | | Go | `server.WaitFor(ctx, tmux.WaitForRequest{Channel: name, Mode: tmux.WaitForModeSignal})` | `tmux.WaitForRequest{Channel: name}` (the zero-value `WaitForRequest.Mode` waits) | | Rust | `server.signal_channel(name).await?` | `server.wait_for_channel(name, timeout).await?` → `ChannelWait::Signalled` or `TimedOut` | | Java | `server.channel(name).signal()` | `server.channel(name).await(timeout)` → a `WakeReason`, never silently "success" | | .NET | `server.OpenWaitChannel(name)` returns a `TmuxWaitChannel`; signalling is the same request with a different mode | `await using` the channel, then `WaitAsync(budget)` | | C++ | `server.signal(channel)` | `server.wait_for(channel, timeout)` | | Swift | `try await server.signal(channel)` | `try await server.wait(for: channel)` | ```swift try await server.signal("built") try await server.wait(for: "built") ``` tmux remembers a signal sent before a waiter starts. The next wait on that channel returns immediately, so completion is not lost when the command finishes first. A raw `wait-for` client can exit zero when the server dies, as well as when the channel is signalled. Java's `WakeReason` and Swift's `wait(for:)` distinguish server loss from a signal; Swift checks the server PID before and after the wait. Use a channel name specific to the task, or clear an old signal with Java's `drain()` when appropriate. A remembered signal can otherwise satisfy an unrelated later wait. --- # Environment Source: https://libtmux.org/en/swift/latest/topics/environment/ > Locate tmux objects from process variables and manage the environment inherited by new panes. tmux exposes two environment APIs. Process variables such as `TMUX` and `TMUX_PANE` let code inside a pane identify its server and pane. The server also stores variables through `set-environment` and `show-environment` for new processes to inherit. Like the tables in [Options and hooks](../options-and-hooks/), this persistent store has explicit scopes. ## Locating yourself from inside a pane Inside a pane, `TMUX` contains `,,`, and `TMUX_PANE` contains the pane ID, such as `%1`. Use these variables to locate the current tmux objects. Ports expose different levels of environment lookup: ### Python **Server:** `Server.from_env()` **Session:** `Session.from_env()` **Window:** `Window.from_env()` **Pane:** `Pane.from_env()` ### TypeScript **Server:** Not listed. **Session:** `Session.fromEnv()` **Window:** Not listed. **Pane:** Not listed. ### Go **Server:** `NewServerFromEnv(env)` **Session:** `SessionFromEnv(ctx, env)` **Window:** `WindowFromEnv(ctx, env)` **Pane:** `PaneFromEnv(ctx, env)` ### Rust **Server:** `Server::from_env()` **Session:** `Session::from_env(&server)` **Window:** `Window::from_env(&server)` **Pane:** `Pane::from_env(&server)` ### Java **Server:** Not listed. **Session:** Not listed. **Window:** Not listed. **Pane:** See the Java context example below. ### .NET **Server:** `Server.FromEnvironment(env)` **Session:** `Session.FromEnvironmentAsync()` **Window:** `Window.FromEnvironmentAsync()` **Pane:** `Pane.FromEnvironmentAsync()` ### C++ **Server:** `Server::from_env()` **Session:** Not listed. **Window:** Not listed. **Pane:** Not listed. ### Swift **Server:** `TmuxContext.current()` **Session:** `TmuxContext.current()` (same call: see below) **Window:** Not listed. **Pane:** Not listed. ### Examples Python, Go, and .NET provide standalone environment lookups at each level. Rust's `Session`, `Window`, and `Pane::from_env` require an existing `&Server`. TypeScript provides `Session.fromEnv()`; this page lists no equivalent for its other object types. A process not started inside a pane has nothing truthful to answer with, so every one of these raises rather than guessing: Python's `NotInsideTmux`, Go's `FromEnvError`, .NET's `TmuxObjectNotFoundException`, and so on, each naming the missing or malformed variable rather than returning an empty or default object. ### Java and C++ stop short of the pane Neither port gives you a live object back the way the other five do, and they stop at different points: - **C++** provides `Server::from_env()` to select the socket. Use the resulting server to resolve sessions or panes. - **Java** parses `TMUX` and `TMUX_PANE` into identifiers: socket path, server PID, `SessionId`, and `Optional`. It returns context data rather than a live pane handle: ```java TmuxEnvironment here = TmuxEnvironment.current().orElseThrow(); try (Server server = Server.open(here.config())) { Session mine = server.sessions().stream() .filter(session -> session.id().equals(here.session())) .findFirst() .orElseThrow(); } ``` ### Swift context fields Swift's `TmuxContext.current()` parses the socket path, server PID, and session ID from `TMUX`. It does not read `TMUX_PANE`, so it cannot identify the current pane: ```swift let context = TmuxContext.current()! // socket path, server pid, session id let server = try context.server() ``` Read `TMUX_PANE` separately if you need the pane ID; `TmuxContext` does not provide it. ## tmux's own environment variable store Like [Options and hooks](../options-and-hooks/#window-and-pane-hook-scopes-are-mostly-fiction), tmux's persistent environment store has global and per-session scopes. It is read with `show-environment` and updated with `set-environment`. Newly spawned processes inherit it; existing processes retain their own environments. ### Python **Set:** `server.set_environment(name, value)`, `session.set_environment(...)` **Read all:** `server.show_environment()`, `session.show_environment()` **Unset:** `server.unset_environment(name)`. See below for `.remove_environment()`. ### TypeScript **Set:** `server.setEnvironment(name, value)`, `session.setEnvironment(...)` **Read all:** `server.showEnvironment()`, `session.showEnvironment()` **Unset:** `server.unsetEnvironment(name)`, `session.unsetEnvironment(name)` ### Go **Set:** `server.SetEnvironment(ctx, name, value, opts)` (global, `-g`) **Read all:** `server.ShowEnvironment(ctx)` **Unset:** `server.UnsetEnvironment(ctx, name)` ### Rust **Set:** `server.set_environment(...)`, `session.set_environment(...)` **Read all:** `server.environment_all()`, `session.environment_all()` **Unset:** `server.unset_environment(name)`, `session.unset_environment(name)` ### Java **Set:** Not documented here; see the Java and C++ note below. **Read all:** Not documented here; see the Java and C++ note below. **Unset:** Not documented here; see the Java and C++ note below. ### .NET **Set:** `server.Environment.SetAsync(name, value)`, `session.Environment.SetAsync(...)` **Read all:** `server.Environment.GetAllAsync()` **Unset:** `server.Environment.UnsetAsync(name)`, `.RemoveAsync(name)` ### C++ **Set:** Not documented here; see the Java and C++ note below. **Read all:** Not documented here; see the Java and C++ note below. **Unset:** Not documented here; see the Java and C++ note below. ### Swift **Set:** `server.setEnvironment(name, to: value, in: scope)` **Read all:** `server.environment(scope)` **Unset:** `server.unsetEnvironment(name, in: scope)`, `.removeEnvironment(name, in:)` ### Examples ```swift try await server.setEnvironment("EDITOR", to: "vim", in: .global) try await server.setEnvironment("EDITOR", to: "hx", in: .session(session.id.rawValue)) try await server.environment(.session(session.id.rawValue)) ``` Python's `set_environment` writes a value, and `unset_environment` (`-u`) removes the entry. `remove_environment` (`-r`) marks the variable for exclusion from new processes, including when tmux inherited it at server startup; the listing retains it as `-NAME`. Swift exposes the same distinction through `setEnvironment`, `unsetEnvironment`, and `removeEnvironment`. A remove operation is not documented here for TypeScript, Go, or Rust. ### Java and C++: no verified access to this table at all Java and C++ examples here cover environment values supplied at process creation, not reads or writes to an existing persistent table. Java's `SessionSpec.Builder.environment(Map)`, `WindowSpec.Builder.environment(Map)`, and `SplitSpec.Builder.environment(Map)` pass initial variables to `new-session -e`, `new-window -e`, and `split-window -e`. Consult the port reference if you need to change the environment of an existing session. --- # Socket and servers Source: https://libtmux.org/en/swift/latest/topics/socket-and-servers/ > How a port names one specific tmux server among several, checks whether it's actually there, and tells one running instance apart from another. A tmux server is selected by its Unix-domain socket. Use different sockets for independent servers, such as a development session and an isolated test server. Ports expose tmux's default, named (`-L`), and explicit-path (`-S`) socket selectors: ## Naming a server | Port | Default | Named socket (`-L`) | Explicit path (`-S`) | |------|---------|----------------------|------------------------| | Python | `Server()` | `Server(socket_name="work")` | `Server(socket_path="/tmp/tmux-1000/work")` | | TypeScript | `new Server()` | `new Server({ socketName: "work" })` | `new Server({ socketPath: "..." })` | | Go | `tmux.NewServer(tmux.ServerOptions{})` | `tmux.ServerOptions{SocketName: "work"}` | `tmux.ServerOptions{SocketPath: "..."}` | | Rust | `Server::new()` | `Server::builder().socket_name("work").build()?` | `Server::builder().socket_path("...").build()?` | | Java | `ServerEndpoint.defaultSocket()` | `ServerEndpoint.namedSocket("work")` | `ServerEndpoint.socketPath(path)` | | .NET | `new ServerConnectionOptions()` | `new ServerConnectionOptions(socketName: "work")` | `new ServerConnectionOptions(socketPath: "...")` | | C++ | `Server::at_default()` | `Server::at_socket_name("work")` | `Server::at_socket_path("...")` | | Swift | no bare default: see below | `Server(socketName: "work")` | `Server(socketPath: "...")` | ```swift let named = try Server(socketName: "work") ``` Choose either a socket name or a socket path. TypeScript rejects both together with `TypeError`; Go documents that `SocketPath` takes precedence. tmux uses `TMUX_TMPDIR` to resolve the directory for default and named sockets. Swift requires an explicit `socketPath` or `socketName` argument. To reach tmux's default socket, use `Server(socketName: "default")`. Python's `Server(socket_name_factory=...)` and .NET's `ServerConnectionOptions(socketNameFactory: ...)` accept a callable that generates socket names. Use a unique name for each isolated test server. ## Is the server actually there? A server handle does not prove that the target server is running. Use a liveness check when your program needs to distinguish a live server from an unavailable socket: | Port | Check | |------|-------| | Python | `server.is_alive()` → `bool` | | TypeScript | `await server.isAlive()` → `Promise`; `await server.raiseIfDead()` throws with tmux's own reason instead | | Go | `server.IsAlive(ctx)` → `(bool, error)`: the `error` is reserved for a question that couldn't be answered at all, not for "not alive" | | Rust | `server.is_alive().await` → `bool`; `server.check_alive().await` is the fallible twin, for when the *reason* matters | | Java | `server.isAlive()` → `boolean` | | .NET | `await server.IsAliveAsync()` → `Task` | | C++ | `server.is_alive(timeout)` → `bool` | | Swift | `try await server.isRunning()` → `Bool` | ```swift if try await server.isRunning() { try await server.sessions() } ``` TypeScript's `isAlive()` and Rust's `is_alive()` return a boolean. Use TypeScript's `raiseIfDead()` or Rust's `check_alive()` when you need failure details. ## Killing a server, and telling two apart Kill an entire server with Python's `server.kill_server()`, TypeScript's `await server.kill()`, Go's `server.Kill(ctx)`, Rust's `server.kill().await?`, Java's or Swift's `server.killServer()`, .NET's `await server.KillAsync()`, or C++'s `server.kill()`. Java's `Server.close()` only releases the local connection; see [Context managers](../context-managers/#java-server-is-closeable-but-closing-one-doesnt-kill-it). Two handles can select the same socket. Python's `Server.__eq__` compares `socket_name` and `socket_path`. Go's `server.Equal(other)` resolves relative paths and environment-dependent socket names against each handle's captured binding. A restarted server can reuse a socket path while having different state. TypeScript's `TmuxServerRestartedError` and Go's `ErrDaemonReplaced` detect a handle encountering a replacement daemon. --- # Errors and exceptions Source: https://libtmux.org/en/swift/latest/topics/errors-and-exceptions/ > What a failed tmux command becomes in each port, and the question every one of them has to answer before letting you retry it. A command can fail because tmux rejects it, or because the transport stops before returning a reply. Ports report these failures through typed exceptions or return values. Before retrying a mutation, determine whether tmux may already have received it. For lookup failures caused by zero or multiple matches, see [Filtering and queries](/concepts/queries/#the-cardinality-contract-side-by-side). ## A failed command, as a value | Port | How it fails | Base type | |------|--------------|-----------| | Python | throws | `LibTmuxException`: carries an optional `subcommand`; `str()` reads `": "` | | TypeScript | throws | `LibTmuxException extends Error`, with `TmuxCommandError` (tmux ran and refused) and `TmuxTransportError` (it didn't get an answer) as the two shapes that matter here | | Go | returns `(T, error)` | no shared base type: small typed `...Error` structs plus sentinel `errors.New` values, composed with `errors.Is` / `errors.As` and `%w` wrapping | | Rust | returns `Result` | one `Error` enum, `#[non_exhaustive]`, matched rather than caught | | Java | throws (unchecked) | `LibTmuxException extends RuntimeException` | | .NET | throws | `LibTmuxException`, with typed subclasses per failure (`TmuxCommandException`, `TmuxTransportException`, `TmuxObjectNotFoundException`, and a dozen more) | | C++ | returns `expected` | `CommandFailure { kind, delivery, exit_code, diagnostic }`: no exception type at all | | Swift | throws (typed) | `enum TmuxError: Error`, thrown as `throws(TmuxError)`: Swift's typed-throws syntax, not a bare `throws` | Rust and C++ return result values. Go returns an `error` that callers inspect with `errors.Is` or `errors.As`. Exception-based ports report failures through their exception hierarchies. TypeScript's own docs make the split between its two exception shapes concrete: ## Is it safe to retry? Retry a mutation automatically only when you know it was not dispatched, or when repeating it is safe for your operation. A timeout, cancellation, or dropped connection can occur after tmux has acted. These APIs expose delivery information: | Port | Name | States | |------|------|--------| | TypeScript | `TmuxTransportError.delivery` | `"not_started"` / `"written"` / `"replied"` / `"indeterminate"`: only `not_started` is safe to retry blindly | | .NET | `LibTmuxException.Dispatch` (`TmuxDispatchState`) | `NotDispatched` / `Dispatched` / `Unknown` (the default) | | Java | `DispatchOutcome`, via `TmuxTimeoutException.outcome()` | `NOT_DISPATCHED` / `COMPLETE` / `UNKNOWN` | | C++ | `DeliveryStatus` | `not_started` / `written` / `replied` / `indeterminate` | | Rust | `ControlModeErrorKind` (behind the `control-mode` feature) | `DispatchTimedOut` (safe to retry) vs. plain `TimedOut` (not: the connection may have already committed the command) | | Python, Go's one-shot path | - | inspect the exit status after normal completion; an interrupted call needs separate state verification | | Go's control-mode pool | handled internally, not exposed | a failed pooled connection is retired rather than reused, rather than handing the caller a retry-safety flag to check | | Swift | documented, not typed | `ControlSession`'s own doc comment states the same rule in prose ("a command that never reached tmux is safe to retry") without a dedicated enum | A state such as `not_started`, `NotDispatched`, `NOT_DISPATCHED`, or `DispatchTimedOut` identifies a request that did not reach tmux. Treat unknown delivery as potentially executed. C++ and TypeScript also distinguish `written`, where the transport accepted the request but no terminal reply has arrived. For subprocess calls that complete normally, inspect the exit status. If a call is interrupted or times out without a delivery state, check tmux's resulting state before repeating a mutation.