summaryrefslogtreecommitdiff
path: root/docs/coverage.html
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-09-14 23:40:26 +0300
committerPaul Buetow <paul@buetow.org>2025-09-14 23:40:26 +0300
commitf4470bbcfbe3b14c99baeef475fe872825a13a39 (patch)
treee12fc6168d21119dfff3a0fef5b6c5b54149f3ab /docs/coverage.html
parent68438c98d23545ff791768e3e219cd21d3814e0c (diff)
release: v0.10.0v0.10.0
Diffstat (limited to 'docs/coverage.html')
-rw-r--r--docs/coverage.html2904
1 files changed, 1791 insertions, 1113 deletions
diff --git a/docs/coverage.html b/docs/coverage.html
index 059834b..2d72d59 100644
--- a/docs/coverage.html
+++ b/docs/coverage.html
@@ -61,7 +61,7 @@
<option value="file2">codeberg.org/snonux/hexai/cmd/hexai/main.go (71.4%)</option>
- <option value="file3">codeberg.org/snonux/hexai/internal/appconfig/config.go (90.6%)</option>
+ <option value="file3">codeberg.org/snonux/hexai/internal/appconfig/config.go (90.8%)</option>
<option value="file4">codeberg.org/snonux/hexai/internal/editor/editor.go (58.3%)</option>
@@ -69,63 +69,67 @@
<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 (85.0%)</option>
+ <option value="file7">codeberg.org/snonux/hexai/internal/hexaiaction/prompts.go (87.5%)</option>
- <option value="file8">codeberg.org/snonux/hexai/internal/hexaiaction/run.go (69.2%)</option>
+ <option value="file8">codeberg.org/snonux/hexai/internal/hexaiaction/run.go (67.2%)</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_delegate.go (100.0%)</option>
+ <option value="file10">codeberg.org/snonux/hexai/internal/hexaiaction/tui_custom.go (83.8%)</option>
- <option value="file11">codeberg.org/snonux/hexai/internal/hexaicli/run.go (88.7%)</option>
+ <option value="file11">codeberg.org/snonux/hexai/internal/hexaiaction/tui_delegate.go (100.0%)</option>
- <option value="file12">codeberg.org/snonux/hexai/internal/hexailsp/run.go (92.5%)</option>
+ <option value="file12">codeberg.org/snonux/hexai/internal/hexaicli/run.go (88.6%)</option>
- <option value="file13">codeberg.org/snonux/hexai/internal/llm/copilot.go (82.4%)</option>
+ <option value="file13">codeberg.org/snonux/hexai/internal/hexailsp/run.go (83.7%)</option>
- <option value="file14">codeberg.org/snonux/hexai/internal/llm/ollama.go (89.8%)</option>
+ <option value="file14">codeberg.org/snonux/hexai/internal/llm/copilot.go (82.4%)</option>
- <option value="file15">codeberg.org/snonux/hexai/internal/llm/openai.go (85.5%)</option>
+ <option value="file15">codeberg.org/snonux/hexai/internal/llm/ollama.go (89.8%)</option>
- <option value="file16">codeberg.org/snonux/hexai/internal/llm/provider.go (100.0%)</option>
+ <option value="file16">codeberg.org/snonux/hexai/internal/llm/openai.go (85.5%)</option>
- <option value="file17">codeberg.org/snonux/hexai/internal/llm/util.go (100.0%)</option>
+ <option value="file17">codeberg.org/snonux/hexai/internal/llm/provider.go (100.0%)</option>
- <option value="file18">codeberg.org/snonux/hexai/internal/llmutils/client.go (100.0%)</option>
+ <option value="file18">codeberg.org/snonux/hexai/internal/llm/util.go (100.0%)</option>
- <option value="file19">codeberg.org/snonux/hexai/internal/logging/chatlogger.go (100.0%)</option>
+ <option value="file19">codeberg.org/snonux/hexai/internal/llmutils/client.go (100.0%)</option>
- <option value="file20">codeberg.org/snonux/hexai/internal/logging/logging.go (90.9%)</option>
+ <option value="file20">codeberg.org/snonux/hexai/internal/logging/chatlogger.go (100.0%)</option>
- <option value="file21">codeberg.org/snonux/hexai/internal/lsp/context.go (74.4%)</option>
+ <option value="file21">codeberg.org/snonux/hexai/internal/logging/logging.go (90.9%)</option>
- <option value="file22">codeberg.org/snonux/hexai/internal/lsp/document.go (90.1%)</option>
+ <option value="file22">codeberg.org/snonux/hexai/internal/lsp/context.go (74.4%)</option>
- <option value="file23">codeberg.org/snonux/hexai/internal/lsp/handlers.go (92.9%)</option>
+ <option value="file23">codeberg.org/snonux/hexai/internal/lsp/document.go (90.1%)</option>
- <option value="file24">codeberg.org/snonux/hexai/internal/lsp/handlers_codeaction.go (78.8%)</option>
+ <option value="file24">codeberg.org/snonux/hexai/internal/lsp/handlers.go (92.9%)</option>
- <option value="file25">codeberg.org/snonux/hexai/internal/lsp/handlers_completion.go (87.6%)</option>
+ <option value="file25">codeberg.org/snonux/hexai/internal/lsp/handlers_codeaction.go (82.3%)</option>
- <option value="file26">codeberg.org/snonux/hexai/internal/lsp/handlers_document.go (88.9%)</option>
+ <option value="file26">codeberg.org/snonux/hexai/internal/lsp/handlers_completion.go (87.9%)</option>
- <option value="file27">codeberg.org/snonux/hexai/internal/lsp/handlers_execute.go (75.0%)</option>
+ <option value="file27">codeberg.org/snonux/hexai/internal/lsp/handlers_document.go (88.9%)</option>
- <option value="file28">codeberg.org/snonux/hexai/internal/lsp/handlers_init.go (66.7%)</option>
+ <option value="file28">codeberg.org/snonux/hexai/internal/lsp/handlers_execute.go (75.0%)</option>
- <option value="file29">codeberg.org/snonux/hexai/internal/lsp/handlers_utils.go (89.1%)</option>
+ <option value="file29">codeberg.org/snonux/hexai/internal/lsp/handlers_init.go (63.6%)</option>
- <option value="file30">codeberg.org/snonux/hexai/internal/lsp/server.go (82.6%)</option>
+ <option value="file30">codeberg.org/snonux/hexai/internal/lsp/handlers_utils.go (89.4%)</option>
- <option value="file31">codeberg.org/snonux/hexai/internal/lsp/transport.go (71.4%)</option>
+ <option value="file31">codeberg.org/snonux/hexai/internal/lsp/server.go (81.8%)</option>
- <option value="file32">codeberg.org/snonux/hexai/internal/testutil/fixtures.go (100.0%)</option>
+ <option value="file32">codeberg.org/snonux/hexai/internal/lsp/transport.go (71.4%)</option>
- <option value="file33">codeberg.org/snonux/hexai/internal/textutil/textutil.go (89.0%)</option>
+ <option value="file33">codeberg.org/snonux/hexai/internal/testutil/fixtures.go (100.0%)</option>
- <option value="file34">codeberg.org/snonux/hexai/internal/tmux/status.go (66.7%)</option>
+ <option value="file34">codeberg.org/snonux/hexai/internal/textutil/human.go (92.3%)</option>
- <option value="file35">codeberg.org/snonux/hexai/internal/tmux/tmux.go (88.6%)</option>
+ <option value="file35">codeberg.org/snonux/hexai/internal/textutil/textutil.go (90.4%)</option>
+
+ <option value="file36">codeberg.org/snonux/hexai/internal/tmux/status.go (68.5%)</option>
+
+ <option value="file37">codeberg.org/snonux/hexai/internal/tmux/tmux.go (88.6%)</option>
</select>
</div>
@@ -178,33 +182,32 @@ func main() <span class="cov8" title="1">{
<pre class="file" id="file1" style="display: none">package main
import (
- "context"
- "flag"
- "fmt"
- "os"
+ "context"
+ "flag"
+ "fmt"
+ "os"
- "codeberg.org/snonux/hexai/internal/hexaiaction"
+ "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")
- 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,
- }
- if err := hexaiaction.RunCommand(context.Background(), opts, os.Stdin, os.Stdout, os.Stderr); err != nil </span><span class="cov0" title="0">{
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }</span>
-}
+ 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")
+ 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,
+ }
+ if err := hexaiaction.RunCommand(context.Background(), opts, os.Stdin, os.Stdout, os.Stderr); err != nil </span><span class="cov0" title="0">{
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }</span>
+}
</pre>
<pre class="file" id="file2" style="display: none">// Summary: Hexai CLI entrypoint; parses flags and delegates to internal/hexaicli.
@@ -310,21 +313,37 @@ type App struct {
// 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:"-"`
+ 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:"-"`
+}
+
+// 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="19">{
+func newDefaultConfig() App <span class="cov5" title="30">{
// Coding-friendly default temperature across providers
// Users can override per provider in config.toml (including 0.0).
t := 0.2
@@ -365,10 +384,10 @@ func newDefaultConfig() App <span class="cov5" title="19">{
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}}",
+ 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.",
@@ -377,18 +396,18 @@ func newDefaultConfig() App <span class="cov5" title="19">{
// 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="18">{
+func Load(logger *log.Logger) App <span class="cov5" title="29">{
cfg := newDefaultConfig()
- if logger == nil </span><span class="cov3" title="4">{
+ if logger == nil </span><span class="cov4" title="8">{
return cfg // Return defaults if no logger is provided (e.g. in tests)
}</span>
- <span class="cov5" title="14">configPath, err := getConfigPath()
+ <span class="cov5" title="21">configPath, err := getConfigPath()
if err != nil </span><span class="cov0" title="0">{
logger.Printf("%v", err)
// Even if config path cannot be resolved, still allow env overrides below.
- }</span> else<span class="cov5" title="14"> {
- if fileCfg, err := loadFromFile(configPath, logger); err == nil &amp;&amp; fileCfg != nil </span><span class="cov3" title="4">{
+ }</span> else<span class="cov5" title="21"> {
+ if fileCfg, err := loadFromFile(configPath, logger); err == nil &amp;&amp; fileCfg != nil </span><span class="cov4" title="11">{
cfg.mergeWith(fileCfg)
}</span>
// When the config file is missing or invalid, we keep defaults and still
@@ -396,10 +415,10 @@ func Load(logger *log.Logger) App <span class="cov5" title="18">{
}
// Environment overrides (take precedence over file)
- <span class="cov5" title="14">if envCfg := loadFromEnv(logger); envCfg != nil </span><span class="cov1" title="1">{
+ <span class="cov5" title="21">if envCfg := loadFromEnv(logger); envCfg != nil </span><span class="cov1" title="1">{
cfg.mergeWith(envCfg)
}</span>
- <span class="cov5" title="14">return cfg</span>
+ <span class="cov5" title="21">return cfg</span>
}
// Private helpers
@@ -417,6 +436,7 @@ type fileConfig struct {
Copilot sectionCopilot `toml:"copilot"`
Ollama sectionOllama `toml:"ollama"`
Prompts sectionPrompts `toml:"prompts"`
+ Tmux sectionTmux `toml:"tmux"`
}
type sectionGeneral struct {
@@ -496,16 +516,17 @@ type sectionPromptsChat struct {
}
type sectionPromptsCodeAction struct {
- RewriteSystem string `toml:"rewrite_system"`
- DiagnosticsSystem string `toml:"diagnostics_system"`
- DocumentSystem string `toml:"document_system"`
- RewriteUser string `toml:"rewrite_user"`
- DiagnosticsUser string `toml:"diagnostics_user"`
- DocumentUser string `toml:"document_user"`
- GoTestSystem string `toml:"go_test_system"`
- GoTestUser string `toml:"go_test_user"`
- SimplifySystem string `toml:"simplify_system"`
- SimplifyUser string `toml:"simplify_user"`
+ RewriteSystem string `toml:"rewrite_system"`
+ DiagnosticsSystem string `toml:"diagnostics_system"`
+ DocumentSystem string `toml:"document_system"`
+ RewriteUser string `toml:"rewrite_user"`
+ DiagnosticsUser string `toml:"diagnostics_user"`
+ DocumentUser string `toml:"document_user"`
+ GoTestSystem string `toml:"go_test_system"`
+ GoTestUser string `toml:"go_test_user"`
+ SimplifySystem string `toml:"simplify_system"`
+ SimplifyUser string `toml:"simplify_user"`
+ Custom []sectionCustomAction `toml:"custom"`
}
type sectionPromptsCLI struct {
@@ -517,7 +538,22 @@ type sectionPromptsProviderNative struct {
Completion string `toml:"completion"`
}
-func (fc *fileConfig) toApp() App <span class="cov3" title="4">{
+type sectionCustomAction struct {
+ ID string `toml:"id"`
+ Title string `toml:"title"`
+ Kind string `toml:"kind"`
+ Scope string `toml:"scope"`
+ Hotkey string `toml:"hotkey"`
+ Instruction string `toml:"instruction"`
+ System string `toml:"system"`
+ User string `toml:"user"`
+}
+
+type sectionTmux struct {
+ CustomMenuHotkey string `toml:"custom_menu_hotkey"`
+}
+
+func (fc *fileConfig) toApp() App <span class="cov4" title="11">{
out := App{}
// Merge section: general
@@ -533,13 +569,13 @@ func (fc *fileConfig) toApp() App <span class="cov3" title="4">{
}</span>
// logging
- <span class="cov3" title="4">if (fc.Logging != sectionLogging{}) </span><span class="cov1" title="1">{
+ <span class="cov4" title="11">if (fc.Logging != sectionLogging{}) </span><span class="cov1" title="1">{
tmp := App{LogPreviewLimit: fc.Logging.LogPreviewLimit}
out.mergeBasics(&amp;tmp)
}</span>
// completion
- <span class="cov3" title="4">if (fc.Completion != sectionCompletion{}) </span><span class="cov2" title="3">{
+ <span class="cov4" title="11">if (fc.Completion != sectionCompletion{}) </span><span class="cov2" title="3">{
tmp := App{
CompletionDebounceMs: fc.Completion.CompletionDebounceMs,
CompletionThrottleMs: fc.Completion.CompletionThrottleMs,
@@ -549,31 +585,31 @@ func (fc *fileConfig) toApp() App <span class="cov3" title="4">{
}</span>
// triggers
- <span class="cov3" title="4">if len(fc.Triggers.TriggerCharacters) &gt; 0 </span><span class="cov2" title="3">{
+ <span class="cov4" title="11">if len(fc.Triggers.TriggerCharacters) &gt; 0 </span><span class="cov2" title="3">{
tmp := App{TriggerCharacters: fc.Triggers.TriggerCharacters}
out.mergeBasics(&amp;tmp)
}</span>
// inline
- <span class="cov3" title="4">if (fc.Inline != sectionInline{}) </span><span class="cov1" title="1">{
+ <span class="cov4" title="11">if (fc.Inline != sectionInline{}) </span><span class="cov1" title="1">{
tmp := App{InlineOpen: fc.Inline.InlineOpen, InlineClose: fc.Inline.InlineClose}
out.mergeBasics(&amp;tmp)
}</span>
// chat
- <span class="cov3" title="4">if strings.TrimSpace(fc.Chat.ChatSuffix) != "" || len(fc.Chat.ChatPrefixes) &gt; 0 </span><span class="cov1" title="1">{
+ <span class="cov4" title="11">if strings.TrimSpace(fc.Chat.ChatSuffix) != "" || len(fc.Chat.ChatPrefixes) &gt; 0 </span><span class="cov1" title="1">{
tmp := App{ChatSuffix: fc.Chat.ChatSuffix, ChatPrefixes: fc.Chat.ChatPrefixes}
out.mergeBasics(&amp;tmp)
}</span>
// provider
- <span class="cov3" title="4">if strings.TrimSpace(fc.Provider.Name) != "" </span><span class="cov2" title="3">{
+ <span class="cov4" title="11">if strings.TrimSpace(fc.Provider.Name) != "" </span><span class="cov2" title="3">{
tmp := App{Provider: fc.Provider.Name}
out.mergeBasics(&amp;tmp)
}</span>
// openai
- <span class="cov3" title="4">if (fc.OpenAI != sectionOpenAI{}) || fc.OpenAI.Temperature != nil </span><span class="cov2" title="3">{
+ <span class="cov4" title="11">if (fc.OpenAI != sectionOpenAI{}) || fc.OpenAI.Temperature != nil </span><span class="cov2" title="3">{
tmp := App{
OpenAIBaseURL: fc.OpenAI.BaseURL,
OpenAIModel: fc.OpenAI.Model,
@@ -583,7 +619,7 @@ func (fc *fileConfig) toApp() App <span class="cov3" title="4">{
}</span>
// copilot
- <span class="cov3" title="4">if (fc.Copilot != sectionCopilot{}) || fc.Copilot.Temperature != nil </span><span class="cov2" title="3">{
+ <span class="cov4" title="11">if (fc.Copilot != sectionCopilot{}) || fc.Copilot.Temperature != nil </span><span class="cov2" title="3">{
tmp := App{
CopilotBaseURL: fc.Copilot.BaseURL,
CopilotModel: fc.Copilot.Model,
@@ -593,7 +629,7 @@ func (fc *fileConfig) toApp() App <span class="cov3" title="4">{
}</span>
// ollama
- <span class="cov3" title="4">if (fc.Ollama != sectionOllama{}) || fc.Ollama.Temperature != nil </span><span class="cov2" title="3">{
+ <span class="cov4" title="11">if (fc.Ollama != sectionOllama{}) || fc.Ollama.Temperature != nil </span><span class="cov2" title="3">{
tmp := App{
OllamaBaseURL: fc.Ollama.BaseURL,
OllamaModel: fc.Ollama.Model,
@@ -604,7 +640,7 @@ func (fc *fileConfig) toApp() App <span class="cov3" title="4">{
// prompts
// completion
- <span class="cov3" title="4">if (fc.Prompts.Completion != sectionPromptsCompletion{}) </span><span class="cov1" title="1">{
+ <span class="cov4" title="11">if (fc.Prompts.Completion != sectionPromptsCompletion{}) </span><span class="cov1" title="1">{
if strings.TrimSpace(fc.Prompts.Completion.SystemGeneral) != "" </span><span class="cov1" title="1">{
out.PromptCompletionSystemGeneral = fc.Prompts.Completion.SystemGeneral
}</span>
@@ -625,44 +661,68 @@ func (fc *fileConfig) toApp() App <span class="cov3" title="4">{
}</span>
}
// chat
- <span class="cov3" title="4">if strings.TrimSpace(fc.Prompts.Chat.System) != "" </span><span class="cov1" title="1">{
+ <span class="cov4" title="11">if strings.TrimSpace(fc.Prompts.Chat.System) != "" </span><span class="cov1" title="1">{
out.PromptChatSystem = fc.Prompts.Chat.System
}</span>
// code action
- <span class="cov3" title="4">if (fc.Prompts.CodeAction != sectionPromptsCodeAction{}) </span><span class="cov1" title="1">{
+ <span class="cov4" title="11">if strings.TrimSpace(fc.Prompts.CodeAction.RewriteSystem) != "" ||
+ strings.TrimSpace(fc.Prompts.CodeAction.DiagnosticsSystem) != "" ||
+ strings.TrimSpace(fc.Prompts.CodeAction.DocumentSystem) != "" ||
+ strings.TrimSpace(fc.Prompts.CodeAction.RewriteUser) != "" ||
+ strings.TrimSpace(fc.Prompts.CodeAction.DiagnosticsUser) != "" ||
+ strings.TrimSpace(fc.Prompts.CodeAction.DocumentUser) != "" ||
+ strings.TrimSpace(fc.Prompts.CodeAction.GoTestSystem) != "" ||
+ strings.TrimSpace(fc.Prompts.CodeAction.GoTestUser) != "" ||
+ strings.TrimSpace(fc.Prompts.CodeAction.SimplifySystem) != "" ||
+ strings.TrimSpace(fc.Prompts.CodeAction.SimplifyUser) != "" ||
+ len(fc.Prompts.CodeAction.Custom) &gt; 0 </span><span class="cov3" title="7">{
if strings.TrimSpace(fc.Prompts.CodeAction.RewriteSystem) != "" </span><span class="cov1" title="1">{
out.PromptCodeActionRewriteSystem = fc.Prompts.CodeAction.RewriteSystem
}</span>
- <span class="cov1" title="1">if strings.TrimSpace(fc.Prompts.CodeAction.DiagnosticsSystem) != "" </span><span class="cov1" title="1">{
+ <span class="cov3" title="7">if strings.TrimSpace(fc.Prompts.CodeAction.DiagnosticsSystem) != "" </span><span class="cov1" title="1">{
out.PromptCodeActionDiagnosticsSystem = fc.Prompts.CodeAction.DiagnosticsSystem
}</span>
- <span class="cov1" title="1">if strings.TrimSpace(fc.Prompts.CodeAction.DocumentSystem) != "" </span><span class="cov1" title="1">{
+ <span class="cov3" title="7">if strings.TrimSpace(fc.Prompts.CodeAction.DocumentSystem) != "" </span><span class="cov1" title="1">{
out.PromptCodeActionDocumentSystem = fc.Prompts.CodeAction.DocumentSystem
}</span>
- <span class="cov1" title="1">if strings.TrimSpace(fc.Prompts.CodeAction.RewriteUser) != "" </span><span class="cov1" title="1">{
+ <span class="cov3" title="7">if strings.TrimSpace(fc.Prompts.CodeAction.RewriteUser) != "" </span><span class="cov1" title="1">{
out.PromptCodeActionRewriteUser = fc.Prompts.CodeAction.RewriteUser
}</span>
- <span class="cov1" title="1">if strings.TrimSpace(fc.Prompts.CodeAction.DiagnosticsUser) != "" </span><span class="cov1" title="1">{
+ <span class="cov3" title="7">if strings.TrimSpace(fc.Prompts.CodeAction.DiagnosticsUser) != "" </span><span class="cov1" title="1">{
out.PromptCodeActionDiagnosticsUser = fc.Prompts.CodeAction.DiagnosticsUser
}</span>
- <span class="cov1" title="1">if strings.TrimSpace(fc.Prompts.CodeAction.DocumentUser) != "" </span><span class="cov1" title="1">{
+ <span class="cov3" title="7">if strings.TrimSpace(fc.Prompts.CodeAction.DocumentUser) != "" </span><span class="cov1" title="1">{
out.PromptCodeActionDocumentUser = fc.Prompts.CodeAction.DocumentUser
}</span>
- <span class="cov1" title="1">if strings.TrimSpace(fc.Prompts.CodeAction.GoTestSystem) != "" </span><span class="cov1" title="1">{
+ <span class="cov3" title="7">if strings.TrimSpace(fc.Prompts.CodeAction.GoTestSystem) != "" </span><span class="cov1" title="1">{
out.PromptCodeActionGoTestSystem = fc.Prompts.CodeAction.GoTestSystem
}</span>
- <span class="cov1" title="1">if strings.TrimSpace(fc.Prompts.CodeAction.GoTestUser) != "" </span><span class="cov1" title="1">{
- out.PromptCodeActionGoTestUser = fc.Prompts.CodeAction.GoTestUser
- }</span>
- <span class="cov1" title="1">if strings.TrimSpace(fc.Prompts.CodeAction.SimplifySystem) != "" </span><span class="cov0" title="0">{
- out.PromptCodeActionSimplifySystem = fc.Prompts.CodeAction.SimplifySystem
- }</span>
- <span class="cov1" title="1">if strings.TrimSpace(fc.Prompts.CodeAction.SimplifyUser) != "" </span><span class="cov0" title="0">{
- out.PromptCodeActionSimplifyUser = fc.Prompts.CodeAction.SimplifyUser
- }</span>
- }
+ <span class="cov3" title="7">if strings.TrimSpace(fc.Prompts.CodeAction.GoTestUser) != "" </span><span class="cov1" title="1">{
+ out.PromptCodeActionGoTestUser = fc.Prompts.CodeAction.GoTestUser
+ }</span>
+ <span class="cov3" title="7">if strings.TrimSpace(fc.Prompts.CodeAction.SimplifySystem) != "" </span><span class="cov0" title="0">{
+ out.PromptCodeActionSimplifySystem = fc.Prompts.CodeAction.SimplifySystem
+ }</span>
+ <span class="cov3" title="7">if strings.TrimSpace(fc.Prompts.CodeAction.SimplifyUser) != "" </span><span class="cov0" title="0">{
+ out.PromptCodeActionSimplifyUser = fc.Prompts.CodeAction.SimplifyUser
+ }</span>
+ <span class="cov3" title="7">if len(fc.Prompts.CodeAction.Custom) &gt; 0 </span><span class="cov3" title="6">{
+ for _, ca := range fc.Prompts.CodeAction.Custom </span><span class="cov4" title="10">{
+ out.CustomActions = append(out.CustomActions, CustomAction{
+ ID: strings.TrimSpace(ca.ID),
+ Title: strings.TrimSpace(ca.Title),
+ Kind: strings.TrimSpace(ca.Kind),
+ Scope: strings.ToLower(strings.TrimSpace(ca.Scope)),
+ Hotkey: strings.TrimSpace(ca.Hotkey),
+ Instruction: ca.Instruction,
+ System: ca.System,
+ User: ca.User,
+ })
+ }</span>
+ }
+ }
// cli
- <span class="cov3" title="4">if (fc.Prompts.CLI != sectionPromptsCLI{}) </span><span class="cov1" title="1">{
+ <span class="cov4" title="11">if (fc.Prompts.CLI != sectionPromptsCLI{}) </span><span class="cov1" title="1">{
if strings.TrimSpace(fc.Prompts.CLI.DefaultSystem) != "" </span><span class="cov1" title="1">{
out.PromptCLIDefaultSystem = fc.Prompts.CLI.DefaultSystem
}</span>
@@ -671,14 +731,19 @@ func (fc *fileConfig) toApp() App <span class="cov3" title="4">{
}</span>
}
// provider-native
- <span class="cov3" title="4">if strings.TrimSpace(fc.Prompts.ProviderNative.Completion) != "" </span><span class="cov1" title="1">{
+ <span class="cov4" title="11">if strings.TrimSpace(fc.Prompts.ProviderNative.Completion) != "" </span><span class="cov1" title="1">{
out.PromptNativeCompletion = fc.Prompts.ProviderNative.Completion
}</span>
- <span class="cov3" title="4">return out</span>
+ // tmux
+ <span class="cov4" title="11">if (fc.Tmux != sectionTmux{}) </span><span class="cov2" title="3">{
+ out.TmuxCustomMenuHotkey = strings.TrimSpace(fc.Tmux.CustomMenuHotkey)
+ }</span>
+
+ <span class="cov4" title="11">return out</span>
}
-func loadFromFile(path string, logger *log.Logger) (*App, error) <span class="cov5" title="15">{
+func loadFromFile(path string, logger *log.Logger) (*App, error) <span class="cov5" title="22">{
b, err := os.ReadFile(path)
if err != nil </span><span class="cov4" title="9">{
if !os.IsNotExist(err) &amp;&amp; logger != nil </span><span class="cov0" title="0">{
@@ -687,7 +752,7 @@ func loadFromFile(path string, logger *log.Logger) (*App, error) <span class="co
<span class="cov4" title="9">return nil, err</span>
}
- <span class="cov3" title="6">var tables fileConfig
+ <span class="cov4" title="13">var tables fileConfig
errTables := toml.NewDecoder(strings.NewReader(string(b))).Decode(&amp;tables)
// Raw map for validation/presence checks
var raw map[string]any
@@ -700,7 +765,7 @@ func loadFromFile(path string, logger *log.Logger) (*App, error) <span class="co
}
// Reject legacy flat keys at top-level (sectioned-only config is allowed)
- <span class="cov3" title="4">legacy := map[string]struct{}{
+ <span class="cov4" title="11">legacy := map[string]struct{}{
"max_tokens": {}, "context_mode": {}, "context_window_lines": {}, "max_context_tokens": {},
"log_preview_limit": {}, "completion_debounce_ms": {}, "completion_throttle_ms": {},
"manual_invoke_min_prefix": {}, "trigger_characters": {}, "inline_open": {}, "inline_close": {},
@@ -709,22 +774,22 @@ func loadFromFile(path string, logger *log.Logger) (*App, error) <span class="co
"ollama_model": {}, "ollama_base_url": {}, "ollama_temperature": {},
"copilot_model": {}, "copilot_base_url": {}, "copilot_temperature": {},
}
- for k := range raw </span><span class="cov6" title="27">{
- if _, isTable := map[string]struct{}{"general": {}, "logging": {}, "completion": {}, "triggers": {}, "inline": {}, "chat": {}, "provider": {}, "openai": {}, "copilot": {}, "ollama": {}, "prompts": {}}[k]; isTable </span><span class="cov6" title="27">{
+ for k := range raw </span><span class="cov6" title="36">{
+ if _, isTable := map[string]struct{}{"general": {}, "logging": {}, "completion": {}, "triggers": {}, "inline": {}, "chat": {}, "provider": {}, "openai": {}, "copilot": {}, "ollama": {}, "prompts": {}}[k]; isTable </span><span class="cov6" title="33">{
continue</span>
}
- <span class="cov0" title="0">if _, isLegacy := legacy[k]; isLegacy </span><span class="cov0" title="0">{
+ <span class="cov2" title="3">if _, isLegacy := legacy[k]; isLegacy </span><span class="cov0" title="0">{
return nil, fmt.Errorf("unsupported flat key '%s' in config; use sectioned tables (see config.toml.example)", k)
}</span>
}
- <span class="cov3" title="4">if logger != nil </span><span class="cov3" title="4">{
+ <span class="cov4" title="11">if logger != nil </span><span class="cov4" title="11">{
logger.Printf("loaded configuration from %s (TOML)", path)
}</span>
// Merge order: flat first, then tables (so tables win over zero flat values)
// Build App from tables only
- <span class="cov3" title="4">tab := tables.toApp()
+ <span class="cov4" title="11">tab := tables.toApp()
// Ensure explicit values from raw map are respected (defensive for ints)
if t, ok := raw["completion"].(map[string]any); ok </span><span class="cov2" title="3">{
if v, present := t["manual_invoke_min_prefix"]; present </span><span class="cov2" title="3">{
@@ -738,7 +803,7 @@ func loadFromFile(path string, logger *log.Logger) (*App, error) <span class="co
}
}
}
- <span class="cov3" title="4">if t, ok := raw["logging"].(map[string]any); ok </span><span class="cov2" title="3">{
+ <span class="cov4" title="11">if t, ok := raw["logging"].(map[string]any); ok </span><span class="cov2" title="3">{
if v, present := t["log_preview_limit"]; present </span><span class="cov2" title="3">{
switch vv := v.(type) </span>{
case int64:<span class="cov2" title="3">
@@ -750,167 +815,231 @@ func loadFromFile(path string, logger *log.Logger) (*App, error) <span class="co
}
}
}
- <span class="cov3" title="4">return &amp;tab, nil</span>
+ <span class="cov4" title="11">return &amp;tab, nil</span>
}
-func (a *App) mergeWith(other *App) <span class="cov3" title="5">{
+func (a *App) mergeWith(other *App) <span class="cov4" title="12">{
a.mergeBasics(other)
a.mergeProviderFields(other)
a.mergePrompts(other)
}</span>
// mergeBasics merges general (non-provider) fields.
-func (a *App) mergeBasics(other *App) <span class="cov5" title="20">{
- if other.MaxTokens &gt; 0 </span><span class="cov4" title="7">{
+func (a *App) mergeBasics(other *App) <span class="cov5" title="27">{
+ if other.MaxTokens &gt; 0 </span><span class="cov3" title="7">{
a.MaxTokens = other.MaxTokens
}</span>
- <span class="cov5" title="20">if s := strings.TrimSpace(other.ContextMode); s != "" </span><span class="cov4" title="7">{
+ <span class="cov5" title="27">if s := strings.TrimSpace(other.ContextMode); s != "" </span><span class="cov3" title="7">{
a.ContextMode = s
}</span>
- <span class="cov5" title="20">if other.ContextWindowLines &gt; 0 </span><span class="cov4" title="7">{
+ <span class="cov5" title="27">if other.ContextWindowLines &gt; 0 </span><span class="cov3" title="7">{
a.ContextWindowLines = other.ContextWindowLines
}</span>
- <span class="cov5" title="20">if other.MaxContextTokens &gt; 0 </span><span class="cov4" title="7">{
+ <span class="cov5" title="27">if other.MaxContextTokens &gt; 0 </span><span class="cov3" title="7">{
a.MaxContextTokens = other.MaxContextTokens
}</span>
- <span class="cov5" title="20">if other.LogPreviewLimit &gt;= 0 </span><span class="cov5" title="20">{
+ <span class="cov5" title="27">if other.LogPreviewLimit &gt;= 0 </span><span class="cov5" title="27">{
a.LogPreviewLimit = other.LogPreviewLimit
}</span>
- <span class="cov5" title="20">if other.CodingTemperature != nil </span><span class="cov4" title="7">{ // allow explicit 0.0
+ <span class="cov5" title="27">if other.CodingTemperature != nil </span><span class="cov3" title="7">{ // allow explicit 0.0
a.CodingTemperature = other.CodingTemperature
}</span>
- <span class="cov5" title="20">if other.ManualInvokeMinPrefix &gt;= 0 </span><span class="cov5" title="20">{
+ <span class="cov5" title="27">if other.ManualInvokeMinPrefix &gt;= 0 </span><span class="cov5" title="27">{
a.ManualInvokeMinPrefix = other.ManualInvokeMinPrefix
}</span>
- <span class="cov5" title="20">if other.CompletionDebounceMs &gt; 0 </span><span class="cov4" title="7">{
+ <span class="cov5" title="27">if other.CompletionDebounceMs &gt; 0 </span><span class="cov3" title="7">{
a.CompletionDebounceMs = other.CompletionDebounceMs
}</span>
- <span class="cov5" title="20">if other.CompletionThrottleMs &gt; 0 </span><span class="cov4" title="7">{
+ <span class="cov5" title="27">if other.CompletionThrottleMs &gt; 0 </span><span class="cov3" title="7">{
a.CompletionThrottleMs = other.CompletionThrottleMs
}</span>
- <span class="cov5" title="20">if len(other.TriggerCharacters) &gt; 0 </span><span class="cov4" title="7">{
+ <span class="cov5" title="27">if len(other.TriggerCharacters) &gt; 0 </span><span class="cov3" title="7">{
a.TriggerCharacters = slices.Clone(other.TriggerCharacters)
}</span>
- <span class="cov5" title="20">if s := strings.TrimSpace(other.InlineOpen); s != "" </span><span class="cov2" title="2">{
+ <span class="cov5" title="27">if s := strings.TrimSpace(other.InlineOpen); s != "" </span><span class="cov2" title="2">{
a.InlineOpen = s
}</span>
- <span class="cov5" title="20">if s := strings.TrimSpace(other.InlineClose); s != "" </span><span class="cov2" title="2">{
+ <span class="cov5" title="27">if s := strings.TrimSpace(other.InlineClose); s != "" </span><span class="cov2" title="2">{
a.InlineClose = s
}</span>
- <span class="cov5" title="20">if s := strings.TrimSpace(other.ChatSuffix); s != "" </span><span class="cov2" title="2">{
+ <span class="cov5" title="27">if s := strings.TrimSpace(other.ChatSuffix); s != "" </span><span class="cov2" title="2">{
a.ChatSuffix = s
}</span>
- <span class="cov5" title="20">if len(other.ChatPrefixes) &gt; 0 </span><span class="cov2" title="2">{