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>
240 lines
6.0 KiB
Go
240 lines
6.0 KiB
Go
package cmd
|
|
|
|
import (
|
|
"cmp"
|
|
"fmt"
|
|
"slices"
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
"github.com/spf13/cobra"
|
|
"github.com/steveyegge/gastown/internal/ui"
|
|
)
|
|
|
|
// Style definitions for thanks output using ui package colors
|
|
var (
|
|
thanksTitleStyle = lipgloss.NewStyle().
|
|
Bold(true).
|
|
Foreground(ui.ColorWarn)
|
|
|
|
thanksSubtitleStyle = lipgloss.NewStyle().
|
|
Foreground(ui.ColorMuted)
|
|
|
|
thanksSectionStyle = lipgloss.NewStyle().
|
|
Foreground(ui.ColorAccent).
|
|
Bold(true)
|
|
|
|
thanksNameStyle = lipgloss.NewStyle().
|
|
Foreground(ui.ColorPass)
|
|
|
|
thanksDimStyle = lipgloss.NewStyle().
|
|
Foreground(ui.ColorMuted)
|
|
)
|
|
|
|
// thanksBoxStyle returns a bordered box style for the thanks header
|
|
func thanksBoxStyle(width int) lipgloss.Style {
|
|
return lipgloss.NewStyle().
|
|
Border(lipgloss.DoubleBorder()).
|
|
BorderForeground(ui.ColorMuted).
|
|
Padding(1, 4).
|
|
Width(width - 4).
|
|
Align(lipgloss.Center)
|
|
}
|
|
|
|
// gastownContributors maps HUMAN contributor names to their commit counts.
|
|
// Agent names (gastown/*, beads/*, lowercase single-word names) are excluded.
|
|
// Generated from: git shortlog -sn --all (then filtered for humans only)
|
|
var gastownContributors = map[string]int{
|
|
"Steve Yegge": 2056,
|
|
"Mike Lady": 19,
|
|
"Olivier Debeuf De Rijcker": 13,
|
|
"Danno Mayer": 11,
|
|
"Dan Shapiro": 7,
|
|
"Subhrajit Makur": 7,
|
|
"Julian Knutsen": 5,
|
|
"Darko Luketic": 4,
|
|
"Martin Emde": 4,
|
|
"Greg Hughes": 3,
|
|
"Avyukth": 2,
|
|
"Ben Kraus": 2,
|
|
"Joshua Vial": 2,
|
|
"Austin Wallace": 1,
|
|
"Cameron Palmer": 1,
|
|
"Chris Sloane": 1,
|
|
"Cong": 1,
|
|
"Dave Laird": 1,
|
|
"Dave Williams": 1,
|
|
"Jacob": 1,
|
|
"Johann Taberlet": 1,
|
|
"Joshua Samuel": 1,
|
|
"Madison Bullard": 1,
|
|
"PepijnSenders": 1,
|
|
"Raymond Weitekamp": 1,
|
|
"Sohail Mohammad": 1,
|
|
"Zachary Rosen": 1,
|
|
}
|
|
|
|
var thanksCmd = &cobra.Command{
|
|
Use: "thanks",
|
|
Short: "Thank the human contributors to Gas Town",
|
|
GroupID: GroupDiag,
|
|
Long: `Display acknowledgments to all the humans who have contributed
|
|
to the Gas Town project. This command celebrates the collaborative
|
|
effort behind the multi-agent workspace manager.`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
printThanksPage()
|
|
},
|
|
}
|
|
|
|
// getContributorsSorted returns contributor names sorted by commit count descending
|
|
func getContributorsSorted() []string {
|
|
names := make([]string, 0, len(gastownContributors))
|
|
for name := range gastownContributors {
|
|
names = append(names, name)
|
|
}
|
|
|
|
slices.SortFunc(names, func(a, b string) int {
|
|
// sort by commit count descending, then by name ascending for ties
|
|
countCmp := cmp.Compare(gastownContributors[b], gastownContributors[a])
|
|
if countCmp != 0 {
|
|
return countCmp
|
|
}
|
|
return cmp.Compare(a, b)
|
|
})
|
|
|
|
return names
|
|
}
|
|
|
|
// printThanksPage renders the complete thanks page
|
|
func printThanksPage() {
|
|
fmt.Println()
|
|
|
|
// get sorted contributors, split into featured (top 20) and rest
|
|
sorted := getContributorsSorted()
|
|
featuredCount := 20
|
|
if len(sorted) < featuredCount {
|
|
featuredCount = len(sorted)
|
|
}
|
|
featured := sorted[:featuredCount]
|
|
additional := sorted[featuredCount:]
|
|
|
|
// calculate content width based on 4 columns
|
|
cols := 4
|
|
contentWidth := calculateColumnsWidth(featured, cols)
|
|
if contentWidth < 60 {
|
|
contentWidth = 60
|
|
}
|
|
|
|
// build header content
|
|
title := thanksTitleStyle.Render("THANK YOU!")
|
|
subtitle := thanksSubtitleStyle.Render("To all the humans who contributed to Gas Town")
|
|
headerContent := title + "\n\n" + subtitle
|
|
|
|
// render header in bordered box
|
|
header := thanksBoxStyle(contentWidth).Render(headerContent)
|
|
fmt.Println(header)
|
|
fmt.Println()
|
|
|
|
// print featured contributors section
|
|
fmt.Println(thanksSectionStyle.Render(" Featured Contributors"))
|
|
fmt.Println()
|
|
printThanksColumns(featured, cols)
|
|
|
|
// print additional contributors if any
|
|
if len(additional) > 0 {
|
|
fmt.Println()
|
|
fmt.Println(thanksSectionStyle.Render(" Additional Contributors"))
|
|
fmt.Println()
|
|
printThanksWrappedList("", additional, contentWidth)
|
|
}
|
|
fmt.Println()
|
|
}
|
|
|
|
// calculateColumnsWidth determines the width needed for n columns of names
|
|
func calculateColumnsWidth(names []string, cols int) int {
|
|
maxWidth := 0
|
|
for _, name := range names {
|
|
if len(name) > maxWidth {
|
|
maxWidth = len(name)
|
|
}
|
|
}
|
|
|
|
// cap at 20 characters per column
|
|
if maxWidth > 20 {
|
|
maxWidth = 20
|
|
}
|
|
|
|
// add padding between columns
|
|
colWidth := maxWidth + 2
|
|
|
|
return colWidth * cols
|
|
}
|
|
|
|
// printThanksColumns prints names in n columns, reading left-to-right
|
|
func printThanksColumns(names []string, cols int) {
|
|
if len(names) == 0 {
|
|
return
|
|
}
|
|
|
|
// find max width for alignment
|
|
maxWidth := 0
|
|
for _, name := range names {
|
|
if len(name) > maxWidth {
|
|
maxWidth = len(name)
|
|
}
|
|
}
|
|
if maxWidth > 20 {
|
|
maxWidth = 20
|
|
}
|
|
colWidth := maxWidth + 2
|
|
|
|
// print in rows, reading left to right (matches bd thanks)
|
|
for i := 0; i < len(names); i += cols {
|
|
fmt.Print(" ")
|
|
for j := 0; j < cols && i+j < len(names); j++ {
|
|
name := names[i+j]
|
|
if len(name) > 20 {
|
|
name = name[:17] + "..."
|
|
}
|
|
// pad BEFORE styling to avoid ANSI code width issues
|
|
padded := fmt.Sprintf("%-*s", colWidth, name)
|
|
fmt.Print(thanksNameStyle.Render(padded))
|
|
}
|
|
fmt.Println()
|
|
}
|
|
}
|
|
|
|
// printThanksWrappedList prints a comma-separated list with word wrapping
|
|
func printThanksWrappedList(label string, names []string, maxWidth int) {
|
|
indent := " "
|
|
|
|
fmt.Print(indent)
|
|
lineLen := len(indent)
|
|
|
|
if label != "" {
|
|
fmt.Print(thanksSectionStyle.Render(label) + " ")
|
|
lineLen += len(label) + 1
|
|
}
|
|
|
|
for i, name := range names {
|
|
suffix := ", "
|
|
if i == len(names)-1 {
|
|
suffix = ""
|
|
}
|
|
entry := name + suffix
|
|
|
|
if lineLen+len(entry) > maxWidth && lineLen > len(indent) {
|
|
fmt.Println()
|
|
fmt.Print(indent)
|
|
lineLen = len(indent)
|
|
}
|
|
|
|
fmt.Print(thanksDimStyle.Render(entry))
|
|
lineLen += len(entry)
|
|
}
|
|
fmt.Println()
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(thanksCmd)
|
|
}
|