pairmux
Let AI agents drive interactive terminal programs — and hand off to a human when they can't.
Driving a raw terminal is hard for agents for three separate reasons:
- Timing. After sending a command, an agent has no signal for when it is done, so it guesses
sleep N— and reads half-finished output or wastes time. - Information quality. A raw
capture-paneview is screen-oriented: it may contain ANSI-driven redraws, omit scrolled-off output, and does not provide a command exit code or completion signal. - Human access. Many automation interfaces do not expose the same live terminal to a human who needs to observe or provide authorized input.
pairmux is an ACI (Agent-Computer Interface) layer on top of tmux: blocking calls return on a
command completion, a recognized prompt, a requested condition, or timeout; a journal retains raw
pane output while CLI reads shape it for agents; and pairmux attach opens a native tmux client on
the managed session so a human can collaborate in the same pane.
Requirements
- tmux >= 3.2 (for stable
pipe-paneand pane user-option behavior). - macOS 12+ or Linux, on x86-64 or ARM64. There is no native Windows build; use a supported Linux distribution inside WSL.
- Released pairmux executables are static Go binaries. Core terminal state needs no background
pairmux service, although tmux keeps its normal server and
mcp serveis an explicit foreground process. Installing the PyPI wheel requires Python 3.9+; building from a checkout requires Go 1.25+ andmake.
Install
- uvx / uv
- Homebrew
- PyPI installer
- RPM
- Manual archive
- Source checkout
- Windows (WSL)
With uv installed, run pairmux without
adding a persistent pairmux command to your PATH:
uvx pairmux version
uvx pairmux doctor
uvx runs the tool in an isolated environment. It can reuse a cached environment or an existing
uv-managed installation; a quick run does not promise a fresh download or the latest version every
time. For a persistent command, install the tool and ensure uv's tool executable directory is on
your PATH:
uv tool install pairmux
pairmux version
pairmux doctor
The PyPI wheels bundle the native Go binary and require Python
3.9+ to install. uv can obtain a managed Python if needed; the installed binary itself does not
need Python. Supported wheels target macOS 12+ and manylinux_2_17 (glibc 2.17+) on x86-64 and
ARM64. You can also use pipx install pairmux, or python -m pip install pairmux inside a dedicated
Python 3.9+ virtual environment.
uv and pipx do not install tmux. Use brew install tmux, sudo apt install tmux on Debian/Ubuntu,
or your distribution's package manager for tmux >= 3.2. These direct uv commands respect your
normal uv configuration; use the website installer below for its explicit PyPI-only source policy.
The tap cask installs pairmux and its tmux dependency in one command (macOS and Linuxbrew), and strips the quarantine attribute so the binary runs without a Gatekeeper prompt. Available from v0.2.0; republished automatically with every stable release:
brew install --cask treeleaves30760/pairmux/pairmux
curl -fsSL https://pairmux.treeleaves30760.com/install.sh | bash
The Bash installer uses uv to install the pairmux wheel from PyPI, not a GitHub tarball. It bootstraps uv from Astral's official installer if absent, and lets uv obtain a compatible managed Python if necessary. Those uv/Python bootstrap downloads are separate from the pairmux wheel's PyPI source.
It installs to ~/.local/bin by default, uses no sudo, does not write shell profiles, and only
checks for tmux rather than installing it. It clears inherited UV_* settings and ignores uv
configuration/index overrides, uses https://pypi.org/simple for pairmux, and accepts wheels only,
not source distributions. It reinstalls with uv's persistent cache disabled so an existing tool or
shared cache cannot stand in for the PyPI wheel. Ordinary uvx / uv tool install commands can
reuse cache; they do not promise a fresh network download.
For a complete download before execution, inspect the script first:
installer_dir=$(mktemp -d) &&
curl -fsSL https://pairmux.treeleaves30760.com/install.sh -o "$installer_dir/install.sh" &&
less "$installer_dir/install.sh" &&
bash "$installer_dir/install.sh" --version v0.5.2 --dry-run
Replace v0.5.2 with your chosen published version, or omit --version for the latest stable PyPI
release. Remove --dry-run only after a successful download and review to perform the installation.
The downloaded installer's --dry-run does not access the network or write files. The pipe form is
a convenience, not a complete-download-before-execution check.
Set PAIRMUX_INSTALL_DIR to choose the uv tool executable directory. A piped installer cannot alter
your parent shell's PATH. Follow its PATH hint and use command -v pairmux to check whether an older
installation shadows the new command. It does not overwrite commands owned by a different installer.
GitHub Releases contain native .rpm files for Linux x86-64 (amd64) and ARM64. Choose a version
from the release page, then download and verify
the matching asset in a new directory:
PAIRMUX_VERSION=X.Y.Z # replace with the selected release version, without the leading v
PAIRMUX_ARCH=amd64 # use arm64 on aarch64 systems
PAIRMUX_PACKAGE="pairmux_${PAIRMUX_VERSION}_linux_${PAIRMUX_ARCH}.rpm"
download_dir=$(mktemp -d)
curl -fL "https://github.com/treeleaves30760/pairmux/releases/download/v${PAIRMUX_VERSION}/${PAIRMUX_PACKAGE}" -o "$download_dir/$PAIRMUX_PACKAGE"
curl -fL "https://github.com/treeleaves30760/pairmux/releases/download/v${PAIRMUX_VERSION}/checksums.txt" -o "$download_dir/checksums.txt"
(cd "$download_dir" && sha256sum --ignore-missing -c checksums.txt) &&
sudo dnf install "$download_dir/$PAIRMUX_PACKAGE"
The RPM declares a tmux >= 3.2 dependency. There is no Yum repository; upgrading requires a new
RPM download. pairmux no longer provides an APT installation path; Debian/Ubuntu users should use
PyPI/uv, Homebrew, or a manual archive.
For installation without Python, download a .tar.gz and checksums.txt from
GitHub Releases. This is a separate manual
path; the website's installer uses PyPI.
PAIRMUX_VERSION=X.Y.Z # replace with the selected release version, without the leading v
PAIRMUX_OS=linux # use darwin on macOS
PAIRMUX_ARCH=amd64 # use arm64 on Apple Silicon / aarch64 systems
PAIRMUX_ARCHIVE="pairmux_${PAIRMUX_VERSION}_${PAIRMUX_OS}_${PAIRMUX_ARCH}.tar.gz"
download_dir=$(mktemp -d)
curl -fL "https://github.com/treeleaves30760/pairmux/releases/download/v${PAIRMUX_VERSION}/${PAIRMUX_ARCHIVE}" -o "$download_dir/$PAIRMUX_ARCHIVE"
curl -fL "https://github.com/treeleaves30760/pairmux/releases/download/v${PAIRMUX_VERSION}/checksums.txt" -o "$download_dir/checksums.txt"
# Linux:
(cd "$download_dir" && sha256sum --ignore-missing -c checksums.txt)
# macOS: use (cd "$download_dir" && shasum -a 256 --ignore-missing -c checksums.txt)
Only after the checksum check succeeds, extract the binary and install it in a user-writable location. Review any existing file at that location rather than overwriting another installer:
tar -xzf "$download_dir/$PAIRMUX_ARCHIVE" -C "$download_dir" pairmux
mkdir -p "$HOME/.local/bin"
install -m 0755 "$download_dir/pairmux" "$HOME/.local/bin/pairmux"
export PATH="$HOME/.local/bin:$PATH"
pairmux version
Install tmux >= 3.2 separately. This archive path does not provide automatic upgrades.
From the repository root:
make build
mkdir -p "$HOME/.local/bin"
install -m 0755 bin/pairmux "$HOME/.local/bin/pairmux"
pairmux version
There is no native Windows artifact because tmux does not run on Windows. The supported arrangement is pairmux inside WSL. The PowerShell entry point finds WSL, checks it has a distribution, downloads the Bash installer successfully, and then runs it inside that distribution:
irm https://pairmux.treeleaves30760.com/install.ps1 | iex
A piped script takes no arguments, so configure it through the environment — PAIRMUX_VERSION,
PAIRMUX_WSL_DISTRO, PAIRMUX_INSTALL_DIR — or inspect the downloaded install.ps1 before invoking
it as a file. pairmux then lives inside the distribution: run it as wsl -- pairmux version, and
install tmux there too (sudo apt install tmux on Debian/Ubuntu) if it is not already present.
Migrating from APT / Debian packages
pairmux's APT repository and .deb distribution are being retired for v0.5.3. Keep tmux installed
through your system package manager. Follow the APT migration guide to
inspect and retire the old source, origin pin, keyring/package, and command without destructive
cleanup shortcuts, and check whether an old installation shadows the new command on PATH.
Verify your environment
pairmux doctor probes everything pairmux depends on and prints an actionable report:
pairmux doctor
The exact paths and shell list depend on the machine. A healthy Linux result has this shape:
ok
pairmux doctor
✓ tmux 3.4 at /usr/bin/tmux (>= 3.2)
✓ state dir writable: /home/alice/.local/state/pairmux/.sockets/<endpoint-id>
✓ live probe bash: hooks; dash: sentinel
✓ notifier notify-send at /usr/bin/notify-send (desktop notifications available)
The live probe line reports which completion-detection tier each shell reaches. Fish 4+ emits
compatible OSC 133 marks natively; fish without a ready mark degrades to a $status sentinel. See
Concepts.
Quick start
Every command that emits a single pairmux response accepts --json for a machine-readable
pairmux.v1 envelope; without it you get a friendly text
block. attach and watch are interactive human interfaces, while mcp serve reserves stdout for
MCP JSON-RPC. Commands followed by JSON below explicitly enable --json.
1. Open a terminal
pairmux --json new --name build
{"schema":"pairmux.v1","ok":true,"status":"created","terminal":"build","mode":"hooks","next":["pairmux run build \"echo hello\""]}
This opens a tmux window and starts capturing output into a journal. zsh, bash, and compatible fish
configurations normally report mode: hooks; other shells, or a failed hook-ready probe, report
mode: sentinel. Always act on the returned mode and status rather than assuming either one.
2. Run a command and block for an actionable outcome
No sleep, no guessing — run returns when the command completes, a recognized prompt has been
quiet long enough to trust, or its timeout expires. A completed command includes the exit code and
duration:
pairmux --json run build "echo hello world"
{"schema":"pairmux.v1","ok":true,"status":"done","terminal":"build","mode":"hooks","exit_code":0,"duration_ms":101,"output":"hello world"}
3. Long-running commands: the run → wait → peek loop
If a command outlives --timeout (default 60s), run returns status: running with the current tail instead of failing. You then keep waiting or look whenever you like:
pairmux --json run build 'for step in 1 2 3; do echo "step $step"; sleep 1; done' --timeout 1s
pairmux --json wait build --idle 800 # returns on idle, input, pane death, or timeout
pairmux --json peek build # read-only snapshot, any time
See the long-running commands guide.
4. Interactive programs
Start a separate terminal for an interactive flow. When its command goes quiet on a recognized
prompt, pairmux reports awaiting-input and tells you how to answer:
pairmux --json new --name confirm
pairmux --json run confirm 'printf "Continue? [y/N] "; read answer; printf "answer=%s\n" "$answer"'
pairmux --json send confirm --text y --enter
pairmux --json wait confirm --idle 800
pairmux --json peek confirm
For password, passphrase, and passcode prompts, the returned next omits send and recommends a
human handoff. The CLI cannot enforce that policy, so the driving agent must never guess or send the
secret itself. See interactive programs and
human collaboration.
5. Humans jump into the same terminal
pairmux attach build # run from an interactive terminal outside tmux
pairmux watch # live dashboard of every terminal
attach refuses a non-TTY or nested tmux client. If you are already in tmux, detach to the outer
shell first or use another terminal, then run pairmux attach; switch-client cannot cross tmux
servers to pairmux's named socket.
Next steps
- CLI Reference — every command and flag, plus the full envelope schema.
- Concepts — why pairmux is built the way it is: tmux as a state engine, endpoint-isolated journals, completion modes, and the idle backstop.
- Long-running commands guide — the complete run/wait/peek playbook.
- Agent Skills — install targets and the repeatable cross-agent benchmark runner.