golang-cli-cobra-viper

Go CLI Development with Cobra & Viper

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "golang-cli-cobra-viper" with this command: npx skills add bobmatnyc/claude-mpm-skills/bobmatnyc-claude-mpm-skills-golang-cli-cobra-viper

Go CLI Development with Cobra & Viper

Overview

Cobra and Viper are the industry-standard libraries for building production-quality CLIs in Go. Cobra provides command structure and argument parsing, while Viper manages configuration from multiple sources with clear precedence rules.

Key Features:

  • 🎯 Cobra Commands: POSIX-compliant CLI with subcommands (app verb noun --flag )

  • ⚙️ Viper Config: Unified configuration from flags, env vars, and config files

  • 🔄 Integration: Seamless Cobra + Viper plumbing patterns

  • 🐚 Shell Completion: Auto-generated completions for bash, zsh, fish, PowerShell

  • ✅ Production Ready: Battle-tested by kubectl, docker, gh, hugo

Used By: Kubernetes (kubectl), Docker CLI, GitHub CLI (gh), Hugo, Helm, and 100+ major projects

When to Use This Skill

Activate this skill when:

  • Building multi-command CLI tools with subcommands

  • Creating developer tools, project generators, or scaffolding utilities

  • Implementing admin CLIs for services or infrastructure

  • Requiring flexible configuration (flags > env vars > config files > defaults)

  • Adding shell completion for frequently-used CLIs

  • Building DevOps automation tools or deployment scripts

Cobra Framework

Command Structure Pattern

Cobra follows the APPNAME VERB NOUN --FLAG pattern popularized by git and kubectl.

// cmd/root.go package cmd

import ( "fmt" "os"

"github.com/spf13/cobra"
"github.com/spf13/viper"

)

var cfgFile string

var rootCmd = &cobra.Command{ Use: "myapp", Short: "A powerful CLI tool for developers", Long: `MyApp is a CLI tool that demonstrates best practices for building production-quality command-line applications.

Complete documentation is available at https://myapp.example.com`, }

func Execute() { if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }

func init() { cobra.OnInitialize(initConfig)

// Persistent flags (available to all subcommands)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.myapp.yaml)")
rootCmd.PersistentFlags().Bool("verbose", false, "verbose output")

// Bind persistent flags to viper
viper.BindPFlag("config", rootCmd.PersistentFlags().Lookup("config"))
viper.BindPFlag("verbose", rootCmd.PersistentFlags().Lookup("verbose"))

}

func initConfig() { if cfgFile != "" { viper.SetConfigFile(cfgFile) } else { home, err := os.UserHomeDir() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) }

    viper.AddConfigPath(home)
    viper.AddConfigPath(".")
    viper.SetConfigType("yaml")
    viper.SetConfigName(".myapp")
}

viper.SetEnvPrefix("MYAPP")
viper.AutomaticEnv()

if err := viper.ReadInConfig(); err == nil {
    if viper.GetBool("verbose") {
        fmt.Println("Using config file:", viper.ConfigFileUsed())
    }
}

}

Subcommands with Arguments

// cmd/deploy.go package cmd

import ( "fmt"

"github.com/spf13/cobra"
"github.com/spf13/viper"

)

var deployCmd = &cobra.Command{ Use: "deploy [environment]", Short: "Deploy application to specified environment", Long: Deploy the application to the specified environment. Supports: dev, staging, production, Args: cobra.ExactArgs(1), ValidArgs: []string{"dev", "staging", "production"}, PreRunE: func(cmd *cobra.Command, args []string) error { // Validation logic runs before RunE env := args[0] if env == "production" && !viper.GetBool("force") { return fmt.Errorf("production deploys require --force flag") } return nil }, RunE: func(cmd *cobra.Command, args []string) error { env := args[0] region := viper.GetString("region") force := viper.GetBool("force")

    fmt.Printf("Deploying to %s in region %s (force=%v)\n", env, region, force)

    // Actual deployment logic
    return deploy(env, region, force)
},
PostRunE: func(cmd *cobra.Command, args []string) error {
    // Cleanup or notifications
    fmt.Println("Deployment complete")
    return nil
},

}

