# libtmux for C++ > The C++ port of libtmux (libtmux-cxx). Every code sample below is C++; the same pages exist for the other nine ports under their own prefix. - [C++ API reference](https://libtmux.org/en/cxx/latest/reference/): every public symbol, generated from the source. Hosted on libtmux.org. --- # MCP for C++ Source: https://libtmux.org/en/cxx/latest/mcp/ > Build the native C++ MCP executable and use its platform-specific tmux tool catalog. `libtmux-mcp-server` is a native executable that exposes tmux through MCP over standard input and output. It is an optional consumer of the C++ library, enabled separately in the CMake build. The catalog exposes discovery, creation, capture, input, search, and bounded text waits. Native Windows advertises the same catalog, with unsupported psmux operations failing explicitly at dispatch. ## Start here - [Install](#install) points an MCP client at this server. - [Tools](./tools/) lists the MCP operations, arguments, and results. - [Guides](./guides/) build the executable and select an endpoint. - [Topics](./topics/) explain platform coverage, identifiers, and failures. - [Examples](./examples/) call a tool, then explore server internals. - [Language API](./reference/) documents embedding and implementation types. Toolsets and exact tool names select the startup surface. The static `tmux://capabilities` resource reports it; workflow prompts and dynamic resource templates are absent. The [Workspace Manager](../workspace/) is another source consumer. Its configuration types are not part of the installed core API, and the MCP server does not include a workspace-file operation. [Executable and platform contract](https://github.com/libtmux/libtmux-cxx/blob/393d4b0ad666f18a6581f1eb281741a75a7503f0/apps/mcp/README.md). --- # C++ MCP topics Source: https://libtmux.org/en/cxx/latest/mcp/topics/ > Select toolsets, inspect the pinned endpoint, and observe bounded commands. The server selects one tmux endpoint and freezes its offered tools at startup. Read `tmux://capabilities` to inspect that endpoint's provenance and the effective tool selection. ## Select tools `LIBTMUX_TOOLSETS` selects any combination of `inspect`, `manage`, `execute`, and `teardown`. `LIBTMUX_TOOLS` adds exact names; `LIBTMUX_EXCLUDE_TOOLS` removes names last. Unknown names and malformed lists fail startup. Use `inspect` for discovery and terminal reads. Add `manage` for topology changes and `execute` for input and process creation. Select `teardown` explicitly when removal is needed on an existing or explicitly selected server. A default dedicated daemon can receive teardown tools when the launcher verifies its own minimal-configuration provenance. Tool selection shapes the callable interface. Execute tools act with the tmux user's authority; selecting a socket does not confine shell effects. ## Observe a command Use [`run_shell_command`](../tools/run_shell_command/) for a bounded command and its exit status. A deadline ends the wait; the pane command may still be running. Inspect it before submitting another command. Use [`capture_since`](../tools/capture_since/) to collect subsequent output and [`wait_for_text`](../tools/wait_for_text/) for an expected terminal condition. Their schemas and result limits are in the [tool reference](../tools/). The current catalog has no detached job-handle API. ## Resources and prompts The server exposes the static `tmux://capabilities` resource. Read live hierarchy and terminal state through tools. The current surface has no workflow prompts or dynamic resource templates. [Configuration and lifecycle contract](https://github.com/libtmux/libtmux-cxx/blob/393d4b0ad666f18a6581f1eb281741a75a7503f0/apps/mcp/README.md). --- # Connect a C++ MCP client Source: https://libtmux.org/en/cxx/latest/mcp/guides/ > Build the optional native executable and launch it against an explicit POSIX tmux socket. Build the optional MCP executable from the C++ repository, then configure the client to launch it. This guide targets POSIX tmux. Native Windows has separate psmux prerequisites and command-dependent support. ## Build the executable Use a toolchain supported by the repository's C++23 or C++20 build. Enable the server and its JSON dependency: ```console $ cmake \ -S . \ -B build/mcp \ -DLIBTMUX_BUILD_MCP_SERVER=ON \ -DLIBTMUX_FETCH_DEPS=ON \ -DLIBTMUX_BUILD_TESTS=OFF \ -DLIBTMUX_BUILD_EXAMPLES=OFF ``` Build it: ```console $ cmake --build build/mcp ``` Install into the user's local prefix: ```console $ cmake --install build/mcp \ --prefix ~/.local ``` ## Select the endpoint Make the installed binary directory available to the MCP client. For clients using `mcpServers`: ```json { "mcpServers": { "tmux-cpp": { "command": "libtmux-mcp-server", "args": ["--socket-name", "docs-agent"] } } } ``` Use `--socket-path` for an explicit POSIX socket path. Without a selector, the executable uses the dedicated `libtmux-mcp` socket and minimal configuration. Use `--socket inherit` to select an inherited `TMUX` route. ## Verify the catalog Have the client list tools, then call `list_sessions`. Retain the returned session, window, and pane identities for later calls. Use `LIBTMUX_TOOLSETS=inspect` for discovery and reads. Read `tmux://capabilities` to inspect the startup selection; see [Topics](../topics/) for exact tool inclusions and exclusions. [Build instructions](https://github.com/libtmux/libtmux-cxx/blob/393d4b0ad666f18a6581f1eb281741a75a7503f0/apps/mcp/README.md) and [selector implementation](https://github.com/libtmux/libtmux-cxx/blob/393d4b0ad666f18a6581f1eb281741a75a7503f0/apps/mcp/src/cli.cpp). --- # C++ MCP examples Source: https://libtmux.org/en/cxx/latest/mcp/examples/ > List sessions through the C++ MCP server and inspect implementation examples. Connect the server using the [setup guide](../guides/), then call [`list_sessions`](../tools/list_sessions/) from your MCP client. ## List sessions This is the `params` object for an MCP `tools/call` request. Send it through the connected client: ```json { "name": "list_sessions", "arguments": {} } ``` Use the returned session IDs when choosing a window or pane. The [tool reference](../tools/list_sessions/) describes this port's result and optional arguments. ## Internals The following examples are for applications that embed or extend the server. Installing and connecting an MCP client does not require this code. The MCP consumer separates its tool model from JSON-RPC encoding. Source applications can inspect that model without starting a tmux server. ### Inspect the consumer catalog This program uses the declarations in the [consumer header](https://github.com/libtmux/libtmux-cxx/blob/393d4b0ad666f18a6581f1eb281741a75a7503f0/apps/mcp/include/libtmux_consumers/mcp.hpp): ```cpp #include #include "libtmux_consumers/mcp.hpp" int main() { const auto catalog = libtmux::mcp::default_tools(); if (!catalog) { std::cerr << catalog.error() << '\n'; return 1; } for (const auto& tool : catalog->tools()) { std::cout << tool.name << '\n'; } } ``` Build this within an application that includes the repository's `mcp_tools` CMake target. The header and target belong to the source consumer; the installed core package does not export them. The [consumer tests](https://github.com/libtmux/libtmux-cxx/blob/393d4b0ad666f18a6581f1eb281741a75a7503f0/apps/mcp/tests/mcp_test.cpp) exercise the same catalog and direct calls. This small listing program is a source-derived example, not one of those collected tests. ### Drive the executable After connecting an MCP client using the [guide](../guides/), start with `list_sessions`, then `list_windows` and `list_panes`. Preserve returned object identities for subsequent calls. On POSIX, use `capture_pane` to inspect a discovered pane, then `wait_for_text` for a bounded wait. Read its match/timeout result and final capture before choosing another operation. The [protocol tests](https://github.com/libtmux/libtmux-cxx/blob/393d4b0ad666f18a6581f1eb281741a75a7503f0/apps/mcp/tests/protocol_test.cpp) cover client lifecycle, validation, concurrency, and cancellation. Native Windows advertises the same catalog; unsupported psmux operations fail explicitly when called. --- # C++ MCP API Source: https://libtmux.org/en/cxx/latest/mcp/reference/ > Distinguish installed protocol tools from the C++ source consumer's tool-model API. For MCP client requests, use the [tool reference](../tools/). This page covers language APIs for embedding or extending the server. The installed product is `libtmux-mcp-server`. Its public MCP operations are available through the protocol. The C++ tool model lives in a source consumer and is not exported by the installed core package. ## Consumer API `libtmux::mcp::default_tools()` returns an expected value containing a `ToolRegistry` or an error. `tools()` enumerates definitions, `find()` looks up a name, and `call()` invokes a handler against a `Server` with named arguments and an optional `CallContext`. `ToolResult` is an expected value containing structured output or a `ToolError`. `CallContext` provides cooperative cancellation and progress callbacks. `ToolDefinition` describes parameters, output shape, and effect annotations independently of a JSON library. [Consumer declarations](https://github.com/libtmux/libtmux-cxx/blob/393d4b0ad666f18a6581f1eb281741a75a7503f0/apps/mcp/include/libtmux_consumers/mcp.hpp) and [build target](https://github.com/libtmux/libtmux-cxx/blob/393d4b0ad666f18a6581f1eb281741a75a7503f0/apps/mcp/CMakeLists.txt). ## Protocol API The [tool reference](../tools/) covers registered names and schemas. Toolsets and exact names filter the catalog at startup. The static `tmux://capabilities` resource reports that selection. The server has no workflow prompts or dynamic resource templates. Successful tool calls return matching structured content and serialized JSON text. Strict argument validation precedes tmux execution. [Protocol tests](https://github.com/libtmux/libtmux-cxx/blob/393d4b0ad666f18a6581f1eb281741a75a7503f0/apps/mcp/tests/protocol_test.cpp) cover supported lifecycle revisions and result shapes. [Workspace builder API](../../workspace/reference/) documents a separate source consumer; it is not a workspace operation in this MCP catalog. ## API declarations - [libtmux::mcp::ArgumentMap](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-argumentmap/) - [libtmux::mcp::Arguments](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-arguments/) - [libtmux::mcp::ArgumentType](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-argumenttype/) - [libtmux::mcp::CallContext](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-callcontext/) - [libtmux::mcp::configured_tools](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-configured_tools/) - [libtmux::mcp::default_tools](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-default_tools/) - [libtmux::mcp::Effect](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-effect/) - [libtmux::mcp::FlatArguments](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-flatarguments/) - [libtmux::json_wire::from_json](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-json_wire-from_json/) - [libtmux::mcp::Handler](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-handler/) - [libtmux::mcp::InputControl](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-inputcontrol/) - [libtmux::mcp::InputSink](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-inputsink/) - [libtmux::json_wire::is_known_field](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-json_wire-is_known_field/) - [libtmux::mcp::kConservativeAnnotations](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-kconservativeannotations/) - [libtmux::json_wire::kind_from](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-json_wire-kind_from/) - [libtmux::json_wire::kSchemaVersion](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-json_wire-kschemaversion/) - [libtmux::json_wire::name_of](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-json_wire-name_of/) - [libtmux::mcp::NestedAuthority](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-nestedauthority/) - [libtmux::mcp::OutputClass](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-outputclass/) - [libtmux::mcp::OutputShape](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-outputshape/) - [libtmux::mcp::Parameter](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-parameter/) - [libtmux::mcp::parse_tool_selection](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-parse_tool_selection/) - [libtmux::mcp::ProcessReach](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-processreach/) - [libtmux::mcp::ReadToolCall](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-readtoolcall/) - [libtmux::mcp::Sink](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-sink/) - [libtmux::mcp::StructuredValue](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-structuredvalue/) - [libtmux::json_wire::to_json](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-json_wire-to_json/) - [libtmux::mcp::ToolAnnotations](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-toolannotations/) - [libtmux::mcp::ToolAuthority](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-toolauthority/) - [libtmux::mcp::ToolDefinition](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-tooldefinition/) - [libtmux::mcp::ToolError](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-toolerror/) - [libtmux::mcp::ToolOutput](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-tooloutput/) - [libtmux::mcp::ToolRegistry](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-toolregistry/) - [libtmux::mcp::ToolResult](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-toolresult/) - [libtmux::mcp::ToolSchema](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-toolschema/) - [libtmux::mcp::ToolSelection](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-toolselection/) - [libtmux::mcp::Toolset](https://libtmux.org/en/cxx/latest/mcp/reference/libtmux-mcp-toolset/) [Protocol catalog](https://libtmux.org/en/cxx/latest/mcp/tools.json) --- # Workspace Manager for C++ (in development) Source: https://libtmux.org/en/cxx/latest/workspace/ > Build the local C++ tmux-workspace CLI; implementation coverage remains partial and unreleased. **Workspace Manager for C++ is in development.** The local `workspace-cli` checkout contains a `tmux-workspace` CLI with native services. This implementation is partial and unreleased; published library packages do not provide this local CLI checkpoint. Native services load and attach sessions, reuse exact names, append windows, capture topology, discover and search documents, convert formats, import configurations and run editors. Ordinary search uses C++ regular expressions. ## Load a workspace from the terminal Follow the [local installation walkthrough](./guides/installation/) from a `workspace-cli` checkout of the [C++ repository](https://github.com/libtmux/libtmux-cxx). It builds the native command and loads a small workspace on a private socket. After building, inspect the command without starting tmux: ```console $ build/cxx-dev/apps/workspace/tmux-workspace --help ``` Use detached load for the walkthrough. JSON and NDJSON output are available; choose the mode explicitly when scripting. The command/configuration reference below also documents tmuxp behavior and compatibility targets, so it is not a claim that every referenced feature works in this local implementation. ## Current coverage `before_script` runs a quoted command directly after session creation or append selection, before settings and windows. All inputs are validated first. Script failure removes a newly owned session and preserves a borrowed append session; NDJSON streams script output while the child runs. Human load requires a foreground terminal. It attaches outside tmux or switches the unique terminal client viewing the invoking pane. The final input selects the session, including reuse. Output is flushed before handoff; failures retain loaded changes. See [loading and attachment](./cli/load/#native-c-loading). Independent `active-pane` focus on the invoking physical window requires `-d` or `--append`, including linked windows. `load --log-file PATH` appends JSON diagnostics. Log levels filter optional records without hiding required errors or changing machine results. See [logging](./reference/output/#native-c-logging) for file and failure behavior. Human load has terminal progress with presets, templates and bounded script output. Native Bash, Zsh and Fish completion covers commands, flags, choices and paths. Dynamic session/configuration-name suggestions remain unavailable. Human `ls --tree` groups workspaces by directory; `--full` includes parsed configuration. The optional [shell](./cli/shell/#native-execution) uses an installed tmuxp 1.74.0 executable, with streaming and terminal restoration. Native loading remains independent of Python. Plugin/custom-builder execution, full importer/configuration coverage and portable packaging remain unfinished. Capture preserves local session/window options but omits inherited/global options and environment. It cannot recover original command arguments or history. For the released Python workflow, use [tmuxp](https://tmuxp.git-pull.com/) and its [Python workspace guide](/py/latest/workspace/guides/). It is a separate application and remains useful when a required native feature is incomplete. ## Start here The `workspace_builder` source consumer remains available for applications that build sessions through code. [Internals](./internals/) documents that API: - [Guides](./internals/guides/) show application setup and builder calls. - [Topics](./internals/topics/) explain supported configuration and behavior. - [Examples](./internals/examples/) exercise the library or source consumer. - [API](./reference/) covers the builder and configuration interfaces. ## tmuxp command and configuration reference Use the local CLI's help and the limits above when applying these compatibility references to native execution. - [Installation walkthrough](./guides/installation/) builds and runs the local native CLI. - [Inspect through MCP](./guides/inspect-with-mcp/) connects to the loaded session. - [Command reference](./cli/) lists tmuxp commands, flags and compatibility targets. - [Configuration](./configuration/) covers fields, normalization and execution. - [Example gallery](./examples/gallery/) includes upstream fixtures and prerequisites. - [Compatibility status](./reference/compatibility/) records builder/reference gaps. - [JSON, NDJSON, and color](./reference/output/) describes the shared output design. --- # tmuxp load Source: https://libtmux.org/en/cxx/latest/workspace/cli/load/ > Load a workspace file, saved workspace name, or project directory. Multiple inputs build in order; without `-d`, the final session is attached or selected through the current-client flow. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. The local C++ `tmux-workspace load` supports foreground attachment, detached loading and explicit append. Native `before_script` runs direct argv before settings and windows; all inputs are validated first. Script failure removes a newly owned session and preserves a borrowed append session. NDJSON streams bounded script output. 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. ## Native C++ loading Human load requires a foreground controlling terminal before mutation. Outside tmux it attaches to the final input's session, including a reused session. Inside tmux it switches the unique terminal client viewing the invoking pane; zero or multiple matching clients require `-d`. Machine load requires `-d` or `--append`; `-d` takes precedence when both are supplied. The CLI cannot identify independent `active-pane` focus. If a terminal client uses it on the invoking physical window, including a linked window, use `-d` or `--append`. Clients on other physical windows do not block switching. The check repeats before handoff; enabling the flag during a script preserves loaded changes and prevents switching. Load publishes and flushes both streams before handing over the terminal. Handoff failure preserves loaded changes and reports the completed summary when possible. The client is checked again before switching; tmux's subsequent name-targeted operation still has a race. If all three standard streams are redirected, use `-d`: attachment needs a stream identifying the concrete controlling tty. See [output](../../reference/output/#native-c-load-output) for publication failures. `--log-file PATH` appends JSON diagnostics; see [native logging](../../reference/output/#native-c-logging) for levels and file failure behavior. ## Loading and attachment Create [`workspace.yaml`](../../guides/installation/#create-the-input) using the [installation walkthrough](../../guides/installation/), then load it on that walkthrough's dedicated socket: ```console $ tmuxp load \ -S "$WORKSPACE_TMP/tmux.sock" \ -d \ "$WORKSPACE_TMP/workspace.yaml" ``` `-d` avoids attachment. Inside an existing tmux client, the normal interactive flow can switch to the new session, append windows, or stay detached. `--append` explicitly selects the append flow and needs a current target session. An existing session is handled through tmuxp's load policy; loading is not a declarative reconciliation operation that removes surplus windows. Put flags before the complete group of filenames. The reference accepts flags before or after that group, but a flag between two filenames can cause an argument error. `-s` overrides the final input's session name when several files are loaded. tmuxp parses `-2` and `-8` as mutually exclusive flags, separate from the CLI text's `--color` setting. Legacy `-8` is unsupported; [tmux removed 88-color support](https://raw.githubusercontent.com/tmux/tmux/3.2a/CHANGES). ## Progress and script output `--progress-format` accepts `default`, `minimal`, `"window"`, `"pane"`, `verbose`, or a custom format. Available tokens include `{session}`, `{window}`, `{window_index}`, `{window_total}`, `{window_progress}`, `{window_progress_rel}`, `{windows_done}`, `{windows_remaining}`, `{pane_index}`, `{pane_total}`, `{pane_progress}`, `{progress}`, `{session_pane_progress}`, `{overall_percent}`, `{bar}`, `{pane_bar}`, `{window_bar}`, and `{status_icon}`. The 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 native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/load.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp freeze Source: https://libtmux.org/en/cxx/latest/workspace/cli/freeze/ > Capture a live session as a starting workspace file. A capture records observable session state; it cannot recover the original scripts, plugin intent, comments, or every application state. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Capture a live session as a starting workspace file. A capture records observable session state; it cannot recover the original scripts, plugin intent, comments, or every application state. ## Export a named session After the [installation walkthrough](../../guides/installation/) creates `workspace-guide`, export it to a new destination: ```console $ tmuxp freeze \ -L workspace-guide \ --workspace-format yaml \ --save-to captured-workspace.yaml \ --yes \ workspace-guide ``` Without a session argument, tmuxp resolves or asks for a live session. Without a format or destination, it can ask for those choices. `--yes` answers yes/no questions; it does not supply every missing selection. `--quiet` suppresses explanatory status, but prompts can still occur. Successful export writes the workspace document to a file, not stdout. Declining a confirmation can return without saving. In this reference, an explicit `--save-to` path bypasses the overwrite confirmation used by the prompted path. Select a new path deliberately. The native contract applies the same protection to explicit and prompted destinations and keeps `--force` distinct from `--yes`. Inspect the capture before reloading. See the [export and reload workflow](../../guides/export-session/) and the [native machine output](../../reference/output/) for the distinction between a saved file's format and a CLI result stream. ## What the captured document holds The seven native ports agree on three things a capture records, and on one it does not. **Window options are written under `options_after`.** tmux applies them after the panes exist, and options like `automatic-rename: off` do not hold if they are applied before. `load` accepts either spelling. **A pane sitting at a shell gets no `shell_command`.** Naming the shell would start a shell inside a shell when the document is reloaded. The shell is recognised by name as well as by the session's `default-shell`, because macOS runs bash for `/bin/sh` and the pane reports `bash`. **Nothing about the machine it was captured on.** No session environment, so a document does not carry the capturing host's `SSH_AUTH_SOCK` or `DISPLAY`, and no `default-size`, which would otherwise pin a reloaded session to the size of the terminal that captured it. ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `"session_name"` | optional | | | `-S` | value; None | pass-through for tmux -S | | `-L` | value; None | pass-through for tmux -L | | `-f`, `--workspace-format` | value; None | yaml, json | | `-o`, `--save-to` | value; None | file to save to | | `--yes`, `-y` | flag; False | always answer yes | | `--quiet`, `-q` | flag; False | suppress explanatory/status text; prompts still occur despite the parser help claiming otherwise | | `--force` | flag; False | overwrite the workspace file | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/freeze.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp convert Source: https://libtmux.org/en/cxx/latest/workspace/cli/convert/ > Convert a workspace document between YAML and JSON while retaining its mapping keys. Conversion does not prove that a native builder supports every retained field. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Convert a workspace document between YAML and JSON while retaining its mapping keys. Conversion does not prove that a native builder supports every retained field. ## Change the file format Given the [`workspace.yaml`](../../guides/installation/#create-the-input) from the [installation walkthrough](../../guides/installation/), convert it to [`workspace.json`](./): ```console $ tmuxp convert \ --yes \ workspace.yaml ``` The destination has the same stem and opposite extension. In the reference, `--yes` permits replacement of an existing destination without an additional existence check. The original input remains. YAML comments and textual formatting do not round-trip through JSON. Conversion uses the complete document mapping. A serializer for a reduced native workspace struct can silently discard extension keys and is insufficient for this command. The native machine mode returns a document without writing a guessed destination; its extra save and overwrite controls are described in [output](../../reference/output/). ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `workspace_file` | required | checks tmuxp and current directory for workspace files. | | `--yes`, `-y` | flag; False | always answer yes | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/convert.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp edit Source: https://libtmux.org/en/cxx/latest/workspace/cli/edit/ > Resolve a saved workspace or file and open it in the configured editor. The lookup rules are shared with loading. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Resolve a saved workspace or file and open it in the configured editor. The lookup rules are shared with loading. ## Open a workspace With [`workspace.yaml`](../../guides/installation/#create-the-input) already saved and `$EDITOR` set to a single executable: ```console $ tmuxp edit workspace.yaml ``` The reference passes the entire `$EDITOR` value as one executable string followed by the file path. An editor command containing arguments is not tokenized. It waits for the child process but does not propagate that child's exit status. Use an editor wrapper executable when arguments are needed. The native contract defines argument parsing and propagates child failure. These are deliberate behavior changes, not existing Python guarantees. See [discovery](../../guides/discovery/) and [environment](../../configuration/environment/). ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `workspace_file` | required | checks current tmuxp and current directory for workspace files. | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/edit.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp debug-info Source: https://libtmux.org/en/cxx/latest/workspace/cli/debug-info/ > Collect tmuxp, Python, tmux, configuration, and environment diagnostics for troubleshooting. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Collect tmuxp, Python, tmux, configuration, and environment diagnostics for troubleshooting. ## Inspect structured diagnostics ```console $ tmuxp debug-info --json ``` The command writes one JSON object. Named path fields mask the home directory, but raw tmux output arrays are preserved. Review diagnostics before sharing them because names, commands, and raw tmux values may identify your environment. `--ndjson` is a native extension. The native result should name the port and runtime and define redaction for raw values. See [troubleshooting](../../guides/troubleshooting/) and [output](../../reference/output/). ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `--json` | flag; False | output as JSON | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/debug_info.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp ls Source: https://libtmux.org/en/cxx/latest/workspace/cli/ls/ > List discovered project and saved workspace files, with optional grouping and configuration content. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. List discovered project and saved workspace files, with optional grouping and configuration content. ## List workspace records ```console $ tmuxp ls --json ``` JSON output is an object with `workspaces` and `global_workspace_dirs`; no workspaces still produces the object with an empty array. `--ndjson` emits one workspace record per line and emits zero lines for no records. When both format flags are supplied, NDJSON wins. `--full` includes full configuration content. `--tree` groups the human display by directory. Discovery includes nearest project configurations and configured global directories. It is not a recursive scan of every descendant directory. See [discovery rules](../../guides/discovery/) for precedence and candidate names. ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `--tree` | flag; False | display workspaces grouped by directory | | `--json` | flag; False | output as JSON | | `--ndjson` | flag; False | output as NDJSON (one JSON per line) | | `--full` | flag; False | include full config content in output | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/ls.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp search Source: https://libtmux.org/en/cxx/latest/workspace/cli/search/ > Search discovered workspace fields using regular expressions or literal strings. Queries combine with AND unless `--any` selects OR. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Search discovered workspace fields using regular expressions or literal strings. Queries combine with AND unless `--any` selects OR. ## Find workspaces by session name ```console $ tmuxp search \ --json \ --fixed-strings \ --field session \ workspace ``` Field prefixes in query terms and repeated `--field` restrictions select name, session (`s`), path (`p`), window (`w`), or pane data. `--ignore-case` ignores case; `--smart-case` does so only when a pattern has no uppercase. `--word-regexp` requires whole words and `--invert-match` selects nonmatches. JSON results contain `"name"`, `"path"`, `"session_name"`, `"source"`, `matched_fields`, and `"matches"`. The pinned reference emits an empty byte stream for no matches, even with `--json`. With no query, machine search can print human help and return normally. An invalid regular expression can also yield no machine output. The native contract instead uses `[]` for an empty JSON result and usage status 2 for a missing or invalid pattern. Python regular-expression behavior is part of compatibility. Native regex libraries differ in lookaround, backreferences, Unicode, flags, and word boundaries. Matching the options alone does not establish expression equivalence. See [compatibility](../../reference/compatibility/) and [output](../../reference/output/). ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `"query_terms"` | zero or more | search patterns (prefix with field: for field-scoped search) | | `-f`, `--field` | value; None | restrict search to field(s): name, session/s, path/p, window/w, pane | | `-i`, `--ignore-case` | flag; False | case-insensitive matching | | `-S`, `--smart-case` | flag; False | case-insensitive unless pattern has uppercase | | `-F`, `--fixed-strings` | flag; False | treat patterns as literal strings, not regex | | `-w`, `--word-regexp` | flag; False | match whole words only | | `-v`, `--invert-match` | flag; False | show workspaces that do NOT match | | `--any` | flag; False | match ANY pattern (OR logic); default is ALL (AND logic) | | `--json` | flag; False | output as JSON | | `--ndjson` | flag; False | output as NDJSON (one JSON per line) | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/search.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp shell Source: https://libtmux.org/en/cxx/latest/workspace/cli/shell/ > Open a Python shell with tmux objects available, or evaluate Python using `-c`. This command remains Python-specific even when reached through a native command. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Open a Python shell with tmux objects available, or evaluate Python using `-c`. This command remains Python-specific even when reached through a native command. ## Native execution The development C++ CLI invokes an installed tmuxp 1.74.0 console executable. Put it on `PATH`, or set `TMUX_WORKSPACE_TMUXP` to its executable path. That value accepts a single path, including spaces, without interpreter arguments. Optional backends belong in the selected executable's Python environment. Continue the [installation walkthrough](../../guides/installation/) through its detached load, leaving `workspace-guide` running in the same shell: ```console $ build/cxx-dev/apps/workspace/tmux-workspace shell \ -S "$WORKSPACE_TMP/tmux.sock" \ -c 'print(pane.pane_id)' \ --json \ workspace-guide editor ``` JSON captures runtime stdout, stderr and status under `"script_output"`. NDJSON emits flushed `script-output` records with `"stream"` and `"text"`, then a completed or failed result. Capture is limited to 1 MiB per stream; overflow stops the child group and reports `OUTPUT_LIMIT` with bounded output. Human output streams to its original destinations. The runtime's own messages remain part of its output. Machine calls require `-c`, including an empty code string. Interactive human calls require a foreground controlling terminal; exit restores its settings and foreground group. SIGINT and SIGTERM sent to the workspace process cancel its owned child group. `-S` takes precedence over `-L`, and opposing startup flags keep their original order. See the [native shell contract](https://github.com/libtmux/libtmux-cxx/blob/3963cd7792a72b2b500b8ed4ba08016a791c0d41/apps/workspace/README.md#optional-tmuxp-shell). ## Evaluate with a selected server After the [installation walkthrough](../../guides/installation/) starts its dedicated server: ```console $ tmuxp shell \ -S "$WORKSPACE_TMP/tmux.sock" \ -c 'print(server.sessions)' \ workspace-guide editor ``` Use `-c`; the reference does not define `--command`. Optional session and window arguments select context. `--best` chooses the best available shell backend; the explicit selectors require their corresponding Python packages. The `--pdb` path uses a debugger rather than a normal REPL. The paired `--use-pythonrc` / `--no-startup` and `--use-vi-mode` / `--no-vi-mode` options share destinations. The last occurrence wins. The negative options disable the corresponding setting. See [environment](../../configuration/environment/) for startup and backend settings. A native REPL is not an equivalent implementation of Python `-c`, IPython, or PTPython. Native ports that support this command use an optional version-checked Python bridge and report an unsupported-runtime error if it is absent. Check [port coverage](../../reference/compatibility/) before relying on that bridge. The C++ command requires `-c` for machine output. 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 native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/shell.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp import Source: https://libtmux.org/en/cxx/latest/workspace/cli/import/ > Import a workspace from a supported external configuration format. Select one of the two child commands. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Import a workspace from a supported external configuration format. Select one of the two child commands. ## Choose the source format - [Teamocil](../import-teamocil/) converts a Teamocil workspace. - [tmuxinator](../import-tmuxinator/) converts a tmuxinator workspace. The parent command groups importers and has no workspace file argument of its own. Each child requires a source at the parser boundary, despite optional-looking help. Conversion is schema translation; it does not make tmuxinator Ruby or ERB evaluation available in a native YAML reader. ## Native C++ imports The local `tmux-workspace` command previews with `--json` and saves with an explicit destination. Both source and translated workspace are validated before a destination is replaced: ```console $ tmux-workspace import teamocil \ --save-to team.json \ team.yml ``` Use `--force` to replace an existing destination. A refused import leaves it intact. Unknown fields, ERB templates, host lifecycle hooks, pane titles and unsupported synchronization timing are reported explicitly. Imports do not execute Ruby. See each importer for supported translations. Command text and paths should be reviewed before loading the result. ## Arguments and flags The parent accepts `-h` / `--help` and selects a child command. All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/import_config.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp import teamocil Source: https://libtmux.org/en/cxx/latest/workspace/cli/import-teamocil/ > Translate a Teamocil workspace into tmuxp configuration, review the result, and select its saved representation. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Translate a Teamocil workspace into tmuxp configuration, review the result, and select its saved representation. ## Select an existing source With [`project.yml`](../../guides/discovery/) already present in the Teamocil configuration directory: ```console $ tmuxp import teamocil project ``` The source lookup uses [`~/.teamocil`](./). A source argument is effectively required: although its positional action has optional arity, it belongs to a required exclusive group, and omission exits 2. The reference previews and saves the transformed document interactively. Native automation needs explicit destination, encoding, and overwrite policy instead of implicit prompts. See [output](../../reference/output/). Inspect translated commands and directories before loading the result. An extensionless name searches the configured source directory. A filename with an extension is resolved relative to the current directory unless you provide an explicit path. ## Native C++ translation `tmux-workspace import teamocil` preserves names, roots, layouts, window options and pane/window focus. It accepts pane command strings and `commands` mappings. Command arrays join into one semicolon-separated shell input; the first true focus flag wins in each scope. The legacy `session` wrapper and `project_name`, `project_root`, `tabs`, `splits` and `cmd` aliases are accepted. Null aliases fall back to the other spelling; conflicting non-null values are refused. An omitted session name uses the source filename stem. Relative roots resolve against the directory where import runs and are saved as absolute paths. Dollar expansion and `~user` paths are unsupported. Enabled `synchronize-panes` is refused because the native builder creates all panes before sending commands, changing which panes would receive input. See [native import saving](../import/#native-c-imports). ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `workspace_file` | effectively required | `nargs="?"` belongs to a required exclusive group; omission exits 2. Source lookup uses [`~/.teamocil`](./). | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/import_config.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp import tmuxinator Source: https://libtmux.org/en/cxx/latest/workspace/cli/import-tmuxinator/ > Translate a tmuxinator workspace into tmuxp configuration, preserving an explicit boundary around dynamic Ruby configuration. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Translate a tmuxinator workspace into tmuxp configuration, preserving an explicit boundary around dynamic Ruby configuration. ## Select an existing source With [`project.yml`](../../guides/discovery/) already present in the configured tmuxinator directory: ```console $ tmuxp import tmuxinator project ``` `TMUXINATOR_CONFIG` overrides the source directory and expands a leading tilde. A source argument is effectively required, and omission exits 2 despite optional-looking help. The reference previews and saves the transformed document interactively. Do not interpret successful YAML parsing as support for ERB templates or arbitrary Ruby execution. Native importers must either implement a documented bridge or reject unsupported dynamic input. Inspect the resulting commands and directories before loading. See [environment](../../configuration/environment/) and [output](../../reference/output/). An extensionless name searches the configured source directory. A filename with an extension is resolved relative to the current directory unless you provide an explicit path. ## Native C++ translation `tmux-workspace import tmuxinator` translates session names, project roots, named windows, layouts and pane commands. A command array used as a window body remains one pane with ordered commands; explicit panes may each contain a command array. `pre_window` arrays join with `; `, while window `pre` arrays join with ` && `. A nonempty window `pre` requires explicit nonempty panes. Synchronization is supported only with `synchronize: after`. Host lifecycle hooks, ERB templates, named pane titles, startup selectors and endpoint/attachment settings are refused before saving. Relative project roots resolve where import runs; relative window roots resolve against the project root. Saved paths are absolute. `project_name`, `project_root` and `tabs` aliases use non-null fallback and refuse conflicts. See [native import saving](../import/#native-c-imports). ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `workspace_file` | effectively required | `nargs="?"` belongs to a required exclusive group; omission exits 2. Source lookup honors `TMUXINATOR_CONFIG`. | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/import_config.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Workspace command reference Source: https://libtmux.org/en/cxx/latest/workspace/cli/ > The tmuxp command tree, root options, and native compatibility scope. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../reference/compatibility/) describes this port's implemented coverage. The installed Python command is `tmuxp`. Native command names and installation artifacts are not established by this documentation prototype. The pages below document the Python reference grammar and identify native extensions. ## Commands - [load](./load/) builds sessions from files or saved workspace names. - [freeze](./freeze/) captures a live session to a workspace file. - [convert](./convert/) changes YAML and JSON representation. - [edit](./edit/) opens a resolved workspace in an editor. - [ls](./ls/) lists discovered configurations. - [search](./search/) searches their names and content. - [debug-info](./debug-info/) reports runtime and tmux diagnostics. - [shell](./shell/) evaluates Python with tmux context. - [import](./import/) selects [Teamocil](./import-teamocil/) or [tmuxinator](./import-tmuxinator/) conversion. ## Root options `-h` / `--help` shows help; `-V` / `--version` prints the version. `--log-level` selects `"debug"`, `"info"`, `warning`, `"error"`, or `critical`, with parser default `warning`. `--color` accepts `auto`, `always`, or `never`, defaulting to `auto`. Place root flags before the subcommand: ```console $ tmuxp --color never ls ``` Short flags belong to their command: `search -S` is smart case, while `load -S` selects a tmux socket path. Similarly, `-f` selects a tmux configuration for load, a field for search, and an output format for freeze. ## Machine mode and automation Current Python machine output exists on `ls`, `search`, and `debug-info` as described by their own flags. The native command tree adds `--json` and `--ndjson` to every leaf and allows these long options before or after the command. These additions are not installed Python features. Read [output and color](../reference/output/), [exit behavior](../reference/exit-codes/), [completion](./completion/), and [automation](../guides/automation/) before building a script around a command. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Shell completion Source: https://libtmux.org/en/cxx/latest/workspace/cli/completion/ > Generate completion from command definitions and check native availability. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. tmuxp uses the separately installed `shtab` package for experimental completion. The parser entry point is [`tmuxp.cli.create_parser`](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). Generate a script in an environment that can import both packages, then use the shell-specific installation procedure from the [upstream completion guide](https://tmuxp.git-pull.com/cli/completion/). ## Native completion The local C++ CLI generates Bash, Zsh and Fish completion. Build it through [installation](../../guides/installation/) and make the resulting `tmux-workspace` executable available on `PATH`. Enable Bash completion in the current shell: ```console $ source <(tmux-workspace --generate-completion bash) ``` For Zsh, initialise its completion system and load the generated script: ```console $ autoload -Uz compinit && compinit && source <(tmux-workspace --generate-completion zsh) ``` For Fish: ```console $ tmux-workspace --generate-completion fish | source ``` Completion includes nested import commands, command-specific flags, enumerated values and file paths, including names containing spaces. It does not start tmux or read workspace files. Dynamic session names and configuration aliases are not suggested. Add the matching setup command to your shell configuration to enable completion in later sessions. This is part of the unreleased native CLI. The documentation exporter remains separate from shell completion. 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/cxx/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/cxx/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: ```cpp const auto sessions = server.sessions(); if (!sessions.has_value()) return 1; for (const libtmux::Session& session : *sessions) { std::printf("%s (%lld windows)\n", std::string{session.name()}.c_str(), session.window_count()); const auto windows = session.windows(); if (!windows.has_value()) continue; for (const libtmux::Window& window : *windows) { std::printf(" %s\n", std::string{window.name()}.c_str()); const auto panes = window.panes(); if (!panes.has_value()) continue; for (const libtmux::Pane& pane : *panes) { std::printf(" %s\n", std::string{pane.id()}.c_str()); } } } ``` ## 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/cxx/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. ```cpp #include // One-shot: every call answers with a value; no tmux failure is thrown. const auto server = libtmux::Server::at_default(); if (!server.has_value()) { return 1; } const auto session = server->new_session("work"); if (!session.has_value()) { return 1; } const auto pane = session->active_pane(); if (pane.has_value()) { (void)pane->send_text("echo hello"); (void)pane->send_key("Enter"); } ``` 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/cxx/latest/concepts/queries/ > How you get from every session on the server to the one pane you mean, and what happens when zero or several match. Use a collection filter to find matching sessions, windows, or panes. Use an exactly-one lookup when your next operation requires a single target. - **Filtering returns a collection; exactly-one lookup checks the result count.** A `.filter()` or `.where()` call returns zero or more matches. Methods such as `.get()`, `.one()`, and `Selections.exactlyOne()` return one object or report a missing or ambiguous match. - **Choose where to filter.** Filter a snapshot in your program when you need several queries over the same data. A tmux format filter can reduce the rows returned by a live read. The cost depends on the data and queries you need. ## Python: `.filter()` and `.get()`, Django-style `server.sessions`, `session.windows`, and `window.panes` are `QueryList` collections. Call `.filter()` with field names and optional lookup suffixes: Lookups include `exact`, `contains`, `startswith`, `endswith`, `regex`, and their case-insensitive `i`-prefixed variants. Multiple keywords and chained `.filter()` calls combine with AND. `.get()` requires exactly one match. Its `default` argument handles an absent result; multiple matches still raise `MultipleObjectsReturned`. Server-wide collections (`server.windows`, `server.panes`) enumerate window links. A window linked to two sessions appears once per session, so a lookup can be ambiguous even when the window ID is unique. Use `Window.linked_sessions` to find its sessions. For a known ID, use `Pane.from_pane_id()` or `Window.from_window_id()` to resolve the object directly. For servers with hundreds or thousands of panes, `.filter()` still builds every object before you discard the ones that don't match. `search_sessions`, `search_windows`, and `search_panes` push a tmux `-f` filter expression down to the server instead, so libtmux builds objects only for the matches: Python-side lookups work with the library's supported tmux versions. The tmux filter grammar requires tmux 3.2 or newer. An unknown format token expands to an empty value, so a malformed filter can look like a valid filter with no matches. If `search_*()` unexpectedly returns no results, try `#{m:*,#{session_name}}` to check that the session data is available. ## TypeScript: criteria as data TypeScript's `Selection.where()` accepts structured, serializable criteria that can be stored in a configuration file or sent through MCP: `some`, `every`, and `none` test related objects. `{ mode: "insensitive" }` enables case-insensitive comparison. Use `.where()` for criteria that can be encoded with `encodeWhereDocument` and decoded with `decodeWhereDocument`; use `.filter()` for a predicate function. `.one()` throws `NoMatchError` or `MultipleMatchesError`. `.oneOrUndefined()` permits an absent result. ## Go, Rust, Java, C++: typed fields that fail queries at compile time These ports use typed fields to reject invalid comparisons at compile time: - **Go** offers both `tmux.PaneFilter{Active: tmux.Ptr(true), ...}` structs that push down into `SearchPanes` (one tmux command, only matches returned), and a `snapshot()` read followed by `tmuxq.Where(panes, predicate)` when you want several answers from one read. - **Rust** uses typed fields: `fields.pane_active.eq(true)` is valid, but `.gt(...)` on that boolean field is not. Expressions compose with `.and()`. With the `serde` feature, a query can be encoded as a versioned JSON document for configuration or MCP. - **Java** exposes each field as a typed accessor (`Pane_.index()`, `Session_.name()`) that plugs straight into an ordinary `Stream.filter()`; `Pane_.index().startsWith("2")` doesn't compile because the index is a number, not a string. `Selections.exactlyOne(...)` is the `.get()`-shaped call, throwing `NoMatchException` or `MultipleMatchesException`. - **C++** composes `FilterExpr` values with `&&`, `||`, and `!`, as in `pane::command.starts_with("nv") && pane::active`. Invalid field operations such as `pane::active.starts_with("x")` fail to compile. Examples of typed and local filters: ```cpp // A filter is a value built from typed fields; `window::active.starts_with(...)` // would not compile: a flag has no string operations. const auto interesting = libtmux::window::name.starts_with("e") || libtmux::window::name == "logs"; auto matched = *windows | libtmux::matching(interesting); // "Exactly one, or say why not" is a question the library answers directly. auto logs = *windows | libtmux::matching(libtmux::window::name == "logs"); if (const auto only = libtmux::exactly_one(logs); only.has_value()) { std::printf("exactly one: %s\n", std::string{only->get().id()}.c_str()); } ``` ## 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/cxx/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: ```cpp const auto window = session.new_window({.name = "dev"}); if (!window.has_value()) return 1; // `focus = true` makes the new pane active, so the next split divides it. const auto terminal = window->split({.percentage = 30, .focus = true}); if (!terminal.has_value()) return 1; const auto logs = window->split({.horizontal = true}); if (!logs.has_value()) return 1; (void)window->select_layout("main-vertical"); ``` `Window.split()` or `Pane.split()` adds a pane. Direction and size control its placement. `select_layout()` rearranges the panes while their processes continue running. tmux provides `even-horizontal`, `even-vertical`, `main-horizontal`, `main-vertical`, and `tiled` layouts. Python's `attach=False` and C++'s detached creation keep new windows in the background. Check the creation defaults for your port if focus matters. Splits and resizes require tmux commands; [Control mode vs one-shot](../transports/) covers their transport costs and batching. ## Building one declaratively These packages read or build workspace configurations based on tmuxp: | Port | Package | Shape | |------|---------|-------| | Python | tmuxp itself | the format this whole idea is named after | | TypeScript | `@libtmux/workspace` | `applyWorkspace(server, { session_name, windows: [...] })` | | Go | `workspace` | tmuxp-shaped, per the port's own module layout | | Rust | `tmux-workspace` | tmuxp-shaped | | Java | `libtmux-workspace` | "enough of tmuxp's format to describe a workspace" | | C# | `LibTmux.Workspace` | reads tmuxp YAML directly | | Swift | `TmuxWorkspace` | Swift, JSON, or YAML (YAML needs the `YAMLWorkspaces` trait) | TypeScript's `applyWorkspace` applies a desired configuration. Applying the same configuration again reuses its existing objects: 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: ```cpp // Not a package: this is the examples/workspace/ consumer, showing the // shape a tmuxp document builds into rather than a library entry point. const workspace::Workspace description{ .session_name = "dev", .windows = {{.name = "editor", .panes = {{}, {}}}}}; const auto built = workspace::build(server, description); ``` ## Cleaning up For temporary workspaces, Python's `Window` and `Session` context managers kill their objects on block exit, including when the block raises: See [Context managers](/topics/context-managers/) for cleanup support in each port. Use explicit kill methods when the handle does not provide scope-based cleanup. --- # Workspace configuration Source: https://libtmux.org/en/cxx/latest/workspace/configuration/ > Tmuxp workspace configuration and current C++ builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../reference/compatibility/) describes this port's implemented coverage. A workspace file describes one tmux session, its windows and panes, and the commands sent to them. YAML and JSON carry the same field names. Save this complete example as [`workspace.yaml`](../guides/installation/#create-the-input): ```yaml session_name: workspace-example start_directory: ./ windows: - window_name: editor layout: even-horizontal panes: - echo ready - blank ``` The equivalent JSON is: ```json { "session_name": "workspace-example", "start_directory": "./", "windows": [ { "window_name": "editor", "layout": "even-horizontal", "panes": ["echo ready", "blank"] } ] } ``` With Python tmuxp installed, load this file detached on a socket reserved for the example: ```console $ tmuxp load \ -L configuration-example \ -d \ workspace.yaml ``` Inspect the resulting panes: ```console $ tmux -L configuration-example list-panes -t '=workspace-example' ``` Remove the example session when finished: ```console $ tmux -L configuration-example kill-session -t '=workspace-example' ``` ## How configuration becomes a session The tmuxp loader reads a document, expands command shorthand and shell variables, and applies inherited defaults before selecting a workspace builder. The classic builder then creates tmux objects and sends commands. Completion means construction and command delivery finished; it does not establish that a server launched in a pane is ready. `"session_name"` and `"windows"` are needed by normal loading. A window can omit its name and let tmux choose one; an omitted `"panes"` list defaults to one blank pane. Supplying explicit names and pane lists makes a portable example clearer. An empty pane list is not the same input as an omitted list. The internal `validate_schema` helper requires each window_name, but the current CLI/classic builder path does not call it. It is not a complete JSON Schema or an exact description of what load accepts. A YAML parser accepting a key also does not mean a builder implements it. ## Configuration reference - [Session](./session/) covers identity, options, environment, and root keys. - [Windows](./windows/) covers names, indexes, option timing, and focus. - [Panes](./panes/) covers shorthand, blank forms, shell, and overrides. - [Commands](./commands/) covers before commands, Enter, delays, and history. - [Environment](./environment/) separates process settings from pane values. - [Directories](./directories/) explains file discovery and path resolution. - [Layouts](./layouts/) explains pane arrangement and terminal size. - [Hooks and builders](./hooks/) covers scripts and Python extensions. Use the [configuration gallery](../examples/gallery/) for more complete files. Configuration conversion should preserve the source mapping, including extension keys; loading that mapping requires separate support for every execution feature. ## Current C++ builder The C++ workspace consumer is a source-tree library target. Its YAML parser rejects fields outside its supported subset. Building can leave earlier tmux changes in place after failure. See the [native builder behavior](../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/src/tmuxp.cpp) before using these fields through application code. ## Reference source [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [validation.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/validation.py); [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py). --- # Session configuration Source: https://libtmux.org/en/cxx/latest/workspace/configuration/session/ > Tmuxp session configuration and current C++ builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. The root mapping names the session and supplies defaults inherited by its windows and panes. The file's name is independent of `"session_name"`: loading [`project.yaml`](../../guides/discovery/) can create a session named `development`. ```yaml session_name: development start_directory: ./ suppress_history: true shell_command_before: - echo preparing pane environment: WORKSPACE_ROLE: development options: default-shell: /bin/sh global_options: status: true windows: - window_name: main panes: - echo ready ``` `"global_options"` changes the shared tmux server. Use a dedicated socket while learning these options. The example assumes `/bin/sh` exists. ## Root keys | Key | Meaning | | --- | --- | | `"session_name"` | Session identity; expanded before building | | `"windows"` | Ordered list of window mappings | | `start_directory` | Starting directory and base for inherited window directories | | `"options"` | tmux session options applied during construction | | `"global_options"` | tmux options applied with global scope on the selected server | | `"environment"` | Values placed in the tmux session environment | | `shell_command_before` | Commands prepended to commands in every pane | | `"suppress_history"` | Default history suppression inherited by windows and panes | | `"before_script"` | Process run after initial session creation, before configured windows | | `"plugins"` | List of Python plugin class references | | `workspace_builder` | Classic builder, registered builder name, or Python class reference | | `workspace_builder_paths` | Trusted directories used for Python builder imports | | `workspace_builder_options` | Builder behavior settings such as pane_readiness | Options use tmux option names and values. A workspace key such as `start_directory` is not a tmux option and does not belong in `"options"`. Some tmux settings are window options even when tmux permits them through a session target; consult tmux's option scope when choosing the catalog. ## Construction order The classic builder creates the initial session, runs its initial plugin hook and workspace before_script, then applies root options, global options, and session environment before creating configured windows. The temporary initial window is replaced. Hooks can observe those tmux operations; loading is not an invisible transaction. The same-named session is handled by the [load command](../../cli/load/) and its attach, switch, append, and detached choices. Changing a YAML file does not automatically reconcile an existing running session. An explicit `load -s` value overrides the configured name for that invocation. ## Inherited defaults A window can override its start directory and history policy, and a pane can override them again. Before commands accumulate in session, window, then pane order. Session environment remains distinct from window/pane launch environments. Read [directories](../directories/), [commands](../commands/), and [environment](../environment/) before relying on inheritance. Python scripts, plugins, and custom builder references execute code. Their exact timing and runtime requirements are in [hooks and builders](../hooks/). ## Current C++ builder Session names, directories, environment, options, and command-related settings have native fields. The consumer does not import Python plugins or custom builders. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/src/tmuxp.cpp) before using these fields through application code. ## Reference source [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [load.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/load.py). --- # Window configuration Source: https://libtmux.org/en/cxx/latest/workspace/configuration/windows/ > Tmuxp window configuration and current C++ builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Each item in `"windows"` describes one window and its ordered panes. Set `"window_name"` when the name matters; an omitted name lets tmux choose it. `"window_index"` selects a tmux numeric index independently of the item's position in the list. ```yaml session_name: window-example start_directory: ./ windows: - window_name: tools window_index: 1 start_directory: ./ layout: even-horizontal focus: true options: automatic-rename: false options_after: synchronize-panes: true panes: - echo left - echo right ``` This configuration sends each pane its own initial command before enabling synchronized input. Later typing in one synchronized pane can affect the other pane. ## Window keys | Key | Meaning | | --- | --- | | `"window_name"` | Label used by tmux; shell variables are expanded | | `"window_index"` | Explicit numeric position, otherwise use tmux's available index | | `"panes"` | Ordered pane list; omission supplies one blank pane | | `"layout"` | Named tmux layout or explicit layout description | | `start_directory` | Directory inherited by panes unless a pane overrides it | | `"window_shell"` | Initial shell/application for panes, subject to pane shell override | | `focus` | Select the window after building | | `"options"` | Window options applied during creation | | `"options_after"` | Window options applied after panes and their initial commands | | `"environment"` | Launch environment used when a pane lacks its own map | | `shell_command_before` | Commands prepended after session before commands | | `"suppress_history"` | Window default overriding the session history policy | ## First pane and later panes A new window already has its initial pane. Tmuxp uses the first configured pane's start_directory, shell, and environment when launching that window, then creates splits for later panes. A first-pane override therefore matters during new-window, not only while creating splits. A pane shell overrides window_shell. Window environment is used when the pane has no environment map; providing a pane map selects that map instead. These rules have native-port differences, so use the current builder note on this page. ## Index, layout, and focus The classic builder moves the temporary initial window before creating the configured first window. This permits an explicit first index without reusing the temporary window's content. Do not assume window list position and tmux numeric index are identical. Window options precede layout application. `"options_after"` exists for settings such as synchronize-panes that should take effect after individual setup commands. [Layouts](../layouts/) explains named layouts, dimensions, and focus; [panes](../panes/) describes the pane forms accepted inside a window. ## Current C++ builder Names, indexes, directories, environment, options, layouts, focus, and options_after are supported in the subset. Inspect actual creation order and current tmux state when matching reference semantics. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/src/tmuxp.cpp) before using these fields through application code. ## Reference source [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [2-pane-synchronized.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/2-pane-synchronized.yaml). --- # Pane configuration Source: https://libtmux.org/en/cxx/latest/workspace/configuration/panes/ > Tmuxp pane configuration and current C++ builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. A pane can be a command string, a list of commands, or a mapping of settings. Each item in the window's `"panes"` list creates one pane; a command list inside that item describes several commands in that same pane. ```yaml session_name: pane-example start_directory: ./ windows: - window_name: main start_directory: ./ panes: - echo one command - [echo first command, echo second command] - shell_command: - echo configured pane start_directory: ./ focus: true - blank ``` ## Blank forms | Form inside `"panes"` | Reference interpretation | | --- | --- | | null, omitted YAML value, `blank`, or `"pane"` | Pane without its own commands | | Empty mapping or empty list | Expands to a pane without its own commands | | Mapping with no shell_command | Keeps the pane's other settings and uses no own commands | | `shell_command: null` or a single null command | No own commands | | Empty string `""` | Sends an empty command, normally pressing Enter | A blank pane can still receive inherited [before commands](../commands/). Blank forms do not disable session/window/pane setup. An omitted window panes key is defaulted to one blank pane; an explicitly empty panes list is a different shape and should not be used as a portable way to request that default. ## Pane keys | Key | Meaning | | --- | --- | | `shell_command` | String, ordered command list, or supported command dictionaries | | `shell_command_before` | Setup prepended after session/window before commands | | `start_directory` | Pane directory override | | `"shell"` | Shell/application launched for this pane | | `focus` | Select this pane in its window | | `"environment"` | Environment map selected for this pane's launch | | `"suppress_history"` | Pane history policy override | | `"enter"` | Default for whether to submit each command | | `sleep_before`, `sleep_after` | Default delays in seconds around each command | ## Launch a shell or type commands `"shell"` chooses the process tmux starts in the pane. `shell_command` sends text into the process already running there. Launching an application through shell can work with tmux's remain-on-exit behavior, while typing that application's name into a shell has different process semantics. A pane shell overrides window_shell, including on the first pane. The first pane also supplies its directory and environment during window creation. Use an installed shell/application path rather than assuming the same executable exists on every host. A successful build confirms command delivery, not application readiness or command exit status. See [commands](../commands/) for Enter, timing, and history, and [environment](../environment/) for launch-map selection. ## Current C++ builder Native pane commands support Enter, pause, and history metadata. Exact shorthand and shell/environment combinations still need the parser and execution behavior checked separately. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/src/tmuxp.cpp) before using these fields through application code. ## Reference source [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [blank-panes.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/blank-panes.yaml); [pane-shell.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/pane-shell.yaml). --- # Workspace commands Source: https://libtmux.org/en/cxx/latest/workspace/configuration/commands/ > Tmuxp workspace commands and current C++ builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Tmuxp sends workspace commands into panes in order. A command can be a string or a mapping with `"cmd"` and execution controls. `shell_command_before` adds shared setup without copying it into every pane. ```yaml session_name: command-example shell_command_before: - echo session setup windows: - window_name: main shell_command_before: - echo window setup panes: - shell_command_before: - echo pane setup shell_command: - cmd: echo ready sleep_after: 0.2 - cmd: echo typed but not submitted enter: false sleep_after: 0 ``` This sends the three setup commands, submits echo ready, waits 0.2 seconds, and leaves the final command at the prompt. The delay pauses construction; it does not ask the application whether it is ready. ## Forms and order `shell_command` and `shell_command_before` accept a scalar command or a list. A command dictionary requires `"cmd"` for the text. The loader expands shell variables and tilde expressions in command strings before the builder sends them. Variables already known to the process running tmuxp can therefore be substituted before a pane shell sees the command. For each pane, the loader concatenates session before commands, window before commands, pane before commands, then the pane's own commands. A blank pane still receives inherited setup. Quoting affects the pane shell too; YAML quoting alone does not bypass tmuxp's expansion step. ## Enter and delays | Setting | Default and scope | | --- | --- | | `"enter"` | True; pane default, then command override | | `sleep_before` | No delay; pane default, then command override in seconds | | `sleep_after` | No delay; pane default, then command override in seconds | In the classic builder, a command's enter/delay override carries forward to subsequent commands in that pane. Set `enter: true` or an explicit zero delay to restore that behavior for a later command. Absence means keep the current value; zero is an intentional delay override. Pauses run synchronously during construction. They can let a startup command settle but do not monitor its success. A failed pane command is not necessarily a failed tmux send operation, and starting a web server is not proof it is accepting connections. ## History and prompt readiness History suppression defaults to true. A session value trickles to a window, and a pane can override it. Suppression prefixes command text with a space. Bash needs HISTCONTROL containing ignorespace or ignoreboth; zsh needs HIST_IGNORE_SPACE. Without shell support, the command can still enter history. Builder pane_readiness is separate from command delays. Auto waits for the configured zsh session shell, while custom pane/window launch commands skip prompt waiting. See [hooks and builders](../hooks/) for the policy and its limits. A workspace [before_script](../hooks/) runs as a process outside the panes and checks its exit status. Use it for bootstrap work that must succeed before configured windows are built, rather than treating pane command delivery as a checked process result. ## Current C++ builder Command metadata supports enter, delays, and suppression. The consumer creates topology before delivering commands; it does not perform the complete tmuxp normalization pipeline. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/src/tmuxp.cpp) before using these fields through application code. ## Reference source [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [sleep.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/sleep.yaml); [skip-send.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/skip-send.yaml). --- # Workspace environment Source: https://libtmux.org/en/cxx/latest/workspace/configuration/environment/ > Tmuxp workspace environment and current C++ builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. The environment of the process running tmuxp controls discovery, expansion, and presentation. A workspace's `"environment"` mapping controls the tmux session or the environment passed when launching a pane. These are separate settings. ```yaml session_name: environment-example environment: WORKSPACE_ROLE: shared windows: - window_name: main environment: WINDOW_ROLE: tools panes: - echo window environment - environment: PANE_ROLE: isolated shell_command: env ``` The first pane receives the window launch map. The second selects its own pane map instead of merging it with the window map. It still inherits applicable tmux session/process environment. A pane map does not mean a completely empty environment plus that map. ## Expansion before launch Tmuxp expands tilde and environment-variable expressions in session/window names, paths, before_script, command strings, environment values, and string option values. It uses the environment of the process invoking tmuxp. It does not first populate that process environment from the workspace's environment mapping. For example, a command using an already-set process variable can be substituted before pane creation. Inspect the expanded intent when a variable should instead be read dynamically by a pane shell. Unknown variables and shell-specific expressions follow the loader's expansion and the eventual shell's rules, not a general template language. ## CLI and runtime variables | Variables | Effect | | --- | --- | | `TMUXP_CONFIGDIR`, `XDG_CONFIG_HOME`, `HOME` | Global workspace directory selection and home expansion | | `TMUXINATOR_CONFIG` | Tmuxinator import source directory | | `$EDITOR` | Editor executable; reference default is vim | | `$TMUX`, `$TMUX_PANE` | Current tmux connection and shell object context | | `TMUXP_PROGRESS` | Value 0 disables animated load progress | | `TMUXP_PROGRESS_FORMAT` | Default/minimal/window/pane/verbose preset or custom tokens | | `TMUXP_PROGRESS_LINES` | Script panel lines: default 3, 0 hides, -1 caps to terminal height | | `TMUXP_DETECT_TERMINAL_SIZE` | Value 1 enables size detection; default 1 | | `TMUXP_DEFAULT_COLUMNS`, `TMUXP_DEFAULT_ROWS` | Fallback session dimensions | | `COLUMNS`, `LINES`, `ROWS` | Terminal helper overrides and fallback sizing inputs | | `NO_COLOR`, `FORCE_COLOR` | Color policy; nonempty values are significant | | `PYTHONSTARTUP` | Startup file used by supported shell startup behavior | | `IPYTHON_ARGUMENTS` | Whitespace-split arguments for the IPython backend | | `PYTHONBREAKPOINT` | Can affect debugger selection in tmuxp shell | | `SHELL` | Shell diagnostics and readiness fallback | | `DISABLE_AUTO_TITLE` | Oh My Zsh automatic-title warning | | `PATH` | Executable lookup and diagnostics | | `LIBTMUX_TMUX_FORMAT_SEPARATOR` | Python libtmux format collection override | Explicit progress flags take precedence over their corresponding defaults. Nonempty NO_COLOR disables color even with always; otherwise explicit never/always precede FORCE_COLOR and automatic TTY detection. Machine-output extensions must disable ANSI regardless of forced color. `CLICOLOR` and `CLICOLOR_FORCE` are proposed cross-port presentation extensions, not variables read by this tmuxp reference. Native format codecs also need separate evidence before claiming the Python separator override works. See [directories](../directories/) for existing-directory precedence, [layouts](../layouts/) for size resolution, and [shell](../../cli/shell/) for Python-specific environment effects. ## Current C++ builder The parser accepts supported environment values, but tmuxp expansion and pane/window selection must be checked against native execution. Core format separator behavior is a codec concern, not a workspace print option. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/src/tmuxp.cpp) before using these fields through application code. ## Reference source [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [finders.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/finders.py); [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [load.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/load.py); [shell.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/shell.py); [shell.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/shell.py); [colors.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/_internal/colors.py); [util.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/util.py). --- # Workspace files and directories Source: https://libtmux.org/en/cxx/latest/workspace/configuration/directories/ > Tmuxp workspace files and directories and current C++ builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Tmuxp accepts an explicit workspace file, a saved workspace name, or a project directory. The location of the selected file determines how config-relative paths expand. Use an explicit file while debugging a discovery problem. Load a file on a dedicated server: ```console $ tmuxp load \ -L directory-example \ -d \ ./workspace.yaml ``` Load the current project's workspace: ```console $ tmuxp load . ``` These commands use Python tmuxp and assume the corresponding workspace file already exists. The first command leaves its session detached; use the session name from the file when inspecting or cleaning it up. ## Global and local discovery For the preferred global workspace directory, tmuxp tries TMUXP_CONFIGDIR, XDG_CONFIG_HOME/tmuxp (or the XDG default), then the legacy [`~/.tmuxp`](../../guides/discovery/) directory. It chooses the first existing directory. If none exists, it returns the legacy location. Setting TMUXP_CONFIGDIR to a nonexistent path does not automatically select that path over an existing fallback. Project discovery walks the current directory and its parents, choosing at most one workspace per directory in `.tmuxp.yaml`, [`.tmuxp.yml`](../../guides/discovery/), [`.tmuxp.json`](../../guides/discovery/) order. It stops at home or filesystem root. `ls` can report global directory candidates and locally discovered workspaces; that inventory is not identical to resolving one explicit load argument. The importers use their own source roots: Teamocil uses [`~/.teamocil`](../../cli/import-teamocil/); tmuxinator uses TMUXINATOR_CONFIG, with tilde expansion, or [`~/.tmuxinator`](../../cli/import-tmuxinator/). Their source argument is effectively required, even though the parser's positional arity looks optional. ## Start directories ```yaml session_name: directory-example start_directory: ./ windows: - window_name: root panes: - pwd - window_name: child start_directory: ./src panes: - pwd - start_directory: ./ shell_command: pwd ``` This example assumes the project has a `"src"` directory. The first window inherits the session directory. The child window resolves ./src against the explicit session directory, and its second pane resolves ./ against that window directory. Both child panes start in src. The root start_directory resolves a dot-relative path from the configuration file's directory. At child levels, the pinned loader resolves dot-relative paths against the immediate parent's start_directory before inherited defaults are filled. Define that parent value explicitly when using ./ or ../ in a child. A missing parent start_directory can raise KeyError during expansion. A plain relative window directory such as src is joined to the session directory later, during trickle. A dot-relative pane under that still-relative window can resolve against the invoking process's current directory first. Use an explicit ./src window value as above, or an absolute path, to avoid that inconsistency. Native normalization that resolves every child consistently would correct this reference behavior. When a document names no `start_directory` at any level, panes start in the directory the command was run from, not the directory the workspace file lives in. That is what tmuxp does, and all eight implementations agree on it. An explicit relative value such as `./src` is the other case: it always resolves against the workspace file's directory, at every level, so a workspace stays portable no matter where it is loaded from. Absolute and expanded-home paths retain their explicit location. Quote `~` in YAML to avoid its null spelling. A pane override also applies to the first pane in the Python classic builder. Inspect resolved values and the resulting pane directory when loading a workspace from a different working directory. ## Missing directories and bootstrap A nonexistent path can make tmux start somewhere unexpected, including a home directory, rather than producing a useful configuration error. Inspect the actual pane directory when verifying a workspace. Native ports may reject, retain, report, or pass through the value differently. A relative before_script path is resolved from the workspace file. Its process working directory uses the session start_directory when supplied. A bootstrap process can create required project files, but it runs after the initial tmux session exists. See [hooks](../hooks/) for failure handling and [environment](../environment/) for variable expansion. ## Current C++ builder The model carries directories; config discovery and all tmuxp expansion rules are not a CLI feature here. Open a core server handle against the intended live endpoint before using the consumer. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/src/tmuxp.cpp) before using these fields through application code. ## Reference source [finders.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/finders.py); [loader.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/loader.py); [import_config.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/import_config.py); [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [start-directory.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/start-directory.yaml). --- # Workspace layouts and focus Source: https://libtmux.org/en/cxx/latest/workspace/configuration/layouts/ > Tmuxp workspace layouts and focus and current C++ builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. A window's `"layout"` chooses how tmux arranges its panes. Named layouts adapt to the window size; explicit layout strings describe geometry more directly. The selected tmux version and terminal dimensions affect the final result. ```yaml session_name: layout-example windows: - window_name: main layout: main-horizontal focus: true options: main-pane-height: 60% panes: - shell_command: echo main pane focus: true - echo first lower pane - echo second lower pane ``` ## Layout names and options Common tmux layout names are even-horizontal, even-vertical, main-horizontal, main-vertical, and tiled. Layout availability belongs to the target tmux version. Options such as main-pane-height and main-pane-width shape applicable main-pane layouts. A row/column count and a percentage are different values; preserve that distinction in YAML/JSON. The classic builder applies window options before selecting a layout and applies options_after after panes and their setup commands. This lets a workspace size its main pane while delaying synchronize-panes until individual setup is complete. An explicit layout string can depend on the current number of panes and window size. A layout captured from one terminal is a starting point to inspect on another, not a portable pixel diagram. ## Terminal dimensions When TMUXP_DETECT_TERMINAL_SIZE is 1 (the default), the classic builder asks Python's terminal-size helper for initial session dimensions. COLUMNS and LINES can influence that helper. Fallback width uses TMUXP_DEFAULT_COLUMNS, then COLUMNS, then 80. Fallback height uses TMUXP_DEFAULT_ROWS, then ROWS, with nominal default 24. The helper can choose terminal dimensions instead of its fallback, so a TMUXP_DEFAULT value alone is not an unconditional size override. Detached sessions also need dimensions for reproducible layouts. Invalid numeric environment values can fail before useful construction. ## Focus and indexes Set window focus to select that window after construction, and pane focus to select the active pane within its window. Prefer one focused window and one focused pane per window; multiple true values depend on build order and are harder to reason about. A window_index selects its numeric tmux position. Pane IDs such as `%3` are runtime identities, not YAML pane list positions. Base-index and pane-base-index settings can make visible indexes differ from zero-based list positions. Inspect a running workspace's geometry and selection: ```console $ tmux -L layout-example list-windows -t '=layout-example' ``` This command assumes the sample workspace was loaded on the dedicated layout-example socket. Use [load](../../cli/load/) to select that socket, then inspect panes as needed. A successful load does not prove every native port honors the same focus/index/options policy. ## Current C++ builder Window options precede layout application and options_after follows pane creation. Commands address actual pane IDs. Failures report a window position and reason without rollback. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/src/tmuxp.cpp) before using these fields through application code. ## Reference source [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [main-pane-height.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/main-pane-height.yaml); [main-pane-height-percentage.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/main-pane-height-percentage.yaml); [focus-window-and-panes.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/focus-window-and-panes.yaml); [window-index.yaml](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/window-index.yaml). --- # Workspace hooks and builders Source: https://libtmux.org/en/cxx/latest/workspace/configuration/hooks/ > Tmuxp workspace hooks and builders and current C++ builder compatibility. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Workspace scripts, plugins, and custom builders extend how Python tmuxp creates a session. They are execution features, not passive configuration metadata. A workspace naming Python code needs that code installed or importable in tmuxp's environment. ## Bootstrap with before_script ```yaml session_name: bootstrap-example start_directory: ./ before_script: ./bootstrap.sh windows: - window_name: main panes: - echo bootstrap completed ``` This complete configuration assumes bootstrap.sh exists and is executable. Tmuxp resolves the script relative to the workspace file and uses the session start_directory as the process working directory when supplied. A zero exit status permits configured-window construction to continue; a failing script raises an error. The classic builder has already created the initial session when before_script runs. It kills that session when the bootstrap process fails. That does not undo files or other external effects created by the script. Pane shell_command is different: successful text delivery does not check the command's exit status. ## Python plugins `"plugins"` is a list of Python class references, conventionally a class in a package's plugin module. Install that package into the same Python environment as tmuxp. Plugins can declare tmux, libtmux, and tmuxp version requirements. The lifecycle includes these distinct hooks: | Hook | When it applies | | --- | --- | | `before_workspace_builder` | Initial session exists, before configured windows | | `on_window_create` | A window has been created, before its panes finish | | `after_window_finished` | That window's panes and setup have finished | | `"before_script"` | Plugin callback after session construction | | `reattach` | Reattachment to a session that already exists | The plugin callback named before_script is not the workspace before_script process. Their timing and execution mechanism differ. Plugin methods can change live tmux state; choose a plugin only when its behavior is intended for the workspace. ## Select a workspace builder The default is the built-in classic builder. `workspace_builder` can name a registered entry point in the [`tmuxp.workspace_builders`](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/registry.py) group, a `module:attribute` reference, or a dotted Python path. Custom builders receive expanded configuration and a libtmux server. `workspace_builder_paths` lists trusted directories temporarily added to Python's import path. Tilde and environment variables expand, relative entries resolve against the workspace file, and entries must exist as directories. Tmuxp does not use site.addsitedir for these paths. Adding an import path is not permission to treat arbitrary workspace files as inert data. A builder implements the synchronous build/session interface and cooperates with plugin, progress, before-script, script-output, and build-event callbacks. The configuration alone cannot establish that an arbitrary custom builder honors those callbacks or the classic builder's behavior. ## Pane readiness ```yaml session_name: readiness-example workspace_builder: classic workspace_builder_options: pane_readiness: auto windows: - window_name: main panes: - echo ready ``` Auto, the default, waits for a prompt when the configured session shell is zsh. Always requests the wait for default-shell panes; never skips it. Accepted aliases include true/on/yes/1 for always and false/off/no/0 for never, with strings normalized for case and surrounding whitespace. Unknown values are rejected. Custom pane/window launch commands skip prompt waiting. Readiness checks concern a shell prompt, not the eventual application's health, and do not acknowledge that every later command was consumed. Use [commands](../commands/) for explicit delays and Enter behavior. ## Current C++ builder The source consumer does not run Python plugin or builder imports. Session-building code can be extended in C++, but that is a distinct interface from tmuxp workspace hook configuration. See the [native builder behavior](../../internals/topics/) and [configuration source](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/src/tmuxp.cpp) before using these fields through application code. ## Reference source [classic.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/classic.py); [registry.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/registry.py); [protocol.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/builder/protocol.py); [options.py](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/workspace/options.py); [plugins.md](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/docs/topics/plugins.md). --- # Examples Source: https://libtmux.org/en/cxx/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/cxx/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. ```cpp // No tmux failure is thrown. Every call answers with a value that is either // the result or the reason there isn't one. const auto sessions = server.sessions(); if (!sessions.has_value()) { std::fprintf(stderr, "%s\n", sessions.error().diagnostic.c_str()); return 1; } for (const libtmux::Session& session : *sessions) { std::printf("%s has %lld window(s)\n", std::string{session.name()}.c_str(), session.window_count()); } const libtmux::Session& session = sessions->at(0); // Build an arrangement without composing a single tmux argument. const auto editor = session.new_window({.name = "editor"}); if (!editor.has_value()) { std::fprintf(stderr, "%s\n", editor.error().diagnostic.c_str()); return 1; } const auto logs = editor->split({.horizontal = true, .percentage = 30}); if (!logs.has_value()) { std::fprintf(stderr, "%s\n", logs.error().diagnostic.c_str()); return 1; } (void)logs->send_text("journalctl -f"); (void)logs->send_key("Enter"); ``` ## 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/cxx/latest/examples/capture-pane-output/ > Capture a pane's screen and wait for expected output or a completion signal. Read a pane after [sending input](../attach-and-send-keys/). [Sending keys](/guides/sending-keys/#the-race-you-cant-see-from-the-call-site) explains why an immediate capture can miss output. These examples show screen capture and waiting; [Capturing output](/guides/capturing-output/) explains the choices. See [source details](#where-this-comes-from) for each example's source and validation. ## Read what's on screen The .NET example under "Wait for text instead of guessing a delay" uses `Pane.CaptureAsync` within a wait. Its separate Psmux transport also provides a capture API, shown in `examples/LibTmux.Examples/Snippets/Psmux.cs`. ```cpp const auto visible = pane.capture(); if (visible.has_value()) { std::printf("%zu bytes on screen\n", visible->size()); } const auto history = pane.capture({.whole_history = true}); if (history.has_value()) { std::printf("%zu bytes of scrollback\n", history->size()); } ``` ## Wait for text instead of guessing a delay The Python example uses `wait_for`, tmux's signal channel. It waits for a signal from the command rather than matching pane text. `Session.OpenNotifications` receives tmux events as a stream. For tests that need to wait for screen text, use `tmuxtest.WaitForText`; see [Testing with libtmux](/guides/testing-with-libtmux/). Rust's `wait_for_text` looks before it sleeps, joins wrapped lines so a needle spanning a wrap still matches, and returns `PaneWait::Dead` rather than hanging forever if the pane's process ends first. Attach the `ControlClient` to receive `%output` notifications. An unattached client receives command replies only. `TmuxWait.UntilAsync` polls a read against a predicate rather than sleeping a fixed amount. C++ has no checked snippet that waits on pane *text*. `Server::wait_for(channel, timeout)`, in `include/libtmux/server.hpp`, uses tmux's own `wait-for` signal instead of scraping output, and its doc comment explains why that is the safer choice when the command you are waiting on can be made to announce itself: "a server that dies under a waiter makes tmux exit zero, which is indistinguishable from being signalled ... this reports that as a failure instead." `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/cxx/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/cxx/latest/workspace/examples/gallery/ > Pinned YAML examples, JSON counterparts, and execution prerequisites. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. The examples below reproduce the pinned tmuxp YAML fixture corpus. Each record links to its source and, where present, its JSON twin. They illustrate configuration features; they are not all self-contained runnable projects. The `minimal` fixture and several other files omit window names despite the validator requiring them. Treat these as normalization and compatibility probes, not a promise that every fixture passes every load path. Where this port's native parser accepts a fixture below, that establishes only that its document parses, not that it fully executes. The [installation walkthrough](../../guides/installation/) supplies a complete runnable starting file. ## 2-pane-synchronized [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/2-pane-synchronized.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/2-pane-synchronized.json). ```yaml session_name: 2-pane-synchronized windows: - window_name: Two synchronized panes panes: - ssh server1 - ssh server2 options_after: synchronize-panes: on ``` ## 2-pane-vertical [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/2-pane-vertical.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/2-pane-vertical.json). ```yaml session_name: 2-pane-vertical windows: - window_name: my test window panes: - echo hello - echo hello ``` ## 3-pane [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/3-pane.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/3-pane.json). ```yaml session_name: 3-panes windows: - window_name: dev window layout: main-vertical shell_command_before: - cd ~/ panes: - shell_command: - cd /var/log - ls -al | grep \.log - echo hello - echo hello ``` ## 4-pane [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/4-pane.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/4-pane.json). ```yaml session_name: 4-pane-split windows: - window_name: dev window layout: tiled shell_command_before: - cd ~/ panes: - shell_command: - cd /var/log - ls -al | grep \.log - echo hello - echo hello - echo hello ``` ## blank-panes [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/blank-panes.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/blank-panes.json). ```yaml session_name: Blank pane test windows: # Emptiness will simply open a blank pane, if no shell_command_before. # All these are equivalent - window_name: Blank pane test panes: - - pane - blank - window_name: More blank panes panes: - null - shell_command: - shell_command: - # an empty string will be treated as a carriage return - window_name: Empty string (return) panes: - "" - shell_command: "" - shell_command: - "" # a pane can have other options but still be blank - window_name: Blank with options panes: - focus: true - start_directory: /tmp ``` ## env-variables [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/env-variables.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/env-variables.json). ```yaml start_directory: "${PWD}/test" shell_command_before: "echo ${PWD}" before_script: "${MY_ENV_VAR}/test3.sh" session_name: session - ${USER} (${MY_ENV_VAR}) windows: - window_name: editor panes: - shell_command: - tail -F /var/log/syslog start_directory: /var/log - window_name: logging for ${USER} options: automatic-rename: true panes: - shell_command: - htop - ls $PWD ``` ## focus-window-and-panes [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/focus-window-and-panes.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/focus-window-and-panes.json). ```yaml session_name: focus windows: - window_name: attached window focus: true panes: - shell_command: - echo hello - echo 'this pane should be selected on load' focus: true - shell_command: - cd /var/log - echo hello - window_name: second window shell_command_before: cd /var/log panes: - pane - shell_command: - echo 'this pane should be focused, when window switched to first time' focus: true - pane ``` ## main-pane-height-percentage [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/main-pane-height-percentage.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/main-pane-height-percentage.json). ```yaml session_name: main-pane-height start_directory: "~" windows: - layout: main-horizontal options: main-pane-height: 67% panes: - shell_command: - top start_directory: "~" - shell_command: - echo "hey" - shell_command: - echo "moo" window_name: my window name ``` ## main-pane-height [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/main-pane-height.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/main-pane-height.json). ```yaml session_name: main-pane-height start_directory: "~" windows: - layout: main-horizontal options: main-pane-height: 30 panes: - shell_command: - top start_directory: "~" - shell_command: - echo "hey" - shell_command: - echo "moo" window_name: my window name ``` ## minimal [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/minimal.yaml). ```yaml session_name: My tmux session windows: - panes: - ``` ## options [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/options.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/options.json). ```yaml session_name: test window options start_directory: "~" global_options: default-shell: /bin/sh default-command: /bin/sh options: main-pane-height: ${MAIN_PANE_HEIGHT} # works with env variables windows: - layout: main-horizontal options: automatic-rename: on panes: - shell_command: - man echo start_directory: "~" - shell_command: - echo "hey" - shell_command: - echo "moo" ``` ## pane-shell [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/pane-shell.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/pane-shell.json). ```yaml session_name: Pane shell example windows: - window_name: first window_shell: /usr/bin/python2 layout: even-vertical suppress_history: false options: remain-on-exit: true panes: - shell: /usr/bin/python3 shell_command: - print('This is python 3') - shell: /usr/bin/vim -u none shell_command: - iAll panes have the `remain-on-exit` setting on. - When you exit out of the shell or application, the panes will remain. - Use tmux command `:kill-pane` to remove the pane. - Use tmux command `:respawn-pane` to restart the shell in the pane. - Use and then `:q!` to get out of this vim window. :-) - shell_command: - print('Hello World 2') - shell: /usr/bin/top ``` ## plugin-system [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/plugin-system.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/plugin-system.json). ```yaml session_name: plugin-system plugins: - "tmuxp_plugin_extended_build.plugin.PluginExtendedBuild" windows: - window_name: editor layout: tiled shell_command_before: - cd ~/ panes: - shell_command: - cd /var/log - ls -al | grep *.log - echo "hello world" ``` ## session-environment [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/session-environment.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/session-environment.json). ```yaml session_name: Environment variables test environment: EDITOR: /usr/bin/vim DJANGO_SETTINGS_MODULE: my_app.settings.local SERVER_PORT: "8009" windows: - window_name: Django project panes: - ./manage.py runserver 0.0.0.0:${SERVER_PORT} - window_name: Another Django project environment: DJANGO_SETTINGS_MODULE: my_app.settings.local SERVER_PORT: "8010" panes: - ./manage.py runserver 0.0.0.0:${SERVER_PORT} - environment: DJANGO_SETTINGS_MODULE: my_app.settings.local-testing SERVER_PORT: "8011" shell_command: ./manage.py runserver 0.0.0.0:${SERVER_PORT} ``` ## shorthands [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/shorthands.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/shorthands.json). ```yaml session_name: shorthands windows: - window_name: long form panes: - shell_command: - echo 'did you know' - echo 'you can inline' - shell_command: echo 'single commands' - echo 'for panes' ``` ## skip-send-pane-level [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/skip-send-pane-level.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/skip-send-pane-level.json). ```yaml session_name: Skip command execution (pane-level) windows: - panes: - shell_command: echo "___$((1 + 3))___" enter: false - shell_command: - echo "___$((1 + 3))___"\; - echo "___$((1 + 3))___" enter: false ``` ## skip-send [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/skip-send.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/skip-send.json). ```yaml session_name: Skip command execution (command-level) windows: - panes: - shell_command: # You can see this - echo "___$((11 + 1))___" # This is skipped - cmd: echo "___$((1 + 3))___" enter: false ``` ## sleep-pane-level [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/sleep-pane-level.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/sleep-pane-level.json). ```yaml session_name: Pause / skip command execution (pane-level) windows: - panes: - # Wait 2 seconds before sending all commands in this pane sleep_before: 2 shell_command: - echo "___$((11 + 1))___" - cmd: echo "___$((1 + 3))___" - cmd: echo "___$((1 + 3))___" - cmd: echo "Stuff rendering here!" - cmd: echo "2 seconds later" ``` ## sleep-virtualenv [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/sleep-virtualenv.yaml). ```yaml session_name: virtualenv shell_command_before: # - cmd: source $(poetry env info --path)/bin/activate # - cmd: source `pipenv --venv`/bin/activate - cmd: source .venv/bin/activate sleep_before: 1 sleep_after: 1 windows: - panes: - shell_command: - ./manage.py runserver ``` ## sleep [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/sleep.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/sleep.json). ```yaml session_name: Pause / skip command execution (command-level) windows: - panes: - shell_command: # Executes immediately - echo "___$((11 + 1))___" # Delays before sending 2 seconds - cmd: echo "___$((1 + 3))___" sleep_before: 2 # Executes immediately - cmd: echo "___$((1 + 3))___" # Pauses 2 seconds after - cmd: echo "Stuff rendering here!" sleep_after: 2 # Executes after earlier commands (after 2 sec) - cmd: echo "2 seconds later" ``` ## start-directory [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/start-directory.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/start-directory.json). ```yaml session_name: start directory start_directory: /var/ windows: - window_name: should be /var/ panes: - shell_command: - echo "\033c - it trickles down from session-level" - echo hello - window_name: should be /var/log start_directory: log panes: - shell_command: - echo '\033c - window start_directory concatenates to session start_directory - if it is not absolute' - echo hello - window_name: should be ~ start_directory: "~" panes: - shell_command: - 'echo \\033c ~ has precedence. note: remember to quote ~ in YAML' - echo hello - window_name: should be /bin start_directory: /bin panes: - echo '\033c absolute paths also have precedence.' - echo hello - window_name: should be workspace file's dir start_directory: ./ panes: - shell_command: - echo '\033c - ./ is relative to workspace file location - ../ will be parent of workspace file - ./test will be \"test\" dir inside dir of workspace file' - shell_command: - echo '\033c - This way you can load up workspaces from projects and maintain - relative paths.' ``` ## suppress-history [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/suppress-history.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/suppress-history.json). ```yaml session_name: suppress suppress_history: false windows: - window_name: appended focus: true suppress_history: false panes: - echo "window in the history!" - window_name: suppressed suppress_history: true panes: - echo "window not in the history!" - window_name: default panes: - echo "session in the history!" - window_name: mixed suppress_history: false panes: - shell_command: - echo "command in the history!" suppress_history: false - shell_command: - echo "command not in the history!" suppress_history: true - shell_command: - echo "window in the history!" ``` ## window-index [YAML source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/window-index.yaml); [JSON source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/examples/window-index.json). ```yaml session_name: Window index example windows: - window_name: zero panes: - echo "this window's index will be zero" - window_name: five panes: - echo "this window's index will be five" window_index: 5 - window_name: one panes: - echo "this window's index will be one" ``` ## Prerequisites and portability SSH examples need the named hosts. Django and virtualenv examples need their project and environment. The plugin example needs its named Python plugin. Shell paths, log directories, top, `htop`, and editors are host-specific. The pane-shell fixture includes an obsolete Python 2 path; it is retained as upstream evidence, not recommended installation guidance. Review [configuration](../../configuration/), [commands](../../configuration/commands/), and [compatibility](../../reference/compatibility/) before adapting these fixtures. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Guides Source: https://libtmux.org/en/cxx/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/cxx/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. ```cpp // vcpkg install libtmux-cxx const auto sessions = server.sessions(); // sessions->at(0) is this example's session, from an already-open scratch server. const libtmux::Session& session = sessions->at(0); // Build an arrangement without composing a single tmux argument. const auto editor = session.new_window({.name = "editor"}); const auto logs = editor->split({.horizontal = true, .percentage = 30}); (void)logs->send_text("journalctl -f"); (void)logs->send_key("Enter"); // Read a pane's visible contents, or its scrollback. const auto visible = logs->capture(); ``` ## 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/cxx/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`. ```cpp // Four named constructors instead of one flexible one: pick the one that // names how you're reaching this tmux: libtmux::Server::from_env(); // inside tmux libtmux::Server::at_socket_name(name); libtmux::Server::at_socket_path(path); libtmux::Server::at_default(); // "my tmux", to a person ``` 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. 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/cxx/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. ```cpp // Literal text, never interpreted as key names or formats, and never // followed by a newline the caller did not ask for. pane.send_text("echo hey"); // One named key, sent separately: this is how Enter gets pressed. pane.send_key("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/cxx/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: ```cpp // A capture that doesn't fit is reported, not silently truncated: // output_limit says how much you're prepared to hold. const auto visible = pane.capture(); const auto history = pane.capture({.whole_history = true}); ``` 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: 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/cxx/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: ```cpp auto addressed = *panes | libtmux::matching(libtmux::pane::id == panes->at(0).id()); if (const auto one = libtmux::exactly_one(addressed); one.has_value()) { std::printf("exactly one: %s\n", std::string{one->get().id()}.c_str()); } ``` 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 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/cxx/latest/guides/testing-with-libtmux/ > Every port ships a way to give your own tests a real, disposable tmux server. What each one hands you, and what it guarantees about cleanup. Use a private tmux server to test code that creates sessions, sends input, or captures output. The fixtures below allocate a separate socket and manage normal test cleanup. Cleanup after abrupt process termination depends on the fixture; Java's stale-server cleanup is described below. Request the fixture to obtain its server. Python's `session` fixture depends on `server`, so requesting a session also creates an isolated server: That exact block is a doctest in `src/libtmux/pytest_plugin.py`, checked by running it as a nested pytest run and asserting it passes. `session_params` overrides how the fixture builds a session (window size, for instance) without forking it; a temporary `HOME` and tmux config keep window and pane indices stable across machines, so an assertion like `window_name == "test"` doesn't depend on whatever `.tmux.conf` the test runner happens to have. `tmuxtest.NewServer(ctx, t)` captures the environment and working directory, resolves the tmux executable, and creates a server on its own socket. Construction can return an error. Test cleanup kills the server, and wait failures include the last captured screen. Source: `README.md`, "Testing your own code," backed by `tmux/tmuxtest/`. Enable the `test-support` feature in a dev-dependency. The crate README uses these guards in doctests through `#![doc = include_str!("../README.md")]`. `libtmux::test::retry_until(deadline, condition)` polls an arbitrary async condition; `Pane::wait_for_text` waits specifically for pane text. See [Capturing output](../capturing-output/). `libtmux-junit5` supplies each test with a running `Server` containing a session named `libtmux`. Request `TmuxSocketPath` when your code takes a socket path. Fixtures live in JUnit's per-test extension store. A shutdown hook kills servers owned by that JVM, and startup cleanup removes servers left by JVMs that have exited. Source: `libtmux-junit5/README.md`. The port's `docs-tests` module compiles Java fences from READMEs and guides, then runs them against `libtmux-junit5` servers. A `` directive can instead require a named exception, a compile failure, or an explicit skip reason. Source: `docs-tests/README.md`. `LibTmux.Testing` ships as a separate package, under `src/LibTmux.Testing/`. `await using` disposes the scope and kills its server when the block exits. Use `TmuxWait.UntilAsync` to wait for expected state; see [Capturing output](../capturing-output/). Source: `README.md`, "Testing your own code," exercised by `ReadmeExampleTests`. ```cpp // A private tmux for a suite of your own, gone when the scope ends. auto fixture = libtmux::test::ScopedTmuxServer::start( {.socket_namespace = libtmux::test::SocketNamespace::consumer("my-suite")}); if (!fixture.has_value()) { std::fprintf(stderr, "%s\n", fixture.error().c_str()); return 1; } const auto under_test = libtmux::Server::at_socket_path(fixture->socket_path().string()); std::printf("sessions on it: %zu\n", under_test->sessions()->size()); ``` 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/cxx/latest/workspace/guides/installation/ > Build the local C++ workspace CLI and load a session on a private tmux socket. Build and run the native C++ `tmux-workspace` command from the local `workspace-cli` checkout. **This is a partial, unreleased implementation.** These commands require that local source; they are not registry installation instructions or a claim that the CLI is available on the published branch. ## Build from the local checkout Run these commands from the native repository root. Use a Unix environment with tmux 3.2a or newer on `PATH` for this walkthrough. The development preset requires Clang 18.1.3 with libc++ 18.1, CMake 3.25 or newer, and Ninja. It builds C++23 and fetches pinned optional CLI dependencies. Core libtmux remains independent of CLI11, yaml-cpp and nlohmann JSON. ```console $ cmake --preset cxx-dev \ -DLIBTMUX_BUILD_WORKSPACE_CLI=ON ``` ```console $ cmake --build --preset cxx-dev \ --target tmux-workspace \ --parallel 2 ``` Use `load -d` for the detached workflow below. The CLI rejects unsupported legacy `-8` before file or backend access; `-2` selects 256-colour mode. Several other planned commands/options appear in help with explicit unavailable behavior. Inspect the built command: ```console $ build/cxx-dev/apps/workspace/tmux-workspace --help ``` ## Create the input Keep this shell open for the walkthrough. Create a temporary directory for its configuration and private tmux socket: ```console $ WORKSPACE_TMP="$(mktemp -d)" ``` Write a minimal configuration with two blank shell panes to [`workspace.yaml`](./#create-the-input) inside that directory: ```console $ cat > "$WORKSPACE_TMP/workspace.yaml" <<'YAML' session_name: workspace-guide windows: - window_name: editor layout: even-horizontal panes: [null, null] YAML ``` ## Load and inspect Load detached on the temporary socket. The JSON result describes the load; `-d` prevents terminal attachment: ```console $ build/cxx-dev/apps/workspace/tmux-workspace load \ -S "$WORKSPACE_TMP/tmux.sock" \ -d \ --json \ "$WORKSPACE_TMP/workspace.yaml" ``` Inspect the two panes through the same endpoint: ```console $ tmux \ -S "$WORKSPACE_TMP/tmux.sock" \ list-panes \ -t '=workspace-guide:editor' ``` Attach with tmux when ready: ```console $ tmux \ -S "$WORKSPACE_TMP/tmux.sock" \ attach-session \ -t '=workspace-guide' ``` Detach with your configured tmux detach binding. Capture the live session without choosing a file destination: ```console $ build/cxx-dev/apps/workspace/tmux-workspace freeze \ -S "$WORKSPACE_TMP/tmux.sock" \ --json \ workspace-guide ``` Capture reports recoverable live state. It cannot reconstruct the original command history, script or plugin definitions. Remove the walkthrough session when finished: ```console $ tmux \ -S "$WORKSPACE_TMP/tmux.sock" \ kill-session \ -t '=workspace-guide' ``` The configuration remains in the temporary directory until you remove it. Every tmux command above addresses that private socket. ## Current limits Python shell/plugin/custom-builder execution, full importer/configuration coverage and portable packaging remain unfinished. Native progress and [shell completion](../../cli/completion/#native-completion) are implemented; dynamic session/configuration-name suggestions remain unavailable. Native before scripts, terminal handoff and file logging are implemented; see [current coverage](../../reference/compatibility/) for their limits. Capture preserves local session/window options but omits inherited/global options and environment. It cannot recover original command arguments or history. ## Python alternative For the separate released tmuxp application, install its isolated Python tool environment with uv: ```console $ uv tool install tmuxp ``` Follow the [Python installation guide](/py/latest/workspace/guides/installation/) for that workflow. Installing tmuxp does not install the native command. ## Continue [Discovery](../discovery/), [configuration](../../configuration/) and the [load reference](../../cli/load/) explain the tmuxp compatibility model. Compare those references with the local command's help and the limits above. [Export and reload](../export-session/) explains the capture workflow, and the [compatibility reference](../../reference/compatibility/) records builder gaps. Use [Internals](../../internals/) for the library and consumer APIs. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Find saved workspaces Source: https://libtmux.org/en/cxx/latest/workspace/guides/discovery/ > Resolve project files, explicit paths, and global workspace names. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Use an explicit YAML or JSON file path when you need an unambiguous input. A directory resolves its project configuration, and a saved name resolves through the tmuxp configuration roots. ## Global directories An existing `TMUXP_CONFIGDIR` takes precedence, followed by the XDG configuration directory and then the legacy [`~/.tmuxp`](./) directory. A nonexistent explicit directory does not automatically win discovery. See [environment](../../configuration/environment/) for the relevant variables. ## Project files Project discovery walks from the current directory toward its ancestors, stopping at home or the filesystem root. It selects at most one candidate per directory, preferring `.tmuxp.yaml`, then [`.tmuxp.yml`](./), then [`.tmuxp.json`](./). Nearer directories come first. It does not recursively enumerate child projects. Load the current project's configuration: ```console $ tmuxp load . ``` The normal command can attach or prompt. Use `-d` when you want detached execution and select `-L` or `-S` for an isolated server. [ls](../../cli/ls/), [search](../../cli/search/), and [edit](../../cli/edit/) use the same discovery concepts with their own result and error behavior. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Automate workspace operations Source: https://libtmux.org/en/cxx/latest/workspace/guides/automation/ > Python automation and the local native CLI machine protocol. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. For the current Python loader, supply an explicit file, dedicated socket, and `-d` to avoid attachment. `--yes` answers yes/no questions; it is not a universal replacement for missing session, format, or destination choices. ## Read current records ```console $ tmuxp ls --json ``` Validate the JSON object before consuming its `workspaces` array. Python search has different empty-result behavior: no matches can produce no bytes rather than `[]`. See [search](../../cli/search/) before relying on a pipeline. ## Proposed native machine protocol Local native CLIs provide `--json` and `--ndjson`. Implemented services and terminal behavior vary by port. The shared machine contract resolves choices from arguments, avoid implicit stdin prompts, keep diagnostics on stderr, and prevent child output or attachment from corrupting stdout. The operation result must report partial completion instead of claiming rollback. NDJSON load events must be observable while work is still running, followed by exactly one terminal record. A buffered array split into lines after completion does not meet this requirement. Both output flags together select NDJSON. Read [output](../../reference/output/) for stream shapes and [exit behavior](../../reference/exit-codes/) for errors and interruption. [Export and reload](../export-session/) distinguishes document encoding from CLI result encoding. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Export and reload a session Source: https://libtmux.org/en/cxx/latest/workspace/guides/export-session/ > Capture, inspect, and replay a workspace without assuming lossless recovery. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Use the session created by the [installation walkthrough](../installation/). Capture it to a new YAML destination: ```console $ tmuxp freeze \ -L workspace-guide \ --workspace-format yaml \ --save-to captured-workspace.yaml \ --yes \ workspace-guide ``` Review the output file. Capture cannot reconstruct original scripts, shell history, plugin decisions, comments, or every application's state. Compare window and pane topology, directories, layouts, environment, and options explicitly. Replay under a new name on the same dedicated server: ```console $ tmuxp load \ -L workspace-guide \ -d \ -s workspace-replay \ captured-workspace.yaml ``` Inspect the replay: ```console $ tmux -L workspace-guide list-panes -t '=workspace-replay' ``` Remove the replay when finished: ```console $ tmux -L workspace-guide kill-session -t '=workspace-replay' ``` The [freeze reference](../../cli/freeze/) explains prompts and overwrite behavior. [convert](../../cli/convert/) changes document representation without validating native execution support. The [native compatibility page](../../reference/compatibility/) records which ports lack capture or lose fields. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Troubleshoot workspace loading Source: https://libtmux.org/en/cxx/latest/workspace/guides/troubleshooting/ > Diagnose argument, discovery, configuration, shell, and partial-build failures. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Start by identifying the failing stage. A parser error happens before a workspace is loaded. A missing file is a discovery problem. An accepted document can still fail during tmux creation, shell startup, command dispatch, or a plugin callback. ## Arguments and files Use the [command reference](../../cli/) for local flag meanings. Place root `--color` and `--log-level` before the command. Keep multiple load filenames together. Both importer children require a source argument. Confirm an explicit file works before investigating saved-name discovery. ## Configuration and commands Check the [configuration reference](../../configuration/) and the [example gallery](../../examples/gallery/). YAML parsing alone does not validate keys or prove builder support. A missing shell executable, directory, plugin package, SSH target, or application can prevent an otherwise valid workspace from behaving as intended. Commands are sent into panes and can require shell readiness. `enter: false` intentionally leaves text unsubmitted. Delays are measured in seconds; explicit zero and omission differ. See [commands](../../configuration/commands/) and [hooks](../../configuration/hooks/). ## Diagnostics and remaining state ```console $ tmuxp debug-info --json ``` Inspect raw tmux values before sharing diagnostics. Use the same `-L` or `-S` endpoint when inspecting a failed load. A partial build can leave sessions, windows, and panes behind; inspect them before cleanup. Do not assume rollback or kill an unrelated default server. Native failures must be evaluated against [port compatibility](../../reference/compatibility/), not inferred from a Python example. The native machine contract reports completed and failed stages explicitly. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Inspect a workspace through MCP Source: https://libtmux.org/en/cxx/latest/workspace/guides/inspect-with-mcp/ > Connect the development C++ MCP server to a session loaded by its native workspace CLI. Inspect the session you loaded with the C++ workspace CLI by pointing its MCP server at the same tmux socket. The loaded windows and panes are ordinary tmux objects; discovery returns their existing IDs. **This guide uses development `workspace-cli` source.** Continue the [installation walkthrough](../installation/#load-and-inspect) through its detached load, keeping that shell and `WORKSPACE_TMP` available. Leave the `workspace-guide` session running. Build both executables from the same native repository checkout; released package instructions may describe different MCP contracts. ## Build the MCP server Run from the native repository root with the installation walkthrough's toolchain and dependencies. ```console $ cmake --preset cxx-dev \ -DLIBTMUX_BUILD_WORKSPACE_CLI=ON \ -DLIBTMUX_BUILD_MCP_SERVER=ON ``` Build the MCP executable; the target also copies its minimal configuration: ```console $ cmake --build --preset cxx-dev \ --target libtmux_mcp_server \ --parallel 2 ``` ## Select the same socket Configure an MCP client to launch the following command with the shown environment. The client owns the process's standard input and output for JSON-RPC messages. ```console $ LIBTMUX_TOOLSETS=inspect \ build/cxx-dev/apps/mcp/libtmux-mcp-server \ --socket-path "$WORKSPACE_TMP/tmux.sock" ``` In a client's configuration file, use absolute executable or script paths and expand `WORKSPACE_TMP` to its actual value. Configuration files do not perform shell variable expansion. Retain the environment used to build and run the native executable. For a workspace loaded with `-L NAME`, use MCP's `--socket-name NAME` instead. Keep the same tmux executable on `PATH` for both processes. ## Inspect and wait 1. Discover tools with `tools/list` and read the `tmux://capabilities` resource. Confirm that its resolved endpoint matches the loaded socket. 2. Call [list_sessions][mcp-source], then [list_windows][mcp-source] with `session: "workspace-guide"`, and [list_panes][mcp-source]. Retain the returned session, window and pane IDs. 3. Select one returned pane ID for capture or a bounded text wait. Use the argument names below; discover the full schema before adding options. | Tool | Arguments | | --- | --- | | [capture_pane][mcp-source] | `"paneId"` | | [wait_for_text][mcp-source] | `"target"`, literal `"text"`, `"timeout_ms"` in milliseconds | Set `"timeout_ms"` to `10000` for a ten-second wait. A pending wait permits other inspection calls on the same connection. To check that behavior, start a wait for text absent from the pane, then request [list_panes][mcp-source] before its deadline. Client cancellation uses `notifications/cancelled` with the outstanding request ID; the connection remains usable for inspection. [capture_pane][mcp-source] returns visible text. Use [snapshot_pane][mcp-source] for structured screen state and [capture_since][mcp-source] for incremental observation. If discovery does not show `workspace-guide`, compare the resolved socket in capabilities with the CLI's `-S` path. A different socket selects a different daemon even when session names match. ## Close the connection Close the MCP connection's standard input to stop the server and release pending work. This separately loaded workspace remains running. When you finish the walkthrough, remove only its session on the same socket: ```console $ tmux \ -S "$WORKSPACE_TMP/tmux.sock" \ kill-session \ -t '=workspace-guide' ``` The configuration remains in the temporary directory until you remove it. See the verified [native workspace workflow][workspace-source] and [development MCP reference][mcp-source] for this source contract. The site's released MCP pages retain their version-pinned contracts. [workspace-source]: https://github.com/libtmux/libtmux-cxx/blob/ef40c60dceafa890fc32b85ff7ee0c24baf84440/apps/workspace/README.md [mcp-source]: https://github.com/libtmux/libtmux-cxx/blob/ef40c60dceafa890fc32b85ff7ee0c24baf84440/apps/mcp/README.md --- # C++ workspace internals Source: https://libtmux.org/en/cxx/latest/workspace/internals/ > Architecture and development interfaces of the C++ workspace builder. These pages document the in-development workspace builder for contributors and applications that call its APIs. For workspace loading from a terminal, see [tmuxp](https://tmuxp.git-pull.com/). ## Builder pipeline `parse_tmuxp` converts YAML into typed workspace data. `build` applies that data through a core `Server`. The `workspace_builder` target keeps YAML parsing separate from the core library. Its executables exercise parsing and builder tests; they do not load workspace files as a user application. ## Read the implementation - [Guides](./guides/) show builder setup and application code. - [Topics](./topics/) explain configuration, behavior, and failures. - [Examples](./examples/) exercise the builder through the language API. - [API](../reference/) links the configuration and construction interfaces. ## Implementation scope The C++ repository includes a workspace consumer that builds a described tmux session and reads [tmuxp](https://tmuxp.git-pull.com)-style YAML. It exercises libtmux's public API from a separate target. The workspace headers and parser belong to `examples/workspace`. They are not installed with the core libtmux package. Use the consumer from a source checkout or adapt it into your own application with its dependencies. ## Dependencies The workspace builder uses the core C++ API. Reading YAML adds yaml-cpp to the consumer target; the core library does not acquire that dependency. Building and exercising the consumer requires the repository's CMake toolchain and tmux on a supported host. The consumer creates a new session. It does not converge an existing session or export a live session back to a workspace file. [Consumer documentation](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/README.md) --- # C++ workspace builder behavior Source: https://libtmux.org/en/cxx/latest/workspace/internals/topics/ > Internal configuration, application, and failure contracts of the C++ workspace builder. The consumer separates parsing from application. `parse_tmuxp` reads a YAML document into a `Workspace`; `build` applies the typed description through a core `Server`. ## Supported data Configuration covers session and window names, working directories, environment values, options, layouts, focus, window indexes, and pane commands. The YAML reader rejects keys outside its supported subset and returns a document path and reason. Python runtime features are not loaded. A `Command` holds text, whether to press Enter, pauses before and after sending, and history suppression. A command with `enter: false` leaves text at the prompt. Pauses delay construction; they do not check application readiness. History suppression adds a leading space, whose effect depends on the shell's history configuration. ## Build ordering Open the core server handle after its tmux socket exists. A handle opened before daemon startup can become stale when the builder creates the first session. The consumer tests use a running isolated fixture before connecting. The builder creates all described windows and panes before delivering pane commands. It addresses panes by their actual IDs, so a configured `pane-base-index` does not redirect input to the wrong pane. Window options are applied before layouts. `options_after` is applied once panes exist, which allows settings such as synchronized input to take effect after creation. Focus choices are applied after the corresponding objects exist. ## Failure effects The requested session must be new. A failure returns `BuildError` with a window position and diagnostic reason. Operations completed before the failure remain in tmux; there is no rollback or attached partial-session handle in the error. Inspect the dedicated server before retrying or removing a session. The parser has its own `ParseError`, with a path through the YAML document. Keep parsing errors separate from tmux failures when reporting a problem to someone editing the workspace file. [Build and command contracts](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/include/libtmux_consumers/workspace.hpp); [Parsing contract](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/include/libtmux_consumers/tmuxp.hpp). --- # Develop the C++ workspace consumer Source: https://libtmux.org/en/cxx/latest/workspace/internals/guides/ > Build and exercise the source-only C++ workspace consumer. Try the workspace consumer from the libtmux C++ source checkout. It is built as a repository target, so installing the core package alone does not provide its headers or YAML reader. ## Build the consumer Use the repository's prepared development toolchain and install tmux. From the checkout root, configure the development preset: ```console $ cmake --preset cxx-dev ``` Build the workspace test executable: ```console $ cmake --build --preset cxx-dev --target workspace_builder_test ``` Run the consumer tests against their isolated tmux fixtures: ```console $ ctest --preset cxx-dev -R consumer.workspace --output-on-failure ``` The tests exercise both YAML parsing and typed configuration. The [example](../examples/) shows the session shape used by the builder test. ## Use it in an application The `workspace_builder` CMake target provides the consumer include directory, links the core [`libtmux::libtmux`](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/CMakeLists.txt) target publicly, and keeps yaml-cpp private to the YAML reader. If you adapt the consumer, preserve those dependency boundaries and include the parser implementation when using `parse_tmuxp`. Parse first and inspect `ParseError` before contacting tmux. Start the target tmux server before opening its core handle, as the test fixture does. Pass a core `Server` and the parsed description to `build`. Check the returned expected value before reading its session; after failure, inspect the target server because earlier operations may remain. There is no separate installed workspace product in this source tree. Do not expect core package managers to expose `libtmux_consumers/workspace.hpp`. [Consumer build targets](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/CMakeLists.txt) --- # C++ workspace builder examples Source: https://libtmux.org/en/cxx/latest/workspace/internals/examples/ > Internal examples for building and inspecting workspaces through the C++ API. The consumer's builder test creates an editor window with two panes and a logs window with one pane. It uses `ScopedTmuxServer` to isolate and clean up tmux. ## Describe two windows With `libtmux_consumers/workspace.hpp` included and a core `Server` named `server`, this is the description used by the real-tmux test: ```cpp namespace workspace = libtmux::workspace; const workspace::Workspace description{ .session_name = "built", .windows = {{.name = "editor", .panes = {{}, {}}}, {.name = "logs", .panes = {{}}}}}; const auto built = workspace::build(server, description); if (!built.has_value()) { throw std::runtime_error(built.error().reason); } ``` The error-handling line needs ``. The consumer headers and `workspace_builder` target come from the source checkout. For a complete isolated setup, run the linked test rather than targeting your normal server. ## YAML and command checks `tmuxp_test.cpp` reads command shorthand and mappings, reports unsupported fields, and verifies the resulting values. `workspace_test.cpp` checks live windows and panes, command delivery under non-default pane indexes, environment values, and text sent without Enter. Run both groups with the [consumer guide](../guides/). These tests exercise the consumer's public use of libtmux; they do not make its workspace types part of the installed core package. [Builder test](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/tests/workspace_test.cpp); [YAML tests](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/tests/tmuxp_test.cpp). --- # C++ workspace builder API Source: https://libtmux.org/en/cxx/latest/workspace/reference/ > Internal reference for the C++ workspace builder and configuration APIs. The workspace API described here belongs to the repository consumer. Its headers are available through the `workspace_builder` target and are not installed core library headers. ## Typed description `libtmux_consumers/workspace.hpp` defines the `libtmux::workspace` namespace: - `Workspace` holds the session name, directories, options, environment, and windows. - `Window` holds layout, options, index, focus, and panes. - `Pane` holds its shell, directory, environment, focus, and commands. - `Command` controls text, Enter, delays, and history suppression. `build(const Server&, const Workspace&)` returns `libtmux::expected`. `BuildError` contains a window index and reason. It does not contain a rollback result or guarantee that the server is unchanged. ## YAML reader `libtmux_consumers/tmuxp.hpp` declares `parse_tmuxp(std::string_view)`, returning `libtmux::expected`. `ParseError.where` identifies the configuration path and `reason` explains the refusal. The compiled implementation in `src/tmuxp.cpp` is the part that depends on yaml-cpp. Applications constructing `Workspace` values directly do not need to parse YAML. ## Core operations The returned session is a core libtmux value. Use the [C++ core reference](../../reference/) for subsequent inspection and mutation. Consumer source contracts remain the authority for the workspace types. [Workspace header](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/include/libtmux_consumers/workspace.hpp); [YAML header](https://github.com/libtmux/libtmux-cxx/blob/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee/examples/workspace/include/libtmux_consumers/tmuxp.hpp). ## API declarations - [libtmux::workspace::build](https://libtmux.org/en/cxx/latest/workspace/reference/libtmux-workspace-build/) - [libtmux::workspace::BuildError](https://libtmux.org/en/cxx/latest/workspace/reference/libtmux-workspace-builderror/) - [libtmux::workspace::Command](https://libtmux.org/en/cxx/latest/workspace/reference/libtmux-workspace-command/) - [libtmux::workspace::Pane](https://libtmux.org/en/cxx/latest/workspace/reference/libtmux-workspace-pane/) - [libtmux::workspace::parse_tmuxp](https://libtmux.org/en/cxx/latest/workspace/reference/libtmux-workspace-parse_tmuxp/) - [libtmux::workspace::ParseError](https://libtmux.org/en/cxx/latest/workspace/reference/libtmux-workspace-parseerror/) - [libtmux::workspace::Window](https://libtmux.org/en/cxx/latest/workspace/reference/libtmux-workspace-window/) - [libtmux::workspace::Workspace](https://libtmux.org/en/cxx/latest/workspace/reference/libtmux-workspace-workspace/) --- # Workspace reference generation Source: https://libtmux.org/en/cxx/latest/workspace/internals/documentation/ > Keep help, completion, and site references aligned with command metadata. The native C++ CLI derives help and exports from its parser definitions. [Compatibility](../../reference/compatibility/) records current coverage. ## Command metadata CLI11 defines the native command graph. Its filtered `get_subcommands` overload enumerates command definitions; the unfiltered overload describes parsed commands. A site exporter and shell completion remain unimplemented. The [command reference](../../cli/) covers the Python grammar. Native metadata exports need to record command paths, aliases, positional arity, option spellings, types, defaults, choices, required and exclusive groups, store-constant values, repeat and ordering behavior, environment bindings, and child commands. Output schemas and availability belong alongside that metadata. Generate help, completion, and static reference from the same definitions. Compare generated results in CI and test actual installed help separately. A rendered example or successful parser-only probe does not establish service execution, distribution, or tmux compatibility. ## Site integration Keep equivalent task pages at the same workspace path in every port. The existing page switcher offers authored counterparts and leaves absent pages unavailable. A CLI guide and a builder API guide are different counterparts; preserve [builder reference](../../reference/) as its own surface. Write executable shell examples per port. Shared console, YAML, and JSON blocks survive language filtering. Installing a native library must not be presented as installing a workspace executable when it has no such artifact. The site version and workspace package version can differ. Record the package and source revision independently, and do not use an older version-shaped URL to imply that today's command existed in an older release. The installation guide identifies the available source builds and published artifacts. ## Verification Check [configuration](../../configuration/), [machine output](../../reference/output/), and [compatibility](../../reference/compatibility/) together. Exercise equivalent page paths, API backlinks, fragments, sidebars, metadata exports, search, and preview prefixes in assembled output. Verify command examples against isolated tmux servers, with explicit cleanup and known limitations. --- # Third-party notices Source: https://libtmux.org/en/cxx/latest/third-party-notices/ > Licences and attribution for the tools that build libtmux.org and the software libtmux depends on. libtmux and this site are built with open-source software. Several of those licences ask that their notice text travel with the work, so it is reproduced here. ## Documentation toolchain | Tool | Licence | Role | |---|---|---| | [Astro](https://astro.build) | MIT | The site shell | | [Tailwind CSS](https://tailwindcss.com) | MIT | Styling | | [Pagefind](https://pagefind.app) | MIT | Site-wide search | | [Expressive Code](https://expressive-code.com) | MIT | Code blocks | | [Sphinx](https://www.sphinx-doc.org) | BSD-2-Clause | Python and C++ reference | | [Furo](https://github.com/pradyunsg/furo) | MIT | Sphinx theme | | [Breathe](https://github.com/breathe-doc/breathe) | BSD-3-Clause | Doxygen XML into Sphinx | | [Doxygen](https://www.doxygen.nl) | GPL-2.0-only | Parses C++ headers to XML | | [API Extractor](https://api-extractor.com) | MIT | TypeScript API model | | [IBM Plex](https://github.com/IBM/plex) | OFL-1.1 | Typeface | ### A note on Doxygen Doxygen is licensed GPL-2.0-only. It runs as a build step that reads libtmux's own headers and emits XML; that XML is rendered by Breathe and Sphinx, and no Doxygen-generated HTML is published. Running a GPL program over your own input does not place its licence on the output, and libtmux does not distribute Doxygen or any modified version of it. ## Reference hosting Three ports deep-link to the canonical host their ecosystem already uses, rather than duplicating it here: - Rust: [docs.rs](https://docs.rs/libtmux) - Go: [pkg.go.dev](https://pkg.go.dev/github.com/libtmux/libtmux-go/tmux) - Java and Kotlin: [javadoc.io](https://javadoc.io/doc/io.github.libtmux/libtmux) Those sites are operated independently of this project and carry their own terms. ## libtmux itself Each port is MIT licensed. See the `LICENSE` file in that port's repository for the authoritative text. --- # Exit codes and errors Source: https://libtmux.org/en/cxx/latest/workspace/reference/exit-codes/ > Observed Python exit behavior and the proposed native error contract. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../compatibility/) describes this port's implemented coverage. Python tmuxp uses 0 for success, 1 for general failures, and 2 for argument usage errors. Those categories do not guarantee every current command propagates every underlying error. ## Current exceptions `edit` ignores the editor child's status. Machine `search` can return normally with no output for an invalid regular expression, and with human help for a missing query. Both import children exit 2 when their source argument is missing. See their command pages before treating status alone as proof of success. The root entry point checks for a supported tmux executable before parsing arguments, including help. Missing or unsupported tmux can print a diagnostic and exit with status 0. `tmuxp freeze` also catches a missing-session error, prints it, and returns normally. These are current implementation quirks. ## Proposed native behavior Parse errors return 2 before backend work. Input validation or execution failures return 1. A partial operation includes completed work and the failed stage in its result and returns 1; it must not claim rollback unless rollback occurred. An interruption returns 130 after owned streams and handles are cleaned up, without killing unrelated tmux sessions. Machine errors on stderr are compact JSON objects, one per line. Parse or validation failures before work leave stdout empty. Human errors stay readable text. Stable error codes complement readable messages; native exception objects and stack traces are not the public JSON schema. ## Machine error codes In `--json` and `--ndjson` mode an error record on stderr is `{"schema_version": 1, "code": ..., "message": ...}`, and every entry of a load summary's `errors` array carries the same `code`. The seven native ports report the same code for the same condition: | Condition | Code | | --- | --- | | Workspace file or name not found | `workspace_not_found` | | Malformed document, wrong type, or invalid value | `invalid_workspace` | | An unknown or unsupported key refused | `unsupported_key` | | tmux missing, or its server unreachable | `tmux_unavailable` | | A tmux command failed while building | `tmux_failed` | | `before_script` exited nonzero, or could not start | `script_failed` | | A target session does not exist | `session_not_found` | | A destination exists without `--force` | `destination_exists` | | A confirmation is needed but impossible | `confirmation_required` | | Interrupted by a signal | `interrupted` | | Arguments or mode misused | `usage`, with status 2 | Other conditions keep port-specific codes, in lower snake_case. Match on the code, not the message. See [output](../output/), [automation](../../guides/automation/), and [troubleshooting](../../guides/troubleshooting/). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # JSON, NDJSON, and semantic color Source: https://libtmux.org/en/cxx/latest/workspace/reference/output/ > Python output and the native CLI stream and color contract. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../compatibility/) describes this port's implemented coverage. **Native CLI development contract.** The local `workspace-cli` worktrees provide JSON and NDJSON output. The schema below is the shared target; implemented command coverage remains port-specific. Python tmuxp provides JSON and NDJSON on `ls` and `search`, and JSON on `debug-info`. ## Format selection The shared contract accepts `--json` and `--ndjson` before or after every leaf command. NDJSON wins when both are present. A saved workspace's encoding is separate: `freeze -f yaml` describes the file, while `--json` describes the CLI result stream. Machine output has no ANSI styling, prompts, spinner frames, or raw child output. ## Native C++ load output Human load publishes its summary and flushes stdout and stderr before terminal attachment or client switching. A handoff failure preserves loaded changes. If final publication fails, a writable diagnostic stream reports the completed summary under `retained_state`; human stderr labels the same observed state. A previous nonzero load status survives a later output failure. Closed event output while building retains completed inputs and borrowed-session effects in the failure summary. A known script failure keeps its status and captured output when its completion event cannot be delivered. Scripts can make additional changes outside the builder's retained-window records. NDJSON can emit its terminal result only while its output stream remains writable. See [loading and attachment](../../cli/load/#native-c-loading) for terminal and client requirements. ## Native C++ logging `load --log-file PATH` appends one JSON diagnostic record per line. The global `--log-level` defaults to `warning`; `info` includes lifecycle events and `debug` adds script-output chunks. Other records omit structured script captures. Required errors and primary JSON/NDJSON results remain visible at every level. The destination must be a regular file. New files receive owner-only permissions; existing contents and permissions are preserved. Invalid paths, symlinks and non-regular destinations fail before backend mutation. A later write failure disables the file and reports one optional warning after primary output checks. It preserves child status, cleanup and terminal handoff, but can leave an incomplete final log record. ## 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. Every result names `input`, `input_index`, `session_id`, `session_name` and `reused`; ports add their own fields around those. An input that failed keeps its result record beside its `errors` entry, so a reader can tell which input failed and what became of its session. `status` is `ok` when every input completed, `partial` when some input completed or a failed one left effects behind, such as a borrowed or appended session, and `error` when nothing completed and nothing was retained. NDJSON operation events have `schema_version`, `"command"`, `event`, a monotonically increasing `"sequence"`, and event-specific data. The initial vocabulary is `started`, `workspace-started`, `session-created`, `window-created`, `pane-created`, `script-output`, `warning`, `workspace-completed`, `"failed"`, `"completed"`. Emit `"completed"` or `"failed"` once per invocation. Include operation/input identifiers where several files are involved. `workspace-started` names the `input` and `input_index` it belongs to, and `session-created` follows it directly, before any `window-created`, so a reader knows the session a window belongs to as soon as the windows arrive. Flush records as events arrive; buffering the whole run and splitting a JSON array into lines is not streaming. Drain child stdout and stderr concurrently to avoid pipe deadlocks. In machine mode, script text belongs inside escaped JSON strings; it must never be written directly to stdout. Apply backpressure, cap retained output and expose truncation explicitly. Line breaks, tabs, ANSI bytes, Unicode and arbitrary workspace names must remain valid encoded data. Binary output needs an explicit byte encoding or a documented replacement policy. Machine stderr contains one compact diagnostic JSON object per line. Parse/validation failures before work leave stdout empty and return 2 or 1 respectively. A partial load emits a partial/failure result describing completed work and returns 1; do not claim rollback unless it occurred. Human-mode diagnostics remain readable text. Interruptions should stop scheduling new work, drain or close owned streams, release owned process handles, and return 130 without killing unrelated tmux sessions. ## Prompts and file writes Machine mode resolves choices from arguments and never treats missing input as yes. Detached loading avoids mixing terminal attachment with JSON. Interactive editor and REPL display require a separate controlling terminal; otherwise the command rejects the request before execution. The native contract defines explicit save, format, and overwrite controls for conversion/import automation. With no destination, machine conversion returns the document and writes no guessed file. Existing files require explicit overwrite authorization, including explicit freeze destinations. These rules differ from the Python overwrite behavior documented by [freeze](../../cli/freeze/) and [convert](../../cli/convert/). ## Semantic color Use roles at the point that a domain value is rendered. Formatting an entire line with one success color loses the structure the requested style should communicate. Compose a status, subject, identifiers, paths, counts and hints separately, and reset styling after every token. | Role | Default tmuxp-aligned style | Typical values | | --- | --- | --- | | Heading | Bold bright cyan | Command and section headings | | Primary subject | Bold magenta | Workspace/session name, selected window | | Information | Cyan | Paths, targets, useful values | | Success | Green | Created, loaded, saved | | Warning | Yellow | Partial support, lossy capture, retained objects | | Error | Red | Failed operation, invalid field | | Secondary text | Blue or dim text, verified against terminal contrast | Sizes, timestamps, source labels, hints | | Command syntax | Distinct option/argument roles from the same theme | Flags, metavariables, examples | Copy tmuxp's policy explicitly: nonempty `NO_COLOR` disables; explicit never disables; explicit always enables; nonempty `FORCE_COLOR` enables auto; otherwise use the destination stream's terminal capability. To support the supplied reports' extra variables, add `CLICOLOR_FORCE` and `CLICOLOR` below those explicit/reference choices. `CLICOLOR_FORCE=0` does not force. Machine mode takes precedence over all color choices, including forced color. Measure layout using visible terminal width, not byte length or ANSI-bearing string length. Exercise narrow terminals, wrapped paths, Unicode and redirected output. Keep status words and labels even when color is enabled, so meaning survives monochrome output. Progress updates belong on stderr, animate only on a terminal, and become discrete records in NDJSON mode. Honor the reference progress presets, custom tokens and panel-line rules. ## Related reference See [exit codes](../exit-codes/), [command flags](../../cli/), and [environment](../../configuration/environment/). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp compatibility and port status Source: https://libtmux.org/en/cxx/latest/workspace/reference/compatibility/ > Current local CLI capabilities, remaining gaps, and the historical builder audit. **Local implementation, unpublished.** The C++ `tmux-workspace` CLI is available in the `workspace-cli` source worktree. Its command and configuration coverage remains partial. See [installation](../../guides/installation/) for local setup; installing a published library does not establish availability of this CLI. Compatibility targets useful tmuxp 1.74.0 commands and workspace workflows, with native validation, execution and output conventions. Matching command names does not promise identical runtime behavior or Python semantics. ## This port The optional application provides native `ls`, `search`, `edit`, `convert`, `import teamocil`, `import tmuxinator`, `debug-info`, `load` and `freeze`. The optional [shell](../../cli/shell/#native-execution) invokes an installed tmuxp 1.74.0 executable through native process and terminal handling. Select the executable with `PATH` or `TMUX_WORKSPACE_TMUXP`; native loading does not require Python. Machine shells require `-c` and retain structured output. Loading starts tmux when needed, creates sessions or reuses exact names. It retains created object identities and supports command settings, directories, environment, shells, layouts, indexes and focus. A session-name override applies to the final input. Failed builds remove their own session and preserve earlier successful inputs. Cold startup verifies the daemon and bootstrap session; uncertain identity is reported as possibly retained state. Append authenticates the inherited daemon and current pane, preserves existing windows and reports new windows/settings retained after failure. Ordinary human load requires a foreground controlling terminal before mutation. Outside tmux it attaches; inside tmux it switches the unique non-control client viewing the invoking pane. Zero or multiple matching clients are refused with `-d` guidance. The final input selects the destination, including a reused session. Machine load requires `-d` or `--append`. Independent `active-pane` focus on the invoking physical window requires `-d` or `--append`, including linked windows. This check runs before loading and handoff; clients on other physical windows do not block switching. Load flushes both output streams before handoff and retains loaded changes on handoff failure. It rechecks the selected client's identity before switching; tmux's name-targeted switch still leaves a race after that check. Attachment needs a standard stream identifying the concrete controlling tty. With all three standard streams redirected, use `-d`. See [load](../../cli/load/#native-c-loading). `before_script` invokes quoted argv directly, after session creation or append selection and before workspace settings and windows. Reusing a session skips the script. All inputs' script arguments, directories and environment names are validated before creation. The working directory is the configured session directory, or the invoking directory when omitted. Script stdin is closed. Each output stream retains up to 1 MiB; NDJSON also emits script-output records while the child runs. Script failure or an output limit removes only a newly owned session. Append preserves its borrowed session and reports partial effects. Interruption joins the child group, and remaining group processes are terminated when the script exits. No fixed script deadline is imposed. Conversion preserves extension fields. Imports validate source shapes and translate supported command grouping, roots, layouts, focus and options. Unsupported lifecycle, title and synchronization behavior is refused before saving. See [native imports](../../cli/import/#native-c-imports). Capture records current commands, directories, window names, indexes, focus, layouts and local session/window options. Indexed options and escaped values survive reload; `synchronize-panes` is restored after pane creation. Inherited and global options and environment are omitted. Capture warns about unrecoverable original arguments, history and scripts. Search uses native C++ ECMAScript regular expressions. Layout syntax is checked across all inputs before scripts or session creation. Named-layout and JSON-format availability follow the running daemon's version, or the selected client when starting a new server. Saved layouts accept legacy checksum strings and JSON v2 from tmux next-3.9, including floating-pane metadata. Custom layout checks validate syntax and pane capacity; tmux owns geometry and pruning to the requested pane count. Every command accepts `--json` and `--ndjson`; NDJSON takes precedence. Load flushes events and a terminal result. Diagnostics use stderr and control bytes remain escaped in machine strings. Explicit saves publish an owned temporary file and require `--force` for replacement. `load -2` selects 256-color mode; `-8` fails before document lookup or tmux access. Human load progress uses terminal stderr, five presets or a custom template. Counters track delivered pane commands and configured delays, not program exits. `--progress-lines` bounds the script panel; failure retains its final bounded tail. Redirected stdout receives script output once. Without the panel, both script streams flush to their original destinations as data arrives. Machine output, redirected stderr, `TERM=dumb` and explicit progress disabling suppress the panel. Resize clears it and restores ordinary stream delivery. Progress preserves cursor visibility and terminal modes. Interruptions cancel configured delays and check between topology, command, option and focus steps. `load --log-file PATH` appends JSON diagnostics. `--log-level` defaults to `warning`; `info` includes load lifecycle records and `debug` adds script-output chunks. Required errors and machine results remain visible at every level. Invalid file destinations fail before mutation; later file errors preserve command status, cleanup and handoff. See [logging](../output/#native-c-logging). Native Bash, Zsh and Fish completion covers nested commands, flags, enumerated values and file paths. Dynamic session/configuration-name discovery remains unavailable. See [completion](../../cli/completion/#native-completion). The editor receives parsed argv directly, using `VISUAL`, then `EDITOR`, then `vi`. It can take the controlling terminal while machine stdout stays separate. Without a terminal, output is bounded; exceeding the limit terminates the owned child group. SIGINT/SIGTERM cancel captured children and their pipe-owning descendants. Suspend/resume and non-Linux behavior need further validation. The local source reference is apps/workspace/README.md. Use the native executable's `--help` for the options implemented in that checkout. ### Remaining gaps - Python plugins and custom builders are unavailable. - Windows created by arbitrary scripts are outside the builder's retained-window records, including when a borrowed append session survives script failure. - Dynamic session/configuration-name completion remains unavailable. - Additional importer fields, full configuration/capture coverage and supported-platform packaging remain open. Append's first explicit window index must name a free slot in the existing session. ## Historical builder audit Audit date: 2026-09-09. [Native source snapshot](https://github.com/libtmux/libtmux-cxx/tree/c7f1146d2ebd7a8323d9f9814517dc3cdf86b4ee). The following results describe that original library revision, before the local CLI implementation. They are historical evidence, not its current capability list or a support guarantee for a published artifact. The library parsed 21 upstream YAML examples in the original audit. Live probes found gaps in index assignment and command routing, first-pane environment, split shell inheritance and `options_after` timing. The source snapshot had no native CLI. At that baseline, the workspace consumer was a source-tree library target. Its YAML parser rejected fields outside its supported subset, and builder failures could leave earlier tmux changes in place. These historical library results do not describe the current CLI's owned-session cleanup and append reporting. Read the port's [builder topics](../../internals/topics/) and [API](../) for its library interface. Use the language switcher to compare the same topic across ports; each port has its own coverage limits. ## Shared gaps Parser acceptance does not prove execution support. Native regex engines and Python plugin runtimes have different contracts; identical flags alone do not establish compatibility. See [shell](../../cli/shell/), [search](../../cli/search/) and [hooks](../../configuration/hooks/), and apply this port's current limitations above when reading those reference pages. ## Optional format separator Python libtmux exposes `LIBTMUX_TMUX_FORMAT_SEPARATOR` in its format collector. This native CLI does not claim that setting as a supported codec control. Its framing and decoding need their own compatible seam and collision, empty value, Unicode and line-break checks before accepting such a setting. ## Reading examples The [gallery](../../examples/gallery/) contains the upstream fixture corpus. Parsing a fixture and executing its applications are separate checks. Several require external programs, remote hosts, project directories or plugin packages. A successful YAML read does not establish those dependencies or the complete workspace behavior. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Topics Source: https://libtmux.org/en/cxx/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/cxx/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: ```cpp pane->send_text("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/cxx/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: ```cpp auto sessions = server.sessions(); // expected, CommandFailure> const auto& session = sessions->at(0); auto windows = session.windows(); // expected, CommandFailure> const auto& window = windows->at(0); auto back = window.session(); // expected *back == session; // operator== is defined directly on Session/Window/Pane ``` ## 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/cxx/latest/topics/context-managers/ > Scope-based cleanup for tmux objects, and when your program must kill them explicitly. A tmux session, window, or pane normally remains until you kill it. Scope-based cleanup can kill it when your code leaves a block, including after an exception. See [Workspaces](/concepts/workspaces/) for a temporary layout example. Python provides context managers for tmux objects. .NET provides ownership scopes for servers, sessions, and windows. Other ports require explicit cleanup or offer guards for test servers: | Port | Server | Session | Window | Pane | |------|:------:|:-------:|:------:|:----:| | Python | yes | yes | yes | yes | | .NET | yes | yes | yes | - | | Java | closes conn. | - | - | - | | Rust | test-only | - | - | - | | C++ | test-only | - | - | - | | TypeScript | - | - | - | - | | Go | - | - | - | - | | Swift | - | - | - | - | "test-only" means a guard owns an entire disposable test server. Java's `AutoCloseable` server releases its transport but leaves tmux running. A dash means no built-in cleanup scope is listed for that object; use an explicit kill call with the cleanup mechanism appropriate to your language. ## Python: every level, including nested Python's `Server`, `Session`, `Window`, and `Pane` support context managers. Entry returns the existing object; exit kills it, including when the block raises: Nested scopes exit in reverse order: pane, window, session, then server. ## .NET: an explicit ownership type, stopping at Window .NET's `OwnedSessionScope` and `OwnedWindowScope` wrap the created object and implement `IAsyncDisposable`. The `Session` and `Window` handles themselves are not disposable: There is no `OwnedPaneScope`. For tests, `TmuxTestFactory.CreateHierarchyAsync()` returns a `TemporaryHierarchyScope` containing a private server, session, window, and pane. Disposing it kills the server. ## Java: `Server` is closeable, but closing one doesn't kill it Java's `Server` implements `AutoCloseable`. Exiting `try (Server server = Server.open(config))` releases the owned transport while tmux and its sessions remain running. Kill sessions, windows, panes, or the server explicitly when your program owns their cleanup. ## Rust: no async `Drop`, so cleanup is explicit or best-effort Rust's `Drop::drop` is synchronous and cannot await an async tmux kill. Use explicit shutdown when you need to observe cleanup failures: - **`kill(self)` consumes the handle.** Session, window, and pane kill methods take `self` by value, preventing subsequent use of that handle. - **`libtmux::test::TestServer` provides a test guard.** Call `guard.shutdown().await?` to handle cleanup errors. Its `Drop` implementation falls back to synchronous, best-effort `force_cleanup()`. ## C++: RAII exists, but only for a private test server C++'s `Session`, `Window`, and `Pane` are non-owning values; destroying a handle does not kill its tmux object. `libtmux::test::ScopedTmuxServer`, in the separate `testing` CMake component, owns a private test server and its temporary socket directory: ```cpp auto fixture = libtmux::test::ScopedTmuxServer::start( {.socket_namespace = libtmux::test::SocketNamespace::consumer("my-suite")}); // fixture killed, and its tree removed, when this scope ends: // even if the test that follows fails ``` ## 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/cxx/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: ```cpp pane->send_text("echo hi"); pane->send_key("Enter"); // separate command: no combined convenience exists ``` ## 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`. ```cpp pane->capture(); ``` ## 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/cxx/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: ```cpp pane->set_option("automatic-rename", "off"); pane->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: ```cpp session.set_hook("session-renamed", "display-message 'renamed'"); session.hooks(); // No session unset helper is listed above. ``` ## 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/cxx/latest/topics/format-tokens/ > The typed fields every object exposes, mirroring tmux's own format tokens, and why a field is sometimes absent. Object fields expose values from tmux's [FORMATS](https://man.openbsd.org/tmux.1#FORMATS), such as `pane_id`, `window_zoomed_flag`, and `session_name`. The available fields depend on the port, object scope, tmux version, and data requested by the read. A token needs the right **scope** and **tmux version**. For example, a pane token needs a pane context, and a token added after your tmux release may be absent. Ports represent absence with optional values, flags, or errors, as described below. ## The absence idiom, per port | Port | What an excluded field looks like | |------|--------------------------------------| | Python | the attribute is `None` | | TypeScript | the property is `undefined` | | Go | a two-return-value accessor: `pane.DeadSignal()` returns `(string, bool)`: Go's own "comma ok" idiom | | Rust | `Option`; consult the reference for the accessor name | | Java | `Optional`: `pane.floating()` returns `Optional`, empty when the field isn't populated | | .NET | nullable values or `IncompleteSnapshotException`, depending on whether the value or captured field is absent | | C++, Swift | fixed, non-optional fields; see below for access to other tokens | These examples read optional fields, including `pane_dead_signal` on tmux 3.3 or newer: Rust's `formats.rs` marks this token as optional. Consult its generated reference for the accessor name. .NET also distinguishes missing values from incomplete captures. `Pane.Title` is nullable because tmux may report no title. `Pane.Height`, `.Width`, and `.Index` throw `IncompleteSnapshotException` when the read that produced the handle did not request those fields. A handle resolved by ID alone may therefore lack enough data to answer: ## A generated table under the accessor Several ports generate scope- and version-tagged field catalogs from tmux source or documentation. [Architecture](../architecture/) describes the layouts. Examples include: - **TypeScript** uses `_generated/format_fields.ts` rows with `scope`, `since`, and `token`. For example, `pane_zoomed_flag` has pane scope and requires tmux 3.7. `_generated/field_aliases.ts` supplies the camelCase alias `pane.zoomedFlag`. - **Rust** uses a macro row in `formats.rs` for each token's wire name, scope, tmux version, and type. `pane_dead_signal` has `Pane` scope, requires `V3_3`, and is decoded as `Text`. - **Go** generates `format_generated.go` with `internal/generate/formats`. Some accessors decode richer values: `pane.DeadTime()` returns `(time.Time, bool)` and performs timestamp parsing for the caller. Two per-token facts survive across every one of these catalogs, because they're facts about tmux, not about any one port's generator: `pane_dead_signal` and `pane_dead_time` arrived in tmux 3.3, and a cluster of pane-geometry and floating-pane tokens (`pane_floating_flag`, `pane_pb_progress`, `pane_x`, `pane_y`, `pane_z`, `pane_zoomed_flag`, `bracket_paste_flag`, `synchronized_output_flag`, among others) arrived together in 3.7. ## The two ports that didn't generate the full catalog Swift and C++ expose fixed, non-optional fields on `Session`, `Window`, and `Pane`: - **Swift** carries `index`, `width`, `height`, `isActive`, `currentCommand`, `currentPath`, and the four edge flags. - **C++** declares fields in `kFields` arrays. Pane fields include `id`, `command`, `active`, `index`, `title`, `pid`, `tty`, `path`, `width`, `height`, `dead`, `in_mode`, edge flags, and `piping`. Accessors return `std::string_view`, `bool`, or `long long`. ```cpp pane->active(); // bool, not std::optional pane->command(); // std::string_view, likewise ``` For a token outside the fixed fields, C++ provides one-shot expansion with `pane->expand("#{pane_dead_signal}")`. Swift uses `FormatSubscription` on a control connection, delivering `SubscriptionChange` when tmux re-evaluates the token. That API observes changes over time. [Architecture](../architecture/) describes the fixed-field model. ## Fields promoted from the active child Python exposes fields promoted from an active child. For example, `session.pane_id` identifies the active pane of the session's active window: tmux's format engine includes active-child fields when listing a parent. A `list-sessions -F` row can include `window_id` and `pane_id` for the active window and pane. Check the port reference for typed access to those fields, or use the explicit relationships described in [Traversal](../traversal/). A pane context can include parent window and session fields. A session cannot identify one attached client when several clients may be attached, so client tokens such as `client_name` require a client context. --- # Waiting and retrying Source: https://libtmux.org/en/cxx/latest/topics/waiting-and-retry/ > Polling a condition instead of guessing a sleep, and tmux's own wait-for signal channel as the alternative to polling. After sending input or starting a process, wait for the state your next step requires. [Pane interaction](../pane-interaction/#waiting-for-something-to-finish) covers waiting for screen text. This page covers arbitrary conditions and tmux's named `wait-for` signal channels. ## Polling a condition Polling checks a condition repeatedly until it succeeds or a deadline expires. The helpers below expose an interval and timeout; several live in test-support packages: ### Python **Helper:** `libtmux.test.retry_until(fn, seconds=, interval=)` **Where it lives:** The `libtmux.test` module in the main package; raises `WaitTimeout`. ### TypeScript **Helper:** `connectedServer.waitFor(matches, options)` **Where it lives:** The public control-connection API. Tests a predicate over `ServerSnapshot`; see [Control mode vs one-shot](/concepts/transports/). ### Go **Helper:** `tmuxtest.WaitFor(ctx, interval, condition)` **Where it lives:** `tmuxtest`, a separate test-support package from `tmux` ### Rust **Helper:** `libtmux::test::retry_until(within, condition)` **Where it lives:** `libtmux::test`, enabled with the `test-support` Cargo feature. ### Java **Helper:** Not listed. **Where it lives:** not found in the shipped library; a package-private `Await.until(...)` exists only inside the `integration-tests` module, which downstream code cannot depend on ### .NET **Helper:** `LibTmux.Testing.TmuxWait.UntilAsync(probe, timeout, interval)` **Where it lives:** `LibTmux.Testing`, part of the same shipped `LibTmux` package ### C++ **Helper:** Not listed. **Where it lives:** no generic condition-poll helper found in the public library; a `wait_until` exists only in the private `testing` component, for waiting on a spawned child process, not on tmux state ### Swift **Helper:** Not listed. **Where it lives:** a `waitUntil` helper exists only inside the test target's own support code, not shipped ### Examples Python, Rust, Go, and .NET provide general polling helpers in their test-support APIs. TypeScript's public `waitFor` instead waits on a server-snapshot predicate through a control connection. It subscribes before reading so it does not miss a change between those steps. For Java, C++, and Swift, this page lists no public arbitrary-condition polling helper. Use a loop with a deadline and interval if a more specific wait API does not fit; [Pane interaction](../pane-interaction/#waiting-for-something-to-finish) covers output waits. ## tmux's own wait-for channel Use `tmux wait-for -S ` to signal and `tmux wait-for ` to block until signalled. This avoids repeated screen captures when the command can announce its own completion: | Port | Signal | Wait | |------|--------|------| | Python | `server.wait_for(channel, set_flag=True)` | `server.wait_for(channel)` | | TypeScript | not exposed as public API: used only inside the test-server's own startup handshake | - | | Go | `server.WaitFor(ctx, tmux.WaitForRequest{Channel: name, Mode: tmux.WaitForModeSignal})` | `tmux.WaitForRequest{Channel: name}` (the zero-value `WaitForRequest.Mode` waits) | | Rust | `server.signal_channel(name).await?` | `server.wait_for_channel(name, timeout).await?` → `ChannelWait::Signalled` or `TimedOut` | | Java | `server.channel(name).signal()` | `server.channel(name).await(timeout)` → a `WakeReason`, never silently "success" | | .NET | `server.OpenWaitChannel(name)` returns a `TmuxWaitChannel`; signalling is the same request with a different mode | `await using` the channel, then `WaitAsync(budget)` | | C++ | `server.signal(channel)` | `server.wait_for(channel, timeout)` | | Swift | `try await server.signal(channel)` | `try await server.wait(for: channel)` | ```cpp server.signal("built"); server.wait_for("built", std::chrono::seconds{5}); ``` 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/cxx/latest/topics/environment/ > Locate tmux objects from process variables and manage the environment inherited by new panes. tmux exposes two environment APIs. Process variables such as `TMUX` and `TMUX_PANE` let code inside a pane identify its server and pane. The server also stores variables through `set-environment` and `show-environment` for new processes to inherit. Like the tables in [Options and hooks](../options-and-hooks/), this persistent store has explicit scopes. ## Locating yourself from inside a pane Inside a pane, `TMUX` contains `,,`, and `TMUX_PANE` contains the pane ID, such as `%1`. Use these variables to locate the current tmux objects. Ports expose different levels of environment lookup: ### Python **Server:** `Server.from_env()` **Session:** `Session.from_env()` **Window:** `Window.from_env()` **Pane:** `Pane.from_env()` ### TypeScript **Server:** Not listed. **Session:** `Session.fromEnv()` **Window:** Not listed. **Pane:** Not listed. ### Go **Server:** `NewServerFromEnv(env)` **Session:** `SessionFromEnv(ctx, env)` **Window:** `WindowFromEnv(ctx, env)` **Pane:** `PaneFromEnv(ctx, env)` ### Rust **Server:** `Server::from_env()` **Session:** `Session::from_env(&server)` **Window:** `Window::from_env(&server)` **Pane:** `Pane::from_env(&server)` ### Java **Server:** Not listed. **Session:** Not listed. **Window:** Not listed. **Pane:** See the Java context example below. ### .NET **Server:** `Server.FromEnvironment(env)` **Session:** `Session.FromEnvironmentAsync()` **Window:** `Window.FromEnvironmentAsync()` **Pane:** `Pane.FromEnvironmentAsync()` ### C++ **Server:** `Server::from_env()` **Session:** Not listed. **Window:** Not listed. **Pane:** Not listed. ### Swift **Server:** `TmuxContext.current()` **Session:** `TmuxContext.current()` (same call: see below) **Window:** Not listed. **Pane:** Not listed. ### Examples Python, Go, and .NET provide standalone environment lookups at each level. Rust's `Session`, `Window`, and `Pane::from_env` require an existing `&Server`. TypeScript provides `Session.fromEnv()`; this page lists no equivalent for its other object types. A process not started inside a pane has nothing truthful to answer with, so every one of these raises rather than guessing: Python's `NotInsideTmux`, Go's `FromEnvError`, .NET's `TmuxObjectNotFoundException`, and so on, each naming the missing or malformed variable rather than returning an empty or default object. ### Java and C++ stop short of the pane Neither port gives you a live object back the way the other five do, and they stop at different points: - **C++** provides `Server::from_env()` to select the socket. Use the resulting server to resolve sessions or panes. - **Java** parses `TMUX` and `TMUX_PANE` into identifiers: socket path, server PID, `SessionId`, and `Optional`. It returns context data rather than a live pane handle: ```java TmuxEnvironment here = TmuxEnvironment.current().orElseThrow(); try (Server server = Server.open(here.config())) { Session mine = server.sessions().stream() .filter(session -> session.id().equals(here.session())) .findFirst() .orElseThrow(); } ``` ### Swift context fields Swift's `TmuxContext.current()` parses the socket path, server PID, and session ID from `TMUX`. It does not read `TMUX_PANE`, so it cannot identify the current pane: 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'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/cxx/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: "...")` | ```cpp auto named = libtmux::Server::at_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` | ```cpp if (server.is_alive(std::chrono::seconds{2})) { 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/cxx/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 | ```cpp auto result = session.new_window({.name = "build"}); if (!result.has_value() && result.error().delivery == libtmux::DeliveryStatus::not_started) { // safe to retry } ``` 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.