Documentation
¶
Overview ¶
backlog.go detects when idle agents have ready work in the bead backlog. It runs a periodic check every 2 minutes: if any agent is idle without a bead AND bd has ready beads, it emits EventAgentIdle events so the operator knows dispatch is needed.
help.go renders the full-screen help reference card modal. Opened by typing "help" or "?" in the command modal. Closed by pressing Esc, backtick, or q.
ipc_lifecycle.go contains IPC handlers for pane lifecycle operations: stop, start, restart, add, and remove. These handlers create, replace, or destroy panes in the running TUI session.
Separated from ipc.go (which owns the socket server, router, and message-oriented handlers) to reduce merge conflicts when lifecycle and messaging logic are edited concurrently.
logger.go provides structured application logging for the TUI. Writes to .initech/initech.log with automatic rotation at 10MB. Uses log/slog for leveled, structured output.
Package tui implements a terminal multiplexer with PTY management, VT emulation via charmbracelet/x/vt, and a tcell-based rendering engine.
pane_journal.go contains JSONL session file watching, entry parsing, activity state derivation, and event detection (bead claims, completions, stalls, stuck loops). The watchJSONL goroutine polls for new entries and feeds them into the pane's ring buffer and event detectors.
pane_render.go contains the Render method and visual conversion helpers for drawing a pane's terminal content and ribbon onto the tcell screen.
pid.go manages the .initech/initech.pid file and post-mortem crash detection.
On startup: write current PID. On clean exit: delete it. If the file exists at startup, the previous run exited uncleanly (signal, OOM, cgo crash). We log a warning and query the macOS system log and DiagnosticReports for evidence of what happened.
Package tui resource management.
resource.go is the home for all resource-aware agent lifecycle code: memory monitoring, auto-suspend policy, and resume-on-message. All of this is gated behind the autoSuspend bool on the TUI struct.
When autoSuspend is false (the default), nothing in this file runs. The memory monitor goroutine is never started, the suspend policy never checks, and agents are never automatically suspended or resumed.
signals.go installs OS signal handlers so every external termination leaves a trace in initech.log before the process exits.
Without this, SIGTERM/SIGHUP/SIGKILL from the OS kill the process silently and leave the terminal in raw mode (screen.Fini never runs). The handlers here fix that for catchable signals. SIGKILL still can't be caught — for that case, the PID file + system log check in pid.go provides post-mortem evidence.
stderr_linux.go redirects os.Stderr (fd 2) to .initech/stderr.log at the OS file-descriptor level. This must happen before screen.Init() puts the terminal into raw mode, so that cgo/native crash stack traces are written to a file rather than into the garbled terminal buffer.
Go's own panic handler writes through os.Stderr (Go level), which also goes through fd 2, so this captures both Go panics and cgo crashes.
Uses syscall.Dup3 instead of syscall.Dup2 because Dup2 is not available on linux/arm64 (the kernel exposes only dup3 on that architecture). Dup3 with flags=0 is semantically identical to Dup2.
Index ¶
- Constants
- func DeleteLayout(projectRoot string) error
- func EmitEvent(ch chan<- AgentEvent, ev AgentEvent)
- func InitLogger(projectRoot string, level slog.Level) func()
- func LogDebug(component string, msg string, args ...any)
- func LogError(component string, msg string, args ...any)
- func LogInfo(component string, msg string, args ...any)
- func LogWarn(component string, msg string, args ...any)
- func Run(cfg Config) error
- func SaveLayout(projectRoot string, state LayoutState) error
- func SocketPath(projectRoot, projectName string) string
- type ActivityState
- type AgentEvent
- type AgentInfo
- type Config
- type Divider
- type EventType
- type IPCRequest
- type IPCResponse
- type JournalEntry
- type LayoutMode
- type LayoutState
- type Pane
- func (p *Pane) Activity() ActivityState
- func (p *Pane) BacklogCount() int
- func (p *Pane) BeadID() string
- func (p *Pane) ClearIdleWithBacklog()
- func (p *Pane) Close()
- func (p *Pane) DrainQueue() []QueuedMessage
- func (p *Pane) EnqueueMessage(text string, enter bool) bool
- func (p *Pane) ForwardMouse(ev uv.MouseEvent)
- func (p *Pane) IdleWithBacklog() bool
- func (p *Pane) InResumeGrace() bool
- func (p *Pane) InScrollback() bool
- func (p *Pane) IsAlive() bool
- func (p *Pane) IsPinned() bool
- func (p *Pane) IsSuspended() bool
- func (p *Pane) LastOutputTime() time.Time
- func (p *Pane) MemoryRSS() int64
- func (p *Pane) QueueLen() int
- func (p *Pane) RecentEntries() []JournalEntry
- func (p *Pane) Render(screen tcell.Screen, focused bool, dimmed bool, index int, sel Selection)
- func (p *Pane) Resize(rows, cols int)
- func (p *Pane) ScrapeQuota() int
- func (p *Pane) ScrollDown(n int)
- func (p *Pane) ScrollUp(n int)
- func (p *Pane) SendKey(ev *tcell.EventKey)
- func (p *Pane) SendPaste(start bool)
- func (p *Pane) SessionDesc() string
- func (p *Pane) SetBead(id, title string)
- func (p *Pane) SetIdleWithBacklog(n int)
- func (p *Pane) SetPinned(v bool)
- func (p *Pane) SetResumeGrace(until time.Time)
- func (p *Pane) SetSuspended(v bool)
- func (p *Pane) SetVisible(v bool)
- func (p *Pane) Start()
- func (p *Pane) Visible() bool
- type PaneConfig
- type PaneRender
- type PersistentLayout
- type QueuedMessage
- type Region
- type RenderPlan
- type Selection
- type TUI
Constants ¶
const DefaultStallThreshold = 10 * time.Minute
DefaultStallThreshold is the duration of inactivity before an agent with an assigned bead is considered stalled.
Variables ¶
This section is empty.
Functions ¶
func DeleteLayout ¶
DeleteLayout removes .initech/layout.yaml. Returns nil if the file doesn't exist (idempotent).
func EmitEvent ¶
func EmitEvent(ch chan<- AgentEvent, ev AgentEvent)
EmitEvent sends an event to the TUI's event channel without blocking. If the channel is full, the event is dropped (producers must not stall).
func InitLogger ¶
InitLogger sets up the application logger writing to .initech/initech.log. level is the minimum severity (slog.LevelDebug for verbose, slog.LevelInfo for normal). Safe to call multiple times; subsequent calls replace the logger. Returns a cleanup function that closes the log file.
func SaveLayout ¶
func SaveLayout(projectRoot string, state LayoutState) error
SaveLayout writes the layout state to .initech/layout.yaml using atomic write (temp file + rename) to prevent corruption. Creates .initech/ if it doesn't exist.
func SocketPath ¶
SocketPath returns the socket path for a project. The socket is placed inside the project's .initech/ directory (not /tmp/) so it is scoped to the project and not world-visible.
Types ¶
type ActivityState ¶
type ActivityState int
ActivityState describes what an agent is doing based on JSONL session tailing.
const ( StateRunning ActivityState = iota // Claude is processing. StateIdle // Waiting for input. StateDead // Process has exited; pane is no longer alive. StateSuspended // Auto-suspended by resource policy. Eligible for resume. )
func (ActivityState) String ¶
func (s ActivityState) String() string
String returns a human-readable label for the state.
type AgentEvent ¶
type AgentEvent struct {
Type EventType
Pane string // Agent name (e.g., "eng1").
BeadID string // Relevant bead ID (empty if N/A).
Detail string // Human-readable description.
Time time.Time // When the event was detected.
}
AgentEvent represents a semantic event from an agent's activity. Emitted by JSONL watchers and consumed by the TUI main loop.
type AgentInfo ¶
type AgentInfo struct {
Name string
Status string // Display text: activity string or bead ID.
Activity ActivityState // Actual activity state for dot color.
Visible bool
IdleWithBacklog bool // True when idle with ready beads in the backlog.
BacklogCount int // Number of ready beads (when IdleWithBacklog is true).
Pinned bool // True when operator has pinned this agent.
}
AgentInfo describes an agent for the status overlay.
type Config ¶
type Config struct {
Agents []PaneConfig // One entry per agent pane.
ProjectName string // Used for socket path.
ProjectRoot string // Project root for .initech/ layout persistence.
ResetLayout bool // Ignore saved layout and start with defaults.
Verbose bool // Enable DEBUG-level logging (default: INFO).
Version string // Build version for crash reports.
AutoSuspend bool // Enable resource-aware auto-suspend/resume.
PressureThreshold int // RSS percentage threshold (0 uses default 85).
PaneConfigBuilder func(name string) (PaneConfig, error) // Optional factory for hot-add. Nil disables add command.
}
Config controls what agents the TUI launches.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns a config with standard shell-only agents.
type EventType ¶
type EventType int
EventType classifies semantic events from agent activity detection.
const ( EventBeadCompleted EventType = iota // Agent finished a bead (DONE comment, ready_for_qa). EventBeadClaimed // Agent claimed a bead (in_progress). EventBeadFailed // QA failed a bead or agent reported failure. EventAgentStalled // No output for configurable threshold (warning). EventAgentStuck // Extended inactivity or error loop detected. EventAgentIdle // Agent returned to idle after work. EventAgentIdleWithBead // Agent went running->idle while holding a bead. EventAgentSuspended // Agent auto-suspended by resource pressure policy. EventAgentResumed // Agent resumed from suspension (triggered by message). )
type IPCRequest ¶
type IPCRequest struct {
Action string `json:"action"` // "send", "peek", "list"
Target string `json:"target"` // Role name (for send/peek).
Text string `json:"text"` // Text to inject (for send).
Lines int `json:"lines"` // Number of lines to return (for peek, 0 = all).
Enter bool `json:"enter"` // Append Enter after text (for send).
}
IPCRequest is the JSON structure sent by CLI commands to the TUI socket.
type IPCResponse ¶
type IPCResponse struct {
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
Data string `json:"data,omitempty"` // Pane content for peek, pane list for list.
}
IPCResponse is the JSON structure returned by the TUI socket.
type JournalEntry ¶
type JournalEntry struct {
Type string // "user", "assistant", "progress", "system", "last-prompt", etc.
Content string // Text content (assistant message, tool output). Capped at 4KB.
ToolName string // For tool_use/tool_result: which tool was called.
ExitCode int // For Bash tool results: exit code if available.
Timestamp time.Time // When this entry was written.
}
JournalEntry represents a parsed JSONL entry from a Claude Code session.
type LayoutMode ¶
type LayoutMode int
LayoutMode determines how panes are arranged on screen.
const ( LayoutFocus LayoutMode = iota // Single pane, full screen. LayoutGrid // Arbitrary NxM grid. Layout2Col // Main pane left, stacked right. )
type LayoutState ¶
type LayoutState struct {
Mode LayoutMode `yaml:"mode"`
GridCols int `yaml:"grid_cols"`
GridRows int `yaml:"grid_rows"`
Zoomed bool `yaml:"zoomed,omitempty"`
Focused string `yaml:"focused"` // Pane name, not index.
Hidden map[string]bool `yaml:"hidden,omitempty"` // Pane names that are hidden.
Pinned map[string]bool `yaml:"pinned,omitempty"` // Pane names protected from auto-suspend.
Order []string `yaml:"order,omitempty"` // Pane names in display order (from show command).
Overlay bool `yaml:"overlay"`
// Per-column and per-row proportional sizing (future).
// nil means uniform. Values are relative weights (e.g., [60, 40] = 60%/40%).
ColWeights []int `yaml:"col_weights,omitempty"`
RowWeights []int `yaml:"row_weights,omitempty"`
}
LayoutState captures the complete layout intent. It is the single authority on what the screen should look like. Trivially serializable to YAML for persistent layout.
func DefaultLayoutState ¶
func DefaultLayoutState(paneNames []string) LayoutState
DefaultLayoutState creates a LayoutState with auto-calculated grid dimensions for the given pane names.
func LoadLayout ¶
func LoadLayout(projectRoot string, paneNames []string) (LayoutState, bool)
LoadLayout reads .initech/layout.yaml and merges it into a LayoutState, filtering stale pane references. Returns false if the file doesn't exist, is empty, contains invalid YAML, or would result in all panes hidden.
type Pane ¶
type Pane struct {
// contains filtered or unexported fields
}
Pane represents a terminal pane backed by a PTY process. It uses a SafeEmulator from charmbracelet/x/vt for terminal emulation.
func NewPane ¶
func NewPane(cfg PaneConfig, rows, cols int) (*Pane, error)
NewPane creates a terminal pane running the configured command (or $SHELL).
func (*Pane) Activity ¶
func (p *Pane) Activity() ActivityState
Activity returns the current activity state based on JSONL session tailing.
func (*Pane) BacklogCount ¶
BacklogCount returns the number of ready beads at the last idle-with-backlog detection.
func (*Pane) ClearIdleWithBacklog ¶
func (p *Pane) ClearIdleWithBacklog()
ClearIdleWithBacklog clears the idle-with-backlog indicator.
func (*Pane) Close ¶
func (p *Pane) Close()
Close terminates the PTY, kills the process, and signals goroutines to exit.
func (*Pane) DrainQueue ¶
func (p *Pane) DrainQueue() []QueuedMessage
DrainQueue returns all queued messages in FIFO order and clears the queue. Called on resume to deliver buffered messages.
Caller must be on the main goroutine (via runOnMain).
func (*Pane) EnqueueMessage ¶
EnqueueMessage appends a message to the pane's queue. If the queue is at capacity (maxMessageQueue), the oldest message is dropped. Returns true if a message was dropped to make room.
Caller must be on the main goroutine (via runOnMain).
func (*Pane) ForwardMouse ¶
func (p *Pane) ForwardMouse(ev uv.MouseEvent)
ForwardMouse sends a mouse event to the emulator with pane-local content coordinates translated to emulator coordinates. The emulator silently drops the event if the child process hasn't enabled mouse reporting.
func (*Pane) IdleWithBacklog ¶
IdleWithBacklog returns true when the pane is idle and ready beads exist.
func (*Pane) InResumeGrace ¶
InResumeGrace returns true if the pane is within the post-resume grace period. During this window the pane is exempt from auto-suspend and idle-with-bead notifications.
func (*Pane) InScrollback ¶
InScrollback returns true when the pane is viewing scrollback history.
func (*Pane) IsPinned ¶
IsPinned reports whether the operator has pinned this pane to prevent auto-suspension. Pinned panes are always kept running.
func (*Pane) IsSuspended ¶
IsSuspended returns whether the pane has been stopped by the auto-suspend policy. A suspended pane is distinct from dead (crashed) and will auto-resume when a message arrives.
func (*Pane) LastOutputTime ¶
LastOutputTime returns the last time PTY output was received.
func (*Pane) MemoryRSS ¶
MemoryRSS returns the pane's last polled RSS in kilobytes. Returns 0 if the memory monitor has not yet polled or the process is dead.
func (*Pane) RecentEntries ¶
func (p *Pane) RecentEntries() []JournalEntry
RecentEntries returns a copy of the recent JSONL entries ring buffer.
func (*Pane) Render ¶
Render draws the pane's bottom ribbon and terminal content onto the tcell screen. When dimmed is true, foreground colors are reduced to ~70% brightness. The index parameter is the 1-based pane number shown in the ribbon badge. All writes are clamped to the pane's region to prevent bleed-through.
func (*Pane) ScrapeQuota ¶ added in v0.21.3
ScrapeQuota reads the emulator's status bar rows and extracts the quota percentage ("N% of limit"). Returns 0-100 on success, -1 if not found. Skips panes in alt-screen mode (vim, less) where the status bar is hidden.
func (*Pane) ScrollDown ¶
ScrollDown moves the viewport down (toward live output) by n rows. When scrollOffset reaches 0, the pane returns to live view.
func (*Pane) SendKey ¶
SendKey translates a tcell key event into a charmbracelet KeyPressEvent and sends it through the emulator, which encodes it for the PTY.
func (*Pane) SendPaste ¶
SendPaste writes a bracketed paste marker to the PTY. On start=true it writes \x1b[200~ (paste start); on start=false it writes \x1b[201~ (paste end). The child process uses these delimiters to distinguish pasted content from typed keystrokes. No-op if the PTY is not open.
func (*Pane) SessionDesc ¶
SessionDesc returns the session description extracted from Claude's cursor row.
func (*Pane) SetIdleWithBacklog ¶
SetIdleWithBacklog marks the pane as idle with n ready beads in the backlog.
func (*Pane) SetResumeGrace ¶
SetResumeGrace sets the post-resume grace period expiration.
func (*Pane) SetSuspended ¶
SetSuspended marks the pane as suspended or resumed by the auto-suspend policy.
func (*Pane) SetVisible ¶
SetVisible controls whether the pane appears in the layout. Hiding a pane does not stop its process or resize its emulator.
type PaneConfig ¶
type PaneConfig struct {
Name string // Display name (role name).
Command []string // Command + args. Empty means use $SHELL.
Dir string // Working directory. Empty means inherit.
Env []string // Extra env vars (KEY=VALUE). TERM is always set.
}
PaneConfig describes how to launch a pane's process.
type PaneRender ¶
type PaneRender struct {
Pane *Pane
Region Region
Index int // 1-based pane number (position in full pane list).
Focused bool // Receives keyboard input.
Dimmed bool // Render with reduced contrast.
}
PaneRender describes how to render a single pane.
type PersistentLayout ¶
type PersistentLayout struct {
Grid string `yaml:"grid"` // e.g. "3x2"
Hidden []string `yaml:"hidden,omitempty"` // Role names of hidden panes.
Pinned []string `yaml:"pinned,omitempty"` // Role names protected from auto-suspend.
Order []string `yaml:"order,omitempty"` // Pane display order (from show command).
Mode string `yaml:"mode"` // "grid", "focus", "main"
}
PersistentLayout is the subset of LayoutState that survives sessions. Focused pane is deliberately excluded (momentary choice, not a preference). Overlay and weights are excluded (not layout-changing from the user's perspective).
type QueuedMessage ¶
QueuedMessage is a message waiting to be delivered to a suspended pane. On resume, the queue is drained in FIFO order via injectText.
type Region ¶
type Region struct {
X, Y, W, H int
}
Region defines a rectangular area on screen (outer bounds including border).
type RenderPlan ¶
type RenderPlan struct {
Panes []PaneRender // One entry per pane to draw (ordered).
Dividers []Divider // Vertical lines between pane columns.
ScreenW int
ScreenH int
// ValidatedFocus is the name of the pane that actually receives input.
// May differ from LayoutState.Focused if that pane is hidden.
ValidatedFocus string
}
RenderPlan is the complete set of instructions for one frame. Produced by computeLayout, consumed by the render loop.
type TUI ¶
type TUI struct {
// contains filtered or unexported fields
}
TUI is the main terminal multiplexer. It owns the tcell screen, a set of terminal panes, and handles input routing, layout, and rendering.
func (*TUI) PressureThreshold ¶
PressureThreshold returns the configured RSS percentage above which agents may be auto-suspended. Returns 85 (the default) when not explicitly set.
func (*TUI) ResourceEnabled ¶
ResourceEnabled reports whether resource-aware auto-suspend is active for this TUI instance. All resource management code should check this gate before taking any action.
func (*TUI) SystemMemoryAvailable ¶
SystemMemoryAvailable returns the last polled available system RAM in kilobytes. Returns 0 if not yet polled or if the query failed.
func (*TUI) SystemMemoryTotal ¶
SystemMemoryTotal returns total system RAM in kilobytes.