func init() { rootCmd.AddCommand(deployCmd)

// Local flags (only for this command)
deployCmd.Flags().StringP("region", "r", "us-east-1", "AWS region")
deployCmd.Flags().BoolP("force", "f", false, "Force deployment without confirmation")

// Bind flags to viper
viper.BindPFlag("region", deployCmd.Flags().Lookup("region"))
viper.BindPFlag("force", deployCmd.Flags().Lookup("force"))

}

func deploy(env, region string, force bool) error { // Implementation return nil }

Persistent vs. Local Flags

// Persistent flags: Available to command and all subcommands rootCmd.PersistentFlags().String("config", "", "config file path") rootCmd.PersistentFlags().Bool("verbose", false, "verbose output")

// Local flags: Only available to this specific command deployCmd.Flags().String("region", "us-east-1", "deployment region") deployCmd.Flags().Bool("force", false, "force deployment")

// Required flags deployCmd.MarkFlagRequired("region")

// Flag dependencies deployCmd.MarkFlagsRequiredTogether("username", "password") deployCmd.MarkFlagsMutuallyExclusive("json", "yaml")

PreRun/PostRun Hooks

Cobra provides execution hooks for setup and cleanup:

var serverCmd = &cobra.Command{ Use: "server", Short: "Start API server",

// Execution order (all optional):
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
    // Runs before PreRunE, inherited by subcommands
    return setupLogging()
},
PreRunE: func(cmd *cobra.Command, args []string) error {
    // Validation and setup before RunE
    return validateConfig()
},
RunE: func(cmd *cobra.Command, args []string) error {
    // Main command logic
    return startServer()
},
PostRunE: func(cmd *cobra.Command, args []string) error {
    // Cleanup after RunE
    return cleanup()
},
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
    // Runs after PostRunE, inherited by subcommands
    return flushLogs()
},

}

Important: Use RunE , PreRunE , PostRunE (error-returning versions) instead of Run , PreRun , PostRun .

Viper Configuration Management

Configuration Priority

Viper follows a strict precedence order (highest to lowest):

  • Explicit Set (viper.Set("key", value) )

  • Command-line Flags (bound with viper.BindPFlag )

  • Environment Variables (MYAPP_KEY=value )

  • Config File (~/.myapp.yaml , ./config.yaml )

  • Key/Value Store (etcd, Consul - optional)

  • Defaults (viper.SetDefault("key", value) )

func initConfig() { // 1. Set defaults (lowest priority) viper.SetDefault("port", 8080) viper.SetDefault("database.host", "localhost") viper.SetDefault("database.port", 5432)

// 2. Config file locations (checked in order)
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("/etc/myapp/")
viper.AddConfigPath("$HOME/.myapp")
viper.AddConfigPath(".")

// 3. Environment variables (prefix + automatic mapping)
viper.SetEnvPrefix("MYAPP")
viper.AutomaticEnv() // MYAPP_PORT, MYAPP_DATABASE_HOST, etc.

// 4. Read config file (optional)
if err := viper.ReadInConfig(); err != nil {
    if _, ok := err.(viper.ConfigFileNotFoundError); ok {
        // Config file not found - use defaults and env vars
    } else {
        // Config file found but error reading it
        return err
    }
}

// 5. Flags will be bound in init() functions (highest priority)

}

Environment Variable Mapping

Viper automatically maps environment variables with prefix and dot notation:

viper.SetEnvPrefix("MYAPP") // Prefix for env vars viper.AutomaticEnv() // Enable automatic mapping

// Config key → Environment variable // "port" → MYAPP_PORT // "database.host" → MYAPP_DATABASE_HOST // "database.port" → MYAPP_DATABASE_PORT // "aws.s3.region" → MYAPP_AWS_S3_REGION

Manual mapping for non-standard env var names:

viper.BindEnv("database.host", "DB_HOST") // Custom env var name viper.BindEnv("database.password", "DB_PASSWORD") // Different naming convention

Config File Formats

Viper supports multiple formats: YAML, JSON, TOML, HCL, INI, envfile, Java properties.

config.yaml:

port: 8080 log_level: info

database: host: localhost port: 5432 user: postgres ssl_mode: require

