-
Notifications
You must be signed in to change notification settings - Fork 212
Description
Executive Summary
This analysis examines console output patterns across the gh-aw codebase, focusing on consistency, styling practices, and adherence to modern terminal UI best practices using the Charmbracelet ecosystem (Lipgloss and Huh).
Key Findings:
- ✅ Well-architected console system with 17 specialized formatting functions
- ✅ Excellent Lipgloss integration with adaptive theming and TTY-aware rendering
- ✅ Consistent Huh usage across 13 interactive files with accessibility support
⚠️ Mixed output patterns with 170+ files still using directfmt.*calls- ✅ Centralized styling via
pkg/styles/theme.gowith 30+ pre-configured styles
🎨 Console Package Architecture
Available Formatting Functions (17 total)
Message Types
FormatSuccessMessage()→ ✓ (green) - Operation completed successfullyFormatErrorMessage()→ ✗ (red) - Operation failedFormatInfoMessage()→ ℹ (cyan) - Informational messagesFormatWarningMessage()→ ⚠ (yellow) - Warnings and cautions
Contextual Formatters
FormatLocationMessage()→ 📁 - File paths and locationsFormatCommandMessage()→ ⚡ - Commands being executedFormatProgressMessage()→ 🔨 - Activity in progressFormatPromptMessage()→ ❓ - User input promptsFormatCountMessage()→ 📊 - Metrics and countsFormatVerboseMessage()→ 🔍 - Debug/verbose output
Structural Elements
FormatSectionHeader()- Section dividersFormatListHeader()- List titlesFormatListItem()- Bullet point itemsFormatBanner()- ASCII bannersFormatFileSize()- Human-readable sizesFormatErrorWithSuggestions()- Errors with actionable fixesFormatError()- IDE-parseable compiler errors
Design Principles
TTY-Aware Rendering:
// ✅ GOOD - Automatically adapts to terminal capabilities
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Compiled successfully"))
// Output (TTY): ✅ Compiled successfully (with color)
// Output (pipe): ✅ Compiled successfully (no ANSI codes)Adaptive Theming:
All colors use lipgloss.AdaptiveColor for light/dark terminal backgrounds:
// From pkg/styles/theme.go
SuccessColor = lipgloss.AdaptiveColor{
Light: "#43A047", // Darker green for light terminals
Dark: "#66BB6A", // Brighter green for dark terminals
}📊 Current Usage Patterns
Files by Output Method
| Pattern | Count | Location | Status |
|---|---|---|---|
console.Format*() |
100+ | pkg/cli/, pkg/workflow/, pkg/parser/ |
✅ Correct |
Direct fmt.* |
170+ | Widespread | |
| Lipgloss styling | 5 core | pkg/console/, pkg/styles/ |
✅ Excellent |
| Huh forms | 13 | pkg/console/, pkg/cli/*interactive* |
✅ Consistent |
Console Formatter Adoption
High adoption areas (✅ Good):
pkg/cli/- Commands properly useFormatSuccessMessage,FormatErrorMessagepkg/workflow/- Compilation output usesFormatInfoMessage,FormatWarningMessagepkg/parser/- Validation errors useFormatErrorWithSuggestions
Legacy fmt usage (
pkg/logger/- Debug output uses directfmt.Fprintf(os.Stderr, ...)- Various utilities - Simple logging still uses
fmt.Printf/fmt.Println - Test output - Intentionally uses raw fmt for test diagnostics
🎯 Lipgloss Integration Analysis
Centralized Theme System
pkg/styles/theme.go serves as the single source of truth:
- 30+ pre-configured
lipgloss.Styleobjects - Consistent spacing (1-2 char padding)
- Border styles: Rounded for tables, Normal for info boxes
- Adaptive colors for all text and backgrounds
Example: Table Styling
// From pkg/styles/theme.go
TableHeaderStyle = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.AdaptiveColor{Light: "#1A1A1A", Dark: "#F8F8F2"}).
Background(lipgloss.AdaptiveColor{Light: "#E0E0E0", Dark: "#44475A"}).
Padding(0, 1)Best Practices Observed
✅ Excellent patterns found:
- TTY Detection - All styled output checks
isTTY()before applying ANSI codes - Accessibility Mode - Respects
ACCESSIBLEenv var, disables animations - Layout Composition - Uses
lipgloss.JoinHorizontal/Verticalfor complex layouts - Border Consistency - Rounded borders for data tables, subtle borders for info sections
- Zebra Striping - Alternating row colors for table readability
Example: Conditional Rendering
// From pkg/console/render.go
func applyStyle(style lipgloss.Style, text string) string {
if !isTTY() {
return text // No ANSI codes when piped
}
return style.Render(text)
}Areas for Enhancement
- A few utility functions still construct colors manually
- Opportunity to migrate to Lipgloss for consistency
- Some manual table formatting could use
github.com/charmbracelet/lipgloss/table - Would improve consistency across different table outputs
🗳️ Huh Forms Analysis
Interactive Command Pattern
Consistent structure across 8 CLI interactive commands:
// From pkg/console/form.go - Reusable abstraction
func NewForm(groups ...*huh.Group) *huh.Form {
return huh.NewForm(groups...).
WithAccessible(IsAccessibleMode()). // ✅ Accessibility support
WithTheme(GetHuhTheme()) // ✅ Consistent theming
}Form Types Used
| Command | Fields | Validation | Notes |
|---|---|---|---|
init |
Input, Select | ✅ | Workflow name, engine selection |
add engine |
Select, Confirm | ✅ | Engine type, auth method |
add auth |
Input, Select | ✅ | Secret name, type selection |
add workflow |
Input, Confirm | ✅ | Workflow name, template |
add orchestrator |
Select, MultiSelect | ✅ | Provider, features |
add git |
Input, Select | ✅ | Remote, branch patterns |
Best Practices Observed
✅ Excellent patterns:
- Centralized form creation -
pkg/console/form.goprovides reusable builders - Accessibility built-in - All forms use
WithAccessible() - Consistent theming - Single
GetHuhTheme()function for uniform look - Validation - All input fields have
.Validate()callbacks - Error handling - Forms return actionable errors on invalid input
Example: Input Validation
// From pkg/cli/add_interactive_workflow.go
huh.NewInput().
Title("Workflow name").
Validate(func(s string) error {
if s == "" {
return fmt.Errorf("workflow name cannot be empty")
}
if !isValidWorkflowName(s) {
return fmt.Errorf("invalid characters in workflow name")
}
return nil
})Enhancement Opportunities
- Some simple prompts (
pkg/console/input.go) could be migrated to Huh for consistency - Consider
huh.FilePickerfor file selection inadd workflow --template - Multi-step flows could benefit from progress indicators between form groups
🔍 Anti-Patterns & Remediation
1. Direct fmt.Print* Usage
❌ Anti-pattern found in some files:
// From various utility files
fmt.Printf("Processing workflow...\n")
fmt.Println("Error: invalid configuration")✅ Should be:
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Processing workflow..."))
fmt.Fprintln(os.Stderr, console.FormatErrorMessage("Invalid configuration"))Impact: Inconsistent styling, no TTY detection, no adaptive colors
2. Manual ANSI Escape Sequences
❌ Anti-pattern (rare, but found):
fmt.Printf("\033[31mError\033[0m: %s\n", msg)✅ Should be:
fmt.Fprintln(os.Stderr, console.FormatErrorMessage(msg))
// Or use Lipgloss directly:
errorStyle := styles.ErrorStyle // from pkg/styles/theme.go
fmt.Fprintln(os.Stderr, errorStyle.Render("Error: " + msg))Impact: Hardcoded colors, no theme support, breaks when piped
3. Stdout vs Stderr Confusion
❌ Anti-pattern found:
fmt.Println("Processing...") // Diagnostic output to stdout✅ Should be:
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Processing..."))Impact: Mixes diagnostic output with structured data (JSON, hashes), breaks piping
4. Inconsistent Table Formatting
Some commands manually format tables with string padding/alignment. Could migrate to:
lipgloss/tablepackage for consistent rendering- Centralized table builder in
pkg/console/table.go(already exists!)
📋 Recommendations
Priority 1: High Impact, Low Effort
-
Audit fmt.Printf/Println usage - Create linting rule to flag:
fmt.Printf→ suggestfmt.Fprintf(os.Stderr, ...)fmt.Println→ suggestfmt.Fprintln(os.Stderr, console.Format*())
-
Document output routing - Add to
DEVGUIDE.md:**Output Routing:** - Diagnostic messages → stderr with console.Format*() - Structured data (JSON, hashes) → stdout - Never mix diagnostics on stdout
-
Standardize error output - Ensure all error paths use
FormatErrorMessage:# Find direct error printing grep -rn 'fmt.Printf.*error' pkg/ --include="*.go" | grep -v '_test.go'
Priority 2: Consistency Improvements
-
Migrate manual tables - Replace string-based tables with
pkg/console/table.gohelpers -
Consolidate themes - Ensure all Lipgloss styles come from
pkg/styles/theme.go -
Add linting - Consider golangci-lint custom rules:
- Forbid
\033[ANSI sequences (use Lipgloss instead) - Flag
fmt.Printlnin non-test files - Require console package import when using stderr output
- Forbid
Priority 3: Enhancement Opportunities
-
Expand Huh usage - Migrate simple prompts to Huh forms:
pkg/console/input.go→ usehuh.NewInput()- File selection → use
huh.FilePicker
-
Table rendering library - Evaluate
github.com/charmbracelet/lipgloss/table:- More consistent borders, padding, colors
- Automatic row striping
- Responsive width handling
-
Progress indicators - For long operations:
- Use
console.NewSpinner()(already exists!) - Consider
github.com/charmbracelet/bubbles/progressfor longer tasks
- Use
🎯 Success Metrics
To measure console output quality over time:
| Metric | Current | Target | Check Command |
|---|---|---|---|
Files using console.Format* |
~100 | 150+ | grep -r "console.Format" pkg/ | wc -l |
Direct fmt.Println usage |
170+ | <50 | grep -r "fmt.Println" pkg/ --include="*.go" | grep -v test | wc -l |
| Manual ANSI sequences | ~5 | 0 | grep -r "\\033\[" pkg/ --include="*.go" | wc -l |
| Huh form coverage | 13 | 20+ | grep -r "huh.New" pkg/ | wc -l |
| Lipgloss files | 5 core | 10+ | grep -r "lipgloss.New" pkg/ --include="*.go" | wc -l |
🏆 Exemplary Files
These files demonstrate excellent console output practices:
pkg/console/console.go- Perfect use of Lipgloss, TTY detection, adaptive colorspkg/console/form.go- Reusable Huh form builders with accessibilitypkg/styles/theme.go- Centralized theme management, comprehensive stylingpkg/cli/compile_command.go- Proper console formatter usage throughoutpkg/cli/init_interactive.go- Multi-step Huh forms with validation
Study these files when implementing new console output.
🔧 Quick Reference Card
Console Output Cheat Sheet
// ✅ CORRECT PATTERNS
import (
"fmt"
"os"
"github.com/github/gh-aw/pkg/console"
)
// Success
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Operation completed"))
// Error
fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error()))
// Info
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Processing..."))
// Warning
fmt.Fprintln(os.Stderr, console.FormatWarningMessage("File has uncommitted changes"))
// Structured output (stdout only)
fmt.Println(string(jsonBytes)) // JSON
fmt.Println(hash) // HashesInteractive Forms Cheat Sheet
// ✅ CORRECT PATTERNS
import (
"github.com/charmbracelet/huh"
"github.com/github/gh-aw/pkg/console"
)
// Simple input
form := console.NewForm(
huh.NewGroup(
huh.NewInput().
Title("Workflow name").
Value(&workflowName).
Validate(validateWorkflowName),
),
)
err := form.Run()
// Selection
form := console.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Title("Choose engine").
Options(
huh.NewOption("Copilot", "copilot"),
huh.NewOption("Claude", "claude"),
).
Value(&engine),
),
)Styling Cheat Sheet
// ✅ CORRECT PATTERNS
import (
"github.com/github/gh-aw/pkg/styles"
"github.com/charmbracelet/lipgloss"
)
// Use centralized styles
fmt.Fprintln(os.Stderr, styles.ErrorStyle.Render("Critical error"))
fmt.Fprintln(os.Stderr, styles.SuccessStyle.Render("All tests passed"))
// Build custom styles with adaptive colors
customStyle := lipgloss.NewStyle().
Foreground(styles.SuccessColor). // From theme.go
Bold(true).
Padding(0, 1)
// TTY-aware rendering
if console.IsTTY() {
fmt.Fprintln(os.Stderr, customStyle.Render(text))
} else {
fmt.Fprintln(os.Stderr, text) // Plain text when piped
}📚 Additional Resources
- Lipgloss Documentation: https://github.com/charmbracelet/lipgloss
- Huh Documentation: https://github.com/charmbracelet/huh
- Console Package README:
pkg/console/README.md - Styles Theme:
pkg/styles/theme.go - Developer Guide:
DEVGUIDE.md(logging guidelines)
Analysis Date: 2026-02-15
Agent: Terminal Stylist
Repository: github/gh-aw
Files Analyzed: 250+ Go source files in pkg/
Next Steps: Review recommendations, prioritize based on team capacity, and consider adding linting rules to enforce console output best practices.
Note: This was intended to be a discussion, but discussions could not be created due to permissions issues. This issue was created as a fallback.
Tip: Discussion creation may fail if the specified category is not announcement-capable. Consider using the "Announcements" category or another announcement-capable category in your workflow configuration.
Generated by Terminal Stylist
- expires on Feb 22, 2026, 10:27 PM UTC