diff options
| author | Paul Buetow <paul@buetow.org> | 2025-08-17 08:54:20 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-08-17 08:54:20 +0300 |
| commit | a61f40a4221c1586d801308da9ae1869fb6bdbb6 (patch) | |
| tree | 477ecc7313b4f9e6fd5dd57ea2a727b7befc8f13 /internal/appconfig | |
| parent | 17e9d33f2a5dc9305b4dbae1bc400b28f9b6c211 (diff) | |
cli: add hexai command-line tool and split LSP to hexai-lsp
- New cmd/hexai CLI: reads stdin/arg, prints LLM output to stdout
- Prints provider/model immediately to stderr; summary at end
- Refactor config loader to internal/appconfig
- Update Taskfile to build/install/run both binaries
- Update README with new CLI and LSP names
Diffstat (limited to 'internal/appconfig')
| -rw-r--r-- | internal/appconfig/config.go | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/internal/appconfig/config.go b/internal/appconfig/config.go new file mode 100644 index 0000000..d12bdbe --- /dev/null +++ b/internal/appconfig/config.go @@ -0,0 +1,102 @@ +package appconfig + +import ( + "encoding/json" + "log" + "os" + "path/filepath" + "strings" +) + +// App holds user-configurable settings read from ~/.config/hexai/config.json. +type App struct { + MaxTokens int `json:"max_tokens"` + ContextMode string `json:"context_mode"` + ContextWindowLines int `json:"context_window_lines"` + MaxContextTokens int `json:"max_context_tokens"` + LogPreviewLimit int `json:"log_preview_limit"` + NoDiskIO bool `json:"no_disk_io"` + TriggerCharacters []string `json:"trigger_characters"` + Provider string `json:"provider"` + // Provider-specific options + OpenAIBaseURL string `json:"openai_base_url"` + OpenAIModel string `json:"openai_model"` + OllamaBaseURL string `json:"ollama_base_url"` + OllamaModel string `json:"ollama_model"` + CopilotBaseURL string `json:"copilot_base_url"` + CopilotModel string `json:"copilot_model"` +} + +// Load reads configuration from ~/.config/hexai/config.json and merges with defaults. +func Load(logger *log.Logger) App { + cfg := App{ + MaxTokens: 4000, + ContextMode: "always-full", + ContextWindowLines: 120, + MaxContextTokens: 4000, + LogPreviewLimit: 100, + NoDiskIO: true, + } + home, err := os.UserHomeDir() + if err != nil { + return cfg + } + path := filepath.Join(home, ".config", "hexai", "config.json") + f, err := os.Open(path) + if err != nil { + return cfg + } + defer f.Close() + dec := json.NewDecoder(f) + var fileCfg App + if err := dec.Decode(&fileCfg); err != nil { + if logger != nil { + logger.Printf("invalid config file %s: %v", path, err) + } + return cfg + } + // Merge: file overrides defaults when provided + if fileCfg.MaxTokens > 0 { + cfg.MaxTokens = fileCfg.MaxTokens + } + if strings.TrimSpace(fileCfg.ContextMode) != "" { + cfg.ContextMode = fileCfg.ContextMode + } + if fileCfg.ContextWindowLines > 0 { + cfg.ContextWindowLines = fileCfg.ContextWindowLines + } + if fileCfg.MaxContextTokens > 0 { + cfg.MaxContextTokens = fileCfg.MaxContextTokens + } + if fileCfg.LogPreviewLimit >= 0 { + cfg.LogPreviewLimit = fileCfg.LogPreviewLimit + } + cfg.NoDiskIO = fileCfg.NoDiskIO + if len(fileCfg.TriggerCharacters) > 0 { + cfg.TriggerCharacters = append([]string{}, fileCfg.TriggerCharacters...) + } + if strings.TrimSpace(fileCfg.Provider) != "" { + cfg.Provider = fileCfg.Provider + } + // Provider-specific options + if strings.TrimSpace(fileCfg.OpenAIBaseURL) != "" { + cfg.OpenAIBaseURL = fileCfg.OpenAIBaseURL + } + if strings.TrimSpace(fileCfg.OpenAIModel) != "" { + cfg.OpenAIModel = fileCfg.OpenAIModel + } + if strings.TrimSpace(fileCfg.OllamaBaseURL) != "" { + cfg.OllamaBaseURL = fileCfg.OllamaBaseURL + } + if strings.TrimSpace(fileCfg.OllamaModel) != "" { + cfg.OllamaModel = fileCfg.OllamaModel + } + if strings.TrimSpace(fileCfg.CopilotBaseURL) != "" { + cfg.CopilotBaseURL = fileCfg.CopilotBaseURL + } + if strings.TrimSpace(fileCfg.CopilotModel) != "" { + cfg.CopilotModel = fileCfg.CopilotModel + } + return cfg +} + |