aws: region: us-east-1 s3: bucket: my-app-bucket

Accessing config values:

port := viper.GetInt("port") // 8080 dbHost := viper.GetString("database.host") // "localhost" s3Bucket := viper.GetString("aws.s3.bucket") // "my-app-bucket"

// Type-safe access if viper.IsSet("database.ssl_mode") { sslMode := viper.GetString("database.ssl_mode") }

// Unmarshal into struct type Config struct { Port int mapstructure:"port" LogLevel string mapstructure:"log_level" Database struct { Host string mapstructure:"host" Port int mapstructure:"port" User string mapstructure:"user" SSLMode string mapstructure:"ssl_mode" } mapstructure:"database" }

var config Config if err := viper.Unmarshal(&config); err != nil { return err }

Cobra + Viper Integration

Critical Integration Pattern

The key to Cobra + Viper integration is binding flags to Viper keys:

// cmd/root.go func init() { cobra.OnInitialize(initConfig) // Load config before command execution

// Define flags
rootCmd.PersistentFlags().String("config", "", "config file")
rootCmd.PersistentFlags().String("log-level", "info", "log level")
rootCmd.PersistentFlags().Int("port", 8080, "server port")

// Bind flags to Viper (critical step!)
viper.BindPFlag("config", rootCmd.PersistentFlags().Lookup("config"))
viper.BindPFlag("log_level", rootCmd.PersistentFlags().Lookup("log-level"))
viper.BindPFlag("port", rootCmd.PersistentFlags().Lookup("port"))

}

func initConfig() { // This runs BEFORE command execution via cobra.OnInitialize if cfgFile := viper.GetString("config"); cfgFile != "" { viper.SetConfigFile(cfgFile) } else { viper.AddConfigPath("$HOME/.myapp") viper.AddConfigPath(".") viper.SetConfigName("config") }

viper.SetEnvPrefix("MYAPP")
viper.AutomaticEnv()

viper.ReadInConfig() // Ignore errors - config file is optional

}

Flag binding strategies:

// Strategy 1: Bind each flag individually (explicit) viper.BindPFlag("log_level", rootCmd.Flags().Lookup("log-level"))

// Strategy 2: Bind all flags automatically (convenient) viper.BindPFlags(rootCmd.Flags())

// Strategy 3: Hybrid approach (recommended) // - Bind persistent flags globally // - Bind local flags in each command's init() rootCmd.PersistentFlags().String("config", "", "config file") viper.BindPFlags(rootCmd.PersistentFlags())

deployCmd.Flags().String("region", "us-east-1", "AWS region") viper.BindPFlag("deploy.region", deployCmd.Flags().Lookup("region"))

PersistentPreRun for Config Loading

Use PersistentPreRunE to load and validate configuration:

var rootCmd = &cobra.Command{ Use: "myapp", Short: "My application", PersistentPreRunE: func(cmd *cobra.Command, args []string) error { // Runs before ALL commands (inherited by subcommands)

    // 1. Validate required config
    if !viper.IsSet("api_key") {
        return fmt.Errorf("API key not configured (set MYAPP_API_KEY or add to config file)")
    }

    // 2. Setup logging based on config
    logLevel := viper.GetString("log_level")
    if err := setupLogging(logLevel); err != nil {
        return fmt.Errorf("invalid log level: %w", err)
    }

    // 3. Initialize clients/services
    apiKey := viper.GetString("api_key")
    if err := initAPIClient(apiKey); err != nil {
        return fmt.Errorf("failed to initialize API client: %w", err)
    }

    return nil
},

}

Shell Completion

Cobra generates shell completion scripts for bash, zsh, fish, and PowerShell.

Adding Completion Command

// cmd/completion.go package cmd

import ( "os"

"github.com/spf13/cobra"

)

