Import beads' UX design system into gastown: - Add internal/ui/ package with Ayu theme colors and semantic styling - styles.go: AdaptiveColor definitions for light/dark mode - terminal.go: TTY detection, NO_COLOR/CLICOLOR support - markdown.go: Glamour rendering with agent mode bypass - pager.go: Smart paging with GT_PAGER support - Add colorized help output (internal/cmd/help.go) - Group headers in accent color - Command names styled for scannability - Flag types and defaults muted - Add gt thanks command (internal/cmd/thanks.go) - Contributor display with same logic as bd thanks - Styled with Ayu theme colors - Update gt doctor to match bd doctor UX - Category grouping (Core, Infrastructure, Rig, Patrol, etc.) - Semantic icons (✓ ⚠ ✖) with Ayu colors - Tree connectors for detail lines - Summary line with pass/warn/fail counts - Warnings section at end with numbered issues - Migrate existing styles to use ui package - internal/style/style.go uses ui.ColorPass etc. - internal/tui/feed/styles.go uses ui package colors Co-Authored-By: SageOx <ox@sageox.ai>
131 lines
3.3 KiB
Go
131 lines
3.3 KiB
Go
package doctor
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/steveyegge/gastown/internal/formula"
|
|
)
|
|
|
|
// FormulaCheck verifies that embedded formulas are up-to-date.
|
|
// It detects outdated formulas (binary updated), missing formulas (user deleted),
|
|
// and modified formulas (user customized). Can auto-fix outdated and missing.
|
|
type FormulaCheck struct {
|
|
FixableCheck
|
|
}
|
|
|
|
// NewFormulaCheck creates a new formula check.
|
|
func NewFormulaCheck() *FormulaCheck {
|
|
return &FormulaCheck{
|
|
FixableCheck: FixableCheck{
|
|
BaseCheck: BaseCheck{
|
|
CheckName: "formulas",
|
|
CheckDescription: "Check embedded formulas are up-to-date",
|
|
CheckCategory: CategoryConfig,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// Run checks if formulas need updating.
|
|
func (c *FormulaCheck) Run(ctx *CheckContext) *CheckResult {
|
|
report, err := formula.CheckFormulaHealth(ctx.TownRoot)
|
|
if err != nil {
|
|
return &CheckResult{
|
|
Name: c.Name(),
|
|
Status: StatusWarning,
|
|
Message: fmt.Sprintf("Could not check formulas: %v", err),
|
|
}
|
|
}
|
|
|
|
// All good
|
|
if report.Outdated == 0 && report.Missing == 0 && report.Modified == 0 && report.New == 0 && report.Untracked == 0 {
|
|
return &CheckResult{
|
|
Name: c.Name(),
|
|
Status: StatusOK,
|
|
Message: fmt.Sprintf("%d formulas up-to-date", report.OK),
|
|
}
|
|
}
|
|
|
|
// Build details
|
|
var details []string
|
|
var needsFix bool
|
|
|
|
for _, f := range report.Formulas {
|
|
switch f.Status {
|
|
case "outdated":
|
|
details = append(details, fmt.Sprintf(" %s: update available", f.Name))
|
|
needsFix = true
|
|
case "missing":
|
|
details = append(details, fmt.Sprintf(" %s: missing (will reinstall)", f.Name))
|
|
needsFix = true
|
|
case "modified":
|
|
details = append(details, fmt.Sprintf(" %s: locally modified (skipping)", f.Name))
|
|
case "new":
|
|
details = append(details, fmt.Sprintf(" %s: new formula available", f.Name))
|
|
needsFix = true
|
|
case "untracked":
|
|
details = append(details, fmt.Sprintf(" %s: untracked (will update)", f.Name))
|
|
needsFix = true
|
|
}
|
|
}
|
|
|
|
// Determine status
|
|
status := StatusOK
|
|
if needsFix {
|
|
status = StatusWarning
|
|
}
|
|
|
|
// Build message
|
|
var parts []string
|
|
if report.Outdated > 0 {
|
|
parts = append(parts, fmt.Sprintf("%d outdated", report.Outdated))
|
|
}
|
|
if report.Missing > 0 {
|
|
parts = append(parts, fmt.Sprintf("%d missing", report.Missing))
|
|
}
|
|
if report.New > 0 {
|
|
parts = append(parts, fmt.Sprintf("%d new", report.New))
|
|
}
|
|
if report.Untracked > 0 {
|
|
parts = append(parts, fmt.Sprintf("%d untracked", report.Untracked))
|
|
}
|
|
if report.Modified > 0 {
|
|
parts = append(parts, fmt.Sprintf("%d modified", report.Modified))
|
|
}
|
|
|
|
message := fmt.Sprintf("Formulas: %s", strings.Join(parts, ", "))
|
|
|
|
result := &CheckResult{
|
|
Name: c.Name(),
|
|
Status: status,
|
|
Message: message,
|
|
Details: details,
|
|
}
|
|
|
|
if needsFix {
|
|
result.FixHint = "Run 'gt doctor --fix' to update formulas"
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// Fix updates outdated and missing formulas.
|
|
func (c *FormulaCheck) Fix(ctx *CheckContext) error {
|
|
updated, skipped, reinstalled, err := formula.UpdateFormulas(ctx.TownRoot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Log what was done (caller will re-run check to show new status)
|
|
if updated > 0 || reinstalled > 0 || skipped > 0 {
|
|
// The doctor framework will re-run the check after fix
|
|
// so we don't need to log here
|
|
_ = updated
|
|
_ = reinstalled
|
|
_ = skipped
|
|
}
|
|
|
|
return nil
|
|
}
|