tmux-workspace
Load tmux sessions from YAML, and save running ones back to it. A native tmuxp: it reads tmuxp's workspace files, finds them where tmuxp does and takes tmuxp's flags, as one binary with no Python.
Alpha. Commands and flags can change between releases, and changes are listed in the changelog rather than flagged as breaking.
cargo installskips prereleases unless asked, so the command below names the version.
Install
$ cargo install tmux-workspace \
--version 0.1.0-alpha.16 \
--locked
It needs tmux 3.3a or newer on PATH, and Rust 1.85 to build.
cargo binstall fetches a
prebuilt binary for Linux or macOS when the release carries one, and builds
from source when it does not:
$ cargo binstall tmux-workspace --version 0.1.0-alpha.16
Load a session
Describe the session:
session_name: dev
windows:
- window_name: editor
layout: main-vertical
panes:
- vim
- git status
- window_name: monitor
panes:
- top
Save it as dev.yaml, and load it:
$ tmux-workspace load ./dev.yaml
That builds the session and attaches to it. Run inside tmux, it asks whether to switch to the new session, keep it in the background, or add its windows to the session you are in.
Loading
Build the .tmuxp.yaml in a project directory:
$ tmux-workspace load .
Keep workspaces in ~/.config/tmuxp/ and load them by name from anywhere:
$ tmux-workspace load dev
See what one would build -- sessions, windows, panes, directories and
commands -- without touching tmux or running its before_script:
$ tmux-workspace load --dry-run dev
Load several, attaching to the last:
$ tmux-workspace load api web
Build without attaching:
$ tmux-workspace load -d dev
Build under a different session name:
$ tmux-workspace load -s scratch dev
Add the windows to the session you are in:
$ tmux-workspace load -a tools
Build on a separate tmux server, named or by socket path:
$ tmux-workspace load -L work dev
Saving and converting
Save a running session as a workspace:
$ tmux-workspace freeze dev -o dev.yaml
Freezing recovers windows, panes, layouts and working directories. It cannot recover the command someone typed to start a pane, only what the pane is running now, so read the file before relying on it.
Convert between YAML and JSON; it shows the destination and asks first:
$ tmux-workspace convert dev.yaml
Import a tmuxinator project or a teamocil layout. It asks, then saves it
where load finds it by name -- here ~/.config/tmuxp/api.yaml, so
tmux-workspace load api builds it next:
$ tmux-workspace import tmuxinator api
$ tmux-workspace import teamocil dev
Open a workspace in $EDITOR:
$ tmux-workspace edit dev
Finding workspaces
List every workspace it can find, grouped by directory:
$ tmux-workspace ls --tree
Search them; window:, session:, pane: and path: limit a pattern to
one field:
$ tmux-workspace search window:logs
Shell setup
Completions and the manual page are generated by the binary. For zsh, with
~/.zfunc on fpath:
$ tmux-workspace --generate zsh > ~/.zfunc/_tmux-workspace
$ tmux-workspace --generate man > ~/.local/share/man/man1/tmux-workspace.1
bash, fish, PowerShell and elvish are in the
command reference. Every
command has examples under --help.
Attach this to a bug report:
$ tmux-workspace debug-info
Coming from tmuxp
Your workspace files load unchanged: the same keys, the same .tmuxp.yaml
project files, the same directories ($TMUXP_CONFIGDIR, ~/.config/tmuxp,
~/.tmuxp) and the same load flags (-d, -s, -a, -L, -S, -f,
-2, -y). Where it differs:
- An unknown key is refused rather than ignored. Prefix a key with
x-to carry your own fields;convertpreserves them. - A window that names no
layoutis tiled rather than stacked. - A
session_namecontaining:or.is refused: tmux reads those as window and pane separators. $VARin a command is expanded by the pane's shell, with your value passed to it, rather than pasted into the command text: a value holding;stays a value.freezesaves only where-osays, or prints with--json.pluginsand customworkspace_builders run through tmuxp itself, so they need tmuxp 1.74.x and Python libtmux declaring a hard tmux minimum of 3.3a or newer with per-command daemon version check. Runtime discovery and the builder child check this before importing extension hooks.
The Python bridge depends on the coordinated Python libtmux tmux-support
change, which has not been
released. CI qualifies its pinned source with tmux 3.3a. Released tmuxp 1.74.0
pins libtmux below 0.62; installing that release alone does not qualify the
bridge. Use a compatible tmuxp environment carrying the updated Python library
and select it with TMUX_WORKSPACE_PYTHON.
The command reference has the complete list.
It starts in about a millisecond where tmuxp takes about 125, and a cold two-window load takes about 150 ms against 205 (one Linux machine, tmux 3.7d, tmuxp 1.74.0, medians of 20 starts and 5 loads). To measure your own:
$ just bench-cli --binary target/release/tmux-workspace --tmuxp "$(command -v tmuxp)"
Scripting
--json prints one JSON document, and --ndjson streams one event per line
as windows and panes are created. A load under either needs -d, because
nothing is attached:
$ tmux-workspace --ndjson load -d api web
A failure writes {"schema_version":1,"code":"...","message":"..."} to
stderr and exits 1, or 2 when the command line itself is wrong. The codes and
events are in the command reference.
Let an agent watch it
tmux-mcp gives an MCP client typed,
read-only access to the sessions a workspace builds. Load on a named server
and point the server at the same one:
$ tmux-workspace load -d -L agents dev
$ LIBTMUX_TOOLSETS=inspect tmux-mcp -L agents
Configure your MCP client to launch that second command. The workspace keeps running after the client disconnects. The tmux-mcp README covers installing it and wiring clients.
Use it from Rust
The same package is a library: parse a workspace, preview the tmux commands it would run, build it, or freeze a session, through libtmux. It reads a document the way the command does, tmuxp's way; the command reference lists where the two differ.
$ cargo add tmux-workspace@0.1.0-alpha.16 --no-default-features
--no-default-features leaves the command and its dependencies out.
Examples and the API are on docs.rs.
Development
The tests drive a real tmux on an isolated socket, so tmux must be on $PATH:
$ cargo test -p tmux-workspace --all-features
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.