CLI Application Development
Standards for building CLI applications in Go. Currently uses Cobra for command structure and Huh for interactive UI.
Interactive UI patterns: See Interactive UI Reference
Command Organization
- One file per command in
cmd/, file name matches command name (camelCase) - All commands registered in their own
init()function viarootCmd.AddCommand() - See
cmd/root.gofor the root command structure and initialization chain - See any existing command file (e.g.,
cmd/version.go) for a minimal example
Adding a New Command
- Create a new file in
cmd/(camelCase name matching the command) - Define a
cobra.Commandvariable withUseandShortfields - In
init(): register withrootCmd.AddCommand(), define flags, bind to Viper - Suppress the init lint:
//nolint:gochecknoinits // Cobra requires an init function to set up the command structure.
Flag Conventions
| Scope | Method |
|---|---|
| Global (all commands) | rootCmd.PersistentFlags() |
| Local (one command) | cmd.Flags() |
- Use
StringVar/BoolVar/CountVarP(pointer-binding) for all flags - Bind every flag to Viper:
viper.BindPFlag("name", cmd.Flags().Lookup("name")) - Use kebab-case for flag names:
--git-clone-protocol, not--gitCloneProtocol - Provide meaningful defaults and descriptions
Initialization Chain
Global dependencies are initialized via cobra.OnInitialize() in root.go. Each initializer sets a package-level global. Order matters — later initializers may depend on earlier ones. Read root.go for the current chain.
Error Handling in Commands
- Use
RunE(notRun) — return errors from the function; Cobra handles display and exit - Set
SilenceUsage: trueon the root command so runtime errors don't print usage - Keep
RunEfunctions thin — delegate to business logic packages
Signal Handling and Cleanup
- Register signal handlers (e.g.,
os.Interrupt,syscall.SIGTERM) early in the root command initialization - Use
PersistentPostRunon the root command for successful completion cleanup - Provide a dedicated cleanup function for error and signal exit paths
- Always clean up resources (loggers, temp files) on all exit paths
Key Rules
- Commands should not import each other; share state via package-level variables in
cmd/ - Use
fmt.Fprint(os.Stderr, ...)for error output