Learn-skills.dev go-cobra
Write, scaffold, and debug Go CLI applications using the spf13/cobra library. Use this skill whenever the user wants to build a command-line interface in Go, add commands/subcommands, work with flags (persistent, local, required), implement shell completions, generate documentation, or asks anything about cobra-based CLI development.
git clone https://github.com/NeverSight/learn-skills.dev
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/aaronflorey/agent-skills/go-cobra" ~/.claude/skills/neversight-learn-skills-dev-go-cobra && rm -rf "$T"
data/skills-md/aaronflorey/agent-skills/go-cobra/SKILL.mdCobra CLI Framework Skill
Write, scaffold, and debug Go CLI applications using the spf13/cobra library. Use this skill whenever the user wants to build a command-line interface in Go, add commands/subcommands to a CLI, work with flags (persistent, local, required), implement shell completions, generate documentation, or asks anything about cobra-based CLI development -- even if they just say "I want to build a CLI tool" or "how do I add commands to my Go app".
Quick Start
Installation
go get -u github.com/spf13/cobra@latest
For the CLI generator (optional but recommended for scaffolding):
go install github.com/spf13/cobra-cli@latest
Project Structure
myapp/ cmd/ root.go # Root command and Execute() version.go # Additional commands serve.go main.go # Just calls cmd.Execute()
Minimal main.go
package main import "myapp/cmd" func main() { cmd.Execute() }
Minimal root.go
package cmd import ( "fmt" "os" "github.com/spf13/cobra" ) var rootCmd = &cobra.Command{ Use: "myapp", Short: "A brief description of your application", Long: `A longer description that spans multiple lines.`, Run: func(cmd *cobra.Command, args []string) { fmt.Println("Hello from myapp!") }, } func Execute() { if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }
Core Concepts
Commands represent actions. Args are things. Flags modify behavior.
Pattern:
APPNAME COMMAND ARG --FLAG
Examples:
— "server" is command, "port" is flaghugo server --port=1313
— "clone" is command, "URL" is arg, "bare" is flaggit clone URL --bare
Command Structure
Creating Commands
var serveCmd = &cobra.Command{ Use: "serve", // How to call it Short: "Start the server", // One-line help Long: `Detailed description here...`, // Full help text Example: ` myapp serve --port 8080`, // Usage examples Run: func(cmd *cobra.Command, args []string) { // Command logic here }, } func init() { rootCmd.AddCommand(serveCmd) }
Key Command Fields
| Field | Purpose |
|---|---|
| Usage pattern: |
| Alternative names: |
| Brief description for help listings |
| Full description for |
| Usage examples (indent with 2 spaces) |
| The actual command logic |
| Same as Run but returns error |
| Positional argument validation |
| Static list for shell completion |
| Dynamic completion function |
| Hide from help output |
| Mark as deprecated with message |
| Version string (enables --version) |
Run vs RunE
Use
RunE when you need to return errors to the caller:
var tryCmd = &cobra.Command{ Use: "try", Short: "Try something", RunE: func(cmd *cobra.Command, args []string) error { if err := doSomething(); err != nil { return err // Error handled by Cobra } return nil }, }
Flags
Flag Types
Persistent Flags — Available to this command AND all subcommands:
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file path") rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
Local Flags — Only for this specific command:
serveCmd.Flags().IntVarP(&port, "port", "p", 8080, "port to listen on") serveCmd.Flags().StringVar(&host, "host", "localhost", "host address")
Flag Methods
| Method | Example |
|---|---|
| |
| or |
| (true) or |
| |
| or |
| (no comma splitting) |
| (1), (2), (3) |
Required Flags
cmd.Flags().StringVarP(®ion, "region", "r", "", "AWS region") cmd.MarkFlagRequired("region") // For persistent flags cmd.MarkPersistentFlagRequired("config")
Flag Groups
// Must be used together cmd.MarkFlagsRequiredTogether("username", "password") // Cannot be used together cmd.MarkFlagsMutuallyExclusive("json", "yaml") // At least one required cmd.MarkFlagsOneRequired("json", "yaml") // Exactly one required (combine both) cmd.MarkFlagsOneRequired("json", "yaml") cmd.MarkFlagsMutuallyExclusive("json", "yaml")
Count Flags (Verbosity Pattern)
var verbose int func init() { rootCmd.PersistentFlags().CountVarP(&verbose, "verbose", "v", "verbosity (-v, -vv, -vvv)") } // Usage: // myapp -v → verbose = 1 // myapp -vv → verbose = 2 // myapp -vvv → verbose = 3
Positional Arguments
Built-in Validators
cmd.Args = cobra.NoArgs // No args allowed cmd.Args = cobra.ArbitraryArgs // Any args (default) cmd.Args = cobra.MinimumNArgs(1) // At least N cmd.Args = cobra.MaximumNArgs(3) // At most N cmd.Args = cobra.ExactArgs(2) // Exactly N cmd.Args = cobra.RangeArgs(1, 3) // Between min and max cmd.Args = cobra.OnlyValidArgs // Only from ValidArgs list
Combining Validators
cmd.Args = cobra.MatchAll(cobra.ExactArgs(2), cobra.OnlyValidArgs)
Custom Validator
cmd.Args = func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return fmt.Errorf("requires at least 1 arg") } if !isValidInput(args[0]) { return fmt.Errorf("invalid argument: %s", args[0]) } return nil }
Lifecycle Hooks
Hooks execute in this order:
(inherited by children)PersistentPreRunPreRunRunPostRun
(inherited by children)PersistentPostRun
var rootCmd = &cobra.Command{ Use: "app", PersistentPreRun: func(cmd *cobra.Command, args []string) { // Runs before every command (including subcommands) initLogging() }, PersistentPostRun: func(cmd *cobra.Command, args []string) { // Runs after every command cleanup() }, } var subCmd = &cobra.Command{ Use: "sub", PreRun: func(cmd *cobra.Command, args []string) { // Only for this command, before Run }, Run: func(cmd *cobra.Command, args []string) { // Main logic }, }
Use
*RunE variants to return errors:
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { return initConfig() }
Viper Integration
Cobra integrates seamlessly with Viper for configuration management. See references/viper-integration.md for complete documentation.
import "github.com/spf13/viper" func init() { cobra.OnInitialize(initConfig) rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file") rootCmd.PersistentFlags().String("author", "Me", "author name") // Bind flag to viper - priority: flag > env > config > default viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")) } func initConfig() { if cfgFile != "" { viper.SetConfigFile(cfgFile) } else { home, _ := os.UserHomeDir() viper.AddConfigPath(home) viper.SetConfigType("yaml") viper.SetConfigName(".myapp") } viper.AutomaticEnv() viper.ReadInConfig() } // IMPORTANT: Use viper.GetString("author"), not the flag variable // Flag variables are NOT updated from config file values
Help & Usage
Automatic Features
/--help
flag added automatically-h
subcommand added when you have subcommandshelp- Typo suggestions:
→ "Did you mean 'server'?"myapp srever
Customization
// Custom help template cmd.SetHelpTemplate(`Custom help: {{.Use}}`) // Custom usage template cmd.SetUsageTemplate(`Usage: {{.UseLine}}`) // Custom help function cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { fmt.Println("Custom help!") }) // Disable suggestions cmd.DisableSuggestions = true // Adjust suggestion distance cmd.SuggestionsMinimumDistance = 1
Version Flag
var rootCmd = &cobra.Command{ Use: "myapp", Version: "1.0.0", // Enables --version flag } // Custom version template cmd.SetVersionTemplate(`Version: {{.Version}}`)
Grouping Commands
rootCmd.AddGroup(&cobra.Group{ID: "core", Title: "Core Commands:"}) rootCmd.AddGroup(&cobra.Group{ID: "util", Title: "Utility Commands:"}) serveCmd.GroupID = "core" versionCmd.GroupID = "util"
Shell Completions
Cobra automatically provides a
completion command. For detailed shell completion implementation, see references/completions.md.
Quick usage:
# Bash source <(myapp completion bash) # Zsh myapp completion zsh > "${fpath[1]}/_myapp" # Fish myapp completion fish > ~/.config/fish/completions/myapp.fish # PowerShell myapp completion powershell | Out-String | Invoke-Expression
Dynamic Completions
cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) { if len(args) != 0 { return nil, cobra.ShellCompDirectiveNoFileComp } return []cobra.Completion{"option1", "option2"}, cobra.ShellCompDirectiveNoFileComp }
Documentation Generation
Cobra can generate man pages, markdown, and more. See
references/docgen.md for details.
Quick example:
import "github.com/spf13/cobra/doc" // Markdown doc.GenMarkdownTree(rootCmd, "./docs") // Man pages header := &doc.GenManHeader{Title: "MYAPP", Section: "1"} doc.GenManTree(rootCmd, header, "./man")
Creating Plugins
For tools like kubectl that use plugins (
kubectl-myplugin called as kubectl myplugin):
rootCmd := &cobra.Command{ Use: "kubectl-myplugin", Annotations: map[string]string{ cobra.CommandDisplayNameAnnotation: "kubectl myplugin", }, }
Global Settings
// Enable case-insensitive command matching cobra.EnableCaseInsensitive = true // Enable prefix matching (dangerous for production) cobra.EnablePrefixMatching = true // Execute all parent hooks (not just first found) cobra.EnableTraverseRunHooks = true // Disable command sorting in help cobra.EnableCommandSorting = false
Testing Commands
func TestServeCommand(t *testing.T) { cmd := rootCmd // Capture output buf := new(bytes.Buffer) cmd.SetOut(buf) cmd.SetErr(buf) // Set args cmd.SetArgs([]string{"serve", "--port", "9000"}) // Execute err := cmd.Execute() if err != nil { t.Fatalf("unexpected error: %v", err) } // Check output if !strings.Contains(buf.String(), "expected") { t.Errorf("unexpected output: %s", buf.String()) } }
Context Support
var rootCmd = &cobra.Command{ Use: "myapp", Run: func(cmd *cobra.Command, args []string) { ctx := cmd.Context() // Use context for cancellation, timeouts, etc. }, } // Execute with context ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() rootCmd.ExecuteContext(ctx)
Common Patterns
Subcommand Organization
For large apps, organize subcommands in separate packages:
cmd/ root.go config/ config.go # Defines configCmd get.go # config get set.go # config set
// cmd/config/config.go var ConfigCmd = &cobra.Command{Use: "config", Short: "Manage configuration"} func init() { ConfigCmd.AddCommand(getCmd) ConfigCmd.AddCommand(setCmd) } // cmd/root.go import "myapp/cmd/config" func init() { rootCmd.AddCommand(config.ConfigCmd) }
Error Handling
func Execute() { if err := rootCmd.Execute(); err != nil { // Error already printed by Cobra os.Exit(1) } } // To silence automatic error printing: rootCmd.SilenceErrors = true rootCmd.SilenceUsage = true
Silent/Quiet Mode
var quiet bool func init() { rootCmd.PersistentFlags().BoolVarP(&quiet, "quiet", "q", false, "suppress output") } func output(msg string) { if !quiet { fmt.Println(msg) } }
Reference Files
For more detailed information on specific topics:
— Shell completion implementation detailsreferences/completions.md
— Documentation generation (man, markdown, yaml, rst)references/docgen.md
— Advanced patterns and edge casesreferences/advanced.md