Programmatic terminal pane control for AI agents — spawn named tmux sessions,
inject commands via send-keys,
capture scrollback output, and build multi-pane grid layouts.
Each agent executes inside its own isolated, observable terminal environment.
Ctrl/Cmd + wheel to zoom. Scroll to pan. Double-click to fit.
SendCommand passes Enter as a distinct
send-keys argument — never appended as \n or C-m.
This prevents tmux from interpreting special characters (semicolons, hashes) in the command text.
The execTmux helper is the single point for all tmux CLI calls.
| Method | Signature | tmux Command | Feature |
|---|---|---|---|
| SpawnSession | (name, cmd string) (Session, error) |
new-session -d -s {name} {cmd} |
F1 |
| DestroySession | (name string) error |
kill-session -t {name} |
F1 |
| SendCommand | (session, cmd string) error |
send-keys -t {session} {cmd} Enter |
F2 |
| CaptureOutput | (session string, lines int) (string, error) |
capture-pane -t {session} -p -S -{lines} |
F2 |
| ListSessions | () ([]Session, error) |
list-sessions -F "#{...}\t#{...}" |
F3 |
| SplitPane | (session string, dir Direction) (Pane, error) |
split-window {-h|-v} -t {session} -P -F |
F3 |
Defines the Substrate interface — 6 methods that abstract terminal multiplexer operations. Callers compile against this interface, not the tmux implementation.
Shared types usable without importing implementation: Session, Pane, Direction, and 5 sentinel errors for errors.Is matching.
Core TmuxSubstrate struct. Constructor verifies tmux on PATH via exec.LookPath. Implements SpawnSession (detached), DestroySession (kill). Private execTmux helper wraps all CLI calls.
SendCommand implementation. Invokes tmux send-keys -t {session} {cmd} Enter with Enter as a separate argument. Classifies tmux errors via error utilities.
CaptureOutput implementation. Validates lines > 0 (returns ErrInvalidLines), then invokes capture-pane -p -S -{lines}. Scrollback math: -S -100 = 100 lines before cursor.
ListSessions parses tab-delimited list-sessions -F output with lenient dimension handling. SplitPane invokes split-window -h/-v with pane metadata capture via -P -F.
Centralized error handling: CheckTmux(), TmuxVersion(), parseTmuxError() with 7 known stderr patterns, ValidateSessionName() with regex ^[a-zA-Z0-9_.\-]+$.
7 integration tests gated behind //go:build integration. Marker-based verification: echo unique marker, sleep 200ms, capture, assert. Cleanup via t.Cleanup + pre-clean stale sessions.
strings.ToLower + strings.Contains
to handle tmux version differences. When no tmux server is running, ListSessions returns an empty
[]Session{} (not nil, not error) — the "no server running" message is caught before error classification.
F1 is the foundation. F2 and F3 branch from F1. F4 integrates all three.
| Feature | Issue | Branch | Status | Scope |
|---|---|---|---|---|
| F1 — Substrate Interface & Core | #42 | feature/42-...-f1 |
merged | Substrate interface, types, TmuxSubstrate, SpawnSession, DestroySession |
| F2 — Command Injection & Output Capture | #44 | feature/44-...-f2 |
merged | SendCommand (send-keys + Enter), CaptureOutput (capture-pane -S) |
| F3 — Layout Management & Error Handling | #48 | feature/48-...-f3 |
merged | ListSessions, SplitPane, CheckTmux, parseTmuxError, ValidateSessionName |
| F4 — Integration Tests | #53 | feature/53-...-f4 |
merged | 7 integration tests, marker-based verification, CI setup |
| T6 — Wire into Orchestrator | #25 | — | merged | Supervisor spawns agents into tmux sessions |
//go:build integration — tests skip in default go test, run only with -tags=integration
Echo a unique marker string, sleep 200ms for tmux processing, capture output, assert marker is present. No mocks — real tmux end-to-end.
t.Cleanup(DestroySession) + pre-clean stale sessions from failed runs. Unique session names per test prevent cross-test interference.
apt-get install tmux in GitHub Actions runner, then go test -tags=integration -timeout 60s