tmux Skill
Use tmux as a programmable terminal multiplexer for interactive work. Works on Linux and macOS with stock tmux; avoid custom config by using a private socket.
Quickstart (isolated socket)
SOCKET_DIR=${TMPDIR:-/tmp}/claude-tmux-sockets # well-known dir for all agent sockets
mkdir -p "$SOCKET_DIR"
SOCKET="$SOCKET_DIR/claude.sock" # keep agent sessions separate from your personal tmux
SESSION=claude-python # slug-like names; avoid spaces
tmux -S "$SOCKET" new -d -s "$SESSION" -n shell
tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- 'python3 -q' Enter
tmux -S "$SOCKET" capture-pane -p -J -t "$SESSION":0.0 -S -200 # watch output
tmux -S "$SOCKET" kill-session -t "$SESSION" # clean up
After starting a session, tell the user how to monitor the session by giving them a command to copy paste:
To monitor this session yourself:
tmux -S "$SOCKET" attach -t claude-lldb
Or to capture the output once:
tmux -S "$SOCKET" capture-pane -p -J -t claude-lldb:0.0 -S -200
Socket convention
- Agents MUST place tmux sockets under
CLAUDE_TMUX_SOCKET_DIR (defaults to ${TMPDIR:-/tmp}/claude-tmux-sockets) and use tmux -S "$SOCKET" so we can enumerate/clean them. Create the dir first: mkdir -p "$CLAUDE_TMUX_SOCKET_DIR".
- Default socket path to use unless you must isolate further:
SOCKET="$CLAUDE_TMUX_SOCKET_DIR/claude.sock".
Targeting panes and naming
- Target format:
{session}:{window}.{pane}, defaults to :0.0 if omitted. Keep names short (e.g., claude-py, claude-gdb).
- Use
-S "$SOCKET" consistently to stay on the private socket path. If you need user config, drop -f /dev/null; otherwise -f /dev/null gives a clean config.
- Inspect:
tmux -S "$SOCKET" list-sessions, tmux -S "$SOCKET" list-panes -a.
Finding sessions
- List sessions on your active socket with metadata:
./scripts/find-sessions.sh -S "$SOCKET"; add -q partial-name to filter.
- Scan all sockets under the shared directory:
./scripts/find-sessions.sh --all (uses CLAUDE_TMUX_SOCKET_DIR or ${TMPDIR:-/tmp}/claude-tmux-sockets).
Sending input safely
- Prefer literal sends to avoid shell splitting:
tmux -L "$SOCKET" send-keys -t target -l -- "$cmd"
- When composing inline commands, use single quotes or ANSI C quoting to avoid expansion:
tmux ... send-keys -t target -- $'python3 -m http.server 8000'.
- To send control keys:
tmux ... send-keys -t target C-c, C-d, C-z, Escape, etc.
Watching output
- Capture recent history (joined lines to avoid wrapping artifacts):
tmux -L "$SOCKET" capture-pane -p -J -t target -S -200.
- For continuous monitoring, poll with the helper script (below) instead of
tmux wait-for (which does not watch pane output).
- You can also temporarily attach to observe:
tmux -L "$SOCKET" attach -t "$SESSION"; detach with Ctrl+b d.
- When giving instructions to a user, explicitly print a copy/paste monitor command alongside the action don't assume they remembered the command.
Spawning Processes
Some special rules for processes:
- when asked to debug, use lldb by default
- when starting a python interactive shell, always set the
PYTHON_BASIC_REPL=1 environment variable. This is very important as the non-basic console interferes with your send-keys.
Synchronizing / waiting for prompts
Interactive tool recipes
- Python REPL:
tmux ... send-keys -- 'python3 -q' Enter; wait for ^>>>; send code with -l; interrupt with C-c. Always with PYTHON_BASIC_REPL.
- gdb:
tmux ... send-keys -- 'gdb --quiet ./a.out' Enter; disable paging tmux ... send-keys -- 'set pagination off' Enter; break with C-c; issue bt, info locals, etc.; exit via quit then confirm y.
- Other TTY apps (ipdb, psql, mysql, node, bash): same pattern—start the program, poll for its prompt, then send literal text and Enter.
Cleanup
- Kill a session when done:
tmux -S "$SOCKET" kill-session -t "$SESSION".
- Kill all sessions on a socket:
tmux -S "$SOCKET" list-sessions -F '#{session_name}' | xargs -r -n1 tmux -S "$SOCKET" kill-session -t.
- Remove everything on the private socket:
tmux -S "$SOCKET" kill-server.
Helper: wait-for-text.sh
./scripts/wait-for-text.sh polls a pane for a regex (or fixed string) with a timeout. Works on Linux/macOS with bash + tmux + grep.
./scripts/wait-for-text.sh -t session:0.0 -p 'pattern' [-F] [-T 20] [-i 0.5] [-l 2000]
-t/--target pane target (required)
-p/--pattern regex to match (required); add -F for fixed string
-T timeout seconds (integer, default 15)
-i poll interval seconds (default 0.5)
-l history lines to search from the pane (integer, default 1000)
- Exits 0 on first match, 1 on timeout. On failure prints the last captured text to stderr to aid debugging.
1---2name: tmux3description: Remote control tmux sessions for interactive CLIs (python, gdb, etc.) by sending keystrokes and scraping pane output. TRIGGER when user requests to "operate tmux", "split pane", "run in tmux", "interactive debug", "start REPL", or running interactive CLI commands.4license: Vibecoded5---67# tmux Skill89Use tmux as a programmable terminal multiplexer for interactive work. Works on Linux and macOS with stock tmux; avoid custom config by using a private socket.1011## Quickstart (isolated socket)1213```bash14SOCKET_DIR=${TMPDIR:-/tmp}/claude-tmux-sockets # well-known dir for all agent sockets15mkdir -p "$SOCKET_DIR"16SOCKET="$SOCKET_DIR/claude.sock" # keep agent sessions separate from your personal tmux17SESSION=claude-python # slug-like names; avoid spaces18tmux -S "$SOCKET" new -d -s "$SESSION" -n shell19tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- 'python3 -q' Enter20tmux -S "$SOCKET" capture-pane -p -J -t "$SESSION":0.0 -S -200 # watch output21tmux -S "$SOCKET" kill-session -t "$SESSION" # clean up22```2324After starting a session, tell the user how to monitor the session by giving them a command to copy paste:2526```27To monitor this session yourself:28 tmux -S "$SOCKET" attach -t claude-lldb2930Or to capture the output once:31 tmux -S "$SOCKET" capture-pane -p -J -t claude-lldb:0.0 -S -20032```3334## Socket convention3536- Agents MUST place tmux sockets under `CLAUDE_TMUX_SOCKET_DIR` (defaults to `${TMPDIR:-/tmp}/claude-tmux-sockets`) and use `tmux -S "$SOCKET"` so we can enumerate/clean them. Create the dir first: `mkdir -p "$CLAUDE_TMUX_SOCKET_DIR"`.37- Default socket path to use unless you must isolate further: `SOCKET="$CLAUDE_TMUX_SOCKET_DIR/claude.sock"`.3839## Targeting panes and naming4041- Target format: `{session}:{window}.{pane}`, defaults to `:0.0` if omitted. Keep names short (e.g., `claude-py`, `claude-gdb`).42- Use `-S "$SOCKET"` consistently to stay on the private socket path. If you need user config, drop `-f /dev/null`; otherwise `-f /dev/null` gives a clean config.43- Inspect: `tmux -S "$SOCKET" list-sessions`, `tmux -S "$SOCKET" list-panes -a`.4445## Finding sessions4647- List sessions on your active socket with metadata: `./scripts/find-sessions.sh -S "$SOCKET"`; add `-q partial-name` to filter.48- Scan all sockets under the shared directory: `./scripts/find-sessions.sh --all` (uses `CLAUDE_TMUX_SOCKET_DIR` or `${TMPDIR:-/tmp}/claude-tmux-sockets`).4950## Sending input safely5152- Prefer literal sends to avoid shell splitting: `tmux -L "$SOCKET" send-keys -t target -l -- "$cmd"`53- When composing inline commands, use single quotes or ANSI C quoting to avoid expansion: `tmux ... send-keys -t target -- $'python3 -m http.server 8000'`.54- To send control keys: `tmux ... send-keys -t target C-c`, `C-d`, `C-z`, `Escape`, etc.5556## Watching output5758- Capture recent history (joined lines to avoid wrapping artifacts): `tmux -L "$SOCKET" capture-pane -p -J -t target -S -200`.59- For continuous monitoring, poll with the helper script (below) instead of `tmux wait-for` (which does not watch pane output).60- You can also temporarily attach to observe: `tmux -L "$SOCKET" attach -t "$SESSION"`; detach with `Ctrl+b d`.61- When giving instructions to a user, **explicitly print a copy/paste monitor command** alongside the action don't assume they remembered the command.6263## Spawning Processes6465Some special rules for processes:6667- when asked to debug, use lldb by default68- when starting a python interactive shell, always set the `PYTHON_BASIC_REPL=1` environment variable. This is very important as the non-basic console interferes with your send-keys.6970## Synchronizing / waiting for prompts7172- Use timed polling to avoid races with interactive tools. Example: wait for a Python prompt before sending code:73 ```bash74 ./scripts/wait-for-text.sh -t "$SESSION":0.0 -p '^>>>' -T 15 -l 400075 ```76- For long-running commands, poll for completion text (`"Type quit to exit"`, `"Program exited"`, etc.) before proceeding.7778## Interactive tool recipes7980- **Python REPL**: `tmux ... send-keys -- 'python3 -q' Enter`; wait for `^>>>`; send code with `-l`; interrupt with `C-c`. Always with `PYTHON_BASIC_REPL`.81- **gdb**: `tmux ... send-keys -- 'gdb --quiet ./a.out' Enter`; disable paging `tmux ... send-keys -- 'set pagination off' Enter`; break with `C-c`; issue `bt`, `info locals`, etc.; exit via `quit` then confirm `y`.82- **Other TTY apps** (ipdb, psql, mysql, node, bash): same pattern—start the program, poll for its prompt, then send literal text and Enter.8384## Cleanup8586- Kill a session when done: `tmux -S "$SOCKET" kill-session -t "$SESSION"`.87- Kill all sessions on a socket: `tmux -S "$SOCKET" list-sessions -F '#{session_name}' | xargs -r -n1 tmux -S "$SOCKET" kill-session -t`.88- Remove everything on the private socket: `tmux -S "$SOCKET" kill-server`.8990## Helper: wait-for-text.sh9192`./scripts/wait-for-text.sh` polls a pane for a regex (or fixed string) with a timeout. Works on Linux/macOS with bash + tmux + grep.9394```bash95./scripts/wait-for-text.sh -t session:0.0 -p 'pattern' [-F] [-T 20] [-i 0.5] [-l 2000]96```9798- `-t`/`--target` pane target (required)99- `-p`/`--pattern` regex to match (required); add `-F` for fixed string100- `-T` timeout seconds (integer, default 15)101- `-i` poll interval seconds (default 0.5)102- `-l` history lines to search from the pane (integer, default 1000)103- Exits 0 on first match, 1 on timeout. On failure prints the last captured text to stderr to aid debugging.