# libtmux for Python > The Python port of libtmux (libtmux). Every code sample below is Python; the same pages exist for the other nine ports under their own prefix. - [Python API reference](https://libtmux.org/en/py/latest/reference/): every public symbol, generated from the source. Hosted on libtmux.org. --- # tmuxp load Source: https://libtmux.org/en/py/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. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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 \ -L workspace-guide \ -d \ 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 output 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. ## 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 proposed 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/py/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. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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 proposed 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 [proposed machine output](../../reference/output/) for the distinction between a saved file's format and a CLI result stream. ## 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 proposed 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/py/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. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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 proposed 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 proposed 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/py/latest/workspace/cli/edit/ > Resolve a saved workspace or file and open it in the configured editor. The lookup rules are shared with loading. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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 proposed 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 proposed 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/py/latest/workspace/cli/debug-info/ > Collect tmuxp, Python, tmux, configuration, and environment diagnostics for troubleshooting. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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 proposed 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 proposed 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/py/latest/workspace/cli/ls/ > List discovered project and saved workspace files, with optional grouping and configuration content. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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 proposed 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/py/latest/workspace/cli/search/ > Search discovered workspace fields using regular expressions or literal strings. Queries combine with AND unless `--any` selects OR. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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 proposed 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 proposed 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/py/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 future native command. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. Open a Python shell with tmux objects available, or evaluate Python using `-c`. This command remains Python-specific even when reached through a future native command. ## Evaluate with a selected server After the [installation walkthrough](../../guides/installation/) starts its dedicated server: ```console $ tmuxp shell \ -L workspace-guide \ -c 'print(server.sessions)' ``` 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. The compatibility proposal uses an optional version-checked Python bridge and reports an unsupported-runtime error if it is absent. Interactive machine output needs a separate terminal; an interactive transcript cannot share JSON stdout. Backend selectors are mutually exclusive. ## 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 proposed 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/py/latest/workspace/cli/import/ > Import a workspace from a supported external configuration format. Select one of the two child commands. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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 proposed 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/py/latest/workspace/cli/import-teamocil/ > Translate a Teamocil workspace into tmuxp configuration, review the result, and select its saved representation. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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 proposed 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/py/latest/workspace/cli/import-tmuxinator/ > Translate a tmuxinator workspace into tmuxp configuration, preserving an explicit boundary around dynamic Ruby configuration. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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 proposed 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/py/latest/workspace/cli/ > The tmuxp command tree, root options, and native compatibility scope. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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 proposed 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 proposed 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/py/latest/workspace/cli/completion/ > Generate completion from the Python parser and track native generator requirements. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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/). There is no native workspace executable to generate completions from in this prototype. The proposed parser choices provide different generators: Cobra has command-tree completion and documentation export, clap has completion and man-page companions, ArgumentParser has completion and documentation tools, picocli has code generation, and Commander, System.CommandLine, and CLI11 need their documented tooling or an explicit metadata adapter. 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 once implemented. Do not ship a static completion script for a guessed executable name. 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/py/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/py/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: ```python for session in server.sessions: print(session.session_name) for window in session.windows: print(" ", window.window_index, window.window_name) for pane in window.panes: print(" ", pane.pane_id, pane.pane_current_command) ``` ## 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/py/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. ```python import libtmux # One-shot: every call underneath this handle spawns a `tmux` process. server = libtmux.Server() session = server.new_session(session_name="work") session.active_window.active_pane.send_keys("echo hello") ``` 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/py/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: ```python >>> session.windows.filter(window_name__startswith='api') [Window(@... ...:api-server, Session($... ...))] >>> session.windows.filter(window_name__iregex=r'n?vim') ``` 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 >>> server.search_sessions(filter='#{==:#{session_name},alpha-1}') ``` 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: ## 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/py/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: ```python def create_dev_workspace(session, name='dev'): window = session.new_window(window_name=name, attach=False) window.resize(height=50, width=160) main_pane = window.active_pane terminal_pane = main_pane.split(size='30%') log_pane = terminal_pane.split(direction=PaneDirection.Right) return {'window': window, 'main': main_pane, 'terminal': terminal_pane, 'logs': log_pane} ``` `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: 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: ```python with session.new_window(window_name='temp-window') as temp_win: pane = temp_win.active_pane pane.send_keys('echo "temporary workspace"') # window is gone here, even if the block raised ``` 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/py/latest/workspace/configuration/ > Tmuxp workspace configuration, field meanings, defaults, and execution behavior. This page documents Python tmuxp configuration at the pinned reference revision. Use tmuxp for the command examples below. 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. ## 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/py/latest/workspace/configuration/session/ > Tmuxp session configuration, field meanings, defaults, and execution behavior. This page documents Python tmuxp configuration at the pinned reference revision. Use tmuxp for the command examples below. 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/). ## 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/py/latest/workspace/configuration/windows/ > Tmuxp window configuration, field meanings, defaults, and execution behavior. This page documents Python tmuxp configuration at the pinned reference revision. Use tmuxp for the command examples below. 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. ## 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/py/latest/workspace/configuration/panes/ > Tmuxp pane configuration, field meanings, defaults, and execution behavior. This page documents Python tmuxp configuration at the pinned reference revision. Use tmuxp for the command examples below. 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. ## 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/py/latest/workspace/configuration/commands/ > Tmuxp workspace commands, field meanings, defaults, and execution behavior. This page documents Python tmuxp configuration at the pinned reference revision. Use tmuxp for the command examples below. 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. ## 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/py/latest/workspace/configuration/environment/ > Tmuxp workspace environment, field meanings, defaults, and execution behavior. This page documents Python tmuxp configuration at the pinned reference revision. Use tmuxp for the command examples below. 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. ## 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/py/latest/workspace/configuration/directories/ > Tmuxp workspace files and directories, field meanings, defaults, and execution behavior. This page documents Python tmuxp configuration at the pinned reference revision. Use tmuxp for the command examples below. 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. ## 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/py/latest/workspace/configuration/layouts/ > Tmuxp workspace layouts and focus, field meanings, defaults, and execution behavior. This page documents Python tmuxp configuration at the pinned reference revision. Use tmuxp for the command examples below. 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. ## 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/py/latest/workspace/configuration/hooks/ > Tmuxp workspace hooks and builders, field meanings, defaults, and execution behavior. This page documents Python tmuxp configuration at the pinned reference revision. Use tmuxp for the command examples below. 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. ## 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/py/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/py/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. ```python >>> import libtmux >>> server = libtmux.Server() >>> session = server.new_session(session_name='demo') Session(...) >>> window = session.active_window >>> pane = window.split(shell='sh') >>> pane.capture_pane() ['$'] >>> pane.send_keys('echo "Hello world"', enter=True) >>> pane.capture_pane() ['$ echo "Hello world"', 'Hello world', '$'] ``` ## 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/py/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 ```python >>> pane = window.split(shell='sh') >>> pane.capture_pane() ['$'] >>> pane.send_keys('echo "Hello world"', enter=True) >>> pane.capture_pane() ['$ echo "Hello world"', 'Hello world', '$'] ``` 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`. ## 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. ```python >>> server.new_session(session_name='wait_test') Session(...) >>> server.wait_for('test_channel', set_flag=True) ``` `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." `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/py/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'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/py/latest/workspace/examples/gallery/ > Pinned YAML examples, JSON counterparts, and execution prerequisites. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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. The [installation walkthrough](../../guides/installation/) supplies a complete runnable starting file. ## 2-pane-synchronized Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/minimal.yaml). ```yaml session_name: My tmux session windows: - panes: - ``` ## options Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application 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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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 Pinned Python reference fixture. This docs run did not execute its full application environment. [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/py/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/py/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. ```python # pip install libtmux >>> import libtmux >>> server = libtmux.Server() >>> session = server.sessions[0] >>> window = session.active_window >>> pane = window.split(shell='sh') >>> pane.capture_pane() ['$'] >>> pane.send_keys('echo "Hello world"', enter=True) >>> pane.capture_pane() ['$ echo "Hello world"', 'Hello world', '$'] ``` ## 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/py/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`. ```python # Server() with no arguments talks to tmux's own default socket. # Server(socket_name=...) or Server(socket_path=...) pick a different one. server = libtmux.Server() # from_env() is also on Session, Window, and Pane, for code running inside a # pane that wants to ask "where am I" instead of being told. server = libtmux.Server.from_env() ``` 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. ```python # default only stands in for *absence*: an ambiguous match still raises # MultipleObjectsReturned even with a default supplied: handing back an # arbitrary match from several is how a script ends up driving the wrong # pane. See Filtering and queries. session = server.sessions.get(session_name="demo", default=None) if session is None: session = server.new_session(session_name="demo") ``` 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/py/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. ```python # literal=True disables tmux's key-name lookup; left at its default, a # string that happens to look like a key name is interpreted as one. pane.send_keys(cmd, literal=True) # enter defaults to True. Pass enter=False to type without submitting, then # press Enter yourself: the README's own example, to show the steps apart. pane.send_keys('echo hey', enter=False) pane.enter() ``` 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/py/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: ```python # 0 is the first visible line; positive numbers stay in the visible pane; # negative numbers reach into history; "-" means "the start of the # history." With no arguments you get the visible screen. >>> pane = window.split(shell='sh') >>> pane.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: 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: ```python >>> server.new_session(session_name='wait_test') Session(...) >>> server.wait_for('test_channel', set_flag=True) ``` 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: 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/py/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: 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 ```python # An i-prefixed lookup. session.windows.filter(window_name__istartswith="bg") ``` 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/py/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: ```python >>> def test_example(session: "Session") -> None: ... assert isinstance(session.name, str) ... assert session.name.startswith('libtmux_') ... window = session.new_window(window_name='new one') ... assert window.name == 'new one' ``` 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. `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/py/latest/workspace/guides/installation/ > Run the available Python workspace tool on a dedicated tmux socket. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. The runnable terminal loader is the separate Python application tmuxp. Its documented prerequisites are Python 3.10 or newer and tmux 3.2 or newer. Install it in an isolated tool environment with uv: ```console $ uv tool install tmuxp ``` The tool environment owns tmuxp's Python dependencies. Installing a native libtmux workspace library does not install a tmuxp-compatible CLI. ## Create the input Save this file as [`workspace.yaml`](./#create-the-input) in a writable directory: ```yaml session_name: workspace-guide windows: - window_name: editor layout: even-horizontal panes: - echo ready - echo second ``` Load the session detached. Reserve the socket name for this walkthrough: ```console $ tmuxp load \ -L workspace-guide \ -d \ workspace.yaml ``` Inspect the two panes: ```console $ tmux -L workspace-guide list-panes -t '=workspace-guide:editor' ``` Attach when ready: ```console $ tmux -L workspace-guide attach-session -t '=workspace-guide' ``` Detach with your configured tmux detach binding. Before cleanup, optionally try [export and reload](../export-session/). Remove only this walkthrough's session when finished: ```console $ tmux -L workspace-guide kill-session -t '=workspace-guide' ``` ## Continue [Discovery](../discovery/) explains project files and saved names. [Configuration](../../configuration/) describes accepted fields, and [load](../../cli/load/) documents all flags. The [compatibility reference](../../reference/compatibility/) records native builder limitations. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Find saved workspaces Source: https://libtmux.org/en/py/latest/workspace/guides/discovery/ > Resolve project files, explicit paths, and global workspace names. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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/py/latest/workspace/guides/automation/ > Python automation and the local native CLI machine protocol. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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/py/latest/workspace/guides/export-session/ > Capture, inspect, and replay a workspace without assuming lossless recovery. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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/py/latest/workspace/guides/troubleshooting/ > Diagnose argument, discovery, configuration, shell, and partial-build failures. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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 proposed machine contract reports completed and failed stages explicitly. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Python workspace internals Source: https://libtmux.org/en/py/latest/workspace/internals/ > How tmuxp loads configuration and builds sessions through libtmux. These pages describe tmuxp's internal implementation for contributors and extension authors. The Python interfaces have no stability guarantee and may change between releases. Use [tmuxp load](../guides/) to launch a workspace from the terminal. ## Builder pipeline The CLI reads YAML or JSON, expands shorthand and variables, applies inherited defaults, and passes the result to a workspace builder. The builder uses libtmux to create the session, windows, and panes. The CLI then handles attachment or client switching. - [Topics](./topics/) explain the loader pipeline and builder extension points. - [Examples](./examples/) show expansion and building on an isolated server. - [API](../reference/) links the internal loading, building, and freezing interfaces. The upstream [Internals documentation](https://tmuxp.git-pull.com/internals/) contains the full architecture and module reference. Use the [libtmux Python API](../../reference/) for general tmux programming. --- # Python workspace builder behavior Source: https://libtmux.org/en/py/latest/workspace/internals/topics/ > tmuxp's internal configuration pipeline, builder selection, and session handling. The CLI separates configuration processing, construction, and attachment. These Python implementation interfaces have no stability guarantee. ## Expand before building The loader reads YAML or JSON, expands command shorthand, variables, and paths, then applies inherited defaults. The builder expects the expanded configuration. The [internal example](../examples/) shows this sequence with an isolated libtmux server. ## Select a builder `ClassicWorkspaceBuilder` is the default builder. `workspace_builder` selects an importable class or a registered entry point; `workspace_builder_paths` adds explicitly configured import directories. Plugins and custom builders run inside the Python process. Those extension imports are not portable workspace data for the other language ports. The classic builder accepts an optional existing session and an append choice. Failure handling depends on the operation and CLI path; a workspace build does not have universal transactional rollback. The CLI owns its existing-session prompts and the attachment or client-switching workflow. See the upstream [custom builder guide](https://tmuxp.git-pull.com/topics/custom-workspace-builders/) for extension configuration and the [API](../../reference/) for interface contracts. [Configuration loader](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [Classic builder](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py). --- # Python workspace builder example Source: https://libtmux.org/en/py/latest/workspace/internals/examples/ > An isolated example of tmuxp internal configuration expansion and session building. This example demonstrates tmuxp's internal builder pipeline. These Python interfaces have no stability guarantee. Load workspace files through the [CLI examples](../../examples/) for normal use. ## Build from Python data Run this inside an environment containing tmuxp and its compatible libtmux dependency. The example expands shorthand and inherited defaults before calling the classic builder, then removes its dedicated server. ```python from uuid import uuid4 import libtmux from tmuxp.workspace import loader from tmuxp.workspace.builder import WorkspaceBuilder config = { "session_name": "workspace-example", "windows": [ {"window_name": "editor", "panes": ["echo ready", "echo ready"]} ], } expanded = loader.trickle(loader.expand(config)) server = libtmux.Server(socket_name=f"workspace-{uuid4().hex}") try: builder = WorkspaceBuilder(session_config=expanded, server=server) builder.build() print(builder.session.name) print(len(builder.session.windows)) finally: server.kill() ``` The builder stores the resulting session on `ClassicWorkspaceBuilder.session`. Its `build` method does not return that session as the return value. The code uses a new socket for each run so cleanup cannot select a normal user server. ## Further reading The example combines the loader sequence used by the CLI with the classic builder's documented API. The upstream builder tests exercise its expanded configuration contract, and freezer tests cover reading live sessions back into configuration. Read the upstream [configuration examples](https://tmuxp.git-pull.com/configuration/examples/) for focus, layouts, directories, environment values, and command shorthand. Those examples depend on their commands and paths; inspect each file before loading it on your own server. [Builder examples and contract](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [Builder tests](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/tests/workspace/test_builder.py). --- # Python workspace internal API Source: https://libtmux.org/en/py/latest/workspace/reference/ > tmuxp APIs for expanding configuration, building sessions, and exporting layouts. tmuxp's workspace APIs are internal implementation details with no stability guarantee. They use libtmux's `Server`, `Session`, `Window`, and `Pane` objects for tmux control. Use the [CLI guide](../../guides/) to load workspace files. ## Load and validate `tmuxp.workspace.loader.expand` normalizes shorthand, variables, and paths. `loader.trickle` applies inherited configuration after expansion. Both operate on workspace dictionaries. `validation.validate_schema` checks required structure; it is not a complete machine-readable schema of every runtime behavior. Consult the upstream [loader API](https://tmuxp.git-pull.com/internals/api/workspace/loader/) and [validation API](https://tmuxp.git-pull.com/internals/api/workspace/validation/) for parameter and error details. ## Build and extend `tmuxp.workspace.builder.WorkspaceBuilder` is the compatibility alias for `ClassicWorkspaceBuilder`. Construct it with expanded `session_config` and a libtmux server, call `build`, then read `ClassicWorkspaceBuilder.session`. `WorkspaceBuilderProtocol` defines the interface used by the CLI, including construction callbacks, building into an optional existing session, and session discovery. The registry resolves named entry points or import paths. Use the upstream [builder API](https://tmuxp.git-pull.com/internals/api/workspace/builder/) and [custom builder guide](https://tmuxp.git-pull.com/topics/custom-workspace-builders/) when implementing an extension. ## Export and CLI `tmuxp.workspace.freezer.freeze(session)` reads a live session into a configuration dictionary. `freezer.inline` compacts that expanded structure for a file. The [freezer API](https://tmuxp.git-pull.com/internals/api/workspace/freezer/) documents both operations. The [CLI reference](https://tmuxp.git-pull.com/cli/) covers loading, freezing, listing, searching, editing, importing, and converting workspaces. These are application commands, separate from the language-level workspace builder. [Public builder exports](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/__init__.py) ## API declarations - [tmuxp.exc.ActiveSessionMissingWorkspaceException](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-activesessionmissingworkspaceexception/) - [tmuxp.workspace.builder.available_builders](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-builder-available_builders/) - [tmuxp.exc.BeforeLoadScriptError](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-beforeloadscripterror/) - [tmuxp.exc.BeforeLoadScriptNotExists](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-beforeloadscriptnotexists/) - [tmuxp.workspace.builder.ClassicWorkspaceBuilder](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-builder-classicworkspacebuilder/) - [tmuxp.cli.cli](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-cli/) - [tmuxp.cli.CLI_DESCRIPTION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-cli_description/) - [tmuxp.cli.debug_info.CLIDebugInfoNamespace](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-debug_info-clidebuginfonamespace/) - [tmuxp.cli.freeze.CLIFreezeNamespace](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-freeze-clifreezenamespace/) - [tmuxp.cli.load.CLILoadNamespace](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-load-cliloadnamespace/) - [tmuxp.cli.ls.CLILsNamespace](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-ls-clilsnamespace/) - [tmuxp.cli.CLINamespace](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-clinamespace/) - [tmuxp.cli.search.CLISearchNamespace](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-clisearchnamespace/) - [tmuxp.cli.shell.CLIShellNamespace](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-shell-clishellnamespace/) - [tmuxp.workspace.builder.classic.COLUMNS_FALLBACK](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-builder-classic-columns_fallback/) - [tmuxp.cli.convert.command_convert](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-convert-command_convert/) - [tmuxp.cli.debug_info.command_debug_info](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-debug_info-command_debug_info/) - [tmuxp.cli.edit.command_edit](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-edit-command_edit/) - [tmuxp.cli.freeze.command_freeze](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-freeze-command_freeze/) - [tmuxp.cli.import_config.command_import](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-import_config-command_import/) - [tmuxp.cli.import_config.command_import_teamocil](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-import_config-command_import_teamocil/) - [tmuxp.cli.import_config.command_import_tmuxinator](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-import_config-command_import_tmuxinator/) - [tmuxp.cli.load.command_load](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-load-command_load/) - [tmuxp.cli.ls.command_ls](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-ls-command_ls/) - [tmuxp.cli.search.command_search](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-command_search/) - [tmuxp.cli.shell.command_shell](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-shell-command_shell/) - [tmuxp.cli.search.compile_search_patterns](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-compile_search_patterns/) - [tmuxp.plugin.Config](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-plugin-config/) - [tmuxp.cli.convert.CONVERT_DESCRIPTION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-convert-convert_description/) - [tmuxp.cli.convert.ConvertUnknownFileType](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-convert-convertunknownfiletype/) - [tmuxp.cli.convert.create_convert_subparser](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-convert-create_convert_subparser/) - [tmuxp.cli.debug_info.create_debug_info_subparser](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-debug_info-create_debug_info_subparser/) - [tmuxp.cli.edit.create_edit_subparser](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-edit-create_edit_subparser/) - [tmuxp.cli.freeze.create_freeze_subparser](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-freeze-create_freeze_subparser/) - [tmuxp.cli.import_config.create_import_subparser](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-import_config-create_import_subparser/) - [tmuxp.cli.load.create_load_subparser](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-load-create_load_subparser/) - [tmuxp.cli.ls.create_ls_subparser](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-ls-create_ls_subparser/) - [tmuxp.cli.create_parser](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-create_parser/) - [tmuxp.cli.search.create_search_subparser](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-create_search_subparser/) - [tmuxp.cli.shell.create_shell_subparser](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-shell-create_shell_subparser/) - [tmuxp.cli.debug_info.DEBUG_INFO_DESCRIPTION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-debug_info-debug_info_description/) - [tmuxp.log.debug_log_template](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-log-debug_log_template/) - [tmuxp.log.DebugLogFormatter](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-log-debuglogformatter/) - [tmuxp.plugin.DEFAULT_CONFIG](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-plugin-default_config/) - [tmuxp.cli.search.DEFAULT_FIELDS](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-default_fields/) - [tmuxp.shell.detect_best_shell](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-detect_best_shell/) - [tmuxp.cli.edit.EDIT_DESCRIPTION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-edit-edit_description/) - [tmuxp.exc.EmptyWorkspaceException](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-emptyworkspaceexception/) - [tmuxp.cli.search.evaluate_match](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-evaluate_match/) - [tmuxp.workspace.loader.expand](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-loader-expand/) - [tmuxp.workspace.loader.expand_cmd](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-loader-expand_cmd/) - [tmuxp.workspace.loader.expandshell](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-loader-expandshell/) - [tmuxp.cli.search.extract_workspace_fields](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-extract_workspace_fields/) - [tmuxp.cli.search.FIELD_ALIASES](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-field_aliases/) - [tmuxp.workspace.finders.find_local_workspace_files](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-finders-find_local_workspace_files/) - [tmuxp.cli.search.find_search_matches](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-find_search_matches/) - [tmuxp.workspace.finders.find_workspace_file](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-finders-find_workspace_file/) - [tmuxp.workspace.freezer.freeze](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-freezer-freeze/) - [tmuxp.cli.freeze.FREEZE_DESCRIPTION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-freeze-freeze_description/) - [tmuxp.shell.get_bpython](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-get_bpython/) - [tmuxp.shell.get_code](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-get_code/) - [tmuxp.util.get_current_pane](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-util-get_current_pane/) - [tmuxp.workspace.builder.get_default_columns](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-builder-get_default_columns/) - [tmuxp.workspace.builder.get_default_rows](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-builder-get_default_rows/) - [tmuxp.shell.get_ipython](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-get_ipython/) - [tmuxp.shell.get_ipython_arguments](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-get_ipython_arguments/) - [tmuxp.shell.get_launch_args](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-get_launch_args/) - [tmuxp.util.get_pane](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-util-get_pane/) - [tmuxp.shell.get_ptipython](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-get_ptipython/) - [tmuxp.shell.get_ptpython](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-get_ptpython/) - [tmuxp.util.get_session](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-util-get_session/) - [tmuxp.cli.import_config.get_teamocil_dir](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-import_config-get_teamocil_dir/) - [tmuxp.cli.import_config.get_tmuxinator_dir](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-import_config-get_tmuxinator_dir/) - [tmuxp.util.get_window](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-util-get_window/) - [tmuxp.workspace.finders.get_workspace_dir](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-finders-get_workspace_dir/) - [tmuxp.workspace.finders.get_workspace_dir_candidates](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-finders-get_workspace_dir_candidates/) - [tmuxp.shell.has_bpython](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-has_bpython/) - [tmuxp.shell.has_ipython](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-has_ipython/) - [tmuxp.shell.has_ptipython](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-has_ptipython/) - [tmuxp.shell.has_ptpython](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-has_ptpython/) - [tmuxp.cli.search.highlight_matches](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-highlight_matches/) - [tmuxp.cli.import_config.import_config](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-import_config-import_config/) - [tmuxp.cli.import_config.IMPORT_DESCRIPTION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-import_config-import_description/) - [tmuxp.workspace.importers.import_teamocil](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-importers-import_teamocil/) - [tmuxp.workspace.importers.import_tmuxinator](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-importers-import_tmuxinator/) - [tmuxp.cli.import_config.ImportConfigFn](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-import_config-importconfigfn/) - [tmuxp.workspace.finders.in_cwd](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-finders-in_cwd/) - [tmuxp.workspace.finders.in_dir](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-finders-in_dir/) - [tmuxp.workspace.freezer.inline](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-freezer-inline/) - [tmuxp.cli.search.InvalidFieldError](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-invalidfielderror/) - [tmuxp.workspace.validation.InvalidPluginsValidationError](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-validation-invalidpluginsvalidationerror/) - [tmuxp.exc.InvalidWorkspaceBuilder](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-invalidworkspacebuilder/) - [tmuxp.exc.InvalidWorkspaceBuilderOption](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-invalidworkspacebuilderoption/) - [tmuxp.workspace.finders.is_pure_name](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-finders-is_pure_name/) - [tmuxp.workspace.finders.is_workspace_file](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-finders-is_workspace_file/) - [tmuxp.shell.launch](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-shell-launch/) - [tmuxp.log.LEVEL_COLORS](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-log-level_colors/) - [tmuxp.plugin.LIBTMUX_MAX_VERSION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-plugin-libtmux_max_version/) - [tmuxp.plugin.LIBTMUX_MIN_VERSION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-plugin-libtmux_min_version/) - [tmuxp.cli.load.LOAD_DESCRIPTION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-load-load_description/) - [tmuxp.cli.load.load_plugins](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-load-load_plugins/) - [tmuxp.cli.load.load_workspace](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-load-load_workspace/) - [tmuxp.workspace.finders.LOCAL_WORKSPACE_FILES](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-finders-local_workspace_files/) - [tmuxp.log.LogFormatter](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-log-logformatter/) - [tmuxp.cli.ls.LS_DESCRIPTION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-ls-ls_description/) - [tmuxp.cli.search.normalize_fields](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-normalize_fields/) - [tmuxp.cli.ns](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-ns/) - [tmuxp.util.oh_my_zsh_auto_title](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-util-oh_my_zsh_auto_title/) - [tmuxp.exc.PaneNotFound](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-panenotfound/) - [tmuxp.workspace.options.PaneReadiness](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-options-panereadiness/) - [tmuxp.cli.search.parse_query_terms](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-parse_query_terms/) - [tmuxp.workspace.builder.prepended_sys_path](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-builder-prepended_sys_path/) - [tmuxp.cli.utils.prompt](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-utils-prompt/) - [tmuxp.cli.utils.prompt_bool](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-utils-prompt_bool/) - [tmuxp.cli.utils.prompt_choices](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-utils-prompt_choices/) - [tmuxp.cli.utils.prompt_yes_no](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-utils-prompt_yes_no/) - [tmuxp.util.PY2](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-util-py2/) - [tmuxp.workspace.builder.resolve_builder_class](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-builder-resolve_builder_class/) - [tmuxp.workspace.builder.resolve_builder_paths](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-builder-resolve_builder_paths/) - [tmuxp.workspace.options.resolve_session_shell](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-options-resolve_session_shell/) - [tmuxp.workspace.builder.classic.ROWS_FALLBACK](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-builder-classic-rows_fallback/) - [tmuxp.util.run_before_script](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-util-run_before_script/) - [tmuxp.workspace.validation.SchemaValidationError](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-validation-schemavalidationerror/) - [tmuxp.cli.search.SEARCH_DESCRIPTION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-search_description/) - [tmuxp.cli.search.SearchPattern](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-searchpattern/) - [tmuxp.cli.search.SearchToken](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-searchtoken/) - [tmuxp.exc.SessionMissingWorkspaceException](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-sessionmissingworkspaceexception/) - [tmuxp.workspace.validation.SessionNameMissingValidationError](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-validation-sessionnamemissingvalidationerror/) - [tmuxp.exc.SessionNotFound](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-sessionnotfound/) - [tmuxp.log.set_style](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-log-set_style/) - [tmuxp.log.setup_log_file](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-log-setup_log_file/) - [tmuxp.cli.setup_logger](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-setup_logger/) - [tmuxp.plugin.setup_plugin_config](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-plugin-setup_plugin_config/) - [tmuxp.cli.shell.SHELL_DESCRIPTION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-shell-shell_description/) - [tmuxp.workspace.options.shell_is_zsh](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-options-shell_is_zsh/) - [tmuxp.cli.startup](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-startup/) - [tmuxp.types.StrPath](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-types-strpath/) - [tmuxp.plugin.TMUX_MAX_VERSION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-plugin-tmux_max_version/) - [tmuxp.plugin.TMUX_MIN_VERSION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-plugin-tmux_min_version/) - [tmuxp.log.tmuxp_echo](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-log-tmuxp_echo/) - [tmuxp.plugin.TMUXP_MAX_VERSION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-plugin-tmuxp_max_version/) - [tmuxp.plugin.TMUXP_MIN_VERSION](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-plugin-tmuxp_min_version/) - [tmuxp.cli.debug_info.tmuxp_path](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-debug_info-tmuxp_path/) - [tmuxp.exc.TmuxpException](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-tmuxpexception/) - [tmuxp.log.TmuxpLoggerAdapter](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-log-tmuxploggeradapter/) - [tmuxp.plugin.TmuxpPlugin](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-plugin-tmuxpplugin/) - [tmuxp.exc.TmuxpPluginException](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-tmuxppluginexception/) - [tmuxp.workspace.loader.trickle](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-loader-trickle/) - [tmuxp.cli.search.VALID_FIELDS](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-valid_fields/) - [tmuxp.workspace.constants.VALID_WORKSPACE_DIR_FILE_EXTENSIONS](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-constants-valid_workspace_dir_file_extensions/) - [tmuxp.plugin.validate_plugin_config](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-plugin-validate_plugin_config/) - [tmuxp.workspace.validation.validate_schema](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-validation-validate_schema/) - [tmuxp.workspace.validation.WindowListMissingValidationError](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-validation-windowlistmissingvalidationerror/) - [tmuxp.workspace.validation.WindowNameMissingValidationError](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-validation-windownamemissingvalidationerror/) - [tmuxp.exc.WindowNotFound](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-windownotfound/) - [tmuxp.workspace.builder.WORKSPACE_BUILDERS_GROUP](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-builder-workspace_builders_group/) - [tmuxp.workspace.builder.WorkspaceBuilder](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-builder-workspacebuilder/) - [tmuxp.exc.WorkspaceBuilderError](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-workspacebuildererror/) - [tmuxp.exc.WorkspaceBuilderImportError](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-workspacebuilderimporterror/) - [tmuxp.exc.WorkspaceBuilderNotFound](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-workspacebuildernotfound/) - [tmuxp.workspace.options.WorkspaceBuilderOptions](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-options-workspacebuilderoptions/) - [tmuxp.exc.WorkspaceBuilderPathError](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-workspacebuilderpatherror/) - [tmuxp.workspace.builder.WorkspaceBuilderProtocol](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-workspace-builder-workspacebuilderprotocol/) - [tmuxp.exc.WorkspaceError](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-exc-workspaceerror/) - [tmuxp.cli.search.WorkspaceFields](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-workspacefields/) - [tmuxp.cli.ls.WorkspaceInfo](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-ls-workspaceinfo/) - [tmuxp.cli.search.WorkspaceSearchResult](https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-workspacesearchresult/) --- # Workspace reference generation Source: https://libtmux.org/en/py/latest/workspace/internals/documentation/ > Keep help, completion, and site references aligned with command metadata. This page describes the documentation integration for Python. Native CLI references remain compatibility targets until an installed command exists. ## Command metadata tmuxp uses argparse metadata from create_parser for its upstream Sphinx CLI reference. Its separately installed shtab integration consumes that parser for completion. The documentation adapter must preserve required groups rather than infer arity from optional-looking help. The [command reference](../../cli/) covers the Python grammar. A future native export must 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. This local research prototype is reviewed against current source. ## 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/py/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. --- # MCP for Python Source: https://libtmux.org/en/py/latest/mcp/ > Expose tmux tools, resources, and prompts through Python's libtmux-mcp server. `libtmux-mcp` lets an MCP client inspect tmux, create sessions and panes, run commands, and wait for their results. The distribution and executable are `libtmux-mcp`; Python imports use `libtmux_mcp`. The server runs over standard input and output. It requires Python 3.10 or newer and tmux 3.2a or newer. Its default toolsets are `inspect`, `manage`, and `execute`; deletion tools require an explicit selection. ## Start here - [Install](#install) points an MCP client at this server. - [Tools](./tools/) lists the MCP operations, arguments, and results. - [Guides](./guides/) connect a client and select a tmux socket. - [Topics](./topics/) explain toolsets, trust, waiting, and caller context. - [Examples](./examples/) call a tool, then explore server internals. - [Language API](./reference/) documents embedding and implementation types. For declarative session configuration, use the [Workspace Manager](../workspace/), provided by the separate `tmuxp` project. The MCP server does not provide a tmuxp file loader. The [upstream Python documentation](https://libtmux-mcp.git-pull.com/) covers client integrations and the complete tool reference. These pages follow the [Python implementation](https://github.com/tmux-python/libtmux-mcp/tree/v0.1.0a22). --- # Python MCP topics Source: https://libtmux.org/en/py/latest/mcp/topics/ > Understand Python toolsets, socket overrides, terminal output, and command completion. Python's MCP toolsets select advertised and callable tools. They do not confine the programs running inside tmux. ## Select independent toolsets `inspect` requests state or output. `manage` changes tmux structure, presentation, or coordination without supplying executable input. `execute` starts processes, sends input, or changes executable configuration. `teardown` removes objects or retained history. The default is `inspect,manage,execute`. `LIBTMUX_TOOLS` adds exact names, and `LIBTMUX_EXCLUDE_TOOLS` removes them last. Unknown names fail startup. An empty `LIBTMUX_TOOLSETS` selects no sets. These settings filter tools only. Hierarchy resources and native prompts remain available even with no tools. Resource reads contact tmux; native prompts return text without contacting it. ## Know the endpoint and caller `LIBTMUX_SOCKET` selects a default socket name. Targeted tools can accept `socket_name` to override it for one call. This differs from servers that pin every call to one endpoint. Dedicated teardown tools refuse the pane containing the MCP process and its enclosing window, session, or server. The check compares socket identity as well as `TMUX_PANE`. It cannot turn open-ended shell input into a constrained operation. ## Choose the completion signal Use `run_command` for a command the agent authors. Read `exit_status`, `timed_out`, and `output`; a timeout does not prove the shell stopped. Use `wait_for_text` for output produced elsewhere and `capture_since` for repeated observation with an opaque cursor. The default wait ceiling is 30 seconds, configurable within 1 to 120 seconds. Oversized requests are clamped. Command-history suppression defaults on for MCP calls to `run_command`, while direct Python calls default it off; this is best-effort shell behavior. ## Treat terminal output as data A private socket separates tmux objects. It does not restrict filesystem, network, or same-user process access. Server aliases and hooks can add effects even to a nominal inspection. Pane output can contain credentials or instructions from another program; it remains untrusted data. See the [upstream trust model](https://libtmux-mcp.git-pull.com/topics/trust/) and [configuration source](https://github.com/tmux-python/libtmux-mcp/blob/v0.1.0a22/docs/configuration.md). --- # Connect a Python MCP client Source: https://libtmux.org/en/py/latest/mcp/guides/ > Launch libtmux-mcp on a chosen socket and verify the tools your client receives. Configure the client to launch `libtmux-mcp` with a named tmux socket. Install [uv](https://docs.astral.sh/uv/) first, and make tmux 3.2a or newer available in the environment the client passes to the server. ## Launch the server This command resolves the package in its own uv environment and starts its stdio transport. It waits for an MCP client to send requests. ```console $ LIBTMUX_SOCKET=docs-agent LIBTMUX_TOOLSETS=inspect \ uvx libtmux-mcp@latest ``` For a client that accepts an `mcpServers` object, use: ```json { "mcpServers": { "tmux-python": { "command": "uvx", "args": ["libtmux-mcp@latest"], "env": { "LIBTMUX_SOCKET": "docs-agent", "LIBTMUX_TOOLSETS": "inspect" } } } } ``` Client configuration formats differ; the [upstream client guide](https://libtmux-mcp.git-pull.com/clients/) gives their individual formats. Restart the MCP process after changing startup environment variables. ## Verify the connection Ask the client to list the tools, then call `list_sessions`. Ask it to identify a pane before capturing content. An empty session listing can be correct for the selected socket. To allow commands and topology creation, change the toolset selection to `inspect,manage,execute`. Check the new listing after reconnecting. Removing `teardown` does not stop a shell command from deleting work. ## Diagnose a mismatch Compare the client's executable path and environment with your shell. `LIBTMUX_TMUX_BIN` selects the tmux executable. `LIBTMUX_SOCKET_PATH` selects an explicit socket path. A targeted tool's `socket_name` argument can override the default endpoint. Keep this application in its own Python environment when also using [tmuxp](../../workspace/); the projects have independent libtmux dependency requirements. [Configuration contract](https://github.com/tmux-python/libtmux-mcp/blob/v0.1.0a22/docs/configuration.md). --- # Python MCP examples Source: https://libtmux.org/en/py/latest/mcp/examples/ > List sessions through the Python 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. Use a FastMCP client to inspect the same registered server that the `libtmux-mcp` executable serves. This checks the advertised contract without creating tmux sessions. ### Inspect the catalog in Python Run this in an environment containing `libtmux-mcp`. It uses the production factory and closes the in-process client when the context ends. ```python import asyncio from fastmcp import Client from libtmux_mcp.server import build_mcp_server async def main() -> None: async with Client(build_mcp_server()) as client: tools = await client.list_tools() for tool in tools: print(tool.name, tool.input_schema) asyncio.run(main()) ``` The [production factory](https://github.com/tmux-python/libtmux-mcp/blob/v0.1.0a22/src/libtmux_mcp/server.py) registers tools and applies visibility once. The [server tests](https://github.com/tmux-python/libtmux-mcp/blob/v0.1.0a22/tests/test_server.py) exercise this client/factory pattern. Set selection variables before importing the server in a fresh process. ### Run and observe through a client On a disposable session, ask the client to create a pane, run `printf 'ready\n'` with `run_command`, and report its typed exit status. For subsequent output, seed `capture_since` and reuse the returned cursor. Use `wait_for_text` when waiting for output from a process you did not launch. These are workflows for the client to carry out, not literal tool argument objects. Use the [tool reference](../tools/) for each operation's schema. The [upstream quickstart](https://libtmux-mcp.git-pull.com/quickstart/) describes command completion and lower-level channel composition. --- # Python MCP API Source: https://libtmux.org/en/py/latest/mcp/reference/ > Find Python server entry points, typed models, and the separate MCP wire contract. For MCP client requests, use the [tool reference](../tools/). This page covers language APIs for embedding or extending the server. The Python API and MCP protocol expose different interfaces. Python callers import functions and models; MCP clients send registered tool names and schema-validated arguments. ## MCP operations Browse the [tool reference](../tools/) for the protocol catalog. `tools/list` on a running server is the effective selection after startup filtering. Registration defaults can differ from direct Python function defaults, including command-history suppression. The server also registers hierarchy resources and workflow prompts. These remain available independently of the toolset selection. ## Python entry points `libtmux_mcp.server.build_mcp_server()` returns the registered production FastMCP server. `run_server()` serves it over stdio. The factory uses the module's server instance; repeated calls do not create independent configuration contexts. The package separates tool functions, typed models, middleware, resource handlers, and prompt recipes. Model classes describe request/result data; their existence does not make them separate MCP tools. - [Server API](https://libtmux-mcp.git-pull.com/reference/api/server/) - [Tools API](https://libtmux-mcp.git-pull.com/reference/api/tools/) - [Models API](https://libtmux-mcp.git-pull.com/reference/api/models/) - [Registration source](https://github.com/tmux-python/libtmux-mcp/blob/v0.1.0a22/src/libtmux_mcp/server.py) Use [Examples](../examples/) to inspect the protocol with an in-process client. Use the [Workspace builder API](../../workspace/reference/) for tmuxp configuration and builders. ## API declarations - [libtmux_mcp.middleware.AuditMiddleware](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-middleware-auditmiddleware/) - [libtmux_mcp.models.BufferContent](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-buffercontent/) - [libtmux_mcp.models.BufferRef](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-bufferref/) - [libtmux_mcp.prompts.recipes.build_dev_workspace](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-prompts-recipes-build_dev_workspace/) - [libtmux_mcp.server.build_mcp_server](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-server-build_mcp_server/) - [libtmux_mcp.tools.batch_tools.call_read_tools_batch](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-batch_tools-call_read_tools_batch/) - [libtmux_mcp.tools.pane_tools.io.CAPTURE_DEFAULT_MAX_LINES](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-io-capture_default_max_lines/) - [libtmux_mcp.tools.pane_tools.capture_pane](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-capture_pane/) - [libtmux_mcp.tools.pane_tools.capture_since](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-capture_since/) - [libtmux_mcp.tools.pane_tools.capture_since.CAPTURE_SINCE_DEFAULT_MAX_BYTES](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-capture_since-capture_since_default_max_bytes/) - [libtmux_mcp.tools.pane_tools.capture_since.CAPTURE_SINCE_DEFAULT_MAX_LINES](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-capture_since-capture_since_default_max_lines/) - [libtmux_mcp.models.CaptureSinceResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-capturesinceresult/) - [libtmux_mcp.tools.pane_tools.clear_pane](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-clear_pane/) - [libtmux_mcp.tools.server_tools.create_session](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-server_tools-create_session/) - [libtmux_mcp.tools.session_tools.create_window](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-session_tools-create_window/) - [libtmux_mcp.middleware.DEFAULT_RESPONSE_LIMIT_BYTES](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-middleware-default_response_limit_bytes/) - [libtmux_mcp.server.DEFAULT_TOOLSETS](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-server-default_toolsets/) - [libtmux_mcp.tools.buffer_tools.delete_buffer](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-buffer_tools-delete_buffer/) - [libtmux_mcp.prompts.recipes.diagnose_failing_pane](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-prompts-recipes-diagnose_failing_pane/) - [libtmux_mcp.tools.pane_tools.display_message](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-display_message/) - [libtmux_mcp.tools.pane_tools.enter_copy_mode](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-enter_copy_mode/) - [libtmux_mcp.prompts.ENV_PROMPTS_AS_TOOLS](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-prompts-env_prompts_as_tools/) - [libtmux_mcp.models.EnvironmentResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-environmentresult/) - [libtmux_mcp.models.EnvironmentSetResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-environmentsetresult/) - [libtmux_mcp.tools.pane_tools.exit_copy_mode](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-exit_copy_mode/) - [libtmux_mcp.tools.pane_tools.find_pane_by_position](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-find_pane_by_position/) - [libtmux_mcp.tools.pane_tools.get_pane_info](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-get_pane_info/) - [libtmux_mcp.tools.server_tools.get_server_info](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-server_tools-get_server_info/) - [libtmux_mcp.tools.session_tools.get_session_info](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-session_tools-get_session_info/) - [libtmux_mcp.tools.window_tools.get_window_info](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-window_tools-get_window_info/) - [libtmux_mcp.tools.pane_tools.state.HISTORY_LIMIT_FORMAT](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-state-history_limit_format/) - [libtmux_mcp.models.HookEntry](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-hookentry/) - [libtmux_mcp.models.HookListResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-hooklistresult/) - [libtmux_mcp.middleware.install_fastmcp_validation_log_filter](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-middleware-install_fastmcp_validation_log_filter/) - [libtmux_mcp.prompts.recipes.interrupt_gracefully](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-prompts-recipes-interrupt_gracefully/) - [libtmux_mcp.tools.pane_tools.kill_pane](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-kill_pane/) - [libtmux_mcp.tools.server_tools.kill_server](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-server_tools-kill_server/) - [libtmux_mcp.tools.session_tools.kill_session](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-session_tools-kill_session/) - [libtmux_mcp.tools.window_tools.kill_window](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-window_tools-kill_window/) - [libtmux_mcp.tools.window_tools.list_panes](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-window_tools-list_panes/) - [libtmux_mcp.tools.server_tools.list_servers](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-server_tools-list_servers/) - [libtmux_mcp.tools.server_tools.list_sessions](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-server_tools-list_sessions/) - [libtmux_mcp.tools.session_tools.list_windows](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-session_tools-list_windows/) - [libtmux_mcp.tools.buffer_tools.load_buffer](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-buffer_tools-load_buffer/) - [libtmux_mcp.main](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-main/) - [libtmux_mcp.tools.batch_tools.MAX_BATCH_OPERATIONS](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-batch_tools-max_batch_operations/) - [libtmux_mcp.server.mcp](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-server-mcp/) - [libtmux_mcp.tools.window_tools.move_window](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-window_tools-move_window/) - [libtmux_mcp.models.OptionResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-optionresult/) - [libtmux_mcp.models.OptionSetResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-optionsetresult/) - [libtmux_mcp.tools.pane_tools.state.PANE_STATE_FORMAT](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-state-pane_state_format/) - [libtmux_mcp.models.PaneContentMatch](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-panecontentmatch/) - [libtmux_mcp.tools.pane_tools.lifecycle.PaneCorner](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-lifecycle-panecorner/) - [libtmux_mcp.models.PaneInfo](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-paneinfo/) - [libtmux_mcp.models.PaneSnapshot](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-panesnapshot/) - [libtmux_mcp.tools.buffer_tools.paste_buffer](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-buffer_tools-paste_buffer/) - [libtmux_mcp.tools.pane_tools.paste_text](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-paste_text/) - [libtmux_mcp.tools.pane_tools.pipe_pane](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-pipe_pane/) - [libtmux_mcp.resources.hierarchy.register](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-resources-hierarchy-register/) - [libtmux_mcp.tools.batch_tools.register](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-batch_tools-register/) - [libtmux_mcp.tools.buffer_tools.register](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-buffer_tools-register/) - [libtmux_mcp.tools.env_tools.register](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-env_tools-register/) - [libtmux_mcp.tools.hook_tools.register](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-hook_tools-register/) - [libtmux_mcp.tools.option_tools.register](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-option_tools-register/) - [libtmux_mcp.tools.pane_tools.register](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-register/) - [libtmux_mcp.tools.server_tools.register](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-server_tools-register/) - [libtmux_mcp.tools.session_tools.register](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-session_tools-register/) - [libtmux_mcp.tools.wait_for_tools.register](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-wait_for_tools-register/) - [libtmux_mcp.tools.window_tools.register](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-window_tools-register/) - [libtmux_mcp.resources.hierarchy.register_completions](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-resources-hierarchy-register_completions/) - [libtmux_mcp.prompts.register_prompts](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-prompts-register_prompts/) - [libtmux_mcp.resources.register_resources](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-resources-register_resources/) - [libtmux_mcp.tools.register_tools](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-register_tools/) - [libtmux_mcp.tools.session_tools.rename_session](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-session_tools-rename_session/) - [libtmux_mcp.tools.window_tools.rename_window](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-window_tools-rename_window/) - [libtmux_mcp.tools.pane_tools.resize_pane](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-resize_pane/) - [libtmux_mcp.tools.window_tools.resize_window](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-window_tools-resize_window/) - [libtmux_mcp.tools.pane_tools.respawn_pane](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-respawn_pane/) - [libtmux_mcp.prompts.recipes.run_and_wait](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-prompts-recipes-run_and_wait/) - [libtmux_mcp.tools.pane_tools.run_command](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-run_command/) - [libtmux_mcp.server.run_server](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-server-run_server/) - [libtmux_mcp.models.RunCommandResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-runcommandresult/) - [libtmux_mcp.tools.pane_tools.search.SEARCH_DEFAULT_LIMIT](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-search-search_default_limit/) - [libtmux_mcp.tools.pane_tools.search.SEARCH_DEFAULT_MAX_LINES_PER_PANE](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-search-search_default_max_lines_per_pane/) - [libtmux_mcp.tools.pane_tools.search.SEARCH_MATCH_MAX_SECONDS](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-search-search_match_max_seconds/) - [libtmux_mcp.tools.pane_tools.search.SEARCH_MAX_PATTERN_LENGTH](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-search-search_max_pattern_length/) - [libtmux_mcp.tools.pane_tools.search_panes](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-search_panes/) - [libtmux_mcp.models.SearchPanesResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-searchpanesresult/) - [libtmux_mcp.tools.window_tools.select_layout](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-window_tools-select_layout/) - [libtmux_mcp.tools.pane_tools.select_pane](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-select_pane/) - [libtmux_mcp.tools.session_tools.select_window](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-session_tools-select_window/) - [libtmux_mcp.tools.pane_tools.send_keys](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-send_keys/) - [libtmux_mcp.tools.pane_tools.send_keys_batch](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-send_keys_batch/) - [libtmux_mcp.models.SendKeysBatchResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-sendkeysbatchresult/) - [libtmux_mcp.models.SendKeysOperation](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-sendkeysoperation/) - [libtmux_mcp.models.SendKeysOperationResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-sendkeysoperationresult/) - [libtmux_mcp.models.ServerInfo](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-serverinfo/) - [libtmux_mcp.models.SessionInfo](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-sessioninfo/) - [libtmux_mcp.tools.env_tools.set_environment](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-env_tools-set_environment/) - [libtmux_mcp.tools.option_tools.set_option](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-option_tools-set_option/) - [libtmux_mcp.tools.pane_tools.set_pane_title](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-set_pane_title/) - [libtmux_mcp.tools.buffer_tools.show_buffer](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-buffer_tools-show_buffer/) - [libtmux_mcp.tools.buffer_tools.SHOW_BUFFER_DEFAULT_MAX_LINES](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-buffer_tools-show_buffer_default_max_lines/) - [libtmux_mcp.tools.env_tools.show_environment](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-env_tools-show_environment/) - [libtmux_mcp.tools.hook_tools.show_hook](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-hook_tools-show_hook/) - [libtmux_mcp.tools.hook_tools.show_hooks](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-hook_tools-show_hooks/) - [libtmux_mcp.tools.option_tools.show_option](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-option_tools-show_option/) - [libtmux_mcp.tools.wait_for_tools.signal_channel](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-wait_for_tools-signal_channel/) - [libtmux_mcp.tools.pane_tools.snapshot_pane](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-snapshot_pane/) - [libtmux_mcp.tools.server_tools.SOCKET_NAME_EXEMPT](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-server_tools-socket_name_exempt/) - [libtmux_mcp.tools.window_tools.split_window](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-window_tools-split_window/) - [libtmux_mcp.tools.pane_tools.swap_pane](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-swap_pane/) - [libtmux_mcp.middleware.TailPreservingResponseLimitingMiddleware](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-middleware-tailpreservingresponselimitingmiddleware/) - [libtmux_mcp.models.ToolCallBatchResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-toolcallbatchresult/) - [libtmux_mcp.models.ToolCallOperation](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-toolcalloperation/) - [libtmux_mcp.models.ToolCallOperationResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-toolcalloperationresult/) - [libtmux_mcp.middleware.ToolErrorResultMiddleware](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-middleware-toolerrorresultmiddleware/) - [libtmux_mcp.middleware.ToolsetMiddleware](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-middleware-toolsetmiddleware/) - [libtmux_mcp.tools.wait_for_tools.wait_for_channel](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-wait_for_tools-wait_for_channel/) - [libtmux_mcp.tools.pane_tools.wait_for_text](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-tools-pane_tools-wait_for_text/) - [libtmux_mcp.models.WaitForTextResult](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-waitfortextresult/) - [libtmux_mcp.models.WindowInfo](https://libtmux.org/en/py/latest/mcp/reference/libtmux_mcp-models-windowinfo/) [Protocol catalog](https://libtmux.org/en/py/latest/mcp/tools.json) --- # Workspace Manager for Python Source: https://libtmux.org/en/py/latest/workspace/ > Create, load, and export tmux workspaces with tmuxp. [tmuxp](https://tmuxp.git-pull.com/) is Python's workspace manager built on libtmux. A YAML or JSON file describes a session, its windows and panes, and the commands to run. `tmuxp load` builds that workspace and can attach you to it or leave it detached. The application also finds saved workspaces, exports a running session, converts configuration formats, and supports Python plugins and custom workspace builders. ## Start here - [Guides](./guides/) install tmuxp and load a workspace on a dedicated socket. - [Topics](./topics/) explain configuration, existing sessions, and exports. - [Examples](./examples/) load YAML and JSON through the CLI. - [Internals](./internals/) describe the builder pipeline and Python APIs for contributors and extension authors. ## Package and documentation Install `tmuxp` separately from the core `libtmux` package. Let its dependency resolver choose a compatible libtmux version. The MCP server is another application with its own requirements, so use separate tool environments when their dependency ranges differ. The [tmuxp documentation](https://tmuxp.git-pull.com/) provides the complete upstream CLI reference, workspace format, and extension documentation. [Upstream quickstart source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/docs/quickstart.md) ## tmuxp command and configuration reference The Python command pages document the current tmuxp reference and label proposed native extensions. - [Installation walkthrough](./guides/installation/) uses the available Python tool. - [Command reference](./cli/) lists commands, flags, and observed behavior. - [Configuration](./configuration/) covers fields, normalization, and execution. - [Example gallery](./examples/gallery/) includes upstream fixtures and prerequisites. - [Compatibility status](./reference/compatibility/) records native builder gaps. - [JSON, NDJSON, and color](./reference/output/) defines the proposed native output contract. --- # Python workspace topics Source: https://libtmux.org/en/py/latest/workspace/topics/ > Understand workspace configuration, existing sessions, and tmuxp freeze. Use `tmuxp load` to turn a YAML or JSON configuration into a tmux session. The configuration controls its windows, panes, directories, and commands. ## Configuration and commands A workspace names a session and contains windows with panes. Pane shorthand can name a command directly, while mappings describe command lists, working directories, focus, and other configuration. The loader expands environment variables and resolves relative paths using the workspace location. `shell_command_before` supplies setup commands inherited by the relevant panes. Commands, scripts, plugins, and custom builders execute code in your runtime. Use workspace files whose commands and extension imports you intend to run. Successful construction does not mean a launched application has completed startup; its own readiness check is a separate task. ## Existing sessions `tmuxp load` handles attachment and switching as part of its CLI workflow. It can attach to an existing named session, and its append mode adds windows to the current session. Those choices differ from replacing a session or converging a description automatically. Read the load command's prompts and options before automating an existing-session workflow. ## Export a session `tmuxp freeze` writes the structure of a running session as YAML or JSON. It recovers current layouts and working directories, and uses observable current programs when forming commands. It cannot recover the original command line, process memory, or a complete application checkpoint. Review its output before relying on it as a launcher. See the upstream [workspace configuration](https://tmuxp.git-pull.com/configuration/) for supported fields. Contributor details about the loader and custom builders belong in [Internals](../internals/topics/). [Expansion implementation](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [Freeze implementation](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/freezer.py). --- # Load a Python workspace Source: https://libtmux.org/en/py/latest/workspace/guides/ > Install tmuxp, load YAML on a dedicated socket, and inspect the session. Install tmuxp as a Python tool, with Python 3.10 or newer and tmux 3.2 or newer available on the host: ```console $ uv tool install tmuxp ``` The tool environment owns tmuxp's dependencies. Keep a separately installed MCP application in its own environment if its libtmux requirement differs. ## Describe the workspace Save this upstream example as `workspace.yaml`: ```yaml session_name: 2-pane-vertical windows: - window_name: my test window panes: - echo hello - echo hello ``` Load it detached on a dedicated socket, without changing your attached session: ```console $ tmuxp load \ -L workspace-guide \ -d \ workspace.yaml ``` The `-L` value selects the tmux server and `-d` prevents attachment. Reserve that socket name for this example. Omit `-d` when you want tmuxp to attach or offer its normal client-switching flow. Inspect the created session: ```console $ tmux -L workspace-guide list-sessions ``` When finished, remove only the example session: ```console $ tmux -L workspace-guide kill-session -t '=2-pane-vertical' ``` ## Saved workspaces and export `tmuxp load` accepts file paths and names resolved through its configuration search. Use an explicit path while learning the format so the loaded file is unambiguous. Use `tmuxp freeze` for a session you want to capture as a starting configuration. It offers YAML or JSON output. Inspect the generated command lists and paths before using the export later. The upstream [load reference](https://tmuxp.git-pull.com/cli/load/) covers append, session-name, socket, and attachment options. The [freeze reference](https://tmuxp.git-pull.com/cli/freeze/) covers output choices. [Workspace source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/2-pane-vertical.yaml); [CLI option definitions](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/load.py). --- # Python workspace examples Source: https://libtmux.org/en/py/latest/workspace/examples/ > Load tmux workspaces from YAML or JSON with the tmuxp CLI. Save a workspace file and pass it to [tmuxp](https://tmuxp.git-pull.com/). These examples require tmuxp and tmux; the [guide](../guides/) covers installation. No Python program is needed. ## Load YAML Save this upstream two-pane example as `workspace.yaml`: ```yaml session_name: 2-pane-vertical windows: - window_name: my test window panes: - echo hello - echo hello ``` Load the workspace and attach to it: ```console $ tmuxp load workspace.yaml ``` Inside tmux, the loader offers to switch clients or append windows. Detach with your tmux prefix followed by `d` to leave the session running. ## Load JSON without attaching The same configuration fields also work in JSON. Save this separate workspace as `workspace.json`: ```json { "session_name": "json-workspace", "windows": [ { "window_name": "editor", "panes": ["echo ready", "echo ready"] } ] } ``` Load it detached on a dedicated socket: ```console $ tmuxp load \ -L workspace-json-example \ -d \ workspace.json ``` Inspect its panes: ```console $ tmux -L workspace-json-example list-panes -t '=json-workspace:editor' ``` Remove the example session when finished: ```console $ tmux -L workspace-json-example kill-session -t '=json-workspace' ``` ## More configurations The upstream [configuration examples](https://tmuxp.git-pull.com/configuration/examples/) cover layouts, focus, directories, environment values, and command shorthand. The [load reference](https://tmuxp.git-pull.com/cli/load/) documents file selection, attachment, and existing sessions. For contributors studying how a file becomes a session, see the [internal builder example](../internals/examples/). [Two-pane YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/2-pane-vertical.yaml) --- # Exit codes and errors Source: https://libtmux.org/en/py/latest/workspace/reference/exit-codes/ > Observed Python exit behavior and the proposed native error contract. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. 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. 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/py/latest/workspace/reference/output/ > Python output and the native CLI stream and color contract. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. **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. ## 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. Interactive REPL requires a separate terminal; otherwise reject before execution. | For `-c`, stream captured Python output events and one terminal result. Interactive behavior has the same terminal requirement. | 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. 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. 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 proposal adds explicit save, format, and overwrite controls to 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 are proposed improvements over 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/py/latest/workspace/reference/compatibility/ > Separate parser acceptance, builder behavior, proposed CLI services, and installed capabilities. This page documents the available Python tmuxp reference. Proposed native extensions are labeled separately. The reference is Python tmuxp 1.74.0 at the source revision linked below. A native workspace library, parser experiment, and installed CLI are different deliverables. None of the seven native ports currently supplies the full command-line application described by the compatibility proposal. ## This port Python tmuxp is the executable reference. Its YAML normalization, loader, capture, importers, search, shell, and plugins establish the comparison behavior. Current machine-output exceptions remain documented on the command pages. These observations are a dated local research snapshot, not a support guarantee for a published artifact. Read the port's [builder topics](../../internals/topics/) and [API](../) for the actual library interface. Use the page's language switcher to compare the same topic across ports. ## Shared gaps Full parity needs document discovery and conversion, schema normalization, load/attach/append policy, capture, search, editor execution, diagnostics, and Python shell/plugin compatibility. Accepting YAML without rejecting unknown keys can silently lose behavior. Passing a parser probe does not establish execution parity. Python shell switches and plugin import paths need a Python bridge or an explicitly unsupported result. Regex behavior also differs by language; identical search flags do not imply Python regular-expression semantics. See [shell](../../cli/shell/), [search](../../cli/search/), and [hooks](../../configuration/hooks/). ## Optional format separator Python libtmux exposes `LIBTMUX_TMUX_FORMAT_SEPARATOR` in its format collector. Native ports use different framing and decoding strategies. This prototype does not claim support for the variable in those codecs. A port needs an explicit compatible seam and live tests for collisions, empty values, Unicode, and line breaks before accepting the 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. Current native gaps remain visible even when a YAML reader accepts the file. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Topics Source: https://libtmux.org/en/py/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/py/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: ```python pane.send_keys("echo hi") pane.set_option("automatic-rename", "off") pane.kill() ``` 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: 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/py/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: ```python session = server.sessions[0] window = session.windows[0] pane = window.panes[0] pane.window.window_id == window.window_id window.session.session_id == session.session_id ``` ## 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/py/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: ```python with Server() as server: with server.new_session() as session: with session.new_window() as window: with window.split() as pane: pane.send_keys('echo "Hello"') # everything above is killed on the way out, in reverse order ``` 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/py/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: ```python pane.send_keys("echo hi", enter=False) # type without pressing Enter pane.send_keys("echo hi") # default: presses Enter afterward ``` ## 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`. ```python pane.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/py/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: ```python pane.set_option("automatic-rename", "off") pane.show_options() pane.unset_option("automatic-rename") ``` ## 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: ```python session.set_hook("session-renamed", "display-message 'renamed'") session.show_hooks() ``` ## 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/py/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: ```python pane.pane_dead_signal # None below tmux 3.3, or on a pane that isn't dead ``` 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`. 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: ```python >>> session = server.new_session() >>> session.pane_id == session.active_window.active_pane.pane_id True ``` 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/py/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. ```python def is_window_up(pane, name): return any(w.window_name == name for w in pane.window.session.windows) libtmux.test.retry_until(lambda: is_window_up(pane, "build"), seconds=5.0) ``` 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)` | ```python server.new_session(session_name="work") server.wait_for("built", set_flag=True) # signal server.wait_for("built") # block until signalled ``` 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/py/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. ```python Pane.from_env().pane_id Window.from_env().window_id Session.from_env().session_id Server.from_env().sessions ``` 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: 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 ```python server.set_environment("EDITOR", "vim") # global session.set_environment("EDITOR", "hx") # this session only session.show_environment() ``` 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/py/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: "...")` | ```python default_server = libtmux.Server() named = libtmux.Server(socket_name="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` | ```python if server.is_alive(): 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/py/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.