# Fang - CLI Starter Kit Fang is a small, experimental Go library that provides batteries-included styling and enhancements for Cobra-based CLI applications. It transforms standard Cobra commands into beautifully styled, feature-rich command-line interfaces with minimal effort. By wrapping the execution of Cobra commands, Fang automatically adds styled help pages, error handling, version management, manpage generation, and shell completion support. The library leverages lipgloss for terminal styling and provides automatic dark/light theme detection for optimal display across different terminal environments. It includes sensible defaults for common CLI patterns like automatic version flags from build info, silent usage output after errors, and customizable color schemes. Fang is designed to eliminate boilerplate and provide a polished user experience out of the box for CLI applications. ## Core API Functions ### Execute - Main Entry Point Applies Fang styling and features to a Cobra command and executes it with context support. This is the primary function that wraps your Cobra command and adds all Fang functionality including styled help, error handling, version management, manpages, and completions. ```go package main import ( "context" "os" "github.com/charmbracelet/fang" "github.com/spf13/cobra" ) func main() { cmd := &cobra.Command{ Use: "myapp", Short: "A simple example application", Long: "A more detailed description of what the application does.", RunE: func(c *cobra.Command, args []string) error { c.Println("Application executed successfully!") return nil }, } // Execute with default settings if err := fang.Execute(context.Background(), cmd); err != nil { os.Exit(1) } } ``` ### WithVersion - Custom Version String Sets a custom version string instead of using the automatic build info. The version is displayed when the user runs `--version` or `-v` flags. ```go package main import ( "context" "os" "github.com/charmbracelet/fang" "github.com/spf13/cobra" ) func main() { cmd := &cobra.Command{ Use: "myapp", Short: "Application with custom version", } err := fang.Execute( context.Background(), cmd, fang.WithVersion("v1.2.3"), fang.WithCommit("abc1234"), // Adds commit SHA to version ) if err != nil { os.Exit(1) } // Output when running with --version: v1.2.3 (abc1234) } ``` ### WithColorSchemeFunc - Custom Theme Configures a custom color scheme function that receives a light/dark detection function and returns a ColorScheme. This allows for automatic theme adaptation based on terminal background. ```go package main import ( "context" "image/color" "os" "github.com/charmbracelet/fang" "github.com/charmbracelet/lipgloss/v2" "github.com/spf13/cobra" ) func main() { cmd := &cobra.Command{ Use: "myapp", Short: "Application with custom colors", } // Define custom color scheme customScheme := func(c lipgloss.LightDarkFunc) fang.ColorScheme { return fang.ColorScheme{ Base: c(lipgloss.Color("#000000"), lipgloss.Color("#FFFFFF")), Title: lipgloss.Color("#FF6B6B"), Description: c(lipgloss.Color("#4A4A4A"), lipgloss.Color("#CCCCCC")), Program: lipgloss.Color("#4ECDC4"), Command: lipgloss.Color("#95E1D3"), Flag: lipgloss.Color("#F38181"), QuotedString: lipgloss.Color("#AA96DA"), ErrorHeader: [2]color.Color{lipgloss.Color("#000000"), lipgloss.Color("#FF0000")}, } } err := fang.Execute( context.Background(), cmd, fang.WithColorSchemeFunc(customScheme), ) if err != nil { os.Exit(1) } } ``` ### WithErrorHandler - Custom Error Display Provides a custom error handler function to control how errors are displayed to users. The handler receives the writer, styles, and error to format and display. ```go package main import ( "context" "fmt" "io" "os" "github.com/charmbracelet/fang" "github.com/spf13/cobra" ) func main() { cmd := &cobra.Command{ Use: "myapp", Short: "Application with custom error handling", RunE: func(c *cobra.Command, args []string) error { return fmt.Errorf("something went wrong with input: %v", args) }, } customErrorHandler := func(w io.Writer, styles fang.Styles, err error) { fmt.Fprintf(w, "\n") fmt.Fprintf(w, styles.ErrorHeader.Render(" OOPS! ")) fmt.Fprintf(w, "\n\n") fmt.Fprintf(w, styles.ErrorText.Render(err.Error())) fmt.Fprintf(w, "\n\n") fmt.Fprintf(w, "Please check your input and try again.\n") } err := fang.Execute( context.Background(), cmd, fang.WithErrorHandler(customErrorHandler), ) if err != nil { os.Exit(1) } } ``` ### WithNotifySignal - Signal Handling Configures which OS signals should interrupt program execution. The context passed to Execute will be canceled when any of the specified signals are received. ```go package main import ( "context" "os" "time" "github.com/charmbracelet/fang" "github.com/spf13/cobra" ) func main() { cmd := &cobra.Command{ Use: "myapp", Short: "Long-running application with signal handling", RunE: func(c *cobra.Command, args []string) error { c.Println("Starting long operation...") select { case <-time.After(30 * time.Second): c.Println("Operation completed!") case <-c.Context().Done(): c.Println("Operation interrupted!") return c.Context().Err() } return nil }, } err := fang.Execute( context.Background(), cmd, fang.WithNotifySignal(os.Interrupt, os.Kill), ) if err != nil { os.Exit(1) } } ``` ### WithoutCompletions - Disable Shell Completions Disables the automatic shell completion command that Cobra provides by default. Use this when you don't want to expose shell completion functionality. ```go package main import ( "context" "os" "github.com/charmbracelet/fang" "github.com/spf13/cobra" ) func main() { cmd := &cobra.Command{ Use: "myapp", Short: "Application without shell completions", } err := fang.Execute( context.Background(), cmd, fang.WithoutCompletions(), // Removes 'completion' command ) if err != nil { os.Exit(1) } } ``` ### WithoutManpage - Disable Manpage Generation Disables the hidden 'man' command that generates manpages using the mango library. Use this when you don't want manpage generation functionality. ```go package main import ( "context" "os" "github.com/charmbracelet/fang" "github.com/spf13/cobra" ) func main() { cmd := &cobra.Command{ Use: "myapp", Short: "Application without manpage support", } err := fang.Execute( context.Background(), cmd, fang.WithoutManpage(), // Removes hidden 'man' command ) if err != nil { os.Exit(1) } } ``` ### WithoutVersion - Disable Version Flag Skips adding the automatic `--version` and `-v` flags. Use this when you want to implement custom version handling or don't want version information exposed. ```go package main import ( "context" "os" "github.com/charmbracelet/fang" "github.com/spf13/cobra" ) func main() { cmd := &cobra.Command{ Use: "myapp", Short: "Application without version flag", } err := fang.Execute( context.Background(), cmd, fang.WithoutVersion(), // No --version flag available ) if err != nil { os.Exit(1) } } ``` ### Complete Example with Subcommands and Flags A comprehensive example showing how to build a full-featured CLI application with Fang, including subcommands, flags, groups, and examples. ```go package main import ( "context" "errors" "os" "time" "github.com/charmbracelet/fang" "github.com/spf13/cobra" ) func main() { var name string var verbose bool var timeout time.Duration // Root command root := &cobra.Command{ Use: "myapp [command] [flags]", Short: "A comprehensive CLI application", Long: `MyApp is a full-featured command-line tool that demonstrates all the capabilities of Fang including styled help, error handling, and automatic version management.`, Example: `# Run with default settings: myapp # Run with verbose output: myapp --verbose --name=Alice # Use a subcommand: myapp process --timeout=5s data.txt`, RunE: func(c *cobra.Command, args []string) error { if verbose { c.Printf("Running in verbose mode for user: %s\n", name) } c.Println("Root command executed!") return nil }, } // Persistent flags (available to all subcommands) root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose output") // Root-level flags root.Flags().StringVarP(&name, "name", "n", "World", "Name to use in output") root.Flags().DurationVar(&timeout, "timeout", 30*time.Second, "Operation timeout") // Create command groups root.AddGroup(&cobra.Group{ ID: "data", Title: "Data Operations", }) // Add subcommand to group processCmd := &cobra.Command{ Use: "process [file]", Short: "Process data from a file", GroupID: "data", Example: `myapp process input.txt myapp process --timeout=10s large-file.txt`, Args: cobra.ExactArgs(1), RunE: func(c *cobra.Command, args []string) error { file := args[0] c.Printf("Processing file: %s with timeout: %s\n", file, timeout) time.Sleep(time.Second) c.Println("Processing complete!") return nil }, } root.AddCommand(processCmd) // Add another subcommand that returns an error failCmd := &cobra.Command{ Use: "fail", Short: "Demonstrates error handling", GroupID: "data", RunE: func(c *cobra.Command, args []string) error { return errors.New("operation failed due to invalid configuration") }, } root.AddCommand(failCmd) // Execute with Fang err := fang.Execute( context.Background(), root, fang.WithVersion("v2.0.0"), fang.WithCommit("def5678"), fang.WithNotifySignal(os.Interrupt), ) if err != nil { os.Exit(1) } } ``` ## Default Color Schemes ### DefaultColorScheme - Adaptive Theme Returns the default color scheme that automatically adapts to light or dark terminal backgrounds using the Charm color palette. ```go package main import ( "context" "os" "github.com/charmbracelet/fang" "github.com/charmbracelet/lipgloss/v2" "github.com/spf13/cobra" ) func main() { cmd := &cobra.Command{ Use: "myapp", Short: "Application using default adaptive theme", } // Explicitly use the default color scheme err := fang.Execute( context.Background(), cmd, fang.WithColorSchemeFunc(fang.DefaultColorScheme), ) if err != nil { os.Exit(1) } } ``` ### AnsiColorScheme - ANSI Colors Provides a simplified color scheme using basic ANSI colors, suitable for terminals with limited color support or when you want maximum compatibility. ```go package main import ( "context" "os" "github.com/charmbracelet/fang" "github.com/spf13/cobra" ) func main() { cmd := &cobra.Command{ Use: "myapp", Short: "Application using ANSI colors", } // Use basic ANSI colors for maximum compatibility err := fang.Execute( context.Background(), cmd, fang.WithColorSchemeFunc(fang.AnsiColorScheme), ) if err != nil { os.Exit(1) } } ``` ## Summary Fang is ideal for Go developers building CLI applications with Cobra who want professional, styled output without implementing custom help formatters and error handlers. The library excels at creating polished user experiences for developer tools, system utilities, and command-line interfaces where aesthetics and usability matter. Common use cases include package managers, build tools, deployment utilities, and any CLI application that benefits from clear, visually organized help text and error messages. Integration is straightforward: wrap your existing Cobra command tree with `fang.Execute()` instead of calling `cmd.Execute()` directly. Fang handles the rest automatically, including VT processing on Windows, color profile detection, terminal width adaptation, and context-aware styling. The library is extensible through options that allow customizing colors, error handling, and feature flags while maintaining sensible defaults for immediate use. With Fang, CLI applications gain a modern, consistent interface that adapts to user environments and provides familiar patterns like manpage generation and shell completions.