SKILL.md
$2b
go get github.com/spf13/cobra@latest
Cobra vs. viper
These libraries do fundamentally different things and can be used independently.
Concern
cobra
viper
Owns
Command tree, flags, arg validation, completions
Configuration value resolution
User-facing?
Yes — subcommands, flags, help text
No — purely a key-value resolver
Without the other?
Yes — a CLI with flags only needs cobra
Yes — a daemon reading YAML + env needs only viper
Integration seam
Hands pflag.Flag to viper via BindPFlag
Treats the cobra flag as the highest-precedence layer
Use cobra alone when your binary takes flags and args but needs no config file or env resolution. Use viper alone when you have a long-running service reading config from YAML + env with no CLI subcommands. Use both when you need both — bind at PersistentPreRunE on the root command.
→ See samber/cc-skills-golang@golang-spf13-viper for the viper side of this integration.
Command tree
Every cobra CLI has a root command plus zero or more subcommands registered with AddCommand. The root command name is the binary name.
var rootCmd = &cobra.Command{
Use: "myapp",
Short: "One-line summary",
SilenceUsage: true, // ✓ prevents usage wall on every error
SilenceErrors: true, // ✓ lets you control error output format
}
Use AddGroup to label subcommands in help output — register groups before the AddCommand calls that reference them; cobra does not retroactively assign groups.
The Run* family
Cobra commands have five run hooks executed in order:
PersistentPreRunE → PreRunE → RunE → PostRunE → PersistentPostRunE
Always use *E variants — the non-E forms cannot return errors. Key rules:
PersistentPreRunEon the root runs before every subcommand — use it for config init and auth checks.
- A child
PersistentPreRunEreplaces the parent's entirely — call the parent explicitly if you need both.
PostRunEruns only ifRunEsucceeded.
For the full lifecycle and inheritance rules, see commands-and-args.md.
Args validators
Cobra validates positional arguments before RunE runs. Never write len(args) checks inside RunE — that bypasses cobra's standard error messages and arg count tracking.
Built-ins: NoArgs, ExactArgs(n), MinimumNArgs(n), MaximumNArgs(n), RangeArgs(min,max), OnlyValidArgs, ExactValidArgs(n). Compose with MatchAll(v1, v2). Custom validator: func(cmd *cobra.Command, args []string) error.
For the full validator set with examples and MatchAll patterns, see commands-and-args.md.
Flags primer
Cobra delegates flag parsing to pflag. Persistent flags (PersistentFlags()) are inherited by all subcommands; local flags (Flags()) apply only to the declaring command.
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file path") // inherited by all subcommands
serveCmd.Flags().IntVar(&port, "port", 8080, "listen port") // local to serveCmd only
serveCmd.MarkFlagRequired("port")
serveCmd.MarkFlagsMutuallyExclusive("json", "yaml")
For pflag types, custom flag values, flag groups, and viper binding, see flags.md.
Completions primer
Cobra generates shell completions automatically. Extend them with:
- **
ValidArgs []string** — static positional arg completion.
- **
ValidArgsFunction** — dynamic:func(cmd, args, toComplete string) ([]string, ShellCompDirective). ReturnShellCompDirectiveNoFileCompto suppress file fallback.
- **
RegisterFlagCompletionFunc(name, fn)** — flag value completion.
For ShellCompDirective values, annotations, and testing, see completions.md.
Testing commands
Test commands by executing them programmatically. **Never use os.Stdout / os.Stderr directly** in command handlers — use cmd.OutOrStdout() / cmd.ErrOrStderr() so tests can redirect output.
func TestServeCmd(t *testing.T) {
buf := new(bytes.Buffer)
rootCmd.SetOut(buf)
rootCmd.SetArgs([]string{"serve", "--port", "9090"})
require.NoError(t, rootCmd.Execute())
assert.Contains(t, buf.String(), "listening on :9090")
}
Cobra accumulates flag state across Execute() calls — build a fresh command tree per test. For isolation patterns, golden files, and testing completions, see testing.md.
Best Practices
- **Always use
RunE, neverRun** —Runcannot return an error; the only escape isos.Exitor panic, bypassing defers.
- **Put config initialization in
PersistentPreRunE** — it runs before every subcommand; the right place for viper binding and auth checks.
- **Validate positional args with
Args, not insideRunE** —Argsgives cobra's standard error messages;MatchAllcomposes validators.
- **Use
cmd.OutOrStdout()/cmd.ErrOrStderr()for all output** — directos.Stdoutwrites cannot be captured by tests.
- Re-create the command tree per test — cobra accumulates flag state across
Execute()calls on the same instance.
Common Mistakes
Mistake
Why it fails
Fix
Using Run instead of RunE
Cannot return an error — only escape is os.Exit or panic, bypassing defers
Use RunE — return the error, let cobra handle the exit
Writing len(args) checks in RunE
Bypasses cobra's standard error messages ("accepts 1 arg, received 2")
Declare Args: cobra.ExactArgs(1) on the command
Writing to os.Stdout directly
Tests cannot capture output — os-level file handles can't be redirected
Use cmd.OutOrStdout() / cmd.ErrOrStderr()
Child PersistentPreRunE silently drops parent's
Cobra does not chain — the child replaces the parent's hook entirely
Call parent.PersistentPreRunE(cmd, args) from the child's hook
Reusing a root command across tests
Cobra accumulates flag state; second Execute() sees flags from the first
Build a fresh command tree per test
Further Reading
- commands-and-args.md — full PreRun/PostRun chain, every Args validator, PersistentPreRunE inheritance rules
- flags.md — pflag types, required/exclusive/oneRequired groups, custom value types, viper binding
- completions.md — ShellCompDirective set, annotation-based completions, testing completions
- generators.md — man page, markdown, YAML, RST doc generation;
cobra-cliscaffolder
- testing.md — isolation patterns, golden files, testing completions, table-driven command tests
Cross-References
- → See
samber/cc-skills-golang@golang-cliskill for general CLI architecture — project layout, exit codes, signal handling, I/O patterns
- → See
samber/cc-skills-golang@golang-spf13-viperskill for configuration layering alongside cobra (flag → env → file → default precedence)
- → See
samber/cc-skills-golang@golang-testingskill for general Go testing patterns
If you encounter a bug or unexpected behavior in spf13/cobra, open an issue at https://github.com/spf13/cobra/issues.