refactor(shared): add git status styles and check helpers

Add to shared package:
- Git status styles (dirty/ahead/behind/clean/conflict)
- CheckMark() helper for presence indicators (✓/—)
- Label() helper for key-value labels
- CheckResult() helper for environment check output

Update packages to use new shared utilities:
- cmd/dev: use shared git styles for table cells
- cmd/docs: use CheckMark() and Label() helpers
- cmd/doctor: use CheckResult() and Success()/Error() helpers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-01-30 01:09:03 +00:00
parent 1b76d4c145
commit 4fa0e0310b
5 changed files with 102 additions and 64 deletions

View file

@ -45,22 +45,12 @@ var (
repoNameStyle = shared.RepoNameStyle
)
// Table styles for status display
// Table styles for status display (with padding for table cells)
var (
cellStyle = lipgloss.NewStyle().
Padding(0, 1)
dirtyStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#ef4444")). // red-500
Padding(0, 1)
aheadStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#22c55e")). // green-500
Padding(0, 1)
cleanStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#6b7280")). // gray-500
Padding(0, 1)
cellStyle = lipgloss.NewStyle().Padding(0, 1)
dirtyStyle = shared.GitDirtyStyle.Padding(0, 1)
aheadStyle = shared.GitAheadStyle.Padding(0, 1)
cleanStyle = shared.GitCleanStyle.Padding(0, 1)
)
// AddCommands registers the 'dev' command and all subcommands.

View file

