chore(lint): clear remaining errcheck and staticcheck findings

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).
This commit is contained in:
2026-05-19 17:53:42 +02:00
parent 397a39250c
commit ec9433d783
33 changed files with 194 additions and 197 deletions
+6 -6
View File
@@ -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
+36 -36
View File
@@ -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 {
+1 -1
View File
@@ -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 {
+3 -3
View File
@@ -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 {
+5 -5
View File
@@ -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)
+6 -6
View File
@@ -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 {
+5 -4
View File
@@ -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()
+2 -2
View File
@@ -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")
+1 -1
View File
@@ -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)
}
+3 -7
View File
@@ -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)
}
+1 -1
View File
@@ -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")
+7 -7
View File
@@ -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 {
+4 -4
View File
@@ -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 {
+6 -6
View File
@@ -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(`{}`))
+8 -8
View File
@@ -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")
}
+8 -7
View File
@@ -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")
+17 -17
View File
@@ -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())
+7 -7
View File
@@ -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 {
+4 -5
View File
@@ -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
}
+3 -3
View File
@@ -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() {
}
+2 -2
View File
@@ -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)
+2 -2
View File
@@ -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
+7 -7
View File
@@ -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()
+4 -4
View File
@@ -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() {
}
+7 -7
View File
@@ -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()
+4 -4
View File
@@ -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 {
+4 -4
View File
@@ -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[:])
+1 -1
View File
@@ -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()
+3 -5
View File
@@ -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)
}
+16 -16
View File
@@ -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}))
+4 -4
View File
@@ -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")
+1 -1
View File
@@ -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"},
+6 -4
View File
@@ -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 {