summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpaul@buetow.org <paul@buetow.org>2026-02-06 16:35:45 +0200
committerpaul@buetow.org <paul@buetow.org>2026-02-06 16:35:45 +0200
commite58ae44d26a26d38150ced1ac09f69a589d96f49 (patch)
tree5e9ae4fbd1696d1b668dfe0be791004a87fc7a6a
parent356fd474e9454f21853417428df05d99dbcf5c89 (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>
-rw-r--r--PR.md1
-rw-r--r--README.md2
-rw-r--r--cmd/hexai/main.go4
-rw-r--r--config.toml.example11
-rw-r--r--docs/configuration.md8
-rw-r--r--docs/coverage.html9825
-rw-r--r--docs/coverage.out52849
-rw-r--r--internal/appconfig/config.go51
-rw-r--r--internal/appconfig/config_env_model_test.go8
-rw-r--r--internal/appconfig/config_test.go41
-rw-r--r--internal/hexaiaction/prompts.go4
-rw-r--r--internal/hexaiaction/prompts_more_test.go4
-rw-r--r--internal/hexaiaction/run_more_test.go2
-rw-r--r--internal/hexaicli/run.go8
-rw-r--r--internal/hexaicli/run_test.go20
-rw-r--r--internal/hexaicli/testhelpers_test.go1
-rw-r--r--internal/hexailsp/run.go10
-rw-r--r--internal/llm/copilot.go412
-rw-r--r--internal/llm/copilot_http_test.go276
-rw-r--r--internal/llm/copilot_test.go35
-rw-r--r--internal/llm/openai_temp_test.go6
-rw-r--r--internal/llm/provider.go15
-rw-r--r--internal/llm/provider_more2_test.go12
-rw-r--r--internal/llm/provider_more_test.go10
-rw-r--r--internal/llm/provider_test.go8
-rw-r--r--internal/llmutils/client.go9
-rw-r--r--internal/lsp/handlers_utils.go2
-rw-r--r--internal/lsp/llm_request_opts_test.go8
-rw-r--r--internal/lsp/server.go15
-rw-r--r--internal/runtimeconfig/store_test.go4
30 files changed, 28903 insertions, 34758 deletions
diff --git a/PR.md b/PR.md
index 50a5393..686c7ab 100644
--- a/PR.md
+++ b/PR.md
@@ -36,7 +36,6 @@ export HEXAI_REQUEST_TIMEOUT=120
- `internal/llm/openai.go` - Added `newOpenAIWithTimeout`
- `internal/llm/ollama.go` - Added `newOllamaWithTimeout`
- `internal/llm/openrouter.go` - Added `newOpenRouterWithTimeout`
-- `internal/llm/copilot.go` - Added `newCopilotWithTimeout`
- `internal/llm/anthropic.go` - Added `newAnthropicWithTimeout`
- `internal/hexailsp/run.go` - Pass `RequestTimeout` to `llm.Config`
- `internal/llmutils/client.go` - Pass `RequestTimeout` to `llm.Config`
diff --git a/README.md b/README.md
index 4ffc0ef..88ee031 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ It has got improved capabilities for Go code understanding (for example, create
* Parallel completions and CLI responses from multiple providers/models for side-by-side comparison
* TUI AI code-action runner (`hexai-tmux-action`) with Bubble Tea
- Includes a “Custom prompt” action (hotkey `p`) that opens your editor (`$HEXAI_EDITOR` or `$EDITOR`) on a temporary Markdown file.
-* Support for OpenAI, OpenRouter, GitHub Copilot, Anthropic, and Ollama
+* Support for OpenAI, OpenRouter, Anthropic, and Ollama
## Documentation
diff --git a/cmd/hexai/main.go b/cmd/hexai/main.go
index 7caedc6..15fda42 100644
--- a/cmd/hexai/main.go
+++ b/cmd/hexai/main.go
@@ -100,8 +100,8 @@ func pickDefaultModel(cfg appconfig.App, provider string) string {
switch strings.ToLower(strings.TrimSpace(provider)) {
case "ollama":
return strings.TrimSpace(cfg.OllamaModel)
- case "copilot":
- return strings.TrimSpace(cfg.CopilotModel)
+ case "anthropic":
+ return strings.TrimSpace(cfg.AnthropicModel)
default:
return strings.TrimSpace(cfg.OpenAIModel)
}
diff --git a/config.toml.example b/config.toml.example
index cc34e04..bb8165d 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -52,8 +52,8 @@ chat_prefixes = ["?", "!", ":", ";"] # single-character items
# [[models.code_action]]
# # Only the first entry is used; extras are ignored with a warning.
-# provider = "copilot"
-# model = "gpt-4o"
+# provider = "anthropic"
+# model = "claude-3-5-sonnet"
# temperature = 0.4
# [[models.cli]]
@@ -62,7 +62,7 @@ chat_prefixes = ["?", "!", ":", ";"] # single-character items
# temperature = 0.6
[provider]
-name = "openai" # openai | openrouter | copilot | ollama | anthropic
+name = "openai" # openai | openrouter | anthropic | ollama
[openai]
model = "gpt-4.1"
@@ -74,11 +74,6 @@ model = "openrouter/auto"
base_url = "https://openrouter.ai/api/v1"
temperature = 0.2
-[copilot]
-model = "gpt-4o-mini"
-base_url = "https://api.githubcopilot.com"
-temperature = 0.2
-
[ollama]
model = "qwen3-coder:30b-a3b-q4_K_M"
base_url = "http://localhost:11434"
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(&amp;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) &gt; 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 &lt; 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 &lt; 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 &gt; 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 &gt; 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: &gt;!text&gt; and &gt;&gt;!text&gt;)
- InlineOpen string `json:"inline_open" toml:"inline_open"`
- InlineClose string `json:"inline_close" toml:"inline_close"`
- // In-editor chat triggers (default: suffix "&gt;" 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: &amp;t,
- OpenAITemperature: &amp;t,
- OllamaTemperature: &amp;t,
- CopilotTemperature: &amp;t,
- ManualInvokeMinPrefix: 0,
- CompletionDebounceMs: 800,
- CompletionThrottleMs: 0,
- // Inline/chat trigger defaults
- InlineOpen: "&gt;!",
- InlineClose: "&gt;",
- ChatSuffix: "&gt;",
- 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}}\nCurren