tmux

package
v0.0.0-...-bde0b67 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 15, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package tmux provides theme support for GongShow tmux sessions.

Package tmux provides a wrapper for tmux session operations via subprocess.

Index

Constants

View Source
const (
	// SIGTERMGracePeriod is the time to wait after SIGTERM before sending SIGKILL.
	// 500ms gives processes time to handle cleanup gracefully.
	SIGTERMGracePeriod = 500 * time.Millisecond

	// DescendantRescanDelay is the delay between descendant discovery passes.
	// This helps catch processes that fork during the initial scan.
	DescendantRescanDelay = 50 * time.Millisecond

	// DescendantRescanAttempts is the number of times to rescan for new descendants.
	// Multiple passes help catch race conditions where processes fork during cleanup.
	DescendantRescanAttempts = 3
)

Process cleanup constants

Variables

View Source
var (
	ErrNoServer        = errors.New("no tmux server running")
	ErrSessionExists   = errors.New("session already exists")
	ErrSessionNotFound = errors.New("session not found")
)

Common errors

View Source
var DefaultPalette = []Theme{
	{Name: "ocean", BG: "#1e3a5f", FG: "#e0e0e0"},
	{Name: "forest", BG: "#2d5a3d", FG: "#e0e0e0"},
	{Name: "rust", BG: "#8b4513", FG: "#f5f5dc"},
	{Name: "plum", BG: "#4a3050", FG: "#e0e0e0"},
	{Name: "slate", BG: "#4a5568", FG: "#e0e0e0"},
	{Name: "ember", BG: "#b33a00", FG: "#f5f5dc"},
	{Name: "midnight", BG: "#1a1a2e", FG: "#c0c0c0"},
	{Name: "wine", BG: "#722f37", FG: "#f5f5dc"},
	{Name: "teal", BG: "#0d5c63", FG: "#e0e0e0"},
	{Name: "copper", BG: "#6d4c41", FG: "#f5f5dc"},
}

DefaultPalette is the curated set of distinct, professional color themes. Each theme has good contrast and is visually distinct from others.

Functions

func IsInsideTmux

func IsInsideTmux() bool

IsInsideTmux checks if the current process is running inside a tmux session. This is detected by the presence of the TMUX environment variable.

func ListThemeNames

func ListThemeNames() []string

ListThemeNames returns the names of all themes in the default palette.

Types

type SessionInfo

type SessionInfo struct {
	Name         string
	Windows      int
	Created      string
	Attached     bool
	Activity     string // Last activity time
	LastAttached string // Last time the session was attached
}

SessionInfo contains information about a tmux session.

type SessionSet

type SessionSet struct {
	// contains filtered or unexported fields
}

SessionSet provides O(1) session existence checks by caching session names. Use this when you need to check multiple sessions to avoid N+1 subprocess calls.

func (*SessionSet) Has

func (s *SessionSet) Has(name string) bool

Has returns true if the session exists in the set. This is an O(1) lookup - no subprocess is spawned.

func (*SessionSet) Names

func (s *SessionSet) Names() []string

Names returns all session names in the set.

type Theme

type Theme struct {
	Name string // Human-readable name
	BG   string // Background color (hex or tmux color name)
	FG   string // Foreground color (hex or tmux color name)
}

Theme represents a tmux status bar color scheme.

func AssignTheme

func AssignTheme(rigName string) Theme

AssignTheme picks a theme for a rig based on its name. Uses consistent hashing so the same rig always gets the same color.

func AssignThemeFromPalette

func AssignThemeFromPalette(rigName string, palette []Theme) Theme

AssignThemeFromPalette picks a theme using a custom palette.

func DeaconTheme

func DeaconTheme() Theme

DeaconTheme returns the special theme for the Deacon session. Purple/silver - ecclesiastical, distinct from Mayor's gold.

func GetThemeByName

func GetThemeByName(name string) *Theme

GetThemeByName finds a theme by name from the default palette. Returns nil if not found.

func MayorTheme

func MayorTheme() Theme

MayorTheme returns the special theme for the Mayor session. Gold/dark to distinguish it from rig themes.

func (Theme) Style

func (t Theme) Style() string

Style returns the tmux status-style string for this theme.

type Tmux

type Tmux struct{}

Tmux wraps tmux operations.

