Source-verified agent skills for Go: Cobra command-line interfaces, Viper configuration, and Wails desktop apps, each backed by tested examples.
99
100%
Does it follow best practices?
Impact
99%
1.02xAverage score across 18 eval scenarios
Passed
No findings from the security scan
Build Go command-line programs with Cobra so they stay testable and predictable: fresh command trees, thin handlers over shared operations, explicit argument validation, payloads on stdout, diagnostics on stderr, and one place that reports errors. The pitfalls below are the lifecycle and flag behaviors that cause real bugs.
go.mod. Use only APIs available in its Cobra version;
do not upgrade a dependency just to match this skill. Write Go for the
version in its go directive (if a modern-Go guidelines skill such as
use-modern-go is installed, follow it for that version).A new command starts from a constructor that takes its services and returns a fresh tree:
// examples/cobra/cli/cli.go (trimmed: text output, empty results, and completion omitted)
func NewRoot(list List, tags Tags) *cobra.Command {
root := &cobra.Command{
Use: "catalog",
SilenceErrors: true, // the process boundary owns error reporting
SilenceUsage: true, // explicit --help still works
}
var tag string
ls := &cobra.Command{
Use: "list",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
names, err := list(cmd.Context(), tag)
if err != nil {
return err
}
return json.NewEncoder(cmd.OutOrStdout()).Encode(names)
},
}
ls.Flags().StringVar(&tag, "tag", "", "Filter by tag")
root.AddCommand(ls)
return root
}A test builds its own tree, sets SetArgs, SetOut, and SetErr, runs
ExecuteContext(t.Context()), and asserts on the captured buffers. The full
version, with text output, completion, and tests, is in
references/commands.md.
NewRoot(...) *cobra.Command (or an
error-returning constructor if setup can fail). Inject services and writers;
avoid package-level commands, mutable options, and registration in init().
Fresh instances prevent flags and tests from leaking state between executions.RunE, with explicit Args validation (NoArgs, ExactArgs,
MinimumNArgs, etc.). Without Args, a leaf command accepts arbitrary
arguments, and a nested command group that has no RunE prints help for
app group typo and exits 0. A script cannot tell that from success.Flags() for a command's own options and PersistentFlags() for options
genuinely shared by descendants. Parent local flags are not inherited.ExecuteContext(ctx) and pass cmd.Context() into core operations.
Define signal handling and exit codes at the process boundary. Cobra does not
choose your cancellation or partial-failure exit codes.cmd.OutOrStdout() and diagnostics through
cmd.ErrOrStderr(). Keep JSON output free of progress, styling, and prose.
Do not use cmd.Print* for payloads: it writes to the SetOut writer when
one is set and otherwise to the process's stderr, so a test that calls
SetOut passes while the real binary sends the payload to stderr.SilenceErrors: true; use SilenceUsage deliberately so runtime failures do
not emit a help page. Still return the error. Do not os.Exit, log.Fatal, or
cobra.CheckErr inside reusable commands.--help, --version,
and completion should not require a valid config, network login, or a writable
state database merely because the command tree was constructed.Changed), not whether an effective value is nonzero. If config or env can
satisfy a required setting, validate the resolved setting instead.--enabled=false from an omitted flag using Changed. Prefer
StringArray for repeated literal values, StringSlice for CSV/repeated
values, and Count for -v, -vv, etc.MarkFlagsMutuallyExclusive can panic when given an undefined flag name;
register flags first.PersistentPreRunE is skipped for --help and --version, but it
runs for completion <shell>, every __complete request, and
help <command>. If it loads config and fails, tab completion breaks
wherever the config file is missing or invalid. Load config inside the
operation that needs it, or return early from the hook for those commands.cobra.EnableTraverseRunHooks is a process-global opt-in: persistent pre-hooks
then run root-to-leaf and post-hooks leaf-to-root. Do not toggle it per command
or in parallel tests. TraverseChildren is a different flag-parsing option.Args validation runs first, then pre-hooks, then required-flag and
flag-group validation. Keep mutations in RunE. Post-hooks do not run after a
failed RunE; acquire resources and defer cleanup in the same operation
instead.StringVar from config or environment values.viper skill if it is
installed. Without it, consult the upstream Viper API; do not add Viper merely
because a command uses Cobra.references/commands.md also covers Version, help
groups, hidden and deprecated commands, and flag groups. For MatchAll, custom
pflag.Value types, and the rest of the API, see the API documentation under
Sources.
Before finishing, test with fresh command instances using SetArgs, SetIn,
SetOut, SetErr, and ExecuteContext, and check:
Avoid snapshots of entire generated help pages unless their exact layout is a
requirement. Run go vet and go test for the packages you changed. When a
check fails, find the matching entry in
Flags and lifecycle pitfalls, fix the
command, and rerun every check.
Verified against cobra v1.10.2 and pflag v1.0.9; each stated behavior has a test
in the examples module.
When the project's version differs, trust go doc github.com/spf13/cobra.Command
and its module source over this skill.