# libtmux for Java > The Java port of libtmux (io.github.libtmux:libtmux). Every code sample below is Java and Kotlin; the same pages exist for the other nine ports under their own prefix. - [Java API reference](https://libtmux.org/en/java/latest/reference/): every public symbol, generated from the source. Hosted on libtmux.org. --- # tmuxp load Source: https://libtmux.org/en/java/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. Load a workspace file, saved workspace name, or project directory. Multiple inputs build in order; without `-d`, the final session is attached or selected through the current-client flow. ## Loading and attachment Create [`workspace.yaml`](../../guides/installation/#create-the-input) using the [installation walkthrough](../../guides/installation/), then load it on that walkthrough's dedicated socket: ```console $ tmuxp load \ -S "$WORKSPACE_TMP/tmux.sock" \ -d \ "$WORKSPACE_TMP/workspace.yaml" ``` `-d` avoids attachment. Inside an existing tmux client, the normal interactive flow can switch to the new session, append windows, or stay detached. `--append` explicitly selects the append flow and needs a current target session. An existing session is handled through tmuxp's load policy; loading is not a declarative reconciliation operation that removes surplus windows. Put flags before the complete group of filenames. The reference accepts flags before or after that group, but a flag between two filenames can cause an argument error. `-s` overrides the final input's session name when several files are loaded. tmuxp parses `-2` and `-8` as mutually exclusive flags, separate from the CLI text's `--color` setting. Legacy `-8` is unsupported; [tmux removed 88-color support](https://raw.githubusercontent.com/tmux/tmux/3.2a/CHANGES). ## Progress and script output `--progress-format` accepts `default`, `minimal`, `"window"`, `"pane"`, `verbose`, or a custom format. Available tokens include `{session}`, `{window}`, `{window_index}`, `{window_total}`, `{window_progress}`, `{window_progress_rel}`, `{windows_done}`, `{windows_remaining}`, `{pane_index}`, `{pane_total}`, `{pane_progress}`, `{progress}`, `{session_pane_progress}`, `{overall_percent}`, `{bar}`, `{pane_bar}`, `{window_bar}`, and `{status_icon}`. The 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/java/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/java/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/java/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/java/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/java/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/java/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/java/latest/workspace/cli/shell/ > Open a Python shell with tmux objects available, or evaluate Python using `-c`. This command remains Python-specific even when reached through a native command. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Open a Python shell with tmux objects available, or evaluate Python using `-c`. This command remains Python-specific even when reached through a native command. ## Evaluate with a selected server Continue the [installation walkthrough](../../guides/installation/) through its detached load, leaving `workspace-guide` running in the same shell: ```console $ tmuxp shell \ -S "$WORKSPACE_TMP/tmux.sock" \ -c 'print(server.sessions)' \ workspace-guide editor ``` Use `-c`; the reference does not define `--command`. Optional session and window arguments select context. `--best` chooses the best available shell backend; the explicit selectors require their corresponding Python packages. The `--pdb` path uses a debugger rather than a normal REPL. The paired `--use-pythonrc` / `--no-startup` and `--use-vi-mode` / `--no-vi-mode` options share destinations. The last occurrence wins. The negative options disable the corresponding setting. See [environment](../../configuration/environment/) for startup and backend settings. A native REPL is not an equivalent implementation of Python `-c`, IPython, or PTPython. Native ports that support this command use an optional version-checked Python bridge and report an unsupported-runtime error if it is absent. Check [port coverage](../../reference/compatibility/) before relying on that bridge. Interactive machine output needs a separate terminal; an interactive transcript cannot share JSON stdout. Backend selectors are mutually exclusive. ## 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/java/latest/workspace/cli/import/ > Import a workspace from a supported external configuration format. Select one of the two child commands. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Import a workspace from a supported external configuration format. Select one of the two child commands. ## Choose the source format - [Teamocil](../import-teamocil/) converts a Teamocil workspace. - [tmuxinator](../import-tmuxinator/) converts a tmuxinator workspace. The parent command groups importers and has no workspace file argument of its own. Each child requires a source at the parser boundary, despite optional-looking help. Conversion is schema translation; it does not make tmuxinator Ruby or ERB evaluation available in a native YAML reader. ## Arguments and flags The parent accepts `-h` / `--help` and selects a child command. All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/import_config.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp import teamocil Source: https://libtmux.org/en/java/latest/workspace/cli/import-teamocil/ > Translate a Teamocil workspace into tmuxp configuration, review the result, and select its saved representation. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Translate a Teamocil workspace into tmuxp configuration, review the result, and select its saved representation. ## Select an existing source With [`project.yml`](../../guides/discovery/) already present in the Teamocil configuration directory: ```console $ tmuxp import teamocil project ``` The source lookup uses [`~/.teamocil`](./). A source argument is effectively required: although its positional action has optional arity, it belongs to a required exclusive group, and omission exits 2. The reference previews and saves the transformed document interactively. Native automation needs explicit destination, encoding, and overwrite policy instead of implicit prompts. See [output](../../reference/output/). Inspect translated commands and directories before loading the result. An extensionless name searches the configured source directory. A filename with an extension is resolved relative to the current directory unless you provide an explicit path. ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `workspace_file` | effectively required | `nargs="?"` belongs to a required exclusive group; omission exits 2. Source lookup uses [`~/.teamocil`](./). | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/import_config.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # tmuxp import tmuxinator Source: https://libtmux.org/en/java/latest/workspace/cli/import-tmuxinator/ > Translate a tmuxinator workspace into tmuxp configuration, preserving an explicit boundary around dynamic Ruby configuration. **tmuxp compatibility reference.** Examples using `tmuxp` run the Python reference. [Local CLI status](../../reference/compatibility/) describes this port's implemented coverage. Translate a tmuxinator workspace into tmuxp configuration, preserving an explicit boundary around dynamic Ruby configuration. ## Select an existing source With [`project.yml`](../../guides/discovery/) already present in the configured tmuxinator directory: ```console $ tmuxp import tmuxinator project ``` `TMUXINATOR_CONFIG` overrides the source directory and expands a leading tilde. A source argument is effectively required, and omission exits 2 despite optional-looking help. The reference previews and saves the transformed document interactively. Do not interpret successful YAML parsing as support for ERB templates or arbitrary Ruby execution. Native importers must either implement a documented bridge or reject unsupported dynamic input. Inspect the resulting commands and directories before loading. See [environment](../../configuration/environment/) and [output](../../reference/output/). An extensionless name searches the configured source directory. A filename with an extension is resolved relative to the current directory unless you provide an explicit path. ## Arguments and flags | Argument or flags | Arity / default | Choices or meaning | | --- | --- | --- | | `workspace_file` | effectively required | `nargs="?"` belongs to a required exclusive group; omission exits 2. Source lookup honors `TMUXINATOR_CONFIG`. | All commands accept `-h` / `--help`. Root options precede the command; see the [CLI overview](../). The [output reference](../../reference/output/) distinguishes current Python flags from native all-command JSON and NDJSON. [Parser and implementation source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/import_config.py). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Workspace command reference Source: https://libtmux.org/en/java/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/java/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 Picocli defines the native command graph. `--generate schema` exports metadata and `--generate bash` emits completion through its code generator. Other manual and completion formats are not exposed by the workspace executable. Normal Bash generation writes the script directly. Add `--json` for a versioned artifact with `"command": "generate"`, `"format": "bash"`, the exact text in `"script"`, and `"status": "ok"`. `--ndjson` emits that artifact as one `completed` event with `"sequence": 1` and takes precedence over `--json`. Schema generation retains its metadata document in every output mode. See the [native generation contract](https://github.com/libtmux/libtmux-java/blob/887a5e079d38898bdffaad19a7961936eabe1ce0/workspace-cli/README.md#generated-reference-and-development). Completion is derived from actual command metadata. It must include nested import commands, local short flags, positional arity, mutually exclusive choices, and all-command machine options. Verify generated scripts with the installed executable and target shell. See the [command tree](../) and [compatibility reference](../../reference/compatibility/). [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Concepts Source: https://libtmux.org/en/java/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/java/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: ```java for (Session session : server.sessions()) { System.out.println(session.name()); for (Window window : session.windows()) { System.out.println(" " + window.index() + " " + window.name()); for (Pane pane : window.panes()) { System.out.println(" " + pane.id().value() + " " + pane.currentCommand()); } } } ``` ## Client: a view, not a child A `Client` represents a terminal attached to a session. Several clients can view the same server, and each can switch sessions or windows independently. Client fields describe the view at the time of the read. A [control-mode connection](../transports/) is also a client. It appears in `list-clients`, counts toward `session_attached`, and affects attachment-dependent behavior such as `destroy-unattached`. Closing the last attached client can therefore destroy a session configured with that option. ## What differs between ports Ports differ in how they read state and report failures: - **Refreshing state.** Python objects reflect their last read until you call `.refresh()`. TypeScript, Swift, and Go also provide snapshots whose relationships can be queried without another tmux command. - **Blocking and async calls.** Python and Java use blocking calls. Rust, TypeScript, .NET, and Swift provide async APIs. Go uses ordinary calls with contexts for cancellation and deadlines. - **Failure handling.** Python and .NET raise exceptions. C++ returns `expected`. Some commands have an expected negative answer, such as `has-session` when a session is absent; check the method's result contract before treating that answer as a failure. See [Control mode vs one-shot](../transports/) for command costs and connection behavior. --- # Control mode vs one-shot Source: https://libtmux.org/en/java/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. ```java ServerConfig config = ServerConfig.builder() .endpoint(ServerEndpoint.defaultSocket()) .build(); // One-shot: every call underneath this handle spawns a `tmux` process. try (Server server = Server.open(config)) { Session session = server.newSession("work"); Pane pane = session.windows().get(0).panes().get(0); pane.sendLine("echo hello"); } ``` To inspect tmux's control protocol, attach a control client: ```console $ tmux -C attach-session -t work ``` --- # Filtering and queries Source: https://libtmux.org/en/java/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: ```java List editors = server.windows().stream() .filter(Window_.name().startsWith("edit")) .toList(); // Selections.exactlyOne() is the `.get()`-shaped call. Session build = Selections.exactlyOne( server.sessions().stream().filter(Session_.name().is("build")).toList()); ``` ## 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/java/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: ```java Window window = session.newWindow(w -> w.named("dev").detached()); Pane terminal = window.split(split -> split.percent(30)); Pane logs = terminal.split(split -> split.toRight()); window.selectLayout(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: ```java Workspace workspace = WorkspaceBuilder.parse(yaml); Session session = WorkspaceBuilder.build(server, workspace); ``` C++ provides a consumer example in `examples/workspace/` that reads tmuxp configuration. The workspace builder is part of that example, rather than a library package: ## Cleaning up For temporary workspaces, Python's `Window` and `Session` context managers kill their objects on block exit, including when the block raises: See [Context managers](/topics/context-managers/) for cleanup support in each port. Use explicit kill methods when the handle does not provide scope-based cleanup. --- # Workspace configuration Source: https://libtmux.org/en/java/latest/workspace/configuration/ > Tmuxp workspace configuration and current Java 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. ## Native Java CLI The local `tmux-workspace` CLI validates its native configuration before scripts run or tmux state changes. It supports the session, window, pane and command fields described in the sections below. Unsupported native keys fail explicitly; Python plugin/custom-builder inputs use the separately selected extension path. Generic conversion preserves document fields without proving native execution. See the [CLI configuration parser](https://github.com/libtmux/libtmux-java/blob/2d7e8028986b99c8e9496dc40b5d1e90fb2368c9/workspace-cli/src/main/java/io/github/libtmux/workspace/cli/WorkspacePlan.java). Application code using the lower-level workspace library has a separate [builder API](../internals/topics/). Its schema is not the CLI configuration contract. ## 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/java/latest/workspace/configuration/session/ > Tmuxp session configuration and current Java 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/). ## Native Java CLI The local CLI accepts session identity, directories, environment, session and global options, `before_script`, inherited before commands and history policy. Python plugins and custom builders use an explicit, version-checked runtime; they do not run through the native builder. Existing-session reuse and append follow the [load contract](../../cli/load/), including retained partial effects. See the [CLI configuration parser](https://github.com/libtmux/libtmux-java/blob/2d7e8028986b99c8e9496dc40b5d1e90fb2368c9/workspace-cli/src/main/java/io/github/libtmux/workspace/cli/WorkspacePlan.java). Application code using the lower-level workspace library has a separate [builder API](../../internals/topics/). Its schema is not the CLI configuration contract. ## 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/java/latest/workspace/configuration/windows/ > Tmuxp window configuration and current Java 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. ## Native Java CLI The local CLI accepts window names, explicit indexes, layouts, focus, directories, launch environments, options, `options_after` and `window_shell`. Window environment supplies the launch map when a pane has no environment map. A pane override replaces that map. Native layout preflight precedes scripts; `options_after` runs after pane command delivery. See the [CLI configuration parser](https://github.com/libtmux/libtmux-java/blob/2d7e8028986b99c8e9496dc40b5d1e90fb2368c9/workspace-cli/src/main/java/io/github/libtmux/workspace/cli/WorkspacePlan.java). Application code using the lower-level workspace library has a separate [builder API](../../internals/topics/). Its schema is not the CLI configuration contract. ## 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/java/latest/workspace/configuration/panes/ > Tmuxp pane configuration and current Java 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. ## Native Java CLI The local CLI accepts null and `blank`/`pane`/`empty` shorthand, command strings, command lists and pane mappings. Mappings support directories, launch environments, focus, before commands, history policy, Enter and delays. `shell:` and `pane_shell:` are aliases and cannot both be set. A successful load confirms command delivery, not a shell command's completion or exit status. See the [CLI configuration parser](https://github.com/libtmux/libtmux-java/blob/2d7e8028986b99c8e9496dc40b5d1e90fb2368c9/workspace-cli/src/main/java/io/github/libtmux/workspace/cli/WorkspacePlan.java). Application code using the lower-level workspace library has a separate [builder API](../../internals/topics/). Its schema is not the CLI configuration contract. ## 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/java/latest/workspace/configuration/commands/ > Tmuxp workspace commands and current Java 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. ## Native Java CLI The local CLI supports command strings, lists and `{cmd, enter, sleep_before, sleep_after}` objects. Enter and delay defaults carry forward until an explicit override. Before commands accumulate in session/window/pane order, and history suppression prefixes delivered commands with a space. Command variables remain for the pane shell to expand; this differs from tmuxp's invoking-process expansion. [Readiness policy](../../reference/compatibility/) is separate from command completion. See the [CLI configuration parser](https://github.com/libtmux/libtmux-java/blob/2d7e8028986b99c8e9496dc40b5d1e90fb2368c9/workspace-cli/src/main/java/io/github/libtmux/workspace/cli/WorkspacePlan.java). Application code using the lower-level workspace library has a separate [builder API](../../internals/topics/). Its schema is not the CLI configuration contract. ## 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/java/latest/workspace/configuration/environment/ > Tmuxp workspace environment and current Java 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. ## Native Java CLI The local CLI accepts session environment and window/pane launch maps. Window environment applies when a pane has no map; a pane map replaces it. Names, directories, environment values and option strings expand tilde and defined invoking-process variables. Shell command text retains variables for the pane shell. Session-local environment is currently omitted by native capture; a whole-session environment round trip is not claimed. See the [CLI configuration parser](https://github.com/libtmux/libtmux-java/blob/2d7e8028986b99c8e9496dc40b5d1e90fb2368c9/workspace-cli/src/main/java/io/github/libtmux/workspace/cli/WorkspacePlan.java). Application code using the lower-level workspace library has a separate [builder API](../../internals/topics/). Its schema is not the CLI configuration contract. ## 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/java/latest/workspace/configuration/directories/ > Tmuxp workspace files and directories and current Java 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. ## Native Java CLI The local CLI supports `start_directory` at session, window and pane scope. Relative paths resolve from the source document's directory, then inherit through the session/window/pane hierarchy. Tilde and defined invoking-process variables expand before existing-directory checks. `before_script` uses the invocation directory unless the workspace sets `start_directory` explicitly. Imports instead record absolute invocation-based roots before saving. See the [CLI configuration parser](https://github.com/libtmux/libtmux-java/blob/2d7e8028986b99c8e9496dc40b5d1e90fb2368c9/workspace-cli/src/main/java/io/github/libtmux/workspace/cli/WorkspacePlan.java). Application code using the lower-level workspace library has a separate [builder API](../../internals/topics/). Its schema is not the CLI configuration contract. ## 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/java/latest/workspace/configuration/layouts/ > Tmuxp workspace layouts and focus and current Java 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. ## Native Java CLI The local CLI validates layout syntax, checksum, structure and pane capacity before scripts or session mutation. Version-sensitive named layouts use the selected daemon. tmux owns geometry correction and pruning. All panes in a window are created and its layout is applied before commands are sent; focus and `options_after` follow command delivery. See the [CLI configuration parser](https://github.com/libtmux/libtmux-java/blob/2d7e8028986b99c8e9496dc40b5d1e90fb2368c9/workspace-cli/src/main/java/io/github/libtmux/workspace/cli/WorkspacePlan.java). Application code using the lower-level workspace library has a separate [builder API](../../internals/topics/). Its schema is not the CLI configuration contract. ## 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/java/latest/workspace/configuration/hooks/ > Tmuxp workspace hooks and builders and current Java 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. ## Native Java CLI The local CLI runs `before_script` through native child-process services and supports inherited pane before commands. Python plugins and custom builders use the explicit tmuxp bridge with version and import-path checks before mutation. Their arbitrary effects remain outside native rollback guarantees. Native load reports owned cleanup and retained borrowed-state changes; it cannot reverse shell side effects. See the [CLI configuration parser](https://github.com/libtmux/libtmux-java/blob/2d7e8028986b99c8e9496dc40b5d1e90fb2368c9/workspace-cli/src/main/java/io/github/libtmux/workspace/cli/WorkspacePlan.java). Application code using the lower-level workspace library has a separate [builder API](../../internals/topics/). Its schema is not the CLI configuration contract. ## 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/java/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/java/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. ```java file="examples/src/main/java/io/github/libtmux/examples/BuildAWorkspace.java" package io.github.libtmux.examples; import io.github.libtmux.Layout; import io.github.libtmux.Pane; import io.github.libtmux.Server; import io.github.libtmux.ServerConfig; import io.github.libtmux.ServerEndpoint; import io.github.libtmux.ServerNotRunningException; import io.github.libtmux.Session; import io.github.libtmux.Window; import java.nio.file.Path; import java.util.Optional; /** * Lays out a session the way you would set one up by hand before starting work. * *
{@code
 * java BuildAWorkspace.java /tmp/libtmux-java-dev/demo/s
 * }