func NewTmux

func NewTmux() *Tmux

NewTmux creates a new Tmux wrapper.

func (*Tmux) AcceptBypassPermissionsWarning

func (t *Tmux) AcceptBypassPermissionsWarning(session string) error

AcceptBypassPermissionsWarning dismisses the Claude Code bypass permissions warning dialog. When Claude starts with --dangerously-skip-permissions, it shows a warning dialog that requires pressing Down arrow to select "Yes, I accept" and then Enter to confirm. This function checks if the warning is present before sending keys to avoid interfering with sessions that don't show the warning (e.g., already accepted or different config).

Call this after starting Claude and waiting for it to initialize (WaitForCommand), but before sending any prompts.

func (*Tmux) ApplyTheme

func (t *Tmux) ApplyTheme(session string, theme Theme) error

ApplyTheme sets the status bar style for a session.

func (*Tmux) AttachSession

func (t *Tmux) AttachSession(session string) error

AttachSession attaches to an existing session. Note: This replaces the current process with tmux attach.

func (*Tmux) CapturePane

func (t *Tmux) CapturePane(session string, lines int) (string, error)

CapturePane captures the visible content of a pane.

func (*Tmux) CapturePaneAll

func (t *Tmux) CapturePaneAll(session string) (string, error)

CapturePaneAll captures all scrollback history.

func (*Tmux) CapturePaneLines

func (t *Tmux) CapturePaneLines(session string, lines int) ([]string, error)

CapturePaneLines captures the last N lines of a pane as a slice.

func (*Tmux) ClearHistory

func (t *Tmux) ClearHistory(pane string) error

ClearHistory clears the scrollback history buffer for a pane. This resets copy-mode display from 0/N to 0/0. The pane parameter should be a pane ID (e.g., "%0") or session:window.pane format.

func (*Tmux) ConfigureGasTownSession

func (t *Tmux) ConfigureGasTownSession(session string, theme Theme, rig, worker, role string) error

ConfigureGasTownSession applies full GongShow theming to a session. This is a convenience method that applies theme, status format, and dynamic status.

func (*Tmux) DisplayMessage

func (t *Tmux) DisplayMessage(session, message string, durationMs int) error

DisplayMessage shows a message in the tmux status line. This is non-disruptive - it doesn't interrupt the session's input. Duration is specified in milliseconds.

func (*Tmux) DisplayMessageDefault

func (t *Tmux) DisplayMessageDefault(session, message string) error

DisplayMessageDefault shows a message with default duration (5 seconds).

func (*Tmux) EnableMouseMode

func (t *Tmux) EnableMouseMode(session string) error

EnableMouseMode enables mouse support for a tmux session. This allows clicking to select panes/windows, scrolling with mouse wheel, and dragging to resize panes. Hold Shift for native terminal text selection.

func (*Tmux) EnsureSessionFresh

func (t *Tmux) EnsureSessionFresh(name, workDir string) error

EnsureSessionFresh ensures a session is available and healthy. If the session exists but is a zombie (Claude not running), it kills the session first. This prevents "session already exists" errors when trying to restart dead agents.

A session is considered a zombie if: - The tmux session exists - But Claude (node process) is not running in it

Returns nil if session was created successfully.

func (*Tmux) FindSessionByWorkDir

func (t *Tmux) FindSessionByWorkDir(targetDir string, processNames []string) ([]string, error)

FindSessionByWorkDir finds tmux sessions where the pane's current working directory matches or is under the target directory. Returns session names that match. If processNames is provided, only returns sessions that match those processes. If processNames is nil or empty, returns all sessions matching the directory.

func (*Tmux) GetAllEnvironment

func (t *Tmux) GetAllEnvironment(session string) (map[string]string, error)

GetAllEnvironment returns all environment variables for a session.

func (*Tmux) GetEnvironment

func (t *Tmux) GetEnvironment(session, key string) (string, error)

GetEnvironment gets an environment variable from the session.

func (*Tmux) GetPaneCommand

func (t *Tmux) GetPaneCommand(session string) (string, error)

GetPaneCommand returns the current command running in a pane. Returns "bash", "zsh", "claude", "node", etc.

func (*Tmux) GetPaneID

func (t *Tmux) GetPaneID(session string) (string, error)

