diff options
| author | paul@buetow.org <paul@buetow.org> | 2026-02-06 16:35:45 +0200 |
|---|---|---|
| committer | paul@buetow.org <paul@buetow.org> | 2026-02-06 16:35:45 +0200 |
| commit | 12a249282d5dd9dc2ee1e66f08d6acc26dd29eba (patch) | |
| tree | 5e9ae4fbd1696d1b668dfe0be791004a87fc7a6a /docs | |
| parent | 89dc2aab0b6be2620766a4b4b750fa888641b89d (diff) | |
Remove GitHub Copilot provider support
Remove all GitHub Copilot integration from the codebase to streamline
the supported provider set to OpenAI, OpenRouter, Anthropic, and Ollama.
Changes:
- Delete core Copilot implementation (copilot.go) and all related tests
- Remove Copilot configuration fields from App struct and Config
- Remove Copilot from provider factory and API key handling
- Update all test files to replace Copilot references with other providers
- Remove Copilot documentation from README, configuration guide, and examples
- Remove Copilot section from config.toml.example
All tests pass successfully after removal.
Co-authored-by: Cursor <cursoragent@cursor.com>
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/configuration.md | 8 | ||||
| -rw-r--r-- | docs/coverage.html | 9825 | ||||
| -rw-r--r-- | docs/coverage.out | 52849 |
3 files changed, 28845 insertions, 33837 deletions
diff --git a/docs/configuration.md b/docs/configuration.md index 54ac85f..f4469a9 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -20,7 +20,6 @@ Environment overrides - `HEXAI_CHAT_SUFFIX`, `HEXAI_CHAT_PREFIXES` (comma-separated) - `HEXAI_OPENAI_MODEL`, `HEXAI_OPENAI_BASE_URL`, `HEXAI_OPENAI_TEMPERATURE` - `HEXAI_OPENROUTER_MODEL`, `HEXAI_OPENROUTER_BASE_URL`, `HEXAI_OPENROUTER_TEMPERATURE` - - `HEXAI_COPILOT_MODEL`, `HEXAI_COPILOT_BASE_URL`, `HEXAI_COPILOT_TEMPERATURE` - `HEXAI_OLLAMA_MODEL`, `HEXAI_OLLAMA_BASE_URL`, `HEXAI_OLLAMA_TEMPERATURE` - Per-surface overrides: `HEXAI_MODEL_COMPLETION`, `HEXAI_MODEL_CODE_ACTION`, `HEXAI_MODEL_CHAT`, `HEXAI_MODEL_CLI` - Per-surface temperatures: `HEXAI_TEMPERATURE_COMPLETION`, `HEXAI_TEMPERATURE_CODE_ACTION`, `HEXAI_TEMPERATURE_CHAT`, `HEXAI_TEMPERATURE_CLI` @@ -37,7 +36,7 @@ Per-surface models [models.code_action] model = "gpt-4o" - provider = "copilot" + provider = "openai" temperature = 0.4 [models.cli] @@ -47,7 +46,7 @@ Per-surface models - Repeating the table (`[[models.<surface>]]`) configures multiple provider/model pairs. Completion requests and the Hexai CLI fan out to every configured entry concurrently and label the responses with `provider:model`. Code actions continue to use the first entry only; any extra [[models.code_action]] tables are ignored at runtime and the loader logs a warning so you know an additional entry was skipped. -- When a per-surface value is omitted, Hexai falls back to the provider’s configured default. Temperatures inherit from `coding_temperature` unless explicitly set, and OpenAI `gpt-5*` models automatically raise an unspecified coding temperature to `1.0` for exploratory behavior. Provider overrides support `"openai"`, `"openrouter"`, `"copilot"`, or `"ollama"` and read the matching credential variables. +- When a per-surface value is omitted, Hexai falls back to the provider’s configured default. Temperatures inherit from `coding_temperature` unless explicitly set, and OpenAI `gpt-5*` models automatically raise an unspecified coding temperature to `1.0` for exploratory behavior. Provider overrides support `"openai"`, `"openrouter"`, `"anthropic"`, or `"ollama"` and read the matching credential variables. Runtime reloads @@ -61,11 +60,10 @@ API keys: - OpenAI: prefer `HEXAI_OPENAI_API_KEY`, falling back to `OPENAI_API_KEY`. - OpenRouter: prefer `HEXAI_OPENROUTER_API_KEY`, falling back to `OPENROUTER_API_KEY`. -- Copilot: prefer `HEXAI_COPILOT_API_KEY`, falling back to `COPILOT_API_KEY`. Selecting a provider -- Sectioned: set `[provider] name = "openai" | "openrouter" | "copilot" | "ollama"`. +- Sectioned: set `[provider] name = "openai" | "openrouter" | "anthropic" | "ollama"`. - If omitted, Hexai defaults to `openai`. - Selecting `openrouter` uses https://openrouter.ai/api/v1 by default and automatically sends the required `HTTP-Referer` (`https://github.com/snonux/hexai`) and `X-Title` (`Hexai`) headers. Override the base URL via `[openrouter]` or environment variables when needed. diff --git a/docs/coverage.html b/docs/coverage.html deleted file mode 100644 index 4526ad1..0000000 --- a/docs/coverage.html +++ /dev/null @@ -1,9825 +0,0 @@ - -<!DOCTYPE html> -<html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - <title>hexai-lsp: Go Coverage Report</title> - <style> - body { - background: black; - color: rgb(80, 80, 80); - } - body, pre, #legend span { - font-family: Menlo, monospace; - font-weight: bold; - } - #topbar { - background: black; - position: fixed; - top: 0; left: 0; right: 0; - height: 42px; - border-bottom: 1px solid rgb(80, 80, 80); - } - #content { - margin-top: 50px; - } - #nav, #legend { - float: left; - margin-left: 10px; - } - #legend { - margin-top: 12px; - } - #nav { - margin-top: 10px; - } - #legend span { - margin: 0 5px; - } - .cov0 { color: rgb(192, 0, 0) } -.cov1 { color: rgb(128, 128, 128) } -.cov2 { color: rgb(116, 140, 131) } -.cov3 { color: rgb(104, 152, 134) } -.cov4 { color: rgb(92, 164, 137) } -.cov5 { color: rgb(80, 176, 140) } -.cov6 { color: rgb(68, 188, 143) } -.cov7 { color: rgb(56, 200, 146) } -.cov8 { color: rgb(44, 212, 149) } -.cov9 { color: rgb(32, 224, 152) } -.cov10 { color: rgb(20, 236, 155) } - - </style> - </head> - <body> - <div id="topbar"> - <div id="nav"> - <select id="files"> - - <option value="file0">codeberg.org/snonux/hexai/cmd/hexai-lsp/main.go (73.3%)</option> - - <option value="file1">codeberg.org/snonux/hexai/cmd/hexai-tmux-action/main.go (0.0%)</option> - - <option value="file2">codeberg.org/snonux/hexai/cmd/hexai/main.go (61.9%)</option> - - <option value="file3">codeberg.org/snonux/hexai/internal/appconfig/config.go (82.5%)</option> - - <option value="file4">codeberg.org/snonux/hexai/internal/editor/editor.go (58.3%)</option> - - <option value="file5">codeberg.org/snonux/hexai/internal/hexaiaction/cmdentry.go (84.5%)</option> - - <option value="file6">codeberg.org/snonux/hexai/internal/hexaiaction/parse.go (92.6%)</option> - - <option value="file7">codeberg.org/snonux/hexai/internal/hexaiaction/prompts.go (92.3%)</option> - - <option value="file8">codeberg.org/snonux/hexai/internal/hexaiaction/run.go (71.1%)</option> - - <option value="file9">codeberg.org/snonux/hexai/internal/hexaiaction/tui.go (65.5%)</option> - - <option value="file10">codeberg.org/snonux/hexai/internal/hexaiaction/tui_custom.go (83.8%)</option> - - <option value="file11">codeberg.org/snonux/hexai/internal/hexaiaction/tui_delegate.go (100.0%)</option> - - <option value="file12">codeberg.org/snonux/hexai/internal/hexaicli/run.go (72.3%)</option> - - <option value="file13">codeberg.org/snonux/hexai/internal/hexailsp/run.go (88.9%)</option> - - <option value="file14">codeberg.org/snonux/hexai/internal/llm/copilot.go (82.4%)</option> - - <option value="file15">codeberg.org/snonux/hexai/internal/llm/ollama.go (89.8%)</option> - - <option value="file16">codeberg.org/snonux/hexai/internal/llm/openai.go (87.1%)</option> - - <option value="file17">codeberg.org/snonux/hexai/internal/llm/openrouter.go (76.2%)</option> - - <option value="file18">codeberg.org/snonux/hexai/internal/llm/provider.go (86.0%)</option> - - <option value="file19">codeberg.org/snonux/hexai/internal/llm/util.go (100.0%)</option> - - <option value="file20">codeberg.org/snonux/hexai/internal/llmutils/client.go (100.0%)</option> - - <option value="file21">codeberg.org/snonux/hexai/internal/logging/chatlogger.go (100.0%)</option> - - <option value="file22">codeberg.org/snonux/hexai/internal/logging/logging.go (90.9%)</option> - - <option value="file23">codeberg.org/snonux/hexai/internal/lsp/chat_commands.go (83.3%)</option> - - <option value="file24">codeberg.org/snonux/hexai/internal/lsp/context.go (74.4%)</option> - - <option value="file25">codeberg.org/snonux/hexai/internal/lsp/document.go (91.5%)</option> - - <option value="file26">codeberg.org/snonux/hexai/internal/lsp/handlers.go (89.8%)</option> - - <option value="file27">codeberg.org/snonux/hexai/internal/lsp/handlers_codeaction.go (82.0%)</option> - - <option value="file28">codeberg.org/snonux/hexai/internal/lsp/handlers_completion.go (79.0%)</option> - - <option value="file29">codeberg.org/snonux/hexai/internal/lsp/handlers_document.go (78.1%)</option> - - <option value="file30">codeberg.org/snonux/hexai/internal/lsp/handlers_execute.go (75.0%)</option> - - <option value="file31">codeberg.org/snonux/hexai/internal/lsp/handlers_init.go (66.7%)</option> - - <option value="file32">codeberg.org/snonux/hexai/internal/lsp/handlers_utils.go (85.3%)</option> - - <option value="file33">codeberg.org/snonux/hexai/internal/lsp/server.go (82.1%)</option> - - <option value="file34">codeberg.org/snonux/hexai/internal/lsp/transport.go (73.0%)</option> - - <option value="file35">codeberg.org/snonux/hexai/internal/runtimeconfig/store.go (88.1%)</option> - - <option value="file36">codeberg.org/snonux/hexai/internal/stats/lock_posix.go (83.3%)</option> - - <option value="file37">codeberg.org/snonux/hexai/internal/stats/stats.go (75.8%)</option> - - <option value="file38">codeberg.org/snonux/hexai/internal/testutil/fixtures.go (100.0%)</option> - - <option value="file39">codeberg.org/snonux/hexai/internal/textutil/human.go (92.3%)</option> - - <option value="file40">codeberg.org/snonux/hexai/internal/textutil/textutil.go (90.4%)</option> - - <option value="file41">codeberg.org/snonux/hexai/internal/tmux/status.go (76.7%)</option> - - <option value="file42">codeberg.org/snonux/hexai/internal/tmux/tmux.go (88.6%)</option> - - </select> - </div> - <div id="legend"> - <span>not tracked</span> - - <span class="cov0">no coverage</span> - <span class="cov1">low coverage</span> - <span class="cov2">*</span> - <span class="cov3">*</span> - <span class="cov4">*</span> - <span class="cov5">*</span> - <span class="cov6">*</span> - <span class="cov7">*</span> - <span class="cov8">*</span> - <span class="cov9">*</span> - <span class="cov10">high coverage</span> - - </div> - </div> - <div id="content"> - - <pre class="file" id="file0" style="display: none">// Summary: Hexai LSP entrypoint; parses flags and delegates to internal/hexailsp. -package main - -import ( - "flag" - "fmt" - "log" - "os" - "strings" - - "codeberg.org/snonux/hexai/internal" - "codeberg.org/snonux/hexai/internal/appconfig" - "codeberg.org/snonux/hexai/internal/hexailsp" -) - -func main() <span class="cov8" title="1">{ - logPath := flag.String("log", "/tmp/hexai-lsp.log", "path to log file (optional)") - defaultCfg := defaultConfigPath() - configPath := flag.String("config", "", fmt.Sprintf("path to config file (default: %s)", defaultCfg)) - showVersion := flag.Bool("version", false, "print version and exit") - flag.Parse() - if *showVersion </span><span class="cov8" title="1">{ - log.Println(internal.Version) - return - }</span> - - <span class="cov0" title="0">path := strings.TrimSpace(*configPath) - if err := hexailsp.RunWithConfig(*logPath, path, os.Stdin, os.Stdout, os.Stderr); err != nil </span><span class="cov0" title="0">{ - log.Fatalf("server error: %v", err) - }</span> -} - -func defaultConfigPath() string <span class="cov8" title="1">{ - path, err := appconfig.ConfigPath() - if err != nil </span><span class="cov0" title="0">{ - return "$XDG_CONFIG_HOME/hexai/config.toml" - }</span> - <span class="cov8" title="1">return path</span> -} -</pre> - - <pre class="file" id="file1" style="display: none">package main - -import ( - "context" - "flag" - "fmt" - "os" - "strings" - - "codeberg.org/snonux/hexai/internal/appconfig" - "codeberg.org/snonux/hexai/internal/hexaiaction" -) - -func main() <span class="cov0" title="0">{ - infile := flag.String("infile", "", "Read input from this file instead of stdin") - outfile := flag.String("outfile", "", "Write output to this file instead of stdout") - uiChild := flag.Bool("ui-child", false, "INTERNAL: run interactive UI and write to -outfile atomically") - defaultPath := defaultConfigPath() - configPath := flag.String("config", "", fmt.Sprintf("path to config file (default: %s)", defaultPath)) - tmuxTarget := flag.String("tmux-target", "", "tmux split target (advanced)") - tmuxSplit := flag.String("tmux-split", "v", "tmux split orientation: v or h") - tmuxPercent := flag.Int("tmux-percent", 33, "tmux split size percentage (1-100)") - flag.Parse() - - opts := hexaiaction.Options{ - Infile: *infile, Outfile: *outfile, - UIChild: *uiChild, TmuxTarget: *tmuxTarget, TmuxSplit: *tmuxSplit, TmuxPercent: *tmuxPercent, - } - ctx := context.Background() - if path := strings.TrimSpace(*configPath); path != "" </span><span class="cov0" title="0">{ - ctx = hexaiaction.WithConfigPath(ctx, path) - }</span> - <span class="cov0" title="0">if err := hexaiaction.RunCommand(ctx, opts, os.Stdin, os.Stdout, os.Stderr); err != nil </span><span class="cov0" title="0">{ - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - }</span> -} - -func defaultConfigPath() string <span class="cov0" title="0">{ - path, err := appconfig.ConfigPath() - if err != nil </span><span class="cov0" title="0">{ - return "$XDG_CONFIG_HOME/hexai/config.toml" - }</span> - <span class="cov0" title="0">return path</span> -} -</pre> - - <pre class="file" id="file2" style="display: none">// Summary: Hexai CLI entrypoint; parses flags and delegates to internal/hexaicli. -package main - -import ( - "context" - "flag" - "fmt" - "io" - "log" - "os" - "strconv" - "strings" - - "codeberg.org/snonux/hexai/internal" - "codeberg.org/snonux/hexai/internal/appconfig" - "codeberg.org/snonux/hexai/internal/hexaicli" -) - -func main() <span class="cov8" title="1">{ - configPath, remaining := splitConfigPath(os.Args[1:]) - logger := log.New(io.Discard, "", 0) - cfg := appconfig.LoadWithOptions(logger, appconfig.LoadOptions{ConfigPath: configPath}) - cliEntries := cfg.CLIConfigs - if len(cliEntries) == 0 </span><span class="cov8" title="1">{ - cliEntries = []appconfig.SurfaceConfig{{Provider: cfg.Provider}} - }</span> - <span class="cov8" title="1">fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError) - defaultPath := defaultConfigPath() - configFlag := fs.String("config", configPath, fmt.Sprintf("path to config file (default: %s)", defaultPath)) - showVersion := fs.Bool("version", false, "print version and exit") - selectedFlags := make([]bool, len(cliEntries)) - for i, entry := range cliEntries </span><span class="cov8" title="1">{ - name := strconv.Itoa(i) - provider := strings.TrimSpace(entry.Provider) - if provider == "" </span><span class="cov8" title="1">{ - provider = cfg.Provider - }</span> - <span class="cov8" title="1">model := strings.TrimSpace(entry.Model) - if model == "" </span><span class="cov8" title="1">{ - model = pickDefaultModel(cfg, provider) - }</span> - <span class="cov8" title="1">desc := fmt.Sprintf("use only provider #%d (%s:%s)", i, provider, model) - fs.BoolVar(&selectedFlags[i], name, false, desc)</span> - } - <span class="cov8" title="1">_ = fs.Parse(remaining) - if *showVersion </span><span class="cov8" title="1">{ - fmt.Fprintln(os.Stdout, internal.Version) - return - }</span> - <span class="cov0" title="0">var selection []int - for i, sel := range selectedFlags </span><span class="cov0" title="0">{ - if sel </span><span class="cov0" title="0">{ - selection = append(selection, i) - }</span> - } - <span class="cov0" title="0">finalPath := strings.TrimSpace(*configFlag) - if finalPath == "" </span><span class="cov0" title="0">{ - finalPath = configPath - }</span> - <span class="cov0" title="0">ctx := context.Background() - if finalPath != "" </span><span class="cov0" title="0">{ - ctx = hexaicli.WithCLIConfigPath(ctx, finalPath) - }</span> - <span class="cov0" title="0">if len(selection) > 0 </span><span class="cov0" title="0">{ - ctx = hexaicli.WithCLISelection(ctx, selection) - }</span> - <span class="cov0" title="0">if err := hexaicli.Run(ctx, fs.Args(), os.Stdin, os.Stdout, os.Stderr); err != nil </span><span class="cov0" title="0">{ - os.Exit(1) - }</span> -} - -func splitConfigPath(args []string) (string, []string) <span class="cov8" title="1">{ - var path string - rest := make([]string, 0, len(args)) - skip := false - for i := 0; i < len(args); i++ </span><span class="cov8" title="1">{ - if skip </span><span class="cov0" title="0">{ - skip = false - continue</span> - } - <span class="cov8" title="1">arg := args[i] - switch </span>{ - case arg == "--config" || arg == "-config":<span class="cov0" title="0"> - if i+1 < len(args) </span><span class="cov0" title="0">{ - path = args[i+1] - skip = true - }</span> - case strings.HasPrefix(arg, "--config="):<span class="cov0" title="0"> - path = arg[len("--config="):]</span> - case strings.HasPrefix(arg, "-config="):<span class="cov0" title="0"> - path = arg[len("-config="):]</span> - default:<span class="cov8" title="1"> - rest = append(rest, arg)</span> - } - } - <span class="cov8" title="1">return strings.TrimSpace(path), rest</span> -} - -func pickDefaultModel(cfg appconfig.App, provider string) string <span class="cov8" title="1">{ - switch strings.ToLower(strings.TrimSpace(provider)) </span>{ - case "ollama":<span class="cov0" title="0"> - return strings.TrimSpace(cfg.OllamaModel)</span> - case "copilot":<span class="cov0" title="0"> - return strings.TrimSpace(cfg.CopilotModel)</span> - default:<span class="cov8" title="1"> - return strings.TrimSpace(cfg.OpenAIModel)</span> - } -} - -func defaultConfigPath() string <span class="cov8" title="1">{ - cfgPath, err := appconfig.ConfigPath() - if err != nil </span><span class="cov0" title="0">{ - return "$XDG_CONFIG_HOME/hexai/config.toml" - }</span> - <span class="cov8" title="1">return cfgPath</span> -} -</pre> - - <pre class="file" id="file3" style="display: none">// Summary: Application configuration model and loader; reads ~/.config/hexai/config.toml and merges defaults. -package appconfig - -import ( - "fmt" - "log" - "os" - "path/filepath" - "slices" - "strconv" - "strings" - - "github.com/pelletier/go-toml/v2" -) - -// SurfaceConfig describes a provider/model pairing (with optional temperature). -type SurfaceConfig struct { - Provider string - Model string - Temperature *float64 -} - -// App holds user-configurable settings read from ~/.config/hexai/config.toml. -type App struct { - MaxTokens int `json:"max_tokens" toml:"max_tokens"` - ContextMode string `json:"context_mode" toml:"context_mode"` - ContextWindowLines int `json:"context_window_lines" toml:"context_window_lines"` - MaxContextTokens int `json:"max_context_tokens" toml:"max_context_tokens"` - LogPreviewLimit int `json:"log_preview_limit" toml:"log_preview_limit"` - // Single knob for LSP requests; if set, overrides hardcoded temps in LSP. - CodingTemperature *float64 `json:"coding_temperature" toml:"coding_temperature"` - // Minimum identifier characters required for manual (TriggerKind=1) invoke - // to proceed without structural triggers. 0 means always allow. - ManualInvokeMinPrefix int `json:"manual_invoke_min_prefix" toml:"manual_invoke_min_prefix"` - - // Completion debounce in milliseconds. When > 0, the server waits until - // there has been no text change for at least this duration before sending - // an LLM completion request. - CompletionDebounceMs int `json:"completion_debounce_ms" toml:"completion_debounce_ms"` - // Completion throttle in milliseconds. When > 0, caps the minimum spacing - // between LLM requests (both chat and code-completer paths). - CompletionThrottleMs int `json:"completion_throttle_ms" toml:"completion_throttle_ms"` - - TriggerCharacters []string `json:"trigger_characters" toml:"trigger_characters"` - Provider string `json:"provider" toml:"provider"` - - // Inline prompt trigger characters (default: >!text> and >>!text>) - InlineOpen string `json:"inline_open" toml:"inline_open"` - InlineClose string `json:"inline_close" toml:"inline_close"` - // In-editor chat triggers (default: suffix ">" after one of [?, !, :, ;]) - ChatSuffix string `json:"chat_suffix" toml:"chat_suffix"` - ChatPrefixes []string `json:"chat_prefixes" toml:"chat_prefixes"` - - // Provider-specific options - OpenAIBaseURL string `json:"openai_base_url" toml:"openai_base_url"` - OpenAIModel string `json:"openai_model" toml:"openai_model"` - // Default temperature for OpenAI requests (nil means use provider default) - OpenAITemperature *float64 `json:"openai_temperature" toml:"openai_temperature"` - OpenRouterBaseURL string `json:"openrouter_base_url" toml:"openrouter_base_url"` - OpenRouterModel string `json:"openrouter_model" toml:"openrouter_model"` - // Default temperature for OpenRouter requests (nil means use provider default) - OpenRouterTemperature *float64 `json:"openrouter_temperature" toml:"openrouter_temperature"` - OllamaBaseURL string `json:"ollama_base_url" toml:"ollama_base_url"` - OllamaModel string `json:"ollama_model" toml:"ollama_model"` - // Default temperature for Ollama requests (nil means use provider default) - OllamaTemperature *float64 `json:"ollama_temperature" toml:"ollama_temperature"` - CopilotBaseURL string `json:"copilot_base_url" toml:"copilot_base_url"` - CopilotModel string `json:"copilot_model" toml:"copilot_model"` - // Default temperature for Copilot requests (nil means use provider default) - CopilotTemperature *float64 `json:"copilot_temperature" toml:"copilot_temperature"` - - // Per-surface provider/model configurations (ordered; first entry is primary) - CompletionConfigs []SurfaceConfig `json:"-" toml:"-"` - CodeActionConfigs []SurfaceConfig `json:"-" toml:"-"` - ChatConfigs []SurfaceConfig `json:"-" toml:"-"` - CLIConfigs []SurfaceConfig `json:"-" toml:"-"` - - // Prompt templates (configured only via file; no env overrides) - // Completion/chat/code action/CLI prompt strings. See config.toml.example for placeholders. - // Completion - PromptCompletionSystemGeneral string `json:"-" toml:"-"` - PromptCompletionSystemParams string `json:"-" toml:"-"` - PromptCompletionSystemInline string `json:"-" toml:"-"` - PromptCompletionUserGeneral string `json:"-" toml:"-"` - PromptCompletionUserParams string `json:"-" toml:"-"` - PromptCompletionExtraHeader string `json:"-" toml:"-"` - // Provider-native code-completer - PromptNativeCompletion string `json:"-" toml:"-"` - // In-editor chat - PromptChatSystem string `json:"-" toml:"-"` - // Code actions - PromptCodeActionRewriteSystem string `json:"-" toml:"-"` - PromptCodeActionDiagnosticsSystem string `json:"-" toml:"-"` - PromptCodeActionDocumentSystem string `json:"-" toml:"-"` - PromptCodeActionRewriteUser string `json:"-" toml:"-"` - PromptCodeActionDiagnosticsUser string `json:"-" toml:"-"` - PromptCodeActionDocumentUser string `json:"-" toml:"-"` - PromptCodeActionGoTestSystem string `json:"-" toml:"-"` - PromptCodeActionGoTestUser string `json:"-" toml:"-"` - PromptCodeActionSimplifySystem string `json:"-" toml:"-"` - PromptCodeActionSimplifyUser string `json:"-" toml:"-"` - // CLI - PromptCLIDefaultSystem string `json:"-" toml:"-"` - PromptCLIExplainSystem string `json:"-" toml:"-"` - - // Custom code actions and tmux integration - CustomActions []CustomAction `json:"-" toml:"-"` - TmuxCustomMenuHotkey string `json:"-" toml:"-"` - // Stats - StatsWindowMinutes int `json:"-" toml:"-"` -} - -// CustomAction describes a user-defined code action. -type CustomAction struct { - ID string - Title string - Kind string // optional; default "refactor" - Scope string // "selection" (default) | "diagnostics" - Hotkey string // optional, used by tmux submenu - Instruction string // optional; if set and User is empty, use global rewrite templates - System string // optional; used only when User is set - User string // optional; if set, render with available vars -} - -// Constructor: defaults for App (kept first among functions) -func newDefaultConfig() App <span class="cov5" title="51">{ - // Coding-friendly default temperature across providers - // Users can override per provider in config.toml (including 0.0). - t := 0.2 - return App{ - MaxTokens: 4000, - ContextMode: "always-full", - ContextWindowLines: 120, - MaxContextTokens: 4000, - LogPreviewLimit: 100, - CodingTemperature: &t, - OpenAITemperature: &t, - OllamaTemperature: &t, - CopilotTemperature: &t, - ManualInvokeMinPrefix: 0, - CompletionDebounceMs: 800, - CompletionThrottleMs: 0, - // Inline/chat trigger defaults - InlineOpen: ">!", - InlineClose: ">", - ChatSuffix: ">", - ChatPrefixes: []string{"?", "!", ":", ";"}, - - // Default prompt templates (match current hard-coded strings) - PromptCompletionSystemParams: "You are a code completion engine for function signatures. Return only the parameter list contents (without parentheses), no braces, no prose. Prefer idiomatic names and types.", - PromptCompletionUserParams: "Cursor is inside the function parameter list. Suggest only the parameter list (no parentheses).\nFunction line: {{function}}\nCurrent line (cursor at {{char}}): {{current}}", - PromptCompletionSystemGeneral: "You are a terse code completion engine. Return only the code to insert, no surrounding prose or backticks. Only continue from the cursor; never repeat characters already present to the left of the cursor on the current line (e.g., if 'name :=' is already typed, only return the right-hand side expression).", - PromptCompletionUserGeneral: "Provide the next likely code to insert at the cursor.\nFile: {{file}}\nFunction/context: {{function}}\nAbove line: {{above}}\nCurrent line (cursor at character {{char}}): {{current}}\nBelow line: {{below}}\nOnly return the completion snippet.", - PromptCompletionSystemInline: "You are a precise code completion/refactoring engine. Output only the code to insert with no prose, no comments, and no backticks. Return raw code only.", - PromptCompletionExtraHeader: "Additional context:\n{{context}}", - - PromptNativeCompletion: "// Path: {{path}}\n{{before}}", - - PromptChatSystem: "You are a helpful coding assistant. Answer concisely and clearly.", - - PromptCodeActionRewriteSystem: "You are a precise code refactoring engine. Rewrite the given code strictly according to the instruction. Return only the updated code with no prose or backticks. Preserve formatting where reasonable.", - PromptCodeActionDiagnosticsSystem: "You are a precise code fixer. Resolve the given diagnostics by editing only the selected code. Return only the corrected code with no prose or backticks. Keep behavior and style, and avoid unrelated changes.", - PromptCodeActionDocumentSystem: "You are a precise code documentation engine. Add idiomatic documentation comments to the given code. Preserve exact behavior and formatting as much as possible. Return only the updated code with comments, no prose or backticks.", - PromptCodeActionRewriteUser: "Instruction: {{instruction}}\n\nSelected code to transform:\n{{selection}}", - PromptCodeActionDiagnosticsUser: "Diagnostics to resolve (selection only):\n{{diagnostics}}\n\nSelected code:\n{{selection}}", - PromptCodeActionDocumentUser: "Add documentation comments to this code:\n{{selection}}", - PromptCodeActionGoTestSystem: "You are a precise Go unit test generator. Given a Go function, write one or more Test* functions using the testing package. Do NOT include package or imports, only the test function(s). Prefer table-driven tests. Keep it minimal and idiomatic.", - PromptCodeActionGoTestUser: "Function under test:\n{{function}}", - PromptCodeActionSimplifySystem: "You are a precise code improvement engine. Simplify and improve the given code while preserving behavior. Return only the improved code with no prose or backticks.", - PromptCodeActionSimplifyUser: "Improve this code:\n{{selection}}", - - PromptCLIDefaultSystem: "You are Hexai CLI. Default to very short, concise answers. If the user asks for commands, output only the commands (one per line) with no commentary or explanation. Only when the word 'explain' appears in the prompt, produce a verbose explanation.", - PromptCLIExplainSystem: "You are Hexai CLI. The user requested an explanation. Provide a clear, verbose explanation with reasoning and details. If commands are needed, include them with brief context.", - - // Stats - StatsWindowMinutes: 60, - } -}</span> - -// Load reads configuration from a file and merges with defaults. -// It respects the XDG Base Directory Specification. -func Load(logger *log.Logger) App <span class="cov5" title="35">{ return LoadWithOptions(logger, LoadOptions{}) }</span> - -// LoadOptions tune how configuration is loaded at runtime. -type LoadOptions struct { - // IgnoreEnv skips applying environment overrides when true. - IgnoreEnv bool - ConfigPath string -} - -// LoadWithOptions reads configuration and applies the requested loading options. -func LoadWithOptions(logger *log.Logger, opts LoadOptions) App <span class="cov5" title="50">{ - cfg := newDefaultConfig() - if logger == nil </span><span class="cov4" title="13">{ - return cfg // Return defaults if no logger is provided (e.g. in tests) - }</span> - - <span class="cov5" title="37">configPath := strings.TrimSpace(opts.ConfigPath) - if configPath != "" </span><span class="cov0" title="0">{ - if fileCfg, err := loadFromFile(configPath, logger); err == nil && fileCfg != nil </span><span class="cov0" title="0">{ - cfg.mergeWith(fileCfg) - }</span> else<span class="cov0" title="0"> if err != nil </span><span class="cov0" title="0">{ - logger.Printf("cannot open config file %s: %v", configPath, err) - }</span> - } else<span class="cov5" title="37"> { - path, err := getConfigPath() - if err != nil </span><span class="cov0" title="0">{ - logger.Printf("%v", err) - }</span> else<span class="cov5" title="37"> if fileCfg, err := loadFromFile(path, logger); err == nil && fileCfg != nil </span><span class="cov4" title="21">{ - cfg.mergeWith(fileCfg) - }</span> - } - - <span class="cov5" title="37">if !opts.IgnoreEnv </span><span class="cov5" title="33">{ - // Environment overrides (take precedence over file) - if envCfg := loadFromEnv(logger); envCfg != nil </span><span class="cov3" title="9">{ - cfg.mergeWith(envCfg) - }</span> - } - <span class="cov5" title="37">return cfg</span> -} - -// Private helpers -// Sectioned (table-based) file format only. -type fileConfig struct { - // Section tables only (flat keys are not allowed) - General sectionGeneral `toml:"general"` - Logging sectionLogging `toml:"logging"` - Completion sectionCompletion `toml:"completion"` - Triggers sectionTriggers `toml:"triggers"` - Inline sectionInline `toml:"inline"` - Chat sectionChat `toml:"chat"` - Provider sectionProvider `toml:"provider"` - OpenAI sectionOpenAI `toml:"openai"` - OpenRouter sectionOpenRouter `toml:"openrouter"` - Copilot sectionCopilot `toml:"copilot"` - Ollama sectionOllama `toml:"ollama"` - Prompts sectionPrompts `toml:"prompts"` - Tmux sectionTmux `toml:"tmux"` - Stats sectionStats `toml:"stats"` -} - -type sectionGeneral struct { - MaxTokens int `toml:"max_tokens"` - ContextMode string `toml:"context_mode"` - ContextWindowLines int `toml:"context_window_lines"` - MaxContextTokens int `toml:"max_context_tokens"` - CodingTemperature *float64 `toml:"coding_temperature"` -} - -type sectionLogging struct { - LogPreviewLimit int `toml:"log_preview_limit"` -} - -type sectionCompletion struct { - CompletionDebounceMs int `toml:"completion_debounce_ms"` - CompletionThrottleMs int `toml:"completion_throttle_ms"` - ManualInvokeMinPrefix int `toml:"manual_invoke_min_prefix"` -} - -type sectionTriggers struct { - TriggerCharacters []string `toml:"trigger_characters"` -} - -type sectionInline struct { - InlineOpen string `toml:"inline_open"` - InlineClose string `toml:"inline_close"` -} - -type sectionChat struct { - ChatSuffix string `toml:"chat_suffix"` - ChatPrefixes []string `toml:"chat_prefixes"` -} - -type sectionProvider struct { - Name string `toml:"name"` -} - -type sectionStats struct { - WindowMinutes int `toml:"window_minutes"` -} - -type sectionOpenAI struct { - Model string `toml:"model"` - BaseURL string `toml:"base_url"` - Temperature *float64 `toml:"temperature"` - Presets map[string]string `toml:"presets"` -} - -func (s sectionOpenAI) isZero() bool <span class="cov4" title="23">{ - return strings.TrimSpace(s.Model) == "" && strings.TrimSpace(s.BaseURL) == "" && s.Temperature == nil && len(s.Presets) == 0 -}</span> - -func (s sectionOpenAI) resolvedModel() string <span class="cov3" title="6">{ - model := strings.TrimSpace(s.Model) - if model == "" </span><span class="cov0" title="0">{ - return "" - }</span> - <span class="cov3" title="6">if len(s.Presets) == 0 </span><span class="cov3" title="5">{ - return model - }</span> - <span class="cov1" title="1">if mapped := strings.TrimSpace(s.Presets[model]); mapped != "" </span><span class="cov1" title="1">{ - return mapped - }</span> - <span class="cov0" title="0">lower := strings.ToLower(model) - for k, v := range s.Presets </span><span class="cov0" title="0">{ - if strings.ToLower(strings.TrimSpace(k)) == lower </span><span class="cov0" title="0">{ - if mapped := strings.TrimSpace(v); mapped != "" </span><span class="cov0" title="0">{ - return mapped - }</span> - } - } - <span class="cov0" title="0">return model</span> -} - -type sectionOpenRouter struct { - Model string `toml:"model"` - BaseURL string `toml:"base_url"` - Temperature *float64 `toml:"temperature"` -} - -type sectionCopilot struct { - Model string `toml:"model"` - BaseURL string `toml:"base_url"` - Temperature *float64 `toml:"temperature"` -} - -type sectionOllama struct { - Model string `toml:"model"` - BaseURL string `toml:"base_url"` - Temperature *float64 `toml:"temperature"` -} - -// Prompts sections -type sectionPrompts struct { - Completion sectionPromptsCompletion `toml:"completion"` - Chat sectionPromptsChat `toml:"chat"` - CodeAction sectionPromptsCodeAction `toml:"code_action"` - CLI sectionPromptsCLI `toml:"cli"` - ProviderNative sectionPromptsProviderNative `toml:"provider_native"` -} - -type sectionPromptsCompletion struct { - SystemGeneral string `toml:"system_general"` - SystemParams string `toml:"system_params"` - SystemInline string `toml:"system_inline"` - UserGeneral string `toml:"user_general"` - UserParams string `toml:"user_params"` - ExtraHeader string `toml:"additional_context"` -} - -type sectionPromptsChat struct { - System string `toml:"system"` -} - -type sectionPromptsCodeAction struct { - RewriteSystem string `toml:"rewrite_system"` - DiagnosticsSystem string `toml:"diagnostics_system"` - DocumentSystem string `toml:"document_system"` |
