mcpField
G
go-cobra

go-cobra

github.com/aaronfloreyDevelopment275 installsUpdated 25w ago
Repository

About go-cobra

A reference skill for building and debugging Go command-line applications with spf13/cobra. It covers command structure, flags, argument validation, completions, documentation generation, and common patterns.

Key features

  1. 01Scaffolds commands and subcommands
  2. 02Configures local and persistent flags
  3. 03Validates positional arguments
  4. 04Covers lifecycle hooks and error handling
  5. 05Generates shell completions and documentation

Use cases

  1. 01Adding commands to a Go application
  2. 02Implementing CLI flags and argument checks
  3. 03Creating shell completions or generated command docs

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/aaronflorey/agent-skills --skill go-cobra

After installing, type/go-cobrain Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
advanced.md
completions.md
docgen.md
viper-integration.md

Details

From the project README

Cobra 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:

  • hugo server --port=1313 — "server" is command, "port" is flag
  • git clone URL --bare — "clone" is command, "URL" is arg, "bare" is flag

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
FieldPurpose
UseUsage pattern: "command [args]"
AliasesAlternative names: []string{"s", "srv"}
ShortBrief description for help listings
LongFull description for --help
ExampleUsage examples (indent with 2 spaces)
RunThe actual command logic
RunESame as Run but returns error
ArgsPositional argument validation
ValidArgsStatic list for shell completion
ValidArgsFunctionDynamic completion function
HiddenHide from help output
DeprecatedMark as deprecated with message
VersionVersion 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
MethodExample
StringVar--name value
StringVarP--name value or -n value
BoolVar--verbose (true) or --verbose=false
IntVar--count 5
StringSliceVar--tag a --tag b or --tag a,b
StringArrayVar--tag a --tag b (no comma splitting)
CountVarP-v (1), -vv (2), -vvv (3)
Required Flags
cmd.Flags().StringVarP(&region, "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:

  1. PersistentPreRun (inherited by children)
  2. PreRun
  3. Run
  4. PostRun
  5. PersistentPostRun (inherited by children)
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 / -h flag added automatically
  • help subcommand added when you have subcommands
  • Typo suggestions: myapp srever → "Did you mean 'server'?"
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:

  • references/completions.md — Shell completion implementation details
  • references/docgen.md — Documentation generation (man, markdown, yaml, rst)
  • references/advanced.md — Advanced patterns and edge cases

Comments

Markdown supported