How SwarmForge Uses tmux Sessions for Role Isolation and Orchestration
SwarmForge uses dedicated tmux sessions to isolate each autonomous role (coder, cleaner, handoff daemon), with a portable socket under /tmp/swarmforge-<user> enabling cross-platform session management, command injection via send-keys, and automatic cleanup on shutdown.
SwarmForge, an open-source autonomous coding orchestration tool in the unclebob/swarm-forge repository, leverages tmux as its core execution substrate. Rather than running roles as background processes hidden from the user, SwarmForge creates visible, attachable tmux sessions that developers can inspect, debug, and interact with in real-time. This design provides process isolation, persistent scrollback history, and a unified interface across macOS, Linux, and Windows Terminal.
Socket Setup and Portability
SwarmForge begins by establishing a portable tmux socket that survives environment changes and enables non-standard tmux installations.
In swarmforge/scripts/swarmforge.bb at lines 768-790, the startup routine:
- Creates a temporary directory under
/tmp/swarmforge-<username> - Generates a uniquely-named socket file
- Persists the socket path to
.swarmforge/tmux-socketfor subprocess access
;; Simplified from swarmforge.bb L768-790
(let [socket-dir (str "/tmp/swarmforge-" (System/getenv "USER"))
_ (fs/create-dirs socket-dir)
socket-path (str socket-dir "/" (System/currentTimeMillis) ".sock")]
(spit ".swarmforge/tmux-socket" socket-path)
socket-path)
This socket-centric approach allows every subsequent tmux operation to target the correct server instance explicitly via the -S flag, preventing collisions with the user's personal tmux sessions.
Base Index Detection for Cross-Environment Consistency
Before creating role sessions, SwarmForge probes tmux configuration to normalize window and pane numbering. The function at lines 88-98 spawns a temporary session to read base-index and pane-base-index values:
tmux -S <socket> start-server
tmux -S <socket> new-session -d -s probe
tmux -S <socket> display-message -p '#{base-index}'
tmux -S <socket> display-message -p '#{pane-base-index}'
tmux -S <socket> kill-session -t probe
These values are stored in the execution context and used by tmux-agent-target (lines 566-568) to construct valid target specifications regardless of the user's tmux configuration.
Per-Role Session Creation
The create-role-session! function (lines 383-386) instantiates isolated environments for each SwarmForge role:
;; Creating a role session in swarmforge.bb
(let [session "swarmforge-coder"
socket (:tmux-socket ctx)
agent-window "agent"]
(sh "tmux" "-S" socket "new-session" "-d" "-s" session "-n" agent-window)
(sh "tmux" "-S" socket "set-option" "-t" session "history-limit" "10000")
(sh "tmux" "-S" socket "rename-window" "-t" (str session ":" agent-window) "Coder"))
Each session receives:
- Detached creation (
-d) to avoid blocking the orchestrator - History limit configuration for scrollback retention
- Descriptive window titles for user clarity
Session metadata—socket path, PID, and pane ID—is written to disk for subsequent operations and cleanup tracking.
Interactive Command Injection with send-keys
SwarmForge drives role behavior by injecting keystrokes into running sessions rather than executing one-shot commands. This preserves state and allows multi-step workflows:
;; Sending "git status" to the coder pane
(let [socket (:tmux-socket ctx)
target (tmux-agent-target "Coder"
(:tmux-pane-base-index ctx)
"swarmforge-coder")]
(sh "tmux" "-S" socket "send-keys" "-t" target "-l" "git status")
(sh "tmux" "-S" socket "send-keys" "-t" target "C-m"))
The tmux-agent-target helper translates logical role names to concrete session:window.pane addresses using the previously detected base indexes, ensuring commands arrive at the correct destination even when base-index is non-zero.
User Attachment and Terminal Integration
SwarmForge exposes sessions to developers through multiple pathways. At launch completion (lines 841-857), it prints re-attachment commands:
tmux -S /tmp/swarmforge-username/12345.sock attach-session -t swarmforge-coder
Platform-specific terminal adapters in swarmforge/scripts/terminal-adapters/ automate this for common environments:
- iTerm2 (
iterm2.shlines 47-50): ReadsTMUX_SOCKETandTMUX_SESSIONfrom environment variables, creates a new tab, and auto-attaches - Windows Terminal: Equivalent PowerShell adaptation for WSL compatibility
- Generic POSIX: Falls back to standard tmux attach commands
These adapters enable one-click debugging of any autonomous role without manual socket path lookup.
Session Cleanup and Lifecycle Management
Graceful shutdown is handled by swarm-cleanup.sh (lines 5-42), which accepts the socket path and session list as arguments:
#!/bin/bash
TMUX_SOCKET=$1
shift
for session in "$@"; do
tmux -S "$TMUX_SOCKET" kill-session -t "$session" 2>/dev/null
done
rm -f "$WINDOW_IDS_FILE"
The orchestrator invokes this automatically when SwarmForge exits, ensuring no orphaned tmux servers consume system resources. The test suite validates this behavior in test/swarmforge/script_test.clj (lines 886-915), asserting that "close-swarm-kills-tmux-sessions" terminates all created sessions without affecting user tmux instances.
Testing Infrastructure
SwarmForge's test suite exercises tmux integration through isolated tmux servers spun up specifically for test cases. Key coverage includes:
- Socket file creation and persistence validation
- Session lifecycle (create, attach, send-keys, destroy)
- Base index detection correctness across tmux versions
- Cleanup verification via process inspection
The pack_ui_test.clj file demonstrates starting and stopping tmux for UI pack testing, while pack_web.bb uses tmux-stub emulation for web-pack execution environments lacking full tmux.
Summary
- SwarmForge uses tmux sessions to isolate autonomous roles with visible, attachable execution contexts
- Portable socket architecture under
/tmp/swarmforge-<user>prevents conflicts with user tmux sessions - Base index probing ensures consistent window/pane addressing across diverse tmux configurations
send-keysinjection drives role behavior while preserving interactive state and scrollback- Terminal adapters provide platform-native attachment for iTerm2, Windows Terminal, and POSIX environments
- Automatic cleanup via
swarm-cleanup.shand validated test coverage prevents resource leaks
Frequently Asked Questions
How does SwarmForge prevent its tmux sessions from interfering with my existing tmux work?
SwarmForge creates a dedicated socket file in /tmp/swarmforge-<username>/ rather than using the default tmux socket. Every tmux command explicitly targets this socket via the -S flag, completely isolating SwarmForge's server instance from your personal tmux sessions. You can verify this by running tmux ls (your sessions) versus tmux -S /tmp/swarmforge-.../xxx.sock ls (SwarmForge sessions).
Can I attach to a running SwarmForge role to see what it's doing?
Yes. SwarmForge prints the attachment command at startup, and terminal adapters automate this. For manual attachment, use: tmux -S $(cat .swarmforge/tmux-socket) attach-session -t swarmforge-coder. Press Ctrl+b then d to detach without stopping the role.
What happens to tmux sessions if SwarmForge crashes?
The swarm-cleanup.sh script is designed for graceful shutdown, but crashes may leave sessions running. You can manually clean up by locating the socket (find /tmp -name "swarmforge-*" 2>/dev/null) and running tmux -S <socket> kill-server or targeting individual sessions with kill-session.
Why does SwarmForge use send-keys instead of run-shell or direct command execution?
send-keys preserves interactive state—environment variables, working directory, and shell history remain intact across commands. This enables multi-step workflows where subsequent commands depend on prior state. It also makes all activity visible in tmux scrollback, crucial for debugging autonomous agent behavior.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →