| name | Cobra CLI |
| description | This skill should be used when the user asks to "create CLI", "setup Cobra", "add commands", "subcommands", "CLI flags", "Cobra with Viper", or works with command-line interface development in Go. |
| version | 0.1.0 |
Cobra CLI
Cobra is a library for creating powerful modern CLI applications in Go.
Core Concepts
Project Structure
Standard Cobra project layout:
cmd/
├── root.go # Root command and shared flags
├── serve.go # Serve subcommand
├── config.go # Config subcommand
└── version.go # Version subcommand
main.go # Entry point
Root Command Setup
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
var rootCmd = &cobra.Command{
Use: "myapp",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application.`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
},
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.myapp.yaml)")
rootCmd.PersistentFlags().String("log-level", "info", "Log level (debug, info, warn, error)")
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
viper.BindPFlag("log-level", rootCmd.PersistentFlags().Lookup("log-level"))
}
func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
home, _ := os.UserHomeDir()
viper.AddConfigPath(home)
viper.SetConfigName(".myapp")
}
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}
Subcommands
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the server",
Long: `Start the HTTP server on the specified port.`,
Run: runServe,
}
var (
serverPort int
serverHost string
enableTLS bool
)
func init() {
rootCmd.AddCommand(serveCmd)
serveCmd.Flags().IntVarP(&serverPort, "port", "p", 8080, "Server port")
serveCmd.Flags().StringVarP(&serverHost, "host", "H", "0.0.0.0", "Server host")
serveCmd.Flags().BoolVarP(&enableTLS, "tls", "t", false, "Enable TLS")
serveCmd.MarkFlagRequired("config")
}
func runServe(cmd *cobra.Command, args []string) {
fmt.Printf("Starting server on %s:%d (TLS: %v)\n", serverHost, serverPort, enableTLS)
}
Nested Subcommands
var configCmd = &cobra.Command{
Use: "config",
Short: "Manage configuration",
Long: `View and modify application configuration.`,
}
func init() {
rootCmd.AddCommand(configCmd)
}
var configGetCmd = &cobra.Command{
Use: "get [key]",
Short: "Get configuration value",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
key := args[0]
value := viper.Get(key)
fmt.Printf("%s = %v\n", key, value)
},
}
var configSetCmd = &cobra.Command{
Use: "set [key] [value]",
Short: "Set configuration value",
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
key, value := args[0], args[1]
viper.Set(key, value)
fmt.Printf("Set %s = %v\n", key, value)
},
}
func init() {
configCmd.AddCommand(configGetCmd)
configCmd.AddCommand(configSetCmd)
}
Argument Validation
cobra.ExactArgs(2)
cobra.MinimumNArgs(1)
cobra.MaximumNArgs(3)
cobra.RangeArgs(1, 3)
cobra.OnlyValidArgs
cobra.NoArgs
cobra.ArbitraryArgs
var customCmd = &cobra.Command{
Use: "custom [args]",
Short: "Custom validation example",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("requires at least one argument")
}
if len(args[0]) < 3 {
return errors.New("first argument must be at least 3 characters")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
},
}
Flag Types
var name string
cmd.Flags().StringVar(&name, "name", "", "Name")
cmd.Flags().StringVarP(&name, "name", "n", "", "Name (shorthand)")
var count int
cmd.Flags().IntVar(&count, "count", 0, "Count")
cmd.Flags().IntVarP(&count, "count", "c", 0, "Count")
var verbose bool
cmd.Flags().BoolVar(&verbose, "verbose", false, "Verbose output")
cmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Verbose output")
var tags []string
cmd.Flags().StringSliceVar(&tags, "tags", []string{}, "Tags (comma-separated)")
var timeout time.Duration
cmd.Flags().DurationVar(&timeout, "timeout", 30*time.Second, "Timeout")
var verbosity int
cmd.Flags().CountVarP(&verbosity, "verbose", "v", 0, "Verbosity level")
cmd.PersistentFlags().String("database", "", "Database connection string")
Help and Documentation
var exampleCmd = &cobra.Command{
Use: "example [name]",
Short: "Short description (shows in help list)",
Long: `Long description shown in detailed help.
This can span multiple lines and include formatting.`,
Example: ` # Basic usage
myapp example foo
# With options
myapp example foo --verbose --count=5`,
ValidArgs: []string{"foo", "bar", "baz"},
Run: func(cmd *cobra.Command, args []string) {
},
}
Command Groups
var (
configCmd = &cobra.Command{
Use: "config",
Short: "Configuration commands",
GroupID: "config",
}
getCmd = &cobra.Command{
Use: "get",
Short: "Get config value",
GroupID: "config",
}
serveCmd = &cobra.Command{
Use: "serve",
Short: "Start server",
GroupID: "server",
}
)
func init() {
rootCmd.AddGroup(&cobra.Group{
ID: "config",
Title: "Configuration Commands:",
})
rootCmd.AddGroup(&cobra.Group{
ID: "server",
Title: "Server Commands:",
})
rootCmd.AddCommand(configCmd)
rootCmd.AddCommand(serveCmd)
}
Pre/Post Run Hooks
var withHooksCmd = &cobra.Command{
Use: "with-hooks",
Short: "Demonstrate hooks",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Println("PersistentPreRun: Runs before this and all subcommands")
},
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Println("PreRun: Runs before this command only")
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Run: Main command execution")
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Println("PostRun: Runs after this command only")
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Println("PersistentPostRun: Runs after this and all subcommands")
},
}
Entry Point (main.go)
package main
import "myapp/cmd"
func main() {
cmd.Execute()
}
Best Practices
- Use
init() for flag registration - Keep flag setup with command definition
- Separate command logic - Put implementation in
Run functions
- Use persistent flags for globals - Share config, logging level across commands
- Document with examples - Provide clear usage examples
- Validate early - Use
Args field for argument validation
- Handle errors properly - Return errors, use
CheckErr for fatal errors
Additional Resources
references/cli-patterns.md - Advanced CLI patterns
examples/full-cli/ - Complete CLI application example