var completionCmd = &cobra.Command{ Use: "completion [bash|zsh|fish|powershell]", Short: "Generate shell completion script", Long: `Generate shell completion script for myapp.

To load completions:

Bash: $ source <(myapp completion bash)

To load automatically, add to ~/.bashrc:

$ echo 'source <(myapp completion bash)' >> ~/.bashrc

Zsh: $ source <(myapp completion zsh)

To load automatically, add to ~/.zshrc:

$ echo 'source <(myapp completion zsh)' >> ~/.zshrc

Fish: $ myapp completion fish | source

To load automatically:

$ myapp completion fish > ~/.config/fish/completions/myapp.fish

PowerShell: PS> myapp completion powershell | Out-String | Invoke-Expression

To load automatically, add to PowerShell profile.

`, DisableFlagsInUseLine: true, ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, Args: cobra.ExactValidArgs(1), RunE: func(cmd *cobra.Command, args []string) error { switch args[0] { case "bash": return cmd.Root().GenBashCompletion(os.Stdout) case "zsh": return cmd.Root().GenZshCompletion(os.Stdout) case "fish": return cmd.Root().GenFishCompletion(os.Stdout, true) case "powershell": return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) } return nil }, }

func init() { rootCmd.AddCommand(completionCmd) }

Custom Completion Functions

Provide dynamic completions for command arguments:

var deployCmd = &cobra.Command{ Use: "deploy [environment]", Short: "Deploy to environment", Args: cobra.ExactArgs(1), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { // Return available environments envs := []string{"dev", "staging", "production"} return envs, cobra.ShellCompDirectiveNoFileComp }, RunE: func(cmd *cobra.Command, args []string) error { return deploy(args[0]) }, }

// Custom flag completion deployCmd.RegisterFlagCompletionFunc("region", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { regions := []string{"us-east-1", "us-west-2", "eu-west-1"} return regions, cobra.ShellCompDirectiveNoFileComp })

CLI Best Practices

User-Friendly Error Messages

// ❌ BAD: Technical jargon return fmt.Errorf("db connection failed: EOF")

// ✅ GOOD: Actionable error message return fmt.Errorf("cannot connect to database at %s:%d\nPlease check:\n - Database is running\n - Credentials are correct (MYAPP_DB_PASSWORD)\n - Network connectivity", host, port)

// ✅ GOOD: Suggest remediation if !viper.IsSet("api_key") { return fmt.Errorf("API key not configured\nSet environment variable: export MYAPP_API_KEY=your-key\nOr add to config file: ~/.myapp.yaml") }

Progress Indicators

import "github.com/briandowns/spinner"

func deploy(env string) error { s := spinner.New(spinner.CharSets[11], 100*time.Millisecond) s.Suffix = " Deploying to " + env + "..." s.Start() defer s.Stop()

// Deployment logic
if err := performDeployment(env); err != nil {
    s.Stop()
    return err
}

s.Stop()
fmt.Println("✓ Deployment successful")
return nil

}

Output Formatting

import ( "encoding/json" "github.com/olekukonko/tablewriter" )

func displayResults(items []Item, format string) error { switch format { case "json": enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") return enc.Encode(items)

case "table":
    table := tablewriter.NewWriter(os.Stdout)
    table.SetHeader([]string{"ID", "Name", "Status"})
    for _, item := range items {
        table.Append([]string{item.ID, item.Name, item.Status})
    }
    table.Render()
    return nil

default:
    return fmt.Errorf("unknown format: %s (use json or table)", format)
}

}

Logging vs. User Output

import ( "log" "os" )

var ( // User-facing output (stdout) out = os.Stdout

// Logging and errors (stderr)
logger = log.New(os.Stderr, "[myapp] ", log.LstdFlags)

)

func RunCommand() error { // User output: stdout fmt.Fprintln(out, "Processing files...")

// Debug/verbose logging: stderr
if viper.GetBool("verbose") {
    logger.Println("Reading config from", viper.ConfigFileUsed())
}

// Errors: stderr
if err := process(); err != nil {
    fmt.Fprintf(os.Stderr, "Error: %v\n", err)
    return err
}

// Success message: stdout
fmt.Fprintln(out, "✓ Complete")
return nil

}

Testing CLI Applications

Testing Command Execution

// cmd/deploy_test.go package cmd

import ( "bytes" "testing"

"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

)

func executeCommand(root *cobra.Command, args ...string) (output string, err error) { buf := new(bytes.Buffer) root.SetOut(buf) root.SetErr(buf) root.SetArgs(args)

err = root.Execute()
return buf.String(), err

}

func TestDeployCommand(t *testing.T) { tests := []struct { name string args []string wantErr bool wantOut string }{ { name: "deploy to dev", args: []string{"deploy", "dev"}, wantErr: false, wantOut: "Deploying to dev", }, { name: "deploy to production without force", args: []string{"deploy", "production"}, wantErr: true, wantOut: "production deploys require --force flag", }, { name: "deploy to production with force", args: []string{"deploy", "production", "--force"}, wantErr: false, wantOut: "Deploying to production", }, }

for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        output, err := executeCommand(rootCmd, tt.args...)

        if tt.wantErr {
            require.Error(t, err)
        } else {
            require.NoError(t, err)
        }

        assert.Contains(t, output, tt.wantOut)
    })
}

}

