summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-11-02 23:42:15 +0200
committerPaul Buetow <paul@buetow.org>2025-11-02 23:42:15 +0200
commit35e1de6f975088ade5dbf0af533fe6fdac8fcc94 (patch)
treec9fc9b6ad86cc10a777b3f510c3c4b2d62c41ebd /internal
parentc60d5703d25b7d76d1da2f368b0632f08a161644 (diff)
some linter fixes
Diffstat (limited to 'internal')
-rw-r--r--internal/hexaiaction/cmdentry.go4
-rw-r--r--internal/hexaiaction/cmdentry_test.go6
-rw-r--r--internal/hexaiaction/run.go12
-rw-r--r--internal/hexaiaction/tui_delegate.go2
-rw-r--r--internal/hexaicli/run.go35
-rw-r--r--internal/hexaicli/run_test.go2
-rw-r--r--internal/hexaicli/testhelpers_test.go10
-rw-r--r--internal/hexailsp/run.go6
-rw-r--r--internal/llm/copilot.go20
-rw-r--r--internal/llm/copilot_http_test.go16
-rw-r--r--internal/llm/ollama.go14
-rw-r--r--internal/llm/openai.go14
-rw-r--r--internal/llm/openai_http_test.go14
-rw-r--r--internal/llm/openai_sse_negative_test.go4
-rw-r--r--internal/llm/openrouter.go12
-rw-r--r--internal/llm/openrouter_test.go4
-rw-r--r--internal/lsp/codeaction_prompts_test.go2
-rw-r--r--internal/lsp/completion_messages_test.go2
-rw-r--r--internal/stats/stats.go14
-rw-r--r--internal/tmux/status_more_test.go10
-rw-r--r--internal/version.go2
21 files changed, 134 insertions, 71 deletions
diff --git a/internal/hexaiaction/cmdentry.go b/internal/hexaiaction/cmdentry.go
index ca33443..7d91ab2 100644
--- a/internal/hexaiaction/cmdentry.go
+++ b/internal/hexaiaction/cmdentry.go
@@ -160,8 +160,8 @@ func catFileTo(w io.Writer, path string) error {
// echoThrough no longer used in tmux-only flow, but kept for potential reuse.
func echoThrough(infile, outfile string, stdin io.Reader, stdout io.Writer) error {
- var in io.Reader = stdin
- var out io.Writer = stdout
+ in := stdin
+ out := stdout
if infile != "" {
f, err := os.Open(infile)
if err != nil {
diff --git a/internal/hexaiaction/cmdentry_test.go b/internal/hexaiaction/cmdentry_test.go
index 9c896f6..71ed9db 100644
--- a/internal/hexaiaction/cmdentry_test.go
+++ b/internal/hexaiaction/cmdentry_test.go
@@ -24,7 +24,11 @@ func TestPersistStdin_WritesFile(t *testing.T) {
t.Fatalf("write src: %v", err)
}
f, _ := os.Open(src)
- defer f.Close()
+ defer func() {
+ if err := f.Close(); err != nil {
+ t.Errorf("failed to close temp file: %v", err)
+ }
+ }()
if err := persistStdin(path, f); err != nil {
t.Fatalf("persistStdin: %v", err)
}
diff --git a/internal/hexaiaction/run.go b/internal/hexaiaction/run.go
index 2a1f940..bf355b0 100644
--- a/internal/hexaiaction/run.go
+++ b/internal/hexaiaction/run.go
@@ -36,7 +36,7 @@ func Run(ctx context.Context, stdin io.Reader, stdout, stderr io.Writer) error {
stats.SetWindow(time.Duration(cfg.StatsWindowMinutes) * time.Minute)
}
if err := cfg.Validate(); err != nil {
- fmt.Fprintf(stderr, logging.AnsiBase+"hexai-tmux-action: %v"+logging.AnsiReset+"\n", err)
+ _, _ = fmt.Fprintf(stderr, logging.AnsiBase+"hexai-tmux-action: %v"+logging.AnsiReset+"\n", err)
return err
}
// Enable custom action submenu with configurable hotkey
@@ -50,7 +50,7 @@ func Run(ctx context.Context, stdin io.Reader, stdout, stderr io.Writer) error {
}
cli, err := newClientFromApp(cfg)
if err != nil {
- fmt.Fprintf(stderr, logging.AnsiBase+"hexai-tmux-action: LLM disabled: %v"+logging.AnsiReset+"\n", err)
+ _, _ = fmt.Fprintf(stderr, logging.AnsiBase+"hexai-tmux-action: LLM disabled: %v"+logging.AnsiReset+"\n", err)
return err
}
primaryModel := strings.TrimSpace(reqOptsFrom(cfg).model)
@@ -61,7 +61,7 @@ func Run(ctx context.Context, stdin io.Reader, stdout, stderr io.Writer) error {
var client chatDoer = cli
parts, err := ParseInput(stdin)
if err != nil {
- fmt.Fprintln(stderr, logging.AnsiBase+"hexai-tmux-action: failed to read input"+logging.AnsiReset)
+ _, _ = fmt.Fprintln(stderr, logging.AnsiBase+"hexai-tmux-action: failed to read input"+logging.AnsiReset)
return err
}
if strings.TrimSpace(parts.Selection) == "" {
@@ -75,7 +75,7 @@ func Run(ctx context.Context, stdin io.Reader, stdout, stderr io.Writer) error {
if err != nil {
return err
}
- io.WriteString(stdout, out)
+ _, _ = io.WriteString(stdout, out)
return nil
}
@@ -123,7 +123,7 @@ func executeAction(ctx context.Context, kind ActionKind, parts InputParts, cfg a
func handleRewriteAction(ctx context.Context, parts InputParts, cfg appconfig.App, client chatDoer, stderr io.Writer) (string, error) {
instr, cleaned := ExtractInstruction(parts.Selection)
if strings.TrimSpace(instr) == "" {
- fmt.Fprintln(stderr, logging.AnsiBase+"hexai-tmux-action: no inline instruction found; echoing input"+logging.AnsiReset)
+ _, _ = fmt.Fprintln(stderr, logging.AnsiBase+"hexai-tmux-action: no inline instruction found; echoing input"+logging.AnsiReset)
return parts.Selection, nil
}
return runWithTimeout(ctx, timeout10s, func(cctx context.Context) (string, error) {
@@ -169,7 +169,7 @@ func handleCustomAction(ctx context.Context, parts InputParts, cfg appconfig.App
func handleCustomPromptAction(ctx context.Context, parts InputParts, cfg appconfig.App, client chatDoer, stderr io.Writer) (string, error) {
prompt, err := editor.OpenTempAndEdit(nil)
if err != nil || strings.TrimSpace(prompt) == "" {
- fmt.Fprintln(stderr, logging.AnsiBase+"hexai-tmux-action: custom prompt canceled or empty; echoing input"+logging.AnsiReset)
+ _, _ = fmt.Fprintln(stderr, logging.AnsiBase+"hexai-tmux-action: custom prompt canceled or empty; echoing input"+logging.AnsiReset)
return parts.Selection, nil
}
return runWithTimeout(ctx, timeout10s, func(cctx context.Context) (string, error) {
diff --git a/internal/hexaiaction/tui_delegate.go b/internal/hexaiaction/tui_delegate.go
index 46d40cb..0b34d7e 100644
--- a/internal/hexaiaction/tui_delegate.go
+++ b/internal/hexaiaction/tui_delegate.go
@@ -31,5 +31,5 @@ func (oneLineDelegate) Render(w io.Writer, m list.Model, index int, listItem lis
if index == m.Index() {
cursor = cursorStyle.Render("> ")
}
- fmt.Fprintf(w, "%s%s%s", cursor, title, hot)
+ _, _ = fmt.Fprintf(w, "%s%s%s", cursor, title, hot)
}
diff --git a/internal/hexaicli/run.go b/internal/hexaicli/run.go
index e2aa9a2..7b360e9 100644
--- a/internal/hexaicli/run.go
+++ b/internal/hexaicli/run.go
@@ -170,13 +170,13 @@ func Run(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.
}
jobs, err := buildCLIJobs(cfg)
if err != nil {
- fmt.Fprintf(stderr, logging.AnsiBase+"hexai: LLM disabled: %v"+logging.AnsiReset+"\n", err)
+ _, _ = fmt.Fprintf(stderr, logging.AnsiBase+"hexai: LLM disabled: %v"+logging.AnsiReset+"\n", err)
return err
}
if selected := selectionFromContext(ctx); len(selected) > 0 {
jobs, err = filterJobsBySelection(jobs, selected)
if err != nil {
- fmt.Fprintf(stderr, logging.AnsiBase+"hexai: %v"+logging.AnsiReset+"\n", err)
+ _, _ = fmt.Fprintf(stderr, logging.AnsiBase+"hexai: %v"+logging.AnsiReset+"\n", err)
return err
}
}
@@ -193,12 +193,12 @@ func Run(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.
}
}
if rerr != nil {
- fmt.Fprintln(stderr, logging.AnsiBase+rerr.Error()+logging.AnsiReset)
+ _, _ = fmt.Fprintln(stderr, logging.AnsiBase+rerr.Error()+logging.AnsiReset)
return rerr
}
msgs := buildMessagesFromConfig(cfg, input)
if err := runCLIJobs(ctx, jobs, msgs, input, stdout, stderr); err != nil {
- fmt.Fprintf(stderr, logging.AnsiBase+"hexai: error: %v"+logging.AnsiReset+"\n", err)
+ _, _ = fmt.Fprintf(stderr, logging.AnsiBase+"hexai: error: %v"+logging.AnsiReset+"\n", err)
return err
}
return nil
@@ -209,14 +209,14 @@ func Run(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.
func RunWithClient(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer, client llm.Client) error {
input, err := readInput(stdin, args)
if err != nil {
- fmt.Fprintln(stderr, logging.AnsiBase+err.Error()+logging.AnsiReset)
+ _, _ = fmt.Fprintln(stderr, logging.AnsiBase+err.Error()+logging.AnsiReset)
return err
}
req := requestArgs{model: strings.TrimSpace(client.DefaultModel())}
printProviderInfo(stderr, client, req.model)
msgs := buildMessages(input)
if err := runChat(ctx, client, req, msgs, input, stdout, stderr); err != nil {
- fmt.Fprintf(stderr, logging.AnsiBase+"hexai: error: %v"+logging.AnsiReset+"\n", err)
+ _, _ = fmt.Fprintf(stderr, logging.AnsiBase+"hexai: error: %v"+logging.AnsiReset+"\n", err)
return err
}
return nil
@@ -620,12 +620,21 @@ func runChat(ctx context.Context, client llm.Client, req requestArgs, msgs []llm
var output string
if s, ok := client.(llm.Streamer); ok {
var b strings.Builder
+ var streamErr error
if err := s.ChatStream(ctx, msgs, func(chunk string) {
+ if streamErr != nil {
+ return
+ }
b.WriteString(chunk)
- fmt.Fprint(out, chunk)
+ if _, err := fmt.Fprint(out, chunk); err != nil {
+ streamErr = err
+ }
}, req.options...); err != nil {
return err
}
+ if streamErr != nil {
+ return streamErr
+ }
output = b.String()
} else {
txt, err := client.Chat(ctx, msgs, req.options...)
@@ -633,7 +642,9 @@ func runChat(ctx context.Context, client llm.Client, req requestArgs, msgs []llm
return err
}
output = txt
- fmt.Fprint(out, output)
+ if _, err := fmt.Fprint(out, output); err != nil {
+ return err
+ }
}
dur := time.Since(start)
// Contribute to global stats and update tmux status
@@ -655,8 +666,10 @@ func runChat(ctx context.Context, client llm.Client, req requestArgs, msgs []llm
}
}
scopeRPM := float64(scopeReqs) / minsWin
- fmt.Fprintf(errw, "\n"+logging.AnsiBase+"done provider=%s model=%s time=%s in_bytes=%d out_bytes=%d | global Σ reqs=%d rpm=%.2f"+logging.AnsiReset+"\n",
- client.Name(), model, dur.Round(time.Millisecond), sent, recv, snap.Global.Reqs, snap.RPM)
+ if _, err := fmt.Fprintf(errw, "\n"+logging.AnsiBase+"done provider=%s model=%s time=%s in_bytes=%d out_bytes=%d | global Σ reqs=%d rpm=%.2f"+logging.AnsiReset+"\n",
+ client.Name(), model, dur.Round(time.Millisecond), sent, recv, snap.Global.Reqs, snap.RPM); err != nil {
+ return err
+ }
_ = tmux.SetStatus(tmux.FormatGlobalStatusColored(snap.Global.Reqs, snap.RPM, snap.Global.Sent, snap.Global.Recv, client.Name(), model, scopeRPM, scopeReqs, snap.Window))
return nil
}
@@ -666,7 +679,7 @@ func printProviderInfo(errw io.Writer, client llm.Client, model string) {
if strings.TrimSpace(model) == "" {
model = client.DefaultModel()
}
- fmt.Fprintf(errw, logging.AnsiBase+"provider=%s model=%s"+logging.AnsiReset+"\n", client.Name(), model)
+ _, _ = fmt.Fprintf(errw, logging.AnsiBase+"provider=%s model=%s"+logging.AnsiReset+"\n", client.Name(), model)
}
// newClientFromConfig is kept for tests; delegates to llmutils.
diff --git a/internal/hexaicli/run_test.go b/internal/hexaicli/run_test.go
index 991965e..43576cf 100644
--- a/internal/hexaicli/run_test.go
+++ b/internal/hexaicli/run_test.go
@@ -146,7 +146,7 @@ model = "gpt-x"
t.Fatalf("expected error due to missing API key")
}
// Accept either explicit "LLM disabled" or a generic provider error emitted by Run.
- if !(strings.Contains(errb.String(), "LLM disabled") || strings.Contains(errb.String(), "openai error") || strings.Contains(errb.String(), "hexai: error:")) {
+ if !strings.Contains(errb.String(), "LLM disabled") && !strings.Contains(errb.String(), "openai error") && !strings.Contains(errb.String(), "hexai: error:") {
t.Fatalf("expected disabled-or-error message, got %q", errb.String())
}
}
diff --git a/internal/hexaicli/testhelpers_test.go b/internal/hexaicli/testhelpers_test.go
index 4cc04f7..16f1639 100644
--- a/internal/hexaicli/testhelpers_test.go
+++ b/internal/hexaicli/testhelpers_test.go
@@ -25,7 +25,9 @@ func setStdin(t *testing.T, content string) (func(), *os.File) {
old := os.Stdin
os.Stdin = f
restore := func() {
- f.Close()
+ if err := f.Close(); err != nil {
+ t.Errorf("failed to close temp stdin file: %v", err)
+ }
os.Stdin = old
}
return restore, f
@@ -71,7 +73,11 @@ func writeTOML(t *testing.T, path string, m map[string]string) {
if err != nil {
t.Fatalf("create: %v", err)
}
- defer f.Close()
+ defer func() {
+ if err := f.Close(); err != nil {
+ t.Errorf("failed to close temp TOML file: %v", err)
+ }
+ }()
for k, v := range m {
if _, err := f.WriteString(k + " = \"" + v + "\"\n"); err != nil {
t.Fatalf("write: %v", err)
diff --git a/internal/hexailsp/run.go b/internal/hexailsp/run.go
index f0ab404..b7f777b 100644
--- a/internal/hexailsp/run.go
+++ b/internal/hexailsp/run.go
@@ -37,7 +37,11 @@ func RunWithConfig(logPath string, configPath string, stdin io.Reader, stdout io
if err != nil {
logger.Fatalf("failed to open log file: %v", err)
}
- defer f.Close()
+ defer func() {
+ if err := f.Close(); err != nil {
+ logger.Printf("failed to close log file: %v", err)
+ }
+ }()
logger.SetOutput(f)
}
logging.Bind(logger)
diff --git a/internal/llm/copilot.go b/internal/llm/copilot.go
index d3b1a9d..b439ed3 100644
--- a/internal/llm/copilot.go
+++ b/internal/llm/copilot.go
@@ -118,7 +118,11 @@ func (c copilotClient) Chat(ctx context.Context, messages []Message, opts ...Req
logging.Logf("llm/copilot ", "%shttp error after %s: %v%s", logging.AnsiRed, time.Since(start), err, logging.AnsiBase)
return "", err
}
- defer resp.Body.Close()
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logging.Logf("llm/copilot", "failed to close response body: %v", err)
+ }
+ }()
if err := handleCopilotNon2xx(resp, start); err != nil {
return "", err
}
@@ -144,7 +148,7 @@ func buildCopilotChatRequest(o Options, messages []Message, defaultTemp *float64
req := copilotChatRequest{Model: o.Model}
req.Messages = make([]copilotMessage, len(messages))
for i, m := range messages {
- req.Messages[i] = copilotMessage{Role: m.Role, Content: m.Content}
+ req.Messages[i] = copilotMessage(m)
}
if o.Temperature != 0 {
req.Temperature = &o.Temperature
@@ -220,7 +224,11 @@ func (c *copilotClient) ensureSession(ctx context.Context) error {
if err != nil {
return err
}
- defer resp.Body.Close()
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logging.Logf("llm/copilot", "failed to close response body: %v", err)
+ }
+ }()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("copilot token http error: %d", resp.StatusCode)
}
@@ -354,7 +362,11 @@ func (c copilotClient) CodeCompletion(ctx context.Context, prompt string, suffix
if err != nil {
return nil, err
}
- defer resp.Body.Close()
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logging.Logf("llm/copilot", "failed to close response body: %v", err)
+ }
+ }()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("copilot codex http error: %d", resp.StatusCode)
}
diff --git a/internal/llm/copilot_http_test.go b/internal/llm/copilot_http_test.go
index 9dd4aee..1371f71 100644
--- a/internal/llm/copilot_http_test.go
+++ b/internal/llm/copilot_http_test.go
@@ -73,8 +73,8 @@ func TestCopilot_CodeCompletion_Success(t *testing.T) {
if r.URL.Host == "copilot-proxy.githubusercontent.com" && strings.HasSuffix(r.URL.Path, "/v1/engines/copilot-codex/completions") {
rw := httptest.NewRecorder()
// two choices for index 0 and 1
- rw.WriteString("data: {\"choices\":[{\"index\":0,\"text\":\"A\"}]}\n")
- rw.WriteString("data: {\"choices\":[{\"index\":1,\"text\":\"B\"}]}\n")
+ _, _ = rw.WriteString("data: {\"choices\":[{\"index\":0,\"text\":\"A\"}]}\n")
+ _, _ = rw.WriteString("data: {\"choices\":[{\"index\":1,\"text\":\"B\"}]}\n")
res := rw.Result()
res.StatusCode = 200
return res, nil
@@ -164,7 +164,7 @@ func TestCopilot_Chat_DecodeError_StatusOK(t *testing.T) {
}
// Chat returns 200 but invalid JSON; expect decode error
srv := newIPv4Server(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, "{invalid")
+ _, _ = io.WriteString(w, "{invalid")
}))
defer srv.Close()
c := newCopilot(srv.URL, "gpt-4o-mini", "KEY", f64p(0.1)).(copilotClient)
@@ -197,9 +197,9 @@ func TestCopilot_CodeCompletion_MalformedAndEmpty(t *testing.T) {
if r.URL.Host == "copilot-proxy.githubusercontent.com" && strings.HasSuffix(r.URL.Path, "/v1/engines/copilot-codex/completions") {
rw := httptest.NewRecorder()
// malformed line
- rw.WriteString("data: {bad}\n")
+ _, _ = rw.WriteString("data: {bad}\n")
// done; should produce empty suggestions
- rw.WriteString("data: [DONE]\n")
+ _, _ = rw.WriteString("data: [DONE]\n")
res := rw.Result()
res.StatusCode = 200
return res, nil
@@ -226,9 +226,9 @@ func TestCopilot_CodeCompletion_MalformedAndEmpty(t *testing.T) {
}
if r.URL.Host == "copilot-proxy.githubusercontent.com" && strings.HasSuffix(r.URL.Path, "/v1/engines/copilot-codex/completions") {
rw := httptest.NewRecorder()
- rw.WriteString("data: {bad}\n")
- rw.WriteString("data: {\"choices\":[{\"index\":0,\"text\":\"OK\"}]}\n")
- rw.WriteString("data: [DONE]\n")
+ _, _ = rw.WriteString("data: {bad}\n")
+ _, _ = rw.WriteString("data: {\"choices\":[{\"index\":0,\"text\":\"OK\"}]}\n")
+ _, _ = rw.WriteString("data: [DONE]\n")
res := rw.Result()
res.StatusCode = 200
return res, nil
diff --git a/internal/llm/ollama.go b/internal/llm/ollama.go
index 374a771..f355166 100644
--- a/internal/llm/ollama.go
+++ b/internal/llm/ollama.go
@@ -81,7 +81,11 @@ func (c ollamaClient) Chat(ctx context.Context, messages []Message, opts ...Requ
logging.Logf("llm/ollama ", "%shttp error after %s: %v%s", logging.AnsiRed, time.Since(start), err, logging.AnsiBase)
return "", err
}
- defer resp.Body.Close()
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logging.Logf("llm/ollama", "failed to close response body: %v", err)
+ }
+ }()
if err := handleOllamaNon2xx(resp, start); err != nil {
return "", err
}
@@ -129,7 +133,11 @@ func (c ollamaClient) ChatStream(ctx context.Context, messages []Message, onDelt
logging.Logf("llm/ollama ", "%shttp error after %s: %v%s", logging.AnsiRed, time.Since(start), err, logging.AnsiBase)
return err
}
- defer resp.Body.Close()
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logging.Logf("llm/ollama", "failed to close response body: %v", err)
+ }
+ }()
if err := handleOllamaNon2xx(resp, start); err != nil {
return err
}
@@ -172,7 +180,7 @@ func buildOllamaRequest(o Options, messages []Message, defaultTemp *float64, str
req := ollamaChatRequest{Model: o.Model, Stream: stream}
req.Messages = make([]oaMessage, len(messages))
for i, m := range messages {
- req.Messages[i] = oaMessage{Role: m.Role, Content: m.Content}
+ req.Messages[i] = oaMessage(m)
}
optsMap := map[string]any{}
if o.Temperature != 0 {
diff --git a/internal/llm/openai.go b/internal/llm/openai.go
index c284bb3..b97111d 100644
--- a/internal/llm/openai.go
+++ b/internal/llm/openai.go
@@ -121,7 +121,11 @@ func (c openAIClient) Chat(ctx context.Context, messages []Message, opts ...Requ
logging.Logf("llm/openai ", "%shttp error after %s: %v%s", logging.AnsiRed, time.Since(start), err, logging.AnsiBase)
return "", err
}
- defer resp.Body.Close()
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logging.Logf("llm/openai", "failed to close response body: %v", err)
+ }
+ }()
if err := handleOpenAINon2xx(resp, start, "llm/openai ", "openai"); err != nil {
return "", err
}
@@ -172,7 +176,11 @@ func (c openAIClient) ChatStream(ctx context.Context, messages []Message, onDelt
logging.Logf("llm/openai ", "%shttp error after %s: %v%s", logging.AnsiRed, time.Since(start), err, logging.AnsiBase)
return err
}
- defer resp.Body.Close()
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logging.Logf("llm/openai", "failed to close response body: %v", err)
+ }
+ }()
if err := handleOpenAINon2xx(resp, start, "llm/openai ", "openai"); err != nil {
return err
}
@@ -200,7 +208,7 @@ func buildOAChatRequest(o Options, messages []Message, defaultTemp *float64, str
req := oaChatRequest{Model: o.Model, Stream: stream}
req.Messages = make([]oaMessage, len(messages))
for i, m := range messages {
- req.Messages[i] = oaMessage{Role: m.Role, Content: m.Content}
+ req.Messages[i] = oaMessage(m)
}
if o.Temperature != 0 {
req.Temperature = &o.Temperature
diff --git a/internal/llm/openai_http_test.go b/internal/llm/openai_http_test.go
index affcae9..d0fc828 100644
--- a/internal/llm/openai_http_test.go
+++ b/internal/llm/openai_http_test.go
@@ -45,8 +45,8 @@ func TestOpenAI_ChatStream_SSE(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Return SSE-like stream
w.Header().Set("Content-Type", "text/event-stream")
- io.WriteString(w, "data: {\"choices\":[{\"delta\":{\"content\":\"Hi\"}}]}\n\n")
- io.WriteString(w, "data: [DONE]\n")
+ _, _ = io.WriteString(w, "data: {\"choices\":[{\"delta\":{\"content\":\"Hi\"}}]}\n\n")
+ _, _ = io.WriteString(w, "data: [DONE]\n")
}))
defer srv.Close()
c := newOpenAI(srv.URL, "g", "KEY", f64p(0.2)).(openAIClient)
@@ -71,8 +71,8 @@ func TestOpenAI_ChatStream_SSE_ErrorChunk(t *testing.T) {
}
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
- io.WriteString(w, "data: {\"error\":{\"message\":\"oops\"}}\n\n")
- io.WriteString(w, "data: [DONE]\n")
+ _, _ = io.WriteString(w, "data: {\"error\":{\"message\":\"oops\"}}\n\n")
+ _, _ = io.WriteString(w, "data: [DONE]\n")
}))
defer srv.Close()
c := newOpenAI(srv.URL, "g", "KEY", f64p(0.2)).(openAIClient)
@@ -104,8 +104,8 @@ func TestOpenAI_ChatStream_SSE_EmptyDelta_NoError(t *testing.T) {
}
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
- io.WriteString(w, "data: {\\\"choices\\\":[{\\\"delta\\\":{\\\"content\\\":\\\"\\\"}}]}\\n\\n")
- io.WriteString(w, "data: [DONE]\\n")
+ _, _ = io.WriteString(w, "data: {\\\"choices\\\":[{\\\"delta\\\":{\\\"content\\\":\\\"\\\"}}]}\\n\\n")
+ _, _ = io.WriteString(w, "data: [DONE]\\n")
}))
defer srv.Close()
c := newOpenAI(srv.URL, "g", "KEY", f64p(0.2)).(openAIClient)
@@ -126,7 +126,7 @@ func TestOpenAI_Chat_DecodeError_StatusOK(t *testing.T) {
// Return status 200 but invalid JSON body; Chat should return an error
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
- io.WriteString(w, "{invalid")
+ _, _ = io.WriteString(w, "{invalid")
}))
defer srv.Close()
c := newOpenAI(srv.URL, "g", "KEY", f64p(0.2)).(openAIClient)
diff --git a/internal/llm/openai_sse_negative_test.go b/internal/llm/openai_sse_negative_test.go
index de2ff71..7f4f7db 100644
--- a/internal/llm/openai_sse_negative_test.go
+++ b/internal/llm/openai_sse_negative_test.go
@@ -16,8 +16,8 @@ func TestOpenAI_ChatStream_SSE_MalformedChunk(t *testing.T) {
// Malformed JSON chunk should be skipped; no onDelta calls; no error.
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
- io.WriteString(w, "data: {not json}\n\n")
- io.WriteString(w, "data: [DONE]\n")
+ _, _ = io.WriteString(w, "data: {not json}\n\n")
+ _, _ = io.WriteString(w, "data: [DONE]\n")
}))
defer srv.Close()
c := newOpenAI(srv.URL, "g", "KEY", f64p(0.2)).(openAIClient)
diff --git a/internal/llm/openrouter.go b/internal/llm/openrouter.go
index f03844a..4aae398 100644
--- a/internal/llm/openrouter.go
+++ b/internal/llm/openrouter.go
@@ -65,7 +65,11 @@ func (c openRouterClient) Chat(ctx context.Context, messages []Message, opts ...
logging.Logf("llm/openrouter ", "%shttp error after %s: %v%s", logging.AnsiRed, time.Since(start), err, logging.AnsiBase)
return "", err
}
- defer resp.Body.Close()
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logging.Logf("llm/openrouter", "failed to close response body: %v", err)
+ }
+ }()
if err := handleOpenAINon2xx(resp, start, "llm/openrouter ", "openrouter"); err != nil {
return "", err
}
@@ -111,7 +115,11 @@ func (c openRouterClient) ChatStream(ctx context.Context, messages []Message, on
logging.Logf("llm/openrouter ", "%shttp error after %s: %v%s", logging.AnsiRed, time.Since(start), err, logging.AnsiBase)
return err
}
- defer resp.Body.Close()
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logging.Logf("llm/openrouter", "failed to close response body: %v", err)
+ }
+ }()
if err := handleOpenAINon2xx(resp, start, "llm/openrouter ", "openrouter"); err != nil {
return err
}
diff --git a/internal/llm/openrouter_test.go b/internal/llm/openrouter_test.go
index 2a07be0..f8efe16 100644
--- a/internal/llm/openrouter_test.go
+++ b/internal/llm/openrouter_test.go
@@ -75,8 +75,8 @@ func TestOpenRouter_ChatStream_SendsHeaders(t *testing.T) {
acceptHeader = r.Header.Get("Accept")
referer = r.Header.Get("HTTP-Referer")
w.Header().Set("Content-Type", "text/event-stream")
- io.WriteString(w, "data: {\"choices\":[{\"delta\":{\"content\":\"hi\"}}]}\n\n")
- io.WriteString(w, "data: [DONE]\n")
+ _, _ = io.WriteString(w, "data: {\"choices\":[{\"delta\":{\"content\":\"hi\"}}]}\n\n")
+ _, _ = io.WriteString(w, "data: [DONE]\n")
}))
defer srv.Close()
diff --git a/internal/lsp/codeaction_prompts_test.go b/internal/lsp/codeaction_prompts_test.go
index c5fd5e2..8b07dfe 100644
--- a/internal/lsp/codeaction_prompts_test.go
+++ b/internal/lsp/codeaction_prompts_test.go
@@ -59,7 +59,7 @@ func TestResolveCodeAction_UsesDiagnosticsPrompts(t *testing.T) {
if cap.msgs[0].Content != "DSYS" || cap.msgs[1].Role != "user" {
t.Fatalf("unexpected diagnostics prompts: %#v", cap.msgs)
}
- if got := cap.msgs[1].Content; !(contains(got, "oops1") && contains(got, "oops2") && contains(got, "var a")) {
+ if got := cap.msgs[1].Content; !contains(got, "oops1") || !contains(got, "oops2") || !contains(got, "var a") {
t.Fatalf("diagnostics/user content mismatch: %q", got)
}
}
diff --git a/internal/lsp/completion_messages_test.go b/internal/lsp/completion_messages_test.go
index f0c693c..bc02645 100644
--- a/internal/lsp/completion_messages_test.go
+++ b/internal/lsp/completion_messages_test.go
@@ -89,7 +89,7 @@ func contains(s, sub string) bool {
}
func stringIndex(s, sub string) int {
- return len([]rune(s[:])) - len([]rune(s[:])) + (func() int { return intIndex(s, sub) })()
+ return intIndex(s, sub)
}
func intIndex(s, sub string) int { return Index(s, sub) }
diff --git a/internal/stats/stats.go b/internal/stats/stats.go
index a8390ef..a98b2c5 100644
--- a/internal/stats/stats.go
+++ b/internal/stats/stats.go
@@ -88,7 +88,7 @@ func Update(ctx context.Context, provider, model string, sentBytes, recvBytes in
if err != nil {
return err
}
- defer f.Close()
+ defer func() { _ = f.Close() }()
unlock, err := acquireFileLock(ctx, f)
if err != nil {
return err
@@ -131,21 +131,21 @@ func Update(ctx context.Context, provider, model string, sentBytes, recvBytes in
enc := json.NewEncoder(tmp)
enc.SetEscapeHTML(false)
if err := enc.Encode(&sf); err != nil {
- tmp.Close()
- os.Remove(tmp.Name())
+ _ = tmp.Close()
+ _ = os.Remove(tmp.Name())
return err
}
if err := tmp.Sync(); err != nil {
- tmp.Close()
- os.Remove(tmp.Name())
+ _ = tmp.Close()
+ _ = os.Remove(tmp.Name())
return err
}
if err := tmp.Close(); err != nil {
- os.Remove(tmp.Name())
+ _ = os.Remove(tmp.Name())
return err
}
if err := os.Rename(tmp.Name(), path); err != nil {
- os.Remove(tmp.Name())
+ _ = os.Remove(tmp.Name())
return err
}
return nil
diff --git a/internal/tmux/status_more_test.go b/internal/tmux/status_more_test.go
index deaf57d..39cbe45 100644
--- a/internal/tmux/status_more_test.go
+++ b/internal/tmux/status_more_test.go
@@ -22,16 +22,16 @@ func TestFormatLLMStatsStatusColored_Basic(t *testing.T) {
func TestFormatGlobalStatusColored_NarrowAndMaxLen(t *testing.T) {
// Narrow mode should elide the tail
- os.Setenv("HEXAI_TMUX_STATUS_NARROW", "1")
- defer os.Unsetenv("HEXAI_TMUX_STATUS_NARROW")
+ _ = os.Setenv("HEXAI_TMUX_STATUS_NARROW", "1")
+ defer func() { _ = os.Unsetenv("HEXAI_TMUX_STATUS_NARROW") }()
s := FormatGlobalStatusColored(10, 3.3, 1000, 2000, "prov", "model", 1.1, 4, 30*time.Minute)
if containsAll(s, []string{"|", "prov:model"}) {
t.Fatalf("narrow mode should not include tail: %q", s)
}
// Max length should also drop the tail when it would overflow
- os.Unsetenv("HEXAI_TMUX_STATUS_NARROW")
- os.Setenv("HEXAI_TMUX_STATUS_MAXLEN", "10")
- defer os.Unsetenv("HEXAI_TMUX_STATUS_MAXLEN")
+ _ = os.Unsetenv("HEXAI_TMUX_STATUS_NARROW")
+ _ = os.Setenv("HEXAI_TMUX_STATUS_MAXLEN", "10")
+ defer func() { _ = os.Unsetenv("HEXAI_TMUX_STATUS_MAXLEN") }()
s2 := FormatGlobalStatusColored(10, 3.3, 1000, 2000, "prov", "model", 1.1, 4, 30*time.Minute)
if containsAll(s2, []string{"|", "prov:model"}) {
t.Fatalf("maxlen should drop tail when overflowing: %q", s2)
diff --git a/internal/version.go b/internal/version.go
index 021cc9d..052c674 100644
--- a/internal/version.go
+++ b/internal/version.go
@@ -1,4 +1,4 @@
// Summary: Hexai semantic version identifier used by CLI and LSP binaries.
package internal
-const Version = "0.15.1"
+const Version = "0.15.2"