*/ public final class BuildAWorkspace { private BuildAWorkspace() {} public static void main(String[] args) { run(Path.of(args.length > 0 ? args[0] : "/tmp/libtmux-java-dev/demo/s")); } /** Separated from {@code main} so the suite can run exactly what a reader runs. */ public static String run(Path socket) { ServerConfig config = ServerConfig.builder() .endpoint(ServerEndpoint.socketPath(socket)) .configFile(Path.of("/dev/null")) .build(); // Closing a server closes this client. The tmux server, and the session, outlive the program // — which is the whole point of tmux and the reason nothing here kills it. try (Server server = Server.open(config)) { // One read decides and answers. An absent daemon means the name cannot be taken, so // that specific failure is the other way this resolves to "not found". Optional existing; try { existing = server.session("work"); } catch (ServerNotRunningException absent) { existing = Optional.empty(); } Session session = existing.orElseGet(() -> server.newSession("work")); Window editor = session.newWindow(window -> window.named("editor").detached()); Pane shell = editor.split(split -> split.toRight()); shell.sendLine("git status --short"); editor.selectLayout(Layout.MAIN_VERTICAL); return "session " + session.name() + " has " + session.refresh().windows().size() + " windows"; } } } ``` ## 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/java/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 ```java Pane pane = server.sessions().get(0).windows().get(0).panes().get(0); pane.sendLine("echo hello from libtmux"); pane.capture().isEmpty(); // → false ``` The .NET example under "Wait for text instead of guessing a delay" uses `Pane.CaptureAsync` within a wait. Its separate Psmux transport also provides a capture API, shown in `examples/LibTmux.Examples/Snippets/Psmux.cs`. ## Wait for text instead of guessing a delay The Python example uses `wait_for`, tmux's signal channel. It waits for a signal from the command rather than matching pane text. `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. ```java file="examples/src/main/java/io/github/libtmux/examples/WatchPaneOutput.java" package io.github.libtmux.examples; import io.github.libtmux.Server; import io.github.libtmux.ServerConfig; import io.github.libtmux.ServerEndpoint; import io.github.libtmux.Session; import io.github.libtmux.control.ControlClient; import io.github.libtmux.control.EventSubscription; import io.github.libtmux.control.PaneOutput; import java.nio.file.Path; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; /** * Watches what a pane prints, as tmux pushes it, rather than polling for it. * *
{@code
 * java WatchPaneOutput.java /tmp/libtmux-java-dev/demo/s
 * }
*/ public final class WatchPaneOutput { private WatchPaneOutput() {} public static void main(String[] args) { Path socket = Path.of(args.length > 0 ? args[0] : "/tmp/libtmux-java-dev/demo/s"); run(socket, Duration.ofSeconds(10), output -> System.out.print(output.data())); } /** * Separated from {@code main} so the suite can run exactly what a reader runs. * * @return everything seen before the deadline */ public static List run(Path socket, Duration watchFor, Consumer onOutput) { ServerConfig config = ServerConfig.builder() .endpoint(ServerEndpoint.socketPath(socket)) .build(); List seen = new ArrayList<>(); try (Server server = Server.open(config)) { Session session = server.sessions().get(0); // Attaching is what makes tmux push %output at all. A client that never attaches hears // about command replies and nothing else. try (ControlClient client = ControlClient.attach(server.config(), session.id()); EventSubscription output = client.subscribeOutput(32)) { client.send("send-keys", "-t", session.name(), "echo watched", "Enter"); long deadline = System.nanoTime() + watchFor.toNanos(); while (System.nanoTime() < deadline && seen.isEmpty()) { try { var next = output.next(Duration.ofNanos(Math.max(0L, deadline - System.nanoTime()))); if (next.isEmpty()) { break; } PaneOutput arrived = next.orElseThrow(); seen.add(arrived); onOutput.accept(arrived); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } } return List.copyOf(seen); } } ``` 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/java/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 Workspace workspace = WorkspaceBuilder.parse(""" session_name: built windows: - window_name: editor layout: even-horizontal panes: - shell_command: echo one - shell_command: echo two - window_name: server panes: - echo three """); Session session = WorkspaceBuilder.build(server, workspace); session.name(); // → built session.windows().size(); // → 2 session.windows().get(0).panes().size(); // → 2 ``` 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/java/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/java/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/java/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. ```java // implementation("io.github.libtmux:libtmux:VERSION") Session session = server.newSession("demo"); Window editor = session.newWindow("editor"); Pane right = editor.split(); session.name(); // → demo editor.name(); // → editor editor.refresh().panes().size(); // → 2 Pane pane = server.sessions().get(0).windows().get(0).panes().get(0); pane.sendLine("echo hello from libtmux"); pane.capture().isEmpty(); // → false ``` ## 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/java/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`. ```java Server server = Server.open( ServerConfig.builder().endpoint(ServerEndpoint.socketPath(socket)).build()); // The pane-local read-back: takes nothing, returns empty outside a pane. TmuxEnvironment.current(); ``` 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. ```java Session session = server.hasSession("work") ? server.sessions().stream() .filter(candidate -> candidate.name().equals("work")) .findFirst() .orElseThrow() : server.newSession("work"); ``` 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/java/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. ```java // Sends the text and submits it as one call: no separate literal switch // and no documented "type without submitting" step as of this page. pane.sendLine("echo hey"); ``` 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/java/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: 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: ```java // A client has to attach first: attaching is what makes tmux push // %output at all; a client that never attaches hears command replies and // nothing else. EventSubscription output = client.subscribeOutput(32); ``` 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/java/latest/guides/querying-and-filtering/ > Filter tmux objects, require one match, and choose where a query runs. Find sessions, windows, or panes with collection filters and exactly-one lookups. [Filtering and queries](/concepts/queries/) explains the result-count contracts and the choice between local and tmux-side filtering. This guide adds examples for common queries. ## Filling in the rest of the cardinality table | Port | Collection filter | Exactly-one | Empty | Several | |------|--------------------|--------------|-------|---------| | Go | `tmuxq.Where(values, predicate)` | `tmuxq.ExactlyOne(values, predicate)` | `tmuxq.ErrNoMatch` | `tmuxq.ErrMultipleMatches` | | Rust | `.iter().matching(&expr)` | `.exactly_one()` | prints via the error's `Display` | same, one error type covers both | | C++ | pipe a range into [`libtmux::matching(expr)`](/cxx/latest/reference/libtmux-matching/) | `libtmux::exactly_one(range)` | `.error()` says which way it went wrong | same call, same error type | Go's `ExampleExactlyOne` in `tmuxq/example_test.go` checks the result with `go test` and `// Output:` assertions: Rust's is `examples/find.rs`, run via `cargo run --example find`: C++'s is quoted straight from `examples/05-readme.cpp`'s `cardinality` region into `README.md`, and `tools/docs/check_readme.py` fails the build if the two ever disagree: For .NET and Swift result-count handling, consult the port reference. The examples here demonstrate .NET's `IEnumerable.Matching(expression)` returning an `IReadOnlyList` and Swift's `hasSession(_:)` returning a `Bool`. The latter checks existence; see [Attaching to tmux](../attaching-to-tmux/#finding-a-session-instead-of-always-creating-one). ## Declarative filters that travel, beyond Python and TypeScript [Filtering and queries](/concepts/queries/) covers Python's `.filter()` lookups and TypeScript's `.where()` documents. Two more ports build the same "a query is data, not code" idea, verified against their own README: Sources: .NET's is `src/LibTmux/README.md`, "Filtering." Swift's is `Examples/Sources/ExampleCode/Filtering.swift`, matched against the README by `Scripts/check_examples.py`. ## Case-insensitive matching 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/java/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/). ```java @ExtendWith(TmuxExtension.class) class MyToolTest { @Test void itRunsSomethingInAPane(Server server) { Session session = server.sessions().get(0); Pane pane = session.windows().get(0).panes().get(0); pane.sendLine("echo hello"); assertTrue(pane.capture().stream().anyMatch(line -> line.contains("hello"))); } } ``` `libtmux-junit5` supplies each test with a running `Server` containing a session named `libtmux`. Request `TmuxSocketPath` when your code takes a socket path. Fixtures live in JUnit's per-test extension store. A shutdown hook kills servers owned by that JVM, and startup cleanup removes servers left by JVMs that have exited. Source: `libtmux-junit5/README.md`. The port's `docs-tests` module compiles Java fences from READMEs and guides, then runs them against `libtmux-junit5` servers. A `` directive can instead require a named exception, a compile failure, or an explicit skip reason. Source: `docs-tests/README.md`. `LibTmux.Testing` ships as a separate package, under `src/LibTmux.Testing/`. `await using` disposes the scope and kills its server when the block exits. Use `TmuxWait.UntilAsync` to wait for expected state; see [Capturing output](../capturing-output/). Source: `README.md`, "Testing your own code," exercised by `ReadmeExampleTests`. The example comes from `README.md`, "Testing your own tmux tools," and is checked against the `fixture` region in `examples/05-readme.cpp` by `tools/docs/check_readme.py`. Enable the `testing` CMake component with `find_package(libtmux COMPONENTS testing)`. It creates a private socket and temporary directory, sets `TMUX_TMPDIR`, and removes `TMUX` and `TMUX_PANE` from the child environment. `SocketNamespace::consumer(...)` labels sockets with the consumer suite's name. `examples/tests/README.md` shows use from outside the library's build tree. `TmuxFixture` is a separate package product. It starts a server with a bootstrap session and limits concurrent fixtures to reduce process and pseudo-terminal exhaustion. `LIBTMUX_TMUX_BIN` selects the executable; otherwise it checks installed locations. Source: `Tests/TmuxFixture/README.md`. TypeScript's harness at `packages/libtmux/src/_internal/test/testkit.ts` is internal and unpublished. For external tests, create an isolated `Server` and manage its cleanup in your test framework. ## Where to go next - [Capturing output](../capturing-output/): the wait helpers most of these fixtures are meant to be used alongside, instead of a fixed `sleep` in a test. - [Attach and send keys](/examples/attach-and-send-keys/) and [Capture pane output](/examples/capture-pane-output/): the same operations these fixtures give you a server to run, shown as tested examples in their own right. --- # Install and load a workspace Source: https://libtmux.org/en/java/latest/workspace/guides/installation/ > Build the local Java workspace CLI and load a session on a private tmux socket. Build and run the native Java `tmux-workspace` command from the local `workspace-cli` checkout. **This is a partial, unreleased implementation.** These commands require that local source; they are not registry installation instructions or a claim that the CLI is available on the published branch. ## Build from the local checkout Run these commands from the native repository root. Use a Unix environment with tmux 3.2a or newer on `PATH` for this walkthrough. Use JDK 21 or newer and the repository's Gradle wrapper. The local application distribution includes its Java dependencies and needs Java on `PATH`, or `JAVA_HOME` set to a compatible JDK. ```console $ ./gradlew :workspace-cli:installDist \ --max-workers=2 \ --no-parallel ``` Keep the generated distribution together: its launcher uses libraries beside it. This local application distribution is separate from published Java library artifacts. Inspect the built command: ```console $ workspace-cli/build/install/tmux-workspace/bin/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 $ workspace-cli/build/install/tmux-workspace/bin/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 $ workspace-cli/build/install/tmux-workspace/bin/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 Human load progress uses terminal stderr, with five presets or a custom `--progress-format` and a bounded `--progress-lines` panel. It preserves script stdout/stderr, clears on completion or interruption, and is disabled for machine output, redirected stderr, `TERM=dumb`, `--no-progress` or `TMUXP_PROGRESS=0`. Python plugins and custom builders execute through the checked tmuxp bridge. Extension append rejects documents containing `before_script` to preserve the borrowed session. Complete configuration/capture coverage and full terminal/platform acceptance remain unfinished. Progress uses the initial terminal dimensions; it does not track resizing, and Unicode clipping is conservative. Python-specific shell behavior requires an interpreter with tmuxp 1.74.0 installed. Select it with `TMUX_WORKSPACE_PYTHON`. Ordinary native loading of this example does not require Python. ## Python alternative For the separate released tmuxp application, install its isolated Python tool environment with uv: ```console $ uv tool install tmuxp ``` Follow the [Python installation guide](/py/latest/workspace/guides/installation/) for that workflow. Installing tmuxp does not install the native command. ## Continue [Discovery](../discovery/), [configuration](../../configuration/) and the [load reference](../../cli/load/) explain the tmuxp compatibility model. Compare those references with the local command's help and the limits above. [Export and reload](../export-session/) explains the capture workflow, and the [compatibility reference](../../reference/compatibility/) records builder gaps. Use [Internals](../../internals/) for the library and consumer APIs. [tmuxp reference source](https://github.com/tmux-python/tmuxp/blob/618b398acc05506d3c682906c36cdeb29dcfa1ff/src/tmuxp/cli/__init__.py). --- # Find saved workspaces Source: https://libtmux.org/en/java/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/java/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/java/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/java/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/java/latest/workspace/guides/inspect-with-mcp/ > Connect the development Java MCP server to a session loaded by its native workspace CLI. Inspect the session you loaded with the Java 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 $ ./gradlew :libtmux-mcp:installDist \ --max-workers=2 \ --no-parallel ``` Keep the installed distribution intact: its launcher loads the JARs in the adjacent `lib` directory. Use the JDK from the installation walkthrough. ## Select the same socket Configure an MCP client to launch the following command with the shown environment. The client owns the process's standard input and output for JSON-RPC messages. ```console $ LIBTMUX_TOOLSETS=inspect \ libtmux-mcp/build/install/libtmux-mcp/bin/libtmux-mcp \ --socket "$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. The path option is `--socket`, not the workspace CLI's `-S`. If the CLI used a tmux executable outside `PATH`, pass the same executable with MCP's `--tmux` option. ## 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] | `"pane_id"`, `"max_lines"` | | [wait_for_text][mcp-source] | `"pane_id"`, `"patterns"`, `"timeout"` in seconds | Set `"timeout"` to `10` 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 omits trailing empty rows. Use [snapshot_pane][mcp-source] for structured pane state and [capture_since][mcp-source] for incremental observation; their budgets and cursor fields are described in the discovered schemas. 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-java/blob/7a3aa4c8cf520d886f9877936c41fd07e7d4754d/workspace-cli/README.md [mcp-source]: https://github.com/libtmux/libtmux-java/blob/7a3aa4c8cf520d886f9877936c41fd07e7d4754d/libtmux-mcp/README.md --- # Java workspace internals Source: https://libtmux.org/en/java/latest/workspace/internals/ > Architecture and development interfaces of the Java 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 The public `WorkspaceBuilder` facade reads or parses YAML into immutable configuration records, then builds through a supplied core `Server`. Parsing and applying use separate package-private helpers. The caller owns command-line handling, attachment, and the server connection. ## 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 `libtmux-workspace` builds a new tmux session from a YAML description. It uses libtmux's Java API and returns the session after capturing the completed window and pane structure. The module supports a focused [tmuxp](https://tmuxp.git-pull.com) subset: session names, windows, layouts, panes, and ordered shell commands. It rejects unknown configuration fields instead of accepting a larger file with missing behavior. ## Package and runtime Use the [`io.github.libtmux:libtmux-workspace`](https://central.sonatype.com/artifact/io.github.libtmux/libtmux-workspace) artifact with the libtmux BOM. The module targets Java 21 and requires tmux on the host for building. Parsing YAML does not create a session. This module does not load Python plugins or implement tmuxp's CLI. Use the [Python workspace documentation](https://tmuxp.git-pull.com/) when those features are required. [Module documentation](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-workspace/README.md) --- # Java workspace builder behavior Source: https://libtmux.org/en/java/latest/workspace/internals/topics/ > Internal configuration, application, and failure contracts of the Java workspace builder. A `Workspace` is an immutable description of a session. `read` and `parse` create that description. `build` is the operation that changes tmux. ## Supported configuration The root accepts `session_name` and `windows`. Windows carry names, layouts, and panes. A pane can be a command string, a list of commands, or a mapping with `shell_command`. An omitted pane list still produces tmux's initial pane. Unknown keys and unsupported value shapes are rejected. Layout names and layout descriptions are checked before construction; support for a named layout is checked against the target tmux version before creating objects. Session names containing `.` or `:` are rejected because those characters conflict with tmux target syntax. ## Construction and commands The builder creates a uniquely named staging session and renames that exact session to the requested name. It does not take over an existing session with the same name. All windows and panes are created before pane commands are sent, and layouts are applied before those commands run. Completion returns a refreshed `Session`. It establishes that the builder's tmux operations completed; it does not establish that a server launched by a pane command is ready to serve requests. ## Failure cleanup When construction fails, the builder attempts to kill the exact session it created. If the initial creation did not produce a usable handle, it targets the unique staging name. This keeps cleanup scoped to the new workspace. Cleanup can also fail. The builder preserves that failure as a suppressed exception on the original exception. Inspect suppressed exceptions before assuming the partial session was removed. Removing tmux objects cannot undo external effects already produced by shell commands. [Construction and cleanup](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-workspace/src/main/java/io/github/libtmux/workspace/WorkspaceApplier.java); [YAML validation](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-workspace/src/main/java/io/github/libtmux/workspace/WorkspaceParser.java). --- # Use the Java workspace builder Source: https://libtmux.org/en/java/latest/workspace/internals/guides/ > Use the in-development Java workspace builder from application code. Add the workspace module to a Java 21 project. The BOM selects compatible libtmux modules: ```kotlin dependencies { implementation(platform("io.github.libtmux:libtmux-bom:0.0.1-alpha.10")) implementation("io.github.libtmux:libtmux-workspace") } ``` Install tmux on the host before running the builder. ## Create and inspect This program builds on a dedicated socket, prints the resulting session name, and removes the session after inspection. Save it in your application's Java sources and run it through your existing Gradle application task. ```java import io.github.libtmux.Server; import io.github.libtmux.ServerEndpoint; import io.github.libtmux.Session; import io.github.libtmux.workspace.Workspace; import io.github.libtmux.workspace.WorkspaceBuilder; import java.util.UUID; public class WorkspaceGuide { public static void main(String[] args) { Workspace workspace = WorkspaceBuilder.parse(""" session_name: guide windows: - window_name: editor panes: - echo ready - echo ready """); try (Server server = Server.builder() .endpoint(ServerEndpoint.namedSocket( "workspace-" + UUID.randomUUID())) .build()) { Session session = WorkspaceBuilder.build(server, workspace); try { System.out.println(session.name()); } finally { session.kill(); } } } } ``` ## Read a file Use `WorkspaceBuilder.read(Path)` to parse YAML from disk. It reports file read failures as `UncheckedIOException`; invalid configuration raises `IllegalArgumentException`. Handle those before calling `build`. For an application that keeps the workspace running, keep the session rather than calling `kill`. Closing the Java server handle releases its transport; it does not serve as workspace removal. If construction fails, inspect the exception and its suppressed cleanup failures. [Topics](../topics/) describes the builder's cleanup scope. [Dependency and workspace usage](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-workspace/README.md); [Server lifetime API](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux/src/main/java/io/github/libtmux/Server.java). --- # Java workspace builder examples Source: https://libtmux.org/en/java/latest/workspace/internals/examples/ > Internal examples for building and inspecting workspaces through the Java API. The module README builds a session with two windows and verifies the returned window and pane counts. The port's `docs-tests` project collects Java README fences and runs them against real tmux. ## Build a described layout Within an application that already has a `Server`, import `Workspace` and `WorkspaceBuilder` from [`io.github.libtmux.workspace`](https://github.com/libtmux/libtmux-java/tree/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-workspace/src/main/java/io/github/libtmux/workspace), and `Session` from [`io.github.libtmux`](../../../reference/). This excerpt uses the same configuration as the module's result example: ```java Workspace workspace = WorkspaceBuilder.parse(""" session_name: built windows: - window_name: editor layout: even-horizontal panes: - shell_command: echo one - shell_command: echo two - window_name: server panes: - echo three """); Session session = WorkspaceBuilder.build(server, workspace); System.out.println(session.windows().size()); System.out.println(session.windows().get(0).panes().size()); ``` The output is two windows and two panes in the first window. Use the [guide](../guides/) for the complete server setup and cleanup. The builder creates a new session, so the requested name must be available. ## Verification From a prepared source checkout, run the documentation example project: ```console $ ./gradlew :docs-tests:test ``` The module README also exercises invalid layouts and creating a workspace beside an existing unrelated session. Those checks cover configuration behavior; they do not prove that arbitrary commands launched in a pane have finished or become ready. [Collected README examples](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-workspace/README.md) --- # Java workspace builder API Source: https://libtmux.org/en/java/latest/workspace/reference/ > Internal reference for the Java workspace builder and configuration APIs. The [`io.github.libtmux.workspace`](https://github.com/libtmux/libtmux-java/tree/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-workspace/src/main/java/io/github/libtmux/workspace) package exposes the builder facade and configuration records. Applications can parse YAML or construct the records before building through a core `Server`. ## Builder facade [`WorkspaceBuilder`](./io-github-libtmux-workspace-workspacebuilder-workspacebuilder/) provides three static entry points: - `read(Path)` reads a YAML file and wraps I/O errors in `UncheckedIOException`. - `parse(String)` reads YAML text and rejects invalid descriptions. - `build(Server, Workspace)` creates the session and returns a refreshed handle. Parsing and reading do not contact tmux. Build-time checks include the target server's support for the requested layout. ## Configuration records [`Workspace`](./io-github-libtmux-workspace-workspace-workspace/) holds the session name and ordered windows. [`WindowSpec`](./io-github-libtmux-workspace-windowspec-windowspec/) holds the name, optional layout, and panes. [`PaneSpec`](./io-github-libtmux-workspace-panespec-panespec/) holds the ordered shell commands. These records copy their lists so later changes to an input list do not alter a description already constructed. They describe the supported Java subset; they are not Python plugin or tmuxp runtime extension interfaces. ## Errors and lifetime Invalid configuration raises `IllegalArgumentException`. Build failures keep the original exception and attach cleanup failures as suppressed exceptions. The caller retains ownership of the supplied server and the successful session. Use the core session API for later inspection or removal. [Public builder contract](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-workspace/src/main/java/io/github/libtmux/workspace/WorkspaceBuilder.java) ## API declarations - [io.github.libtmux.workspace.PaneSpec.PaneSpec](https://libtmux.org/en/java/latest/workspace/reference/io-github-libtmux-workspace-panespec-panespec/) - [io.github.libtmux.workspace.WindowSpec.WindowSpec](https://libtmux.org/en/java/latest/workspace/reference/io-github-libtmux-workspace-windowspec-windowspec/) - [io.github.libtmux.workspace.Workspace.Workspace](https://libtmux.org/en/java/latest/workspace/reference/io-github-libtmux-workspace-workspace-workspace/) - [io.github.libtmux.workspace.WorkspaceBuilder.WorkspaceBuilder](https://libtmux.org/en/java/latest/workspace/reference/io-github-libtmux-workspace-workspacebuilder-workspacebuilder/) --- # Workspace reference generation Source: https://libtmux.org/en/java/latest/workspace/internals/documentation/ > Keep help, completion, and site references aligned with command metadata. The native Java CLI derives help and exports from its parser definitions. [Compatibility](../../reference/compatibility/) records current coverage. ## Command metadata Picocli defines the native command graph. `--generate schema` exports metadata and `--generate bash` emits completion through its code generator. Other manual and completion formats are not exposed by the workspace executable. Bash generation also supports a JSON artifact or one completed NDJSON event; the script remains a string inside the machine result. Schema generation keeps its metadata document. See [machine completion output](../../cli/completion/). 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. --- # MCP for Java Source: https://libtmux.org/en/java/latest/mcp/ > Run or embed the Java MCP server with typed tmux tools and a static capability resource. [`io.github.libtmux:libtmux-mcp`](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-mcp/README.md) provides a Java MCP server and a `libtmux-mcp` application launcher. It discovers tmux objects, captures terminal output, runs framed shell commands, and applies typed topology operations. Use JDK 21 or newer and tmux. The launcher selects its endpoint and toolsets at startup. It serves MCP over stdin and stdout. ## Start here - [Install](#install) points an MCP client at this server. - [Tools](./tools/) lists the MCP operations, arguments, and results. - [Guides](./guides/) build the launcher and connect a client. - [Topics](./topics/) explain toolsets, input preflight, and wait semantics. - [Examples](./examples/) call a tool, then explore server internals. - [Language API](./reference/) documents embedding and implementation types. The only MCP resource is the static `tmux://capabilities` report. The server does not register workflow prompts or dynamic resource subscriptions. The [Workspace Manager](../workspace/) is a separate Java module. Application code can parse and build declarative workspaces; MCP clients compose the exposed topology tools. [Module documentation](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-mcp/README.md). --- # Java MCP topics Source: https://libtmux.org/en/java/latest/mcp/topics/ > Understand Java toolsets, socket provenance, bounded synchronous waits, and stable targets. The Java MCP server freezes its tool selection before serving requests. The same selection controls tool listing and invocation. ## Toolsets and endpoint provenance `LIBTMUX_TOOLSETS` selects any combination of `inspect`, `manage`, `execute`, and `teardown`. `LIBTMUX_TOOLS` adds names; `LIBTMUX_EXCLUDE_TOOLS` removes them last. An empty set selects none. Unknown names and empty list elements fail startup. Without a socket selector, the launcher uses `libtmux-mcp`. If it creates that daemon with the shipped minimal configuration, all four sets are enabled by default. Existing or explicitly selected daemons omit teardown from the default. The static `tmux://capabilities` resource reports the effective surface and its provenance. Toolsets configure the interface; shell input and server configuration still act with the tmux user's authority. ## Stable targets and input checks Use IDs returned by listings. A bare numeric index is refused where a stable object ID is required. Pane input checks caller context, human attention, pane modes, and synchronized-input membership. Framed commands require one live shell and reject a synchronized cohort. A check is an observation, so tmux state can still change before dispatch. ## Waiting and cancellation `run_shell_command` returns output and exit status from a framed subshell. Directory changes and exports do not persist in the parent shell. `wait_for_text` observes output produced elsewhere. `capture_since` returns a cursor and flags discontinuity when retained history no longer follows it. Waits are capped at 30 seconds by default and two minutes absolutely. Oversized requests are clamped. A client's own request timeout is separate. The SDK dispatches synchronous handlers on a worker scheduler. A client timeout or cancellation abandons the answer but does not stop an already running synchronous handler or undo effects. Inspect state before retrying. [Behavior and waiting contract](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/docs/guide/mcp.md). --- # Connect a Java MCP client Source: https://libtmux.org/en/java/latest/mcp/guides/ > Build the Java application distribution and configure its endpoint and toolsets. Build the Java application distribution, then point the MCP client at its launcher. Run these commands from the Java repository with JDK 21 or newer and tmux installed. ## Build the launcher ```console $ ./gradlew :libtmux-mcp:installDist ``` The distribution contains a `bin/libtmux-mcp` launcher and its required JARs. Keep them together. Launch it on a named socket with an inspection surface: ```console $ LIBTMUX_TOOLSETS=inspect \ libtmux-mcp/build/install/libtmux-mcp/bin/libtmux-mcp \ --socket-name docs-agent ``` It serves MCP on standard input and output. Configure the client's command to the launcher location. If the distribution's `bin` directory is on the client's `PATH`, an `mcpServers` entry can use: ```json { "mcpServers": { "tmux-java": { "command": "libtmux-mcp", "args": ["--socket-name", "docs-agent"], "env": { "LIBTMUX_TOOLSETS": "inspect" } } } } ``` ## Verify and change selection Read `tmux://capabilities`, then call `list_sessions`. Select `inspect,manage,execute` for workflows that create topology and run pane commands. Reconnect after changing startup configuration. `--socket` accepts a socket path; `--tmux` selects the tmux executable. The environment also supports `LIBTMUX_SOCKET`, `LIBTMUX_SOCKET_PATH`, and `LIBTMUX_TMUX_CONFIG`. The retired `--safety`, `LIBTMUX_SAFETY`, `--watch`, and `LIBTMUX_WATCH` settings fail startup. Use toolsets and bounded observation tools instead. [Launcher and flags](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-mcp/README.md). --- # Java MCP examples Source: https://libtmux.org/en/java/latest/mcp/examples/ > List sessions through the Java 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 public Java entry point accepts an existing libtmux `Server`. An application can supply a custom MCP transport or use the stdio entry point that the launcher uses. ### Supply a transport This method exposes the [public serving contract](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-mcp/src/main/java/io/github/libtmux/mcp/TmuxMcpServer.java): ```java import io.github.libtmux.Server; import io.github.libtmux.mcp.TmuxMcpServer; import io.modelcontextprotocol.server.McpSyncServer; import io.modelcontextprotocol.spec.McpServerTransportProvider; public final class EmbeddedTmux { private EmbeddedTmux() {} public static McpSyncServer serve( Server server, McpServerTransportProvider transport) { return TmuxMcpServer.serving(server, transport); } } ``` Ownership of the transport transfers on entry. The returned MCP server closes it, including on failed startup. Close the returned server when your application's serving lifetime ends. The method reads selection from the process environment. Set that environment before creating the server. For a subprocess that manages its own lifecycle, use the [launcher guide](../guides/). ### Inspect command completion On a disposable session, use an MCP client to discover a pane and invoke `run_shell_command`. Read its typed exit status and output. Use `capture_since` for later screen changes; the prompt redraw can arrive after the command's completion signal. The port's [MCP tests](https://github.com/libtmux/libtmux-java/tree/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-mcp/src/test) exercise transport and real tmux behavior. The [documentation tests](https://github.com/libtmux/libtmux-java/tree/4f057d367a25dee818d70876fa283fc503a3a7eb/docs-tests) cover the port's collected snippets; this page's embedding wrapper is a source-derived adaptation, not one of those collected snippets. --- # Java MCP API Source: https://libtmux.org/en/java/latest/mcp/reference/ > Find the public TmuxMcpServer entry points and the separate schema-validated tool catalog. For MCP client requests, use the [tool reference](../tools/). This page covers language APIs for embedding or extending the server. `TmuxMcpServer` is the public Java entry point. The MCP catalog includes operations implemented by package-private classes; Java visibility does not determine whether a tool exists on the wire. ## Java entry points `TmuxMcpServer.overStdio(server)` serves through standard input and output. `TmuxMcpServer.serving(server, transport)` accepts a custom [`McpServerTransportProvider`](https://github.com/modelcontextprotocol/java-sdk/blob/v2.0.1/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpServerTransportProvider.java). Both return an SDK [`McpSyncServer`](https://github.com/modelcontextprotocol/java-sdk/blob/v2.0.1/mcp-core/src/main/java/io/modelcontextprotocol/server/McpSyncServer.java). The returned server owns its transport. Ownership transfers on entry, including startup failure. The embedding application owns the broader tmux server lifetime and closes the MCP server when serving ends. [Public source contract](https://github.com/libtmux/libtmux-java/blob/4f057d367a25dee818d70876fa283fc503a3a7eb/libtmux-mcp/src/main/java/io/github/libtmux/mcp/TmuxMcpServer.java). ## MCP tools and resource The [tool reference](../tools/) covers registered operation names and input/output schemas. Results contain typed `structuredContent` and JSON text. Expected tmux failures become tool errors so the caller can inspect the failure and choose a recovery. The `tmux://capabilities` resource is static for the process lifetime. It reports tool selection, connection provenance, and capability declarations. The server does not register prompts or dynamic hierarchy subscriptions. Use the [Workspace builder API](../../workspace/reference/) for declarative configuration. It is a separate Java library and has no corresponding workspace-file MCP route. ## API declarations - [io.github.libtmux.mcp.Main.Main](https://libtmux.org/en/java/latest/mcp/reference/io-github-libtmux-mcp-main-main/) - [io.github.libtmux.mcp.TmuxMcpServer.TmuxMcpServer](https://libtmux.org/en/java/latest/mcp/reference/io-github-libtmux-mcp-tmuxmcpserver-tmuxmcpserver/) [Protocol catalog](https://libtmux.org/en/java/latest/mcp/tools.json) --- # Workspace Manager for Java (in development) Source: https://libtmux.org/en/java/latest/workspace/ > Build the local Java tmux-workspace CLI; implementation coverage remains partial and unreleased. **Workspace Manager for Java 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 capture sessions, discover and search documents, convert formats, import configurations and run editors. Common commands, layout, environment, before scripts and diagnostics are implemented. Python shell evaluation and plugin/custom-builder loading use a checked tmuxp bridge. Human load progress supports presets, custom templates and a bounded script-output panel. Append keeps its original target session across input files. ## Load a workspace from the terminal Follow the [local installation walkthrough](./guides/installation/) from a `workspace-cli` checkout of the [Java repository](https://github.com/libtmux/libtmux-java). It builds the native command and loads a small workspace on a private socket. After building, inspect the command without starting tmux: ```console $ workspace-cli/build/install/tmux-workspace/bin/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 Python plugins and custom builders execute through the checked tmuxp bridge. Extension append rejects documents containing `before_script` to preserve the borrowed session. Complete configuration/capture coverage and full terminal/platform acceptance remain unfinished. Progress uses the initial terminal dimensions; it does not track resizing, and Unicode clipping is conservative. 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 `libtmux-workspace` library module 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. --- # Third-party notices Source: https://libtmux.org/en/java/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/java/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/java/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. ## 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/java/latest/workspace/reference/compatibility/ > Current local CLI capabilities, remaining gaps, and the historical builder audit. **Local implementation, unpublished.** The Java `tmux-workspace` CLI is available in the `workspace-cli` source worktree. Its command and configuration coverage remains partial. See [installation](../../guides/installation/) for local setup; installing a published library does not establish availability of this CLI. Compatibility targets useful tmuxp 1.74.0 commands and workspace workflows, with native validation, execution and output conventions. Matching command names does not promise identical runtime behavior or Python semantics. ## This port Native services cover load, capture, discovery, search, conversion, both importers, editor execution and diagnostics. Loading creates or reuses a session; append authenticates the inherited tmux daemon and keeps the original session across input files even if a script moves the invoking pane. Attached load uses the controlling terminal and an authenticated invoking client/server. Failure results identify retained effects rather than promising rollback. The normalizer supports command shorthand and sequential enter/delay settings, directories, environment, shell overrides, layouts, indexes, focus, options, before-scripts and readiness policies. Blank panes and explicit launch commands skip readiness checks. Unsupported configuration keys fail before tmux access. YAML aliases and merges expand with depth/value bounds; strict JSON rejects YAML syntax. Conversion preserves extension fields. Capture recovers topology, directories, focus and configured options, but cannot recover command history, bootstrap scripts or plugin intent. Search uses native Java regular expressions. Loading checks layouts across all inputs before setup scripts or topology changes. Named layouts accept unique abbreviations supported by the running daemon; only a cold endpoint falls back to the selected client's version. Serialized layouts have bounded checksum, tree and pane-count validation. Tmux remains responsible for geometry correction and pruning. Every command accepts `--json` and `--ndjson`, with NDJSON taking precedence. Load and child output stream as escaped events. Human output has semantic colors; log levels and load log files keep diagnostics separate from results. Interruptions have bounded final-output delivery and can leave an incomplete stream. `load -2` selects 256-color mode; `-8` fails before workspace lookup. The parser can generate command metadata and Bash completion. Human load progress updates from events on terminal stderr. Five presets and custom templates are supported; `--progress-lines` bounds the script panel, with 3 rows by default, 0 hiding it and -1 using available initial terminal height. Script streams keep their original destinations. Machine output, redirected stderr, `TERM=dumb`, `--no-progress` and `TMUXP_PROGRESS=0` disable the display. Completion and interruption clear the frame. Python shell and workspace extension execution require an explicitly selected `TMUX_WORKSPACE_PYTHON` interpreter with tmuxp 1.74.0. Plugin/custom-builder loading checks that runtime before backend mutation and resolves builder imports relative to the source document. Extension results identify observed topology; they do not infer ownership of sessions returned by custom code. Append keeps the authenticated borrowed session even if the invoking pane moves. Extension append rejects any document containing `before_script`, including null or empty values, because tmuxp can delete the borrowed session when that script fails. Opaque extension execution does not emit native per-pane progress. tmux 3.2a alters literal dollar signs in newly created session names; Python's classic builder can fail its name check for those inputs. Ordinary names are covered by the current/floor extension tests. Interactive editors and Python shells use the controlling terminal while machine results remain on stdout. Ordinary native load and read commands do not need Python. The local source reference is workspace-cli/README.md. Use the native executable's `--help` for the options implemented in that checkout. ### Remaining gaps - Interactive prompt behavior and broader Python extension coverage remain open. - Progress samples terminal dimensions once and does not follow resizing. Unicode clipping is conservative. - Complete configuration/importer/capture coverage and full terminal/platform acceptance remain unfinished. Explicit launch commands currently skip readiness even when the policy is `always`. - Cancellation of caller-supplied streams that ignore interruption cannot reap their pending write; it may finish later when the caller drains the stream. Non-Linux descendant cleanup needs additional reparenting tests. ## Historical builder audit Audit date: 2026-09-09. [Native source snapshot](https://github.com/libtmux/libtmux-java/tree/4f057d367a25dee818d70876fa283fc503a3a7eb). 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 3 upstream YAML examples in the original audit. Source and integration tests passed for its supported builder surface, but starting a named-layout workspace on a cold socket could fail. Full normalization and CLI services were absent. The source snapshot had no native CLI. At that baseline, the parser accepted session names/windows, window names/layouts/panes and pane commands, and rejected unknown keys. The builder used a staging session and attempted cleanup on failure. These observations refer to the original library surface, not the current native CLI. 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/java/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/java/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: ```java pane.sendLine("echo hi"); pane.options().set("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/java/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: ```java Session session = server.sessions().get(0); Window window = session.windows().get(0); Session back = window.session(); back.equals(session); ``` ## The active child "Which window is in front right now" and "which pane would a command actually reach" are common enough questions that most ports expose the active child directly rather than making you filter a list: | Port | Session's active window | Window's active pane | |------|---------------------------|------------------------| | Python | `session.active_window` | `window.active_pane` | | TypeScript | `session.activeWindow` (getter) | `window.activePane` (getter) | | Go | `session.ActiveWindow()` → `(Window, bool)` | `window.ActivePane()` → `(Pane, bool)` | | Rust | `await session.active_window()` → `Result, Error>` | `await window.active_pane()` → `Result, Error>` | | Java | `session.activeWindow()` → `Optional` | `window.activePane()` → `Optional` | | .NET | `session.ActiveWindow` (property) | `window.ActivePane` (property) | | C++ | `session->active_window()` | `window->active_pane()` | | Swift | filter for `isActive` on `snapshot.windows(of: session)`: `Window` carries its own `window_active` flag rather than the session exposing an accessor | same pattern, on the pane's own active flag | In Swift, filter snapshot children by `isActive`. Other ports expose an active-child method or property on the parent. [Format-token fields](../format-tokens/) describes the underlying `window_active` and `pane_active` fields. ## Is it in that collection? Checking membership generally goes through whatever your language uses for collection membership, since most of these calls already return an ordinary array, slice, or list: - **Python** overloads `in` directly on its `QueryList`: `window in session.windows`, `pane in window.panes`. - **Java**, **C++**, and **.NET** return standard collections. Use their standard membership operations with the identity comparison appropriate to the port. - **TypeScript** returns iterable `Selection` objects. Iterate over the selection and compare IDs, or spread it into an array for standard array operations. - **Go** and **Rust** return slices or vectors. Iterate and compare object IDs when testing membership by tmux identity. ## Is this the same object? Compare IDs to determine whether two handles refer to the same tmux object on the same server. Equality operators vary by port: | Port | How you check | |------|----------------| | Python | `window.window_id == other.window_id` (or `pane.pane_id == ...`) | | TypeScript | compare `.id` | | Go | `pane.ID() == other.ID()`: `PaneID` is a plain, `==`-comparable `string` | | Rust | `pane.id() == other.id()`: verified from the port's own doctests, not struct equality | | Java | `pane.equals(other)`: overridden to compare server identity plus pane ID | | .NET | `pane.Equals(other)`: overridden to compare a generation counter plus ID | | C++ | `pane == other`: `operator==` is defined directly on `Session`/`Window`/`Pane` | | Swift | compare `.id` for identity; see the equality note below | **Swift's equality compares captured state.** Compiler-synthesized equality for `Session`, `Window`, and `Pane` compares every stored property, including dimensions and current command. Two reads can compare unequal even when they describe the same tmux object. Compare `.id` when checking identity on the same server. --- # Context managers Source: https://libtmux.org/en/java/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. ```java ServerConfig config = ServerConfig.builder() .endpoint(ServerEndpoint.socketPath(socket)) .build(); try (Server server = Server.open(config)) { Session session = server.newSession("demo"); Window window = session.newWindow("build"); Pane pane = window.split(); pane.sendLine("echo hello from libtmux"); // session, window, and pane all outlive this block: only the // connection this `server` handle held is released on the way out. } ``` ## Rust: no async `Drop`, so cleanup is explicit or best-effort Rust's `Drop::drop` is synchronous and cannot await an async tmux kill. Use explicit shutdown when you need to observe cleanup failures: - **`kill(self)` consumes the handle.** Session, window, and pane kill methods take `self` by value, preventing subsequent use of that handle. - **`libtmux::test::TestServer` provides a test guard.** Call `guard.shutdown().await?` to handle cleanup errors. Its `Drop` implementation falls back to synchronous, best-effort `force_cleanup()`. ## C++: RAII exists, but only for a private test server C++'s `Session`, `Window`, and `Pane` are non-owning values; destroying a handle does not kill its tmux object. `libtmux::test::ScopedTmuxServer`, in the separate `testing` CMake component, owns a private test server and its temporary socket directory: ## TypeScript, Go, Swift: no built-in scoping at all TypeScript, Go, and Swift require explicit cleanup of sessions, windows, and panes. Connection or notification handles may have separate disposal APIs: - **TypeScript** implements `[Symbol.asyncDispose]` on control connections and notification streams. `await using` releases those handles; it does not kill the watched session or pane. See [Control mode vs one-shot](/concepts/transports/). Use `finally` for a session your program owns: ```typescript const session = await server.newSession({ name: "work" }); try { const window = await session.newWindow({ name: "editor" }); await window.panes.at(0)?.sendKeys("echo hi"); } finally { await session.kill(); } ``` - **Go** implements `io.Closer` on `ControlClient`, `PaneObservation`, and `NotificationStream`. Use `defer conn.Close()` for those resources and an explicit `Kill(ctx)` for tmux objects: ```go session, err := server.NewSession(ctx, tmux.NewSessionRequest{Name: "work"}) if err != nil { return err } defer session.Kill(ctx) // idiomatic Go: not a library-provided guarantee ``` - **Swift** uses non-owning session, window, and pane values. Call `try await server.kill(session)` or the corresponding window or pane overload when cleanup is required. ## What this means in practice Use explicit cleanup for objects whose handles have no disposal hook. For an entire disposable test server, prefer your port's test fixture or server guard; see [Testing with libtmux](/guides/testing-with-libtmux/). --- # Pane interaction Source: https://libtmux.org/en/java/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: ```java pane.send("echo hi"); // no Enter pane.sendLine("echo hi"); // text and \r in one send-keys -l call ``` ## 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`. ```java 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/java/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: ```java pane.options().set("automatic-rename", "off"); pane.options().all(); pane.options().unset("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: ```java session.hooks().set("session-renamed", "display-message 'renamed'"); session.hooks().all(); ``` ## 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/java/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: ```java pane.floating(); // Optional: a different field, same idiom: empty // rather than a sentinel when the token isn't populated ``` Rust's `formats.rs` marks this token as optional. Consult its generated reference for the accessor name. .NET also distinguishes missing values from incomplete captures. `Pane.Title` is nullable because tmux may report no title. `Pane.Height`, `.Width`, and `.Index` throw `IncompleteSnapshotException` when the read that produced the handle did not request those fields. A handle resolved by ID alone may therefore lack enough data to answer: ## A generated table under the accessor Several ports generate scope- and version-tagged field catalogs from tmux source or documentation. [Architecture](../architecture/) describes the layouts. Examples include: - **TypeScript** uses `_generated/format_fields.ts` rows with `scope`, `since`, and `token`. For example, `pane_zoomed_flag` has pane scope and requires tmux 3.7. `_generated/field_aliases.ts` supplies the camelCase alias `pane.zoomedFlag`. - **Rust** uses a macro row in `formats.rs` for each token's wire name, scope, tmux version, and type. `pane_dead_signal` has `Pane` scope, requires `V3_3`, and is decoded as `Text`. - **Go** generates `format_generated.go` with `internal/generate/formats`. Some accessors decode richer values: `pane.DeadTime()` returns `(time.Time, bool)` and performs timestamp parsing for the caller. Two per-token facts survive across every one of these catalogs, because they're facts about tmux, not about any one port's generator: `pane_dead_signal` and `pane_dead_time` arrived in tmux 3.3, and a cluster of pane-geometry and floating-pane tokens (`pane_floating_flag`, `pane_pb_progress`, `pane_x`, `pane_y`, `pane_z`, `pane_zoomed_flag`, `bracket_paste_flag`, `synchronized_output_flag`, among others) arrived together in 3.7. ## The two ports that didn't generate the full catalog Swift and C++ expose fixed, non-optional fields on `Session`, `Window`, and `Pane`: - **Swift** carries `index`, `width`, `height`, `isActive`, `currentCommand`, `currentPath`, and the four edge flags. - **C++** declares fields in `kFields` arrays. Pane fields include `id`, `command`, `active`, `index`, `title`, `pid`, `tty`, `path`, `width`, `height`, `dead`, `in_mode`, edge flags, and `piping`. Accessors return `std::string_view`, `bool`, or `long long`. For a token outside the fixed fields, C++ provides one-shot expansion with `pane->expand("#{pane_dead_signal}")`. Swift uses `FormatSubscription` on a control connection, delivering `SubscriptionChange` when tmux re-evaluates the token. That API observes changes over time. [Architecture](../architecture/) describes the fixed-field model. ## Fields promoted from the active child Python exposes fields promoted from an active child. For example, `session.pane_id` identifies the active pane of the session's active window: 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/java/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)` | ```java Channel built = server.channel("built"); built.signal(); WakeReason reason = built.await(Duration.ofSeconds(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/java/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/java/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: "...")` | ```java ServerConfig config = ServerConfig.builder() .endpoint(ServerEndpoint.namedSocket("work")) .build(); Server named = Server.open(config); ``` 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` | ```java if (server.isAlive()) { 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/java/latest/topics/errors-and-exceptions/ > What a failed tmux command becomes in each port, and the question every one of them has to answer before letting you retry it. A command can fail because tmux rejects it, or because the transport stops before returning a reply. Ports report these failures through typed exceptions or return values. Before retrying a mutation, determine whether tmux may already have received it. For lookup failures caused by zero or multiple matches, see [Filtering and queries](/concepts/queries/#the-cardinality-contract-side-by-side). ## A failed command, as a value | Port | How it fails | Base type | |------|--------------|-----------| | Python | throws | `LibTmuxException`: carries an optional `subcommand`; `str()` reads `": "` | | TypeScript | throws | `LibTmuxException extends Error`, with `TmuxCommandError` (tmux ran and refused) and `TmuxTransportError` (it didn't get an answer) as the two shapes that matter here | | Go | returns `(T, error)` | no shared base type: small typed `...Error` structs plus sentinel `errors.New` values, composed with `errors.Is` / `errors.As` and `%w` wrapping | | Rust | returns `Result` | one `Error` enum, `#[non_exhaustive]`, matched rather than caught | | Java | throws (unchecked) | `LibTmuxException extends RuntimeException` | | .NET | throws | `LibTmuxException`, with typed subclasses per failure (`TmuxCommandException`, `TmuxTransportException`, `TmuxObjectNotFoundException`, and a dozen more) | | C++ | returns `expected` | `CommandFailure { kind, delivery, exit_code, diagnostic }`: no exception type at all | | Swift | throws (typed) | `enum TmuxError: Error`, thrown as `throws(TmuxError)`: Swift's typed-throws syntax, not a bare `throws` | Rust and C++ return result values. Go returns an `error` that callers inspect with `errors.Is` or `errors.As`. Exception-based ports report failures through their exception hierarchies. TypeScript's own docs make the split between its two exception shapes concrete: ## Is it safe to retry? Retry a mutation automatically only when you know it was not dispatched, or when repeating it is safe for your operation. A timeout, cancellation, or dropped connection can occur after tmux has acted. These APIs expose delivery information: | Port | Name | States | |------|------|--------| | TypeScript | `TmuxTransportError.delivery` | `"not_started"` / `"written"` / `"replied"` / `"indeterminate"`: only `not_started` is safe to retry blindly | | .NET | `LibTmuxException.Dispatch` (`TmuxDispatchState`) | `NotDispatched` / `Dispatched` / `Unknown` (the default) | | Java | `DispatchOutcome`, via `TmuxTimeoutException.outcome()` | `NOT_DISPATCHED` / `COMPLETE` / `UNKNOWN` | | C++ | `DeliveryStatus` | `not_started` / `written` / `replied` / `indeterminate` | | Rust | `ControlModeErrorKind` (behind the `control-mode` feature) | `DispatchTimedOut` (safe to retry) vs. plain `TimedOut` (not: the connection may have already committed the command) | | Python, Go's one-shot path | - | inspect the exit status after normal completion; an interrupted call needs separate state verification | | Go's control-mode pool | handled internally, not exposed | a failed pooled connection is retired rather than reused, rather than handing the caller a retry-safety flag to check | | Swift | documented, not typed | `ControlSession`'s own doc comment states the same rule in prose ("a command that never reached tmux is safe to retry") without a dedicated enum | A state such as `not_started`, `NotDispatched`, `NOT_DISPATCHED`, or `DispatchTimedOut` identifies a request that did not reach tmux. Treat unknown delivery as potentially executed. C++ and TypeScript also distinguish `written`, where the transport accepted the request but no terminal reply has arrived. For subprocess calls that complete normally, inspect the exit status. If a call is interrupted or times out without a delivery state, check tmux's resulting state before repeating a mutation.