@ -2,31 +2,21 @@
package docs
import (
"github.com/charmbracelet/lipgloss"
"github.com/host-uk/core/cmd/shared"
"github.com/spf13/cobra"
)
// Style and utility aliases from shared
var (
repoNameStyle = shared.RepoNameStyle
successStyle = shared.SuccessStyle
errorStyle = shared.ErrorStyle
dimStyle = shared.DimStyle
headerStyle = shared.HeaderStyle
confirm = shared.Confirm
)
// Package-specific styles
var (
docsFoundStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#22c55e")) // green-500
docsMissingStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#6b7280")) // gray-500
docsFileStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#3b82f6")) // blue-500
repoNameStyle = shared.RepoNameStyle
successStyle = shared.SuccessStyle
errorStyle = shared.ErrorStyle
dimStyle = shared.DimStyle
headerStyle = shared.HeaderStyle
confirm = shared.Confirm
docsFoundStyle = shared.SuccessStyle
docsMissingStyle = shared.DimStyle
docsFileStyle = shared.InfoStyle
)
var docsCmd = &cobra.Command{

View file

@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"github.com/host-uk/core/cmd/shared"
"github.com/spf13/cobra"
)
@ -41,22 +42,11 @@ func runDocsList(registryPath string) error {
for _, repo := range reg.List() {
info := scanRepoDocs(repo)
readme := docsMissingStyle.Render("—")
if info.Readme != "" {
readme = docsFoundStyle.Render("✓")
}
readme := shared.CheckMark(info.Readme != "")
claude := shared.CheckMark(info.ClaudeMd != "")
changelog := shared.CheckMark(info.Changelog != "")
claude := docsMissingStyle.Render("—")
if info.ClaudeMd != "" {
claude = docsFoundStyle.Render("✓")
}
changelog := docsMissingStyle.Render("—")
if info.Changelog != "" {
changelog = docsFoundStyle.Render("✓")
}
docsDir := docsMissingStyle.Render("—")
docsDir := shared.CheckMark(false)
if len(info.DocsFiles) > 0 {
docsDir = docsFoundStyle.Render(fmt.Sprintf("%d files", len(info.DocsFiles)))
}
@ -78,7 +68,7 @@ func runDocsList(registryPath string) error {
fmt.Println()
fmt.Printf("%s %d with docs, %d without\n",
dimStyle.Render("Coverage:"),
shared.Label("Coverage"),
withDocs,
withoutDocs,
)

View file

@ -43,14 +43,14 @@ func runDoctor(verbose bool) error {
for _, c := range requiredChecks {
ok, version := runCheck(c)
if ok {
if verbose && version != "" {
fmt.Printf(" %s %s %s\n", successStyle.Render("✓"), c.name, dimStyle.Render(version))
if verbose {
fmt.Println(shared.CheckResult(true, c.name, version))
} else {
fmt.Printf(" %s %s\n", successStyle.Render("✓"), c.name)
fmt.Println(shared.CheckResult(true, c.name, ""))
}
passed++
} else {
fmt.Printf(" %s %s - %s\n", errorStyle.Render("✗"), c.name, c.description)
fmt.Printf(" %s %s - %s\n", errorStyle.Render(shared.SymbolCross), c.name, c.description)
failed++
}
}
@ -60,14 +60,14 @@ func runDoctor(verbose bool) error {
for _, c := range optionalChecks {
ok, version := runCheck(c)
if ok {
if verbose && version != "" {
fmt.Printf(" %s %s %s\n", successStyle.Render("✓"), c.name, dimStyle.Render(version))
if verbose {
fmt.Println(shared.CheckResult(true, c.name, version))
} else {
fmt.Printf(" %s %s\n", successStyle.Render("✓"), c.name)
fmt.Println(shared.CheckResult(true, c.name, ""))
}
passed++
} else {
fmt.Printf(" %s %s - %s\n", dimStyle.Render("○"), c.name, dimStyle.Render(c.description))
fmt.Printf(" %s %s - %s\n", dimStyle.Render(shared.SymbolSkip), c.name, dimStyle.Render(c.description))
optional++
}
}
@ -75,16 +75,16 @@ func runDoctor(verbose bool) error {
// Check GitHub access
fmt.Println("\nGitHub Access:")
if checkGitHubSSH() {
fmt.Printf(" %s SSH key found\n", successStyle.Render("✓"))
fmt.Println(shared.CheckResult(true, "SSH key found", ""))
} else {
fmt.Printf(" %s SSH key missing - run: ssh-keygen && gh ssh-key add\n", errorStyle.Render("✗"))
fmt.Printf(" %s SSH key missing - run: ssh-keygen && gh ssh-key add\n", errorStyle.Render(shared.SymbolCross))
failed++
}
if checkGitHubCLI() {
fmt.Printf(" %s CLI authenticated\n", successStyle.Render("✓"))
fmt.Println(shared.CheckResult(true, "CLI authenticated", ""))
} else {
fmt.Printf(" %s CLI authentication - run: gh auth login\n", errorStyle.Render("✗"))
fmt.Printf(" %s CLI authentication - run: gh auth login\n", errorStyle.Render(shared.SymbolCross))
failed++
}
@ -95,12 +95,12 @@ func runDoctor(verbose bool) error {
// Summary
fmt.Println()
if failed > 0 {
fmt.Printf("%s %d issues found\n", errorStyle.Render("Doctor:"), failed)
fmt.Println(shared.Error(fmt.Sprintf("Doctor: %d issues found", failed)))
fmt.Println("\nInstall missing tools:")
printInstallInstructions()
return fmt.Errorf("%d required tools missing", failed)
}
fmt.Printf("%s Environment ready\n", successStyle.Render("Doctor:"))
fmt.Println(shared.Success("Doctor: Environment ready"))
return nil
}

View file

@ -240,6 +240,27 @@ var (
SeverityLowStyle = lipgloss.NewStyle().Foreground(ColourGray500)
)
// ─────────────────────────────────────────────────────────────────────────────
// Git Status Styles (for repo state indicators)
// ─────────────────────────────────────────────────────────────────────────────
var (
// GitDirtyStyle for uncommitted changes (red).
GitDirtyStyle = lipgloss.NewStyle().Foreground(ColourRed500)
// GitAheadStyle for unpushed commits (green).
GitAheadStyle = lipgloss.NewStyle().Foreground(ColourGreen500)
// GitBehindStyle for unpulled commits (amber).
GitBehindStyle = lipgloss.NewStyle().Foreground(ColourAmber500)
// GitCleanStyle for clean state (gray).
GitCleanStyle = lipgloss.NewStyle().Foreground(ColourGray500)
// GitConflictStyle for merge conflicts (red, bold).
GitConflictStyle = lipgloss.NewStyle().Bold(true).Foreground(ColourRed500)
)
// ─────────────────────────────────────────────────────────────────────────────
// Box Styles (for bordered content)
// ─────────────────────────────────────────────────────────────────────────────
@ -332,6 +353,53 @@ func StatusText(text string, style lipgloss.Style) string {
return style.Render(text)
}
// CheckMark returns a styled checkmark (✓) or dash (—) based on presence.
// Useful for showing presence/absence in tables and lists.
func CheckMark(present bool) string {
if present {
return SuccessStyle.Render(SymbolCheck)
}
return DimStyle.Render("—")
}
// CheckMarkCustom returns a styled indicator with custom symbols and styles.
func CheckMarkCustom(present bool, presentStyle, absentStyle lipgloss.Style, presentSymbol, absentSymbol string) string {
if present {
return presentStyle.Render(presentSymbol)
}
return absentStyle.Render(absentSymbol)
}
// Label returns a styled label for key-value display.
// Example: Label("Status") -> "Status:" in dim gray
func Label(text string) string {
return KeyStyle.Render(text + ":")
}
// LabelValue returns a styled "label: value" pair.
// Example: LabelValue("Branch", "main") -> "Branch: main"
func LabelValue(label, value string) string {
return fmt.Sprintf("%s %s", Label(label), value)
}
// LabelValueStyled returns a styled "label: value" pair with custom value style.
func LabelValueStyled(label, value string, valueStyle lipgloss.Style) string {
return fmt.Sprintf("%s %s", Label(label), valueStyle.Render(value))
}
// CheckResult formats a check result with name and optional version.
// Used for environment checks like `✓ go 1.22.0` or `✗ docker`.
func CheckResult(ok bool, name string, version string) string {
symbol := ErrorStyle.Render(SymbolCross)
if ok {
symbol = SuccessStyle.Render(SymbolCheck)
}
if version != "" {
return fmt.Sprintf(" %s %s %s", symbol, name, DimStyle.Render(version))
}
return fmt.Sprintf(" %s %s", symbol, name)
}
// Bullet returns a bulleted item.
func Bullet(text string) string {
return fmt.Sprintf(" %s %s", DimStyle.Render(SymbolBullet), text)