package config import ( "os" "path/filepath" "sync" "gopkg.in/yaml.v3" ) // Config holds all client configuration. type Config struct { EngineURL string `yaml:"engine_url"` APIKey string `yaml:"api_key"` DefaultFormat string `yaml:"default_format"` } var ( cfg Config once sync.Once ) // Load reads configuration from ~/.kb/client.yaml, then applies environment // variable overrides. Defaults are applied first. func Load() error { var loadErr error once.Do(func() { // Defaults cfg = Config{ EngineURL: "http://localhost:8000", DefaultFormat: "human", } // Read config file if it exists home, err := os.UserHomeDir() if err == nil { path := filepath.Join(home, ".kb", "client.yaml") data, err := os.ReadFile(path) if err == nil { var fileCfg Config if err := yaml.Unmarshal(data, &fileCfg); err == nil { if fileCfg.EngineURL != "" { cfg.EngineURL = fileCfg.EngineURL } if fileCfg.APIKey != "" { cfg.APIKey = fileCfg.APIKey } if fileCfg.DefaultFormat != "" { cfg.DefaultFormat = fileCfg.DefaultFormat } } } } // Environment variable overrides if v := os.Getenv("KB_ENGINE_URL"); v != "" { cfg.EngineURL = v } if v := os.Getenv("KB_API_KEY"); v != "" { cfg.APIKey = v } }) return loadErr } // Get returns the loaded configuration singleton. func Get() *Config { return &cfg } // ApplyFlags overrides configuration with CLI flag values (only if non-empty). func ApplyFlags(engine, format, apiKey string) { if engine != "" { cfg.EngineURL = engine } if format != "" { cfg.DefaultFormat = format } if apiKey != "" { cfg.APIKey = apiKey } }