Go CLI Tools Skill
Build professional command-line applications with Go.
Overview
Complete guide for building CLI tools with Cobra, Viper configuration, terminal UI, and cross-platform support.
Parameters
| Parameter |
Type |
Required |
Default |
Description |
| framework |
string |
no |
"cobra" |
CLI framework |
| config_format |
string |
no |
"yaml" |
Config: "yaml", "json", "toml" |
| interactive |
bool |
no |
false |
Include interactive prompts |
Core Topics
Cobra Setup
var rootCmd = &cobra.Command{
Use: "myapp",
Short: "My CLI application",
Version: version,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return initConfig()
},
}
func Execute() int {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
return 0
}
func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
}
Subcommands
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the server",
RunE: func(cmd *cobra.Command, args []string) error {
port, _ := cmd.Flags().GetInt("port")
return runServer(port)
},
}
func init() {
serveCmd.Flags().IntP("port", "p", 8080, "server port")
rootCmd.AddCommand(serveCmd)
}
Viper Configuration
func initConfig() error {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
home, _ := os.UserHomeDir()
viper.AddConfigPath(home)
viper.AddConfigPath(".")
viper.SetConfigName(".myapp")
viper.SetConfigType("yaml")
}
viper.SetEnvPrefix("MYAPP")
viper.AutomaticEnv()
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return fmt.Errorf("config: %w", err)
}
}
return nil
}
Output Formatting
type OutputFormat string
const (
FormatJSON OutputFormat = "json"
FormatYAML OutputFormat = "yaml"
FormatTable OutputFormat = "table"
)
func PrintOutput(data interface{}, format OutputFormat) error {
switch format {
case FormatJSON:
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(data)
case FormatYAML:
out, err := yaml.Marshal(data)
if err != nil {
return err
}
fmt.Print(string(out))
return nil
case FormatTable:
return printTable(data)
default:
return fmt.Errorf("unknown format: %s", format)
}
}
Signal Handling
func runWithGracefulShutdown(fn func(ctx context.Context) error) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
errCh := make(chan error, 1)
go func() {
errCh <- fn(ctx)
}()
select {
case err := <-errCh:
return err
case sig := <-sigCh:
fmt.Printf("\nReceived %s, shutting down...\n", sig)
cancel()
return <-errCh
}
}
Exit Codes
| Code |
Meaning |
| 0 |
Success |
| 1 |
General error |
| 2 |
Invalid usage |
| 64 |
Command line error |
| 65 |
Data format error |
Troubleshooting
Failure Modes
| Symptom |
Cause |
Fix |
| Exit 0 on error |
Using Run not RunE |
Switch to RunE |
| Flag undefined |
Missing init() |
Register in init() |
| Config not loaded |
Wrong path |
Check viper paths |
Usage
Skill("go-cli-tools")