Files
gastown/internal/doctor/town_root_branch_check.go
Ryan Snodgrass e1f2bb8b4b feat(ui): import comprehensive UX system from beads
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>
2026-01-09 22:46:06 -08:00

119 lines
3.4 KiB
Go

package doctor
import (
"fmt"
"os/exec"
"strings"
)
// TownRootBranchCheck verifies that the town root directory is on the main branch.
// The town root should always stay on main to avoid confusion and broken gt commands.
// Accidental branch switches can happen when git commands run in the wrong directory.
type TownRootBranchCheck struct {
FixableCheck
currentBranch string // Cached during Run for use in Fix
}
// NewTownRootBranchCheck creates a new town root branch check.
func NewTownRootBranchCheck() *TownRootBranchCheck {
return &TownRootBranchCheck{
FixableCheck: FixableCheck{
BaseCheck: BaseCheck{
CheckName: "town-root-branch",
CheckDescription: "Verify town root is on main branch",
CheckCategory: CategoryCore,
},
},
}
}
// Run checks if the town root is on the main branch.
func (c *TownRootBranchCheck) Run(ctx *CheckContext) *CheckResult {
// Get current branch
cmd := exec.Command("git", "branch", "--show-current")
cmd.Dir = ctx.TownRoot
out, err := cmd.Output()
if err != nil {
// Not a git repo - skip this check (handled by town-git check)
return &CheckResult{
Name: c.Name(),
Status: StatusOK,
Message: "Town root is not a git repository (skipped)",
}
}
branch := strings.TrimSpace(string(out))
c.currentBranch = branch
// Empty branch means detached HEAD
if branch == "" {
return &CheckResult{
Name: c.Name(),
Status: StatusWarning,
Message: "Town root is in detached HEAD state",
Details: []string{
"The town root should be on the main branch",
"Detached HEAD can cause gt commands to fail",
},
FixHint: "Run 'gt doctor --fix' or manually: cd ~/gt && git checkout main",
}
}
// Accept main or master
if branch == "main" || branch == "master" {
return &CheckResult{
Name: c.Name(),
Status: StatusOK,
Message: fmt.Sprintf("Town root is on %s branch", branch),
}
}
// On wrong branch - this is the problem we're trying to prevent
return &CheckResult{
Name: c.Name(),
Status: StatusError,
Message: fmt.Sprintf("Town root is on wrong branch: %s", branch),
Details: []string{
"The town root (~/gt) must stay on main branch",
fmt.Sprintf("Currently on: %s", branch),
"This can cause gt commands to fail (missing rigs.json, etc.)",
"The branch switch was likely accidental (git command in wrong dir)",
},
FixHint: "Run 'gt doctor --fix' or manually: cd ~/gt && git checkout main",
}
}
// Fix switches the town root back to main branch.
func (c *TownRootBranchCheck) Fix(ctx *CheckContext) error {
// Only fix if we're not already on main
if c.currentBranch == "main" || c.currentBranch == "master" {
return nil
}
// Check for uncommitted changes that would block checkout
cmd := exec.Command("git", "status", "--porcelain")
cmd.Dir = ctx.TownRoot
out, err := cmd.Output()
if err != nil {
return fmt.Errorf("failed to check git status: %w", err)
}
if strings.TrimSpace(string(out)) != "" {
return fmt.Errorf("cannot switch to main: uncommitted changes in town root (stash or commit first)")
}
// Switch to main
cmd = exec.Command("git", "checkout", "main")
cmd.Dir = ctx.TownRoot
if err := cmd.Run(); err != nil {
// Try master if main doesn't exist
cmd = exec.Command("git", "checkout", "master")
cmd.Dir = ctx.TownRoot
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to checkout main: %w", err)
}
}
return nil
}