GetPaneID returns the pane identifier for a session's first pane. Returns a pane ID like "%0" that can be used with RespawnPane.

func (*Tmux) GetPanePID

func (t *Tmux) GetPanePID(session string) (string, error)

GetPanePID returns the PID of the pane's main process.

func (*Tmux) GetPaneWorkDir

func (t *Tmux) GetPaneWorkDir(session string) (string, error)

GetPaneWorkDir returns the current working directory of a pane.

func (*Tmux) GetSessionInfo

func (t *Tmux) GetSessionInfo(name string) (*SessionInfo, error)

GetSessionInfo returns detailed information about a session.

func (*Tmux) GetSessionSet

func (t *Tmux) GetSessionSet() (*SessionSet, error)

GetSessionSet returns a SessionSet containing all current sessions. Call this once at the start of an operation, then use Has() for O(1) checks. This replaces multiple HasSession() calls with a single ListSessions() call.

Builds the map directly from tmux output to avoid intermediate slice allocation.

func (*Tmux) HasSession

func (t *Tmux) HasSession(name string) (bool, error)

HasSession checks if a session exists (exact match). Uses "=" prefix for exact matching, preventing prefix matches (e.g., "gt-deacon-boot" won't match when checking for "gt-deacon").

func (*Tmux) IsAgentRunning

func (t *Tmux) IsAgentRunning(session string, expectedPaneCommands ...string) bool

IsAgentRunning checks if an agent appears to be running in the session.

If expectedPaneCommands is non-empty, the pane's current command must match one of them. If expectedPaneCommands is empty, any non-shell command counts as "agent running".

func (*Tmux) IsAvailable

func (t *Tmux) IsAvailable() bool

IsAvailable checks if tmux is installed and can be invoked.

func (*Tmux) IsClaudeRunning

func (t *Tmux) IsClaudeRunning(session string) bool

IsClaudeRunning checks if Claude appears to be running in the session. Only trusts the pane command - UI markers in scrollback cause false positives. Claude can report as "node", "claude", or a version number like "2.0.76". Also checks for child processes when the pane is a shell running claude via "bash -c".

func (*Tmux) IsRuntimeRunning

func (t *Tmux) IsRuntimeRunning(session string, processNames []string) bool

IsRuntimeRunning checks if a runtime appears to be running in the session. Only trusts the pane command - UI markers in scrollback cause false positives. This is the runtime-config-aware version of IsAgentRunning.

func (*Tmux) KillServer

func (t *Tmux) KillServer() error

KillServer terminates the entire tmux server and all sessions.

func (*Tmux) KillSession

func (t *Tmux) KillSession(name string) error

KillSession terminates a tmux session.

func (*Tmux) KillSessionWithProcesses

func (t *Tmux) KillSessionWithProcesses(name string) error

KillSessionWithProcesses explicitly kills all processes in a session before terminating it. This prevents orphan processes that survive tmux kill-session due to SIGHUP being ignored.

Process: 1. Get the pane's main process PID 2. Find all descendant processes with retry/rescan to catch forks 3. Send SIGTERM to all descendants (deepest first to avoid orphaning grandchildren) 4. Wait for graceful shutdown (SIGTERMGracePeriod) 5. Rescan for any processes that forked during SIGTERM handling 6. Send SIGKILL to all descendants (deepest first, including newly discovered) 7. Kill the tmux session

The retry/rescan approach addresses race conditions where processes fork new children after the initial descendant list is built but before kill signals are sent. SIGKILL is sent in deepest-first order to prevent brief orphaning of grandchildren.

Performance: Uses native Go syscalls instead of spawning shell commands for each signal.

func (*Tmux) ListSessionIDs

func (t *Tmux) ListSessionIDs() (map[string]string, error)

ListSessionIDs returns a map of session name to session ID. Session IDs are in the format "$N" where N is a number.

func (*Tmux) ListSessions

func (t *Tmux) ListSessions() ([]string, error)

ListSessions returns all session names.

func (*Tmux) NewSession

func (t *Tmux) NewSession(name, workDir string) error

NewSession creates a new detached tmux session.

func (*Tmux) NewSessionWithCommand

func (t *Tmux) NewSessionWithCommand(name, workDir, command string) error

NewSessionWithCommand creates a new detached tmux session that immediately runs a command. Unlike NewSession + SendKeys, this avoids race conditions where the shell isn't ready or the command arrives before the shell prompt. The command runs directly as the initial process of the pane. See: https://github.com/anthropics/gongshow/issues/280

func (*Tmux) NudgePane

func (t *Tmux) NudgePane(pane, message string) error

NudgePane sends a message to a specific pane reliably. Same pattern as NudgeSession but targets a pane ID (e.g., "%9") instead of session name.

func (*Tmux) NudgeSession

func (t *Tmux) NudgeSession(session, message string) error

NudgeSession sends a message to a Claude Code session reliably. This is the canonical way to send messages to Claude sessions. Uses: literal mode + 500ms debounce + ESC (for vim mode) + separate Enter. Verification is the Witness's job (AI), not this function.

func (*Tmux) RenameSession

func (t *Tmux) RenameSession(oldName, newName string) error

RenameSession renames a session.

func (*Tmux) RespawnPane

func (t *Tmux) RespawnPane(pane, command string) error

RespawnPane kills all processes in a pane and starts a new command. This is used for "hot reload" of agent sessions - instantly restart in place. The pane parameter should be a pane ID (e.g., "%0") or session:window.pane format.

func (*Tmux) SelectWindow

func (t *Tmux) SelectWindow(session string, index int) error

SelectWindow selects a window by index.

func (*Tmux) SendKeys

func (t *Tmux) SendKeys(session, keys string) error

SendKeys sends keystrokes to a session and presses Enter. Always sends Enter as a separate command for reliability. Uses a debounce delay between paste and Enter to ensure paste completes.

func (*Tmux) SendKeysDebounced

func (t *Tmux) SendKeysDebounced(session, keys string, debounceMs int) error

SendKeysDebounced sends keystrokes with a configurable delay before Enter. The debounceMs parameter controls how long to wait after paste before sending Enter. This prevents race conditions where Enter arrives before paste is processed.

func (*Tmux) SendKeysDelayed

func (t *Tmux) SendKeysDelayed(session, keys string, delayMs int) error

SendKeysDelayed sends keystrokes after a delay (in milliseconds). Useful for waiting for a process to be ready before sending input.

func (*Tmux) SendKeysDelayedDebounced

func (t *Tmux) SendKeysDelayedDebounced(session, keys string, preDelayMs, debounceMs int) error

SendKeysDelayedDebounced sends keystrokes after a pre-delay, with a custom debounce before Enter. Use this when sending input to a process that needs time to initialize AND the message needs extra time between paste and Enter (e.g., Claude prompt injection). preDelayMs: time to wait before sending text (for process readiness) debounceMs: time to wait between text paste and Enter key (for paste completion)

func (*Tmux) SendKeysRaw

func (t *Tmux) SendKeysRaw(session, keys string) error

SendKeysRaw sends keystrokes without adding Enter.

func (*Tmux) SendKeysReplace

func (t *Tmux) SendKeysReplace(session, keys string, clearDelayMs int) error

SendKeysReplace sends keystrokes, clearing any pending input first. This is useful for "replaceable" notifications where only the latest matters. Uses Ctrl-U to clear the input line before sending the new message. The delay parameter controls how long to wait after clearing before sending (ms).

func (*Tmux) SendNotificationBanner

func (t *Tmux) SendNotificationBanner(session, from, subject string) error

SendNotificationBanner sends a visible notification banner to a tmux session. This interrupts the terminal to ensure the notification is seen. Uses echo to print a boxed banner with the notification details.

func (*Tmux) SetCrewCycleBindings

func (t *Tmux) SetCrewCycleBindings(session string) error

SetCrewCycleBindings sets up C-b n/p to cycle through sessions. This is now an alias for SetCycleBindings - the unified command detects session type automatically.

IMPORTANT: We pass #{session_name} to the command because run-shell doesn't reliably preserve the session context. tmux expands #{session_name} at binding resolution time (when the key is pressed), giving us the correct session.

func (*Tmux) SetCycleBindings

func (t *Tmux) SetCycleBindings(session string) error

SetCycleBindings sets up C-b n/p to cycle through related sessions. The gt cycle command automatically detects the session type and cycles within the appropriate group: - Town sessions: Mayor ↔ Deacon - Crew sessions: All crew members in the same rig

IMPORTANT: These bindings are conditional - they only run gt cycle for GongShow sessions (those starting with "gt-" or "hq-"). For non-GT sessions, the default tmux behavior (next-window/previous-window) is preserved. See: https://github.com/KeithWyatt/gongshow/issues/13

IMPORTANT: We pass #{session_name} to the command because run-shell doesn't reliably preserve the session context. tmux expands #{session_name} at binding resolution time (when the key is pressed), giving us the correct session.

func (*Tmux) SetDynamicStatus

func (t *Tmux) SetDynamicStatus(session string) error

SetDynamicStatus configures the right side with dynamic content. Uses a shell command that tmux calls periodically to get current status.

func (*Tmux) SetEnvironment

func (t *Tmux) SetEnvironment(session, key, value string) error

SetEnvironment sets an environment variable in the session.

func (*Tmux) SetFeedBinding

func (t *Tmux) SetFeedBinding(session string) error

SetFeedBinding configures C-b a to jump to the activity feed window. This creates the feed window if it doesn't exist, or switches to it if it does. Uses `gt feed --window` which handles both creation and switching.

IMPORTANT: This binding is conditional - it only runs for GongShow sessions (those starting with "gt-" or "hq-"). For non-GT sessions, a help message is shown. See: https://github.com/KeithWyatt/gongshow/issues/13

func (*Tmux) SetMailClickBinding

func (t *Tmux) SetMailClickBinding(session string) error

SetMailClickBinding configures left-click on status-right to show mail preview. This creates a popup showing the first unread message when clicking the mail icon area.

func (*Tmux) SetPaneDiedHook

func (t *Tmux) SetPaneDiedHook(session, agentID string) error

SetPaneDiedHook sets a pane-died hook on a session to detect crashes. When the pane exits, tmux runs the hook command with exit status info. The agentID is used to identify the agent in crash logs (e.g., "gongshow/Toast").

func (*Tmux) SetStatusFormat

func (t *Tmux) SetStatusFormat(session, rig, worker, role string) error

SetStatusFormat configures the left side of the status bar. Shows compact identity: icon + minimal context

func (*Tmux) SetTownCycleBindings

func (t *Tmux) SetTownCycleBindings(session string) error

SetTownCycleBindings sets up C-b n/p to cycle through sessions. This is now an alias for SetCycleBindings - the unified command detects session type automatically.

func (*Tmux) SwitchClient

func (t *Tmux) SwitchClient(targetSession string) error

SwitchClient switches the current tmux client to a different session. Used after remote recycle to move the user's view to the recycled session.

func (*Tmux) WaitForCommand

func (t *Tmux) WaitForCommand(session string, excludeCommands []string, timeout time.Duration) error

WaitForCommand polls until the pane is NOT running one of the excluded commands. Useful for waiting until a shell has started a new process (e.g., claude). Returns nil when a non-excluded command is detected, or error on timeout.

func (*Tmux) WaitForRuntimeReady

func (t *Tmux) WaitForRuntimeReady(session string, rc *config.RuntimeConfig, timeout time.Duration) error

WaitForRuntimeReady polls until the runtime's prompt indicator appears in the pane. Runtime is ready when we see the configured prompt prefix at the start of a line.

IMPORTANT: Bootstrap vs Steady-State Observation

This function uses regex to detect runtime prompts - a ZFC violation. ZFC (Zero False Commands) principle: AI should observe AI, not regex.

Bootstrap (acceptable):

During cold startup when no AI agent is running, the daemon uses this
function to get the Deacon online. Regex is acceptable here.

Steady-State (use AI observation instead):

Once any AI agent is running, observation should be AI-to-AI:
- Deacon starting polecats → use 'gt deacon pending' + AI analysis
- Deacon restarting → Mayor watches via 'gt peek'
- Mayor restarting → Deacon watches via 'gt peek'

See: gt deacon pending (ZFC-compliant AI observation) See: gt deacon trigger-pending (bootstrap mode, regex-based)

func (*Tmux) WaitForShellReady

func (t *Tmux) WaitForShellReady(session string, timeout time.Duration) error

WaitForShellReady polls until the pane is running a shell command. Useful for waiting until a process has exited and returned to shell.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL