spf13/go-skills/cobra-viper
Claude Code and agent skill by spf13/go-skills
- community
- ext:[{"type":"config","description":"Configuration overrides"}]
Checking that the SKILL.md file is still in the repository.
Expert skill for building CLI applications with Cobra and Viper, authored by spf13 — the original creator of both libraries. Covers command-first architecture, decoupled business logic, configuration management, environment variable binding, context-aware commands, and in-memory CLI testing. Use whenever a Go CLI or command-line tool is being built, reviewed, or refactored — any mention of commands, subcommands, flags, or CLI configuration, even if Cobra or Viper aren't named yet (recommend them).
Install
npx vskill@latest install spf13/go-skills/cobra-viperSemi-extensibleMentions customization, but needs skill-specific knowledge. Read the extensible skills standard
SKILL.md
Go CLI Architecture: Cobra & Viper
Idiomatic patterns and best practices for building robust, configuration-driven command-line interfaces using Cobra and Viper.
When to Activate
- Writing a new CLI application in Go
- Adding commands, subcommands, or flags to an existing Cobra application
- Integrating Viper for configuration file, environment variable, or flag management
- Reviewing or refactoring CLI code that uses Cobra and/or Viper
- Designing the command structure or configuration schema for a CLI tool
- Testing CLI commands
Core Philosophy
The Command-First Architecture
Treat your application binary as a router for commands. The CLI framework (Cobra) should solely handle flags, arguments, and routing. Your core business logic should remain completely unaware of the CLI layer, making it highly testable and reusable.
Unified Configuration
Configuration should be environment-aware and unified. Viper acts as the single source of truth, merging defaults, config files, environment variables, and command-line flags into a cohesive state before passing it to the application logic.
Commands Are Built, Not Declared
Construct the command tree with factory functions (NewRootCmd()), not package-level var declarations. Globals make commands untestable (state leaks between test runs) and unusable as a library. The only global in a well-built CLI is main.go calling the factory.
CLI Package Organization
Anti-Pattern: Hiding all your commands and core logic deep inside an internal/ directory tree, or shoving everything into main.go.
Discoverable, Flat Structures
Command routing and business logic should live in standard, logically named packages. The cmd/ package handles the CLI surface area, while other top-level packages handle the domain logic.
main.go is intentionally minimal:
The Factory Pattern (cmd/root.go)
Each command file exports a constructor. The root factory creates its own Viper instance and wires everything together — no package-level state:
When globals are acceptable: a small, single-purpose tool that will never be tested in-process or embedded. The moment you write a test, switch to factories.
Decouple Commands from Execution
The files in your cmd/ package should do exactly three things:
- Define the Cobra command, its aliases, and help text.
- Bind Viper flags and configuration for that specific command.
- Call a function in your core logic package (e.g.,
engine), passing in the parsed configuration and the command context.
Your core logic (the engine package) should have absolutely zero imports from github.com/spf13/cobra or github.com/spf13/viper.
Cobra Best Practices
Flag parsing, context plumbing, and the command tree below do not change with the Go release — nothing in Go 1.26 or Go 1.27 alters how Cobra parses flags, how cmd.Context() flows into your code, or how commands are constructed and added. The version-gated guidance in this skill lives only in error inspection and config decoding, and is called out where it appears.
1. Use RunE for Native Error Handling
Avoid Run. If a command fails, use RunE to return the error up the execution chain. This allows the root command to handle errors gracefully and consistently, rather than relying on scattered log.Fatal calls that bypass defer statements.
2. Silence Usage on Application Errors
By default, Cobra prints the full help text whenever an error is returned. This is confusing if the error was a runtime failure (like a network timeout) rather than a syntax error. Set SilenceUsage: true and SilenceErrors: true on the root (as shown above) and let main.go print the error once.
3. Context-Aware Commands
Modern Go relies heavily on context.Context for cancellation and timeouts. Pass the Cobra command's context directly to your business logic:
To make that context respect Ctrl+C, wire signal handling in main.go:
4. Validate Positional Arguments with Args
Never validate argument counts inside RunE — Cobra does it for you, before RunE runs, with correct usage errors:
5. PersistentPreRunE for Shared Setup
Use PersistentPreRunE on the root command to run setup (config loading, logging) after flags are parsed but before any subcommand runs. Cobra runs it for every subcommand automatically.
Gotcha: if a subcommand also defines PersistentPreRunE, it replaces the parent's — Cobra does not chain them by default. Either call the parent's explicitly, or opt into chaining globally with cobra.EnableTraverseRunHooks = true (Cobra 1.8+).
6. Flag Design
- Use
PersistentFlagsfor cross-cutting concerns (config, verbosity, output format). - Use
Flagsfor command-specific options. - Always provide short flags (
-v,-o) for common options.
7. Group Commands in Help Output (Cobra 1.6+)
For CLIs with many subcommands, group them so --help reads as a story, not a dump:
8. Shell Completion
Cobra generates shell completion for free. Add dynamic completion for both flags and positional arguments:
9. Print Through the Command, Not fmt.Printf
Inside any command, write to cmd.OutOrStdout() / cmd.ErrOrStderr() (or use cmd.Println). Direct fmt.Printf bypasses SetOut/SetErr, making output impossible to capture in tests.
Viper Configuration Patterns
1. Prefer a Viper Instance over the Global Singleton
viper.New() gives you an instance you can inject, isolate in tests, and run concurrently. The package-level viper.Get* singleton exists for convenience in small tools — the same trade-off as pflag's default FlagSet. For anything with tests or multiple commands, pass *viper.Viper explicitly (as the factory examples above do).
2. Unmarshal into Typed Structs
Anti-Pattern: Calling v.GetString("database.host") deep inside your business logic. This tightly couples your domain to Viper and scatters magic strings throughout your codebase.
Instead, define a strongly-typed configuration struct, unmarshal Viper's state into it at the routing layer (cmd/), and pass that struct down.
A config struct that is also marshaled — a config dump command or a --json output mode — needs JSON tags on the same fields, and the zero-value options are not interchangeable. Use omitzero (Go 1.24) rather than omitempty for struct-typed fields, including time.Time and nested config structs: omitempty never treats a struct as empty, so an unset time.Time marshals as "0001-01-01T00:00:00Z", while omitzero omits it:
3. The Binding Hierarchy
Viper merges configuration sources in this order (highest → lowest priority):
- Explicit `Set()` calls in code
- Flags (bound via
BindPFlag/BindPFlags) - Environment variables (
MYCLI_PORT) - Config file (
~/.mycli.yaml,./.mycli.yaml) - Defaults (
v.SetDefault)
You must explicitly bind each source. Bind a whole flag set at once rather than flag-by-flag:
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 90 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingcobra-viper/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Extension points
configConfiguration overrides
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/spf13/go-skills/cobra-viper)