hearth

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package hearth provides The Forge's TUI dashboard using Bubbletea.

The TUI has three columns:

  • Queue / Ready to Merge / Needs Attention (left column, stacked): Pending, mergeable, and stuck beads
  • Workers (center column): Active Smith processes
  • Live Activity / Events (right column, stacked): Streaming log + event log

Tab switches focus between panels, j/k scrolls the focused panel, q quits the app.

Index

Constants

View Source
const EventFetchLimit = 100

EventFetchLimit is the maximum number of events retrieved for the Events panel.

View Source
const SpinnerInterval = 100 * time.Millisecond

SpinnerInterval is how often the spinner animation advances.

View Source
const TickInterval = 2 * time.Second

TickInterval is how often the TUI refreshes data.

Variables

View Source
var SpinnerFrames = []string{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"}

SpinnerFrames are the animation frames for active workers (braille dots pattern).

Functions

func FetchAll

func FetchAll(ds *DataSource) tea.Cmd

FetchAll returns a batch command that refreshes all panels. Daemon health is NOT included here; it is fetched on a slower cadence controlled by healthTickDivisor in the TickMsg handler.

func FetchCrucibles

func FetchCrucibles() tea.Cmd

FetchCrucibles reads active Crucible statuses from the daemon via IPC.

func FetchDaemonHealth added in v0.2.0

func FetchDaemonHealth() tea.Cmd

FetchDaemonHealth probes the daemon via IPC and returns connectivity status.

func FetchEvents

func FetchEvents(db *state.DB, limit int) tea.Cmd

FetchEvents reads recent events from the state DB.

func FetchNeedsAttention

func FetchNeedsAttention(ds *DataSource) tea.Cmd

FetchNeedsAttention reads beads that need human intervention from the state DB. This includes both retry-exhausted beads and PRs that have exhausted their CI-fix, review-fix, or rebase attempt limits. Thresholds are taken from ds so the TUI stays in sync with the daemon's configured limits.

func FetchQueue

func FetchQueue(db *state.DB) tea.Cmd

FetchQueue reads the daemon's cached queue from the state DB. The daemon writes queue data on each poll cycle, so the Hearth TUI always reflects the daemon's view without running its own bd ready calls.

func FetchReadyToMerge

func FetchReadyToMerge(db *state.DB) tea.Cmd

FetchReadyToMerge reads PRs that are ready to merge from the state DB.

func FetchUsage added in v0.2.0

func FetchUsage(ds *DataSource) tea.Cmd

FetchUsage reads today's per-provider costs and copilot premium requests.

func FetchWorkers

func FetchWorkers(db *state.DB) tea.Cmd

FetchWorkers reads active workers from the state DB and enriches with last log line from the worker log file.

func FormatCost

func FormatCost(usd float64) string

FormatCost formats a USD cost for display.

func FormatTokens added in v0.2.0

func FormatTokens(n int) string

FormatTokens formats a token count for compact display. Returns "1.2M" for millions, "340k" for thousands, or the raw number for small values.

func SpinnerTick added in v0.3.0

func SpinnerTick() tea.Cmd

SpinnerTick returns a Bubbletea command that sends a SpinnerTickMsg after SpinnerInterval.

func Tick

func Tick() tea.Cmd

Tick returns a Bubbletea command that sends a TickMsg after the interval.

Types

type ActionMenuChoice

type ActionMenuChoice int

ActionMenuChoice represents an action the user can take on a Needs Attention bead.

const (
	ActionRetry ActionMenuChoice = iota
	ActionDismiss
	ActionViewLogs
)

type AttentionReason

type AttentionReason int

AttentionReason categorizes why a bead needs human attention.

const (
	AttentionUnknown            AttentionReason = iota
	AttentionDispatchExhausted                  // Circuit breaker tripped after repeated dispatch failures
	AttentionCIFixExhausted                     // CI fix attempts exhausted
	AttentionReviewFixExhausted                 // Review fix attempts exhausted
	AttentionRebaseExhausted                    // Rebase attempts exhausted
	AttentionClarification                      // Bead flagged as needing clarification
	AttentionStalled                            // Worker stalled (no log activity)
)

type CrucibleItem

type CrucibleItem struct {
	ParentID          string
	ParentTitle       string
	Anvil             string
	Branch            string
	Phase             string // "started", "dispatching", "waiting", "final_pr", "complete", "paused"
	TotalChildren     int
	CompletedChildren int
	CurrentChild      string
	StartedAt         string
}

CrucibleItem represents an active Crucible in the TUI.

type DataSource

type DataSource struct {
	DB *state.DB
	// Exhaustion thresholds from config. Zero values fall back to state package defaults.
	MaxCIFixAttempts     int
	MaxReviewFixAttempts int
	MaxRebaseAttempts    int
	// AnvilNames lists all registered anvil names (sorted) so the Queue panel
	// can show empty anvils with a (0) count.
	AnvilNames []string
	// Cost limits from config for the Usage panel display.
	DailyCostLimit           float64
	CopilotDailyRequestLimit int
}

DataSource holds the dependencies needed to feed the TUI panels.

type EventItem

type EventItem struct {
	Timestamp string
	Type      string
	Message   string
	BeadID    string
}

EventItem represents an event in the event log panel.

type KillWorkerMsg

type KillWorkerMsg struct {
	WorkerID string
	PID      int
}

KillWorkerMsg requests killing the selected worker by PID.

type MergeMenuChoice

type MergeMenuChoice int

MergeMenuChoice represents an action on a ready-to-merge PR.

const (
	MergeActionMerge MergeMenuChoice = iota
)

type MergeResultMsg

type MergeResultMsg struct {
	PRNumber int
	Err      error
}

MergeResultMsg is delivered asynchronously when a merge IPC call completes.

type Model

type Model struct {

	// Callback for killing a worker (set by the caller)
	OnKill func(workerID string, pid int)

	// Callbacks for Needs Attention actions (set by the caller)
	OnRetryBead   func(beadID, anvil string, prID int) error
	OnDismissBead func(beadID, anvil string, prID int) error
	OnViewLogs    func(beadID string) (logPath string, lines []string)

	// Callback for tagging a bead (set by the caller).
	// Called with (beadID, anvil) when user presses 'l' on an unlabeled bead.
	OnTagBead func(beadID, anvil string) error

	// Callback for closing a bead (set by the caller).
	OnCloseBead func(beadID, anvil string) error

	// Callback for merging a PR (set by the caller).
	OnMergePR func(prID, prNumber int, anvil string) error
	// contains filtered or unexported fields
}

Model is the Bubbletea model for the Hearth TUI.

func NewModel

func NewModel(ds *DataSource) Model

NewModel creates a new Hearth TUI model. Pass nil for DataSource to run in display-only mode (no polling).

func (*Model) Init

func (m *Model) Init() tea.Cmd

Init implements tea.Model.

func (*Model) Update

func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model.

func (*Model) View

func (m *Model) View() string

View implements tea.Model.

type NeedsAttentionErrorMsg

type NeedsAttentionErrorMsg struct{ Err error }

NeedsAttentionErrorMsg signals that reading the needs-attention beads failed.

type NeedsAttentionItem

type NeedsAttentionItem struct {
	BeadID         string
	Title          string
	Anvil          string
	Reason         string
	ReasonCategory AttentionReason
	PRID           int // Non-zero when item originates from an exhausted PR
	PRNumber       int
}

NeedsAttentionItem represents a bead requiring human attention.

type Panel

type Panel int

Panel identifies a TUI panel.

const (
	PanelQueue Panel = iota
	PanelCrucibles
	PanelReadyToMerge
	PanelNeedsAttention
	PanelWorkers
	PanelUsage
	PanelLiveActivity
	PanelEvents
)

type ProviderUsage added in v0.2.0

type ProviderUsage struct {
	Provider     string
	Cost         float64
	InputTokens  int
	OutputTokens int
}

ProviderUsage holds cost/token data for a single provider.

type QueueActionMenuChoice

type QueueActionMenuChoice int

QueueActionMenuChoice represents an action the user can take on an unlabeled queue bead.

const (
	QueueActionLabel QueueActionMenuChoice = iota
	QueueActionClose
)

type QueueActionResultMsg

type QueueActionResultMsg struct {
	BeadID string
	Action string // "tag" or "close"
	Err    error
}

QueueActionResultMsg is delivered asynchronously when a queue action (tag/close) completes.

type QueueErrorMsg

type QueueErrorMsg struct{ Err error }

QueueErrorMsg signals that reading the queue cache failed. The model preserves the previous queue data so the UI doesn't flip to "No pending beads" on a transient DB error.

type QueueItem

type QueueItem struct {
	BeadID      string
	Title       string
	Description string
	Anvil       string
	Priority    int
	Status      string
	Section     string // "ready", "unlabeled", "in_progress"
	Assignee    string
}

QueueItem represents a bead in the queue panel.

type ReadyToMergeErrorMsg

type ReadyToMergeErrorMsg struct{ Err error }

ReadyToMergeErrorMsg signals that reading the ready-to-merge PRs failed.

type ReadyToMergeItem

type ReadyToMergeItem struct {
	PRID     int
	PRNumber int
	BeadID   string
	Anvil    string
	Branch   string
	Title    string
}

ReadyToMergeItem represents a PR ready to merge.

type SpinnerTickMsg added in v0.3.0

type SpinnerTickMsg time.Time

SpinnerTickMsg advances the spinner animation frame.

type TickMsg

type TickMsg time.Time

TickMsg triggers a data refresh cycle.

type UpdateCruciblesMsg

type UpdateCruciblesMsg struct{ Items []CrucibleItem }

UpdateCruciblesMsg updates the crucibles panel.

type UpdateDaemonHealthMsg added in v0.2.0

type UpdateDaemonHealthMsg struct {
	Connected bool
	Workers   int
	QueueSize int
	LastPoll  string
	Uptime    string
}

UpdateDaemonHealthMsg carries the result of a daemon health check to the TUI.

type UpdateEventsMsg

type UpdateEventsMsg struct{ Items []EventItem }

UpdateEventsMsg updates the event log panel.

type UpdateNeedsAttentionMsg

type UpdateNeedsAttentionMsg struct{ Items []NeedsAttentionItem }

UpdateNeedsAttentionMsg updates the needs attention panel.

type UpdateQueueMsg

type UpdateQueueMsg struct{ Items []QueueItem }

UpdateQueueMsg updates the queue panel.

type UpdateReadyToMergeMsg

type UpdateReadyToMergeMsg struct{ Items []ReadyToMergeItem }

UpdateReadyToMergeMsg updates the ready to merge panel.

type UpdateUsageMsg added in v0.2.0

type UpdateUsageMsg struct {
	Data UsageData
}

UpdateUsageMsg carries refreshed usage data to the TUI.

type UpdateWorkersMsg

type UpdateWorkersMsg struct{ Items []WorkerItem }

UpdateWorkersMsg updates the workers panel.

type UsageData added in v0.2.0

type UsageData struct {
	Providers    []ProviderUsage
	TotalCost    float64
	CostLimit    float64 // 0 = no limit
	CopilotUsed  float64
	CopilotLimit int // 0 = no limit
}

UsageData holds the aggregated usage information for the Usage panel.

type WorkerItem

type WorkerItem struct {
	ID            string
	BeadID        string
	Title         string // Bead title for display
	Anvil         string
	Status        string
	Duration      string
	CostUSD       float64
	Type          string   // "smith", "warden", "temper", "cifix", "reviewfix", "rebase"
	PRNumber      int      // PR number for bellows-triggered workers
	LastLog       string   // Last line from the worker log
	PID           int      // Process ID for kill
	LogPath       string   // Path to the worker's log file
	ActivityLines []string // Recent parsed activity from the log (tool calls, thinking, text)
}

WorkerItem represents a worker in the workers panel.

Jump to

Keyboard shortcuts

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