From ec9433d783ea8595f58236e84b87899245f7dca9 Mon Sep 17 00:00:00 2001 From: vikingowl <26+vikingowl@noreply.somegit.dev> Date: Tue, 19 May 2026 17:53:42 +0200 Subject: [PATCH] chore(lint): clear remaining errcheck and staticcheck findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brings the project to a clean `make lint` baseline (0 issues). Mechanical: - Wrap deferred resp.Body.Close() in closures (router/discovery.go, router/probe.go) so the unchecked return surfaces as `_ = ...`. - Apply `_ = ...` (single or multi-return blank) to test-file calls that intentionally ignore errors: os.MkdirAll / os.WriteFile / os.Chdir in setup paths, Close / Shutdown in teardown, Submit / Spawn / Send / LoadDir in tests that assert on side effects. Structural: - engine.handleRequestTooLarge drops the unused req parameter and rebuilds the request from compacted history (SA4009 — argument was overwritten before first use). - provider.ClassifyHTTPStatus and google.applyCapabilityOverrides switch to tagged switches over the discriminator (QF1002). - tui.app.go MouseWheel + inputMode and cmd/gnoma main slm-status use tagged switches in place of equality chains (QF1003). - cmd/gnoma main.go merges a var decl with its immediate assignment (S1021). - Three empty-branch sites (dispatcher_test, loader_test, coordinator_test) become real assertions or get the dead `if` removed (SA9003). --- cmd/gnoma/main.go | 12 ++-- internal/config/config_test.go | 72 ++++++++++----------- internal/context/context_test.go | 2 +- internal/elf/elf_test.go | 6 +- internal/engine/engine_test.go | 10 +-- internal/engine/hook_integration_test.go | 12 ++-- internal/engine/loop.go | 9 +-- internal/engine/restore_test.go | 4 +- internal/hook/agent_test.go | 2 +- internal/hook/dispatcher_test.go | 10 +-- internal/hook/prompt_test.go | 2 +- internal/mcp/client_test.go | 14 ++-- internal/mcp/manager_test.go | 8 +-- internal/mcp/tool_test.go | 12 ++-- internal/mcp/transport_test.go | 16 ++--- internal/plugin/loader_test.go | 15 +++-- internal/plugin/manager_test.go | 34 +++++----- internal/provider/errors.go | 14 ++-- internal/provider/google/provider.go | 9 ++- internal/provider/subprocess/stream_test.go | 6 +- internal/router/discovery.go | 4 +- internal/router/probe.go | 4 +- internal/router/probe_test.go | 14 ++-- internal/session/session_test.go | 8 +-- internal/session/store_test.go | 14 ++-- internal/skill/registry_test.go | 8 +-- internal/slm/download_test.go | 8 +-- internal/slm/manager_test.go | 2 +- internal/tool/agent/coordinator_test.go | 8 +-- internal/tool/fs/fs_test.go | 32 ++++----- internal/tool/persist/store_test.go | 8 +-- internal/tool/registry_test.go | 2 +- internal/tui/app.go | 10 +-- 33 files changed, 194 insertions(+), 197 deletions(-) diff --git a/cmd/gnoma/main.go b/cmd/gnoma/main.go index 1dc942e..ed44fc6 100644 --- a/cmd/gnoma/main.go +++ b/cmd/gnoma/main.go @@ -344,8 +344,8 @@ func main() { return } dir := filepath.Join(userCfgDir, "gnoma") - os.MkdirAll(dir, 0o755) - os.WriteFile(filepath.Join(dir, "quality.json"), data, 0o644) + _ = os.MkdirAll(dir, 0o755) + _ = os.WriteFile(filepath.Join(dir, "quality.json"), data, 0o644) }() var armID router.ArmID if primaryProviderOK { @@ -641,8 +641,7 @@ func main() { } // Create context window with summarize strategy (falls back to truncation) - var compactStrategy gnomactx.Strategy - compactStrategy = gnomactx.NewSummarizeStrategy(prov) + var compactStrategy gnomactx.Strategy = gnomactx.NewSummarizeStrategy(prov) ctxWindow := gnomactx.NewWindow(gnomactx.WindowConfig{ MaxTokens: contextWindowSize, Strategy: compactStrategy, @@ -1334,9 +1333,10 @@ func runSLMCommand(args []string, cfg *gnomacfg.Config, logger *slog.Logger) int fmt.Printf(" sha256: %s\n", mf.SHA256[:16]+"...") fmt.Printf(" setup: %s\n", mf.SetupAt.Format("2006-01-02 15:04 UTC")) } - if status == slm.StatusNotSetUp { + switch status { + case slm.StatusNotSetUp: fmt.Println(" run: gnoma slm setup") - } else if status == slm.StatusMissing { + case slm.StatusMissing: fmt.Println(" file is missing; run: gnoma slm setup") } return 0 diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 9e1e92c..48cf130 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -40,7 +40,7 @@ ollama = "http://myhost:11434/v1" bash_timeout = "60s" max_file_size = 2097152 ` - os.WriteFile(path, []byte(content), 0o644) + _ = os.WriteFile(path, []byte(content), 0o644) cfg := Defaults() if err := loadTOML(&cfg, path); err != nil { @@ -122,12 +122,12 @@ func TestApplyEnv_EnvVarReference(t *testing.T) { func TestProjectRoot_GoMod(t *testing.T) { root := t.TempDir() sub := filepath.Join(root, "pkg", "util") - os.MkdirAll(sub, 0o755) - os.WriteFile(filepath.Join(root, "go.mod"), []byte("module example.com/foo\n"), 0o644) + _ = os.MkdirAll(sub, 0o755) + _ = os.WriteFile(filepath.Join(root, "go.mod"), []byte("module example.com/foo\n"), 0o644) origDir, _ := os.Getwd() - os.Chdir(sub) - defer os.Chdir(origDir) + _ = os.Chdir(sub) + defer func() { _ = os.Chdir(origDir) }() got := ProjectRoot() if got != root { @@ -138,12 +138,12 @@ func TestProjectRoot_GoMod(t *testing.T) { func TestProjectRoot_Git(t *testing.T) { root := t.TempDir() sub := filepath.Join(root, "src") - os.MkdirAll(sub, 0o755) - os.MkdirAll(filepath.Join(root, ".git"), 0o755) + _ = os.MkdirAll(sub, 0o755) + _ = os.MkdirAll(filepath.Join(root, ".git"), 0o755) origDir, _ := os.Getwd() - os.Chdir(sub) - defer os.Chdir(origDir) + _ = os.Chdir(sub) + defer func() { _ = os.Chdir(origDir) }() got := ProjectRoot() if got != root { @@ -154,12 +154,12 @@ func TestProjectRoot_Git(t *testing.T) { func TestProjectRoot_GnomaDir(t *testing.T) { root := t.TempDir() sub := filepath.Join(root, "internal") - os.MkdirAll(sub, 0o755) - os.MkdirAll(filepath.Join(root, ".gnoma"), 0o755) + _ = os.MkdirAll(sub, 0o755) + _ = os.MkdirAll(filepath.Join(root, ".gnoma"), 0o755) origDir, _ := os.Getwd() - os.Chdir(sub) - defer os.Chdir(origDir) + _ = os.Chdir(sub) + defer func() { _ = os.Chdir(origDir) }() got := ProjectRoot() if got != root { @@ -171,8 +171,8 @@ func TestProjectRoot_Fallback(t *testing.T) { dir := t.TempDir() origDir, _ := os.Getwd() - os.Chdir(dir) - defer os.Chdir(origDir) + _ = os.Chdir(dir) + defer func() { _ = os.Chdir(origDir) }() got := ProjectRoot() if got != dir { @@ -183,7 +183,7 @@ func TestProjectRoot_Fallback(t *testing.T) { func TestHookConfig_TOML_RoundTrip(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "config.toml") - os.WriteFile(path, []byte(` + _ = os.WriteFile(path, []byte(` [[hooks]] name = "log-tools" event = "post_tool_use" @@ -228,8 +228,8 @@ tool_pattern = "bash*" func TestHookConfig_MergeOrder(t *testing.T) { globalDir := t.TempDir() gnomaDir := filepath.Join(globalDir, "gnoma") - os.MkdirAll(gnomaDir, 0o755) - os.WriteFile(filepath.Join(gnomaDir, "config.toml"), []byte(` + _ = os.MkdirAll(gnomaDir, 0o755) + _ = os.WriteFile(filepath.Join(gnomaDir, "config.toml"), []byte(` [[hooks]] name = "global-hook" event = "pre_tool_use" @@ -239,8 +239,8 @@ exec = "echo global" projectDir := t.TempDir() pGnomaDir := filepath.Join(projectDir, ".gnoma") - os.MkdirAll(pGnomaDir, 0o755) - os.WriteFile(filepath.Join(pGnomaDir, "config.toml"), []byte(` + _ = os.MkdirAll(pGnomaDir, 0o755) + _ = os.WriteFile(filepath.Join(pGnomaDir, "config.toml"), []byte(` [[hooks]] name = "project-hook" event = "post_tool_use" @@ -250,8 +250,8 @@ exec = "echo project" t.Setenv("XDG_CONFIG_HOME", globalDir) origDir, _ := os.Getwd() - os.Chdir(projectDir) - defer os.Chdir(origDir) + _ = os.Chdir(projectDir) + defer func() { _ = os.Chdir(origDir) }() cfg, err := Load() if err != nil { @@ -273,8 +273,8 @@ func TestHookConfig_ProjectOnly(t *testing.T) { // No global hooks, project defines one. projectDir := t.TempDir() pGnomaDir := filepath.Join(projectDir, ".gnoma") - os.MkdirAll(pGnomaDir, 0o755) - os.WriteFile(filepath.Join(pGnomaDir, "config.toml"), []byte(` + _ = os.MkdirAll(pGnomaDir, 0o755) + _ = os.WriteFile(filepath.Join(pGnomaDir, "config.toml"), []byte(` [[hooks]] name = "project-only" event = "stop" @@ -285,8 +285,8 @@ exec = "echo done" emptyGlobalDir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", emptyGlobalDir) origDir, _ := os.Getwd() - os.Chdir(projectDir) - defer os.Chdir(origDir) + _ = os.Chdir(projectDir) + defer func() { _ = os.Chdir(origDir) }() cfg, err := Load() if err != nil { @@ -301,8 +301,8 @@ func TestHookConfig_GlobalOnly(t *testing.T) { // Global defines a hook, no project config. globalDir := t.TempDir() gnomaDir := filepath.Join(globalDir, "gnoma") - os.MkdirAll(gnomaDir, 0o755) - os.WriteFile(filepath.Join(gnomaDir, "config.toml"), []byte(` + _ = os.MkdirAll(gnomaDir, 0o755) + _ = os.WriteFile(filepath.Join(gnomaDir, "config.toml"), []byte(` [[hooks]] name = "global-only" event = "session_start" @@ -313,8 +313,8 @@ exec = "echo start" projectDir := t.TempDir() // no .gnoma dir t.Setenv("XDG_CONFIG_HOME", globalDir) origDir, _ := os.Getwd() - os.Chdir(projectDir) - defer os.Chdir(origDir) + _ = os.Chdir(projectDir) + defer func() { _ = os.Chdir(origDir) }() cfg, err := Load() if err != nil { @@ -329,8 +329,8 @@ func TestLayeredLoad(t *testing.T) { // Set up global config globalDir := t.TempDir() gnomaDir := filepath.Join(globalDir, "gnoma") - os.MkdirAll(gnomaDir, 0o755) - os.WriteFile(filepath.Join(gnomaDir, "config.toml"), []byte(` + _ = os.MkdirAll(gnomaDir, 0o755) + _ = os.WriteFile(filepath.Join(gnomaDir, "config.toml"), []byte(` [provider] default = "anthropic" max_tokens = 4096 @@ -339,8 +339,8 @@ max_tokens = 4096 // Set up project config that overrides projectDir := t.TempDir() pGnomaDir := filepath.Join(projectDir, ".gnoma") - os.MkdirAll(pGnomaDir, 0o755) - os.WriteFile(filepath.Join(pGnomaDir, "config.toml"), []byte(` + _ = os.MkdirAll(pGnomaDir, 0o755) + _ = os.WriteFile(filepath.Join(pGnomaDir, "config.toml"), []byte(` [provider] model = "claude-haiku" `), 0o644) @@ -348,8 +348,8 @@ model = "claude-haiku" // Override XDG_CONFIG_HOME and working directory t.Setenv("XDG_CONFIG_HOME", globalDir) origDir, _ := os.Getwd() - os.Chdir(projectDir) - defer os.Chdir(origDir) + _ = os.Chdir(projectDir) + defer func() { _ = os.Chdir(origDir) }() cfg, err := Load() if err != nil { diff --git a/internal/context/context_test.go b/internal/context/context_test.go index d03ed37..a23ea98 100644 --- a/internal/context/context_test.go +++ b/internal/context/context_test.go @@ -179,7 +179,7 @@ func TestWindow_CircuitBreaker(t *testing.T) { // Try to compact — should fail 3 times then stop for i := 0; i < 5; i++ { - w.CompactIfNeeded() + _, _ = w.CompactIfNeeded() } if failStrategy.calls > 3 { diff --git a/internal/elf/elf_test.go b/internal/elf/elf_test.go index 4b058c0..f0ad84d 100644 --- a/internal/elf/elf_test.go +++ b/internal/elf/elf_test.go @@ -202,9 +202,9 @@ func TestManager_WaitAll(t *testing.T) { mgr := NewManager(ManagerConfig{Router: rtr, Tools: tool.NewRegistry()}) - mgr.Spawn(context.Background(), router.TaskGeneration, "a", "", 30) - mgr.Spawn(context.Background(), router.TaskGeneration, "b", "", 30) - mgr.Spawn(context.Background(), router.TaskGeneration, "c", "", 30) + _, _ = mgr.Spawn(context.Background(), router.TaskGeneration, "a", "", 30) + _, _ = mgr.Spawn(context.Background(), router.TaskGeneration, "b", "", 30) + _, _ = mgr.Spawn(context.Background(), router.TaskGeneration, "c", "", 30) results := mgr.WaitAll() if len(results) != 3 { diff --git a/internal/engine/engine_test.go b/internal/engine/engine_test.go index f33ce63..3d0993c 100644 --- a/internal/engine/engine_test.go +++ b/internal/engine/engine_test.go @@ -429,7 +429,7 @@ func TestEngine_Reset(t *testing.T) { } e, _ := New(Config{Provider: mp, Tools: tool.NewRegistry()}) - e.Submit(context.Background(), "hello", nil) + _, _ = e.Submit(context.Background(), "hello", nil) if len(e.History()) == 0 { t.Fatal("history should not be empty before reset") @@ -463,7 +463,7 @@ func TestEngine_Reset_ClearsContextWindow(t *testing.T) { Tools: tool.NewRegistry(), Context: ctxWindow, }) - e.Submit(context.Background(), "hello", nil) + _, _ = e.Submit(context.Background(), "hello", nil) if len(ctxWindow.Messages()) == 0 { t.Fatal("context window should have messages before reset") @@ -542,7 +542,7 @@ func TestSubmit_TrackerReflectsInputTokens(t *testing.T) { } e, _ := New(Config{Provider: mp, Tools: tool.NewRegistry(), Context: ctxWindow}) - e.Submit(context.Background(), "hi", nil) + _, _ = e.Submit(context.Background(), "hi", nil) // Tracker should be InputTokens + OutputTokens = 150, not more used := ctxWindow.Tracker().Used() @@ -568,8 +568,8 @@ func TestSubmit_CumulativeUsage(t *testing.T) { e, _ := New(Config{Provider: mp, Tools: tool.NewRegistry()}) - e.Submit(context.Background(), "one", nil) - e.Submit(context.Background(), "two", nil) + _, _ = e.Submit(context.Background(), "one", nil) + _, _ = e.Submit(context.Background(), "two", nil) if e.Usage().InputTokens != 300 { t.Errorf("cumulative InputTokens = %d, want 300", e.Usage().InputTokens) diff --git a/internal/engine/hook_integration_test.go b/internal/engine/hook_integration_test.go index bd0ecbc..b22f6b3 100644 --- a/internal/engine/hook_integration_test.go +++ b/internal/engine/hook_integration_test.go @@ -123,7 +123,7 @@ func TestHook_PreToolUse_Deny(t *testing.T) { Tools: reg, Hooks: hookDispatcher(hook.PreToolUse, &blockingExecutor{}), }) - eng.Submit(context.Background(), "run", nil) + _, _ = eng.Submit(context.Background(), "run", nil) if executed { t.Error("tool was executed despite PreToolUse deny") @@ -155,7 +155,7 @@ func TestHook_PreToolUse_Allow(t *testing.T) { Tools: reg, Hooks: hookDispatcher(hook.PreToolUse, &allowingExecutor{}), }) - eng.Submit(context.Background(), "run", nil) + _, _ = eng.Submit(context.Background(), "run", nil) if !executed { t.Error("tool was not executed despite PreToolUse allow") @@ -185,7 +185,7 @@ func TestHook_PreToolUse_DenyMessage(t *testing.T) { Tools: reg, Hooks: hookDispatcher(hook.PreToolUse, &blockingExecutor{}), }) - eng.Submit(context.Background(), "run", nil) + _, _ = eng.Submit(context.Background(), "run", nil) for _, msg := range eng.History() { for _, c := range msg.Content { @@ -226,10 +226,10 @@ func TestHook_PreToolUse_Transform(t *testing.T) { Hooks: hookDispatcher(hook.PreToolUse, &argTransformExecutor{newArgs: json.RawMessage(`{"command":"safe-replacement"}`)}), }) - eng.Submit(context.Background(), "run", nil) + _, _ = eng.Submit(context.Background(), "run", nil) var got map[string]string - json.Unmarshal(receivedArgs, &got) + _ = json.Unmarshal(receivedArgs, &got) if got["command"] != "safe-replacement" { t.Errorf("tool args = %s, want safe-replacement", receivedArgs) } @@ -259,7 +259,7 @@ func TestHook_PostToolUse_Transform(t *testing.T) { Hooks: hookDispatcher(hook.PostToolUse, &resultTransformExecutor{newOutput: "transformed output"}), }) - eng.Submit(context.Background(), "run", nil) + _, _ = eng.Submit(context.Background(), "run", nil) for _, msg := range eng.History() { for _, c := range msg.Content { diff --git a/internal/engine/loop.go b/internal/engine/loop.go index 3e933d7..976bbc7 100644 --- a/internal/engine/loop.go +++ b/internal/engine/loop.go @@ -198,7 +198,7 @@ func (e *Engine) runLoop(ctx context.Context, cb Callback) (*Turn, error) { }) if err != nil { // Try reactive compaction on 413 (request too large) - s, err = e.handleRequestTooLarge(ctx, err, req) + s, err = e.handleRequestTooLarge(ctx, err) if err != nil { decision.Rollback() streamErr := fmt.Errorf("provider stream: %w", err) @@ -698,8 +698,9 @@ func truncate(s string, maxLen int) string { return string(runes[:maxLen]) + "..." } -// handleRequestTooLarge attempts compaction on 413 and retries once. -func (e *Engine) handleRequestTooLarge(ctx context.Context, origErr error, req provider.Request) (stream.Stream, error) { +// handleRequestTooLarge attempts compaction on 413 and retries once. The +// request is rebuilt from the compacted history, so callers don't pass it in. +func (e *Engine) handleRequestTooLarge(ctx context.Context, origErr error) (stream.Stream, error) { var provErr *provider.ProviderError if !errors.As(origErr, &provErr) || provErr.StatusCode != 413 { return nil, origErr @@ -716,7 +717,7 @@ func (e *Engine) handleRequestTooLarge(ctx context.Context, origErr error, req p } e.replaceHistory(e.cfg.Context.Messages()) - req = e.buildRequest(ctx) + req := e.buildRequest(ctx) if e.cfg.Router != nil { prompt := e.latestUserPrompt() diff --git a/internal/engine/restore_test.go b/internal/engine/restore_test.go index 9413edd..985826d 100644 --- a/internal/engine/restore_test.go +++ b/internal/engine/restore_test.go @@ -60,7 +60,7 @@ func TestSetHistory_OverwritesPreviousHistory(t *testing.T) { }, } e, _ := New(Config{Provider: mp, Tools: tool.NewRegistry()}) - e.Submit(context.Background(), "first message", nil) + _, _ = e.Submit(context.Background(), "first message", nil) if len(e.History()) == 0 { t.Fatal("history should not be empty after Submit") @@ -174,7 +174,7 @@ func TestSetUsage_OverwritesPreviousUsage(t *testing.T) { }, } e, _ := New(Config{Provider: mp, Tools: tool.NewRegistry()}) - e.Submit(context.Background(), "hello", nil) + _, _ = e.Submit(context.Background(), "hello", nil) if e.Usage().InputTokens == 0 { t.Fatal("usage should be non-zero after Submit") diff --git a/internal/hook/agent_test.go b/internal/hook/agent_test.go index 03dcbd7..bca849d 100644 --- a/internal/hook/agent_test.go +++ b/internal/hook/agent_test.go @@ -82,7 +82,7 @@ func TestAgentExecutor_TemplateRendered(t *testing.T) { } fn, captured := capturingSpawnFn("ALLOW") ex := NewAgentExecutor(def, fn) - ex.Execute(context.Background(), MarshalPreToolPayload("bash", nil)) + _, _ = ex.Execute(context.Background(), MarshalPreToolPayload("bash", nil)) if *captured != "Tool=bash Event=pre_tool_use" { t.Errorf("prompt = %q", *captured) } diff --git a/internal/hook/dispatcher_test.go b/internal/hook/dispatcher_test.go index 4b8635e..d860714 100644 --- a/internal/hook/dispatcher_test.go +++ b/internal/hook/dispatcher_test.go @@ -173,7 +173,7 @@ func TestDispatcher_TransformChaining(t *testing.T) { makeHandler(PreToolUse, exA), makeHandler(PreToolUse, exB), ) - d.Fire(PreToolUse, []byte(`{"tool":"original"}`)) + _, _, _ = d.Fire(PreToolUse, []byte(`{"tool":"original"}`)) if string(exB.receivedPayload) != string(transformed) { t.Errorf("exB received %q, want %q", exB.receivedPayload, transformed) @@ -190,7 +190,7 @@ func TestDispatcher_TransformChaining_EmptyOutputPassesThrough(t *testing.T) { makeHandler(PreToolUse, exA), makeHandler(PreToolUse, exB), ) - d.Fire(PreToolUse, original) + _, _, _ = d.Fire(PreToolUse, original) if string(exB.receivedPayload) != string(original) { t.Errorf("exB received %q, want %q", exB.receivedPayload, original) @@ -223,11 +223,7 @@ func TestDispatcher_ToolPattern_Empty_MatchesAll(t *testing.T) { payload := MarshalPreToolPayload("fs.read", nil) d := dispatcherWith(PreToolUse, makePatternHandler("", ex)) _, action, _ := d.Fire(PreToolUse, payload) - if action != Allow { - // empty pattern + Deny → resolveAction sees Deny → Deny - // wait, empty pattern means fire for all tools - } - // correct: empty pattern fires → Deny + // Empty pattern fires for all tools; the Deny handler must resolve to Deny. if action != Deny { t.Errorf("empty pattern matches all: action = %v, want Deny", action) } diff --git a/internal/hook/prompt_test.go b/internal/hook/prompt_test.go index 98e7065..6678131 100644 --- a/internal/hook/prompt_test.go +++ b/internal/hook/prompt_test.go @@ -176,7 +176,7 @@ func TestPromptExecutor_TemplateRendered(t *testing.T) { Exec: "Tool={{.Tool}} Event={{.Event}}", } ex := NewPromptExecutor(def, capturingStreamer) - ex.Execute(context.Background(), MarshalPreToolPayload("bash", nil)) + _, _ = ex.Execute(context.Background(), MarshalPreToolPayload("bash", nil)) capturedPrompt = capturingStreamer.prompt if capturedPrompt == "" { t.Fatal("prompt not captured") diff --git a/internal/mcp/client_test.go b/internal/mcp/client_test.go index aa0aba9..b34bf4f 100644 --- a/internal/mcp/client_test.go +++ b/internal/mcp/client_test.go @@ -17,7 +17,7 @@ func writeMCPServer(t *testing.T, tools []MCPTool, callResult string) string { // Write response payloads as files. initResult := `{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"test-server","version":"1.0.0"}}` - os.WriteFile(filepath.Join(dir, "init.json"), []byte(initResult), 0o644) + _ = os.WriteFile(filepath.Join(dir, "init.json"), []byte(initResult), 0o644) toolsJSON, err := json.Marshal(struct { Tools []MCPTool `json:"tools"` @@ -25,8 +25,8 @@ func writeMCPServer(t *testing.T, tools []MCPTool, callResult string) string { if err != nil { t.Fatalf("marshal tools: %v", err) } - os.WriteFile(filepath.Join(dir, "tools.json"), toolsJSON, 0o644) - os.WriteFile(filepath.Join(dir, "call.json"), []byte(callResult), 0o644) + _ = os.WriteFile(filepath.Join(dir, "tools.json"), toolsJSON, 0o644) + _ = os.WriteFile(filepath.Join(dir, "call.json"), []byte(callResult), 0o644) // The script uses pure bash for JSON parsing — no python3 or jq dependency. // We extract "method" and "id" with grep since the JSON-RPC format is predictable. @@ -79,7 +79,7 @@ func TestClient_Initialize(t *testing.T) { } client := NewClient(tr, logger) - defer client.Close() + defer func() { _ = client.Close() }() if err := client.Initialize(ctx); err != nil { t.Fatalf("Initialize: %v", err) @@ -117,7 +117,7 @@ func TestClient_ListTools(t *testing.T) { } client := NewClient(tr, logger) - defer client.Close() + defer func() { _ = client.Close() }() if err := client.Initialize(ctx); err != nil { t.Fatalf("Initialize: %v", err) @@ -159,7 +159,7 @@ func TestClient_CallTool(t *testing.T) { } client := NewClient(tr, logger) - defer client.Close() + defer func() { _ = client.Close() }() if err := client.Initialize(ctx); err != nil { t.Fatalf("Initialize: %v", err) @@ -210,7 +210,7 @@ echo "{\"jsonrpc\":\"2.0\",\"id\":$id,\"error\":{\"code\":-32000,\"message\":\"i } client := NewClient(tr, logger) - defer client.Close() + defer func() { _ = client.Close() }() err := client.Initialize(ctx) if err == nil { diff --git a/internal/mcp/manager_test.go b/internal/mcp/manager_test.go index f380f22..7e54f84 100644 --- a/internal/mcp/manager_test.go +++ b/internal/mcp/manager_test.go @@ -37,7 +37,7 @@ func TestManager_StartAll_RegistersTools(t *testing.T) { if err != nil { t.Fatalf("StartAll: %v", err) } - defer mgr.Shutdown() + defer func() { _ = mgr.Shutdown() }() // Tools should be registered with mcp__ prefix. if _, ok := reg.Get("mcp__git__status"); !ok { @@ -77,7 +77,7 @@ func TestManager_StartAll_ReplaceDefault(t *testing.T) { if err != nil { t.Fatalf("StartAll: %v", err) } - defer mgr.Shutdown() + defer func() { _ = mgr.Shutdown() }() // The "bash" tool should now be the MCP adapter, not the mock. bashTool, ok := reg.Get("bash") @@ -111,7 +111,7 @@ func TestManager_StartAll_BadCommand(t *testing.T) { }, reg) if err == nil { t.Error("expected error for bad command") - mgr.Shutdown() + _ = mgr.Shutdown() } } @@ -176,7 +176,7 @@ func TestManager_StartAll_ReplaceDefault_PicksMatchingTool(t *testing.T) { if err != nil { t.Fatalf("StartAll: %v", err) } - defer mgr.Shutdown() + defer func() { _ = mgr.Shutdown() }() // fs.read and fs.write should be replaced. if fsRead, ok := reg.Get("fs.read"); !ok { diff --git a/internal/mcp/tool_test.go b/internal/mcp/tool_test.go index 0de2e8b..03caf84 100644 --- a/internal/mcp/tool_test.go +++ b/internal/mcp/tool_test.go @@ -105,7 +105,7 @@ func TestAdapter_Execute(t *testing.T) { } client := NewClient(tr, logger) - defer client.Close() + defer func() { _ = client.Close() }() if err := client.Initialize(ctx); err != nil { t.Fatalf("Initialize: %v", err) @@ -140,7 +140,7 @@ func TestAdapter_Execute_MultipleTextBlocks(t *testing.T) { } client := NewClient(tr, logger) - defer client.Close() + defer func() { _ = client.Close() }() if err := client.Initialize(ctx); err != nil { t.Fatalf("Initialize: %v", err) @@ -180,16 +180,16 @@ while IFS= read -r line; do esac done ` - os.WriteFile(script, []byte(content), 0o755) + _ = os.WriteFile(script, []byte(content), 0o755) logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError})) tr := NewTransport("bash", []string{script}, nil, logger) ctx := context.Background() - tr.Start(ctx) + _ = tr.Start(ctx) client := NewClient(tr, logger) - defer client.Close() - client.Initialize(ctx) + defer func() { _ = client.Close() }() + _ = client.Initialize(ctx) a := NewAdapter("err", MCPTool{Name: "broken", InputSchema: json.RawMessage(`{}`)}, client) result, err := a.Execute(ctx, json.RawMessage(`{}`)) diff --git a/internal/mcp/transport_test.go b/internal/mcp/transport_test.go index 5efecbb..419599e 100644 --- a/internal/mcp/transport_test.go +++ b/internal/mcp/transport_test.go @@ -74,7 +74,7 @@ func TestTransport_Call_Success(t *testing.T) { if err := tr.Start(ctx); err != nil { t.Fatalf("Start: %v", err) } - defer tr.Close() + defer func() { _ = tr.Close() }() result, err := tr.Call(ctx, "tools/list", nil) if err != nil { @@ -101,7 +101,7 @@ func TestTransport_Call_RPCError(t *testing.T) { if err := tr.Start(ctx); err != nil { t.Fatalf("Start: %v", err) } - defer tr.Close() + defer func() { _ = tr.Close() }() _, err := tr.Call(ctx, "nonexistent", nil) if err == nil { @@ -131,7 +131,7 @@ func TestTransport_Call_Timeout(t *testing.T) { if err := tr.Start(ctx); err != nil { t.Fatalf("Start: %v", err) } - defer tr.Close() + defer func() { _ = tr.Close() }() ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) defer cancel() @@ -161,7 +161,7 @@ echo "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"val\":\"$TEST_MCP_VAR\"}}" if err := tr.Start(ctx); err != nil { t.Fatalf("Start: %v", err) } - defer tr.Close() + defer func() { _ = tr.Close() }() result, err := tr.Call(ctx, "test", nil) if err != nil { @@ -205,7 +205,7 @@ echo "$line" > "` + filepath.Join(dir, "received.json") + `" // Give the script a moment to write the file. time.Sleep(50 * time.Millisecond) - tr.Close() + _ = tr.Close() data, err := os.ReadFile(filepath.Join(dir, "received.json")) if err != nil { @@ -233,7 +233,7 @@ func TestTransport_MultipleCalls(t *testing.T) { if err := tr.Start(ctx); err != nil { t.Fatalf("Start: %v", err) } - defer tr.Close() + defer func() { _ = tr.Close() }() // First call. r1, err := tr.Call(ctx, "first", nil) @@ -242,7 +242,7 @@ func TestTransport_MultipleCalls(t *testing.T) { } var p1 struct{ Step string } - json.Unmarshal(r1, &p1) + _ = json.Unmarshal(r1, &p1) if p1.Step != "first" { t.Errorf("call 1 step = %q, want %q", p1.Step, "first") } @@ -254,7 +254,7 @@ func TestTransport_MultipleCalls(t *testing.T) { } var p2 struct{ Step string } - json.Unmarshal(r2, &p2) + _ = json.Unmarshal(r2, &p2) if p2.Step != "second" { t.Errorf("call 2 step = %q, want %q", p2.Step, "second") } diff --git a/internal/plugin/loader_test.go b/internal/plugin/loader_test.go index e413cd1..cfe5716 100644 --- a/internal/plugin/loader_test.go +++ b/internal/plugin/loader_test.go @@ -35,7 +35,7 @@ func writePluginWithSkill(t *testing.T, dir, pluginName, skillName, skillContent t.Helper() pluginDir := filepath.Join(dir, pluginName) skillsDir := filepath.Join(pluginDir, "skills") - os.MkdirAll(skillsDir, 0o755) + _ = os.MkdirAll(skillsDir, 0o755) m := Manifest{ Name: pluginName, @@ -45,8 +45,8 @@ func writePluginWithSkill(t *testing.T, dir, pluginName, skillName, skillContent }, } data, _ := marshalManifest(m) - os.WriteFile(filepath.Join(pluginDir, "plugin.json"), data, 0o644) - os.WriteFile(filepath.Join(skillsDir, skillName+".md"), []byte(skillContent), 0o644) + _ = os.WriteFile(filepath.Join(pluginDir, "plugin.json"), data, 0o644) + _ = os.WriteFile(filepath.Join(skillsDir, skillName+".md"), []byte(skillContent), 0o644) } func marshalManifest(m Manifest) ([]byte, error) { @@ -122,8 +122,8 @@ func TestLoader_Discover_SkipsInvalidManifest(t *testing.T) { // Write an invalid plugin (bad JSON). badDir := filepath.Join(globalDir, "bad") - os.MkdirAll(badDir, 0o755) - os.WriteFile(filepath.Join(badDir, "plugin.json"), []byte(`{invalid`), 0o644) + _ = os.MkdirAll(badDir, 0o755) + _ = os.WriteFile(filepath.Join(badDir, "plugin.json"), []byte(`{invalid`), 0o644) loader := NewLoader(testLogger()) plugins, err := loader.Discover(globalDir, filepath.Join(dir, "project")) @@ -274,8 +274,9 @@ func TestLoader_Load_TOFU_RecordsPinOnFirstLoad(t *testing.T) { if err != nil { t.Fatalf("Load: %v", err) } - if len(result.Skills)+len(result.Hooks)+len(result.MCPServers) != 0 { - // No capabilities declared, but plugin should still have been processed. + // Plugin declares no capabilities, but TOFU must still record a pin. + if n := len(result.Skills) + len(result.Hooks) + len(result.MCPServers); n != 0 { + t.Errorf("plugin with no capabilities produced %d entries", n) } if _, ok := pins.Get("newbie"); !ok { t.Error("TOFU did not record a pin for the new plugin") diff --git a/internal/plugin/manager_test.go b/internal/plugin/manager_test.go index c83cbe0..3f2d081 100644 --- a/internal/plugin/manager_test.go +++ b/internal/plugin/manager_test.go @@ -10,15 +10,15 @@ func TestManager_Install(t *testing.T) { dir := t.TempDir() globalDir := filepath.Join(dir, "global") projectDir := filepath.Join(dir, "project") - os.MkdirAll(globalDir, 0o755) - os.MkdirAll(projectDir, 0o755) + _ = os.MkdirAll(globalDir, 0o755) + _ = os.MkdirAll(projectDir, 0o755) // Create a source plugin directory. srcDir := filepath.Join(dir, "src", "my-plugin") - os.MkdirAll(srcDir, 0o755) + _ = os.MkdirAll(srcDir, 0o755) m := Manifest{Name: "my-plugin", Version: "1.0.0", Description: "Test plugin"} data, _ := marshalJSON(m) - os.WriteFile(filepath.Join(srcDir, "plugin.json"), data, 0o644) + _ = os.WriteFile(filepath.Join(srcDir, "plugin.json"), data, 0o644) mgr := NewManager(globalDir, projectDir, testLogger()) @@ -38,14 +38,14 @@ func TestManager_Install_ProjectScope(t *testing.T) { dir := t.TempDir() globalDir := filepath.Join(dir, "global") projectDir := filepath.Join(dir, "project") - os.MkdirAll(globalDir, 0o755) - os.MkdirAll(projectDir, 0o755) + _ = os.MkdirAll(globalDir, 0o755) + _ = os.MkdirAll(projectDir, 0o755) srcDir := filepath.Join(dir, "src", "proj-plugin") - os.MkdirAll(srcDir, 0o755) + _ = os.MkdirAll(srcDir, 0o755) m := Manifest{Name: "proj-plugin", Version: "1.0.0"} data, _ := marshalJSON(m) - os.WriteFile(filepath.Join(srcDir, "plugin.json"), data, 0o644) + _ = os.WriteFile(filepath.Join(srcDir, "plugin.json"), data, 0o644) mgr := NewManager(globalDir, projectDir, testLogger()) @@ -62,18 +62,18 @@ func TestManager_Install_ProjectScope(t *testing.T) { func TestManager_Install_AlreadyInstalled(t *testing.T) { dir := t.TempDir() globalDir := filepath.Join(dir, "global") - os.MkdirAll(globalDir, 0o755) + _ = os.MkdirAll(globalDir, 0o755) srcDir := filepath.Join(dir, "src", "dup") - os.MkdirAll(srcDir, 0o755) + _ = os.MkdirAll(srcDir, 0o755) m := Manifest{Name: "dup", Version: "1.0.0"} data, _ := marshalJSON(m) - os.WriteFile(filepath.Join(srcDir, "plugin.json"), data, 0o644) + _ = os.WriteFile(filepath.Join(srcDir, "plugin.json"), data, 0o644) mgr := NewManager(globalDir, filepath.Join(dir, "project"), testLogger()) // First install. - mgr.Install(srcDir, "user") + _ = mgr.Install(srcDir, "user") // Second install should fail. err := mgr.Install(srcDir, "user") @@ -85,10 +85,10 @@ func TestManager_Install_AlreadyInstalled(t *testing.T) { func TestManager_Install_NoManifest(t *testing.T) { dir := t.TempDir() globalDir := filepath.Join(dir, "global") - os.MkdirAll(globalDir, 0o755) + _ = os.MkdirAll(globalDir, 0o755) srcDir := filepath.Join(dir, "src", "empty") - os.MkdirAll(srcDir, 0o755) + _ = os.MkdirAll(srcDir, 0o755) mgr := NewManager(globalDir, filepath.Join(dir, "project"), testLogger()) err := mgr.Install(srcDir, "user") @@ -100,14 +100,14 @@ func TestManager_Install_NoManifest(t *testing.T) { func TestManager_Uninstall(t *testing.T) { dir := t.TempDir() globalDir := filepath.Join(dir, "global") - os.MkdirAll(globalDir, 0o755) + _ = os.MkdirAll(globalDir, 0o755) // Pre-install a plugin. pluginDir := filepath.Join(globalDir, "to-remove") - os.MkdirAll(pluginDir, 0o755) + _ = os.MkdirAll(pluginDir, 0o755) m := Manifest{Name: "to-remove", Version: "1.0.0"} data, _ := marshalJSON(m) - os.WriteFile(filepath.Join(pluginDir, "plugin.json"), data, 0o644) + _ = os.WriteFile(filepath.Join(pluginDir, "plugin.json"), data, 0o644) mgr := NewManager(globalDir, filepath.Join(dir, "project"), testLogger()) diff --git a/internal/provider/errors.go b/internal/provider/errors.go index b82c3d0..cf24768 100644 --- a/internal/provider/errors.go +++ b/internal/provider/errors.go @@ -83,18 +83,18 @@ func ClassifyHTTPError(status int, message string) (ErrorKind, bool) { // ClassifyHTTPStatus returns the ErrorKind and retryability for an HTTP status code. func ClassifyHTTPStatus(status int) (ErrorKind, bool) { - switch { - case status == 401 || status == 403: + switch status { + case 401, 403: return ErrAuth, false - case status == 400: + case 400: return ErrBadRequest, false - case status == 404: + case 404: return ErrNotFound, false - case status == 429 || status == 529: + case 429, 529: return ErrTransient, true - case status == 500 || status == 502 || status == 503: + case 500, 502, 503: return ErrTransient, true - case status == 504: + case 504: return ErrOverloaded, true default: if status >= 500 { diff --git a/internal/provider/google/provider.go b/internal/provider/google/provider.go index c90a6d0..10259d1 100644 --- a/internal/provider/google/provider.go +++ b/internal/provider/google/provider.go @@ -140,15 +140,14 @@ func inferGoogleModelCapabilities(m *genai.Model) provider.Capabilities { } // Model-specific overrides based on model name - name := m.Name - switch { - case name == "gemini-2.5-pro", name == "gemini-2.5-flash": + switch m.Name { + case "gemini-2.5-pro", "gemini-2.5-flash": caps.ContextWindow = 1048576 caps.MaxOutput = 65536 - case name == "gemini-2.0-pro", name == "gemini-2.0-flash": + case "gemini-2.0-pro", "gemini-2.0-flash": caps.ContextWindow = 1048576 caps.MaxOutput = 8192 - case name == "gemini-1.5-pro", name == "gemini-1.5-flash": + case "gemini-1.5-pro", "gemini-1.5-flash": caps.ContextWindow = 1048576 caps.MaxOutput = 8192 } diff --git a/internal/provider/subprocess/stream_test.go b/internal/provider/subprocess/stream_test.go index fb2ce66..14af424 100644 --- a/internal/provider/subprocess/stream_test.go +++ b/internal/provider/subprocess/stream_test.go @@ -25,7 +25,7 @@ func TestSubprocessStream_EchoShell(t *testing.T) { if err != nil { t.Fatal(err) } - defer s.Close() + defer func() { _ = s.Close() }() var texts []string for s.Next() { @@ -57,7 +57,7 @@ func TestSubprocessStream_ContextCancel(t *testing.T) { if err != nil { t.Fatal(err) } - defer s.Close() + defer func() { _ = s.Close() }() cancel() // Drain — should stop quickly due to context cancellation. @@ -76,7 +76,7 @@ func TestSubprocessStream_ProcessError(t *testing.T) { if err != nil { t.Fatal(err) } - defer s.Close() + defer func() { _ = s.Close() }() for s.Next() { } diff --git a/internal/router/discovery.go b/internal/router/discovery.go index b62b8ee..1f57c68 100644 --- a/internal/router/discovery.go +++ b/internal/router/discovery.go @@ -45,7 +45,7 @@ func DiscoverOllama(ctx context.Context, baseURL string, toolCache map[string]bo if err != nil { return nil, fmt.Errorf("ollama not reachable at %s: %w", baseURL, err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != 200 { return nil, fmt.Errorf("ollama returned %d", resp.StatusCode) @@ -115,7 +115,7 @@ func DiscoverLlamaCpp(ctx context.Context, baseURL string) ([]DiscoveredModel, e if err != nil { return nil, fmt.Errorf("llama.cpp not reachable at %s: %w", baseURL, err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != 200 { return nil, fmt.Errorf("llama.cpp returned %d", resp.StatusCode) diff --git a/internal/router/probe.go b/internal/router/probe.go index bca1492..85d5877 100644 --- a/internal/router/probe.go +++ b/internal/router/probe.go @@ -25,7 +25,7 @@ func probeLlamaCppToolSupport(ctx context.Context, baseURL string) bool { if err != nil { return false } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != 200 { return false @@ -73,7 +73,7 @@ func probeOllamaToolSupport(ctx context.Context, baseURL, modelName string) bool if err != nil { return false } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != 200 { return false diff --git a/internal/router/probe_test.go b/internal/router/probe_test.go index d175de7..94398a6 100644 --- a/internal/router/probe_test.go +++ b/internal/router/probe_test.go @@ -13,7 +13,7 @@ func TestProbeLlamaCppToolSupport_SupportsTools(t *testing.T) { t.Errorf("unexpected path %q", r.URL.Path) } w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{ + _, _ = w.Write([]byte(`{ "chat_template": "...", "chat_template_caps": { "supports_tools": true, @@ -34,7 +34,7 @@ func TestProbeLlamaCppToolSupport_SupportsTools(t *testing.T) { func TestProbeLlamaCppToolSupport_NoToolSupport(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{ + _, _ = w.Write([]byte(`{ "chat_template": "...", "chat_template_caps": { "supports_tools": false, @@ -55,7 +55,7 @@ func TestProbeLlamaCppToolSupport_NoCaps(t *testing.T) { // Old llama.cpp version that doesn't return chat_template_caps srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{"chat_template": "...", "total_slots": 1}`)) + _, _ = w.Write([]byte(`{"chat_template": "...", "total_slots": 1}`)) })) defer srv.Close() @@ -75,7 +75,7 @@ func TestProbeLlamaCppToolSupport_ServerDown(t *testing.T) { func TestProbeLlamaCppToolSupport_ToolsWithoutToolCalls(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{ + _, _ = w.Write([]byte(`{ "chat_template_caps": { "supports_tools": true, "supports_tool_calls": false @@ -96,7 +96,7 @@ func TestProbeOllamaToolSupport_HasTools(t *testing.T) { t.Errorf("unexpected %s %s", r.Method, r.URL.Path) } w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{ + _, _ = w.Write([]byte(`{ "details": {"family": "qwen2", "parameter_size": "7B"}, "capabilities": ["completion", "tools"] }`)) @@ -112,7 +112,7 @@ func TestProbeOllamaToolSupport_HasTools(t *testing.T) { func TestProbeOllamaToolSupport_NoTools(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{ + _, _ = w.Write([]byte(`{ "details": {"family": "phi", "parameter_size": "3B"}, "capabilities": ["completion"] }`)) @@ -129,7 +129,7 @@ func TestProbeOllamaToolSupport_NoCapsField(t *testing.T) { // Old Ollama version without capabilities srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{"details": {"family": "llama"}}`)) + _, _ = w.Write([]byte(`{"details": {"family": "llama"}}`)) })) defer srv.Close() diff --git a/internal/session/session_test.go b/internal/session/session_test.go index 90f0eab..1ba25de 100644 --- a/internal/session/session_test.go +++ b/internal/session/session_test.go @@ -124,7 +124,7 @@ func TestLocal_SendWhileBusy(t *testing.T) { eng, _ := engine.New(engine.Config{Provider: mp, Tools: tool.NewRegistry()}) sess := NewLocal(LocalConfig{Engine: eng, Provider: "test", Model: "model"}) - sess.Send("first") + _ = sess.Send("first") // Try to send while still processing err := sess.Send("second") @@ -151,7 +151,7 @@ func TestLocal_Cancel(t *testing.T) { eng, _ := engine.New(engine.Config{Provider: mp, Tools: tool.NewRegistry()}) sess := NewLocal(LocalConfig{Engine: eng, Provider: "test", Model: "model"}) - sess.Send("slow task") + _ = sess.Send("slow task") // Read a few events then cancel evts := sess.Events() @@ -203,12 +203,12 @@ func TestLocal_StatusTracking(t *testing.T) { sess := NewLocal(LocalConfig{Engine: eng, Provider: "test", Model: "mock-model"}) // Turn 1 - sess.Send("one") + _ = sess.Send("one") for range sess.Events() { } // Turn 2 - sess.Send("two") + _ = sess.Send("two") for range sess.Events() { } diff --git a/internal/session/store_test.go b/internal/session/store_test.go index 5dada2b..c237ef1 100644 --- a/internal/session/store_test.go +++ b/internal/session/store_test.go @@ -73,9 +73,9 @@ func TestSessionStore_Load_CorruptMetadata(t *testing.T) { store := session.NewSessionStore(root, 3, slog.Default()) dir := filepath.Join(root, ".gnoma", "sessions", "corrupt-sess") - os.MkdirAll(dir, 0o755) - os.WriteFile(filepath.Join(dir, "metadata.json"), []byte("not json"), 0o644) - os.WriteFile(filepath.Join(dir, "messages.json"), []byte("[]"), 0o644) + _ = os.MkdirAll(dir, 0o755) + _ = os.WriteFile(filepath.Join(dir, "metadata.json"), []byte("not json"), 0o644) + _ = os.WriteFile(filepath.Join(dir, "messages.json"), []byte("[]"), 0o644) _, err := store.Load("corrupt-sess") if err == nil { @@ -87,9 +87,9 @@ func TestSessionStore_List_SortedByUpdatedAt(t *testing.T) { store := makeStore(t) now := time.Now().UTC() - store.Save(makeSnap("sess-old", now.Add(-2*time.Hour))) - store.Save(makeSnap("sess-new", now)) - store.Save(makeSnap("sess-mid", now.Add(-1*time.Hour))) + _ = store.Save(makeSnap("sess-old", now.Add(-2*time.Hour))) + _ = store.Save(makeSnap("sess-new", now)) + _ = store.Save(makeSnap("sess-mid", now.Add(-1*time.Hour))) list, err := store.List() if err != nil { @@ -131,7 +131,7 @@ func TestSessionStore_Prune_RemovesOldest(t *testing.T) { for i := 0; i < 5; i++ { id := fmt.Sprintf("sess-%03d", i) - store.Save(makeSnap(id, now.Add(time.Duration(i)*time.Minute))) + _ = store.Save(makeSnap(id, now.Add(time.Duration(i)*time.Minute))) } list, err := store.List() diff --git a/internal/skill/registry_test.go b/internal/skill/registry_test.go index 749dfd4..4722852 100644 --- a/internal/skill/registry_test.go +++ b/internal/skill/registry_test.go @@ -61,8 +61,8 @@ func TestRegistry_OverridePrecedence(t *testing.T) { writeSkillFile(t, dir2, "shared.md", "---\nname: shared\ndescription: from dir2\n---\nbody2\n") reg := NewRegistry() - reg.LoadDir(dir1, "user") - reg.LoadDir(dir2, "project") + _ = reg.LoadDir(dir1, "user") + _ = reg.LoadDir(dir2, "project") sk := reg.Get("shared") if sk == nil { @@ -90,7 +90,7 @@ func TestRegistry_Names_Sorted(t *testing.T) { writeSkillFile(t, dir, "middle.md", "---\nname: middle\n---\nbody\n") reg := NewRegistry() - reg.LoadDir(dir, "test") + _ = reg.LoadDir(dir, "test") names := reg.Names() if len(names) != 3 { @@ -116,7 +116,7 @@ func TestRegistry_All_ReturnsCopy(t *testing.T) { writeSkillFile(t, dir, "a.md", "---\nname: aaa\n---\nbody\n") reg := NewRegistry() - reg.LoadDir(dir, "test") + _ = reg.LoadDir(dir, "test") all := reg.All() if len(all) != 1 { diff --git a/internal/slm/download_test.go b/internal/slm/download_test.go index 3e0cf86..8da6cd6 100644 --- a/internal/slm/download_test.go +++ b/internal/slm/download_test.go @@ -14,7 +14,7 @@ import ( func TestDownload_Success(t *testing.T) { content := []byte("hello llamafile") srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Write(content) + _, _ = w.Write(content) })) defer srv.Close() @@ -94,7 +94,7 @@ func TestDownload_ContextCancel(t *testing.T) { func TestDownload_NilProgress(t *testing.T) { content := []byte("data") srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Write(content) + _, _ = w.Write(content) })) defer srv.Close() @@ -111,8 +111,8 @@ func TestHashFile(t *testing.T) { if err != nil { t.Fatal(err) } - f.Write(content) - f.Close() + _, _ = f.Write(content) + _ = f.Close() h := sha256.Sum256(content) want := hex.EncodeToString(h[:]) diff --git a/internal/slm/manager_test.go b/internal/slm/manager_test.go index c35e74c..6ebd06f 100644 --- a/internal/slm/manager_test.go +++ b/internal/slm/manager_test.go @@ -76,7 +76,7 @@ func TestManager_StatusMissing(t *testing.T) { func TestManager_Setup(t *testing.T) { content := []byte("fake llamafile binary") srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Write(content) + _, _ = w.Write(content) })) defer srv.Close() diff --git a/internal/tool/agent/coordinator_test.go b/internal/tool/agent/coordinator_test.go index b2ef196..8b00e1c 100644 --- a/internal/tool/agent/coordinator_test.go +++ b/internal/tool/agent/coordinator_test.go @@ -14,7 +14,7 @@ import ( func makeTestStore(t *testing.T) *persist.Store { t.Helper() s := persist.New("test-coord-" + t.Name()) - t.Cleanup(func() { os.RemoveAll(s.Dir()) }) + t.Cleanup(func() { _ = os.RemoveAll(s.Dir()) }) return s } @@ -26,10 +26,8 @@ func TestListResultsTool_EmptyStore(t *testing.T) { if err != nil { t.Fatal(err) } - if !strings.Contains(result.Output, "no results") && result.Output != "" { - // both "no results" message and empty string are acceptable - } - // Verify it doesn't error on empty store + // Empty store: either a "no results" message or empty output is acceptable; + // verify only that we don't surface a hard error. if strings.Contains(result.Output, "error") { t.Errorf("unexpected error output for empty store: %s", result.Output) } diff --git a/internal/tool/fs/fs_test.go b/internal/tool/fs/fs_test.go index fe25635..83d3ecb 100644 --- a/internal/tool/fs/fs_test.go +++ b/internal/tool/fs/fs_test.go @@ -321,9 +321,9 @@ func TestGlobTool_Interface(t *testing.T) { func TestGlobTool_MatchFiles(t *testing.T) { dir := t.TempDir() - os.WriteFile(filepath.Join(dir, "main.go"), []byte("package main"), 0o644) - os.WriteFile(filepath.Join(dir, "test.go"), []byte("package main"), 0o644) - os.WriteFile(filepath.Join(dir, "readme.md"), []byte("# readme"), 0o644) + _ = os.WriteFile(filepath.Join(dir, "main.go"), []byte("package main"), 0o644) + _ = os.WriteFile(filepath.Join(dir, "test.go"), []byte("package main"), 0o644) + _ = os.WriteFile(filepath.Join(dir, "readme.md"), []byte("# readme"), 0o644) g := NewGlobTool() result, err := g.Execute(context.Background(), mustJSON(t, globArgs{Pattern: "*.go", Path: dir})) @@ -357,12 +357,12 @@ func TestGlobTool_NoMatches(t *testing.T) { func TestGlobTool_Doublestar(t *testing.T) { dir := t.TempDir() - os.MkdirAll(filepath.Join(dir, "internal", "foo"), 0o755) - os.MkdirAll(filepath.Join(dir, "cmd", "bar"), 0o755) - os.WriteFile(filepath.Join(dir, "main.go"), []byte(""), 0o644) - os.WriteFile(filepath.Join(dir, "internal", "foo", "foo.go"), []byte(""), 0o644) - os.WriteFile(filepath.Join(dir, "cmd", "bar", "bar.go"), []byte(""), 0o644) - os.WriteFile(filepath.Join(dir, "cmd", "bar", "bar_test.go"), []byte(""), 0o644) + _ = os.MkdirAll(filepath.Join(dir, "internal", "foo"), 0o755) + _ = os.MkdirAll(filepath.Join(dir, "cmd", "bar"), 0o755) + _ = os.WriteFile(filepath.Join(dir, "main.go"), []byte(""), 0o644) + _ = os.WriteFile(filepath.Join(dir, "internal", "foo", "foo.go"), []byte(""), 0o644) + _ = os.WriteFile(filepath.Join(dir, "cmd", "bar", "bar.go"), []byte(""), 0o644) + _ = os.WriteFile(filepath.Join(dir, "cmd", "bar", "bar_test.go"), []byte(""), 0o644) g := NewGlobTool() @@ -441,9 +441,9 @@ func TestGrepTool_SingleFile(t *testing.T) { func TestGrepTool_Directory(t *testing.T) { dir := t.TempDir() - os.WriteFile(filepath.Join(dir, "a.go"), []byte("func main() {}\nfunc helper() {}"), 0o644) - os.WriteFile(filepath.Join(dir, "b.go"), []byte("func test() {}"), 0o644) - os.WriteFile(filepath.Join(dir, "c.txt"), []byte("func ignored() {}"), 0o644) + _ = os.WriteFile(filepath.Join(dir, "a.go"), []byte("func main() {}\nfunc helper() {}"), 0o644) + _ = os.WriteFile(filepath.Join(dir, "b.go"), []byte("func test() {}"), 0o644) + _ = os.WriteFile(filepath.Join(dir, "c.txt"), []byte("func ignored() {}"), 0o644) g := NewGrepTool() @@ -537,9 +537,9 @@ func TestLSTool_Interface(t *testing.T) { func TestLSTool_ListDirectory(t *testing.T) { dir := t.TempDir() - os.WriteFile(filepath.Join(dir, "hello.go"), []byte("package main"), 0o644) - os.WriteFile(filepath.Join(dir, "readme.md"), []byte("# readme"), 0o644) - os.MkdirAll(filepath.Join(dir, "subdir"), 0o755) + _ = os.WriteFile(filepath.Join(dir, "hello.go"), []byte("package main"), 0o644) + _ = os.WriteFile(filepath.Join(dir, "readme.md"), []byte("# readme"), 0o644) + _ = os.MkdirAll(filepath.Join(dir, "subdir"), 0o755) l := NewLSTool() result, err := l.Execute(context.Background(), mustJSON(t, lsArgs{Path: dir})) @@ -591,7 +591,7 @@ func TestLSTool_DirectoryNotFound(t *testing.T) { func TestLSTool_ShowsSizes(t *testing.T) { dir := t.TempDir() - os.WriteFile(filepath.Join(dir, "small.txt"), []byte("hi"), 0o644) + _ = os.WriteFile(filepath.Join(dir, "small.txt"), []byte("hi"), 0o644) l := NewLSTool() result, err := l.Execute(context.Background(), mustJSON(t, lsArgs{Path: dir})) diff --git a/internal/tool/persist/store_test.go b/internal/tool/persist/store_test.go index 702ab56..87cf56f 100644 --- a/internal/tool/persist/store_test.go +++ b/internal/tool/persist/store_test.go @@ -11,7 +11,7 @@ import ( func TestStore_SaveSkipsSmallContent(t *testing.T) { s := persist.New("test-session-001") - t.Cleanup(func() { os.RemoveAll(s.Dir()) }) + t.Cleanup(func() { _ = os.RemoveAll(s.Dir()) }) path, ok := s.Save("bash", "call-001", "small output") if ok { @@ -24,7 +24,7 @@ func TestStore_SaveSkipsSmallContent(t *testing.T) { func TestStore_SavePersistsLargeContent(t *testing.T) { s := persist.New("test-session-002") - t.Cleanup(func() { os.RemoveAll(s.Dir()) }) + t.Cleanup(func() { _ = os.RemoveAll(s.Dir()) }) content := strings.Repeat("x", 1024) path, ok := s.Save("fs.grep", "call-002", content) @@ -45,7 +45,7 @@ func TestStore_SavePersistsLargeContent(t *testing.T) { func TestStore_ListFilters(t *testing.T) { s := persist.New("test-session-003") - t.Cleanup(func() { os.RemoveAll(s.Dir()) }) + t.Cleanup(func() { _ = os.RemoveAll(s.Dir()) }) bigContent := strings.Repeat("y", 1024) s.Save("bash", "c1", bigContent) @@ -71,7 +71,7 @@ func TestStore_ListFilters(t *testing.T) { func TestStore_ReadValidatesPath(t *testing.T) { s := persist.New("test-session-004") - t.Cleanup(func() { os.RemoveAll(s.Dir()) }) + t.Cleanup(func() { _ = os.RemoveAll(s.Dir()) }) // Path outside session dir must be rejected _, err := s.Read("/etc/passwd") diff --git a/internal/tool/registry_test.go b/internal/tool/registry_test.go index 89b3c5a..474a242 100644 --- a/internal/tool/registry_test.go +++ b/internal/tool/registry_test.go @@ -162,7 +162,7 @@ func TestStubTool_Execute(t *testing.T) { execFn: func(ctx context.Context, args json.RawMessage) (Result, error) { called = true var input struct{ Value string } - json.Unmarshal(args, &input) + _ = json.Unmarshal(args, &input) return Result{ Output: "processed: " + input.Value, Metadata: map[string]any{"key": "val"}, diff --git a/internal/tui/app.go b/internal/tui/app.go index ab3fbc5..ca2db33 100644 --- a/internal/tui/app.go +++ b/internal/tui/app.go @@ -575,9 +575,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case tea.MouseWheelMsg: - if msg.Button == tea.MouseWheelUp { + switch msg.Button { + case tea.MouseWheelUp: m.scrollOffset += 3 - } else if msg.Button == tea.MouseWheelDown { + case tea.MouseWheelDown: m.scrollOffset -= 3 if m.scrollOffset < 0 { m.scrollOffset = 0 @@ -847,7 +848,8 @@ Mark anything you're unsure about with TODO. Be terse — directive-style bullet func (m Model) submitInput(input string) (tea.Model, tea.Cmd) { // Prepend mode prefix and reset mode before dispatching. - if m.inputMode == "command" { + switch m.inputMode { + case "command": if strings.TrimSpace(input) == "" { m.inputMode = "" m.suggestions = nil @@ -860,7 +862,7 @@ func (m Model) submitInput(input string) (tea.Model, tea.Cmd) { return m, nil } input = "/" + strings.TrimSpace(input) - } else if m.inputMode == "execute" { + case "execute": if strings.TrimSpace(input) == "" { m.inputMode = "" m.input.SetPromptFunc(2, func(info textarea.PromptInfo) string {