package cmd import ( "fmt" "os" "path/filepath" "github.com/spf13/cobra" "github.com/spf13/viper" ) var cfgFile string // rootCmd is the base command when called without any subcommands. var rootCmd = &cobra.Command{ Use: "keyhunter", Short: "KeyHunter -- detect leaked LLM API keys across 108+ providers", Long: `KeyHunter scans files, git history, and internet sources for leaked LLM API keys. Supports 108+ providers with Aho-Corasick pre-filtering and regex + entropy detection.`, SilenceUsage: true, } // Execute is the entry point called by main.go. func Execute() { if err := rootCmd.Execute(); err != nil { os.Exit(1) } } func init() { cobra.OnInitialize(initConfig) rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default: ~/.keyhunter.yaml)") rootCmd.AddCommand(scanCmd) rootCmd.AddCommand(providersCmd) rootCmd.AddCommand(configCmd) rootCmd.AddCommand(legalCmd) // Stub commands for future phases (per CLI-01 requirement of 11 commands) rootCmd.AddCommand(verifyCmd) rootCmd.AddCommand(importCmd) rootCmd.AddCommand(reconCmd) rootCmd.AddCommand(keysCmd) rootCmd.AddCommand(serveCmd) rootCmd.AddCommand(dorksCmd) rootCmd.AddCommand(hookCmd) rootCmd.AddCommand(scheduleCmd) } func initConfig() { if cfgFile != "" { viper.SetConfigFile(cfgFile) } else { home, err := os.UserHomeDir() if err != nil { fmt.Fprintln(os.Stderr, "warning: cannot determine home directory:", err) return } viper.SetConfigName(".keyhunter") viper.SetConfigType("yaml") viper.AddConfigPath(home) viper.AddConfigPath(".") } viper.SetEnvPrefix("KEYHUNTER") viper.AutomaticEnv() // Defaults viper.SetDefault("scan.workers", 0) // 0 = auto (CPU*8) viper.SetDefault("database.path", filepath.Join(mustHomeDir(), ".keyhunter", "keyhunter.db")) // Config file is optional -- ignore if not found _ = viper.ReadInConfig() } func mustHomeDir() string { h, _ := os.UserHomeDir() return h }