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 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 claude-lldb
Or to capture the output once:
tmux -S "$SOCKET" capture-pane -p -J -t claude-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
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.
Sending input safely
- Prefer literal sends to avoid shell splitting:
tmux -S "$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 -S "$SOCKET" capture-pane -p -J -t target -S -200.
- For continuous monitoring, poll 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.
Spawning Processes
- When asked to debug, use lldb by default.
- When starting a python interactive shell, always set the
PYTHON_BASIC_REPL=1 environment variable. The non-basic console interferes with send-keys.
Synchronizing / waiting for prompts
- Use timed polling to avoid races with interactive tools.
- For long-running commands, poll for completion text 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; 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.
1---2name: tmux3description: Remote control tmux sessions for interactive CLIs (python, gdb, etc.) by sending keystrokes and scraping pane output.4---56# tmux Skill78Use 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.910## Quickstart (isolated socket)1112```bash13SOCKET_DIR=${TMPDIR:-/tmp}/claude-tmux-sockets # well-known dir for all agent sockets14mkdir -p "$SOCKET_DIR"15SOCKET="$SOCKET_DIR/claude.sock" # keep agent sessions separate from your personal tmux16SESSION=claude-python # slug-like names; avoid spaces17tmux -S "$SOCKET" new -d -s "$SESSION" -n shell18tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- 'python3 -q' Enter19tmux -S "$SOCKET" capture-pane -p -J -t "$SESSION":0.0 -S -200 # watch output20tmux -S "$SOCKET" kill-session -t "$SESSION" # clean up21```2223After starting a session ALWAYS tell the user how to monitor the session by giving them a command to copy paste:2425```26To monitor this session yourself:27 tmux -S "$SOCKET" attach -t claude-lldb2829Or to capture the output once:30 tmux -S "$SOCKET" capture-pane -p -J -t claude-lldb:0.0 -S -20031```3233This 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.3435## Socket convention3637- 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"`.38- Default socket path to use unless you must isolate further: `SOCKET="$CLAUDE_TMUX_SOCKET_DIR/claude.sock"`.3940## Targeting panes and naming4142- Target format: `{session}:{window}.{pane}`, defaults to `:0.0` if omitted. Keep names short (e.g., `claude-py`, `claude-gdb`).43- 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.44- Inspect: `tmux -S "$SOCKET" list-sessions`, `tmux -S "$SOCKET" list-panes -a`.4546## Sending input safely4748- Prefer literal sends to avoid shell splitting: `tmux -S "$SOCKET" send-keys -t target -l -- "$cmd"`49- When composing inline commands, use single quotes or ANSI C quoting to avoid expansion: `tmux ... send-keys -t target -- $'python3 -m http.server 8000'`.50- To send control keys: `tmux ... send-keys -t target C-c`, `C-d`, `C-z`, `Escape`, etc.5152## Watching output5354- Capture recent history (joined lines to avoid wrapping artifacts): `tmux -S "$SOCKET" capture-pane -p -J -t target -S -200`.55- For continuous monitoring, poll instead of `tmux wait-for` (which does not watch pane output).56- You can also temporarily attach to observe: `tmux -L "$SOCKET" attach -t "$SESSION"`; detach with `Ctrl+b d`.57- When giving instructions to a user, **explicitly print a copy/paste monitor command** alongside the action.5859## Spawning Processes6061- When asked to debug, use lldb by default.62- When starting a python interactive shell, always set the `PYTHON_BASIC_REPL=1` environment variable. The non-basic console interferes with send-keys.6364## Synchronizing / waiting for prompts6566- Use timed polling to avoid races with interactive tools.67- For long-running commands, poll for completion text before proceeding.6869## Interactive tool recipes7071- **Python REPL**: `tmux ... send-keys -- 'python3 -q' Enter`; wait for `^>>>`; send code with `-l`; interrupt with `C-c`. Always with `PYTHON_BASIC_REPL`.72- **gdb**: `tmux ... send-keys -- 'gdb --quiet ./a.out' Enter`; disable paging; break with `C-c`; issue `bt`, `info locals`, etc.; exit via `quit` then confirm `y`.73- **Other TTY apps** (ipdb, psql, mysql, node, bash): same pattern—start the program, poll for its prompt, then send literal text and Enter.7475## Cleanup7677- Kill a session when done: `tmux -S "$SOCKET" kill-session -t "$SESSION"`.78- Kill all sessions on a socket: `tmux -S "$SOCKET" list-sessions -F '#{session_name}' | xargs -r -n1 tmux -S "$SOCKET" kill-session -t`.79- Remove everything on the private socket: `tmux -S "$SOCKET" kill-server`.