# The Tao of tmux Source: https://tmuxp.git-pull.com/about_tmux/ (about-tmux)= # The Tao of tmux :::{figure} /\_static/tao-tmux-screenshot.png :scale: 60% :align: center :loading: lazy ISC-licensed terminal multiplexer. ::: tmux is geared for developers and admins who interact regularly with CLI (text-only interfaces) In the world of computers, there are 2 realms: 1. The text realm 2. The graphical realm tmux resides in the text realm. This is about fixed-width fonts and that old fashioned black terminal. tmux is to the console what a desktop is to gui apps. It's a world inside the text dimension. Inside tmux you can: - multitask inside the terminal, run multiple applications. - have multiple command lines (pane) in the same window - have multiple windows (window) in the workspace (session) - switch between multiple workspaces, like virtual desktops ## Thinking tmux ### Text-based window manager | **tmux** | **"Desktop"-Speak** | **Plain English** | | ----------- | ------------------------------- | ------------------------------------- | | Multiplexer | Multi-tasking | Multiple applications simultaneously. | | Session | Desktop | Applications are visible here | | Window | Virtual Desktop or applications | A desktop that stores its own screen | | Pane | Application | Performs operations | :::{mermaid} :caption: A session holds windows; each window holds one or more panes. :alt: tmux session containing windows and panes :name: tmux-session-window-pane-tree :responsive: fit flowchart TD session["session"] --> w1["window"] session --> w2["window"] session --> w3["window"] w1 --> p1["pane"] w1 --> p2["pane"] w1 --> p3["pane"] w1 --> p4["pane"] w3 --> p5["pane"] w3 --> p6["pane"] ::: - 1 {term}`Server`. - has 1 or more {term}`Session`. - has 1 or more {term}`Window`. - has 1 or more {term}`Pane`. :::{seealso} {ref}`glossary` has a dictionary of tmux words. ::: ### CLI Power Tool Multiple applications or terminals to run on the same screen by splitting up 1 terminal into multiple. One screen can be used to edit a file, and another may be used to `$ tail -F` a logfile. :::{tmux-layout} :size: 40x10 :layout: even-horizontal bash --- bash ::: :::{tmux-layout} :size: 40x12 :layout: tiled bash --- bash --- vim --- bash ::: tmux supports as many terminals as you want. :::{mermaid} :caption: Switch between the windows in a session. :alt: switching from one active tmux window to another :name: tmux-window-switching :responsive: fit flowchart TD w1["window 1 · sys (active)"] -->|"switch-window 2"| w2["window 2 · vim (active)"] ::: You can switch between the windows you create. ### Resume everything later You can leave tmux and all applications running (detach), log out, make a sandwich, and re-(attach), all applications are still running! :::{mermaid} :caption: Detach, leave everything running, and reattach later. :alt: detaching from and reattaching to a running tmux session :name: tmux-detach-reattach-cycle :responsive: fit flowchart TD running["session running"] -->|"detach · Prefix d"| detached["screen detached — apps keep running"] detached -->|"tmux attach"| running ::: ### Manage workflow - System administrators monitor logs and services. - Programmers like to have an editor open with a CLI nearby. Applications running on a remote server can be launched inside of a tmux session, detached, and reattached next time your ["train of thought"](http://en.wikipedia.org/wiki/Train_of_thought) and work. Multitasking. Preserving the thinking you have. ## Installing tmux tmux is packaged on most Linux and BSD systems. For the freshest results on how to get tmux installed on your system, "How to install tmux on \" will do, as directions change and are slightly different between distributions. tmuxp requires tmux **3.2 or newer**. The latest stable version is viewable on the [tmux homepage](https://github.com/tmux/tmux). **Mac OS X** users may install the latest stable version of tmux through [MacPorts](http://www.macports.org/), [fink](http://fink.thetis.ig42.org/) or [Homebrew](http://www.brew.sh) (aka brew). If **compiling from source**, the dependencies are [libevent](http://www.monkey.org/~provos/libevent/) and [ncurses](http://invisible-island.net/ncurses/). ## Using tmux ### Start a new session ```console $ tmux ``` That's all it takes to launch yourself into a tmux session. :::{admonition} Common pitfall :class: note Running `$ tmux list-sessions` or any other command for listing tmux entities (such as `$ tmux list-windows` or `$ tmux list-panes`). This can generate the error "failed to connect to server". This could be because: - tmux server has killed its last session, killing the server. - tmux server has encountered a crash. (tmux is highly stable, this will rarely happen) - tmux has not been launched yet at all. ::: (prefix-key)= ### The prefix key Tmux hot keys have to be pressed in a special way. **Read this carefully**, then try it yourself. First, you press the _prefix_ key. This is `C-b` by default. Release. Then pause. For less than a second. Then type what's next. `C-b o` means: Press `Ctrl` and `b` at the same time. Release, Then press `o`. **Remember, prefix + short cut!** `C` is `Ctrl` key. ### Session Name Sessions can be _named upon creation_. ```console $ tmux new-session [-s session-name] ``` Sessions can be _renamed after creation_. ```{eval-rst} =============== ========================================================= Command .. code-block:: bash $ tmux rename-session Short cut ``Prefix`` + ``$`` =============== ========================================================= ``` ### Window Name Windows can be _named upon creation_. ```console $ tmux new-window [-n window-name] ``` Windows can be _renamed after creation_. ```{eval-rst} =============== ========================================================== Command .. code-block:: bash $ tmux rename-window Short cut ``Prefix`` + ``,`` =============== ========================================================== ``` ### Creating new windows ```{eval-rst} =============== ========================================================= Command .. code-block:: bash $ tmux new-window [-n window-name] Short cut ``Prefix`` + ``c`` You may then rename window. =============== ========================================================= ``` ### Traverse windows By number ```console $ tmux select-window ``` Next ```console $ tmux next-window ``` Previous ```console $ tmux previous-window ``` Last-window ```console $ tmux last-window ``` | Short cut | Action | | --------- | ----------------------------------------------------------- | | `n` | Change to the next window. | | `p` | Change to the previous window. | | `w` | Choose the current window interactively. | | `0 to 9` | Select windows 0 to 9. | | `M-n` | Move to the next window with a bell or activity marker. | | `M-p` | Move to the previous window with a bell or activity marker. | ### Move windows Move window ```console $ tmux move-window [-t dst-window] ``` Swap the window ```console $ tmux swap-window [-t dst-window] ``` | Short cut | Action | | --------- | ----------------------------------------------- | | `.` | Prompt for an index to move the current window. | ### Move panes ```console $ tmux move-pane [-t dst-pane] ``` | Short cut | Action | | --------- | ------------------------------------------------ | | `C-o` | Rotate the panes in the current window forwards. | | `{` | Swap the current pane with the previous pane. | | `}` | Swap the current pane with the next pane. | ### Traverse panes Shortcut to move between panes. ```console $ tmux last-window ``` ```console $ tmux next-window ``` | Short cut | Action | | ------------- | --------------------------------------------------- | | `Up, Down` | Change to the pane above, below, to the left, or to | | `Left, Right` | the right of the current pane. | Recipe: tmux conf to `hjkl` commands, add this to your `~/.tmux.conf`: # hjkl pane traversal bind h select-pane -L bind j select-pane -D bind k select-pane -U bind l select-pane -R ### Kill window ```console $ tmux kill-window [-t target-window] ``` | Short cut | Action | | --------- | ------------------------ | | `&` | Kill the current window. | ### Kill pane ```console $ tmux kill-pane [-t target-pane] ``` | Short cut | Action | | --------- | ---------------------- | | `x` | Kill the current pane. | ### Splitting windows into panes ```console $ tmux split-window [-c start-directory] ``` Tmux windows can be split into multiple panes. | Short cut | Action | | --------- | ------------------------------------------------ | | `%` | Split the current pane into two, left and right. | | `"` | Split the current pane into two, top and bottom. | ## Configuring tmux Tmux can be configured via a `tmux(1)` configuration at `~/.tmux.conf`. Depending on your tmux version, there is different options available. ### Vi-style copy and paste ```console # Vi copypaste mode set-window-option -g mode-keys vi bind-key -t vi-copy 'v' begin-selection bind-key -t vi-copy 'y' copy-selection ``` ### Aggressive resizing for clients ```console setw -g aggressive-resize on ``` ### Reload config `` + `r`. ```console bind r source-file ~/.tmux.conf \; display-message "Config reloaded." ``` ### Status lines Tmux allows configuring a status line that displays system information, window list, and even pipe in the `stdout` of an application. You can use [tmux-mem-cpu-load][tmux-mem-cpu-load] to get stats (requires compilation) and [basic-cpu-and-memory.tmux][basic-cpu-and-memory.tmux]. You can pipe in a bash command to a tmux status line like: ```console $(shell-command) ``` So if `/usr/local/bin/tmux-mem-cpu-load` outputs stats to `stdout`, then `$(tmux-mem-cpu-load)` is going to output the first line to the status line. The interval is determined by the `status-interval`: set -g status-interval 1 [tmux-mem-cpu-load]: https://github.com/thewtex/tmux-mem-cpu-load [basic-cpu-and-memory.tmux]: https://github.com/zaiste/tmuxified/blob/master/scripts/basic-cpu-and-memory.tmux ### Examples - - works with tmux 1.5+. Supports screen's `ctrl-a` `Prefix key`. Support for system cpu, memory, uptime stats. - Add yours, edit this page on github. ## Reference ### Short cuts :::{tip} {ref}`prefix-key` is pressed before a short cut! ::: | Short cut | Action | | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | `C-b` | Send the prefix key (C-b) through to the application. | | `C-o` | Rotate the panes in the current window forwards. | | `C-z` | Suspend the tmux client. | | `!` | Break the current pane out of the window. | | `"` | Split the current pane into two, top and bottom. | | `#` | List all paste buffers. | | `$` | Rename the current session. | | `%` | Split the current pane into two, left and right. | | `&` | Kill the current window. | | `'` | Prompt for a window index to select. | | `,` | Rename the current window. | | `-` | Delete the most recently copied buffer of text. | | `.` | Prompt for an index to move the current window. | | `0 to 9` | Select windows 0 to 9. | | `:` | Enter the tmux command prompt. | | `;` | Move to the previously active pane. | | `=` | Choose which buffer to paste interactively from a list. | | `?` | List all key bindings. | | `D` | Choose a client to detach. | | `[` | Enter copy mode to copy text or view the history. | | `]` | Paste the most recently copied buffer of text. | | `c` | Create a new window. | | `d` | Detach the current client. | | `f` | Prompt to search for text in open windows. | | `i` | Display some information about the current window. | | `l` | Move to the previously selected window. | | `n` | Change to the next window. | | `o` | Select the next pane in the current window. | | `p` | Change to the previous window. | | `q` | Briefly display pane indexes. | | `r` | Force redraw of the attached client. | | `s` | Select a new session for the attached client interactively. | | `L` | Switch the attached client back to the last session. | | `t` | Show the time. | | `w` | Choose the current window interactively. | | `x` | Kill the current pane. | | `{` | Swap the current pane with the previous pane. | | `}` | Swap the current pane with the next pane. | | `~` | Show previous messages from tmux, if any. | | `Page Up` | Enter copy mode and scroll one page up. | | `Up, Down` | Change to the pane above, below, to the left, or to | | `Left, Right` | the right of the current pane. | | `M-1 to M-5` | Arrange panes in one of the five preset layouts: even-horizontal, even-vertical, main-horizontal, main-vertical, or tiled. | | `M-n` | Move to the next window with a bell or activity marker. | | `M-o` | Rotate the panes in the current window backwards. | | `M-p` | Move to the previous window with a bell or activity marker. | | `C-Up, C-Down` `C-Left, C-Right` | Resize the current pane in steps of one cell. | | `M-Up, M-Down` `M-Left, M-Right` | Resize the current pane in steps of five cells. | Source: tmux manpage[^id2]. To get the text documentation of a `.1` manual file: ```console $ nroff -mdoc tmux.1|less ``` For more information on how to export and differentiate tmux between versions, see https://github.com/tmux-python/tmux-manuals. [^id2]: [creative commons by-nc-nd 3.0 us]: http://creativecommons.org/licenses/by-nc-nd/3.0/us/ ### The Book :::::::{container} book-container ::::{container} leftside-book :::{figure} https://s3.amazonaws.com/titlepages.leanpub.com/the-tao-of-tmux/large :scale: 100% :width: 301 :height: 390 :align: left :target: https://leanpub.com/the-tao-of-tmux :alt: The Tao of tmux ::: :::: ::::{container} rightside-book _The Tao of tmux_ is available on [Leanpub][leanpub] and [Kindle][kindle] (Amazon). :::{figure} \_static/img/books/amazon-logo.png :scale: 19% :target: http://amzn.to/2gPfRhC :alt: Amazon Kindle ::: Read and browse the book for [free on the web][free on the web]. :::: [free on the web]: https://leanpub.com/the-tao-of-tmux/read [leanpub]: https://leanpub.com/the-tao-of-tmux [kindle]: http://amzn.to/2gPfRhC ::::::: ### License This page is licensed [Creative Commons BY-NC-ND 3.0 US][creative commons by-nc-nd 3.0 us]. --- # Completions Source: https://tmuxp.git-pull.com/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 --- # tmuxp convert Source: https://tmuxp.git-pull.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`. --- # tmuxp debug-info Source: https://tmuxp.git-pull.com/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 ... ``` --- # tmuxp edit Source: https://tmuxp.git-pull.com/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 ``` --- # Exit Codes Source: https://tmuxp.git-pull.com/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 } ``` --- # tmuxp freeze Source: https://tmuxp.git-pull.com/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. --- # tmuxp import Source: https://tmuxp.git-pull.com/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 Reference Source: https://tmuxp.git-pull.com/cli/ (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` ``` --- # tmuxp load Source: https://tmuxp.git-pull.com/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. --- # tmuxp ls Source: https://tmuxp.git-pull.com/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 ``` --- # Recipes Source: https://tmuxp.git-pull.com/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)' ``` --- # tmuxp search Source: https://tmuxp.git-pull.com/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 ``` --- # tmuxp shell Source: https://tmuxp.git-pull.com/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 `, {class}`session `, {class}`window `, and {class}`pane ` already bound. Pass arguments to select a specific one: ```console (Pdb) server (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 --- # Environmental variables Source: https://tmuxp.git-pull.com/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 ` 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 ` 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. --- # Examples Source: https://tmuxp.git-pull.com/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 `. ````{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) `. ````{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 ` 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. - - - 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 --- # Workspace files Source: https://tmuxp.git-pull.com/configuration/ (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 ``` --- # Top-level configuration Source: https://tmuxp.git-pull.com/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` ``` --- # Workspace builders Source: https://tmuxp.git-pull.com/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 `, 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 <workspace-file>"]:::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 ` 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` ``` --- # Glossary Source: https://tmuxp.git-pull.com/glossary/ (glossary)= # Glossary ```{glossary} tmuxp A tool to manage workspaces with tmux. A pythonic abstraction of tmux. tmux tmux(1) The tmux binary. Used internally to distinguish tmuxp is only a layer on top of tmux. ConfigReader The {class}`~tmuxp._internal.config_reader.ConfigReader` configuration management class, for parsing YAML / JSON / etc. files to and from python data (dictionaries, in the future, potentially dataclasses) Server tmux runs in the background of your system as a process. A server holds one or more {term}`Session`. tmux starts the server automatically the first time ``$ tmux`` runs, if it isn't already running. Advanced: you can run more than one by specifying ``[-L socket-name]`` or ``[-S socket-path]``. Client Attaches to a tmux {term}`server`. When you use tmux through CLI, you are using tmux as a client. Session Inside a tmux {term}`server`. The session has 1 or more {term}`Window`. The bottom bar in tmux show a list of windows. Normally they can be navigated with ``Ctrl-a [0-9]``, ``Ctrl-a n`` and ``Ctrl-a p``. Sessions can have a ``session_name``. Uniquely identified by ``session_id``. Window Entity of a {term}`session`. Can have 1 or more {term}`pane`. Panes can be organized with layouts. Windows can have names. Pane Linked to a {term}`Window`. a pseudoterminal. Target A target, cited in the manual as ``[-t target]`` can be a session, window or pane. ``` --- # Changelog Source: https://tmuxp.git-pull.com/history/ (changelog)= (history)= ```{include} ../CHANGES ``` --- # tmuxp Source: https://tmuxp.git-pull.com/ (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 GitHub ``` --- # Colors - tmuxp._internal.colors Source: https://tmuxp.git-pull.com/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: ``` --- # Config reader - tmuxp._internal.config_reader Source: https://tmuxp.git-pull.com/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: ``` --- # Internal Modules Source: https://tmuxp.git-pull.com/internals/api/_internal/ (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 ``` --- # Private path - tmuxp._internal.private_path Source: https://tmuxp.git-pull.com/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: ``` --- # Typings - tmuxp._internal.types Source: https://tmuxp.git-pull.com/internals/api/_internal/types/ # Typings - `tmuxp._internal.types` ```{eval-rst} .. automodule:: tmuxp._internal.types :members: :show-inheritance: :undoc-members: ``` --- # tmuxp convert - tmuxp.cli.convert Source: https://tmuxp.git-pull.com/internals/api/cli/convert/ # tmuxp convert - `tmuxp.cli.convert` ```{eval-rst} .. automodule:: tmuxp.cli.convert :members: :show-inheritance: :undoc-members: ``` --- # tmuxp debug-info - tmuxp.cli.debug_info Source: https://tmuxp.git-pull.com/internals/api/cli/debug_info/ # tmuxp debug-info - `tmuxp.cli.debug_info` ```{eval-rst} .. automodule:: tmuxp.cli.debug_info :members: :show-inheritance: :undoc-members: ``` --- # tmuxp edit - tmuxp.cli.edit Source: https://tmuxp.git-pull.com/internals/api/cli/edit/ # tmuxp edit - `tmuxp.cli.edit` ```{eval-rst} .. automodule:: tmuxp.cli.edit :members: :show-inheritance: :undoc-members: ``` --- # tmuxp formatter - tmuxp.cli._formatter Source: https://tmuxp.git-pull.com/internals/api/cli/formatter/ # tmuxp formatter - `tmuxp.cli._formatter` ```{eval-rst} .. automodule:: tmuxp.cli._formatter :members: :show-inheritance: :undoc-members: ``` --- # tmuxp freeze - tmuxp.cli.freeze Source: https://tmuxp.git-pull.com/internals/api/cli/freeze/ # tmuxp freeze - `tmuxp.cli.freeze` ```{eval-rst} .. automodule:: tmuxp.cli.freeze :members: :show-inheritance: :undoc-members: ``` --- # tmuxp import - tmuxp.cli.import_config Source: https://tmuxp.git-pull.com/internals/api/cli/import_config/ # tmuxp import - `tmuxp.cli.import_config` ```{eval-rst} .. automodule:: tmuxp.cli.import_config :members: :show-inheritance: :undoc-members: ``` --- # CLI Source: https://tmuxp.git-pull.com/internals/api/cli/ (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: ``` --- # tmuxp load - tmuxp.cli.load Source: https://tmuxp.git-pull.com/internals/api/cli/load/ # tmuxp load - `tmuxp.cli.load` ```{eval-rst} .. automodule:: tmuxp.cli.load :members: :show-inheritance: :undoc-members: ``` --- # tmuxp ls - tmuxp.cli.ls Source: https://tmuxp.git-pull.com/internals/api/cli/ls/ # tmuxp ls - `tmuxp.cli.ls` ```{eval-rst} .. automodule:: tmuxp.cli.ls :members: :show-inheritance: :undoc-members: ``` --- # tmuxp progress - tmuxp.cli._progress Source: https://tmuxp.git-pull.com/internals/api/cli/progress/ # tmuxp progress - `tmuxp.cli._progress` ```{eval-rst} .. automodule:: tmuxp.cli._progress :members: :show-inheritance: :undoc-members: ``` --- # tmuxp search - tmuxp.cli.search Source: https://tmuxp.git-pull.com/internals/api/cli/search/ # tmuxp search - `tmuxp.cli.search` ```{eval-rst} .. automodule:: tmuxp.cli.search :members: :show-inheritance: :undoc-members: ``` --- # tmuxp shell - tmuxp.cli.shell Source: https://tmuxp.git-pull.com/internals/api/cli/shell/ # tmuxp shell - `tmuxp.cli.shell` ```{eval-rst} .. automodule:: tmuxp.cli.shell :members: :show-inheritance: :undoc-members: ``` --- # CLI utilities - tmuxp.cli.utils Source: https://tmuxp.git-pull.com/internals/api/cli/utils/ # CLI utilities - `tmuxp.cli.utils` ```{eval-rst} .. automodule:: tmuxp.cli.utils :members: :exclude-members: ColorMode :show-inheritance: :undoc-members: ``` --- # Exceptions - tmuxp.exc Source: https://tmuxp.git-pull.com/internals/api/exc/ # Exceptions - `tmuxp.exc` ```{eval-rst} .. automodule:: tmuxp.exc :members: :show-inheritance: :undoc-members: ``` --- # API Reference Source: https://tmuxp.git-pull.com/internals/api/ (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 `; to drive tmux from Python directly, reach for [libtmux](https://libtmux.git-pull.com/) instead. :::{seealso} See {ref}`libtmux's API ` and {ref}`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 ``` --- # Logging - tmuxp.log Source: https://tmuxp.git-pull.com/internals/api/log/ # Logging - `tmuxp.log` ```{eval-rst} .. automodule:: tmuxp.log :members: :show-inheritance: :undoc-members: ``` --- # Plugin - tmuxp.plugin Source: https://tmuxp.git-pull.com/internals/api/plugin/ # Plugin - `tmuxp.plugin` ```{eval-rst} .. automodule:: tmuxp.plugin :members: :show-inheritance: :undoc-members: ``` --- # Shell - tmuxp.shell Source: https://tmuxp.git-pull.com/internals/api/shell/ # Shell - `tmuxp.shell` ```{eval-rst} .. automodule:: tmuxp.shell :members: :show-inheritance: :undoc-members: ``` --- # Typings - tmuxp.types Source: https://tmuxp.git-pull.com/internals/api/types/ # Typings - `tmuxp.types` ```{eval-rst} .. automodule:: tmuxp.types :members: :show-inheritance: :undoc-members: ``` --- # Utilities - tmuxp.util Source: https://tmuxp.git-pull.com/internals/api/util/ # Utilities - `tmuxp.util` ```{eval-rst} .. automodule:: tmuxp.util :members: :show-inheritance: :undoc-members: ``` --- # Classic builder - tmuxp.workspace.builder.classic Source: https://tmuxp.git-pull.com/internals/api/workspace/builder/classic/ # Classic builder - `tmuxp.workspace.builder.classic` ```{eval-rst} .. automodule:: tmuxp.workspace.builder.classic :members: :show-inheritance: :undoc-members: ``` --- # Builder - tmuxp.workspace.builder Source: https://tmuxp.git-pull.com/internals/api/workspace/builder/ # 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 ` 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 ``` --- # Builder protocol - tmuxp.workspace.builder.protocol Source: https://tmuxp.git-pull.com/internals/api/workspace/builder/protocol/ # Builder protocol - `tmuxp.workspace.builder.protocol` ```{eval-rst} .. automodule:: tmuxp.workspace.builder.protocol :members: :show-inheritance: :undoc-members: ``` --- # Builder registry - tmuxp.workspace.builder.registry Source: https://tmuxp.git-pull.com/internals/api/workspace/builder/registry/ # Builder registry - `tmuxp.workspace.builder.registry` ```{eval-rst} .. automodule:: tmuxp.workspace.builder.registry :members: :show-inheritance: :undoc-members: ``` --- # Constants - tmuxp.workspace.constants Source: https://tmuxp.git-pull.com/internals/api/workspace/constants/ # Constants - `tmuxp.workspace.constants` ```{eval-rst} .. automodule:: tmuxp.workspace.constants :members: :show-inheritance: :undoc-members: ``` --- # Finders - tmuxp.workspace.finders Source: https://tmuxp.git-pull.com/internals/api/workspace/finders/ # Finders - `tmuxp.workspace.finders` ```{eval-rst} .. automodule:: tmuxp.workspace.finders :members: :show-inheritance: :undoc-members: ``` --- # Freezer - tmuxp.workspace.freezer Source: https://tmuxp.git-pull.com/internals/api/workspace/freezer/ # Freezer - `tmuxp.workspace.freezer` ```{eval-rst} .. automodule:: tmuxp.workspace.freezer :members: :show-inheritance: :undoc-members: ``` --- # Importers - tmuxp.workspace.importers Source: https://tmuxp.git-pull.com/internals/api/workspace/importers/ # Importers - `tmuxp.workspace.importers` ```{eval-rst} .. automodule:: tmuxp.workspace.importers :members: :show-inheritance: :undoc-members: ``` --- # Workspace Source: https://tmuxp.git-pull.com/internals/api/workspace/ (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 ``` --- # Loader - tmuxp.workspace.loader Source: https://tmuxp.git-pull.com/internals/api/workspace/loader/ # Loader - `tmuxp.workspace.loader` ```{eval-rst} .. automodule:: tmuxp.workspace.loader :members: :show-inheritance: :undoc-members: ``` --- # Options - tmuxp.workspace.options Source: https://tmuxp.git-pull.com/internals/api/workspace/options/ # Options - `tmuxp.workspace.options` ```{eval-rst} .. automodule:: tmuxp.workspace.options :members: :show-inheritance: :undoc-members: ``` --- # Validation - tmuxp.workspace.validation Source: https://tmuxp.git-pull.com/internals/api/workspace/validation/ # Validation - `tmuxp.workspace.validation` ```{eval-rst} .. automodule:: tmuxp.workspace.validation :members: :show-inheritance: :undoc-members: ``` --- # Architecture Source: https://tmuxp.git-pull.com/internals/architecture/ # Architecture This page traces how a tmuxp command travels from the CLI down to [libtmux](https://libtmux.git-pull.com/), the library that does the actual tmux work. Each subcommand takes its own short path through one or two workspace modules: :::{mermaid} :caption: How each tmuxp subcommand reaches libtmux. :alt: tmuxp CLI subcommands reaching workspace modules and libtmux :name: tmuxp-command-architecture :responsive: fit flowchart TD cli["tmuxp CLI (argparse)"] cli --> load["load"]:::cmd load --> loader["workspace.loader"]:::cmd loader --> builder["workspace.builder"]:::cmd builder --> libtmux["libtmux"]:::cmd cli --> freeze["freeze"]:::cmd freeze --> freezer["workspace.freezer"]:::cmd freezer --> libtmux cli --> convert["convert"]:::cmd convert --> reader["_internal.config_reader"]:::cmd cli --> shell["shell"]:::cmd shell --> interactive["libtmux (interactive)"] cli --> search["ls / search"]:::cmd search --> finders["workspace.finders"]:::cmd ::: ## Key Components ### CLI Layer (`tmuxp.cli`) The CLI uses Python's {mod}`argparse` with a custom formatter ({mod}`tmuxp.cli._formatter`). Each subcommand lives in its own module under {mod}`tmuxp.cli`. The entry point is {func}`tmuxp.cli.cli`, registered as a console script in `pyproject.toml`. ### Workspace Layer (`tmuxp.workspace`) The workspace layer handles configuration lifecycle: 1. **Finding**: {mod}`tmuxp.workspace.finders` locates config files 2. **Loading**: {mod}`tmuxp.workspace.loader` reads and validates configs 3. **Building**: {mod}`tmuxp.workspace.builder` creates tmux sessions via libtmux 4. **Freezing**: {mod}`tmuxp.workspace.freezer` exports running sessions ### Library Layer (libtmux) tmuxp delegates all tmux operations to [libtmux](https://libtmux.git-pull.com/). The {class}`~tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder` creates libtmux {class}`~libtmux.Server`, {class}`~libtmux.Session`, {class}`~libtmux.Window`, and {class}`~libtmux.Pane` objects to construct the requested workspace. --- # Internals Source: https://tmuxp.git-pull.com/internals/ (internals)= # Internals ```{warning} Everything in this section is **internal implementation detail**. There is no stability guarantee. Interfaces may change or be removed without notice between any release. If you are building an application with tmuxp, use the [CLI](../cli/index.md) or refer to the {ref}`libtmux API `. ``` ::::{grid} 1 1 2 2 :gutter: 2 2 3 3 :::{grid-item-card} Architecture :link: architecture :link-type: doc How the CLI dispatches to the workspace builder and libtmux. ::: :::{grid-item-card} Python API :link: api/index :link-type: doc Internal module reference for contributors and plugin authors. ::: :::: ```{toctree} :hidden: architecture api/index ``` --- # Migration notes Source: https://tmuxp.git-pull.com/migration/ (migration)= ```{currentmodule} libtmux ``` ```{include} ../MIGRATION ``` --- # Code Style Source: https://tmuxp.git-pull.com/project/code-style/ # Code Style This page's content split across two files in the repository root, which are also what an AI coding agent reads: - Formatting, linting, type checking, and import conventions — the commands and the gates they are part of — moved to [CONTRIBUTING.md], under "The gates" and "Imports and typing". - The docstring convention — the prose rule, not the command that enforces it — moved to [WRITING.md], under "Docstrings". [CONTRIBUTING.md]: https://github.com/tmux-python/tmuxp/blob/master/.github/CONTRIBUTING.md [WRITING.md]: https://github.com/tmux-python/tmuxp/blob/master/.github/WRITING.md --- # Developing and Testing Source: https://tmuxp.git-pull.com/project/contributing/ (developing)= (install-dev-env)= (test-specific-tests)= (test-builder-visually)= (tmuxp-developer-config)= (gh-actions)= # Developing and Testing Environment setup, the test suite, and the CI gates moved to [CONTRIBUTING.md] in the repository root, which is also the file an AI coding agent reads. This page stays published so existing links and search results keep resolving. [CONTRIBUTING.md]: https://github.com/tmux-python/tmuxp/blob/master/.github/CONTRIBUTING.md --- # Project Source: https://tmuxp.git-pull.com/project/ (project)= # Project Information for contributors and maintainers. ::::{grid} 1 1 2 2 :gutter: 2 2 3 3 :::{grid-item-card} Contributing :link: contributing :link-type: doc Development setup, running tests, submitting PRs. ::: :::{grid-item-card} Code Style :link: code-style :link-type: doc [Ruff](https://ruff.rs), [mypy](https://mypy-lang.org/), [NumPy-style docstrings](https://numpydoc.readthedocs.io/en/latest/format.html), import conventions. ::: :::{grid-item-card} Releasing :link: releasing :link-type: doc Release checklist and version policy. ::: :::: ```{toctree} :hidden: contributing code-style releasing ``` --- # Releasing Source: https://tmuxp.git-pull.com/project/releasing/ # Releasing ## Release process You release tmuxp by tagging a version: pushing a `v` tag triggers a CI workflow that builds the package and publishes it to [PyPI] via [OIDC trusted publishing]. 1. Update `CHANGES` with the release notes. 2. Bump the version in `src/tmuxp/__about__.py`. 3. Commit the bump: ```console $ git commit -m "Tag v" ``` 4. Tag it: ```console $ git tag v ``` 5. Push the commit and the tag: ```console $ git push && git push --tags ``` 6. CI builds and publishes to [PyPI] automatically. ## Changelog format `CHANGES` is rendered as the changelog page. Each release is a Markdown section headed by its version and date: ```text ## tmuxp () ### Breaking changes - Description of the break, with a migration path (#issue) ### What's new - Description of the feature (#issue) ### Fixes - Description of the fix (#issue) ``` Subheadings appear in a fixed order when present: `### Breaking changes`, `### Dependencies`, `### What's new`, `### Fixes`, `### Documentation`, `### Development`. [PyPI]: https://pypi.org/project/tmuxp/ [OIDC trusted publishing]: https://docs.pypi.org/trusted-publishers/ --- # Quickstart Source: https://tmuxp.git-pull.com/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 ` 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 ` 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 ` | {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 --- # Custom workspace builders Source: https://tmuxp.git-pull.com/topics/custom-workspace-builders/ (custom-workspace-builders)= # Custom workspace builders A *workspace builder* turns an expanded workspace {class}`dict` into a live tmux session. tmuxp ships one builder, {class}`~tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder`, and uses it by default. You can point tmuxp at a different builder, and packagers can distribute builders that you select by name. This is an advanced, opt-in feature. Existing workspace files keep using the classic builder with no changes. ```{seealso} If you only want to set these keys in a workspace file, see {ref}`workspace-builders` in the configuration reference. ``` ## How a workspace is built 1. {ref}`tmuxp load ` reads the YAML/JSON file and expands it (shorthand, environment variables, trickle-down defaults). 2. tmuxp resolves which builder to use from the {ref}`workspace_builder ` config key (default: the classic builder). 3. tmuxp constructs the builder with the expanded workspace and a {class}`libtmux.Server`, then calls {meth}`~tmuxp.workspace.builder.protocol.WorkspaceBuilderProtocol.build`. 4. The builder creates the session, windows, and panes, honoring plugin hooks and progress callbacks. ## Selecting a builder ### By dotted path Point `workspace_builder` at an importable class. Both a `module:attr` object reference and a dotted `module.attr` path are accepted: ```yaml session_name: my-session workspace_builder: my_tmuxp_builders.builders:CustomBuilder windows: - window_name: editor panes: - vim ``` ### By entry-point name Packaged builders register under the `tmuxp.workspace_builders` entry-point group, letting users select them by a short name instead of an internal module path: ```yaml workspace_builder: classic ``` The built-in classic builder is registered this way. A distribution registers its own builder in `pyproject.toml`: ```toml [project.entry-points."tmuxp.workspace_builders"] mybuilder = "my_tmuxp_builders.builders:CustomBuilder" ``` ### Trusted import paths When a builder lives outside tmuxp's runtime environment (for example, a script in your config directory), list trusted directories in {ref}`workspace_builder_paths `. tmuxp expands `~` and environment variables, resolves relative entries against the workspace file's directory, requires each entry to be an existing directory, and temporarily prepends them to {data}`sys.path` for the import and build: ```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. tmuxp deliberately does **not** use {func}`site.addsitedir` for these paths — that would execute `.pth` startup files and is broader than making a module importable. ::: ## Writing a builder The simplest custom builder subclasses the classic builder and overrides what it needs: ```python from tmuxp.workspace.builder.classic import ClassicWorkspaceBuilder class CustomBuilder(ClassicWorkspaceBuilder): """A builder that renames the session after building.""" def build(self, session=None, append=False): super().build(session=session, append=append) self.session.rename_session(f"{self.session.name}-custom") ``` A builder written from scratch must satisfy {class}`~tmuxp.workspace.builder.protocol.WorkspaceBuilderProtocol`. The contract covers what `tmuxp load` drives: - **Constructor** accepting `session_config`, `server`, and the optional `plugins` list and `on_progress` / `on_before_script` / `on_script_output` / `on_build_event` callbacks. - **{meth}`build(session=None, append=False) `** — create or populate the session; the `append` path adds windows to an existing session. - **{attr}`session `** — the populated {class}`libtmux.Session`. - **{meth}`session_exists() `** and **{meth}`find_current_attached_session() `** — used by the CLI for attach/append decisions. - **`plugins`** — the list of plugin instances; honor the plugin lifecycle hooks ({meth}`~tmuxp.plugin.TmuxpPlugin.before_workspace_builder`, {meth}`~tmuxp.plugin.TmuxpPlugin.on_window_create`, {meth}`~tmuxp.plugin.TmuxpPlugin.after_window_finished`). - The **`on_*` callbacks** — call them at the documented milestones so the CLI's progress display and `before_script` output stay accurate. The contract is synchronous today. It is shaped so an async builder can be added later as an additive extension without changing this surface. ## Pane readiness tmuxp waits for a pane's shell prompt before dispatching layout and commands, which avoids a zsh prompt-redraw artifact. That wait is only needed for zsh, so it is configurable independently of which builder you use, through the {ref}`workspace_builder_options ` catalog: ```yaml workspace_builder_options: pane_readiness: auto # auto | always | never (+ truthy/falsy aliases) ``` - **`auto`** (default) — wait only when the session's interactive shell is zsh. - **`always`** (or `true`/`on`/`yes`/`1`) — always wait for default-shell panes. - **`never`** (or `false`/`off`/`no`/`0`) — never wait; fastest, but accepts the prompt/layout race for shells that need it. Panes with a custom `shell` or `window_shell` never wait, regardless of policy — those run a command in place of an interactive shell, so there is no prompt to wait for. See {class}`~tmuxp.workspace.options.PaneReadiness` and {class}`~tmuxp.workspace.options.WorkspaceBuilderOptions` for the parsing rules. ## Testing a builder Resolve, construct, and build against a real tmux server fixture: ```python from tmuxp.workspace import loader from tmuxp.workspace.builder import registry def test_custom_builder(server): config = loader.expand( { "session_name": "demo", "workspace_builder": "my_tmuxp_builders.builders:CustomBuilder", "windows": [{"window_name": "main", "panes": [{"shell_command": []}]}], }, ) builder_cls = registry.resolve_builder_class(config) builder = builder_cls(session_config=config, server=server) builder.build() assert builder.session.name == "demo" builder.session.kill() ``` For builders that live in a trusted directory, build the {data}`sys.path` sandbox with {func}`~tmuxp.workspace.builder.registry.resolve_builder_paths` and {func}`~tmuxp.workspace.builder.registry.prepended_sys_path`. ## Choosing an approach - **Classic builder** — the default. Use it for any workspace that depends on strict, pane-by-pane side effects (`start_directory`, `shell`, `window_shell`, pane environment). - **Readiness tuning** — set `pane_readiness` to trade prompt-safety for speed without swapping builders. - **A custom builder** — when you need behavior the classic builder doesn't provide. Keep dependency-sensitive setup in `before_script` or `shell_command_before` if your builder relaxes ordering guarantees. ## Reference - {class}`~tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder` - {class}`~tmuxp.workspace.builder.protocol.WorkspaceBuilderProtocol` - {func}`~tmuxp.workspace.builder.registry.resolve_builder_class` - {class}`~tmuxp.workspace.options.PaneReadiness` --- # Topics Source: https://tmuxp.git-pull.com/topics/ (topics)= # Topics Conceptual guides and workflow documentation. ::::{grid} 1 1 2 2 :gutter: 2 2 3 3 :::{grid-item-card} Workflows :link: workflows :link-type: doc CI integration, scripting, and automation patterns. ::: :::{grid-item-card} Plugins :link: plugins :link-type: doc Plugin system for custom behavior. ::: :::{grid-item-card} Custom workspace builders :link: custom-workspace-builders :link-type: doc Select or ship a custom builder; tune pane readiness. ::: :::{grid-item-card} Library vs CLI :link: library-vs-cli :link-type: doc When to use tmuxp CLI vs [libtmux](https://libtmux.git-pull.com/) directly. ::: :::{grid-item-card} Troubleshooting :link: troubleshooting :link-type: doc Common shell, PATH, and tmux issues. ::: :::: ## Compared to other session managers tmuxp, [tmuxinator](https://github.com/aziz/tmuxinator), and [teamocil](https://github.com/remiprev/teamocil) all load tmux sessions from config files. Key differences: tmuxp is Python (not Ruby), builds sessions through [libtmux](https://libtmux.git-pull.com/)'s ORM layer instead of raw shell commands, supports JSON and YAML, and can [freeze](../cli/freeze.md) running sessions back to config. ```{toctree} :hidden: workflows plugins custom-workspace-builders library-vs-cli troubleshooting ``` --- # Library vs CLI Source: https://tmuxp.git-pull.com/topics/library-vs-cli/ # Library vs CLI tmuxp is a CLI tool. [libtmux](https://libtmux.git-pull.com/) is the Python library it's built on. Both control tmux, but they serve different needs. ## When to Use the CLI Use `tmuxp` when: - You want **declarative workspace configs** — define your layout in YAML, load it with one command - You're setting up **daily development environments** — same windows, same panes, every time - You need **CI/CD tmux sessions** — run {ref}`tmuxp load -d ` in a script - You prefer **configuration over code** — no Python needed ```console $ tmuxp load my-workspace.yaml ``` New to the format? Start with {ref}`quickstart`, then browse ready-to-load files in {ref}`examples`. ## When to Use libtmux Use libtmux directly when: - You need **dynamic logic** — conditionals, loops, branching based on state - You want to **read pane output** — capture what's on screen and react to it - You're **testing** tmux interactions — libtmux provides [pytest](https://docs.pytest.org/) fixtures - You need **multi-server orchestration** — manage multiple tmux servers programmatically - The CLI's config format **can't express** what you need ```python import libtmux server = libtmux.Server() session = server.new_session("my-project") window = session.new_window("editor") pane = window.split() pane.send_keys("vim .") ``` ## Concept Mapping How tmuxp config keys map to libtmux API calls: | tmuxp YAML | libtmux equivalent | |------------|-------------------| | `session_name: foo` | {meth}`server.new_session(session_name="foo") ` | | `windows:` | {meth}`session.new_window(...) ` | | `panes:` | {meth}`window.split(...) ` | | `shell_command:` | {meth}`pane.send_keys(...) ` | | `layout: main-vertical` | {meth}`window.select_layout("main-vertical") ` | | `start_directory: ~/project` | {meth}`session.new_window(start_directory="~/project") ` | | `before_script:` | Run via {mod}`subprocess` before building | ## What the CLI Can't Express tmuxp configs are static declarations. They can't: - **Branch on conditions** — "only create this pane if a file exists" - **Read pane output** — "wait until the server is ready, then open the browser" - **React to state** — "if this session already has 3 windows, add a 4th" - **Orchestrate across servers** — "connect to both local and remote tmux" - **Build layouts dynamically** — "create N panes based on a list of services" For these, use libtmux directly. See the {ref}`libtmux quickstart `. --- # Plugins Source: https://tmuxp.git-pull.com/topics/plugins/ (plugins)= # Plugins Plugins let you customize and extend how tmuxp builds a session — renaming it, reacting when you reattach, running setup at specific moments — without forking tmuxp or hand-editing your workspace files. This is an advanced, opt-in feature: most users never write or install one, and a workspace loads exactly the same with no plugins at all. Reach for a plugin when you want behavior the workspace format can't express and you're comfortable writing a little Python. ## Using a plugin Install the plugin into the same Python environment as tmuxp, then name it in your workspace file under `plugins`: ````{tab} YAML ```{literalinclude} ../../examples/plugin-system.yaml :language: yaml ``` ```` ````{tab} JSON ```{literalinclude} ../../examples/plugin-system.json :language: json ``` ```` ## When your hooks fire A plugin is a class whose methods tmuxp calls at set points while it builds and attaches the session. You override only the hooks you care about; the rest do nothing. {ref}`tmuxp load ` drives the lifecycle in a fixed order: :::{mermaid} :caption: When each plugin hook fires. :alt: tmuxp plugin hook order during tmuxp load :name: tmuxp-plugin-hook-order :responsive: fit flowchart TD load["tmuxp load"]:::cmd --> bwb["before_workspace_builder"]:::cmd bwb --> oc["on_window_create"]:::cmd oc --> panes["create panes, run commands"] panes --> awf["after_window_finished"]:::cmd awf -->|more windows| oc awf -->|all windows built| bs["before_script"]:::cmd bs --> reattach["reattach"]:::cmd ::: {meth}`~tmuxp.plugin.TmuxpPlugin.before_workspace_builder` runs first, once the session exists but before any windows. {meth}`~tmuxp.plugin.TmuxpPlugin.on_window_create` and {meth}`~tmuxp.plugin.TmuxpPlugin.after_window_finished` bracket each window's panes. Two of the names can mislead: {meth}`~tmuxp.plugin.TmuxpPlugin.before_script` runs _after_ the whole session is built — it augments, rather than replaces, the workspace's own `before_script` — and {meth}`~tmuxp.plugin.TmuxpPlugin.reattach` fires only when tmuxp re-attaches you to a session that already exists. ## Developing a plugin tmuxp expects a plugin to be a class in a Python submodule named `plugin`, inside a module installed in the same environment as tmuxp. You inherit from the interface tmuxp provides, {class}`~tmuxp.plugin.TmuxpPlugin`. [uv] is tmuxp's package manager of choice, and what these examples use; `pip` works just as well. You need only one project file, for whichever packaging tool you choose. ```tree python_module ├── tmuxp_plugin_my_plugin_module │ ├── __init__.py │ └── plugin.py └── pyproject.toml # Python project configuration file ``` When publishing to [PyPI], tmuxp suggests the naming convention `tmuxp-plugin-{your-plugin-name}` so others can find it. A minimal `pyproject.toml` looks like this: ```toml [project] name = "tmuxp-plugin-my-tmuxp-plugin" version = "0.0.2" description = "An example tmuxp plugin." authors = [{ name = "Author Name", email = "author.name@example.com" }] requires-python = ">=3.10" dependencies = [ "tmuxp>=1.7.0", ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" ``` The `plugin.py` file holds the class: ```python import datetime from tmuxp.plugin import TmuxpPlugin class MyTmuxpPlugin(TmuxpPlugin): def __init__(self): """Initialize my custom plugin.""" # Optional version-dependency configuration. See the Plugin API # docs for every supported parameter. config = { "tmuxp_min_version": "1.6.2", } TmuxpPlugin.__init__( self, plugin_name="tmuxp-plugin-my-tmuxp-plugin", **config, ) def before_workspace_builder(self, session): session.rename_session("my-new-session-name") def reattach(self, session): now = datetime.datetime.now().strftime("%Y-%m-%d") session.rename_session("session_{}".format(now)) ``` Once it's installed in the same environment, name it in a workspace file: ```yaml session_name: plugin example plugins: - my_plugin_module.plugin.MyTmuxpPlugin # ... the rest of your workspace ``` ## Plugin API ```{eval-rst} .. automethod:: tmuxp.plugin.TmuxpPlugin.__init__ ``` ```{eval-rst} .. automethod:: tmuxp.plugin.TmuxpPlugin.before_workspace_builder ``` ```{eval-rst} .. automethod:: tmuxp.plugin.TmuxpPlugin.on_window_create ``` ```{eval-rst} .. automethod:: tmuxp.plugin.TmuxpPlugin.after_window_finished ``` ```{eval-rst} .. automethod:: tmuxp.plugin.TmuxpPlugin.before_script ``` ```{eval-rst} .. automethod:: tmuxp.plugin.TmuxpPlugin.reattach ``` [uv]: https://github.com/astral-sh/uv [PyPI]: https://pypi.org/ --- # Troubleshooting Source: https://tmuxp.git-pull.com/topics/troubleshooting/ # Troubleshooting ## tmuxp command not found Ensure tmuxp is installed and on your `PATH`: ```console $ which tmuxp ``` If installed with `pip install --user`, ensure `~/.local/bin` is in your `PATH`. ## tmux server not found tmuxp requires a running tmux server or will start one automatically. Ensure tmux is installed: ```console $ tmux -V ``` Minimum required version: tmux 3.2. ## Configuration errors Use {ref}`tmuxp debug-info ` to collect system information for bug reports: ```console $ tmuxp debug-info ``` ## Session already exists If a session with the same name already exists, tmuxp will prompt you. Use {ref}`tmuxp load -d ` to load in detached mode alongside existing sessions. ## Shell completion not working See {ref}`completion` for setup instructions for bash, zsh, and tcsh. --- # Workflows Source: https://tmuxp.git-pull.com/topics/workflows/ # Workflows tmuxp is small enough to drop into a script, a CI job, or your daily startup routine. This page collects a few patterns — running headless, branching on exit codes, and turning a session you arranged by hand into a reusable file. None of it is special machinery; it's the same {ref}`tmuxp load `, {ref}`tmuxp freeze `, and {ref}`exit codes ` you already have. ## CI integration You can build a tmux session inside a CI pipeline for integration testing. Load it detached so nothing waits on a terminal: ```console $ tmuxp load -d my-workspace.yaml ``` The `-d` flag loads the session in the background, which is what you want in a headless environment. ## Scripting tmuxp returns meaningful exit codes, so a script can tell success from failure and branch on it. See {ref}`cli-exit-codes` for the full list. ## Automating development environments You don't have to write a workspace file from scratch. Arrange a session the way you like it, freeze it to capture the layout, then edit and replay it anywhere: :::{mermaid} :caption: Capture a session once, replay it anywhere. :alt: manual tmux session captured with tmuxp freeze and replayed with tmuxp load :name: tmuxp-freeze-load-workflow :responsive: fit flowchart TD arrange["arrange tmux by hand"] --> freeze["tmuxp freeze"]:::cmd freeze --> yaml["workspace.yaml"]:::cmd yaml --> edit["edit + commit"] edit --> load["tmuxp load"]:::cmd load --> arrange ::: 1. Arrange your ideal tmux layout by hand. 2. Freeze it: `tmuxp freeze my-session`. 3. Edit the generated YAML to add commands. 4. Load it on any machine: `tmuxp load my-workspace.yaml`. ## User-level configuration You can store workspace files in any of these, then load them by name from anywhere: - `~/.tmuxp/` (legacy) - `~/.config/tmuxp/` (XDG default) - a project-local `.tmuxp.yaml` or `.tmuxp/` directory ---