The Meowgorithm Dotfiles, Collectors’ Edition
name: tmux description: Drive tmux programmatically — create sessions, send commands to panes, read pane output, manage layouts, and interact with running terminal processes. Use when Crush needs to run something in a terminal and read its output, send keystrokes to a running process (CLI, REPL, editor, server), manage multiple terminal sessions, create persistent dev environments, interact with long-running processes, capture terminal scrollback, send special keys (Ctrl+C, Escape, arrows), split/resize/zoom panes, run background servers alongside a foreground task, or any task that requires interacting with a real TTY.
tmux
Drive tmux from Crush: send commands to panes, read pane output, and manage sessions. This lets Crush interact with any terminal process — REPLs, editors, servers, interactive CLIs.
Prerequisites
Requires tmux on the system. Verify:
command -v tmux && tmux -V
If missing, install: brew install tmux (macOS) or apt-get install tmux (Linux).
Quick Start: Run a Command and Read Output
# 1. Create a session
bash scripts/tmux_session.sh new mywork "$PWD"
# 2. Send a command
bash scripts/tmux_send.sh mywork "echo hello && ls -la"
# 3. Read the output (last 30 lines)
bash scripts/tmux_capture.sh -l 30 mywork
# 4. Send Ctrl+C to stop something
bash scripts/tmux_send.sh -k mywork C-c
# 5. Kill the session when done
bash scripts/tmux_session.sh kill mywork
Target Format
All scripts accept a target string:
| Format | Example | Meaning |
|---|---|---|
session |
mywork |
Active pane in session |
session:window |
mywork:1 |
Active pane in window |
session:window.pane |
mywork:1.2 |
Specific pane |
.pane |
.1 |
Pane in current window (if inside tmux) |
For full details, see references/pane-targeting.md.
Scripts
scripts/tmux_capture.sh — Read Pane Output
Capture visible or scrollback content from a pane.
# Visible content only (default)
bash scripts/tmux_capture.sh mywork
# Last 50 lines of scrollback
bash scripts/tmux_capture.sh -l 50 mywork
# Entire scrollback
bash scripts/tmux_capture.sh -a mywork
# With line numbers
bash scripts/tmux_capture.sh -p -l 100 mywork
# Strip trailing whitespace
bash scripts/tmux_capture.sh -s -l 50 mywork
| Option | Description |
|---|---|
-l, --lines N |
Capture N lines of scrollback |
-a, --all |
Capture entire scrollback |
-s, --strip |
Strip trailing whitespace per line |
-j, --join |
Collapse blank lines, join wrapped content |
-p, --prefix |
Prefix lines with line numbers |
scripts/tmux_send.sh — Send Commands to Panes
Send text commands or special keys to a pane.
# Run a command (Enter is sent automatically)
bash scripts/tmux_send.sh mywork "go test ./..."
# Send without Enter (type into a prompt)
bash scripts/tmux_send.sh -n mywork "docker"
# Send special keys
bash scripts/tmux_send.sh -k mywork C-c
bash scripts/tmux_send.sh -k mywork Enter
bash scripts/tmux_send.sh -k mywork Escape
bash scripts/tmux_send.sh -k mywork Up
bash scripts/tmux_send.sh -k mywork C-a C-k
# Send literal text (no key interpretation)
bash scripts/tmux_send.sh -l mywork "C-c is just text here"
# Send and read back output (waits 0.3s, captures 30 lines)
bash scripts/tmux_send.sh -r mywork "echo \$HOME"
# Send and wait longer before reading back
bash scripts/tmux_send.sh -w 3 mywork "sleep 2 && echo done"
| Option | Description |
|---|---|
-e, --enter |
Send Enter after command (default) |
-n, --no-enter |
Do not send Enter |
-k, --keys |
Treat arguments as key names (C-c, Escape, Up) |
-l, --literal |
Send as literal text, no key interpretation |
-d, --delay S |
Wait S seconds before sending |
-r, --read-back |
Capture and print pane output after sending |
-w, --wait S |
Wait S seconds before read-back (implies -r) |
For all key names (C-c, Escape, F1, arrows, etc.), see references/key-names.md.
scripts/tmux_session.sh — Session/Window/Pane Management
# Sessions
bash scripts/tmux_session.sh new mywork "$PWD" # create detached session
bash scripts/tmux_session.sh list # full tree: sessions→windows→panes
bash scripts/tmux_session.sh list-sessions # session names only
bash scripts/tmux_session.sh kill mywork # kill session
bash scripts/tmux_session.sh exists mywork # check existence (exit 0/1)
bash scripts/tmux_session.sh attached mywork # check if attached (exit 0/1)
# Windows
bash scripts/tmux_session.sh new-window mywork "logs" "$PWD"
bash scripts/tmux_session.sh list-windows mywork
bash scripts/tmux_session.sh kill-window mywork:1
# Panes
bash scripts/tmux_session.sh split mywork:0.1 -v "$PWD" # split vertically (below)
bash scripts/tmux_session.sh split mywork:0.1 -h "$PWD" # split horizontally (right)
bash scripts/tmux_session.sh list-panes mywork:0
bash scripts/tmux_session.sh kill-pane mywork:0.2
bash scripts/tmux_session.sh select mywork:0.1
bash scripts/tmux_session.sh resize mywork:0.1 -L 20 # resize left by 20
bash scripts/tmux_session.sh zoom mywork:0.1 # toggle zoom
bash scripts/tmux_session.sh break-pane mywork:0.2 mywork # pane → new window
# Info
bash scripts/tmux_session.sh info mywork:0.1
Common Patterns
Run a long-lived server and check its output
bash scripts/tmux_session.sh new api "$PWD"
bash scripts/tmux_send.sh -n api "go run ./cmd/api"
sleep 2
bash scripts/tmux_capture.sh -l 50 api
Interact with a REPL (Python, Node, etc.)
bash scripts/tmux_session.sh new repl "$PWD"
bash scripts/tmux_send.sh -n repl "python3"
sleep 1
bash scripts/tmux_send.sh -r repl "import os; print(os.getcwd())"
bash scripts/tmux_send.sh -k repl C-d # exit REPL
Send Ctrl+C to stop a running process
bash scripts/tmux_send.sh -k mywork C-c
Read output, check for a prompt, then send more commands
# Check if a shell prompt is ready
OUTPUT=$(bash scripts/tmux_capture.sh -l 10 mywork)
if echo "$OUTPUT" | grep -q '\$ $'; then
bash scripts/tmux_send.sh mywork "next-command"
fi
Multi-pane dev environment
bash scripts/tmux_session.sh new dev "$PWD"
bash scripts/tmux_session.sh split dev:0.0 -h "$PWD" # right pane
bash scripts/tmux_session.sh split dev:0.1 -v "$PWD" # split right into top/bottom
# Pane 0: Crush, Pane 1: server (top-right), Pane 2: CLI (bottom-right)
bash scripts/tmux_send.sh dev:0.1 "hivemind" # start server
bash scripts/tmux_send.sh dev:0.2 "ls -la" # run in CLI pane
# Read server logs
bash scripts/tmux_capture.sh -l 30 dev:0.1
Send text to an editor (vi/helix/etc.)
# Save in helix
bash scripts/tmux_send.sh -k editor Escape ":w" Enter
# Quit vi
bash scripts/tmux_send.sh -k editor Escape ":q!" Enter
# Type text into insert mode
bash scripts/tmux_send.sh -k editor i "hello world" Escape
Monitor a pane until a pattern appears
for i in $(seq 1 30); do
OUTPUT=$(bash scripts/tmux_capture.sh -l 20 mywork)
echo "$OUTPUT" | grep -q "Server started" && break
sleep 1
done
Paginated output (less, man, etc.)
# Send Space to advance a page
bash scripts/tmux_send.sh -k mywork Space
# Send 'q' to quit pager
bash scripts/tmux_send.sh -k mywork q
# Search in less
bash scripts/tmux_send.sh -k mywork / "error" Enter
Key Decisions
- Always create a named session for tracked work. Unnamed sessions get auto-generated IDs that are hard to target.
- Use
-r(read-back) sparingly — it adds latency. Prefer explicittmux_capture.shcalls after a known delay. - Wait for output before reading. Most processes need 0.5-2s. Use
-w <seconds>orsleepbeforetmux_capture.sh. - Ctrl+C is safe. If a pane is idle, sending
C-cis a no-op. - Check
existsbefore operations on sessions that might have been killed externally. - Clean up sessions when done with
killto avoid orphaned processes.
Running Inside tmux
If Crush itself runs inside a tmux pane, $TMUX is set and $TMUX_PANE
holds the current pane's ID. Scripts work the same way — just use
explicit session names rather than relying on the current session.