Testing with Viper Configuration

func TestCommandWithConfig(t *testing.T) { // Reset viper state before each test viper.Reset()

// Set test configuration
viper.Set("region", "eu-west-1")
viper.Set("api_key", "test-key-123")

output, err := executeCommand(rootCmd, "deploy", "staging")

require.NoError(t, err)
assert.Contains(t, output, "eu-west-1")

}

Capturing Output

func TestOutputFormat(t *testing.T) { // Capture stdout oldStdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w defer func() { os.Stdout = oldStdout }()

// Execute command
err := listCmd.RunE(listCmd, []string{})
require.NoError(t, err)

// Read output
w.Close()
var buf bytes.Buffer
io.Copy(&#x26;buf, r)
output := buf.String()

assert.Contains(t, output, "ID")
assert.Contains(t, output, "Name")

}

Decision Trees

When to Use Cobra

Use Cobra when:

  • ✅ Building multi-command CLI with subcommands (e.g., git clone , docker run )

  • ✅ Need POSIX-compliant flag parsing (--flag , -f )

  • ✅ Want built-in help generation (--help )

  • ✅ Require shell completion support

  • ✅ Building professional CLI used by other developers

Don't use Cobra when:

  • ❌ Simple single-command script (use flag package)

  • ❌ Internal-only tool with 1-2 flags

  • ❌ Prototyping or throwaway scripts

When to Use Viper

Use Viper when:

  • ✅ Need configuration from multiple sources (flags, env vars, files)

  • ✅ Want clear configuration precedence rules

  • ✅ Support multiple config file formats (YAML, JSON, TOML)

  • ✅ Require environment variable mapping with prefixes

  • ✅ Need live config reloading (watch config file changes)

Don't use Viper when:

  • ❌ Only using command-line flags (Cobra alone is sufficient)

  • ❌ Hardcoded configuration values

  • ❌ Simple scripts with no configuration

When to Add Shell Completion

Add shell completion when:

  • ✅ CLI used frequently by developers (daily/hourly)

  • ✅ Many subcommands or complex flag combinations

  • ✅ Arguments have known valid values (e.g., environments, regions)

  • ✅ Building professional developer tools

Skip shell completion when:

  • ❌ CLI used rarely (monthly or less)

  • ❌ Simple commands with few options

  • ❌ Internal-only tools

When to Use Persistent Flags

Use persistent flags when:

  • ✅ Flag applies to ALL subcommands (e.g., --verbose , --config )

  • ✅ Common configuration shared across commands

  • ✅ Global behavior modifiers (e.g., --dry-run , --output )

Use local flags when:

  • ✅ Flag only relevant to specific command

  • ✅ Command-specific parameters (e.g., --region for deploy command)

Anti-Patterns

❌ Not Handling Errors in PreRunE/RunE

Wrong:

var deployCmd = &cobra.Command{ Use: "deploy", Run: func(cmd *cobra.Command, args []string) { deploy(args[0]) // Ignores error! }, }

Correct:

var deployCmd = &cobra.Command{ Use: "deploy", RunE: func(cmd *cobra.Command, args []string) error { return deploy(args[0]) // Proper error handling }, }

❌ Mixing Configuration Sources Without Clear Precedence

Wrong:

// Confusing: Which takes precedence? config.Port = viper.GetInt("port") if os.Getenv("PORT") != "" { config.Port = atoi(os.Getenv("PORT")) } if *flagPort != 0 { config.Port = *flagPort }

Correct:

// Clear: Viper handles precedence automatically viper.BindPFlag("port", rootCmd.Flags().Lookup("port")) viper.SetEnvPrefix("MYAPP") viper.AutomaticEnv() viper.SetDefault("port", 8080)

config.Port = viper.GetInt("port") // Respects: flag > env > config > default

❌ Forgetting to Bind Flags to Viper

Wrong:

rootCmd.Flags().String("region", "us-east-1", "AWS region") // Flag not bound to Viper - won't respect precedence!

func deploy() { region := viper.GetString("region") // Always returns config file value }

Correct:

rootCmd.Flags().String("region", "us-east-1", "AWS region") viper.BindPFlag("region", rootCmd.Flags().Lookup("region")) // Bind it!

func deploy() { region := viper.GetString("region") // Respects flag > env > config }

❌ Not Testing CLI Commands

Wrong:

// No tests for CLI commands - bugs slip through

Correct:

func TestDeployCommand(t *testing.T) { output, err := executeCommand(rootCmd, "deploy", "staging", "--region", "eu-west-1") require.NoError(t, err) assert.Contains(t, output, "Deploying to staging") assert.Contains(t, output, "eu-west-1") }

❌ Poor Error Messages

Wrong:

return fmt.Errorf("connection failed") // Unhelpful

Correct:

return fmt.Errorf("cannot connect to database at %s:%d\nCheck:\n - Database is running\n - Credentials (MYAPP_DB_PASSWORD)\n - Firewall rules", host, port)

❌ Using Run Instead of RunE

Wrong:

var rootCmd = &cobra.Command{ Use: "myapp", Run: func(cmd *cobra.Command, args []string) { if err := execute(); err != nil { fmt.Println(err) // Error not propagated } }, }

Correct:

var rootCmd = &cobra.Command{ Use: "myapp", RunE: func(cmd *cobra.Command, args []string) error { return execute() // Cobra handles error display and exit code }, }

Production Example

Minimal production-ready CLI structure:

myapp/ ├── cmd/ │ ├── root.go # Root command + config loading │ ├── deploy.go # Deploy subcommand │ ├── status.go # Status subcommand │ └── completion.go # Shell completion ├── main.go # Entry point ├── config.yaml # Example config file └── go.mod

main.go:

package main

import "myapp/cmd"

func main() { cmd.Execute() }

cmd/root.go: See "Command Structure Pattern" section above

Building and installing:

Development

go run main.go deploy staging --region us-west-2

Production build

go build -o myapp

Install globally

go install

Enable shell completion

myapp completion bash > /etc/bash_completion.d/myapp

Resources

Official Documentation:

  • Cobra User Guide - Official framework documentation

  • Viper Documentation - Configuration management guide

Learning Resources:

  • "Building CLI Apps in Go with Cobra & Viper" (November 2025) - Comprehensive tutorial

  • "The Cobra & Viper Journey" - Learning path for CLI development

  • Cobra Generator - Scaffolding tool for new CLIs

Production Examples:

  • kubectl - Kubernetes CLI

  • docker - Docker CLI

  • gh - GitHub CLI

  • hugo - Static site generator

Related Skills:

  • golang-testing-strategies

  • Testing CLI commands comprehensively

  • golang-http-servers

  • Building API servers with configuration

  • golang-concurrency-patterns

  • Async operations in CLI tools

Success Criteria

You know you've mastered Cobra + Viper when:

  • ✅ Commands follow POSIX conventions (VERB NOUN --FLAG )

  • ✅ Configuration precedence is clear: flags > env > config > defaults

  • ✅ All flags bound to Viper for unified config access

  • ✅ Shell completion generated for all major shells

  • ✅ Error messages are actionable and user-friendly

  • ✅ CLI commands have comprehensive tests

  • ✅ Help text auto-generated and accurate

  • ✅ PersistentPreRunE used for global setup/validation

  • ✅ Separation of concerns: user output (stdout) vs. logging (stderr)

  • ✅ Config files optional - CLI works with flags/env vars alone

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

nodejs-backend-typescript

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

jest-typescript

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

github-actions

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-core

No summary provided by upstream source.

Repository SourceNeeds Review