### Index

(index)=

# tmuxp

Session manager for tmux. Load, freeze, and convert tmux sessions through
YAML/JSON configuration files. Powered by [libtmux](https://libtmux.git-pull.com/).

::::{grid} 1 2 3 3
:gutter: 2 2 3 3

:::{grid-item-card} Quickstart
:link: quickstart
:link-type: doc
Install and run your first command.
:::

:::{grid-item-card} CLI Reference
:link: cli/index
:link-type: doc
Every command, flag, and option.
:::

:::{grid-item-card} Configuration
:link: configuration/index
:link-type: doc
Config format, examples, and environment variables.
:::

::::

::::{grid} 1 1 2 2
:gutter: 2 2 3 3

:::{grid-item-card} Topics
:link: topics/index
:link-type: doc
Workflows, plugins, and troubleshooting.
:::

:::{grid-item-card} Contributing
:link: project/index
:link-type: doc
Internals, development setup, and release process.
:::

::::

## Install

```console
$ pip install tmuxp
```

```console
$ uv tool install tmuxp
```

```console
$ brew install tmuxp
```

See [Quickstart](quickstart.md) for all installation methods and first steps.

## Load a workspace

```yaml
session_name: my-project
windows:
  - window_name: editor
    panes:
      - shell_command:
          - vim
      - shell_command:
          - git status
```

```console
$ tmuxp load my-project.yaml
```

```{image} _static/tmuxp-demo.gif
:width: 888
:height: 589
:loading: lazy
```

```{toctree}
:hidden:

quickstart
cli/index
configuration/index
topics/index
internals/index
project/index
history
```

```{toctree}
:hidden:
:caption: More

about_tmux
migration
glossary
MCP <https://libtmux-mcp.git-pull.com>
GitHub <https://github.com/tmux-python/tmuxp>
```

---

### Quickstart

(quickstart)=

# Quickstart

tmuxp launches a whole tmux workspace — its windows, panes, and the commands
inside them — from a single YAML or JSON file. Install it, write one file, and
{ref}`tmuxp load <cli-load>` builds the session and drops you into it. This page
takes you from nothing to a running session.

## Installation

Ensure you have at least tmux **>= 3.2** and python **>= 3.10**.

```console
$ pip install --user tmuxp
```

If you manage dependencies with [uv] inside a project environment, add tmuxp to
your lockfile instead:

```console
$ uv add tmuxp
```

To run tmuxp without installing it globally — the way you'd use [pipx] — invoke
it through [uvx]:

```console
$ uvx tmuxp
```

Upgrade to the latest release with:

```console
$ pip install --user --upgrade tmuxp
```

Within a uv-managed project, upgrade by refreshing the lockfile and syncing:

```console
$ uv lock --upgrade-package tmuxp
```

```console
$ uv sync
```

Then install {ref}`completion`.

Homebrew users can install it with:

```console
$ brew install tmuxp
```

(developmental-releases)=

### Developmental releases

New versions of tmuxp are published to [PyPI] as alpha, beta, or release
candidates. Their version carries an `a1`, `b1`, or `rc1` suffix — `1.10.0b4` is
the fourth beta of `1.10.0`, before general availability.

Install the latest pre-release with the tool you use:

```console
$ pip install --user --upgrade --pre tmuxp
```

```console
$ uv add tmuxp --prerelease allow
```

```console
$ uvx --from 'tmuxp' --prerelease allow tmuxp
```

```console
$ pipx install --suffix=@next 'tmuxp' --pip-args '\--pre' --force
```

After the pipx install, load with `tmuxp@next load [session]`.

Or track trunk directly (it can break):

```console
$ pip install --user -e git+https://github.com/tmux-python/tmuxp.git#egg=tmuxp
```

```console
$ uv add "tmuxp @ git+https://github.com/tmux-python/tmuxp.git@master"
```

```console
$ uvx --from "tmuxp @ git+https://github.com/tmux-python/tmuxp.git@master" tmuxp
```

```console
$ pipx install --suffix=@master 'tmuxp @ git+https://github.com/tmux-python/tmuxp.git@master' --force
```

[pip]: https://pip.pypa.io/en/stable/
[pipx]: https://pypa.github.io/pipx/docs/
[PyPI]: https://pypi.org/project/tmuxp/
[uv]: https://docs.astral.sh/uv/getting-started/features/#python-versions
[uvx]: https://docs.astral.sh/uv/guides/tools/

## Commands

:::{seealso}

{ref}`examples`, {ref}`commands`, {ref}`completion`.

:::

tmuxp launches workspaces / sessions from JSON and YAML files.

Workspace files live in `$HOME/.tmuxp`, or in a project directory as
`.tmuxp.yaml`, `.tmuxp.yml`, or `.tmuxp.json`. Every workspace file needs:

1. a `session_name`
2. a list of `windows`
3. a list of `panes` for every window

Create a file, `~/.tmuxp/example.yaml`:

```{literalinclude} ../examples/2-pane-vertical.yaml
:language: yaml
```

```console
$ tmuxp load example.yaml
```

This builds and attaches your session.

Load several at once:

```console
$ tmuxp load example.yaml anothersession.yaml
```

If you're already inside a session, tmuxp offers to `switch-client` for you, or
to append the new windows to the session you're in.

You can point tmuxp at a different config directory with the `TMUXP_CONFIGDIR`
environment variable:

```console
$ TMUXP_CONFIGDIR=$HOME/.tmuxpmoo tmuxp load cpython
```

Or set it in your `~/.bashrc` / `~/.zshrc`:

```console
$ export TMUXP_CONFIGDIR=$HOME/.yourconfigdir/tmuxp
```

You can also [import][import] configs from [teamocil] and [tmuxinator].

## Pythonics

:::{seealso}

{ref}`libtmux python API documentation <libtmux:api>` and {ref}`developing`.

:::

Under the hood, tmuxp drives tmux through
[libtmux](https://libtmux.git-pull.com/) — an
[object-relational mapper][object relational mapper] and
[abstraction layer] over `tmux(1)`'s commands. Each config concept maps to a
libtmux call:

| {ref}`libtmux Python API <libtmux:api>` | {term}`tmux(1)` equivalent |
| ------------------------------------- | -------------------------- |
| {meth}`libtmux.Server.new_session`    | `$ tmux new-session`       |
| {attr}`libtmux.Server.sessions`       | `$ tmux list-sessions`     |
| {attr}`libtmux.Session.windows`       | `$ tmux list-windows`      |
| {meth}`libtmux.Session.new_window`    | `$ tmux new-window`        |
| {attr}`libtmux.Window.panes`          | `$ tmux list-panes`        |
| {meth}`libtmux.Window.split`          | `$ tmux split-window`      |
| {meth}`libtmux.Pane.send_keys`        | `$ tmux send-keys`         |

[import]: http://tmuxp.git-pull.com/commands/#import
[tmuxinator]: https://github.com/aziz/tmuxinator
[teamocil]: https://github.com/remiprev/teamocil
[abstraction layer]: http://en.wikipedia.org/wiki/Abstraction_layer
[object relational mapper]: http://en.wikipedia.org/wiki/Object-relational_mapping

---

### Cli/Completion

(completion)=

(completions)=

(cli-completions)=

# Completions

Shell completion teaches your shell to finish `tmuxp` subcommands and flags when
you press Tab. It's optional, and how you set it up depends on your tmuxp
version — 1.17 and newer use [shtab], while 1.1 through 1.16 used [click]. Pick the
section that matches yours.

## tmuxp 1.17+ (experimental)

```{note}
See the [shtab library's documentation on shell completion](https://docs.iterative.ai/shtab/use/#cli-usage) for the most up to date way of connecting completion for tmuxp.
```

Provisional support for completions in tmuxp 1.17+ are powered by [shtab]. This must be **installed separately**, as it's **not currently bundled with tmuxp**.

```console
$ pip install shtab --user
```

With a [uv](https://docs.astral.sh/uv/getting-started/features/#python-versions) project you can add it directly as a development dependency:

```console
$ uv add --dev shtab
```

Or reach for [uvx](https://docs.astral.sh/uv/guides/tools/) when you want a pipx-style ephemeral install:

```console
$ uvx shtab --help
```

:::{tab} bash

```console
$ shtab --shell=bash -u tmuxp.cli.create_parser \
  | sudo tee "$BASH_COMPLETION_COMPAT_DIR"/TMUXP
```

:::

:::{tab} zsh

```console
$ shtab --shell=zsh -u tmuxp.cli.create_parser \
  | sudo tee /usr/local/share/zsh/site-functions/_TMUXP
```

:::

:::{tab} tcsh

```console
$ shtab --shell=tcsh -u tmuxp.cli.create_parser \
  | sudo tee /etc/profile.d/TMUXP.completion.csh
```

:::

## tmuxp 1.1 to 1.16

```{note}
See the [click library's documentation on shell completion](https://click.palletsprojects.com/en/8.0.x/shell-completion/) for the most up to date way of connecting completion for tmuxp.
```

tmuxp 1.1 to 1.16 use [click]'s completion:

:::{tab} Bash

_~/.bashrc_:

```bash

eval "$(_TMUXP_COMPLETE=bash_source tmuxp)"

```

:::

:::{tab} Zsh

_~/.zshrc_:

```zsh

eval "$(_TMUXP_COMPLETE=zsh_source tmuxp)"

```

:::

[shtab]: https://docs.iterative.ai/shtab/
[click]: https://click.palletsprojects.com

---

### Cli/Convert

(cli-convert)=

# tmuxp convert

Convert workspace configuration files between YAML and JSON formats.

## Command

```{eval-rst}
.. argparse::
    :module: tmuxp.cli
    :func: create_parser
    :prog: tmuxp
    :path: convert
```

## Basic usage

````{tab} YAML -> JSON

```console
$ tmuxp convert /path/to/file.yaml
```

````

````{tab} JSON -> YAML

```console
$ tmuxp convert /path/to/file.json
```

````

tmuxp automatically will prompt to convert `.yaml` to `.json` and
`.json` to `.yaml`.

---

### Cli/Debug Info

(cli-debug-info)=

(tmuxp-debug-info)=

# tmuxp debug-info

Collect and display system information useful for debugging tmuxp issues and submitting bug reports.

## Command

```{eval-rst}
.. argparse::
    :module: tmuxp.cli
    :func: create_parser
    :prog: tmuxp
    :path: debug-info
```

## Example output

```console

$ tmuxp debug-info
--------------------------
environment:
    system: Linux
    arch: x86_64
...

```

---

### Cli/Edit

(edit-config)=

(cli-edit)=

# tmuxp edit

Open a workspace configuration file in your default editor for quick modifications.

## Command

```{eval-rst}
.. argparse::
    :module: tmuxp.cli
    :func: create_parser
    :prog: tmuxp
    :path: edit
```

---

### Cli/Exit Codes

(cli-exit-codes)=

# Exit Codes

tmuxp uses standard exit codes for scripting and automation.

| Code | Meaning |
|------|---------|
| `0` | Success |
| `1` | General error (config validation, tmux command failure) |
| `2` | Usage error (invalid arguments, missing required options) |

## Usage in scripts

Because the exit code is meaningful, a script can check it and react. Test it
explicitly with `$?`:

```bash
#!/bin/bash
tmuxp load my-workspace.yaml
if [ $? -ne 0 ]; then
    echo "Failed to load workspace"
    exit 1
fi
```

Or short-circuit with `||` to handle the failure inline:

```bash
#!/bin/bash
tmuxp load -d my-workspace.yaml || {
    echo "tmuxp failed with exit code $?"
    exit 1
}
```

---

### Cli/Freeze

(cli-freeze)=

(cli-freeze-reference)=

# tmuxp freeze

Export a running tmux session to a workspace configuration file. This allows you to save the current state of your tmux session for later restoration.

## Command

```{eval-rst}
.. argparse::
    :module: tmuxp.cli
    :func: create_parser
    :prog: tmuxp
    :path: freeze
```

## Basic usage

Freeze the current session:

```console
$ tmuxp freeze
```

Freeze a specific session by name:

```console
$ tmuxp freeze [session_name]
```

Overwrite an existing workspace file:

```console
$ tmuxp freeze --force [session_name]
```

## Output format

tmuxp will offer to save your session state to `.json` or `.yaml`.

If no session is specified, it will default to the attached session.

If the `--force` argument is passed, it will overwrite any existing workspace file with the same name.

---

### Cli/Import

(cli-import)=

# tmuxp import

Import and convert workspace configurations from other tmux session managers like
[teamocil] and [tmuxinator].

(import-teamocil)=

## From teamocil

Import teamocil configuration files and convert them to tmuxp format.

### Command

```{eval-rst}
.. argparse::
    :module: tmuxp.cli
    :func: create_parser
    :prog: tmuxp
    :path: import teamocil
```

### Basic usage

````{tab} YAML

```console
$ tmuxp import teamocil /path/to/file.yaml
```

````

````{tab} JSON

```console
$ tmuxp import teamocil /path/to/file.json
```

````

(import-tmuxinator)=

## From tmuxinator

Import tmuxinator configuration files and convert them to tmuxp format.

### Command

```{eval-rst}
.. argparse::
    :module: tmuxp.cli
    :func: create_parser
    :prog: tmuxp
    :path: import tmuxinator
```

### Basic usage

````{tab} YAML

```console
$ tmuxp import tmuxinator /path/to/file.yaml
```

````

````{tab} JSON

```console
$ tmuxp import tmuxinator /path/to/file.json
```

````

[teamocil]: https://github.com/remiprev/teamocil
[tmuxinator]: https://github.com/aziz/tmuxinator

---

### Cli/Index

(cli)=

(commands)=

# CLI Reference

::::{grid} 1 1 2 2
:gutter: 2 2 3 3

:::{grid-item-card} tmuxp load
:link: load
:link-type: doc
Load tmux sessions from workspace configs.
:::

:::{grid-item-card} tmuxp shell
:link: shell
:link-type: doc
Interactive Python shell with tmux context.
:::

:::{grid-item-card} tmuxp freeze
:link: freeze
:link-type: doc
Export running sessions to config files.
:::

:::{grid-item-card} tmuxp convert
:link: convert
:link-type: doc
Convert between YAML and JSON formats.
:::

:::{grid-item-card} Exit Codes
:link: exit-codes
:link-type: doc
Exit codes for scripting and automation.
:::

:::{grid-item-card} Recipes
:link: recipes
:link-type: doc
Copy-pasteable command invocations.
:::

::::

```{toctree}
:caption: General commands
:maxdepth: 1

load
shell
ls
search
```

```{toctree}
:caption: Configuration
:maxdepth: 1

edit
import
convert
freeze
```

```{toctree}
:caption: Diagnostic
:maxdepth: 1

debug-info
```

```{toctree}
:caption: Completion
:maxdepth: 1

completion
```

```{toctree}
:caption: Reference
:maxdepth: 1

exit-codes
recipes
```

(cli-main)=

(tmuxp-main)=

## Main command

The `tmuxp` command is the entry point for all tmuxp operations. Use subcommands to load sessions, manage configurations, and interact with tmux.

### Command

```{eval-rst}
.. argparse::
    :module: tmuxp.cli
    :func: create_parser
    :prog: tmuxp
    :nosubcommands:

    subparser_name : @replace
        See :ref:`cli-ls`
```

---

### Cli/Load

(cli-load)=

(tmuxp-load)=

(tmuxp-load-reference)=

# tmuxp load

Load a tmux session from a workspace file — this is the command you reach for
most. Point it at a project directory or a YAML/JSON file and tmuxp builds the
session and attaches you to it; the flags below are for the cases where you want
something other than that default.

## Command

```{eval-rst}
.. argparse::
    :module: tmuxp.cli
    :func: create_parser
    :prog: tmuxp
    :path: load
```

## Basic usage

You can load your tmuxp file and attach the tmux session via a few
shorthands:

1. The directory with a `.tmuxp.{yaml,yml,json}` file in it
2. The name of the project file in your `$HOME/.tmuxp` folder
3. The direct path of the tmuxp file you want to load

Path to folder with `.tmuxp.yaml`, `.tmuxp.yml`, `.tmuxp.json`:

````{tab} Project based

Projects with a file named `.tmuxp.yaml` or `.tmuxp.json` can be loaded:

```console
// current directory
$ tmuxp load .
```

```console
$ tmuxp load ../
```

```console
$ tmuxp load path/to/folder/
```

```console
$ tmuxp load /path/to/folder/
```

````

````{tab} User based

Name of the config, assume `$HOME/.tmuxp/myconfig.yaml`:

```console
$ tmuxp load myconfig
```

Direct path to json/yaml file:

```console
$ tmuxp load ./myfile.yaml
```

```console
$ tmuxp load /abs/path/to/myfile.yaml
```

```console
$ tmuxp load ~/myfile.yaml
```

````

````{tab} Direct

Absolute and relative directory paths are supported.

```console
$ tmuxp load [filename]
```

````

## Inside sessions

When you load a workspace from _inside_ an existing tmux client, tmuxp asks what
you want rather than guessing — switch to the freshly built session, append its
windows to the session you're in, or build it detached and stay put:

```
Already inside TMUX, switch to session? yes/no
Or (a)ppend windows in the current active session?
[y/n/a]:
```

:::{mermaid}
:caption: Loading from inside an existing tmux client.
:alt: tmuxp load prompt choices inside an existing tmux client
:name: tmuxp-load-inside-client
:responsive: fit

flowchart TD
    load["tmuxp load"]:::cmd --> q{"y / n / a ?"}
    q -->|yes| switch["build the new session, switch the client to it"]
    q -->|a| append["append its windows to the current session"]
    q -->|no| detached["build it detached, stay where you are"]
:::

## Options

All of these options can be preselected to skip the prompt:

- Attach / open the client after load:

  ```console
  $ tmuxp load -y config
  ```

- Detached / open in background:

  ```console
  $ tmuxp load -d config
  ```

- Append windows to existing session

  ```console
  $ tmuxp load -a config
  ```

## Loading multiple sessions

Multiple sessions can be loaded at once. The first ones will be created
without being attached. The last one will be attached if there is no
`-d` flag on the command line.

```console
$ tmuxp load [filename1] [filename2] ...
```

## Custom session name

A session name can be provided at the terminal. If multiple sessions
are created, the last session is named from the terminal.

```console
$ tmuxp load -s [new_session_name] [filename1] ...
```

## Logging

The output of the `load` command can be logged to a file for
debugging purposes. the log level can be controlled with the global
`--log-level` option (defaults to INFO).

```console
$ tmuxp load [filename] --log-file [log_filename]
```

```console
$ tmuxp --log-level [LEVEL] load [filename] --log-file [log_filename]
```

## Progress display

When loading a workspace, tmuxp shows an animated spinner with build progress. The spinner updates as windows and panes are created, giving real-time feedback during session builds.

### Presets

Five built-in presets control the spinner format:

| Preset | Format |
|--------|--------|
| `default` | `Loading workspace: {session} {bar} {progress} {window}` |
| `minimal` | `Loading workspace: {session} [{window_progress}]` |
| `window` | `Loading workspace: {session} {window_bar} {window_progress_rel}` |
| `pane` | `Loading workspace: {session} {pane_bar} {session_pane_progress}` |
| `verbose` | `Loading workspace: {session} [window {window_index} of {window_total} · pane {pane_index} of {pane_total}] {window}` |

Select a preset with `--progress-format`:

```console
$ tmuxp load --progress-format minimal myproject
```

Or via environment variable:

```console
$ TMUXP_PROGRESS_FORMAT=verbose tmuxp load myproject
```

### Custom format tokens

Use a custom format string with any of the available tokens:

| Token | Description |
|-------|-------------|
| `{session}` | Session name |
| `{window}` | Current window name |
| `{window_index}` | Current window number (1-based) |
| `{window_total}` | Total number of windows |
| `{window_progress}` | Window fraction (e.g. `1/3`) |
| `{window_progress_rel}` | Completed windows fraction (e.g. `1/3`) |
| `{windows_done}` | Number of completed windows |
| `{windows_remaining}` | Number of remaining windows |
| `{pane_index}` | Current pane number in the window |
| `{pane_total}` | Total panes in the current window |
| `{pane_progress}` | Pane fraction (e.g. `2/4`) |
| `{progress}` | Combined progress (e.g. `1/3 win · 2/4 pane`) |
| `{session_pane_progress}` | Panes completed across the session (e.g. `5/10`) |
| `{overall_percent}` | Pane-based completion percentage (0–100) |
| `{bar}` | Composite progress bar |
| `{pane_bar}` | Pane-based progress bar |
| `{window_bar}` | Window-based progress bar |
| `{status_icon}` | Status icon (⏸ during before_script) |

Example:

```console
$ tmuxp load --progress-format "{session} {bar} {overall_percent}%" myproject
```

### Panel lines

The spinner shows script output in a panel below the spinner line. Control the panel height with `--progress-lines`:

Hide the panel entirely (script output goes to stdout):

```console
$ tmuxp load --progress-lines 0 myproject
```

Show unlimited lines (capped to terminal height):

```console
$ tmuxp load --progress-lines -1 myproject
```

Set a custom height (default is 3):

```console
$ tmuxp load --progress-lines 5 myproject
```

### Disabling progress

Disable the animated spinner entirely:

```console
$ tmuxp load --no-progress myproject
```

Or via environment variable:

```console
$ TMUXP_PROGRESS=0 tmuxp load myproject
```

When progress is disabled, logging flows normally to the terminal and no spinner is rendered.

### Before-script behavior

During `before_script` execution, the progress bar shows a marching animation and a ⏸ status icon, indicating that tmuxp is waiting for the script to finish before continuing with pane creation.

---

### Cli/Ls

(cli-ls)=

(ls-config)=

# tmuxp ls

List available workspace configurations from your local project and global tmuxp directories.

## Command

```{eval-rst}
.. argparse::
    :module: tmuxp.cli
    :func: create_parser
    :prog: tmuxp
    :path: ls
```

---

### Cli/Recipes

(cli-recipes)=

# Recipes

Copy-pasteable command invocations for common tasks. Each command has a full
reference — with every flag — under {ref}`cli`.

## Load a workspace

```console
$ tmuxp load my-workspace.yaml
```

## Load in detached mode

```console
$ tmuxp load -d my-workspace.yaml
```

## Load from a project directory

```console
$ tmuxp load .
```

## Freeze a running session

```console
$ tmuxp freeze my-session
```

## Convert YAML to JSON

```console
$ tmuxp convert my-workspace.yaml
```

## Convert JSON to YAML

```console
$ tmuxp convert my-workspace.json
```

## List available workspaces

```console
$ tmuxp ls
```

## Search workspaces

```console
$ tmuxp search my-project
```

## Edit a workspace config

```console
$ tmuxp edit my-workspace
```

## Collect debug info

```console
$ tmuxp debug-info
```

## Shell with tmux context

```console
$ tmuxp shell
```

Access [libtmux](https://libtmux.git-pull.com/) objects directly:

```console
$ tmuxp shell --best --command 'print(server.sessions)'
```

---

### Cli/Search

(cli-search)=

(search-config)=

# tmuxp search

Search for workspace configurations by name or content across your tmuxp directories.

## Command

```{eval-rst}
.. argparse::
    :module: tmuxp.cli
    :func: create_parser
    :prog: tmuxp
    :path: search
```

---

### Cli/Shell

(cli-shell)=

(tmuxp-shell)=

# tmuxp shell

Launch an interactive Python shell with [libtmux] objects pre-loaded. Like
Django's `shell` command, it hands you the current tmux server, sessions,
windows, and panes already wired up, so you can poke at a live session or
prototype a script without writing boilerplate.

## Command

```{eval-rst}
.. argparse::
    :module: tmuxp.cli
    :func: create_parser
    :prog: tmuxp
    :path: shell
```

## Interactive usage

Run `tmuxp shell` to drop into a Python console with the current tmux
{class}`server <libtmux.Server>`, {class}`session <libtmux.Session>`,
{class}`window <libtmux.Window>`, and {class}`pane <libtmux.Pane>` already bound.
Pass arguments to select a specific one:

```console
(Pdb) server
<libtmux.server.Server object at 0x7f7dc8e69d10>
(Pdb) server.sessions
[Session($1 your_project)]
(Pdb) session
Session($1 your_project)
(Pdb) session.name
'your_project'
(Pdb) window
Window(@3 1:your_window, Session($1 your_project))
(Pdb) window.name
'your_window'
(Pdb) window.panes
[Pane(%6 Window(@3 1:your_window, Session($1 your_project)))
(Pdb) pane
Pane(%6 Window(@3 1:your_window, Session($1 your_project)))
```

## Running code directly

Pass `-c` to run a snippet and exit, much like `python -c`:

```console
$ tmuxp shell -c 'python code'
```

```{image} ../_static/tmuxp-shell.gif
:width: 878
:height: 109
:loading: lazy
```

The same objects are in scope. Name a server, then a window, to narrow what the
snippet sees:

```console
$ tmuxp shell -c 'print(session.name); print(window.name)'
my_server
my_window
```

```console
$ tmuxp shell my_server my_window -c 'print(window.name.upper())'
MY_WINDOW
```

Inside a tmux pane — or attached to the default server — the pane is in scope
too:

```console
$ tmuxp shell -c 'print(pane.id); print(pane.window.name)'
%2
my_window
```

## Debugger integration

`tmuxp shell` supports [PEP 553][pep 553]'s `PYTHONBREAKPOINT` and compatible
debuggers, such as [ipdb][ipdb]:

```console
$ pip install --user ipdb
```

Inside a [uv](https://docs.astral.sh/uv/getting-started/features/#python-versions)-managed
project, add `ipdb` as a development dependency:

```console
$ uv add --dev ipdb
```

For a pipx-style ad hoc install, run it through [uvx](https://docs.astral.sh/uv/guides/tools/):

```console
$ uvx --from ipdb ipdb3 --help
```

```console
$ env PYTHONBREAKPOINT=ipdb.set_trace tmuxp shell
```

## Shell detection

`tmuxp shell` drops into the richest shell available in your _site packages_. Pick
one yourself with a flag:

- `--pdb`: plain {func}`breakpoint` (python 3.7+) or {func}`pdb.set_trace`
- `--code`: drop into {func}`code.interact`, accepts `--use-pythonrc`
- `--bpython`: drop into [bpython]
- `--ipython`: drop into [IPython]
- `--ptpython`: drop into [ptpython], accepts `--use-vi-mode`
- `--ptipython`: drop into [IPython] + [ptpython], accepts `--use-vi-mode`

[pep 553]: https://www.python.org/dev/peps/pep-0553/
[ipdb]: https://pypi.org/project/ipdb/
[libtmux]: https://libtmux.git-pull.com
[bpython]: https://bpython-interpreter.org/
[IPython]: https://ipython.org/
[ptpython]: https://github.com/prompt-toolkit/ptpython

---

### Configuration/Environmental Variables

(environmental-variables)=

# Environmental variables

These environment variables tune how tmuxp finds your workspaces and how much it
shows you while loading — set from your shell, outside any workspace file. You
rarely need them: tmuxp works out of the box. Reach for one when you want to
point tmuxp at a different config directory, quiet or reshape the load progress
display, or work around a rare tmux quirk. The progress variables each mirror a
{ref}`tmuxp load <cli-load>` flag, noted alongside the variable.

(TMUXP_CONFIGDIR)=

## `TMUXP_CONFIGDIR`

Example: `TMUXP_CONFIGDIR=$HOME/.mytmuxpconfigdir tmuxp load cpython`

(LIBTMUX_TMUX_FORMAT_SEPARATOR)=

## `LIBTMUX_TMUX_FORMAT_SEPARATOR`

:::{seealso}

{ref}`LIBTMUX_TMUX_FORMAT_SEPARATOR <libtmux:libtmux_tmux_format_separator>`
in the libtmux API.

:::

In rare circumstances the `tmux -F` separator under the hood may cause issues
building sessions. For this case you can override it here.

```console
$ env LIBTMUX_TMUX_FORMAT_SEPARATOR='__SEP__' tmuxp load [session]
```

(TMUXP_PROGRESS)=

## `TMUXP_PROGRESS`

Master on/off switch for the animated progress spinner during `tmuxp load`.
Defaults to `1` (enabled). Set to `0` to disable:

```console
$ TMUXP_PROGRESS=0 tmuxp load myproject
```

Equivalent to the `--no-progress` CLI flag.

(TMUXP_PROGRESS_FORMAT)=

## `TMUXP_PROGRESS_FORMAT`

Set the spinner line format. Accepts a preset name (`default`, `minimal`, `window`, `pane`, `verbose`) or a custom format string with tokens like `{session}`, `{bar}`, `{progress}`:

```console
$ TMUXP_PROGRESS_FORMAT=minimal tmuxp load myproject
```

Custom format example:

```console
$ TMUXP_PROGRESS_FORMAT="{session} {bar} {overall_percent}%" tmuxp load myproject
```

Equivalent to the `--progress-format` CLI flag.

(TMUXP_PROGRESS_LINES)=

## `TMUXP_PROGRESS_LINES`

Number of script-output lines shown in the spinner panel. Defaults to `3`.

Set to `0` to hide the panel entirely (script output goes to stdout):

```console
$ TMUXP_PROGRESS_LINES=0 tmuxp load myproject
```

Set to `-1` for unlimited lines (capped to terminal height):

```console
$ TMUXP_PROGRESS_LINES=-1 tmuxp load myproject
```

Equivalent to the `--progress-lines` CLI flag.

---

### Configuration/Examples

(examples)=

# Examples

This is a gallery of working workspace files — each a small, complete YAML (with
its JSON twin) you can copy, load, and adapt. They run from the simplest
two-pane split up to a full development environment, and many preview the panes
they build. Skim for the feature you need — inline shorthand, blank panes,
environment variables, pane shells, focus, history, timed pauses — and lift the
snippet.

## Short hand / inline style

tmuxp has a short-hand syntax for those who like to keep a workspace concise.

:::{tmux-layout}
:size: 36x12
:layout: even-vertical

echo 'did you know'
echo 'you can inline'
---
echo 'single commands'
---
echo 'for panes'
:::

````{tab} YAML

```{literalinclude} ../../examples/shorthands.yaml
:language: yaml

```


````

````{tab} JSON

```{literalinclude} ../../examples/shorthands.json
:language: json

```

````

## Blank panes

No need to repeat `pwd` or a dummy command. A `null`, `'blank'`,
`'pane'` are valid.

Note `''` counts as an empty carriage return.

````{tab} YAML

```{literalinclude} ../../examples/blank-panes.yaml
:language: yaml

```

````

````{tab} JSON

```{literalinclude} ../../examples/blank-panes.json
:language: json

```

````

## 2 panes

:::{tmux-layout}
:size: 30x10
:layout: even-vertical

echo hello
---
echo hello
:::

````{tab} YAML

```{literalinclude} ../../examples/2-pane-vertical.yaml
:language: yaml

```

````

````{tab} JSON

```{literalinclude} ../../examples/2-pane-vertical.json
:language: json

```

````

## 3 panes

:::{tmux-layout}
:size: 44x12
:layout: main-horizontal

cd /var/log
ls -al | grep \.log
---
echo hello
---
echo hello
:::

````{tab} YAML

```{literalinclude} ../../examples/3-pane.yaml
:language: yaml

```

````

````{tab} JSON

```{literalinclude} ../../examples/3-pane.json
:language: json

```

````

## 4 panes

:::{tmux-layout}
:size: 44x12
:layout: tiled

cd /var/log
ls -al | grep \.log
---
echo hello
---
echo hello
---
echo hello
:::

````{tab} YAML

```{literalinclude} ../../examples/4-pane.yaml
:language: yaml

```

````

````{tab} JSON

```{literalinclude} ../../examples/4-pane.json
:language: json

```

````

## Start Directory

Equivalent to `tmux new-window -c <start-directory>`.

````{tab} YAML

```{literalinclude} ../../examples/start-directory.yaml
:language: yaml

```

````

````{tab} JSON

```{literalinclude} ../../examples/start-directory.json
:language: json

```

````

## Environment variable replacing

tmuxp will replace environment variables wrapped in curly brackets
for values of these settings:

- `start_directory`
- `before_script`
- `session_name`
- `window_name`
- `shell_command_before`
- `global_options`
- `options` in session scope and window scope

tmuxp replaces these variables before-hand with variables in the
terminal `tmuxp` invokes in.

In this case of this example, assuming the username "user":

```console
$ MY_ENV_VAR=foo tmuxp load examples/env-variables.yaml
```

and your session name will be `session - user (foo)`.

Shell variables in `shell_command` do not support this type of
concatenation. `shell_command` and `shell_command_before` both
support normal shell variables, since they are sent into panes
automatically via `send-key` in `tmux(1)`. See `ls $PWD` in
example.

If you have a special case and would like to see behavior changed,
please make a ticket on the [issue tracker][issue tracker].

[issue tracker]: https://github.com/tmux-python/tmuxp/issues

````{tab} YAML

```{literalinclude} ../../examples/env-variables.yaml
:language: yaml

```

````

````{tab} JSON

```{literalinclude} ../../examples/env-variables.json
:language: json

```

````

## Environment variables

tmuxp will set session, window and pane environment variables.

```{note}

Setting environment variables for windows and panes requires tmuxp 1.19 or newer.
```

````{tab} YAML

```{literalinclude} ../../examples/session-environment.yaml
:language: yaml

```
````

````{tab} JSON

```{literalinclude} ../../examples/session-environment.json
:language: json

```

````

## Focusing

tmuxp allows `focus: true` for assuring windows and panes are attached /
selected upon loading.

````{tab} YAML

```{literalinclude} ../../examples/focus-window-and-panes.yaml
:language: yaml

```

````

````{tab} JSON

```{literalinclude} ../../examples/focus-window-and-panes.json
:language: json

```

````

## Terminal History

tmuxp allows `suppress_history: false` to override the default command /
suppression when building the workspace.
This will add the `shell_command` to the shell history in the pane.
The suppression of the `shell_command` commands from the shell's history
occurs by prefixing the commands with a space when `suppress_history: true`.
Accordingly, this functionality depends on the shell being appropriately
configured: bash requires the shell variable `HISTCONTROL` to be set and
include either of the values `ignorespace` or `ignoreboth` (to also ignore
sequential duplicate commands), and zsh requires `setopt HIST_IGNORE_SPACE`.

````{tab} YAML

```{literalinclude} ../../examples/suppress-history.yaml
:language: yaml

```

````

````{tab} JSON

```{literalinclude} ../../examples/suppress-history.json
:language: json

```

````

(enter)=

## Skip command execution

See more at {ref}`enter`.

:::{note}

_Experimental setting_: behavior and api is subject to change until stable.

:::

```{versionadded} 1.10.0
`enter: false` option. Pane-level support.
```

Omit sending {kbd}`enter` to key commands. Equivalent to
{meth}`send_keys(enter=False) <libtmux.Pane.send_keys>`.

````{tab} YAML

```{literalinclude} ../../examples/skip-send.yaml
:language: yaml

```

````

````{tab} JSON

```{literalinclude} ../../examples/skip-send.json
:language: json

```

````

````{tab} YAML (pane-level)

```{literalinclude} ../../examples/skip-send-pane-level.yaml
:language: yaml

```

````

````{tab} JSON (pane-level)

```{literalinclude} ../../examples/skip-send-pane-level.json
:language: json

```

````

(sleep)=

## Pausing commands

:::{note}

_Experimental setting_: behavior and api is subject to change until stable.

:::

```{versionadded} 1.10.0
`sleep_before` and `sleep_after` options added. Pane and command-level support.
```

```{warning}
**Blocking.** This will delay loading as it runs synchronously for each pane as
{mod}`asyncio` is not implemented yet.
```

Omit sending {kbd}`enter` to key commands. Equivalent to having
a {func}`time.sleep` before and after {meth}`~libtmux.Pane.send_keys`.

This is especially useful for expensive commands where the terminal needs some
breathing room (virtualenv, [poetry], [pipenv], [uv], sourcing a configuration,
launching a tui app, etc).

````{tab} Virtualenv

```{literalinclude} ../../examples/sleep-virtualenv.yaml
:language: yaml

```
````

````{tab} YAML

```{literalinclude} ../../examples/sleep.yaml
:language: yaml

```

````

````{tab} JSON

```{literalinclude} ../../examples/sleep.json
:language: json

```

````

````{tab} YAML (pane-level)

```{literalinclude} ../../examples/sleep-pane-level.yaml
:language: yaml

```

````

````{tab} JSON (pane-level)

```{literalinclude} ../../examples/sleep-pane-level.json
:language: json

```

````

## Window Index

You can specify a window's index using the `window_index` property. Windows
without `window_index` will use the lowest available window index.

````{tab} YAML

```{literalinclude} ../../examples/window-index.yaml
:language: yaml

```

````

````{tab} JSON

```{literalinclude} ../../examples/window-index.json
:language: json

```

````

## Shell per pane

Every pane can have its own shell or application started. This allows for usage
of the `remain-on-exit` setting to be used properly, but also to have
different shells on different panes.

````{tab} YAML

```{literalinclude} ../../examples/pane-shell.yaml
:language: yaml

```

````

````{tab} JSON

```{literalinclude} ../../examples/pane-shell.json
:language: json

```

````

## Set tmux options

Works with global (server-wide) options, session options
and window options.

Including `automatic-rename`, `default-shell`,
`default-command`, etc.

````{tab} YAML

```{literalinclude} ../../examples/options.yaml
:language: yaml

```
````

````{tab} JSON
```{literalinclude} ../../examples/options.json
:language: json

```
````

## Set window options after pane creation

Apply window options after panes have been created. Useful for
`synchronize-panes` option after executing individual commands in each
pane during creation.

````{tab} YAML
```{literalinclude} ../../examples/2-pane-synchronized.yaml
:language: yaml

```
````

````{tab} JSON
```{literalinclude} ../../examples/2-pane-synchronized.json
:language: json

```
````

## Main pane height

### Percentage

:::{versionadded} 1.46.0

Before this, tmuxp layouts would not detect the terminal's size.

:::

````{tab} YAML
```{literalinclude} ../../examples/main-pane-height-percentage.yaml
:language: yaml

```
````

````{tab} JSON
```{literalinclude} ../../examples/main-pane-height-percentage.json
:language: json

```
````

### Rows

````{tab} YAML
```{literalinclude} ../../examples/main-pane-height.yaml
:language: yaml

```
````

````{tab} JSON
```{literalinclude} ../../examples/main-pane-height.json
:language: json

```
````

## Super-advanced dev environment

:::{seealso}

{ref}`tmuxp-developer-config` in the {ref}`developing` section.

:::

````{tab} YAML
```{literalinclude} ../../.tmuxp.yaml
:language: yaml

```
````

````{tab} JSON
```{literalinclude} ../../.tmuxp.json
:language: json

```
````

## Multi-line commands

You can use YAML's multiline syntax to easily split multiple
commands into the same shell command: https://stackoverflow.com/a/21699210

````{tab} YAML
```yaml
session_name: my project
shell_command_before:
- >
  [ -d `.venv/bin/activate` ] &&
  source .venv/bin/activate &&
  reset
- sleep 1
windows:
- window_name: first window
  layout: main-horizontal
  focus: true
  panes:
  - focus: True
  - blank
  - >
    uv run ./manage.py migrate &&
    npm -C js run start
  - uv run ./manage.py runserver
  options:
    main-pane-height: 35
```
````

## Bootstrap project before launch

You can use `before_script` to run a script before the tmux session
starts building. This can be used to start a script to create a virtualenv
or download a virtualenv/rbenv/package.json's dependency files before
tmuxp even begins building the session.

It works by using the [Exit Status][exit status] code returned by a script. Your
script can be any type, including bash, python, ruby, etc.

A successful script will exit with a status of `0`.

Important: the script file must be chmod executable `+x` or `755`.

Run a python script (and check for its return code), the script is
_relative to the `.tmuxp.yaml`'s root_ (Windows and panes omitted in
this example):

````{tab} YAML
```yaml
session_name: my session
before_script: ./bootstrap.py
# ... the rest of your workspace

```
````

````{tab} JSON
```json
{
    "session_name": "my session",
    "before_script": "./bootstrap.py"
}

```
````

Run a shell script + check for return code on an absolute path. (Windows
and panes omitted in this example)

````{tab} YAML

```yaml
session_name: another example
before_script: /absolute/path/this.sh # abs path to shell script
# ... the rest of your workspace

```
````

````{tab} JSON

```json
{
    "session_name": "my session",
    "before_script": "/absolute/path/this.sh"
}

```
````

[exit status]: http://tldp.org/LDP/abs/html/exit-status.html

## Per-project tmuxp workspaces

You can load your software project in tmux by placing a `.tmuxp.yaml` or
`.tmuxp.json` in the project's workspace and loading it.

Use {ref}`tmuxp load <cli-load>` with an absolute filename, or `$ tmuxp load .`
when the workspace is in the current directory.

```console

$ tmuxp load ~/workspaces/myproject.yaml

```

See examples of `tmuxp` in the wild. Have a project workspace to show off?
Edit this page.

- <https://github.com/tony/dockerfiles/blob/master/.tmuxp.yaml>
- <https://github.com/vcs-python/vcspull/blob/master/.tmuxp.yaml>
- <https://github.com/tony/sphinxcontrib-github/blob/master/.tmuxp.yaml>

You can use `start_directory: ./` to make the directories relative to
the workspace file / project root.

## Bonus: pipenv auto-bootstrapping

:::{versionadded} 1.3.4

`before_script` CWD's into the root (session)-level
`start_directory`.

:::

If you use [pipenv] / [poetry] / [uv], you can use a script like this to ensure
your packages are installed:

````{tab} YAML

```yaml
# assuming your .tmuxp.yaml is in your project root directory
session_name: my pipenv project
start_directory: ./
before_script: pipenv install --dev --skip-lock # ensure dev deps install
windows:
- window_name: django project
  focus: true
  panes:
  - blank
  - pipenv run ./manage.py runserver

```
````

You can also source yourself into the virtual environment using
`shell_command_before`:

````{tab} YAML

```yaml
# assuming your .tmuxp.yaml is in your project root directory
session_name: my pipenv project
start_directory: ./
before_script: pipenv install --dev --skip-lock # ensure dev deps install
shell_command_before:
- '[ -d `pipenv --venv` ] && source `pipenv --venv`/bin/activate && reset'
windows:
- window_name: django project
  focus: true
  panes:
  - blank
  - ./manage.py runserver

```
````

[pipenv]: https://docs.pipenv.org/
[poetry]: https://python-poetry.org/
[uv]: https://github.com/astral-sh/uv

## Kung fu

:::{note}

tmuxp sessions can be scripted in python. The first way is to use the
ORM in the {ref}`API`. The second is to pass a {class}`dict` into
{class}`~tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder` with a correct schema.
See: {func}`~tmuxp.workspace.validation.validate_schema`.

:::

Add yours? Submit a pull request to the [github][github] site!

[github]: https://github.com/tmux-python/tmuxp

---

### Configuration/Index

(config)=

(configuration)=

(workspace)=

# Workspace files

::::{grid} 1 2 3 3
:gutter: 2 2 3 3

:::{grid-item-card} Top-level Options
:link: top-level
:link-type: doc
Session and window configuration keys.
:::

:::{grid-item-card} Environment Variables
:link: environmental-variables
:link-type: doc
TMUXP_CONFIGDIR and other env vars.
:::

:::{grid-item-card} Examples
:link: examples
:link-type: doc
Sample workspace configurations.
:::

:::{grid-item-card} Workspace builders
:link: workspace-builders
:link-type: doc
Select a builder and tune pane readiness.
:::

::::

tmuxp loads your terminal workspace into tmux using workspace files.

The workspace file can be JSON or YAML. Its declarative style resembles tmux's object hierarchy: session, window, and panes.

## Launching your session

Once you have `tmuxp` installed alongside tmux, you can load a workspace with:

```console
$ tmuxp load ./path/to/file
```

tmuxp will offer to assist when:

- _Session already exists_: tmuxp will prompt you to re-attach. It does this
  by checking if the workspace's `session_name` matches a session already
  running on the same server.
- _When inside a tmux client_, `tmuxp` will let you create a new session and switch to it, or append the windows to your existing
  session.

## What's in a workspace file?

1. A session name
2. A list of _windows_
3. A list of _panes_ for each window
4. A list of _commands_ for each pane

:::{mermaid}
:caption: A workspace file mirrors tmux's own hierarchy.
:alt: tmuxp workspace file hierarchy from session to windows, panes, and shell commands
:name: tmuxp-workspace-hierarchy
:responsive: fit

flowchart TD
    session["session_name"]:::cmd --> w1["window"]
    session --> w2["window"]
    w1 --> p1["pane"]
    w1 --> p2["pane"]
    p1 --> c1["shell_command"]:::cmd
    p1 --> c2["shell_command"]:::cmd
:::

````{tab} Basics

```yaml
session_name: My session
windows:
- window_name: Window 1
  panes:
  - shell_command:
    - cmd: echo "pane 1"
  - shell_command:
    - cmd: echo "pane 2"
```

````

````{tab} Smallest possible

```{literalinclude} ../../examples/minimal.yaml
:language: yaml

```

As of 1.11.x.

````

Breaking down the basic workspace into sections:

1. A session name

   ```yaml
   session_name: My session
   ```

2. A list of _windows_

   ```yaml
   windows:
     - window_name: Window 1
       panes: ...
       # window settings
     - window_name: Window 2
       panes: ...
       # window settings
   ```

3. A list of _panes_ for each window

   ```yaml
   windows:
     panes:
       -  # pane settings
       -  # pane settings
   ```

4. A list of _commands_ for each pane

   ```yaml
   windows:
     panes:
       - shell_command:
           - cmd: echo "pane 1 - cmd 1"
             # command options
           - cmd: echo "pane 1 - cmd 2"
             # command options
   ```

## Where do I store workspace files?

### Direct

You can create a workspace and load it from anywhere in your file system.

```console
$ tmuxp load [workspace-file]
```

````{tab} Relative
```console
$ tmuxp load ./favorites.yaml
```
````

````{tab} Absolute
```console
$ tmuxp load /opt/myapp/favorites.yaml
```
````

### User-based workspaces

tmuxp uses the [XDG Base Directory] specification.

Often on POSIX machines, you will store them in `~/.config/tmuxp`.

Assume you store `apple.yaml` in `$XDG_CONFIG_HOME/tmuxp/apple.yaml`, you can
then use:

```console
$ tmuxp load apple
```

:::{seealso}

This path can be overridden by {ref}`TMUXP_CONFIGDIR`

:::

[xdg base directory]: https://specifications.freedesktop.org/basedir-spec/latest/

### Project-specific

You can store a workspace in your project's root directory as `.tmuxp.yaml` or `.tmuxp.json`, then:

Assume `.tmuxp.yaml` inside `/opt/myapp`

```console
$ tmuxp load [workspace-file]
```

````{tab} In project root
```console
$ tmuxp load ./
```
````

````{tab} Absolute
```console
$ tmuxp load /opt/myapp
```
````

## Reference and usage

::::{grid} 1 2 3 3
:gutter: 2 2 3 3

:::{grid-item-card} Top-level Options
:link: top-level
:link-type: doc
Session and window configuration keys.
:::

:::{grid-item-card} Environment Variables
:link: environmental-variables
:link-type: doc
TMUXP_CONFIGDIR and other env vars.
:::

:::{grid-item-card} Examples
:link: examples
:link-type: doc
Sample workspace configurations.
:::

:::{grid-item-card} Workspace builders
:link: workspace-builders
:link-type: doc
Select a builder and tune pane readiness.
:::

::::

```{toctree}
:hidden:

top-level
environmental-variables
examples
workspace-builders
```

---

### Configuration/Top Level

(top-level)=
(top-level-config)=

# Top-level configuration

Top-level keys describe the session as a whole — its name, where it starts,
the tmux options it sets — and sit above the `windows` and `panes` that fill
it. Only `session_name` is required; leave the rest out and a workspace with
just a name and a list of windows loads fine. This page covers `session_name`
and the keys for choosing a workspace builder. For the full set of session,
window, and pane keys, work through {ref}`examples`.

## `session_name`

The name tmux gives the session — and the name tmuxp checks against when it
decides whether that session is already running. It need not match the
workspace filename.

For example, _apple.yaml_:

```yaml
session_name: banana
windows:
  - panes:
      -
```

Load it detached:

```console
$ tmuxp load ./apple.yaml -d
```

tmuxp reads _apple.yaml_ from the current directory and builds a tmux session
called _banana_ in the background — `-d` is detached. Attach to it with tmux
directly:

```console
$ tmux attach -t banana
```

## Workspace builder keys

A workspace file can also choose a custom builder and tune its behavior with
`workspace_builder`, `workspace_builder_paths`, and `workspace_builder_options`.
Most workspaces never set these — leave them out and you get tmuxp's built-in
classic builder.

```{seealso}
{ref}`workspace-builders`
```

---

### Configuration/Workspace Builders

(workspace-builders)=

# Workspace builders

```{versionadded} 1.72.0
```

A *workspace builder* is the part of tmuxp that turns a workspace configuration into a
live tmux session — it creates the session, lays out its windows and panes, and runs
their commands. You usually never have to think about it: tmuxp ships with the
built-in
{class}`classic builder <tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder>`,
and your YAML or JSON workspace files load through it out of the box, just as
they always have. **Everything on this page is optional; leave a setting out to
fall back to the default.**

:::{mermaid}
:caption: How a workspace file becomes a live tmux session.
:alt: tmuxp load reading workspace config through a workspace builder to attach a session
:name: tmuxp-workspace-builder-flow
:responsive: fit

---
config:
  flowchart:
    wrappingWidth: 500
---
flowchart TD
    load["tmuxp load &lt;workspace-file&gt;"]:::cmd -->|reads workspace config| builder["Workspace Builder"]
    builder -->|"creates session, windows, panes"| attach["Attach tmux session"]
:::

Workspaces with special needs can reach for a builder's options to fine-tune how a
session loads. The classic builder, for instance, can wait for a pane's shell prompt
before sending its layout and commands — by default only when that shell is zsh (the
`pane_readiness` option). Waiting makes a session a little slower to load, but
guarantees the workspace is fully prepped before you attach.

You can also send a workspace through a different or custom builder instead of
the classic one, and tune its options the same way. For the braver cases, you
can subclass the
{class}`classic builder <tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder>`
or write your own in Python on top of
[libtmux](https://libtmux.git-pull.com/) — see
{ref}`custom-workspace-builders` for writing, packaging, testing, and the trust
boundary that comes with running builder code.

| Key | Type | Default | Purpose |
| --- | --- | --- | --- |
| `workspace_builder` | string | `classic` | Which builder turns the workspace into a session. |
| `workspace_builder_paths` | string or list of strings | _(none)_ | Trusted directories to import a builder from. |
| `workspace_builder_options` | mapping | _(all defaults)_ | Builder-behavior settings, such as `pane_readiness`. |

(workspace-builder-key)=

## `workspace_builder`

This is where you name the builder. Leave it out — or set `classic` — and you get
tmuxp's built-in builder, with nothing imported. When you name something else, tmuxp
works out what you mean from the shape of the value, in this order:

1. absent or empty → the built-in classic builder (nothing is imported);
2. contains `:` → a `module:attr` object reference;
3. no `.` and no `:` → a builder registered under the `tmuxp.workspace_builders`
   entry-point group, selected by name;
4. dotted with no `:` → an entry-point name if one is registered, otherwise a
   `module.attr` import path.

```yaml
session_name: my-session
workspace_builder: classic
windows:
  - panes:
      - vim
```

See {ref}`custom-workspace-builders` for selecting and packaging builders, and
{func}`~tmuxp.workspace.builder.registry.resolve_builder_class` for the resolver.

(workspace-builder-paths-key)=

## `workspace_builder_paths`

When your builder lives outside tmuxp's environment — say, a script sitting in your
config directory — this tells tmuxp where to find it. Give it a single directory or a
list of them. tmuxp expands `~` and environment variables, reads relative entries
against the workspace file's own directory, and expects each one to be a directory
that already exists. The paths join {data}`sys.path` only for the import and
build, not for the rest of your session.

```yaml
workspace_builder: my_local_builder:CustomBuilder
workspace_builder_paths:
  - ~/.config/tmuxp/builders
```

```{warning}
A workspace file that names a builder runs that builder's Python code. Only load
workspace files you trust. See the security note in {ref}`custom-workspace-builders`.
```

(workspace-builder-options-key)=

## `workspace_builder_options`

This holds builder-behavior settings, whichever builder you use. For now there's just
one, `pane_readiness`, which decides whether tmuxp waits for a pane's shell prompt
before it sends that pane's layout and commands — a guard against a zsh prompt-redraw
artifact:

```yaml
workspace_builder_options:
  pane_readiness: auto
```

| Value | Behavior |
| --- | --- |
| `auto` _(default)_ | Wait only when the session's shell is zsh. |
| `always` | Always wait for default-shell panes. |
| `never` | Never wait; fastest, but accepts the prompt/layout race for shells that need it. |

`pane_readiness` also accepts truthy/falsy aliases — `true`/`on`/`yes`/`1` map to
`always`, and `false`/`off`/`no`/`0` map to `never` (full list in
{ref}`custom-workspace-builders`). An unrecognized value fails the load with:

```text
invalid pane_readiness value: 'sometimes'; expected one of: auto, always/true/on/yes/1, never/false/off/no/0
```

A pane that runs a custom `shell` or `window_shell` never waits, whatever you set here.
See {class}`~tmuxp.workspace.options.PaneReadiness` and
{class}`~tmuxp.workspace.options.WorkspaceBuilderOptions` for the parsing rules.

## Minimal complete example

````{tab} YAML
```yaml
session_name: my-session
workspace_builder: classic
workspace_builder_paths:
  - ~/.config/tmuxp/builders
workspace_builder_options:
  pane_readiness: auto
windows:
  - window_name: editor
    panes:
      - vim
```
````

````{tab} JSON
```json
{
  "session_name": "my-session",
  "workspace_builder": "classic",
  "workspace_builder_paths": ["~/.config/tmuxp/builders"],
  "workspace_builder_options": {
    "pane_readiness": "auto"
  },
  "windows": [
    {
      "window_name": "editor",
      "panes": ["vim"]
    }
  ]
}
```
````

```{seealso}
{ref}`custom-workspace-builders` — narrative guide to selecting, packaging,
writing, and testing builders ·
{class}`~tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder` ·
{class}`~tmuxp.workspace.builder.protocol.WorkspaceBuilderProtocol`
```

---

### Internals/Api/Cli/Convert

# tmuxp convert - `tmuxp.cli.convert`

```{eval-rst}
.. automodule:: tmuxp.cli.convert
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Cli/Debug Info

# tmuxp debug-info - `tmuxp.cli.debug_info`

```{eval-rst}
.. automodule:: tmuxp.cli.debug_info
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Cli/Edit

# tmuxp edit - `tmuxp.cli.edit`

```{eval-rst}
.. automodule:: tmuxp.cli.edit
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Cli/Formatter

# tmuxp formatter - `tmuxp.cli._formatter`

```{eval-rst}
.. automodule:: tmuxp.cli._formatter
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Cli/Freeze

# tmuxp freeze - `tmuxp.cli.freeze`

```{eval-rst}
.. automodule:: tmuxp.cli.freeze
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Cli/Import Config

# tmuxp import - `tmuxp.cli.import_config`

```{eval-rst}
.. automodule:: tmuxp.cli.import_config
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Cli/Index

(api_cli)=

# CLI

:::{warning}
Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions!

If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues).
:::

```{toctree}
convert
debug_info
edit
freeze
formatter
import_config
load
ls
progress
search
shell
utils
```

## `tmuxp.cli`

```{eval-rst}
.. automodule:: tmuxp.cli
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Cli/Load

# tmuxp load - `tmuxp.cli.load`

```{eval-rst}
.. automodule:: tmuxp.cli.load
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Cli/Ls

# tmuxp ls - `tmuxp.cli.ls`

```{eval-rst}
.. automodule:: tmuxp.cli.ls
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Cli/Progress

# tmuxp progress - `tmuxp.cli._progress`

```{eval-rst}
.. automodule:: tmuxp.cli._progress
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Cli/Search

# tmuxp search - `tmuxp.cli.search`

```{eval-rst}
.. automodule:: tmuxp.cli.search
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Cli/Shell

# tmuxp shell - `tmuxp.cli.shell`

```{eval-rst}
.. automodule:: tmuxp.cli.shell
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Cli/Utils

# CLI utilities - `tmuxp.cli.utils`

```{eval-rst}
.. automodule:: tmuxp.cli.utils
   :members:
   :exclude-members: ColorMode
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/ Internal/Colors

# Colors - `tmuxp._internal.colors`

:::{warning}
Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions!

If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues).
:::

```{eval-rst}
.. automodule:: tmuxp._internal.colors
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/ Internal/Config Reader

# Config reader - `tmuxp._internal.config_reader`

:::{warning}
Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions!

If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues).
:::

```{eval-rst}
.. automodule:: tmuxp._internal.config_reader
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/ Internal/Index

(api-internal)=

# Internal Modules

:::{warning}
Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions!

If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues).
:::

```{toctree}
colors
config_reader
private_path
types
```

---

### Internals/Api/ Internal/Private Path

# Private path - `tmuxp._internal.private_path`

:::{warning}
Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions!

If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues).
:::

```{eval-rst}
.. automodule:: tmuxp._internal.private_path
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/ Internal/Types

# Typings - `tmuxp._internal.types`

```{eval-rst}
.. automodule:: tmuxp._internal.types
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Workspace/Builder/Classic

# Classic builder - `tmuxp.workspace.builder.classic`

```{eval-rst}
.. automodule:: tmuxp.workspace.builder.classic
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Workspace/Builder/Index

# Builder - `tmuxp.workspace.builder`

```{eval-rst}
.. py:module:: tmuxp.workspace.builder
```

`tmuxp.workspace.builder` is a package. The classic, default builder lives in
{mod}`tmuxp.workspace.builder.classic`; the public contract and the selection
machinery live alongside it.

{class}`WorkspaceBuilder <tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder>`
remains importable from {mod}`tmuxp.workspace.builder` as a backwards-compatible
alias of {class}`~tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder`.

::::{grid} 1 1 2 2
:gutter: 2 2 3 3

:::{grid-item-card} Classic builder
:link: classic
:link-type: doc
The built-in, default builder — `tmuxp.workspace.builder.classic`.
:::

:::{grid-item-card} Builder protocol
:link: protocol
:link-type: doc
The contract a builder must satisfy — `tmuxp.workspace.builder.protocol`.
:::

:::{grid-item-card} Builder registry
:link: registry
:link-type: doc
Builder selection and trusted import paths — `tmuxp.workspace.builder.registry`.
:::

::::

```{toctree}
:hidden:

classic
protocol
registry
```

---

### Internals/Api/Workspace/Builder/Protocol

# Builder protocol - `tmuxp.workspace.builder.protocol`

```{eval-rst}
.. automodule:: tmuxp.workspace.builder.protocol
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Workspace/Builder/Registry

# Builder registry - `tmuxp.workspace.builder.registry`

```{eval-rst}
.. automodule:: tmuxp.workspace.builder.registry
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Workspace/Constants

# Constants - `tmuxp.workspace.constants`

```{eval-rst}
.. automodule:: tmuxp.workspace.constants
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Workspace/Finders

# Finders - `tmuxp.workspace.finders`

```{eval-rst}
.. automodule:: tmuxp.workspace.finders
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Workspace/Freezer

# Freezer - `tmuxp.workspace.freezer`

```{eval-rst}
.. automodule:: tmuxp.workspace.freezer
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Workspace/Importers

# Importers - `tmuxp.workspace.importers`

```{eval-rst}
.. automodule:: tmuxp.workspace.importers
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Workspace/Index

(workspace)=

# Workspace

:::{warning}
Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions!

If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues).
:::

```{toctree}
builder/index
constants
finders
freezer
importers
loader
options
validation
```

---

### Internals/Api/Workspace/Loader

# Loader - `tmuxp.workspace.loader`

```{eval-rst}
.. automodule:: tmuxp.workspace.loader
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Workspace/Options

# Options - `tmuxp.workspace.options`

```{eval-rst}
.. automodule:: tmuxp.workspace.options
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Workspace/Validation

# Validation - `tmuxp.workspace.validation`

```{eval-rst}
.. automodule:: tmuxp.workspace.validation
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Exc

# Exceptions - `tmuxp.exc`

```{eval-rst}
.. automodule:: tmuxp.exc
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Index

(api)=

# API Reference

This is the internal Python API — the modules tmuxp's CLI is built from,
documented for contributors and plugin authors. Everyday use goes through the
{ref}`CLI <cli>`; to drive tmux from Python directly, reach for
[libtmux](https://libtmux.git-pull.com/) instead.

:::{seealso}
See {ref}`libtmux's API <libtmux:api>` and {ref}`Quickstart <libtmux:quickstart>` to see how you can control
tmux via python API calls.
:::

::::{grid} 1 2 3 3
:gutter: 2 2 3 3

:::{grid-item-card} Workspace
:link: workspace/index
:link-type: doc
Finding, loading, building, and freezing sessions.
:::

:::{grid-item-card} CLI modules
:link: cli/index
:link-type: doc
The {mod}`argparse` commands behind each subcommand.
:::

:::{grid-item-card} Plugin API
:link: plugin
:link-type: doc
The {class}`~tmuxp.plugin.TmuxpPlugin` base class and its hooks.
:::

:::{grid-item-card} Internal helpers
:link: _internal/index
:link-type: doc
Config reader, colors, and private path helpers.
:::

:::{grid-item-card} Exceptions
:link: exc
:link-type: doc
The {exc}`~tmuxp.exc.TmuxpException` hierarchy.
:::

:::{grid-item-card} Logging
:link: log
:link-type: doc
Loggers and user-facing echo helpers.
:::

:::{grid-item-card} Shell
:link: shell
:link-type: doc
Internals of the interactive shell launcher.
:::

:::{grid-item-card} Utilities
:link: util
:link-type: doc
Assorted helpers used across tmuxp.
:::

:::{grid-item-card} Types
:link: types
:link-type: doc
Shared type definitions.
:::

::::

```{toctree}
:hidden:

_internal/index
cli/index
workspace/index
exc
log
plugin
shell
util
types
```

---

### Internals/Api/Log

# Logging - `tmuxp.log`

```{eval-rst}
.. automodule:: tmuxp.log
   :members:
   :show-inheritance:
   :undoc-members:
```

---

### Internals/Api/Plugin

# Plugin - `tmuxp.plugin`

```{eval-rst}
.. automodule:: tmuxp.plugin
   :members:
   :show-inheritance:
   :undoc-members:
```

---