name: "cli-patterns" description: "Cobra commands in cmd/ package, flag conventions" domain: "architecture" confidence: "high" source: "hyoka/cmd/*.go"
Context
Hyoka's CLI is built with Cobra, a popular Go CLI framework. Commands are organized in the cmd/ package with consistent flag naming and help text patterns.
Command Structure
Each command is a Cobra command object:
// cmd/run.go
var runCmd = &cobra.Command{
Use: "run",
Short: "Run evaluation",
Long: "Run an evaluation with prompts and configs.",
Args: cobra.NoArgs, // Positional arguments check
RunE: func(cmd *cobra.Command, args []string) error {
// Retrieve flags
promptID, _ := cmd.Flags().GetString("prompt-id")
config, _ := cmd.Flags().GetString("config")
// Validate
if promptID == "" && config == "" {
return fmt.Errorf("either --prompt-id or --config required")
}
// Execute
return run(promptID, config)
},
}
func init() {
rootCmd.AddCommand(runCmd)
runCmd.Flags().StringP("prompt-id", "", "", "Single prompt ID")
runCmd.Flags().StringP("config", "", "", "Config (comma-sep for multiple)")
}
Flag Naming Conventions
- Kebab-case:
--log-level,--max-files,--prompt-id(not--logLevel,--maxfiles) - Short flags: Single-letter for common flags (e.g.,
-vfor--verbose) - Suffixes:
--*-timeoutfor duration limits (e.g.,--gen-timeout)--skip-*for skipping phases (e.g.,--skip-review)--*-filefor file paths (e.g.,--log-file)
Flag Types and Patterns
String flags:
cmd.Flags().StringP("config", "", "", "Config name")
Comma-separated lists:
cmd.Flags().String("tags", "", "Comma-separated tags (must quote: \"auth,crud\")")
// Parsed as: strings.Split(value, ",")
Boolean flags:
cmd.Flags().Bool("dry-run", false, "Show what would run without executing")
Numeric flags:
cmd.Flags().Int("max-files", 50, "Max files per eval")
cmd.Flags().Duration("gen-timeout", 600*time.Second, "Generation timeout")
Help Text Guidelines
Keep help text brief but actionable:
// Good
--config Config name or comma-sep list
--prompt-id Single prompt ID (e.g., identity-dp-python-default-credential)
--service Azure service (e.g., identity, key-vault)
--dry-run List matching prompts without executing
// Bad (too verbose, no examples)
--prompt-id The prompt identifier
--config The configuration to use
Command Execution Pattern
RunE: func(cmd *cobra.Command, args []string) error {
// 1. Get flags
promptID, _ := cmd.Flags().GetString("prompt-id")
config, _ := cmd.Flags().GetString("config")
// 2. Validate
if promptID == "" {
return fmt.Errorf("--prompt-id required")
}
// 3. Execute
ctx := context.Background()
return executeEval(ctx, promptID, config)
}
Common Commands in Hyoka
run— Execute evaluationlist— List available promptsvalidate— Validate prompt schemacheck-env— Check environment (Copilot SDK, Go version)clean— Clean orphaned sessionsserve— Start local web server for results
Error Handling in Commands
Return errors from RunE. Cobra catches them and prints with exit code 1:
RunE: func(cmd *cobra.Command, args []string) error {
result, err := runEval(...)
if err != nil {
slog.Error("Evaluation failed", "error", err)
return err // Cobra handles exit
}
return nil
}
Output and Logging
- User output (results, progress): Write to stdout
- Diagnostic logs: Use slog with
--log-levelflag - Errors: Return error from
RunE, let Cobra print
Flag Validation Example
RunE: func(cmd *cobra.Command, args []string) error {
config, _ := cmd.Flags().GetString("config")
allConfigs, _ := cmd.Flags().GetBool("all-configs")
if config == "" && !allConfigs {
return fmt.Errorf("either --config or --all-configs required")
}
if config != "" && allConfigs {
return fmt.Errorf("cannot use both --config and --all-configs")
}
return runWithConfig(config, allConfigs)
}
Related Code Locations
- Root command:
hyoka/cmd/root.go - Subcommands:
hyoka/cmd/*.go - Flag parsing utilities:
hyoka/cmd/common.go(if shared parsing logic)
Anti-Patterns
- CamelCase flag names
- Positional arguments instead of flags
- Ambiguous short flags (avoid single
-aif context unclear) - Printing errors and returning them (let Cobra handle)
- Hardcoding defaults without CLI override