Epic #2 — agenKic-orKistrator

Terminal Substrate Layer

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.

4features
6interface methods
7Go source files
7integration tests

Substrate Architecture

Ctrl/Cmd + wheel to zoom. Scroll to pan. Double-click to fit.

Loading...
Interface + types
Implementation
Utilities
Tests
External / future

Session Lifecycle

SpawnSession
new-session -d -s
SendCommand
send-keys ... Enter
CaptureOutput
capture-pane -p -S
SplitPane
split-window -h/-v
DestroySession
Enter as separate argument. 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.

Substrate Interface

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

Module Breakdown

IF

substrate.go

internal/terminal/

Defines the Substrate interface — 6 methods that abstract terminal multiplexer operations. Callers compile against this interface, not the tmux implementation.

  • Substrate
  • 6 methods
TY

types.go

internal/terminal/

Shared types usable without importing implementation: Session, Pane, Direction, and 5 sentinel errors for errors.Is matching.

  • Session
  • Pane
  • Direction
  • ErrTmuxNotFound
  • ErrSessionNotFound
  • ErrSessionExists
  • ErrPaneLimit
  • ErrInvalidLines
TX

tmux.go

internal/terminal/

Core TmuxSubstrate struct. Constructor verifies tmux on PATH via exec.LookPath. Implements SpawnSession (detached), DestroySession (kill). Private execTmux helper wraps all CLI calls.

  • TmuxSubstrate
  • NewTmuxSubstrate
  • SpawnSession
  • DestroySession
  • execTmux
CM

tmux_command.go

internal/terminal/

SendCommand implementation. Invokes tmux send-keys -t {session} {cmd} Enter with Enter as a separate argument. Classifies tmux errors via error utilities.

  • SendCommand
CP

tmux_capture.go

internal/terminal/

CaptureOutput implementation. Validates lines > 0 (returns ErrInvalidLines), then invokes capture-pane -p -S -{lines}. Scrollback math: -S -100 = 100 lines before cursor.

  • CaptureOutput
LY

tmux_layout.go

internal/terminal/

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.

  • ListSessions
  • SplitPane
  • parseSessionLine
  • parsePaneLine
ER

tmux_errors.go

internal/terminal/

Centralized error handling: CheckTmux(), TmuxVersion(), parseTmuxError() with 7 known stderr patterns, ValidateSessionName() with regex ^[a-zA-Z0-9_.\-]+$.

  • CheckTmux
  • TmuxVersion
  • parseTmuxError
  • ValidateSessionName
IT

integration_test.go

internal/terminal/

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.

  • TestFullLifecycle
  • TestDuplicateSession
  • TestSendCommand_NonExistent
  • TestCaptureOutput_NonExistent
  • TestMultiPaneGrid
  • TestValidateSessionName
  • TestCheckTmux

Error Classification

tmux stderr pattern sentinel error
"session not found" ErrSessionNotFound
"can't find session" ErrSessionNotFound
"can't find window" / "can't find pane" ErrSessionNotFound
"duplicate session" ErrSessionExists
"create pane failed" / "no room for new pane" ErrPaneLimit
unrecognized stderr fmt.Errorf("tmux: %s")
Case-insensitive matching. All pattern matching uses 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.

Feature Dependency Chain

F1 is the foundation. F2 and F3 branch from F1. F4 integrates all three.

Loading...

Feature Status

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

Testing Strategy

1

Build Tag Isolation

//go:build integration — tests skip in default go test, run only with -tags=integration

2

Marker Verification

Echo a unique marker string, sleep 200ms for tmux processing, capture output, assert marker is present. No mocks — real tmux end-to-end.

3

Cleanup Discipline

t.Cleanup(DestroySession) + pre-clean stale sessions from failed runs. Unique session names per test prevent cross-test interference.

4

CI Environment

apt-get install tmux in GitHub Actions runner, then go test -tags=integration -timeout 60s

agenKic-orKistrator — Epic #2: Terminal Substrate Layer Generated 2026-03-22