Documentation
¶
Overview ¶
Package tmux provides theme support for Gas Town tmux sessions.
Package tmux provides a wrapper for tmux session operations via subprocess.
Index ¶
- Variables
- func IsInsideTmux() bool
- func ListThemeNames() []string
- type SessionInfo
- type Theme
- type Tmux
- func (t *Tmux) AcceptBypassPermissionsWarning(session string) error
- func (t *Tmux) ApplyTheme(session string, theme Theme) error
- func (t *Tmux) AttachSession(session string) error
- func (t *Tmux) CapturePane(session string, lines int) (string, error)
- func (t *Tmux) CapturePaneAll(session string) (string, error)
- func (t *Tmux) CapturePaneLines(session string, lines int) ([]string, error)
- func (t *Tmux) ClearHistory(pane string) error
- func (t *Tmux) ConfigureGasTownSession(session string, theme Theme, rig, worker, role string) error
- func (t *Tmux) DisplayMessage(session, message string, durationMs int) error
- func (t *Tmux) DisplayMessageDefault(session, message string) error
- func (t *Tmux) EnsureSessionFresh(name, workDir string) error
- func (t *Tmux) FindSessionByWorkDir(targetDir string, requireAgentRunning bool) ([]string, error)
- func (t *Tmux) GetEnvironment(session, key string) (string, error)
- func (t *Tmux) GetPaneCommand(session string) (string, error)
- func (t *Tmux) GetPaneID(session string) (string, error)
- func (t *Tmux) GetPaneWorkDir(session string) (string, error)
- func (t *Tmux) GetSessionInfo(name string) (*SessionInfo, error)
- func (t *Tmux) HasSession(name string) (bool, error)
- func (t *Tmux) IsAgentRunning(session string, expectedPaneCommands ...string) bool
- func (t *Tmux) IsAvailable() bool
- func (t *Tmux) IsCursorRunning(session string) bool
- func (t *Tmux) KillServer() error
- func (t *Tmux) KillSession(name string) error
- func (t *Tmux) ListSessionIDs() (map[string]string, error)
- func (t *Tmux) ListSessions() ([]string, error)
- func (t *Tmux) NewSession(name, workDir string) error
- func (t *Tmux) NudgePane(pane, message string) error
- func (t *Tmux) NudgeSession(session, message string) error
- func (t *Tmux) RenameSession(oldName, newName string) error
- func (t *Tmux) RespawnPane(pane, command string) error
- func (t *Tmux) SelectWindow(session string, index int) error
- func (t *Tmux) SendKeys(session, keys string) error
- func (t *Tmux) SendKeysDebounced(session, keys string, debounceMs int) error
- func (t *Tmux) SendKeysDelayed(session, keys string, delayMs int) error
- func (t *Tmux) SendKeysDelayedDebounced(session, keys string, preDelayMs, debounceMs int) error
- func (t *Tmux) SendKeysRaw(session, keys string) error
- func (t *Tmux) SendKeysReplace(session, keys string, clearDelayMs int) error
- func (t *Tmux) SendNotificationBanner(session, from, subject string) error
- func (t *Tmux) SetCrewCycleBindings(session string) error
- func (t *Tmux) SetCycleBindings(session string) error
- func (t *Tmux) SetDynamicStatus(session string) error
- func (t *Tmux) SetEnvironment(session, key, value string) error
- func (t *Tmux) SetFeedBinding(session string) error
- func (t *Tmux) SetMailClickBinding(session string) error
- func (t *Tmux) SetPaneDiedHook(session, agentID string) error
- func (t *Tmux) SetStatusFormat(session, rig, worker, role string) error
- func (t *Tmux) SetTownCycleBindings(session string) error
- func (t *Tmux) SwitchClient(targetSession string) error
- func (t *Tmux) WaitForCommand(session string, excludeCommands []string, timeout time.Duration) error
- func (t *Tmux) WaitForCursorReady(session string, timeout time.Duration) error
- func (t *Tmux) WaitForShellReady(session string, timeout time.Duration) error
Constants ¶
This section is empty.
Variables ¶
var ( ErrNoServer = errors.New("no tmux server running") ErrSessionExists = errors.New("session already exists") ErrSessionNotFound = errors.New("session not found") )
Common errors
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 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 ¶
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 ¶
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 ¶
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.
type Tmux ¶
type Tmux struct{}
Tmux wraps tmux operations.
func (*Tmux) AcceptBypassPermissionsWarning ¶
AcceptBypassPermissionsWarning dismisses the Cursor bypass permissions warning dialog. When the agent 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 the agent and waiting for it to initialize (WaitForCommand), but before sending any prompts.
func (*Tmux) ApplyTheme ¶
ApplyTheme sets the status bar style for a session.
func (*Tmux) AttachSession ¶
AttachSession attaches to an existing session. Note: This replaces the current process with tmux attach.
func (*Tmux) CapturePane ¶
CapturePane captures the visible content of a pane.
func (*Tmux) CapturePaneAll ¶
CapturePaneAll captures all scrollback history.
func (*Tmux) CapturePaneLines ¶
CapturePaneLines captures the last N lines of a pane as a slice.
func (*Tmux) ClearHistory ¶
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 ¶
ConfigureGasTownSession applies full Gas Town theming to a session. This is a convenience method that applies theme, status format, and dynamic status.
func (*Tmux) DisplayMessage ¶
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 ¶
DisplayMessageDefault shows a message with default duration (5 seconds).
func (*Tmux) EnsureSessionFresh ¶
EnsureSessionFresh ensures a session is available and healthy. If the session exists but is a zombie (agent 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 the agent process is not running in it
Returns nil if session was created successfully.
func (*Tmux) FindSessionByWorkDir ¶
FindSessionByWorkDir finds tmux sessions where the pane's current working directory matches or is under the target directory. Returns session names that match. If requireAgentRunning is true, only returns sessions that have some non-shell command running.
func (*Tmux) GetEnvironment ¶
GetEnvironment gets an environment variable from the session.
func (*Tmux) GetPaneCommand ¶
GetPaneCommand returns the current command running in a pane. Returns "bash", "zsh", "cursor-agent", "node", etc.
func (*Tmux) GetPaneID ¶
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) GetPaneWorkDir ¶
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) HasSession ¶
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 ¶
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 ¶
IsAvailable checks if tmux is installed and can be invoked.
func (*Tmux) IsCursorRunning ¶
IsCursorRunning checks if Cursor appears to be running in the session. Only trusts the pane command - UI markers in scrollback cause false positives.
func (*Tmux) KillServer ¶
KillServer terminates the entire tmux server and all sessions.
func (*Tmux) KillSession ¶
KillSession terminates a tmux session.
func (*Tmux) ListSessionIDs ¶
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 ¶
ListSessions returns all session names.
func (*Tmux) NewSession ¶
NewSession creates a new detached tmux session.
func (*Tmux) NudgePane ¶
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 ¶
NudgeSession sends a message to a Cursor session reliably. This is the canonical way to send messages to Cursor sessions. Uses: literal mode + 500ms debounce + separate Enter. Verification is the Witness's job (AI), not this function.
func (*Tmux) RenameSession ¶
RenameSession renames a session.
func (*Tmux) RespawnPane ¶
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 ¶
SelectWindow selects a window by index.
func (*Tmux) SendKeys ¶
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 ¶
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 ¶
SendKeysDelayed sends keystrokes after a delay (in milliseconds). Useful for waiting for a process to be ready before sending input.
func (*Tmux) SendKeysDelayedDebounced ¶
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., 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 ¶
SendKeysRaw sends keystrokes without adding Enter.
func (*Tmux) SendKeysReplace ¶
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 ¶
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 ¶
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 ¶
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 Gas Town 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/cursorworkshop/cursor-gastown/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 ¶
SetDynamicStatus configures the right side with dynamic content. Uses a shell command that tmux calls periodically to get current status.
func (*Tmux) SetEnvironment ¶
SetEnvironment sets an environment variable in the session.
func (*Tmux) SetFeedBinding ¶
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 Gas Town sessions (those starting with "gt-" or "hq-"). For non-GT sessions, a help message is shown. See: https://github.com/cursorworkshop/cursor-gastown/issues/13
func (*Tmux) SetMailClickBinding ¶
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 ¶
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., "gastown/Toast").
func (*Tmux) SetStatusFormat ¶
SetStatusFormat configures the left side of the status bar. Shows compact identity: icon + minimal context
func (*Tmux) SetTownCycleBindings ¶
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 ¶
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., cursor-agent). Returns nil when a non-excluded command is detected, or error on timeout.
func (*Tmux) WaitForCursorReady ¶
WaitForCursorReady polls until Cursor's prompt indicator appears in the pane. Cursor is ready when we see "> " at the start of a line (the input prompt). This is more reliable than just checking if the agent is running.
IMPORTANT: Bootstrap vs Steady-State Observation
This function uses regex to detect Cursor's prompt - 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)