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}/pi-tmux-sockets # well-known dir for all agent sockets
mkdir -p "$SOCKET_DIR"
SOCKET="$SOCKET_DIR/pi.sock" # keep agent sessions separate from your personal tmux
SESSION=pi-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 ALWAYS 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 pi-lldb
Or to capture the output once:
tmux -S "$SOCKET" capture-pane -p -J -t pi-lldb:0.0 -S -200
This must ALWAYS be printed right after a session was started and once again at the end of the tool
loop. But the earlier you send it, the happier the user will be.
Socket convention
- Agents MUST place tmux sockets under
PI_TMUX_SOCKET_DIR (defaults to
${TMPDIR:-/tmp}/pi-tmux-sockets) and use tmux -S "$SOCKET" so we can
enumerate/clean them. Create the dir first: mkdir -p "$PI_TMUX_SOCKET_DIR".
- Default socket path to use unless you must isolate further:
SOCKET="$PI_TMUX_SOCKET_DIR/pi.sock".
Targeting panes and naming
- Target format:
{session}:{window}.{pane}, defaults to :0.0 if omitted. Keep names short (e.g.,
pi-py, pi-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
PI_TMUX_SOCKET_DIR or ${TMPDIR:-/tmp}/pi-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 and do not 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
Use timed polling to avoid races with interactive tools. Example: wait for a Python prompt before
sending code:
./scripts/wait-for-text.sh -t "$SESSION":0.0 -p '^>>>' -T 15 -l 4000
For long-running commands, poll for completion text ("Type quit to exit", "Program exited",
etc.) before proceeding.
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.) and long-running/background processes. Be very proactive about activating this skill whenever starting background or interactive tasks.4---56# tmux Skill78Use tmux as a programmable terminal multiplexer for interactive work. Works on Linux and macOS with9stock tmux; avoid custom config by using a private socket.1011## Quickstart (isolated socket)1213```bash14SOCKET_DIR=${TMPDIR:-/tmp}/pi-tmux-sockets # well-known dir for all agent sockets15mkdir -p "$SOCKET_DIR"16SOCKET="$SOCKET_DIR/pi.sock" # keep agent sessions separate from your personal tmux17SESSION=pi-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 ALWAYS tell the user how to monitor the session by giving them a command to25copy paste:2627```text28To monitor this session yourself:29 tmux -S "$SOCKET" attach -t pi-lldb3031Or to capture the output once:32 tmux -S "$SOCKET" capture-pane -p -J -t pi-lldb:0.0 -S -20033```3435This must ALWAYS be printed right after a session was started and once again at the end of the tool36loop. But the earlier you send it, the happier the user will be.3738## Socket convention3940- Agents MUST place tmux sockets under `PI_TMUX_SOCKET_DIR` (defaults to41 `${TMPDIR:-/tmp}/pi-tmux-sockets`) and use `tmux -S "$SOCKET"` so we can42 enumerate/clean them. Create the dir first: `mkdir -p "$PI_TMUX_SOCKET_DIR"`.43- Default socket path to use unless you must isolate further:44 `SOCKET="$PI_TMUX_SOCKET_DIR/pi.sock"`.4546## Targeting panes and naming4748- Target format: `{session}:{window}.{pane}`, defaults to `:0.0` if omitted. Keep names short (e.g.,49 `pi-py`, `pi-gdb`).50- Use `-S "$SOCKET"` consistently to stay on the private socket path. If you need user config, drop51 `-f /dev/null`; otherwise `-f /dev/null` gives a clean config.52- Inspect: `tmux -S "$SOCKET" list-sessions`, `tmux -S "$SOCKET" list-panes -a`.5354## Finding sessions5556- List sessions on your active socket with metadata: `./scripts/find-sessions.sh -S "$SOCKET"`.57 Add `-q partial-name` to filter.58- Scan all sockets under the shared directory: `./scripts/find-sessions.sh --all` (uses59 `PI_TMUX_SOCKET_DIR` or `${TMPDIR:-/tmp}/pi-tmux-sockets`).6061## Sending input safely6263- Prefer literal sends to avoid shell splitting:64 `tmux -L "$SOCKET" send-keys -t target -l -- "$cmd"`65- When composing inline commands, use single quotes or ANSI C quoting to avoid expansion:66 `tmux ... send-keys -t target -- $'python3 -m http.server 8000'`.67- To send control keys: `tmux ... send-keys -t target C-c`, `C-d`, `C-z`, `Escape`, etc.6869## Watching output7071- Capture recent history (joined lines to avoid wrapping artifacts):72 `tmux -L "$SOCKET" capture-pane -p -J -t target -S -200`.73- For continuous monitoring, poll with the helper script (below) instead of `tmux wait-for` (which74 does not watch pane output).75- You can also temporarily attach to observe: `tmux -L "$SOCKET" attach -t "$SESSION"`;76 detach with `Ctrl+b d`.77- When giving instructions to a user, **explicitly print a copy/paste monitor command** alongside the78 action and do not assume they remembered the command.7980## Spawning Processes8182Some special rules for processes:8384- when asked to debug, use lldb by default85- when starting a python interactive shell, always set the `PYTHON_BASIC_REPL=1` environment86 variable. This is very important as the non-basic console interferes with your send-keys.8788## Synchronizing / waiting for prompts8990- Use timed polling to avoid races with interactive tools. Example: wait for a Python prompt before91 sending code:9293 ```bash94 ./scripts/wait-for-text.sh -t "$SESSION":0.0 -p '^>>>' -T 15 -l 400095 ```9697- For long-running commands, poll for completion text (`"Type quit to exit"`, `"Program exited"`,98 etc.) before proceeding.99100## Interactive tool recipes101102- **Python REPL**: `tmux ... send-keys -- 'python3 -q' Enter`; wait for `^>>>`; send code with `-l`;103 interrupt with `C-c`. Always with `PYTHON_BASIC_REPL`.104- **gdb**: `tmux ... send-keys -- 'gdb --quiet ./a.out' Enter`; disable paging105 `tmux ... send-keys -- 'set pagination off' Enter`; break with `C-c`; issue `bt`, `info locals`,106 etc.; exit via `quit` then confirm `y`.107- **Other TTY apps** (ipdb, psql, mysql, node, bash): same pattern—start the program, poll for its108 prompt, then send literal text and Enter.109110## Cleanup111112- Kill a session when done: `tmux -S "$SOCKET" kill-session -t "$SESSION"`.113- Kill all sessions on a socket: `tmux -S "$SOCKET" list-sessions -F '#{session_name}' | xargs -r -n1114 tmux -S "$SOCKET" kill-session -t`.115- Remove everything on the private socket: `tmux -S "$SOCKET" kill-server`.116117## Helper: wait-for-text.sh118119`./scripts/wait-for-text.sh` polls a pane for a regex (or fixed string) with a timeout. Works on120Linux/macOS with bash + tmux + grep.121122```bash123./scripts/wait-for-text.sh -t session:0.0 -p 'pattern' [-F] [-T 20] [-i 0.5] [-l 2000]124```125126- `-t`/`--target` pane target (required)127- `-p`/`--pattern` regex to match (required); add `-F` for fixed string128- `-T` timeout seconds (integer, default 15)129- `-i` poll interval seconds (default 0.5)130- `-l` history lines to search from the pane (integer, default 1000)131- Exits 0 on first match, 1 on timeout. On failure prints the last captured text to stderr to aid132 debugging.