Agent Usage Guide for tmuxb
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.
Quickstart (isolated socket)
tmuxb new -S <agent-name>.sock <session-name> # create a new session in an isolated socket 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
Using an isolated socket (-S) avoids using the humans' custom tmux config. <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
Session Files: Simplifying Repeated Commands
tmuxb supports a .tmuxb_session file that stores the session name and socket path.
When present, you can omit these from every command.
Creating a Session with Auto-Generated File
# Simplest form - creates session on default tmux server
tmuxb new myproject
# The file contains:
# {:session "myproject"}
If you need an isolated tmux server (separate from your normal tmux), use -S:
# Creates session on a dedicated socket
tmuxb new -S myproject.sock myproject
# The file contains:
# {:session "myproject", :socket "/run/user/100tmuxb/myproject.sock"}
Using the Session File
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
File Discovery
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.
Session Lifecycle
# 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
Flags for 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_session
The Cardinal Rule: Always Capture First
Before 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:
- •A human user might be watching and interacting with the same session
- •Another agent might be sharing the session
- •A previous command might have produced unexpected output
- •An application might have crashed or exited
- •Time has passed and you have no idea what happened
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!
Verify Before Proceeding
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:
- •Starting an editor (vim, emacs, nano)
- •Entering a REPL (python, node, clojure)
- •Running a command that changes the prompt
- •Opening a menu or dialog
- •Switching modes within an application
Breaking Up Long Sequences
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...
Handling Shell Quoting
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'
Timing Considerations
For repeated actions with delays:
# Press down 5 times with 300ms between each tmuxb send demo -- '[:Down 5 :delay 300]'
Working with Vim
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
Working with Emacs
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
Example: Safe Vim Editing Session
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.
Common Mistakes
- •Forgetting to add
:Enterwhen sending interactive commands - •Not capturing before sending commands
- •Assuming an application started without verifying
- •Sending a long sequence without checkpoints
- •Forgetting
--before keywords like:C-x - •Not accounting for bash history expansion (
!must be escaped) - •Assuming the terminal state hasn't changed after time passes
- •Not verifying mode changes in modal editors
Summary
- •Take advantage of the
.tmuxb_sessionfile to avoid repeating session/socket args - •Capture before every send operation
- •Verify state transitions (app started, mode changed, etc.)
- •Break long sequences into verifiable chunks
- •Use
--before EDN keywords - •Handle shell quoting carefully (single quotes around EDN with double-quoted strings)
- •Trouble using the tool? tmuxb --help
- •When in doubt, capture again