diff --git a/pkg/agent/loop_test.go b/pkg/agent/loop_test.go index 4414398b1..aa9f823b7 100644 --- a/pkg/agent/loop_test.go +++ b/pkg/agent/loop_test.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "slices" "testing" "time" @@ -175,13 +176,7 @@ func TestToolRegistry_ToolRegistration(t *testing.T) { toolsList := toolsInfo["names"].([]string) // Check that our custom tool name is in the list - found := false - for _, name := range toolsList { - if name == "mock_custom" { - found = true - break - } - } + found := slices.Contains(toolsList, "mock_custom") if !found { t.Error("Expected custom tool to be registered") } @@ -250,13 +245,7 @@ func TestToolRegistry_GetDefinitions(t *testing.T) { toolsList := toolsInfo["names"].([]string) // Check that our custom tool name is in the list - found := false - for _, name := range toolsList { - if name == "mock_custom" { - found = true - break - } - } + found := slices.Contains(toolsList, "mock_custom") if !found { t.Error("Expected custom tool to be registered") } diff --git a/pkg/agent/memory.go b/pkg/agent/memory.go index dd5f4441c..939e64b5b 100644 --- a/pkg/agent/memory.go +++ b/pkg/agent/memory.go @@ -104,7 +104,7 @@ func (ms *MemoryStore) GetRecentDailyNotes(days int) string { var sb strings.Builder first := true - for i := 0; i < days; i++ { + for i := range days { date := time.Now().AddDate(0, 0, -i) dateStr := date.Format("20060102") // YYYYMMDD monthDir := dateStr[:6] // YYYYMM diff --git a/pkg/channels/wecom.go b/pkg/channels/wecom.go index f8daf89de..e24157f5c 100644 --- a/pkg/channels/wecom.go +++ b/pkg/channels/wecom.go @@ -596,7 +596,7 @@ func pkcs7UnpadWeCom(data []byte) ([]byte, error) { return nil, fmt.Errorf("padding size larger than data") } // Verify all padding bytes - for i := 0; i < padding; i++ { + for i := range padding { if data[len(data)-1-i] != byte(padding) { return nil, fmt.Errorf("invalid padding byte at position %d", i) } diff --git a/pkg/channels/wecom_app_test.go b/pkg/channels/wecom_app_test.go index abf15c52b..ba911d49f 100644 --- a/pkg/channels/wecom_app_test.go +++ b/pkg/channels/wecom_app_test.go @@ -46,7 +46,7 @@ func encryptTestMessageApp(message, aesKey string) (string, error) { // Prepare message: random(16) + msg_len(4) + msg + corp_id random := make([]byte, 0, 16) - for i := 0; i < 16; i++ { + for i := range 16 { random = append(random, byte(i+1)) } diff --git a/pkg/channels/wecom_test.go b/pkg/channels/wecom_test.go index 8afa7e8c3..88aed8d2b 100644 --- a/pkg/channels/wecom_test.go +++ b/pkg/channels/wecom_test.go @@ -45,7 +45,7 @@ func encryptTestMessage(message, aesKey string) (string, error) { // Prepare message: random(16) + msg_len(4) + msg + receiveid random := make([]byte, 0, 16) - for i := 0; i < 16; i++ { + for i := range 16 { random = append(random, byte(i)) } diff --git a/pkg/config/model_config_test.go b/pkg/config/model_config_test.go index 3c411dc0f..44bbb2fdb 100644 --- a/pkg/config/model_config_test.go +++ b/pkg/config/model_config_test.go @@ -63,7 +63,7 @@ func TestGetModelConfig_RoundRobin(t *testing.T) { // Test round-robin distribution results := make(map[string]int) - for i := 0; i < 30; i++ { + for range 30 { result, err := cfg.GetModelConfig("lb-model") if err != nil { t.Fatalf("GetModelConfig() error = %v", err) @@ -93,17 +93,15 @@ func TestGetModelConfig_Concurrent(t *testing.T) { var wg sync.WaitGroup errors := make(chan error, goroutines*iterations) - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < iterations; j++ { + for range goroutines { + wg.Go(func() { + for range iterations { _, err := cfg.GetModelConfig("concurrent-model") if err != nil { errors <- err } } - }() + }) } wg.Wait() diff --git a/pkg/health/server.go b/pkg/health/server.go index 77b36034d..d1acfb662 100644 --- a/pkg/health/server.go +++ b/pkg/health/server.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "maps" "net/http" "sync" "time" @@ -122,9 +123,7 @@ func (s *Server) readyHandler(w http.ResponseWriter, r *http.Request) { s.mu.RLock() ready := s.ready checks := make(map[string]Check) - for k, v := range s.checks { - checks[k] = v - } + maps.Copy(checks, s.checks) s.mu.RUnlock() if !ready { diff --git a/pkg/providers/anthropic/provider.go b/pkg/providers/anthropic/provider.go index 35f6b8f62..2d5176550 100644 --- a/pkg/providers/anthropic/provider.go +++ b/pkg/providers/anthropic/provider.go @@ -199,14 +199,14 @@ func translateTools(tools []ToolDefinition) []anthropic.ToolUnionParam { } func parseResponse(resp *anthropic.Message) *LLMResponse { - var content string + var content strings.Builder var toolCalls []ToolCall for _, block := range resp.Content { switch block.Type { case "text": tb := block.AsText() - content += tb.Text + content.WriteString(tb.Text) case "tool_use": tu := block.AsToolUse() var args map[string]any @@ -233,7 +233,7 @@ func parseResponse(resp *anthropic.Message) *LLMResponse { } return &LLMResponse{ - Content: content, + Content: content.String(), ToolCalls: toolCalls, FinishReason: finishReason, Usage: &UsageInfo{ @@ -251,8 +251,8 @@ func normalizeBaseURL(apiBase string) string { } base = strings.TrimRight(base, "/") - if strings.HasSuffix(base, "/v1") { - base = strings.TrimSuffix(base, "/v1") + if before, ok := strings.CutSuffix(base, "/v1"); ok { + base = before } if base == "" { return defaultBaseURL diff --git a/pkg/providers/codex_provider.go b/pkg/providers/codex_provider.go index ecc983642..645cf46d1 100644 --- a/pkg/providers/codex_provider.go +++ b/pkg/providers/codex_provider.go @@ -163,8 +163,8 @@ func resolveCodexModel(model string) (string, string) { return codexDefaultModel, "empty model" } - if strings.HasPrefix(m, "openai/") { - m = strings.TrimPrefix(m, "openai/") + if after, ok := strings.CutPrefix(m, "openai/"); ok { + m = after } else if strings.Contains(m, "/") { return codexDefaultModel, "non-openai model namespace" } diff --git a/pkg/providers/cooldown_test.go b/pkg/providers/cooldown_test.go index 47f43ad5c..b517e7feb 100644 --- a/pkg/providers/cooldown_test.go +++ b/pkg/providers/cooldown_test.go @@ -138,7 +138,7 @@ func TestCooldown_FailureWindowReset(t *testing.T) { ct, current := newTestTracker(now) // 4 errors → 1h cooldown - for i := 0; i < 4; i++ { + for range 4 { ct.MarkFailure("openai", FailoverRateLimit) *current = current.Add(2 * time.Second) // small advance between errors } @@ -230,7 +230,7 @@ func TestCooldown_ConcurrentAccess(t *testing.T) { ct := NewCooldownTracker() var wg sync.WaitGroup - for i := 0; i < 100; i++ { + for range 100 { wg.Add(3) go func() { defer wg.Done() diff --git a/pkg/providers/openai_compat/provider.go b/pkg/providers/openai_compat/provider.go index b8528953a..d6a27c07c 100644 --- a/pkg/providers/openai_compat/provider.go +++ b/pkg/providers/openai_compat/provider.go @@ -229,8 +229,8 @@ func parseResponse(body []byte) (*LLMResponse, error) { } func normalizeModel(model, apiBase string) string { - idx := strings.Index(model, "/") - if idx == -1 { + before, after, ok := strings.Cut(model, "/") + if !ok { return model } @@ -238,10 +238,10 @@ func normalizeModel(model, apiBase string) string { return model } - prefix := strings.ToLower(model[:idx]) + prefix := strings.ToLower(before) switch prefix { case "moonshot", "nvidia", "groq", "ollama", "deepseek", "google", "openrouter", "zhipu": - return model[idx+1:] + return after default: return model } diff --git a/pkg/routing/agent_id_test.go b/pkg/routing/agent_id_test.go index 050fe0645..f9a65c969 100644 --- a/pkg/routing/agent_id_test.go +++ b/pkg/routing/agent_id_test.go @@ -1,6 +1,9 @@ package routing -import "testing" +import ( + "strings" + "testing" +) func TestNormalizeAgentID_Empty(t *testing.T) { if got := NormalizeAgentID(""); got != DefaultAgentID { @@ -57,11 +60,11 @@ func TestNormalizeAgentID_AllInvalid(t *testing.T) { } func TestNormalizeAgentID_TruncatesAt64(t *testing.T) { - long := "" - for i := 0; i < 100; i++ { - long += "a" + var long strings.Builder + for range 100 { + long.WriteString("a") } - got := NormalizeAgentID(long) + got := NormalizeAgentID(long.String()) if len(got) > MaxAgentIDLength { t.Errorf("length = %d, want <= %d", len(got), MaxAgentIDLength) } diff --git a/pkg/skills/loader.go b/pkg/skills/loader.go index eb0d5f322..abd084fe3 100644 --- a/pkg/skills/loader.go +++ b/pkg/skills/loader.go @@ -298,7 +298,7 @@ func (sl *SkillsLoader) parseSimpleYAML(content string) map[string]string { normalized := strings.ReplaceAll(content, "\r\n", "\n") normalized = strings.ReplaceAll(normalized, "\r", "\n") - for _, line := range strings.Split(normalized, "\n") { + for line := range strings.SplitSeq(normalized, "\n") { line = strings.TrimSpace(line) if line == "" || strings.HasPrefix(line, "#") { continue diff --git a/pkg/skills/search_cache.go b/pkg/skills/search_cache.go index 5d7d2797e..1686e3f98 100644 --- a/pkg/skills/search_cache.go +++ b/pkg/skills/search_cache.go @@ -1,7 +1,7 @@ package skills import ( - "sort" + "slices" "strings" "sync" "time" @@ -183,7 +183,7 @@ func buildTrigrams(s string) []uint32 { } // Sort and Deduplication - sort.Slice(trigrams, func(i, j int) bool { return trigrams[i] < trigrams[j] }) + slices.Sort(trigrams) n := 1 for i := 1; i < len(trigrams); i++ { if trigrams[i] != trigrams[i-1] { diff --git a/pkg/skills/search_cache_test.go b/pkg/skills/search_cache_test.go index 816bdfb93..6bbb0e6eb 100644 --- a/pkg/skills/search_cache_test.go +++ b/pkg/skills/search_cache_test.go @@ -153,7 +153,7 @@ func TestSearchCacheConcurrency(t *testing.T) { // Concurrent writes go func() { - for i := 0; i < 100; i++ { + for i := range 100 { cache.Put("query-write-"+string(rune('a'+i%26)), []SearchResult{{Slug: "x"}}) } done <- struct{}{} @@ -161,7 +161,7 @@ func TestSearchCacheConcurrency(t *testing.T) { // Concurrent reads go func() { - for i := 0; i < 100; i++ { + for range 100 { cache.Get("query-write-a") } done <- struct{}{} diff --git a/pkg/state/state_test.go b/pkg/state/state_test.go index f717a5bb4..70117ad61 100644 --- a/pkg/state/state_test.go +++ b/pkg/state/state_test.go @@ -135,7 +135,7 @@ func TestConcurrentAccess(t *testing.T) { // Test concurrent writes done := make(chan bool, 10) - for i := 0; i < 10; i++ { + for i := range 10 { go func(idx int) { channel := fmt.Sprintf("channel-%d", idx) sm.SetLastChannel(channel) @@ -144,7 +144,7 @@ func TestConcurrentAccess(t *testing.T) { } // Wait for all goroutines to complete - for i := 0; i < 10; i++ { + for range 10 { <-done } diff --git a/pkg/tools/cron.go b/pkg/tools/cron.go index 562fffc84..3140e5e25 100644 --- a/pkg/tools/cron.go +++ b/pkg/tools/cron.go @@ -3,6 +3,7 @@ package tools import ( "context" "fmt" + "strings" "sync" "time" @@ -218,7 +219,8 @@ func (t *CronTool) listJobs() *ToolResult { return SilentResult("No scheduled jobs") } - result := "Scheduled jobs:\n" + var result strings.Builder + result.WriteString("Scheduled jobs:\n") for _, j := range jobs { var scheduleInfo string if j.Schedule.Kind == "every" && j.Schedule.EveryMS != nil { @@ -230,10 +232,10 @@ func (t *CronTool) listJobs() *ToolResult { } else { scheduleInfo = "unknown" } - result += fmt.Sprintf("- %s (id: %s, %s)\n", j.Name, j.ID, scheduleInfo) + result.WriteString(fmt.Sprintf("- %s (id: %s, %s)\n", j.Name, j.ID, scheduleInfo)) } - return SilentResult(result) + return SilentResult(result.String()) } func (t *CronTool) removeJob(args map[string]any) *ToolResult { diff --git a/pkg/tools/filesystem.go b/pkg/tools/filesystem.go index 1bf50906e..fe4133b72 100644 --- a/pkg/tools/filesystem.go +++ b/pkg/tools/filesystem.go @@ -236,14 +236,14 @@ func (t *ListDirTool) Execute(ctx context.Context, args map[string]any) *ToolRes return ErrorResult(fmt.Sprintf("failed to read directory: %v", err)) } - result := "" + var result strings.Builder for _, entry := range entries { if entry.IsDir() { - result += "DIR: " + entry.Name() + "\n" + result.WriteString("DIR: " + entry.Name() + "\n") } else { - result += "FILE: " + entry.Name() + "\n" + result.WriteString("FILE: " + entry.Name() + "\n") } } - return NewToolResult(result) + return NewToolResult(result.String()) } diff --git a/pkg/tools/shell_test.go b/pkg/tools/shell_test.go index 60f2b7b91..6d35815e8 100644 --- a/pkg/tools/shell_test.go +++ b/pkg/tools/shell_test.go @@ -191,15 +191,15 @@ func TestShellTool_WorkingDir_OutsideWorkspace(t *testing.T) { root := t.TempDir() workspace := filepath.Join(root, "workspace") outsideDir := filepath.Join(root, "outside") - if err := os.MkdirAll(workspace, 0755); err != nil { + if err := os.MkdirAll(workspace, 0o755); err != nil { t.Fatalf("failed to create workspace: %v", err) } - if err := os.MkdirAll(outsideDir, 0755); err != nil { + if err := os.MkdirAll(outsideDir, 0o755); err != nil { t.Fatalf("failed to create outside dir: %v", err) } tool := NewExecTool(workspace, true) - result := tool.Execute(context.Background(), map[string]interface{}{ + result := tool.Execute(context.Background(), map[string]any{ "command": "pwd", "working_dir": outsideDir, }) @@ -218,13 +218,13 @@ func TestShellTool_WorkingDir_SymlinkEscape(t *testing.T) { root := t.TempDir() workspace := filepath.Join(root, "workspace") secretDir := filepath.Join(root, "secret") - if err := os.MkdirAll(workspace, 0755); err != nil { + if err := os.MkdirAll(workspace, 0o755); err != nil { t.Fatalf("failed to create workspace: %v", err) } - if err := os.MkdirAll(secretDir, 0755); err != nil { + if err := os.MkdirAll(secretDir, 0o755); err != nil { t.Fatalf("failed to create secret dir: %v", err) } - os.WriteFile(filepath.Join(secretDir, "secret.txt"), []byte("top secret"), 0644) + os.WriteFile(filepath.Join(secretDir, "secret.txt"), []byte("top secret"), 0o644) // symlink lives inside the workspace but resolves to secretDir outside it link := filepath.Join(workspace, "escape") @@ -233,7 +233,7 @@ func TestShellTool_WorkingDir_SymlinkEscape(t *testing.T) { } tool := NewExecTool(workspace, true) - result := tool.Execute(context.Background(), map[string]interface{}{ + result := tool.Execute(context.Background(), map[string]any{ "command": "cat secret.txt", "working_dir": link, }) diff --git a/pkg/tools/web.go b/pkg/tools/web.go index 301e00daf..66f26d383 100644 --- a/pkg/tools/web.go +++ b/pkg/tools/web.go @@ -141,7 +141,7 @@ func (p *DuckDuckGoSearchProvider) extractResults(html string, count int, query maxItems := min(len(matches), count) - for i := 0; i < maxItems; i++ { + for i := range maxItems { urlStr := matches[i][1] title := stripTags(matches[i][2]) title = strings.TrimSpace(title) @@ -149,9 +149,9 @@ func (p *DuckDuckGoSearchProvider) extractResults(html string, count int, query // URL decoding if needed if strings.Contains(urlStr, "uddg=") { if u, err := url.QueryUnescape(urlStr); err == nil { - idx := strings.Index(u, "uddg=") - if idx != -1 { - urlStr = u[idx+5:] + _, after, ok := strings.Cut(u, "uddg=") + if ok { + urlStr = after } } } diff --git a/pkg/utils/message.go b/pkg/utils/message.go index 1d05950d9..a65506edc 100644 --- a/pkg/utils/message.go +++ b/pkg/utils/message.go @@ -13,10 +13,7 @@ func SplitMessage(content string, maxLen int) []string { var messages []string // Dynamic buffer: 10% of maxLen, but at least 50 chars if possible - codeBlockBuffer := maxLen / 10 - if codeBlockBuffer < 50 { - codeBlockBuffer = 50 - } + codeBlockBuffer := max(maxLen/10, 50) if codeBlockBuffer > maxLen/2 { codeBlockBuffer = maxLen / 2 } @@ -28,10 +25,7 @@ func SplitMessage(content string, maxLen int) []string { } // Effective split point: maxLen minus buffer, to leave room for code blocks - effectiveLimit := maxLen - codeBlockBuffer - if effectiveLimit < maxLen/2 { - effectiveLimit = maxLen / 2 - } + effectiveLimit := max(maxLen-codeBlockBuffer, maxLen/2) // Find natural split point within the effective limit msgEnd := findLastNewline(content[:effectiveLimit], 200) @@ -151,10 +145,7 @@ func findNextClosingCodeBlock(text string, startIdx int) int { // findLastNewline finds the last newline character within the last N characters // Returns the position of the newline or -1 if not found func findLastNewline(s string, searchWindow int) int { - searchStart := len(s) - searchWindow - if searchStart < 0 { - searchStart = 0 - } + searchStart := max(len(s)-searchWindow, 0) for i := len(s) - 1; i >= searchStart; i-- { if s[i] == '\n' { return i @@ -166,10 +157,7 @@ func findLastNewline(s string, searchWindow int) int { // findLastSpace finds the last space character within the last N characters // Returns the position of the space or -1 if not found func findLastSpace(s string, searchWindow int) int { - searchStart := len(s) - searchWindow - if searchStart < 0 { - searchStart = 0 - } + searchStart := max(len(s)-searchWindow, 0) for i := len(s) - 1; i >= searchStart; i-- { if s[i] == ' ' || s[i] == '\t' { return i