prompts/skills/tmux/SKILL.md
Remote control tmux sessions using tmux-buddy (tmuxb) for interactive CLIs (python, gdb, emacs, vim, etc.) by sending keystrokes and scraping pane output.
npx skillsauth add ramblurr/nix-devenv tmuxInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use tmux as a programmable terminal multiplexer for interactive work.
We use a small tmux wrapper called tmux-buddy (cli command: tmuxb).
It provides a few helpers for LLM agents such as yourself when using tmux.
A practical guide for LLM agents using tmuxb to interact with tmux sessions. This guide emphasizes defensive practices that account for shared terminal sessions and timing uncertainties.
tmuxb new # create a new session
tmuxb capture # see the screen inside tmux, including the cursor position
tmuxb capture --if-changed # get the output, but only if its changed
tmux send -- '"echo hello world" :Enter' # Send some key sequences, always remeber to add :Enter if needed, :Enter is never pressed automatically
tmuxb capture --if-changed # view the results
<agent-name> should be something like: claude-codex, codex, gemini. etc It must be a valid part of a filename <session-name> the name of the tmux session, a good value is the name of the project
tmuxb supports a .tmuxb_session file that stores the session name and socket path.
When present, you can omit these from every command.
# Simplest form - creates session on default tmux server
tmuxb new myproject
# The file contains:
# {:session "myproject"}
The -S flag is an isolated socket which may be handy, but in 99.999% of situations you should not use it.
If you need an isolated tmux server (separate from your normal tmux), use -S:
# Creates session on a dedicated socket (ONLY USE -S if there is special need)
tmuxb new -S myproject.sock myproject
# The file contains:
# {:session "myproject", :socket "/run/user/100tmuxb/myproject.sock"}
Relying on the tmux-buddy session file is the preferred way of using tmux-buddy.
Once .tmuxb_session exists, commands automatically use it:
# These all work without specifying session or socket:
tmuxb capture
tmuxb send -- '"echo hello" :Enter'
tmuxb list
You can still override with explicit arguments:
tmuxb capture other-session # uses other-session instead
tmuxb capture -S /other/sock # uses different socket
tmuxb capture -S /other/sock other-session # uses different socket and other-session
tmuxb walks up from the current directory looking for .tmuxb_session, similar to how git finds .git. This means the file can be in a project root and work from subdirectories.
# Create session (writes .tmuxb_session)
tmuxb new -S dev.sock myproject
# Work with session (no args needed)
tmuxb capture
tmuxb send -- '"make build" :Enter'
# Kill session (auto-deletes .tmuxb_session if it matches)
tmuxb kill myproject
new-S, --socket - Socket path (bare names go to $XDG_RUNTIME_DItmuxb/)--no-session-file - Don't create .tmuxb_session-f, --force - Overwrite existing .tmuxb_sessionBefore sending any commands, always capture the current terminal state. The terminal may have changed since you last looked at it.
# ALWAYS do this before sending commands
tmuxb capture
# Or with explicit session if no .tmuxb_session file:
tmuxb capture SESSION
Why? Because:
If more than a few seconds have passed since your last capture, capture again. If you're about to send a complex sequence, capture first. When in doubt, capture. If in doubt, capture!
When you start an application (vim, emacs, python, etc.), verify it actually started before sending commands to it.
Bad pattern:
# DON'T DO THIS - you have no idea if vim actually started
tmuxb send demo -- '"vim" :Enter [:Sleep 500] "i" "Hello"'
Good pattern:
# Start vim
tmuxb send demo -- '"vim" :Enter'
# Wait and verify vim is running
tmuxb capture demo
# Look for vim's interface in the output
# Only then send more commands
tmuxb send demo -- '"i" "Hello"'
This applies to any state transition:
Don't send extremely long command sequences without verification checkpoints. If something fails early in the sequence, you'll send garbage to whatever state the terminal ends up in.
Bad pattern:
# 30+ seconds of commands with no verification
tmuxb send demo -- \
'"vim" :Enter [:Sleep 500] "i"' \
'"line 1" :Enter [:Sleep 1000]' \
'"line 2" :Enter [:Sleep 1000]' \
# ... 20 more lines ...
':Escape ":wq" :Enter'
Better pattern:
# Start vim
tmuxb send demo -- '"vim" :Enter'
sleep 1
# Verify vim started
tmuxb capture demo
# Check output shows vim interface
# Now send the content
tmuxb send demo -- '"i" "line 1" :Enter "line 2" :Enter'
# Verify periodically for long sequences
tmuxb capture demo
# Continue...
Use -- to separate tmuxb options from EDN arguments:
tmuxb send demo -- :C-x
tmuxb send demo -- '"hello" :Enter'
For strings with special characters, use appropriate quoting:
# Exclamation marks need $'...' quoting (bash history expansion)
tmuxb send demo -- $'"Hello!" :Enter'
# Or escape with backslash
tmuxb send demo -- '"Hello World\!" :Enter'
For repeated actions with delays:
# Press down 5 times with 300ms between each
tmuxb send demo -- '[:Down 5 :delay 300]'
Vim has modes. Always be aware of which mode you're in.
# Capture first
tmuxb capture demo
# If in normal mode, enter insert mode before typing
tmuxb send demo -- '"i" "your text here"'
# Return to normal mode
tmuxb send demo -- :Escape
# Vim commands start with : in normal mode
tmuxb send demo -- '":w" :Enter' # save
tmuxb send demo -- $'":q!" :Enter' # quit without saving (note $'' for !)
Verify mode transitions:
# After escaping to normal mode, capture to verify
tmuxb capture demo
# Look for -- INSERT -- or similar mode indicator absence
Emacs uses modifier keys extensively:
# C-x C-s to save
tmuxb send demo -- :C-x :C-s
# M-x for command prompt
tmuxb send demo -- :M-x
# Cancel with C-g
tmuxb send demo -- :C-g
Here's a complete example showing defensive practices. With a .tmuxb_session file, you can omit the session name:
# 1. Capture initial state
tmuxb capture
# Verify we're at a shell prompt
# 2. Start vim
tmuxb send -- '"vim test.txt" :Enter'
# 3. Wait and verify vim started
tmuxb capture
# Look for vim interface, check we're not still at shell
# 4. Enter insert mode and type
tmuxb send -- '"i" "Hello World" :Enter "Line 2"'
# 5. Verify content was entered
tmuxb capture
# Check the text appears in the buffer
# 6. Save and quit
tmuxb send -- ':Escape ":wq" :Enter'
# 7. Verify we're back at shell
tmuxb capture
# Confirm shell prompt is visible
Without a session file, specify session explicitly:
tmuxb capture demo
tmuxb send demo -- '"vim test.txt" :Enter'
# etc.
:Enter when sending interactive commands-- before keywords like :C-x! must be escaped).tmuxb_session file to avoid repeating session/socket args-- before EDN keywordstesting
Use this OCP when executing or preparing to execute commands that change a live or important system, service reloads/restarts, package changes, deployments, migrations, firewall/network/access changes, credential rotation, NixOS switch/test/boot/deploy, or incident mitigation. It guides safe operations with a persisted ledger for scope, preflight, baseline, rollback, validation, and evidence.
development
Create new agent skills with proper structure, progressive disclosure, and bundled resources. Use when user wants to create, write, or build a new skill.
documentation
Naming conventions for workflow documents in prompts/. Use when creating plans, PRDs, research reports, idea capture or other workflow documents. Triggers on (1) creating new planning documents, (2) naming PRDs or research reports, (3) questions about document organization in prompts/.
testing
Grilling session that challenges your plan against the existing domain model, sharpens terminology, and updates documentation (CONTEXT.md, ADRs) inline as decisions crystallise. Use when user wants to stress-test a plan against their project's language and documented decisions.