From 3b61c4656083347e9aacb11e0b52f5936bfdc595 Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:13:34 +0300 Subject: [PATCH 01/19] refactor(acp,bg,agenteval,config): use slices.Contains, for range N, max builtin, remove omitempty - acp: replace manual contains loop with slices.Contains, use for range N - background: remove omitempty from non-pointer CompletedAt field - agenteval: remove omitempty from value-type ContextChecks field, use SplitSeq - config: use slices.Contains, max builtin for clamp patterns --- internal/acp/jsonrpc_test.go | 2 +- internal/acp/permission.go | 8 ++---- internal/agenteval/suite.go | 2 +- internal/agenteval/trace.go | 2 +- internal/background/manager.go | 2 +- internal/config/credentials.go | 7 +++--- internal/config/resolver.go | 5 ++-- internal/config/types.go | 45 ++++++++++++++++------------------ 8 files changed, 32 insertions(+), 41 deletions(-) diff --git a/internal/acp/jsonrpc_test.go b/internal/acp/jsonrpc_test.go index d9e5d3df..a1d07f12 100644 --- a/internal/acp/jsonrpc_test.go +++ b/internal/acp/jsonrpc_test.go @@ -158,7 +158,7 @@ func TestConnSurvivesMalformedLine(t *testing.T) { dec := json.NewDecoder(clientR) var sawParseError, sawPong bool - for i := 0; i < 2; i++ { + for i := range 2 { var msg struct { Result any `json:"result"` Error *struct { diff --git a/internal/acp/permission.go b/internal/acp/permission.go index cbe24f9b..363abd47 100644 --- a/internal/acp/permission.go +++ b/internal/acp/permission.go @@ -2,6 +2,7 @@ package acp import ( "encoding/json" + "slices" "github.com/Gitlawb/zero/internal/agent" ) @@ -85,12 +86,7 @@ func decisionFromOutcome(outcome RequestPermissionOutcome, offered []agent.Permi } func actionOffered(action agent.PermissionDecisionAction, offered []agent.PermissionDecisionAction) bool { - for _, a := range offered { - if a == action { - return true - } - } - return false + return slices.Contains(offered, action) } // permissionToolCall builds the ToolCall descriptor embedded in a diff --git a/internal/agenteval/suite.go b/internal/agenteval/suite.go index 638627e1..ed873d77 100644 --- a/internal/agenteval/suite.go +++ b/internal/agenteval/suite.go @@ -30,7 +30,7 @@ type Task struct { ExpectedChangedFiles []string `json:"expectedChangedFiles"` ForbiddenChangedFiles []string `json:"forbiddenChangedFiles,omitempty"` RequiredTraceEvents []string `json:"requiredTraceEvents,omitempty"` - ContextChecks ContextChecks `json:"contextChecks,omitempty"` + ContextChecks ContextChecks `json:"contextChecks"` } type Command struct { diff --git a/internal/agenteval/trace.go b/internal/agenteval/trace.go index 1ac36fc7..f4312242 100644 --- a/internal/agenteval/trace.go +++ b/internal/agenteval/trace.go @@ -8,7 +8,7 @@ import ( func ParseTraceEventKeys(stdout string) []string { seen := map[string]bool{} - for _, line := range strings.Split(stdout, "\n") { + for line := range strings.SplitSeq(stdout, "\n") { line = strings.TrimSpace(line) if line == "" { continue diff --git a/internal/background/manager.go b/internal/background/manager.go index 514fa5a3..b877b2f4 100644 --- a/internal/background/manager.go +++ b/internal/background/manager.go @@ -33,7 +33,7 @@ type Task struct { Status Status `json:"status"` OutputFile string `json:"outputFile"` StartedAt time.Time `json:"startedAt"` - CompletedAt time.Time `json:"completedAt,omitempty"` + CompletedAt time.Time `json:"completedAt"` ExitCode int `json:"exitCode,omitempty"` } diff --git a/internal/config/credentials.go b/internal/config/credentials.go index 9426698c..f4b7d4f7 100644 --- a/internal/config/credentials.go +++ b/internal/config/credentials.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "slices" "strings" "github.com/Gitlawb/zero/internal/credstore" @@ -200,10 +201,8 @@ func (profile ProviderProfile) OAuthLoginCandidates() []string { if s == "" { return } - for _, existing := range names { - if existing == s { // case-sensitive: the store lookup is case-sensitive - return - } + if slices.Contains(names, s) { // case-sensitive: the store lookup is case-sensitive + return } names = append(names, s) } diff --git a/internal/config/resolver.go b/internal/config/resolver.go index 713cc7c8..a2ad771a 100644 --- a/internal/config/resolver.go +++ b/internal/config/resolver.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "maps" "net/url" "os" "strconv" @@ -782,9 +783,7 @@ func copyMCPStringMap(values map[string]string) map[string]string { return nil } copied := make(map[string]string, len(values)) - for key, value := range values { - copied[key] = value - } + maps.Copy(copied, values) return copied } diff --git a/internal/config/types.go b/internal/config/types.go index 4fb24766..48e41b5b 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -3,6 +3,7 @@ package config import ( "encoding/json" "fmt" + "maps" "strings" ) @@ -136,9 +137,9 @@ type KeyBindingsConfig struct { // background probe is required during startup. type LocalControlConfig struct { Enabled bool `json:"enabled,omitempty"` - Browser LocalControlDriverConfig `json:"browser,omitempty"` - Desktop LocalControlDriverConfig `json:"desktop,omitempty"` - Terminal LocalControlDriverConfig `json:"terminal,omitempty"` + Browser LocalControlDriverConfig `json:"browser"` + Desktop LocalControlDriverConfig `json:"desktop"` + Terminal LocalControlDriverConfig `json:"terminal"` ArtifactsDir string `json:"artifactsDir,omitempty"` enabledSet bool } @@ -219,14 +220,14 @@ type FileConfig struct { ActiveProvider string `json:"activeProvider,omitempty"` Providers []ProviderProfile `json:"providers,omitempty"` MaxTurns int `json:"maxTurns,omitempty"` - MCP MCPConfig `json:"mcp,omitempty"` - Sandbox SandboxConfig `json:"sandbox,omitempty"` - Notify NotifyConfig `json:"notify,omitempty"` - Tools ToolsConfig `json:"tools,omitempty"` - Swarm SwarmConfig `json:"swarm,omitempty"` - Preferences PreferencesConfig `json:"preferences,omitempty"` - KeyBindings KeyBindingsConfig `json:"keybindings,omitempty"` - LocalControl LocalControlConfig `json:"localControl,omitempty"` + MCP MCPConfig `json:"mcp"` + Sandbox SandboxConfig `json:"sandbox"` + Notify NotifyConfig `json:"notify"` + Tools ToolsConfig `json:"tools"` + Swarm SwarmConfig `json:"swarm"` + Preferences PreferencesConfig `json:"preferences"` + KeyBindings KeyBindingsConfig `json:"keybindings"` + LocalControl LocalControlConfig `json:"localControl"` } func (cfg FileConfig) MarshalJSON() ([]byte, error) { @@ -234,13 +235,13 @@ func (cfg FileConfig) MarshalJSON() ([]byte, error) { ActiveProvider string `json:"activeProvider,omitempty"` Providers []ProviderProfile `json:"providers,omitempty"` MaxTurns int `json:"maxTurns,omitempty"` - MCP MCPConfig `json:"mcp,omitempty"` - Sandbox SandboxConfig `json:"sandbox,omitempty"` - Notify NotifyConfig `json:"notify,omitempty"` - Tools ToolsConfig `json:"tools,omitempty"` - Swarm SwarmConfig `json:"swarm,omitempty"` - Preferences PreferencesConfig `json:"preferences,omitempty"` - KeyBindings KeyBindingsConfig `json:"keybindings,omitempty"` + MCP MCPConfig `json:"mcp"` + Sandbox SandboxConfig `json:"sandbox"` + Notify NotifyConfig `json:"notify"` + Tools ToolsConfig `json:"tools"` + Swarm SwarmConfig `json:"swarm"` + Preferences PreferencesConfig `json:"preferences"` + KeyBindings KeyBindingsConfig `json:"keybindings"` LocalControl *LocalControlConfig `json:"localControl,omitempty"` } raw := rawConfig{ @@ -372,9 +373,7 @@ func (cfg *FileConfig) UnmarshalJSON(data []byte) error { if cfg.MCP.Servers == nil && (len(raw.MCPServers) > 0 || len(raw.MCPServersSnake) > 0) { cfg.MCP.Servers = map[string]MCPServerConfig{} } - for name, server := range raw.MCPServers { - cfg.MCP.Servers[name] = server - } + maps.Copy(cfg.MCP.Servers, raw.MCPServers) for name, server := range raw.MCPServersSnake { if _, exists := cfg.MCP.Servers[name]; exists { return fmt.Errorf("MCP server %q is defined in both mcpServers and mcp_servers; mcp_servers would override mcpServers", name) @@ -596,8 +595,6 @@ func copyStringMap(values map[string]string) map[string]string { return nil } copied := make(map[string]string, len(values)) - for key, value := range values { - copied[key] = value - } + maps.Copy(copied, values) return copied } From 7a9fb24b562acfd4e5120b60aa41e70fd872b0c2 Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:13:37 +0300 Subject: [PATCH 02/19] refactor(agent): use maps.Copy and slices.Contains in loop.go - cloneArgs: replace manual map copy loop with maps.Copy - containsToolName: replace manual loop with slices.Contains - containsPermissionDecision test helper: replace manual loop with slices.Contains --- internal/agent/loop.go | 15 +++++---------- internal/agent/loop_test.go | 8 ++------ 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/internal/agent/loop.go b/internal/agent/loop.go index 3cc83774..29db8937 100644 --- a/internal/agent/loop.go +++ b/internal/agent/loop.go @@ -6,6 +6,8 @@ import ( "errors" "fmt" "io" + "maps" + "slices" "sort" "strings" "sync" @@ -2481,9 +2483,7 @@ func cloneArgs(args map[string]any) map[string]any { return nil } copied := make(map[string]any, len(args)) - for key, value := range args { - copied[key] = value - } + maps.Copy(copied, args) return copied } @@ -2679,12 +2679,7 @@ func ToolAllowedByFilters(name string, enabledTools []string, disabledTools []st } func containsToolName(names []string, name string) bool { - for _, candidate := range names { - if candidate == name { - return true - } - } - return false + return slices.Contains(names, name) } func schemaToRuntimeMap(schema tools.Schema) map[string]any { @@ -2803,7 +2798,7 @@ func loadedToolsFromResult(meta map[string]string) []string { return nil } var names []string - for _, part := range strings.Split(raw, ",") { + for part := range strings.SplitSeq(raw, ",") { if trimmed := strings.TrimSpace(part); trimmed != "" { names = append(names, trimmed) } diff --git a/internal/agent/loop_test.go b/internal/agent/loop_test.go index 88052d06..9756ba85 100644 --- a/internal/agent/loop_test.go +++ b/internal/agent/loop_test.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "runtime" + "slices" "strings" "testing" @@ -2393,12 +2394,7 @@ func TestRunAlwaysAllowWithoutSandboxStillAllowsCall(t *testing.T) { } func containsPermissionDecision(decisions []PermissionDecisionAction, want PermissionDecisionAction) bool { - for _, decision := range decisions { - if decision == want { - return true - } - } - return false + return slices.Contains(decisions, want) } // cancelMidStreamProvider cancels the run while the provider stream is open and From f7b349e04e4663bdd93ac44197353498582d98f2 Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:13:41 +0300 Subject: [PATCH 03/19] refactor(agent): use strings.Cut/CutPrefix/CutSuffix instead of Index+slice - command_prefix.go: use CutSuffix instead of HasSuffix+TrimSuffix, use SplitSeq - guardrails.go: use strings.Cut instead of Index+slice in IsNoProgressStop - compaction_preserve.go: use strings.Cut instead of IndexByte for heading extraction - system_prompt.go: use CutPrefix instead of HasPrefix+TrimPrefix for git branch --- internal/agent/command_prefix.go | 6 +++--- internal/agent/compaction_preserve.go | 13 +++++-------- internal/agent/guardrails.go | 8 ++++---- internal/agent/system_prompt.go | 11 ++++------- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/internal/agent/command_prefix.go b/internal/agent/command_prefix.go index 43e666f7..0fc65374 100644 --- a/internal/agent/command_prefix.go +++ b/internal/agent/command_prefix.go @@ -343,7 +343,7 @@ func validSedPrintArg(arg string) bool { if body == "" { return false } - for _, part := range strings.Split(body, ",") { + for part := range strings.SplitSeq(body, ",") { if part == "" { return false } @@ -466,8 +466,8 @@ func commandName(raw string) string { } name = strings.ToLower(name) for _, suffix := range []string{".exe", ".cmd", ".bat", ".com"} { - if strings.HasSuffix(name, suffix) { - return strings.TrimSuffix(name, suffix) + if before, ok := strings.CutSuffix(name, suffix); ok { + return before } } return name diff --git a/internal/agent/compaction_preserve.go b/internal/agent/compaction_preserve.go index 6423a5b2..a9a45242 100644 --- a/internal/agent/compaction_preserve.go +++ b/internal/agent/compaction_preserve.go @@ -210,7 +210,7 @@ func editPathFromArguments(arguments string) string { // editNote reduces an edit tool's result to a single short line (its first // non-empty line, byte-capped on a rune boundary) for the preserved summary. func editNote(content string) string { - for _, line := range strings.Split(content, "\n") { + for line := range strings.SplitSeq(content, "\n") { s := strings.TrimSpace(line) if s == "" { continue @@ -369,11 +369,11 @@ func projectInstructionBlock(content string) (string, string) { if !strings.HasPrefix(content, projectInstructionsHeadingPrefix) { return "", "" } - firstLineEnd := strings.IndexByte(content, '\n') - if firstLineEnd < 0 { + before, _, ok := strings.Cut(content, "\n") + if !ok { return "", "" } - heading := strings.TrimSpace(content[:firstLineEnd]) + heading := strings.TrimSpace(before) if !strings.Contains(heading, projectInstructionsHeadingMarker) { return "", "" } @@ -417,10 +417,7 @@ func capBody(body string) string { if len(body) <= maxPreservedSkillBytes { return body } - limit := maxPreservedSkillBytes - len(truncationNote) - if limit < 0 { - limit = 0 - } + limit := max(maxPreservedSkillBytes-len(truncationNote), 0) // Walk back to the start of a rune so a multibyte sequence is never split. for limit > 0 && !utf8.RuneStart(body[limit]) { limit-- diff --git a/internal/agent/guardrails.go b/internal/agent/guardrails.go index 02776531..b115b6ab 100644 --- a/internal/agent/guardrails.go +++ b/internal/agent/guardrails.go @@ -315,18 +315,18 @@ func IsNoProgressStop(content string) bool { } rest := trimmed[len(noOutputStopPrefix):] const turnsSep = " turns " - sep := strings.Index(rest, turnsSep) - if sep < 0 { + before, after, ok := strings.Cut(rest, turnsSep) + if !ok { return false } // The text between the prefix and " turns " must be exactly the bare integer // count; anything else means this isn't the guard's own answer. - if _, err := strconv.Atoi(rest[:sep]); err != nil { + if _, err := strconv.Atoi(before); err != nil { return false } // The marker must be immediately followed (one space) by the suffix and then // end — no arbitrary text wedged in between. - return rest[sep+len(turnsSep):] == noOutputStopMarker+" "+noOutputStopSuffix + return after == noOutputStopMarker+" "+noOutputStopSuffix } // Reminder markers are stable substrings used both to build the reminder text diff --git a/internal/agent/system_prompt.go b/internal/agent/system_prompt.go index 5cf2431e..e07ddfe2 100644 --- a/internal/agent/system_prompt.go +++ b/internal/agent/system_prompt.go @@ -388,10 +388,7 @@ func truncateGuidelineContent(content string, limit int) string { if limit <= 0 { return "" } - cut := limit - len(truncationMarker) - if cut < 0 { - cut = 0 - } + cut := max(limit-len(truncationMarker), 0) for cut > 0 && !utf8.RuneStart(content[cut]) { cut-- } @@ -443,7 +440,7 @@ func projectGuidelineDirs(cwd, gitRoot string) []string { } dirs := []string{gitRoot} cur := gitRoot - for _, seg := range strings.Split(rel, string(filepath.Separator)) { + for seg := range strings.SplitSeq(rel, string(filepath.Separator)) { if seg == "" || seg == "." { continue } @@ -636,8 +633,8 @@ func gitBranchForPrompt(cwd string) string { return "" } ref := strings.TrimSpace(string(data)) - if strings.HasPrefix(ref, "ref: ") { - return strings.TrimPrefix(strings.TrimPrefix(ref, "ref: "), "refs/heads/") + if after, ok := strings.CutPrefix(ref, "ref: "); ok { + return strings.TrimPrefix(after, "refs/heads/") } if len(ref) >= 7 { return ref[:7] From 7bbfd432f0c198ca158f8cc06cedc15fa7c1b12b Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:13:44 +0300 Subject: [PATCH 04/19] refactor(agent): use max/min builtins for clamp patterns - compaction.go: use max() instead of manual boundary clamp - compaction_preserve.go: use max() for capBody limit clamp - system_prompt.go: use max() for truncateGuidelineContent clamp --- internal/agent/compaction.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/agent/compaction.go b/internal/agent/compaction.go index 75e353cf..19c93237 100644 --- a/internal/agent/compaction.go +++ b/internal/agent/compaction.go @@ -196,10 +196,7 @@ func CompactMessages(messages []zeroruntime.Message, opts CompactionOptions) (Co // Naive boundary: keep the last preserveLast messages. Then widen the suffix // backward to a safe boundary so it never starts on a tool result. - boundary := len(messages) - preserveLast - if boundary < systemEnd { - boundary = systemEnd - } + boundary := max(len(messages)-preserveLast, systemEnd) boundary = safeSuffixBoundary(messages, systemEnd, boundary) middle := messages[systemEnd:boundary] From 144b9ef77b44604f668d45c1eeaeaffa7ce90726 Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:13:51 +0300 Subject: [PATCH 05/19] refactor(agent): use for range N in tests - Replace for i := 0; i < N; i++ with for range N or for i := range N - Covers compaction, defer_savings, guardrails, reconnect, skills_context tests --- internal/agent/compaction_recent_edits_test.go | 2 +- internal/agent/compaction_summarizer_test.go | 2 +- internal/agent/defer_savings_test.go | 2 +- internal/agent/guardrails_test.go | 8 ++++---- internal/agent/reconnect_test.go | 2 +- internal/agent/skills_context_test.go | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/agent/compaction_recent_edits_test.go b/internal/agent/compaction_recent_edits_test.go index c8fe2b73..375f7098 100644 --- a/internal/agent/compaction_recent_edits_test.go +++ b/internal/agent/compaction_recent_edits_test.go @@ -91,7 +91,7 @@ func TestRecentEditsMergeNewerWins(t *testing.T) { func TestRecentEditsCapKeepsReeditedFile(t *testing.T) { // Earlier compaction preserved exactly maxRecentEdits files, f0 the oldest. older := make([]skillEntry, 0, maxRecentEdits) - for i := 0; i < maxRecentEdits; i++ { + for i := range maxRecentEdits { older = append(older, skillEntry{name: fmt.Sprintf("f%d.go", i), body: "old"}) } // Fresh window re-edits the OLDEST file (f0) and adds a brand-new file, so the diff --git a/internal/agent/compaction_summarizer_test.go b/internal/agent/compaction_summarizer_test.go index c9f177a6..ecb7eb2b 100644 --- a/internal/agent/compaction_summarizer_test.go +++ b/internal/agent/compaction_summarizer_test.go @@ -109,7 +109,7 @@ func TestSummarizeWithFallbackChunksOnContextLimit(t *testing.T) { if err != nil { t.Fatalf("summarizeWithFallback failed: %v", err) } - for i := 0; i < n; i++ { + for i := range n { if !strings.Contains(summary, fmt.Sprintf("msg-%d", i)) { t.Fatalf("combined summary missing msg-%d: %q", i, summary) } diff --git a/internal/agent/defer_savings_test.go b/internal/agent/defer_savings_test.go index 4c5bb4d5..a4967ec3 100644 --- a/internal/agent/defer_savings_test.go +++ b/internal/agent/defer_savings_test.go @@ -50,7 +50,7 @@ func (t richMCPTool) Deferred() bool { return true } func TestDeferralTokenSavingsMeasurement(t *testing.T) { const mcpToolCount = 6 registry := tools.NewRegistry() - for i := 0; i < mcpToolCount; i++ { + for i := range mcpToolCount { registry.Register(richMCPTool{name: fmt.Sprintf("mcp__websearch__search_v%d", i)}) } registry.Register(fakeToolSearchTool{}) diff --git a/internal/agent/guardrails_test.go b/internal/agent/guardrails_test.go index bc48e43b..d986832a 100644 --- a/internal/agent/guardrails_test.go +++ b/internal/agent/guardrails_test.go @@ -326,7 +326,7 @@ func TestRunInjectsStalePlanReminderAfterManyToolCalls(t *testing.T) { turns := [][]zeroruntime.StreamEvent{ toolTurn("plan-1", "update_plan", `{"plan":[{"content":"step one"}]}`), } - for i := 0; i < staleToolCallThreshold+2; i++ { + for range staleToolCallThreshold + 2 { turns = append(turns, toolTurn("call", "read_file", `{"path":"notes.txt"}`)) } turns = append(turns, textTurn("done")) @@ -360,7 +360,7 @@ func TestRunStalePlanReminderIsOneShotPerInterval(t *testing.T) { } // Enough tool calls to exceed the threshold by a wide margin; the reminder // must fire once for the interval, not on every subsequent turn. - for i := 0; i < staleToolCallThreshold*2; i++ { + for range staleToolCallThreshold * 2 { turns = append(turns, toolTurn("call", "read_file", `{"path":"notes.txt"}`)) } turns = append(turns, textTurn("done")) @@ -390,7 +390,7 @@ func TestRunInjectsToolOnlyProgressReminder(t *testing.T) { registry.Register(tools.NewReadFileTool(root)) turns := make([][]zeroruntime.StreamEvent, 0, toolOnlyProgressReminderAt+1) - for i := 0; i < toolOnlyProgressReminderAt; i++ { + for range toolOnlyProgressReminderAt { turns = append(turns, toolTurn("call", "read_file", `{"path":"notes.txt"}`)) } turns = append(turns, textTurn("done")) @@ -441,7 +441,7 @@ func repeatedFlakyTurns(n int) [][]zeroruntime.StreamEvent { {Type: zeroruntime.StreamEventDone}, } turns := make([][]zeroruntime.StreamEvent, 0, n) - for i := 0; i < n; i++ { + for range n { turns = append(turns, turn) } return turns diff --git a/internal/agent/reconnect_test.go b/internal/agent/reconnect_test.go index 84bd3a3f..43bb2f22 100644 --- a/internal/agent/reconnect_test.go +++ b/internal/agent/reconnect_test.go @@ -134,7 +134,7 @@ func TestJitteredBackoffStaysInBounds(t *testing.T) { // so backoff still grows attempt over attempt while decorrelating retries. for attempt := 1; attempt <= 5; attempt++ { base := backoffFor(attempt) - for i := 0; i < 200; i++ { + for range 200 { got := jitteredBackoff(attempt) if got < base || got > base+base/2 { t.Fatalf("attempt %d jittered backoff %v out of [%v, %v]", attempt, got, base, base+base/2) diff --git a/internal/agent/skills_context_test.go b/internal/agent/skills_context_test.go index 87073bb6..b141ad34 100644 --- a/internal/agent/skills_context_test.go +++ b/internal/agent/skills_context_test.go @@ -8,7 +8,7 @@ import ( func TestSkillsContextCapsLongList(t *testing.T) { skills := make([]SkillInfo, 0, 40) - for i := 0; i < 40; i++ { + for i := range 40 { n := strconv.Itoa(i) skills = append(skills, SkillInfo{Name: "skill-" + n, Description: "does something useful, number " + n}) } From 7c6f3caa8fc842fd78c25f238bd781e42ec216bc Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:13:59 +0300 Subject: [PATCH 06/19] refactor(cli): use slices.Contains, for range N, SplitSeq, Cut, wg.Go - agent_eval, app_test, exec_spec: use slices.Contains for manual contains loops - cron_claim_test: use for range N with wg.Go instead of manual wg.Add+go - cron_run: use SplitSeq, slices.Contains for contains() - daemon: use strings.Cut for flag parsing - deferred_wiring_test, sandbox_test: use for range N - exec_tools, mcp_config, provider_setup: use slices.Contains - provider_models, provider_onboarding: use max builtin - usage_test: remove unused import --- internal/cli/agent_eval.go | 9 ++++----- internal/cli/app_test.go | 9 ++------- internal/cli/cron_claim_test.go | 8 +++----- internal/cli/cron_run.go | 10 +++------- internal/cli/daemon.go | 4 ++-- internal/cli/deferred_wiring_test.go | 4 ++-- internal/cli/exec_spec_test.go | 8 ++------ internal/cli/exec_tools.go | 8 ++------ internal/cli/mcp_config.go | 5 ++--- internal/cli/provider_models.go | 2 +- internal/cli/provider_onboarding.go | 2 +- internal/cli/provider_setup.go | 7 +++---- internal/cli/sandbox.go | 2 +- internal/cli/sandbox_test.go | 8 ++------ internal/cli/usage_test.go | 2 -- 15 files changed, 30 insertions(+), 58 deletions(-) diff --git a/internal/cli/agent_eval.go b/internal/cli/agent_eval.go index e8cb80fe..b18c41d0 100644 --- a/internal/cli/agent_eval.go +++ b/internal/cli/agent_eval.go @@ -8,6 +8,7 @@ import ( "io" "os" "path/filepath" + "slices" "strings" "time" @@ -316,16 +317,14 @@ func appendAgentEvalModel(models []string, value string) []string { if value == "" { return models } - for _, model := range models { - if model == value { - return models - } + if slices.Contains(models, value) { + return models } return append(models, value) } func appendAgentEvalModels(models []string, value string) []string { - for _, model := range strings.Split(value, ",") { + for model := range strings.SplitSeq(value, ",") { models = appendAgentEvalModel(models, model) } return models diff --git a/internal/cli/app_test.go b/internal/cli/app_test.go index 054148ad..322180b6 100644 --- a/internal/cli/app_test.go +++ b/internal/cli/app_test.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" "runtime" + "slices" "strings" "testing" "time" @@ -812,13 +813,7 @@ func TestRunSkipPermissionsUnsafeMergesAddDirGrants(t *testing.T) { t.Fatal("AgentOptions.Sandbox = nil, want sandbox engine") } roots := launchedOptions.AgentOptions.Sandbox.Scope().Roots() - found := false - for _, root := range roots { - if root == resolvedExtra { - found = true - break - } - } + found := slices.Contains(roots, resolvedExtra) if !found { t.Fatalf("scope roots = %v, want extra root %q", roots, resolvedExtra) } diff --git a/internal/cli/cron_claim_test.go b/internal/cli/cron_claim_test.go index 4108e633..e9e0e54b 100644 --- a/internal/cli/cron_claim_test.go +++ b/internal/cli/cron_claim_test.go @@ -19,14 +19,12 @@ func TestFireJobClaimPreventsDoubleFire(t *testing.T) { // Two schedulers fire the same due job concurrently; the atomic claim must let // exactly one through, so the job never double-fires (M9). var wg sync.WaitGroup - for i := 0; i < 2; i++ { - wg.Add(1) - go func() { - defer wg.Done() + for range 2 { + wg.Go(func() { job, _ := store.Get(due.ID) var out, errb bytes.Buffer fireJob(store, nowFn, job, &out, &errb, fx.run) - }() + }) } wg.Wait() diff --git a/internal/cli/cron_run.go b/internal/cli/cron_run.go index 929e76ee..f5630a9c 100644 --- a/internal/cli/cron_run.go +++ b/internal/cli/cron_run.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "slices" "strings" "time" @@ -89,12 +90,7 @@ func cronRun(store *cron.Store, now func() time.Time, args []string, stdout io.W // contains reports whether ss contains want. func contains(ss []string, want string) bool { - for _, s := range ss { - if s == want { - return true - } - } - return false + return slices.Contains(ss, want) } // reconcileOverdue (forever-mode startup, non --catch-up) reschedules jobs that @@ -291,7 +287,7 @@ func cronTruncate(s string, max int) string { // failure detail rides on stdout, so this recovers it for the run record. func extractStreamJSONError(output string) string { found := "" - for _, line := range strings.Split(output, "\n") { + for line := range strings.SplitSeq(output, "\n") { line = strings.TrimSpace(line) if line == "" { continue diff --git a/internal/cli/daemon.go b/internal/cli/daemon.go index 5074312e..e7340024 100644 --- a/internal/cli/daemon.go +++ b/internal/cli/daemon.go @@ -556,8 +556,8 @@ func runDaemonLink(args []string, stdout io.Writer, stderr io.Writer) int { return writeDaemonUsage(stdout, exitSuccess) } name, inlineVal, hasInline := a, "", false - if eq := strings.IndexByte(a, '='); eq >= 0 { - name, inlineVal, hasInline = a[:eq], a[eq+1:], true + if before, after, ok := strings.Cut(a, "="); ok { + name, inlineVal, hasInline = before, after, true } dst, ok := flags[name] if !ok { diff --git a/internal/cli/deferred_wiring_test.go b/internal/cli/deferred_wiring_test.go index c5740555..5ff38dda 100644 --- a/internal/cli/deferred_wiring_test.go +++ b/internal/cli/deferred_wiring_test.go @@ -39,7 +39,7 @@ func registryHasToolSearch(registry *tools.Registry) bool { func TestRegisterToolSearchIfEligibleRegistersAtThreshold(t *testing.T) { registry := tools.NewRegistry() - for i := 0; i < 3; i++ { + for i := range 3 { registry.Register(cliFakeDeferredTool{name: "mcp_srv_t" + string(rune('a'+i))}) } @@ -66,7 +66,7 @@ func TestRegisterToolSearchIfEligibleSkipsBelowThreshold(t *testing.T) { func TestRegisterToolSearchIfEligibleSkipsWhenThresholdZero(t *testing.T) { registry := tools.NewRegistry() - for i := 0; i < 5; i++ { + for i := range 5 { registry.Register(cliFakeDeferredTool{name: "mcp_srv_t" + string(rune('a'+i))}) } diff --git a/internal/cli/exec_spec_test.go b/internal/cli/exec_spec_test.go index 7dc684c7..6c5eed69 100644 --- a/internal/cli/exec_spec_test.go +++ b/internal/cli/exec_spec_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "path/filepath" + "slices" "strings" "testing" @@ -195,10 +196,5 @@ func (provider *submitSpecExecProvider) requestIncludesTool(name string) bool { } func slicesContains(values []string, value string) bool { - for _, candidate := range values { - if candidate == value { - return true - } - } - return false + return slices.Contains(values, value) } diff --git a/internal/cli/exec_tools.go b/internal/cli/exec_tools.go index d019a425..742d50b7 100644 --- a/internal/cli/exec_tools.go +++ b/internal/cli/exec_tools.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "regexp" + "slices" "sort" "strings" @@ -64,12 +65,7 @@ func validateExecToolFilters(options execOptions, registry *tools.Registry) erro } func toolListContains(names []string, want string) bool { - for _, name := range names { - if name == want { - return true - } - } - return false + return slices.Contains(names, want) } func resolveExecPermissionMode(options execOptions) (agent.PermissionMode, error) { diff --git a/internal/cli/mcp_config.go b/internal/cli/mcp_config.go index eaf2cf69..f6c95f55 100644 --- a/internal/cli/mcp_config.go +++ b/internal/cli/mcp_config.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "maps" "os" "path/filepath" "runtime" @@ -631,9 +632,7 @@ func cloneStringMap(values map[string]string) map[string]string { return nil } cloned := make(map[string]string, len(values)) - for key, value := range values { - cloned[key] = value - } + maps.Copy(cloned, values) return cloned } diff --git a/internal/cli/provider_models.go b/internal/cli/provider_models.go index 9e545dc2..a6f1edeb 100644 --- a/internal/cli/provider_models.go +++ b/internal/cli/provider_models.go @@ -128,7 +128,7 @@ func defaultDiscoverProviderModels(ctx context.Context, profile config.ProviderP func parseProviderModelsArgs(args []string) (providerModelsOptions, bool, error) { options := providerModelsOptions{} - for index := 0; index < len(args); index++ { + for index := range args { arg := args[index] switch { case arg == "-h" || arg == "--help" || arg == "help": diff --git a/internal/cli/provider_onboarding.go b/internal/cli/provider_onboarding.go index c3bd3de4..60071540 100644 --- a/internal/cli/provider_onboarding.go +++ b/internal/cli/provider_onboarding.go @@ -124,7 +124,7 @@ func runProvidersSetup(args []string, stdout io.Writer, stderr io.Writer, deps a func parseProviderUseArgs(args []string) (providerUseOptions, bool, error) { options := providerUseOptions{} - for index := 0; index < len(args); index++ { + for index := range args { arg := args[index] switch { case arg == "-h" || arg == "--help" || arg == "help": diff --git a/internal/cli/provider_setup.go b/internal/cli/provider_setup.go index b177c545..e0b89722 100644 --- a/internal/cli/provider_setup.go +++ b/internal/cli/provider_setup.go @@ -3,6 +3,7 @@ package cli import ( "fmt" "io" + "maps" "strings" "github.com/Gitlawb/zero/internal/config" @@ -374,7 +375,7 @@ func parseProviderAddArgs(args []string) (providerAddOptions, bool, error) { func parseProviderCheckArgs(args []string) (providerCheckOptions, bool, error) { options := providerCheckOptions{} - for index := 0; index < len(args); index++ { + for index := range args { arg := args[index] switch { case arg == "-h" || arg == "--help" || arg == "help": @@ -541,8 +542,6 @@ func copyProviderHeaders(headers map[string]string) map[string]string { return nil } copied := make(map[string]string, len(headers)) - for key, value := range headers { - copied[key] = value - } + maps.Copy(copied, headers) return copied } diff --git a/internal/cli/sandbox.go b/internal/cli/sandbox.go index 56e09988..a0209686 100644 --- a/internal/cli/sandbox.go +++ b/internal/cli/sandbox.go @@ -522,7 +522,7 @@ type sandboxCommandFlags struct { func parseSandboxCommandOptions(args []string, flags sandboxCommandFlags) (sandboxCommandOptions, bool, error) { options := sandboxCommandOptions{} - for index := 0; index < len(args); index++ { + for index := range args { arg := args[index] switch { case arg == "-h" || arg == "--help" || arg == "help": diff --git a/internal/cli/sandbox_test.go b/internal/cli/sandbox_test.go index 6d529039..2b2b7c96 100644 --- a/internal/cli/sandbox_test.go +++ b/internal/cli/sandbox_test.go @@ -9,6 +9,7 @@ import ( "os" "path/filepath" "reflect" + "slices" "strings" "testing" @@ -590,12 +591,7 @@ func filterJSONWriteRoots(value any, excluded map[string]struct{}) any { } func containsString(values []string, want string) bool { - for _, value := range values { - if value == want { - return true - } - } - return false + return slices.Contains(values, want) } func jsonValuesEqual(t *testing.T, wantBytes []byte, gotBytes []byte) bool { diff --git a/internal/cli/usage_test.go b/internal/cli/usage_test.go index 9f57aae2..0754e106 100644 --- a/internal/cli/usage_test.go +++ b/internal/cli/usage_test.go @@ -171,7 +171,6 @@ func TestRunUsageEmptySessionRejected(t *testing.T) { {name: "session-id spaced empty", args: []string{"usage", "report", "--session-id", ""}, flag: "--session-id"}, } for _, tc := range cases { - tc := tc t.Run(tc.name, func(t *testing.T) { var stdout, stderr bytes.Buffer exitCode := runWithDeps(tc.args, &stdout, &stderr, appDeps{ @@ -286,7 +285,6 @@ func TestRunUsageInvalidSince(t *testing.T) { {"2026-06-01", false}, // valid } for _, tc := range cases { - tc := tc t.Run("since="+tc.since, func(t *testing.T) { var stdout, stderr bytes.Buffer exitCode := runWithDeps([]string{"usage", "report", "--since", tc.since}, &stdout, &stderr, appDeps{ From 7464f25c4a8ceb193021f531169c1678dcb10c0c Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:14:11 +0300 Subject: [PATCH 07/19] refactor(cron,daemon,hooks,imageinput,lsp,etc): use slices.Contains, SplitSeq, for range N, min/max - contextreport: use slices.Contains for contains patterns, for range N - cron: use for range N, strings.Cut instead of IndexByte - daemon: use for range N, slices.Contains, for range for session_race_test - hooks: use slices.Contains, for range N with wg.Go - imageinput: use max(), strings.Cut for clipboard - installtest: use for range N - keyring: use slices.Contains - localcontrol: use for range N - lsp: use for range N, slices.Contains --- internal/contextreport/contextreport.go | 5 +---- internal/contextreport/contextreport_test.go | 8 ++------ internal/cron/mutate_test.go | 8 +++----- internal/cron/schedule.go | 8 ++++---- internal/cron/store.go | 2 +- internal/cron/store_test.go | 2 +- internal/daemon/backoff.go | 2 +- internal/daemon/backoff_test.go | 2 +- internal/daemon/lock.go | 2 +- internal/daemon/remote/bundle.go | 4 ++-- internal/daemon/server.go | 6 ++---- internal/daemon/session_race_test.go | 16 ++++++---------- internal/hooks/audit_concurrent_test.go | 2 +- internal/hooks/hooks.go | 15 ++++----------- internal/imageinput/clipboard.go | 7 ++----- internal/imageinput/imageinput.go | 5 +---- internal/imageinput/pdf.go | 10 ++-------- internal/imageinput/pdf_test.go | 5 +---- internal/installtest/install_scripts_test.go | 2 +- internal/keyring/keyring.go | 8 ++------ internal/localcontrol/browser_test.go | 8 ++------ internal/lsp/client_test.go | 2 +- internal/lsp/manager_test.go | 8 +++----- 23 files changed, 45 insertions(+), 92 deletions(-) diff --git a/internal/contextreport/contextreport.go b/internal/contextreport/contextreport.go index f27e0394..e2602b83 100644 --- a/internal/contextreport/contextreport.go +++ b/internal/contextreport/contextreport.go @@ -126,10 +126,7 @@ func Build(options Options) (Report, error) { report.UsedTokens = used if report.ContextWindow > 0 { report.UsedFraction = float64(report.UsedTokens) / float64(report.ContextWindow) - report.FreeTokens = report.ContextWindow - report.UsedTokens - if report.FreeTokens < 0 { - report.FreeTokens = 0 - } + report.FreeTokens = max(report.ContextWindow-report.UsedTokens, 0) } categories = append(categories, category(CategoryFree, "Free", report.FreeTokens, report.ContextWindow)) report.Categories = categories diff --git a/internal/contextreport/contextreport_test.go b/internal/contextreport/contextreport_test.go index 85be3a6e..373fd43e 100644 --- a/internal/contextreport/contextreport_test.go +++ b/internal/contextreport/contextreport_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "os" "path/filepath" + "slices" "strings" "testing" @@ -335,12 +336,7 @@ func categoryKeys(report Report) []string { } func contains(values []string, want string) bool { - for _, value := range values { - if value == want { - return true - } - } - return false + return slices.Contains(values, want) } func writeTestFile(t *testing.T, root string, name string, content string) { diff --git a/internal/cron/mutate_test.go b/internal/cron/mutate_test.go index 42c27e8d..86e545f6 100644 --- a/internal/cron/mutate_test.go +++ b/internal/cron/mutate_test.go @@ -19,10 +19,8 @@ func TestMutateIsAtomicUnderConcurrency(t *testing.T) { const goroutines = 20 var wg sync.WaitGroup start := make(chan struct{}) - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func() { - defer wg.Done() + for range goroutines { + wg.Go(func() { <-start _, merr := store.Mutate(job.ID, func(current Job, readErr error) (Job, error) { if readErr != nil { @@ -34,7 +32,7 @@ func TestMutateIsAtomicUnderConcurrency(t *testing.T) { if merr != nil { t.Errorf("Mutate: %v", merr) } - }() + }) } close(start) wg.Wait() diff --git a/internal/cron/schedule.go b/internal/cron/schedule.go index e6074180..97c7566f 100644 --- a/internal/cron/schedule.go +++ b/internal/cron/schedule.go @@ -78,7 +78,7 @@ func parseField(field string, min, max int, names map[string]int) (uint64, bool, return full(min, max), true, nil } var set uint64 - for _, item := range strings.Split(field, ",") { + for item := range strings.SplitSeq(field, ",") { if item == "" { return 0, false, fmt.Errorf("empty list element in %q", field) } @@ -94,9 +94,9 @@ func parseField(field string, min, max int, names map[string]int) (uint64, bool, func parseItem(item string, min, max int, names map[string]int) (uint64, error) { rangePart := item step := 1 - if slash := strings.IndexByte(item, '/'); slash >= 0 { - rangePart = item[:slash] - stepStr := item[slash+1:] + if before, after, ok := strings.Cut(item, "/"); ok { + rangePart = before + stepStr := after n, err := strconv.Atoi(stepStr) if err != nil || n <= 0 { return 0, fmt.Errorf("invalid step %q in %q", stepStr, item) diff --git a/internal/cron/store.go b/internal/cron/store.go index cee2fa0f..c55e0149 100644 --- a/internal/cron/store.go +++ b/internal/cron/store.go @@ -110,7 +110,7 @@ func (s *Store) Add(job Job) (Job, error) { func (s *Store) allocID() (string, error) { base := s.now().UTC().Format("20060102-150405") - for n := 0; n < 100; n++ { + for n := range 100 { id := base if n > 0 { id = fmt.Sprintf("%s-%d", base, n) diff --git a/internal/cron/store_test.go b/internal/cron/store_test.go index 9aada15e..a081ada8 100644 --- a/internal/cron/store_test.go +++ b/internal/cron/store_test.go @@ -77,7 +77,7 @@ func TestStoreAddListGetRemove(t *testing.T) { func TestStoreAppendRun(t *testing.T) { s := newTestStore(t) job, _ := s.Add(Job{Expr: "* * * * *", Prompt: "x", Status: StatusActive}) - for i := 0; i < 3; i++ { + for i := range 3 { if err := s.AppendRun(job.ID, RunRecord{JobID: job.ID, At: time.Unix(int64(i), 0).UTC(), ExitCode: i}); err != nil { t.Fatalf("AppendRun: %v", err) } diff --git a/internal/daemon/backoff.go b/internal/daemon/backoff.go index 0fed702d..ab953ed1 100644 --- a/internal/daemon/backoff.go +++ b/internal/daemon/backoff.go @@ -21,7 +21,7 @@ func backoffBase(attempt int) time.Duration { // Shift carefully: cap before overflow. 2^attempt * 1s exceeds the cap well // before any int64 overflow (cap is 5m), so clamp once it would exceed it. d := backoffUnit - for i := 0; i < attempt; i++ { + for range attempt { d *= 2 if d >= backoffCap { return backoffCap diff --git a/internal/daemon/backoff_test.go b/internal/daemon/backoff_test.go index 31fb9657..bad9b800 100644 --- a/internal/daemon/backoff_test.go +++ b/internal/daemon/backoff_test.go @@ -28,7 +28,7 @@ func TestBackoffWithJitterBounds(t *testing.T) { base := backoffBase(attempt) lo := time.Duration(float64(base) * 0.5) hi := time.Duration(float64(base) * 1.5) - for i := 0; i < 200; i++ { + for range 200 { got := BackoffWithJitter(attempt) if got < lo || got >= hi { t.Fatalf("BackoffWithJitter(%d) = %s, want in [%s,%s)", attempt, got, lo, hi) diff --git a/internal/daemon/lock.go b/internal/daemon/lock.go index cc0931df..92298c44 100644 --- a/internal/daemon/lock.go +++ b/internal/daemon/lock.go @@ -35,7 +35,7 @@ func acquireLock(path string, isAlive func(pid int) bool) (*fileLock, error) { isAlive = processAlive } // At most two passes: create, or detect-stale-then-retry once. - for attempt := 0; attempt < 2; attempt++ { + for range 2 { f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o600) if err == nil { // A failed PID write would leave a malformed lock file that another diff --git a/internal/daemon/remote/bundle.go b/internal/daemon/remote/bundle.go index 398c1bcb..7223c3f7 100644 --- a/internal/daemon/remote/bundle.go +++ b/internal/daemon/remote/bundle.go @@ -371,8 +371,8 @@ func hashFile(path string) (sum string, size int64, err error) { } func firstLine(s string) string { - if i := strings.IndexByte(s, '\n'); i >= 0 { - return s[:i] + if before, _, ok := strings.Cut(s, "\n"); ok { + return before } return s } diff --git a/internal/daemon/server.go b/internal/daemon/server.go index 21c44e0d..d78e129c 100644 --- a/internal/daemon/server.go +++ b/internal/daemon/server.go @@ -134,12 +134,10 @@ func (s *Server) Serve() error { } } s.trackConn(conn) - s.wg.Add(1) - go func() { - defer s.wg.Done() + s.wg.Go(func() { defer s.untrackConn(conn) s.handleConn(conn) - }() + }) } } diff --git a/internal/daemon/session_race_test.go b/internal/daemon/session_race_test.go index ca5a226c..a570fa2d 100644 --- a/internal/daemon/session_race_test.go +++ b/internal/daemon/session_race_test.go @@ -12,15 +12,13 @@ import ( // "send on closed channel"). Reverting that lock-hold makes this test panic/race; // with it, it passes. Run with: go test -race ./internal/daemon func TestSessionLineRaceWithCancelAndFinish(t *testing.T) { - for iter := 0; iter < 30; iter++ { + for range 30 { sess := newSession("s", "", 0) sess.Started() stop := make(chan struct{}) var pump sync.WaitGroup - pump.Add(1) - go func() { - defer pump.Done() + pump.Go(func() { for { select { case <-stop: @@ -29,19 +27,17 @@ func TestSessionLineRaceWithCancelAndFinish(t *testing.T) { sess.Line("x") // hammers the broadcast/send path } } - }() + }) // Repeatedly attach, spin up a drainer, and cancel while Line runs. Each // cancel closes the subscriber channel under s.mu, racing the pump's send. var drainers sync.WaitGroup - for i := 0; i < 150; i++ { + for range 150 { _, live, cancel := sess.Subscribe() - drainers.Add(1) - go func() { - defer drainers.Done() + drainers.Go(func() { for range live { } - }() + }) cancel() } diff --git a/internal/hooks/audit_concurrent_test.go b/internal/hooks/audit_concurrent_test.go index 4debc4bd..4be6dd09 100644 --- a/internal/hooks/audit_concurrent_test.go +++ b/internal/hooks/audit_concurrent_test.go @@ -29,7 +29,7 @@ func TestAuditStoreSequenceConcurrent(t *testing.T) { var mu sync.Mutex seqs := make([]int, 0, storeCount*perStore) for _, st := range stores { - for i := 0; i < perStore; i++ { + for range perStore { wg.Add(1) go func(st *AuditStore) { defer wg.Done() diff --git a/internal/hooks/hooks.go b/internal/hooks/hooks.go index 34ca6ed5..4985bacf 100644 --- a/internal/hooks/hooks.go +++ b/internal/hooks/hooks.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "regexp" + "slices" "sort" "strings" "sync" @@ -483,7 +484,7 @@ func (store *AuditStore) ReadEvents() ([]AuditEvent, error) { return nil, err } events := []AuditEvent{} - for _, line := range strings.Split(string(data), "\n") { + for line := range strings.SplitSeq(string(data), "\n") { line = strings.TrimSpace(line) if line == "" { continue @@ -565,10 +566,7 @@ func (store *AuditStore) lastSequence() (int, error) { return 0, nil } const tailWindow = 8 * 1024 - start := size - tailWindow - if start < 0 { - start = 0 - } + start := max(size-tailWindow, 0) buf := make([]byte, size-start) // ReadAt may return io.EOF with a short read if the file shrank between Stat // and here; treat EOF as non-fatal and use only the bytes actually read. @@ -890,12 +888,7 @@ func KnownEvents() []Event { // IsValidEvent reports whether event is one Zero recognizes. func IsValidEvent(event Event) bool { - for _, known := range KnownEvents() { - if event == known { - return true - } - } - return false + return slices.Contains(KnownEvents(), event) } func parseEvent(raw any, field string) (Event, error) { diff --git a/internal/imageinput/clipboard.go b/internal/imageinput/clipboard.go index 40db5ef3..b52c3164 100644 --- a/internal/imageinput/clipboard.go +++ b/internal/imageinput/clipboard.go @@ -25,10 +25,7 @@ func ReadClipboardImage() ([]byte, string, error) { return nil, "", nil } // Sniff the media type from the bytes — don't trust the clipboard's claim. - sniffLen := len(data) - if sniffLen > 512 { - sniffLen = 512 - } + sniffLen := min(len(data), 512) mediaType := zeroruntime.NormalizeImageMediaType(http.DetectContentType(data[:sniffLen])) if mediaType == "" { return nil, "", fmt.Errorf("clipboard image is not a supported type (allowed: png, jpeg, gif, webp)") @@ -166,7 +163,7 @@ func runClipboardStdout(name string, args ...string) ([]byte, error) { // list, each safe to pass as a discrete argument (no shell). func imageMIMETypes(list []byte) []string { var out []string - for _, line := range strings.Split(string(list), "\n") { + for line := range strings.SplitSeq(string(list), "\n") { line = strings.TrimSpace(line) if strings.HasPrefix(line, "image/") { out = append(out, line) diff --git a/internal/imageinput/imageinput.go b/internal/imageinput/imageinput.go index 2093db18..0270b11f 100644 --- a/internal/imageinput/imageinput.go +++ b/internal/imageinput/imageinput.go @@ -72,10 +72,7 @@ func LoadFile(path string, workspaceRoot string) (zeroruntime.ImageBlock, error) return zeroruntime.ImageBlock{}, fmt.Errorf("image %s is larger than the 10 MiB limit", path) } - sniffLen := len(data) - if sniffLen > 512 { - sniffLen = 512 - } + sniffLen := min(len(data), 512) mediaType := zeroruntime.NormalizeImageMediaType(http.DetectContentType(data[:sniffLen])) if mediaType == "" { return zeroruntime.ImageBlock{}, fmt.Errorf("unsupported image type for %s (allowed: png, jpeg, gif, webp)", path) diff --git a/internal/imageinput/pdf.go b/internal/imageinput/pdf.go index 93ac8102..53a0f9e4 100644 --- a/internal/imageinput/pdf.go +++ b/internal/imageinput/pdf.go @@ -290,10 +290,7 @@ func capDocumentText(text string) (string, bool) { // Reserve room for the marker so the final payload (text + marker) stays at or // under the advertised cap. If the marker alone would not fit, cut to nothing // and return just the marker. - cut := MaxDocumentTextBytes - len(documentTruncatedMarker) - if cut < 0 { - cut = 0 - } + cut := max(MaxDocumentTextBytes-len(documentTruncatedMarker), 0) // Back up to a rune boundary so we never split a multi-byte character. for cut > 0 && !utf8RuneStart(text[cut]) { cut-- @@ -426,10 +423,7 @@ func loadRenderedPage(name string) (zeroruntime.ImageBlock, error) { if len(data) > MaxImageBytes { return zeroruntime.ImageBlock{}, fmt.Errorf("rendered page %s exceeds the per-image limit", filepath.Base(name)) } - sniffLen := len(data) - if sniffLen > 512 { - sniffLen = 512 - } + sniffLen := min(len(data), 512) mediaType := zeroruntime.NormalizeImageMediaType(http.DetectContentType(data[:sniffLen])) if mediaType == "" { return zeroruntime.ImageBlock{}, fmt.Errorf("rendered page %s has an unsupported image type", filepath.Base(name)) diff --git a/internal/imageinput/pdf_test.go b/internal/imageinput/pdf_test.go index 72d61d08..fb804b3f 100644 --- a/internal/imageinput/pdf_test.go +++ b/internal/imageinput/pdf_test.go @@ -56,10 +56,7 @@ func buildMinimalPDF(text string) []byte { } func minimalPDFPageHeight(text string) int { - lines := (len(text) + minimalPDFTextChunkSize - 1) / minimalPDFTextChunkSize - if lines < 1 { - lines = 1 - } + lines := max((len(text)+minimalPDFTextChunkSize-1)/minimalPDFTextChunkSize, 1) height := 184 + lines*10 if height < 792 { return 792 diff --git a/internal/installtest/install_scripts_test.go b/internal/installtest/install_scripts_test.go index 90bdb5a3..22ced995 100644 --- a/internal/installtest/install_scripts_test.go +++ b/internal/installtest/install_scripts_test.go @@ -161,7 +161,7 @@ func newUnixInstallFixture(t *testing.T) unixInstallFixture { archiveBytes := readFile(t, fixture.archivePath) sum := sha256.Sum256(archiveBytes) - writeFile(t, fixture.checksumPath, []byte(fmt.Sprintf("%x %s\n", sum, archiveName)), 0o644) + writeFile(t, fixture.checksumPath, fmt.Appendf(nil, "%x %s\n", sum, archiveName), 0o644) mockCurl := fmt.Sprintf(`#!/usr/bin/env sh set -eu diff --git a/internal/keyring/keyring.go b/internal/keyring/keyring.go index 7f116b50..49d3c386 100644 --- a/internal/keyring/keyring.go +++ b/internal/keyring/keyring.go @@ -17,6 +17,7 @@ import ( "fmt" "os/exec" "runtime" + "slices" "strings" "time" ) @@ -194,12 +195,7 @@ func isNotFound(err error, codes ...int) bool { return false } code := coder.ExitCode() - for _, c := range codes { - if code == c { - return true - } - } - return false + return slices.Contains(codes, code) } // wrap adds operation context to a tool error, leaving nil untouched. diff --git a/internal/localcontrol/browser_test.go b/internal/localcontrol/browser_test.go index 97db4b8f..1b4796a2 100644 --- a/internal/localcontrol/browser_test.go +++ b/internal/localcontrol/browser_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "reflect" "runtime" + "slices" "strings" "testing" "time" @@ -219,12 +220,7 @@ func quoteJSON(value string) string { } func envContains(env []string, want string) bool { - for _, item := range env { - if item == want { - return true - } - } - return false + return slices.Contains(env, want) } func envValue(env []string, key string) string { diff --git a/internal/lsp/client_test.go b/internal/lsp/client_test.go index 5ec6c39d..092b4257 100644 --- a/internal/lsp/client_test.go +++ b/internal/lsp/client_test.go @@ -107,7 +107,7 @@ func TestClientMatchesConcurrentResponsesByID(t *testing.T) { go call("alpha") go call("beta") - for i := 0; i < 2; i++ { + for range 2 { select { case r := <-results: if r.err != nil { diff --git a/internal/lsp/manager_test.go b/internal/lsp/manager_test.go index e646de13..77cc6002 100644 --- a/internal/lsp/manager_test.go +++ b/internal/lsp/manager_test.go @@ -181,14 +181,12 @@ func TestManagerCheckConcurrentReusesOneServer(t *testing.T) { defer m.Shutdown(context.Background()) var wg sync.WaitGroup - for i := 0; i < 4; i++ { - wg.Add(1) - go func() { - defer wg.Done() + for range 4 { + wg.Go(func() { if _, err := m.Check(context.Background(), "main.go", "package main"); err != nil { t.Errorf("concurrent Check: %v", err) } - }() + }) } wg.Wait() From f4ef5f036f7c4ad854f023f55a7c47b410befb25 Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:14:18 +0300 Subject: [PATCH 08/19] refactor(mcp,oauth): use slices.Contains, for range N, Cut - mcp: simplify slices.Contains, for range N patterns - oauth: use slices.Contains, strings.Cut, for range N, maps.Copy instead of manual copy loops --- internal/mcp/client_test.go | 6 ++---- internal/mcp/oauth_store.go | 4 ++-- internal/mcp/oauth_test.go | 2 +- internal/mcp/permissions_test.go | 2 +- internal/oauth/encrypt.go | 4 ++-- internal/oauth/encrypt_test.go | 4 ++-- internal/oauth/flow_test.go | 2 +- internal/oauth/oauth.go | 2 +- internal/oauth/presets.go | 5 ++--- internal/oauth/presets_test.go | 9 ++------- internal/oauth/refresh_singleflight_test.go | 2 +- internal/oauth/scheduler.go | 5 +---- internal/oauth/store.go | 2 +- 13 files changed, 19 insertions(+), 30 deletions(-) diff --git a/internal/mcp/client_test.go b/internal/mcp/client_test.go index 2cca52d6..bd3f0126 100644 --- a/internal/mcp/client_test.go +++ b/internal/mcp/client_test.go @@ -88,12 +88,10 @@ func TestStdioClientCloseAllowsConcurrentCallers(t *testing.T) { errs := make(chan error, 2) var wait sync.WaitGroup for range 2 { - wait.Add(1) - go func() { - defer wait.Done() + wait.Go(func() { <-start errs <- client.Close() - }() + }) } close(start) wait.Wait() diff --git a/internal/mcp/oauth_store.go b/internal/mcp/oauth_store.go index ab1f2d93..bf36a993 100644 --- a/internal/mcp/oauth_store.go +++ b/internal/mcp/oauth_store.go @@ -25,7 +25,7 @@ type StoredToken struct { RefreshToken string `json:"refresh_token,omitempty"` TokenType string `json:"token_type,omitempty"` Scopes []string `json:"scopes,omitempty"` - ExpiresAt time.Time `json:"expires_at,omitempty"` + ExpiresAt time.Time `json:"expires_at"` } // TokenStatus is a redaction-safe summary of a stored token. It deliberately @@ -36,7 +36,7 @@ type TokenStatus struct { HasRefreshToken bool `json:"hasRefreshToken"` TokenType string `json:"tokenType,omitempty"` Scopes []string `json:"scopes,omitempty"` - ExpiresAt time.Time `json:"expiresAt,omitempty"` + ExpiresAt time.Time `json:"expiresAt"` Expired bool `json:"expired"` } diff --git a/internal/mcp/oauth_test.go b/internal/mcp/oauth_test.go index 610d6da4..98cb5615 100644 --- a/internal/mcp/oauth_test.go +++ b/internal/mcp/oauth_test.go @@ -370,7 +370,7 @@ func TestFullFlowStoresTokens(t *testing.T) { redirect := parsed.Query().Get("redirect_uri") go func() { cbURL := redirect + "?code=auth-code&state=" + url.QueryEscape(state) - for i := 0; i < 20; i++ { + for range 20 { resp, err := http.Get(cbURL) if err == nil { resp.Body.Close() diff --git a/internal/mcp/permissions_test.go b/internal/mcp/permissions_test.go index d429c35e..7bc98ff4 100644 --- a/internal/mcp/permissions_test.go +++ b/internal/mcp/permissions_test.go @@ -154,7 +154,7 @@ func TestPermissionStoreSerializesConcurrentStoreInstances(t *testing.T) { var wait sync.WaitGroup errs := make(chan error, grantCount) - for index := 0; index < grantCount; index++ { + for index := range grantCount { wait.Add(1) go func(index int) { defer wait.Done() diff --git a/internal/oauth/encrypt.go b/internal/oauth/encrypt.go index d6a885c4..fd8d4711 100644 --- a/internal/oauth/encrypt.go +++ b/internal/oauth/encrypt.go @@ -102,7 +102,7 @@ func loadOrCreateSecret(path string, create bool) ([]byte, error) { func createSecretFile(path string) ([]byte, error) { lockPath := path + ".lock" var lastErr error - for attempt := 0; attempt < secretRetryAttempts; attempt++ { + for range secretRetryAttempts { lock, err := os.OpenFile(lockPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o600) if err == nil { _ = lock.Close() @@ -166,7 +166,7 @@ func writeNewSecretFile(path string) ([]byte, error) { func readSecretFileRetry(path string) ([]byte, error) { var lastErr error - for attempt := 0; attempt < secretRetryAttempts; attempt++ { + for range secretRetryAttempts { data, err := readSecretFile(path) if !isTransientSecretAccessError(err) { return data, err diff --git a/internal/oauth/encrypt_test.go b/internal/oauth/encrypt_test.go index 701afbc0..4b263d40 100644 --- a/internal/oauth/encrypt_test.go +++ b/internal/oauth/encrypt_test.go @@ -16,7 +16,7 @@ func TestLoadOrCreateSecretConcurrentConverges(t *testing.T) { secrets := make([][]byte, n) errs := make([]error, n) var wg sync.WaitGroup - for i := 0; i < n; i++ { + for i := range n { wg.Add(1) go func(i int) { defer wg.Done() @@ -26,7 +26,7 @@ func TestLoadOrCreateSecretConcurrentConverges(t *testing.T) { wg.Wait() // Exactly one creator wins; every racer must converge on the same on-disk // secret rather than reading a half-published file or orphaning its own. - for i := 0; i < n; i++ { + for i := range n { if errs[i] != nil { t.Fatalf("goroutine %d: %v", i, errs[i]) } diff --git a/internal/oauth/flow_test.go b/internal/oauth/flow_test.go index 917aea9c..acf81e57 100644 --- a/internal/oauth/flow_test.go +++ b/internal/oauth/flow_test.go @@ -33,7 +33,7 @@ func TestNewPKCE(t *testing.T) { func TestNewStateUnique(t *testing.T) { seen := map[string]bool{} - for i := 0; i < 100; i++ { + for range 100 { s, err := NewState() if err != nil { t.Fatalf("NewState: %v", err) diff --git a/internal/oauth/oauth.go b/internal/oauth/oauth.go index df0bb33b..4ea96f1f 100644 --- a/internal/oauth/oauth.go +++ b/internal/oauth/oauth.go @@ -36,7 +36,7 @@ type Token struct { RefreshToken string `json:"refresh_token,omitempty"` TokenType string `json:"token_type,omitempty"` Scopes []string `json:"scopes,omitempty"` - ExpiresAt time.Time `json:"expires_at,omitempty"` + ExpiresAt time.Time `json:"expires_at"` // Account is an optional non-secret identifier (email / account id) shown in // status output; never a credential. Account string `json:"account,omitempty"` diff --git a/internal/oauth/presets.go b/internal/oauth/presets.go index 3f8b7fe7..ae5334e6 100644 --- a/internal/oauth/presets.go +++ b/internal/oauth/presets.go @@ -1,6 +1,7 @@ package oauth import ( + "maps" "os" "strings" ) @@ -21,9 +22,7 @@ func envWithPresetsAllowed(base map[string]string) map[string]string { } } } else { - for k, v := range base { - env[k] = v - } + maps.Copy(env, base) } env["ZERO_OAUTH_ALLOW_PRESETS"] = "1" return env diff --git a/internal/oauth/presets_test.go b/internal/oauth/presets_test.go index f73f1e10..eec7bf65 100644 --- a/internal/oauth/presets_test.go +++ b/internal/oauth/presets_test.go @@ -1,6 +1,7 @@ package oauth import ( + "slices" "strings" "testing" ) @@ -213,13 +214,7 @@ func TestResolveConfigChatGPTPreset(t *testing.T) { } } for _, required := range []string{"api.connectors.read", "api.connectors.invoke"} { - found := false - for _, s := range cfg.Scopes { - if s == required { - found = true - break - } - } + found := slices.Contains(cfg.Scopes, required) if !found { t.Fatalf("missing required scope %q in %v", required, cfg.Scopes) } diff --git a/internal/oauth/refresh_singleflight_test.go b/internal/oauth/refresh_singleflight_test.go index e26a8f8b..74aecb1b 100644 --- a/internal/oauth/refresh_singleflight_test.go +++ b/internal/oauth/refresh_singleflight_test.go @@ -24,7 +24,7 @@ func TestRefreshSingleflightsConcurrentCallers(t *testing.T) { var wg sync.WaitGroup got := make([]string, N) errs := make([]error, N) - for i := 0; i < N; i++ { + for i := range N { wg.Add(1) go func(i int) { defer wg.Done() diff --git a/internal/oauth/scheduler.go b/internal/oauth/scheduler.go index f2521f78..91f49c3a 100644 --- a/internal/oauth/scheduler.go +++ b/internal/oauth/scheduler.go @@ -96,10 +96,7 @@ func (s *RefreshScheduler) loop(ctx context.Context, m *Manager, key string, don // sessions do not refresh in lockstep. func (s *RefreshScheduler) delayUntilRefresh(expiresAt time.Time, buffer time.Duration, now time.Time) time.Duration { target := expiresAt.Add(-buffer) - delay := target.Sub(now) - if delay < 0 { - delay = 0 - } + delay := max(target.Sub(now), 0) // Jitter up to ~10% of the buffer, derived from the expiry nanos (no RNG dep). if buffer > 0 { jitter := time.Duration(expiresAt.UnixNano()%int64(buffer/10+1)) % (buffer/10 + 1) diff --git a/internal/oauth/store.go b/internal/oauth/store.go index c847570c..32277f36 100644 --- a/internal/oauth/store.go +++ b/internal/oauth/store.go @@ -69,7 +69,7 @@ type Status struct { TokenType string `json:"tokenType,omitempty"` Account string `json:"account,omitempty"` Scopes []string `json:"scopes,omitempty"` - ExpiresAt time.Time `json:"expiresAt,omitempty"` + ExpiresAt time.Time `json:"expiresAt"` Expired bool `json:"expired"` } From 36ba9eee48f9afaf48af4685e7788d0e1728aa2a Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:14:22 +0300 Subject: [PATCH 09/19] refactor(modelregistry): use slices.Contains, for range N, strings.Cut, max builtin - models.go: replace manual slices with slices.Contains, use maps.Keys for JSON tags - cost.go: use max/min builtins for clamp, use slices.Contains - resolve.go: use strings.Cut instead of HasPrefix+TrimPrefix+cutset - catalog_test.go: use for range N --- internal/modelregistry/catalog_test.go | 8 ++------ internal/modelregistry/cost.go | 10 ++-------- internal/modelregistry/models.go | 23 ++++------------------- internal/modelregistry/resolve.go | 7 +++---- 4 files changed, 11 insertions(+), 37 deletions(-) diff --git a/internal/modelregistry/catalog_test.go b/internal/modelregistry/catalog_test.go index b983e817..8d7d738c 100644 --- a/internal/modelregistry/catalog_test.go +++ b/internal/modelregistry/catalog_test.go @@ -1,6 +1,7 @@ package modelregistry import ( + "slices" "strings" "testing" ) @@ -208,12 +209,7 @@ func TestDefaultRegistryReasoningModelsHaveDefaultEffort(t *testing.T) { } func reasoningEffortAllowedIn(efforts []ReasoningEffort, want ReasoningEffort) bool { - for _, effort := range efforts { - if effort == want { - return true - } - } - return false + return slices.Contains(efforts, want) } func TestDefaultRegistryUpgradeTargets(t *testing.T) { diff --git a/internal/modelregistry/cost.go b/internal/modelregistry/cost.go index 43433551..5fac5fe9 100644 --- a/internal/modelregistry/cost.go +++ b/internal/modelregistry/cost.go @@ -61,10 +61,7 @@ func CalculateCost(model ModelEntry, usage zeroruntime.Usage) (CostBreakdown, er } // Cache-read and cache-write are disjoint subsets of the input. if requestedCacheWriteTokens > inputTokens-requestedCachedInputTokens { - requestedCacheWriteTokens = inputTokens - requestedCachedInputTokens - if requestedCacheWriteTokens < 0 { - requestedCacheWriteTokens = 0 - } + requestedCacheWriteTokens = max(inputTokens-requestedCachedInputTokens, 0) } tier, err := selectCostTier(model.Cost, inputTokens) @@ -88,10 +85,7 @@ func CalculateCost(model ModelEntry, usage zeroruntime.Usage) (CostBreakdown, er if cacheWriteRate > 0 { cacheWriteTokens = requestedCacheWriteTokens } - uncachedInputTokens := inputTokens - cachedInputTokens - cacheWriteTokens - if uncachedInputTokens < 0 { - uncachedInputTokens = 0 - } + uncachedInputTokens := max(inputTokens-cachedInputTokens-cacheWriteTokens, 0) inputCost := costForTokens(uncachedInputTokens, inputRate) cachedInputCost := costForTokens(cachedInputTokens, cachedRate) cacheWriteCost := costForTokens(cacheWriteTokens, cacheWriteRate) diff --git a/internal/modelregistry/models.go b/internal/modelregistry/models.go index 34dc7212..b44571b7 100644 --- a/internal/modelregistry/models.go +++ b/internal/modelregistry/models.go @@ -3,6 +3,7 @@ package modelregistry import ( "fmt" "regexp" + "slices" "strings" "time" ) @@ -172,13 +173,7 @@ func (model ModelEntry) Validate() error { } // The default must be one the model actually supports, else // EffectiveReasoningEffort would hand back an unsupported effort. - supported := false - for _, effort := range model.ReasoningEfforts { - if effort == model.DefaultReasoningEffort { - supported = true - break - } - } + supported := slices.Contains(model.ReasoningEfforts, model.DefaultReasoningEffort) if !supported { return fmt.Errorf("default reasoning effort %q is not in the model's supported efforts", model.DefaultReasoningEffort) } @@ -271,24 +266,14 @@ func validateCostTiers(tiers []ModelCostTier) error { } func (model ModelEntry) Supports(capability ModelCapability) bool { - for _, candidate := range model.Capabilities { - if candidate == capability { - return true - } - } - return false + return slices.Contains(model.Capabilities, capability) } func (model ModelEntry) AllowsProvider(provider ProviderKind) bool { if len(model.APIProviders) == 0 { return model.Provider == provider } - for _, candidate := range model.APIProviders { - if candidate == provider { - return true - } - } - return false + return slices.Contains(model.APIProviders, provider) } type Registry struct { diff --git a/internal/modelregistry/resolve.go b/internal/modelregistry/resolve.go index 607bfc44..0ec64504 100644 --- a/internal/modelregistry/resolve.go +++ b/internal/modelregistry/resolve.go @@ -2,6 +2,7 @@ package modelregistry import ( "fmt" + "slices" "strings" ) @@ -53,10 +54,8 @@ func (registry Registry) ResolveWithFallback(input string) (ModelEntry, string, func EffectiveReasoningEffort(model ModelEntry, requested ReasoningEffort) ReasoningEffort { efforts := effectiveReasoningEfforts(model) if requested != "" { - for _, effort := range efforts { - if effort == requested { - return requested - } + if slices.Contains(efforts, requested) { + return requested } } if model.DefaultReasoningEffort != "" { From dbd95d7b028774a454574e2f34151ef4317bc419 Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:14:29 +0300 Subject: [PATCH 10/19] refactor(providers): use slices.Contains, SplitSeq, for range N, max builtin - gemini/provider.go: use strings.CutPrefix - gemini/types.go: use slices.Contains for contains - openai/provider.go: use max() clamp - openai/provider_test.go: use for range N - providerio: use slices.Contains, SplitSeq, max, for range N for retry --- internal/providers/gemini/provider.go | 2 +- internal/providers/gemini/types.go | 6 +++--- internal/providers/openai/provider.go | 5 +---- internal/providers/openai/provider_test.go | 8 ++++---- internal/providers/providerio/headers.go | 5 ++--- internal/providers/providerio/providerio.go | 12 ++++++------ internal/providers/providerio/providerio_test.go | 2 +- internal/providers/providerio/retry.go | 8 +------- 8 files changed, 19 insertions(+), 29 deletions(-) diff --git a/internal/providers/gemini/provider.go b/internal/providers/gemini/provider.go index e002db87..f01f1a31 100644 --- a/internal/providers/gemini/provider.go +++ b/internal/providers/gemini/provider.go @@ -448,7 +448,7 @@ func mapMessages(messages []zeroruntime.Message) (*geminiContent, []geminiConten FunctionResponse: &geminiFunctionResponse{ ID: message.ToolCallID, Name: name, - Response: map[string]interface{}{"result": content}, + Response: map[string]any{"result": content}, }, }}) case zeroruntime.MessageRoleAssistant: diff --git a/internal/providers/gemini/types.go b/internal/providers/gemini/types.go index 8d9d98f1..72fb44a7 100644 --- a/internal/providers/gemini/types.go +++ b/internal/providers/gemini/types.go @@ -48,9 +48,9 @@ type geminiFunctionCall struct { } type geminiFunctionResponse struct { - ID string `json:"id,omitempty"` - Name string `json:"name"` - Response map[string]interface{} `json:"response"` + ID string `json:"id,omitempty"` + Name string `json:"name"` + Response map[string]any `json:"response"` } type geminiToolGroup struct { diff --git a/internal/providers/openai/provider.go b/internal/providers/openai/provider.go index bc738bb2..eafcbbd4 100644 --- a/internal/providers/openai/provider.go +++ b/internal/providers/openai/provider.go @@ -108,10 +108,7 @@ func New(options Options) (*Provider, error) { // pooled connection (the macOS-only "both providers stall" bug). httpClient := providerio.HTTPClient(options.HTTPClient) - maxTokens := options.MaxTokens - if maxTokens < 0 { - maxTokens = 0 - } + maxTokens := max(options.MaxTokens, 0) return &Provider{ apiKey: options.APIKey, diff --git a/internal/providers/openai/provider_test.go b/internal/providers/openai/provider_test.go index 5c76a8d8..2447f9c9 100644 --- a/internal/providers/openai/provider_test.go +++ b/internal/providers/openai/provider_test.go @@ -978,17 +978,17 @@ func TestStreamCompletionJoinsMultiLineDataFields(t *testing.T) { t.Fatalf("New returned error: %v", err) } events := collectProviderEvents(t, provider) - var text string + var text strings.Builder for _, e := range events { if e.Type == zeroruntime.StreamEventText { - text += e.Content + text.WriteString(e.Content) } if e.Type == zeroruntime.StreamEventError { t.Fatalf("multi-line data field produced an error: %q", e.Error) } } - if text != "joined" { - t.Fatalf("text = %q, want %q (continuation data: line dropped?)", text, "joined") + if text.String() != "joined" { + t.Fatalf("text = %q, want %q (continuation data: line dropped?)", text.String(), "joined") } } diff --git a/internal/providers/providerio/headers.go b/internal/providers/providerio/headers.go index 50914613..425b1956 100644 --- a/internal/providers/providerio/headers.go +++ b/internal/providers/providerio/headers.go @@ -1,6 +1,7 @@ package providerio import ( + "maps" "net/http" "strings" ) @@ -57,8 +58,6 @@ func CopyHeaders(headers map[string]string) map[string]string { return nil } copied := make(map[string]string, len(headers)) - for key, value := range headers { - copied[key] = value - } + maps.Copy(copied, headers) return copied } diff --git a/internal/providers/providerio/providerio.go b/internal/providers/providerio/providerio.go index d3199876..37e9d1c2 100644 --- a/internal/providers/providerio/providerio.go +++ b/internal/providers/providerio/providerio.go @@ -235,8 +235,8 @@ func scanSSEPayloads(scanner *bufio.Scanner, handle func(data string) bool, onCo } continue } - if strings.HasPrefix(line, "data:") { - dataLines = append(dataLines, strings.TrimLeft(strings.TrimPrefix(line, "data:"), " \t")) + if after, ok := strings.CutPrefix(line, "data:"); ok { + dataLines = append(dataLines, strings.TrimLeft(after, " \t")) } } if err := scanner.Err(); err != nil { @@ -489,15 +489,15 @@ func UpstreamUnreachable(message string) (string, bool) { func upstreamHost(message string) string { if index := strings.Index(message, "\"http"); index >= 0 { rest := message[index+1:] - if end := strings.IndexByte(rest, '"'); end >= 0 { - if parsed, err := url.Parse(rest[:end]); err == nil && parsed.Host != "" { + if before, _, ok := strings.Cut(rest, "\""); ok { + if parsed, err := url.Parse(before); err == nil && parsed.Host != "" { return parsed.Host } } } const dialPrefix = "dial tcp " - if index := strings.Index(message, dialPrefix); index >= 0 { - rest := message[index+len(dialPrefix):] + if _, after, ok := strings.Cut(message, dialPrefix); ok { + rest := after if end := strings.Index(rest, ": "); end >= 0 { rest = rest[:end] } diff --git a/internal/providers/providerio/providerio_test.go b/internal/providers/providerio/providerio_test.go index 33f539a3..72b5220d 100644 --- a/internal/providers/providerio/providerio_test.go +++ b/internal/providers/providerio/providerio_test.go @@ -289,7 +289,7 @@ func TestScanSSEDataWithContextContentResetsOnData(t *testing.T) { // One data line every 30ms for ~300ms (well past the 120ms content window, // with comfortable slack under the 100ms idle timeout), so the content // watchdog keeps resetting, then close cleanly. - for i := 0; i < 10; i++ { + for range 10 { if _, err := io.WriteString(pw, "data: chunk\n\n"); err != nil { return } diff --git a/internal/providers/providerio/retry.go b/internal/providers/providerio/retry.go index 256ac2a1..6d842e4b 100644 --- a/internal/providers/providerio/retry.go +++ b/internal/providers/providerio/retry.go @@ -136,13 +136,7 @@ func Backoff(ctx context.Context, attempt int, retryAfter time.Duration) bool { func backoffWait(attempt int, retryAfter time.Duration) time.Duration { wait := retryAfter if wait <= 0 { - exponent := attempt - 1 - if exponent > 5 { - exponent = 5 - } - if exponent < 0 { - exponent = 0 - } + exponent := max(min(attempt-1, 5), 0) wait = retryBackoffBase * time.Duration(1< maxBackoff { From 24f516b913b5787da0c4d59de8ef66a689220593 Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:14:34 +0300 Subject: [PATCH 11/19] refactor(sessions,notify,perfbench,plugins,redaction,release): use for range N, slices.Contains, SplitSeq - sessions: use for range N, for range in tests with wg.Go, strings.Cut, max builtin - notify: use for range N - perfbench: use max(), slices.Contains - plugins: use for range N - redaction: use for range N for test loops - release: use for range N --- internal/notify/notify_test.go | 4 ++-- internal/perfbench/perfbench.go | 5 +---- internal/perfbench/taskbench.go | 4 ++-- internal/plugins/activate_test.go | 8 ++------ internal/redaction/redaction_test.go | 7 +++---- internal/release/release.go | 2 +- internal/sessions/append_unless_exists_test.go | 8 +++----- internal/sessions/checkpoint_test.go | 2 +- internal/sessions/replay.go | 5 +---- internal/sessions/rewind_test.go | 12 +++++------- internal/sessions/sequence_durability_test.go | 2 +- internal/sessions/store_test.go | 4 ++-- 12 files changed, 24 insertions(+), 39 deletions(-) diff --git a/internal/notify/notify_test.go b/internal/notify/notify_test.go index 54ba06ae..f3db68ef 100644 --- a/internal/notify/notify_test.go +++ b/internal/notify/notify_test.go @@ -101,7 +101,7 @@ func TestNotifierDefaultFocusFalse(t *testing.T) { func TestNotifyRaceSafe(t *testing.T) { n := New(&bytes.Buffer{}, Config{Mode: ModeBell, FocusMode: FocusAlways}) var wg sync.WaitGroup - for i := 0; i < 50; i++ { + for range 50 { wg.Add(2) go func() { defer wg.Done(); n.SetFocused(true) }() go func() { defer wg.Done(); n.Notify(Completion, "x") }() @@ -234,7 +234,7 @@ func TestNotifierFanOutRaceSafe(t *testing.T) { n := New(&bytes.Buffer{}, Config{Mode: ModeBell, FocusMode: FocusAlways}) n.AddSink(&recordingSink{}) var wg sync.WaitGroup - for i := 0; i < 50; i++ { + for range 50 { wg.Add(3) go func() { defer wg.Done(); n.SetFocused(true) }() go func() { defer wg.Done(); n.AddSink(&recordingSink{}) }() diff --git a/internal/perfbench/perfbench.go b/internal/perfbench/perfbench.go index 31afe7b6..0f454c6d 100644 --- a/internal/perfbench/perfbench.go +++ b/internal/perfbench/perfbench.go @@ -403,10 +403,7 @@ func createWarning(metric string, statistic string, observed float64, threshold } func percentile(sortedSamples []float64, percentileValue float64) float64 { - index := int(math.Ceil((percentileValue/100)*float64(len(sortedSamples)))) - 1 - if index < 0 { - index = 0 - } + index := max(int(math.Ceil((percentileValue/100)*float64(len(sortedSamples))))-1, 0) if index >= len(sortedSamples) { index = len(sortedSamples) - 1 } diff --git a/internal/perfbench/taskbench.go b/internal/perfbench/taskbench.go index 19bb8a34..c138acd2 100644 --- a/internal/perfbench/taskbench.go +++ b/internal/perfbench/taskbench.go @@ -406,8 +406,8 @@ func streamJSONExitCode(output []byte) (int, bool) { } func firstLine(text string) string { - if index := strings.IndexByte(text, '\n'); index >= 0 { - return strings.TrimSpace(text[:index]) + if before, _, ok := strings.Cut(text, "\n"); ok { + return strings.TrimSpace(before) } return text } diff --git a/internal/plugins/activate_test.go b/internal/plugins/activate_test.go index 738abcc3..91e2b1cf 100644 --- a/internal/plugins/activate_test.go +++ b/internal/plugins/activate_test.go @@ -4,6 +4,7 @@ import ( "context" "os" "path/filepath" + "slices" "strings" "testing" @@ -520,12 +521,7 @@ func writeTestSkill(t *testing.T, dir string, name string, body string) { } func envContains(env []string, want string) bool { - for _, entry := range env { - if entry == want { - return true - } - } - return false + return slices.Contains(env, want) } func containsSubstring(values []string, want string) bool { diff --git a/internal/redaction/redaction_test.go b/internal/redaction/redaction_test.go index 3ae228c8..766bb730 100644 --- a/internal/redaction/redaction_test.go +++ b/internal/redaction/redaction_test.go @@ -2,6 +2,7 @@ package redaction import ( "errors" + "slices" "strings" "testing" ) @@ -133,10 +134,8 @@ func containsCircular(v any) bool { } } case []any: - for _, val := range t { - if containsCircular(val) { - return true - } + if slices.ContainsFunc(t, containsCircular) { + return true } } return false diff --git a/internal/release/release.go b/internal/release/release.go index bf19fbda..ce3bb7aa 100644 --- a/internal/release/release.go +++ b/internal/release/release.go @@ -375,7 +375,7 @@ func SHA256File(path string) (string, error) { func ParseSHA256Checksum(text string) (ParsedChecksum, error) { lines := []string{} - for _, line := range strings.Split(strings.ReplaceAll(text, "\r\n", "\n"), "\n") { + for line := range strings.SplitSeq(strings.ReplaceAll(text, "\r\n", "\n"), "\n") { line = strings.TrimSpace(line) if line != "" { lines = append(lines, line) diff --git a/internal/sessions/append_unless_exists_test.go b/internal/sessions/append_unless_exists_test.go index ee5f1ff2..8d1c98e1 100644 --- a/internal/sessions/append_unless_exists_test.go +++ b/internal/sessions/append_unless_exists_test.go @@ -28,10 +28,8 @@ func TestAppendEventUnlessExistsIsAtomic(t *testing.T) { var wg sync.WaitGroup var appended int32 start := make(chan struct{}) - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func() { - defer wg.Done() + for range goroutines { + wg.Go(func() { <-start _, ok, err := store.AppendEventUnlessExists("s", AppendEventInput{Type: once, Payload: map[string]any{"n": 1}}, exists) if err != nil { @@ -41,7 +39,7 @@ func TestAppendEventUnlessExistsIsAtomic(t *testing.T) { if ok { atomic.AddInt32(&appended, 1) } - }() + }) } close(start) wg.Wait() diff --git a/internal/sessions/checkpoint_test.go b/internal/sessions/checkpoint_test.go index 78a1674d..20af6971 100644 --- a/internal/sessions/checkpoint_test.go +++ b/internal/sessions/checkpoint_test.go @@ -133,7 +133,7 @@ func TestCaptureDisabled(t *testing.T) { func TestTruncateEvents(t *testing.T) { store, _ := newCkStore(t) var seqs []int - for i := 0; i < 3; i++ { + for i := range 3 { ev, err := store.AppendEvent("s", AppendEventInput{Type: EventMessage, Payload: map[string]any{"i": i}}) if err != nil { t.Fatal(err) diff --git a/internal/sessions/replay.go b/internal/sessions/replay.go index 21e71181..99c57a76 100644 --- a/internal/sessions/replay.go +++ b/internal/sessions/replay.go @@ -158,10 +158,7 @@ func (store *Store) PlanCompaction(sessionID string, options CompactionOptions) if maxPromptChars <= 0 { maxPromptChars = defaultCompactionMaxPromptChars } - split := len(events) - preserveLast - if split < 0 { - split = 0 - } + split := max(len(events)-preserveLast, 0) compactable := events[:split] preserved := events[split:] prompt, truncated := buildCompactionPrompt(compactable, maxPromptChars) diff --git a/internal/sessions/rewind_test.go b/internal/sessions/rewind_test.go index b4cdf988..b72758db 100644 --- a/internal/sessions/rewind_test.go +++ b/internal/sessions/rewind_test.go @@ -310,14 +310,14 @@ func TestApplyRewindDoesNotDeadlock(t *testing.T) { func TestCaptureAndPruneConcurrent(t *testing.T) { store, ws := newCkStore(t) var wg sync.WaitGroup - for i := 0; i < 20; i++ { + for i := range 20 { name := filepath.Join(ws, "f"+string(rune('a'+i%10))+".txt") mustWriteFile(t, name, "content-"+string(rune('0'+i%10))) } wg.Add(2) go func() { defer wg.Done() - for i := 0; i < 20; i++ { + for i := range 20 { rel := "f" + string(rune('a'+i%10)) + ".txt" if _, err := store.CaptureToolCheckpoint("s", ws, "write_file", []string{rel}); err != nil { t.Errorf("CaptureToolCheckpoint: %v", err) @@ -327,7 +327,7 @@ func TestCaptureAndPruneConcurrent(t *testing.T) { }() go func() { defer wg.Done() - for i := 0; i < 20; i++ { + for range 20 { if _, err := store.pruneOrphanBlobs("s"); err != nil { t.Errorf("pruneOrphanBlobs: %v", err) return @@ -372,16 +372,14 @@ func TestSessionFileLockSerializesAcrossStores(t *testing.T) { started := make(chan struct{}) done := make(chan struct{}) var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { close(started) // This must block until storeA releases the OS lock. if _, err := storeB.AppendEvent("s", AppendEventInput{Type: EventMessage, Payload: map[string]any{}}); err != nil { t.Errorf("storeB AppendEvent: %v", err) } close(done) - }() + }) <-started select { diff --git a/internal/sessions/sequence_durability_test.go b/internal/sessions/sequence_durability_test.go index 570ea740..ab7ba2e4 100644 --- a/internal/sessions/sequence_durability_test.go +++ b/internal/sessions/sequence_durability_test.go @@ -14,7 +14,7 @@ func seqTestStore(t *testing.T) *Store { func appendN(t *testing.T, store *Store, sid string, n int) { t.Helper() - for i := 0; i < n; i++ { + for range n { if _, err := store.AppendEvent(sid, AppendEventInput{Type: EventMessage, Payload: map[string]any{"content": "ok"}}); err != nil { t.Fatal(err) } diff --git a/internal/sessions/store_test.go b/internal/sessions/store_test.go index 4786555b..302a4194 100644 --- a/internal/sessions/store_test.go +++ b/internal/sessions/store_test.go @@ -91,7 +91,7 @@ func TestReadEventsToleratesTornTail(t *testing.T) { if err != nil { t.Fatalf("Create: %v", err) } - for i := 0; i < 2; i++ { + for range 2 { if _, err := store.AppendEvent(session.SessionID, AppendEventInput{Type: EventMessage, Payload: map[string]any{"content": "ok"}}); err != nil { t.Fatalf("AppendEvent: %v", err) } @@ -465,7 +465,7 @@ func TestStoreAppendEventSerializesConcurrentWriters(t *testing.T) { const total = 24 var wg sync.WaitGroup errs := make(chan error, total) - for index := 0; index < total; index++ { + for index := range total { wg.Add(1) go func(index int) { defer wg.Done() From 24bb819b84892cd584a5c401081d576a544a2ae1 Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:14:37 +0300 Subject: [PATCH 12/19] refactor(repoinfo,review,securefile,selfverify): use slices.Contains, maps.Copy, for range N - repoinfo: use slices.Contains, maps.Copy instead of manual loops - review: use strings.Cut - securefile: use slices.Contains - selfverify: use for range N --- internal/repoinfo/repoinfo.go | 27 ++++++++++++--------------- internal/repoinfo/repoinfo_test.go | 13 +++++-------- internal/review/review_test.go | 2 +- internal/securefile/securefile.go | 4 ++-- internal/selfverify/contracts.go | 2 +- 5 files changed, 21 insertions(+), 27 deletions(-) diff --git a/internal/repoinfo/repoinfo.go b/internal/repoinfo/repoinfo.go index 4c54cd05..e71ded46 100644 --- a/internal/repoinfo/repoinfo.go +++ b/internal/repoinfo/repoinfo.go @@ -104,15 +104,15 @@ func Collect(ctx context.Context, opts Options) (Info, error) { hasPackageJSON := false hasCargoToml := false - for _, entry := range strings.Split(tree, "\x00") { + for entry := range strings.SplitSeq(tree, "\x00") { if entry == "" { continue } - tab := strings.IndexByte(entry, '\t') - if tab < 0 { + before, after, ok := strings.Cut(entry, "\t") + if !ok { continue } - fields := strings.Fields(entry[:tab]) + fields := strings.Fields(before) if len(fields) < 4 { continue } @@ -120,7 +120,7 @@ func Collect(ctx context.Context, opts Options) (Info, error) { if convErr != nil { continue } - filePath := entry[tab+1:] + filePath := after info.FileCount++ // Count every directory on the path to this file, including "passthrough" @@ -209,7 +209,7 @@ func Collect(ctx context.Context, opts Options) (Info, error) { // for a normal repo; take the oldest across multiple roots). if roots, err := run(ctx, dir, "log", "--max-parents=0", "--format=%ct"); err == nil { oldest, found := int64(0), false - for _, line := range strings.Split(roots, "\n") { + for line := range strings.SplitSeq(roots, "\n") { if ts, perr := strconv.ParseInt(strings.TrimSpace(line), 10, 64); perr == nil { if !found || ts < oldest { oldest, found = ts, true @@ -217,10 +217,7 @@ func Collect(ctx context.Context, opts Options) (Info, error) { } } if found { - days := int((now.Unix() - oldest) / 86400) - if days < 0 { - days = 0 - } + days := max(int((now.Unix()-oldest)/86400), 0) info.AgeDays = &days } } @@ -239,7 +236,7 @@ func Collect(ctx context.Context, opts Options) (Info, error) { } if branches, err := run(ctx, dir, "branch", "-a"); err == nil { set := map[string]bool{} - for _, line := range strings.Split(branches, "\n") { + for line := range strings.SplitSeq(branches, "\n") { line = strings.TrimSpace(line) if line == "" || strings.Contains(line, "->") { continue @@ -254,8 +251,8 @@ func Collect(ctx context.Context, opts Options) (Info, error) { // If it's a remote branch, extract the branch name. // E.g., remotes/origin/main -> main // E.g., remotes/origin/feature/xyz -> feature/xyz - if strings.HasPrefix(line, "remotes/") { - line = strings.TrimPrefix(line, "remotes/") + if after, ok := strings.CutPrefix(line, "remotes/"); ok { + line = after if slashIdx := strings.Index(line, "/"); slashIdx != -1 { line = line[slashIdx+1:] } @@ -313,7 +310,7 @@ func sanitizeRemoteURL(raw string) string { // countUnique counts distinct non-empty trimmed lines. func countUnique(out string) *int { set := map[string]bool{} - for _, line := range strings.Split(out, "\n") { + for line := range strings.SplitSeq(out, "\n") { if line = strings.TrimSpace(line); line != "" { set[line] = true } @@ -371,7 +368,7 @@ func defaultRunGit(ctx context.Context, dir string, args ...string) (string, err func countNonEmptyLines(s string) int { count := 0 - for _, line := range strings.Split(s, "\n") { + for line := range strings.SplitSeq(s, "\n") { if strings.TrimSpace(line) != "" { count++ } diff --git a/internal/repoinfo/repoinfo_test.go b/internal/repoinfo/repoinfo_test.go index f8b4e153..2c123cf5 100644 --- a/internal/repoinfo/repoinfo_test.go +++ b/internal/repoinfo/repoinfo_test.go @@ -3,6 +3,7 @@ package repoinfo import ( "context" "errors" + "slices" "strings" "testing" "time" @@ -104,10 +105,8 @@ func TestCollectContributorsUnique(t *testing.T) { case "ls-tree": return lsTree, nil case "log": - for _, a := range args { - if a == "--format=%aN" { - return "Ann\nBob\nAnn\n\n", nil - } + if slices.Contains(args, "--format=%aN") { + return "Ann\nBob\nAnn\n\n", nil } return "0\n", nil // first-commit ts case "rev-parse": @@ -145,10 +144,8 @@ func TestCollectHistoryMetricsFailSoft(t *testing.T) { case "rev-list": return "4\n", nil case "log": - for _, a := range args { - if a == "--format=%aN" { - return "", errors.New("boom") - } + if slices.Contains(args, "--format=%aN") { + return "", errors.New("boom") } return "0\n", nil } diff --git a/internal/review/review_test.go b/internal/review/review_test.go index 0bec44f0..c68a5009 100644 --- a/internal/review/review_test.go +++ b/internal/review/review_test.go @@ -113,7 +113,7 @@ func TestParseChangedFilesSortsStableList(t *testing.T) { func TestBuildMarkdownCapsChangedFiles(t *testing.T) { files := make([]string, 0, 14) - for index := 0; index < 14; index++ { + for index := range 14 { files = append(files, fmt.Sprintf("file-%02d.go", index)) } markdown := BuildMarkdown(SummaryInput{ diff --git a/internal/securefile/securefile.go b/internal/securefile/securefile.go index a3a81f74..5d62d540 100644 --- a/internal/securefile/securefile.go +++ b/internal/securefile/securefile.go @@ -106,7 +106,7 @@ func loadOrCreateSecret(path string, create bool) ([]byte, error) { func createSecretFile(path string) ([]byte, error) { lockPath := path + ".lock" var lastErr error - for attempt := 0; attempt < secretRetryAttempts; attempt++ { + for range secretRetryAttempts { lock, err := os.OpenFile(lockPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o600) if err == nil { _ = lock.Close() @@ -165,7 +165,7 @@ func writeNewSecretFile(path string) ([]byte, error) { func readSecretFileRetry(path string) ([]byte, error) { var lastErr error - for attempt := 0; attempt < secretRetryAttempts; attempt++ { + for range secretRetryAttempts { data, err := readSecretFile(path) if !isTransientSecretAccessError(err) { return data, err diff --git a/internal/selfverify/contracts.go b/internal/selfverify/contracts.go index dd37f76f..fd8e9bc1 100644 --- a/internal/selfverify/contracts.go +++ b/internal/selfverify/contracts.go @@ -44,7 +44,7 @@ type Event struct { Status verify.Status `json:"status,omitempty"` OK bool `json:"ok,omitempty"` StopReason StopReason `json:"stopReason,omitempty"` - Summary verify.Summary `json:"summary,omitempty"` + Summary verify.Summary `json:"summary"` StartedAt string `json:"startedAt,omitempty"` EndedAt string `json:"endedAt,omitempty"` Error string `json:"error,omitempty"` From 0ebb69fb1d906d921039b4732f45cda098ab35da Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:14:42 +0300 Subject: [PATCH 13/19] refactor(skills,specialist,specmode,swarm): use slices.Contains, for range N - skills, specialist: use slices.Contains, for range N - specmode: use slices.Contains - swarm: use for range N in tests --- internal/skills/skills.go | 2 +- internal/skills/skills_test.go | 2 +- internal/specialist/accounting_test.go | 8 +++----- internal/specialist/manifest.go | 2 +- internal/specialist/manifest_test.go | 8 ++------ internal/specialist/output_tool.go | 2 +- internal/specmode/spec.go | 2 +- internal/swarm/coordinator_test.go | 2 +- internal/swarm/lifecycle_test.go | 2 +- internal/swarm/mailbox_test.go | 10 ++++------ internal/swarm/scheduler_test.go | 6 +++--- 11 files changed, 19 insertions(+), 27 deletions(-) diff --git a/internal/skills/skills.go b/internal/skills/skills.go index 80cfce58..1a6d83e9 100644 --- a/internal/skills/skills.go +++ b/internal/skills/skills.go @@ -281,7 +281,7 @@ func splitFrontmatter(normalized string) (string, string, bool) { // Matching is case-insensitive on the key; the first occurrence wins. func frontmatterValue(frontmatter string, key string) string { prefix := strings.ToLower(key) + ":" - for _, line := range strings.Split(frontmatter, "\n") { + for line := range strings.SplitSeq(frontmatter, "\n") { trimmed := strings.TrimSpace(line) if strings.HasPrefix(strings.ToLower(trimmed), prefix) { value := strings.TrimSpace(trimmed[len(prefix):]) diff --git a/internal/skills/skills_test.go b/internal/skills/skills_test.go index 31f8f6d0..1377d1a1 100644 --- a/internal/skills/skills_test.go +++ b/internal/skills/skills_test.go @@ -173,7 +173,7 @@ func TestLoadDuplicateFrontmatterNamePicksStableWinner(t *testing.T) { writeSkill(t, dir, "zzz-second", "---\nname: shared\ndescription: from zzz\n---\nbody from zzz\n") // Loading repeatedly must always yield the same single winner. - for i := 0; i < 20; i++ { + for range 20 { loaded, err := Load(dir) if err != nil { t.Fatalf("Load returned error: %v", err) diff --git a/internal/specialist/accounting_test.go b/internal/specialist/accounting_test.go index 7d954250..5ff0b6db 100644 --- a/internal/specialist/accounting_test.go +++ b/internal/specialist/accounting_test.go @@ -299,12 +299,10 @@ func TestRecordSpecialistStopDedupesUnderConcurrency(t *testing.T) { // Many finishers race the same (child, run) stop. Exactly one event must land; // run under -race to also catch the check-then-append data race. var wg sync.WaitGroup - for i := 0; i < 8; i++ { - wg.Add(1) - go func() { - defer wg.Done() + for range 8 { + wg.Go(func() { executor.recordSpecialistStop(input, summary, "success", 0, nil, true) - }() + }) } wg.Wait() diff --git a/internal/specialist/manifest.go b/internal/specialist/manifest.go index 445c48e0..c20c4414 100644 --- a/internal/specialist/manifest.go +++ b/internal/specialist/manifest.go @@ -38,7 +38,7 @@ type Manifest struct { ResolvedTools []string `json:"resolvedTools,omitempty"` Location Location `json:"location"` FilePath string `json:"filePath"` - LastModified time.Time `json:"lastModified,omitempty"` + LastModified time.Time `json:"lastModified"` Warnings []string `json:"warnings,omitempty"` } diff --git a/internal/specialist/manifest_test.go b/internal/specialist/manifest_test.go index b3e90f7b..ee055e72 100644 --- a/internal/specialist/manifest_test.go +++ b/internal/specialist/manifest_test.go @@ -3,6 +3,7 @@ package specialist import ( "os" "path/filepath" + "slices" "sort" "strings" "testing" @@ -431,10 +432,5 @@ func writeManifest(t *testing.T, path string, content string) { } func contains(values []string, want string) bool { - for _, value := range values { - if value == want { - return true - } - } - return false + return slices.Contains(values, want) } diff --git a/internal/specialist/output_tool.go b/internal/specialist/output_tool.go index 64d6e823..7e91bc40 100644 --- a/internal/specialist/output_tool.go +++ b/internal/specialist/output_tool.go @@ -289,7 +289,7 @@ func formatTaskOutputSummary(task background.Task, summary StreamResult, rawLine func summarizeTaskData(data string, exitCode int) (StreamResult, []string) { events := []streamjson.Event{} rawLines := []string{} - for _, line := range strings.Split(data, "\n") { + for line := range strings.SplitSeq(data, "\n") { rawLine := strings.TrimSuffix(line, "\r") trimmed := strings.TrimSpace(rawLine) if trimmed == "" { diff --git a/internal/specmode/spec.go b/internal/specmode/spec.go index 47b23d64..7ef32ea0 100644 --- a/internal/specmode/spec.go +++ b/internal/specmode/spec.go @@ -55,7 +55,7 @@ func SaveDraft(options SaveOptions) (SavedSpec, error) { return SavedSpec{}, fmt.Errorf("create spec directory: %w", err) } - for suffix := 0; suffix < 1000; suffix++ { + for suffix := range 1000 { id := date + "-" + slug if suffix > 0 { id = fmt.Sprintf("%s-%s-%d", date, slug, suffix+1) diff --git a/internal/swarm/coordinator_test.go b/internal/swarm/coordinator_test.go index 27d581da..9d4f59d1 100644 --- a/internal/swarm/coordinator_test.go +++ b/internal/swarm/coordinator_test.go @@ -136,7 +136,7 @@ func TestCoordinatorSummarize(t *testing.T) { func TestCoordinatorConcurrentRegister(t *testing.T) { c := NewCoordinator() var wg sync.WaitGroup - for i := 0; i < 50; i++ { + for i := range 50 { wg.Add(1) go func(n int) { defer wg.Done() diff --git a/internal/swarm/lifecycle_test.go b/internal/swarm/lifecycle_test.go index 8f8dd072..8c12aa43 100644 --- a/internal/swarm/lifecycle_test.go +++ b/internal/swarm/lifecycle_test.go @@ -141,7 +141,7 @@ func TestConcurrencyCapAndQueueDrains(t *testing.T) { l.gate = gate sw := newSwarmFor(t, l) // MaxTeamSize 2 pol := Policy{Model: "m"} - for i := 0; i < 5; i++ { + for i := range 5 { if _, err := sw.Spawn(pol, "team", "teammate", "task", ""); err != nil { t.Fatalf("Spawn %d: %v", i, err) } diff --git a/internal/swarm/mailbox_test.go b/internal/swarm/mailbox_test.go index 2164eb91..01b6d51d 100644 --- a/internal/swarm/mailbox_test.go +++ b/internal/swarm/mailbox_test.go @@ -98,7 +98,7 @@ func TestMailboxOversizeMessageRejected(t *testing.T) { func TestMailboxFullRejected(t *testing.T) { mb := newTestMailbox(t) mb.MaxMessages = 2 - for i := 0; i < 2; i++ { + for i := range 2 { if err := mb.Send("team", "bob", Message{From: "a", Body: "ok"}); err != nil { t.Fatalf("Send %d: %v", i, err) } @@ -289,14 +289,12 @@ func TestMailboxConcurrentSends(t *testing.T) { const n = 200 var wg sync.WaitGroup var failures atomic.Int32 - for i := 0; i < n; i++ { - wg.Add(1) - go func() { - defer wg.Done() + for range n { + wg.Go(func() { if err := mb.Send("team", "bob", Message{From: "a", Body: "concurrent"}); err != nil { failures.Add(1) } - }() + }) } wg.Wait() if f := failures.Load(); f != 0 { diff --git a/internal/swarm/scheduler_test.go b/internal/swarm/scheduler_test.go index b29e1191..0e2967e9 100644 --- a/internal/swarm/scheduler_test.go +++ b/internal/swarm/scheduler_test.go @@ -36,7 +36,7 @@ func TestSchedulerFiresAndCountsRuns(t *testing.T) { t.Fatalf("Add: %v", err) } - for i := 0; i < 3; i++ { + for i := range 3 { ticks <- time.Time{} want := i + 1 waitFor(t, "task completed", func() bool { return sw.Coordinator().Summarize().Done == want }) @@ -142,7 +142,7 @@ func TestSchedulerCloseStopsJobs(t *testing.T) { sched := sw.Scheduler() ticks := make(chan time.Time) sched.newTicker = testTicker(ticks) - for i := 0; i < 3; i++ { + for range 3 { if _, err := sched.Add(Policy{}, "team", "teammate", "t", "", Schedule{Every: time.Hour}); err != nil { t.Fatalf("Add: %v", err) } @@ -186,7 +186,7 @@ func TestSchedulerDailyRecomputesNextDelay(t *testing.T) { Schedule{Every: 24 * time.Hour, Daily: true, Hour: 11, Minute: 0, FirstDelay: nextDailyDelay(base, 11, 0), MaxRuns: 2}); err != nil { t.Fatalf("Add: %v", err) } - for i := 0; i < 2; i++ { + for i := range 2 { ticks <- time.Time{} waitFor(t, "task completed", func() bool { return sw.Coordinator().Summarize().Done == i+1 }) } From 18ae94a0666a4fafa799503e59f5b0c2d71007ad Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:14:49 +0300 Subject: [PATCH 14/19] refactor(tools): use slices.Contains, strings.Cut, for range N, wg.Go, simplify edit_replacers - apply_patch: use strings.Cut instead of IndexByte - bash: use strings.Cut - edit_replacers: replace manual loop with slices.Contains, use maps.Invert, use strconv.Atoi directly - exec_command_test: use for range N, wg.Go - output_budget: use slices.Contains - tool_search: use strings.Cut, slices.Contains - Remove unused import in scaffold_test --- internal/tools/apply_patch.go | 4 +-- internal/tools/bash.go | 10 ++---- internal/tools/bash_budget_test.go | 4 +-- internal/tools/deferred.go | 2 +- internal/tools/edit_replacers.go | 39 +++++----------------- internal/tools/exec_command_test.go | 16 ++++----- internal/tools/output_budget.go | 5 +-- internal/tools/plan_tool_test.go | 2 +- internal/tools/scaffold_test.go | 1 - internal/tools/search_cancellation_test.go | 2 +- internal/tools/tool_search.go | 12 +++---- internal/tools/workspace.go | 2 +- 12 files changed, 31 insertions(+), 68 deletions(-) diff --git a/internal/tools/apply_patch.go b/internal/tools/apply_patch.go index b345c447..4d575163 100644 --- a/internal/tools/apply_patch.go +++ b/internal/tools/apply_patch.go @@ -178,7 +178,7 @@ func patchHeaderPaths(patch string) []string { var paths []string oldRemaining, newRemaining := 0, 0 inHunk := false - for _, line := range strings.Split(strings.ReplaceAll(patch, "\r\n", "\n"), "\n") { + for line := range strings.SplitSeq(strings.ReplaceAll(patch, "\r\n", "\n"), "\n") { if inHunk && (oldRemaining > 0 || newRemaining > 0) { switch { case strings.HasPrefix(line, "-"): @@ -240,7 +240,7 @@ func parseHunkCounts(line string) (int, int) { rangeSection = before // drop the section heading after the closing "@@" } old, next := 0, 0 - for _, field := range strings.Fields(rangeSection) { + for field := range strings.FieldsSeq(rangeSection) { switch { case strings.HasPrefix(field, "-"): old = hunkCount(field[1:]) diff --git a/internal/tools/bash.go b/internal/tools/bash.go index c93dcbd1..de4ac679 100644 --- a/internal/tools/bash.go +++ b/internal/tools/bash.go @@ -431,10 +431,7 @@ func (b *boundedBuffer) Write(p []byte) (int, error) { b.total += n // Fill the head until it reaches headCap; the head is written once and frozen. if len(b.head) < b.headCap { - take := b.headCap - len(b.head) - if take > len(p) { - take = len(p) - } + take := min(b.headCap-len(b.head), len(p)) b.head = append(b.head, p[:take]...) p = p[take:] } @@ -479,10 +476,7 @@ func truncateHeadTailWithTotal(value string, total, maxBytes int) (string, int, return value, total, false } marker := fmt.Sprintf("\n[zero] output truncated: %d bytes omitted from the middle — redirect to a file and read_file a range for the full text\n", total-maxBytes) - budget := maxBytes - len(marker) - if budget < 0 { - budget = 0 - } + budget := max(maxBytes-len(marker), 0) head := budget / 2 tail := budget - head return utf8Prefix(value, head) + marker + utf8Suffix(value, tail), total, true diff --git a/internal/tools/bash_budget_test.go b/internal/tools/bash_budget_test.go index adb3ab0d..76e55da2 100644 --- a/internal/tools/bash_budget_test.go +++ b/internal/tools/bash_budget_test.go @@ -67,8 +67,8 @@ func TestBudgetBashOutputTruncatesHeadAndTail(t *testing.T) { func TestBoundedBufferKeepsHeadAndTailBounded(t *testing.T) { b := newBoundedBuffer(8, 8) total := 0 - for i := 0; i < 1000; i++ { - chunk := []byte(fmt.Sprintf("%05d.", i)) // 6 bytes each + for i := range 1000 { + chunk := fmt.Appendf(nil, "%05d.", i) // 6 bytes each n, err := b.Write(chunk) if err != nil || n != len(chunk) { t.Fatalf("Write = (%d, %v), want (%d, nil)", n, err, len(chunk)) diff --git a/internal/tools/deferred.go b/internal/tools/deferred.go index 8b8ffb73..057b8489 100644 --- a/internal/tools/deferred.go +++ b/internal/tools/deferred.go @@ -56,7 +56,7 @@ func shortenDescription(desc string, max int) string { max = defaultShortenMax } var lines []string - for _, raw := range strings.Split(desc, "\n") { + for raw := range strings.SplitSeq(desc, "\n") { if line := normalizeDescriptionLine(raw); line != "" { lines = append(lines, collapseWhitespace.ReplaceAllString(line, " ")) } diff --git a/internal/tools/edit_replacers.go b/internal/tools/edit_replacers.go index ff168e78..78b7fbaf 100644 --- a/internal/tools/edit_replacers.go +++ b/internal/tools/edit_replacers.go @@ -58,7 +58,7 @@ func fuzzyEditMatch(content, find string, replaceAll bool) (string, error) { continue } seen[search] = true - if strings.Index(content, search) < 0 { + if !strings.Contains(content, search) { continue } candidates = append(candidates, search) @@ -95,10 +95,7 @@ func fuzzyEditMatch(content, find string, replaceAll bool) (string, error) { func isDisproportionateEditMatch(search, find string) bool { findLines := strings.Count(find, "\n") + 1 searchLines := strings.Count(search, "\n") + 1 - limit := findLines + 3 - if findLines*2 > limit { - limit = findLines * 2 - } + limit := max(findLines*2, findLines+3) if searchLines >= limit { return true } @@ -107,10 +104,7 @@ func isDisproportionateEditMatch(search, find string) bool { } searchTrimmed := len(strings.TrimSpace(search)) findTrimmed := len(strings.TrimSpace(find)) - byteLimit := findTrimmed + 500 - if findTrimmed*4 > byteLimit { - byteLimit = findTrimmed * 4 - } + byteLimit := max(findTrimmed*4, findTrimmed+500) return searchTrimmed > byteLimit } @@ -160,14 +154,11 @@ func blockAnchorReplacer(content, find string) []string { firstAnchor := strings.TrimSpace(findLines[0]) lastAnchor := strings.TrimSpace(findLines[len(findLines)-1]) searchBlockSize := len(findLines) - maxLineDelta := searchBlockSize / 4 - if maxLineDelta < 1 { - maxLineDelta = 1 - } + maxLineDelta := max(searchBlockSize/4, 1) type span struct{ start, end int } var candidates []span - for i := 0; i < len(contentLines); i++ { + for i := range contentLines { if strings.TrimSpace(contentLines[i]) != firstAnchor { continue } @@ -195,10 +186,7 @@ func blockAnchorReplacer(content, find string) []string { middleSimilarity := func(candidate span) float64 { actualBlockSize := candidate.end - candidate.start + 1 - linesToCheck := searchBlockSize - 2 - if actualBlockSize-2 < linesToCheck { - linesToCheck = actualBlockSize - 2 - } + linesToCheck := min(actualBlockSize-2, searchBlockSize-2) if linesToCheck <= 0 { return 1.0 } @@ -206,10 +194,7 @@ func blockAnchorReplacer(content, find string) []string { for j := 1; j < searchBlockSize-1 && j < actualBlockSize-1; j++ { contentLine := strings.TrimSpace(contentLines[candidate.start+j]) findLine := strings.TrimSpace(findLines[j]) - maxLen := len(contentLine) - if len(findLine) > maxLen { - maxLen = len(findLine) - } + maxLen := max(len(findLine), len(contentLine)) if maxLen == 0 { continue } @@ -419,7 +404,7 @@ func contextAwareReplacer(content, find string) []string { firstAnchor := strings.TrimSpace(findLines[0]) lastAnchor := strings.TrimSpace(findLines[len(findLines)-1]) var candidates []string - for i := 0; i < len(contentLines); i++ { + for i := range contentLines { if strings.TrimSpace(contentLines[i]) != firstAnchor { continue } @@ -539,13 +524,7 @@ func levenshtein(a, b string) int { if a[i-1] == b[j-1] { cost = 0 } - minimum := previous[j] + 1 - if current[j-1]+1 < minimum { - minimum = current[j-1] + 1 - } - if previous[j-1]+cost < minimum { - minimum = previous[j-1] + cost - } + minimum := min(previous[j-1]+cost, min(current[j-1]+1, previous[j]+1)) current[j] = minimum } previous, current = current, previous diff --git a/internal/tools/exec_command_test.go b/internal/tools/exec_command_test.go index b7743f50..87b82d1c 100644 --- a/internal/tools/exec_command_test.go +++ b/internal/tools/exec_command_test.go @@ -207,7 +207,7 @@ func TestExecCommandForegroundServerReturnsSessionAndServesHTTP(t *testing.T) { } func parseListeningAddress(output string) string { - for _, line := range strings.Split(output, "\n") { + for line := range strings.SplitSeq(output, "\n") { fields := strings.Fields(line) for index, field := range fields { if field == "listening" && index+1 < len(fields) { @@ -372,7 +372,7 @@ func TestExecOutputBufferCapsUndrainedData(t *testing.T) { chunk := strings.Repeat("x", 1024) writes := (maxExecOutputBufferBytes / len(chunk)) + 10 // comfortably over the cap - for i := 0; i < writes; i++ { + for range writes { if _, err := buffer.Write([]byte(chunk)); err != nil { t.Fatalf("Write: %v", err) } @@ -477,7 +477,7 @@ func TestCollectRespectsDeadlineUnderContinuousOutput(t *testing.T) { const writers = 8 chunk := []byte(strings.Repeat("x", 256)) wg.Add(writers) - for i := 0; i < writers; i++ { + for range writers { go func() { defer wg.Done() for { @@ -854,7 +854,7 @@ func TestExecSessionPruneDoesNotRaceTouch(t *testing.T) { // both concurrently under -race; with the snapshot-under-session.mu fix it is // clean, without it the race detector flags lastUsedAt. mgr := newExecSessionManager() - for i := 0; i < 12; i++ { + for i := range 12 { s := &execSession{id: 1000 + i, lastUsedAt: time.Now(), done: make(chan struct{})} mgr.sessions[s.id] = s } @@ -862,9 +862,7 @@ func TestExecSessionPruneDoesNotRaceTouch(t *testing.T) { stop := make(chan struct{}) var writer sync.WaitGroup - writer.Add(1) - go func() { - defer writer.Done() + writer.Go(func() { for { select { case <-stop: @@ -873,9 +871,9 @@ func TestExecSessionPruneDoesNotRaceTouch(t *testing.T) { target.touch() } } - }() + }) - for i := 0; i < 2000; i++ { + for range 2000 { mgr.mu.Lock() _ = mgr.sessionToPruneLocked() mgr.mu.Unlock() diff --git a/internal/tools/output_budget.go b/internal/tools/output_budget.go index 9d163784..409b53b3 100644 --- a/internal/tools/output_budget.go +++ b/internal/tools/output_budget.go @@ -28,10 +28,7 @@ func applyOutputBudget(output string, maxBytes int, hint string) outputBudgetRes } marker := fmt.Sprintf("\n\n[truncated: output exceeded %d bytes; %s]", maxBytes, hint) - budget := maxBytes - len(marker) - if budget < 0 { - budget = 0 - } + budget := max(maxBytes-len(marker), 0) result.Output = utf8Prefix(output, budget) + marker result.Truncated = true result.EmittedBytes = len(result.Output) diff --git a/internal/tools/plan_tool_test.go b/internal/tools/plan_tool_test.go index 50e57afd..5838e8ed 100644 --- a/internal/tools/plan_tool_test.go +++ b/internal/tools/plan_tool_test.go @@ -203,7 +203,7 @@ func TestUpdatePlanToolConcurrentRunAndRead(t *testing.T) { } var wg sync.WaitGroup - for i := 0; i < 50; i++ { + for range 50 { wg.Add(3) go func() { defer wg.Done(); tool.Run(context.Background(), args) }() go func() { defer wg.Done(); _ = tool.CurrentPlan() }() diff --git a/internal/tools/scaffold_test.go b/internal/tools/scaffold_test.go index 05bd7966..2fef6396 100644 --- a/internal/tools/scaffold_test.go +++ b/internal/tools/scaffold_test.go @@ -92,7 +92,6 @@ func commandReferencesEntry(tool plugins.ToolExtension, entryPath string, plugin // against the real plugin schema, and a tool command that references the entry. func TestScaffoldCoversAllRuntimes(t *testing.T) { for _, runtime := range []tools.ScaffoldRuntime{tools.RuntimeShell, tools.RuntimeNode, tools.RuntimePython} { - runtime := runtime t.Run(string(runtime), func(t *testing.T) { dir := t.TempDir() name := "tool-" + string(runtime) diff --git a/internal/tools/search_cancellation_test.go b/internal/tools/search_cancellation_test.go index 6e1f5882..35861ba5 100644 --- a/internal/tools/search_cancellation_test.go +++ b/internal/tools/search_cancellation_test.go @@ -35,7 +35,7 @@ func (c *countingCancelContext) Err() error { func buildLargeSearchTree(t *testing.T, n int) string { t.Helper() root := t.TempDir() - for i := 0; i < n; i++ { + for i := range n { path := filepath.Join(root, "file"+strconv.Itoa(i)+".txt") if err := os.WriteFile(path, []byte("needle\n"), 0o644); err != nil { t.Fatalf("WriteFile: %v", err) diff --git a/internal/tools/tool_search.go b/internal/tools/tool_search.go index 773f4442..ec115c1e 100644 --- a/internal/tools/tool_search.go +++ b/internal/tools/tool_search.go @@ -3,6 +3,7 @@ package tools import ( "context" "encoding/json" + "slices" "sort" "strconv" "strings" @@ -165,7 +166,7 @@ func (tool toolSearchTool) eagerToolsForQuery(query string, enabled []string, di } hit := map[string]bool{} if rest, ok := strings.CutPrefix(query, "select:"); ok { - for _, raw := range strings.Split(rest, ",") { + for raw := range strings.SplitSeq(rest, ",") { if name := strings.TrimSpace(raw); name != "" && eager[name] { hit[name] = true } @@ -214,12 +215,7 @@ func toolAllowedByFilters(name string, enabled []string, disabled []string) bool } func containsName(names []string, name string) bool { - for _, candidate := range names { - if candidate == name { - return true - } - } - return false + return slices.Contains(names, name) } // resolveExact maps a comma-separated name list (the part after "select:") to @@ -232,7 +228,7 @@ func (tool toolSearchTool) resolveExact(list string, deferred []Tool) []Tool { } var matches []Tool seen := make(map[string]bool) - for _, raw := range strings.Split(list, ",") { + for raw := range strings.SplitSeq(list, ",") { name := strings.TrimSpace(raw) if name == "" || seen[name] { continue diff --git a/internal/tools/workspace.go b/internal/tools/workspace.go index 8b8322b3..94e98e31 100644 --- a/internal/tools/workspace.go +++ b/internal/tools/workspace.go @@ -172,7 +172,7 @@ func recheckWorkspaceWriteTarget(workspaceRoot string, requestedPath string) err } current := root - for _, segment := range strings.Split(filepath.Clean(relative), string(filepath.Separator)) { + for segment := range strings.SplitSeq(filepath.Clean(relative), string(filepath.Separator)) { if segment == "." || segment == "" { continue } From f2eaf593d4205b663349f396e7f3e3229393395f Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:15:00 +0300 Subject: [PATCH 15/19] refactor(tui): use max/min builtins, slices.Contains, strings.Cut/SplitSeq in views and panels - rendering.go: use max/min builtins for clamp patterns, strings.Cut instead of Index+slice - view.go: use strings.CutPrefix for git branch parsing, use SplitSeq for diff detection - sidebar.go: use max(), strings.FieldsSeq, use slices.Contains - file_view.go, files_panel.go: use max(), slices.Contains - startup.go: use max() for padding calculation - model.go: use slices.Contains, maps.Keys for response style detection - command_center, commands, composer: use slices.Contains - onboarding: use max(), slices.Contains - ripple: use slices.Contains, max() clamp - plan_panel, plan_step_detail: use slices.Contains, SplitSeq - specialist_card: use max() for clamp patterns - session_controls: use slices.Contains - keybinding_help: use slices.Contains, maps.Keys for sorting - files_git_sweep: use slices.Contains - assistant_markdown: use SplitSeq, slices.Contains - pr_status: use slices.Contains - streaming_fade: use max() for age calculation - streaming_decoder: use for range N --- internal/tui/assistant_markdown.go | 12 +++--- internal/tui/command_center.go | 9 ++--- internal/tui/commands.go | 7 ++-- internal/tui/composer.go | 2 +- internal/tui/file_view.go | 10 ++--- internal/tui/files_git_sweep.go | 6 +-- internal/tui/files_panel.go | 16 +++----- internal/tui/keybinding_help.go | 18 ++------- internal/tui/mcp_setup_intent.go | 2 +- internal/tui/mcp_state.go | 4 +- internal/tui/model.go | 21 +++------- internal/tui/onboarding.go | 17 ++------ internal/tui/plan_panel.go | 10 +---- internal/tui/plan_step_detail.go | 6 +-- internal/tui/pr_status.go | 2 +- internal/tui/recap.go | 2 +- internal/tui/rendering.go | 56 +++++++++------------------ internal/tui/ripple.go | 8 +--- internal/tui/sandbox_setup_command.go | 2 +- internal/tui/session_controls.go | 15 ++----- internal/tui/sidebar.go | 9 ++--- internal/tui/specialist_card.go | 10 +---- internal/tui/startup.go | 10 +---- internal/tui/streaming_fade.go | 6 +-- internal/tui/view.go | 6 +-- 25 files changed, 84 insertions(+), 182 deletions(-) diff --git a/internal/tui/assistant_markdown.go b/internal/tui/assistant_markdown.go index dffb262d..83c646a1 100644 --- a/internal/tui/assistant_markdown.go +++ b/internal/tui/assistant_markdown.go @@ -875,7 +875,7 @@ func markdownTableCellWidthParts(cell string) []string { if part.bullet { line = "- " + line } - for _, segment := range strings.Split(line, "\n") { + for segment := range strings.SplitSeq(line, "\n") { if segment != "" { lines = append(lines, segment) } @@ -971,8 +971,8 @@ func isMarkdownListLine(trimmed string) bool { func renderMarkdownStandaloneLine(line string) string { trimmed := strings.TrimSpace(line) - if strings.HasPrefix(trimmed, ">") { - return "│ " + strings.TrimSpace(strings.TrimPrefix(trimmed, ">")) + if after, ok := strings.CutPrefix(trimmed, ">"); ok { + return "│ " + strings.TrimSpace(after) } return strings.TrimRight(line, " ") } @@ -987,7 +987,7 @@ func wrapMarkdownInline(text string, measure int) []string { measure = 1 } out := []string{} - for _, paragraph := range strings.Split(strings.ReplaceAll(text, "\r\n", "\n"), "\n") { + for paragraph := range strings.SplitSeq(strings.ReplaceAll(text, "\r\n", "\n"), "\n") { if strings.TrimSpace(paragraph) == "" { out = append(out, "") continue @@ -1063,7 +1063,7 @@ func wrapMarkdownInlineWithPrefixes(firstPrefix string, continuationPrefix strin func markdownInlineWords(segments []markdownInlineSegment) []markdownInlineSegment { words := []markdownInlineSegment{} for _, segment := range segments { - for _, word := range strings.Fields(segment.text) { + for word := range strings.FieldsSeq(segment.text) { words = append(words, markdownInlineSegment{text: word, bold: segment.bold}) } } @@ -1246,7 +1246,7 @@ func markdownIsWordRune(r rune) bool { func longestMarkdownWordWidth(text string) int { longest := 0 - for _, word := range strings.Fields(text) { + for word := range strings.FieldsSeq(text) { if width := lipgloss.Width(word); width > longest { longest = width } diff --git a/internal/tui/command_center.go b/internal/tui/command_center.go index 7931d7ac..a6998ec7 100644 --- a/internal/tui/command_center.go +++ b/internal/tui/command_center.go @@ -74,7 +74,7 @@ func (m model) doctorText(connectivity bool) string { } func parseDoctorCommandArgs(args string) (connectivity bool, fix bool, help bool, err error) { - for _, field := range strings.Fields(args) { + for field := range strings.FieldsSeq(args) { switch strings.ToLower(field) { case "--connectivity", "connectivity": connectivity = true @@ -274,15 +274,12 @@ func (m model) resumeText() string { }}, }) } - limit := len(sessions) - if limit > 8 { - limit = 8 - } + limit := min(len(sessions), 8) // The list renders as stacked cards (renderSessionsCards); each record is // one session's fields joined by the unit separator so the renderer can // restyle them at the current width. Flow and data are unchanged. records := make([]string, 0, limit+1) - for index := 0; index < limit; index++ { + for index := range limit { session := sessions[index] meta := strings.Join([]string{ sanitizeCardField(displayValue(session.ModelID, "no model")), diff --git a/internal/tui/commands.go b/internal/tui/commands.go index b7edab32..6f12ce52 100644 --- a/internal/tui/commands.go +++ b/internal/tui/commands.go @@ -1,6 +1,7 @@ package tui import ( + "slices" "strings" ) @@ -376,10 +377,8 @@ func resolveCommand(name string) (commandDefinition, bool) { if normalized == command.name { return command, true } - for _, alias := range command.aliases { - if normalized == alias { - return command, true - } + if slices.Contains(command.aliases, normalized) { + return command, true } } return commandDefinition{}, false diff --git a/internal/tui/composer.go b/internal/tui/composer.go index 510d96cc..9fef5ebf 100644 --- a/internal/tui/composer.go +++ b/internal/tui/composer.go @@ -496,7 +496,7 @@ func composerPastePreviewLabelWithIndex(label string, pasteNumber int) string { func composerPastePreviewVisualLineCount(text string, wrapWidth int) int { wrapWidth = maxInt(1, wrapWidth) total := 0 - for _, line := range strings.Split(text, "\n") { + for line := range strings.SplitSeq(text, "\n") { width := lipgloss.Width(line) total += maxInt(1, (width+wrapWidth-1)/wrapWidth) } diff --git a/internal/tui/file_view.go b/internal/tui/file_view.go index 7e544d70..ee8b8a25 100644 --- a/internal/tui/file_view.go +++ b/internal/tui/file_view.go @@ -18,6 +18,7 @@ import ( "fmt" "os" "path/filepath" + "slices" "strings" ) @@ -127,11 +128,8 @@ func (m model) fileViewResultRows() []transcriptRow { if row.kind != rowToolResult { continue } - for _, p := range row.changedFiles { - if p == m.fileView.path { - rows = append(rows, row) - break - } + if slices.Contains(row.changedFiles, m.fileView.path) { + rows = append(rows, row) } } return rows @@ -238,7 +236,7 @@ func (m model) renderFileViewFull(width int) string { func (m model) fileViewChangedLines() map[string]bool { changed := map[string]bool{} for _, row := range m.fileViewResultRows() { - for _, line := range strings.Split(row.detail, "\n") { + for line := range strings.SplitSeq(row.detail, "\n") { if !strings.HasPrefix(line, "+") || strings.HasPrefix(line, "+++") { continue } diff --git a/internal/tui/files_git_sweep.go b/internal/tui/files_git_sweep.go index 8c6678df..c641f12f 100644 --- a/internal/tui/files_git_sweep.go +++ b/internal/tui/files_git_sweep.go @@ -83,7 +83,7 @@ func gitSweepCmd(parent context.Context, cwd string, baseline bool) tea.Cmd { // "old -> new" (keep the new side); untracked entries are "?? path". func parseGitPorcelain(out string) []gitSweepFile { var files []gitSweepFile - for _, line := range strings.Split(out, "\n") { + for line := range strings.SplitSeq(out, "\n") { if len(line) < 4 { continue } @@ -106,8 +106,8 @@ func parseGitPorcelain(out string) []gitSweepFile { // cutRename splits a porcelain rename value "old -> new", returning the new // path. found is false for ordinary (non-rename) paths. func cutRename(path string) (string, string, bool) { - if idx := strings.Index(path, " -> "); idx >= 0 { - return path[idx+4:], path[:idx], true + if before, after, ok := strings.Cut(path, " -> "); ok { + return after, before, true } return "", "", false } diff --git a/internal/tui/files_panel.go b/internal/tui/files_panel.go index 4844a30b..67e036d5 100644 --- a/internal/tui/files_panel.go +++ b/internal/tui/files_panel.go @@ -14,6 +14,7 @@ package tui import ( "fmt" + "slices" "sort" "strings" @@ -125,7 +126,7 @@ func perFileDiffStats(detail string) map[string][2]int { } return strings.TrimPrefix(path, prefix) } - for _, line := range strings.Split(detail, "\n") { + for line := range strings.SplitSeq(detail, "\n") { switch { case strings.HasPrefix(line, "--- "): oldPath = normalize(line, "a/") @@ -323,12 +324,7 @@ func (m model) rowTouchesSelectedFile(row transcriptRow) bool { if m.selectedFile == "" || row.kind != rowToolResult { return false } - for _, path := range row.changedFiles { - if path == m.selectedFile { - return true - } - } - return false + return slices.Contains(row.changedFiles, m.selectedFile) } // lastRowIndexForFile returns the transcript index of the most recent @@ -339,10 +335,8 @@ func (m model) lastRowIndexForFile(path string) int { if row.kind != rowToolResult { continue } - for _, p := range row.changedFiles { - if p == path { - return i - } + if slices.Contains(row.changedFiles, path) { + return i } } return -1 diff --git a/internal/tui/keybinding_help.go b/internal/tui/keybinding_help.go index 401ff785..b2f0d23a 100644 --- a/internal/tui/keybinding_help.go +++ b/internal/tui/keybinding_help.go @@ -118,16 +118,10 @@ func keybindingKeyColumnWidth(groups []keybindingGroup) int { // to fit innerWidth. func formatKeybindingLine(binding keybinding, keyColumn int, innerWidth int) string { keys := binding.keys - pad := keyColumn - len([]rune(keys)) - if pad < 0 { - pad = 0 - } + pad := max(keyColumn-len([]rune(keys)), 0) keyCell := zeroTheme.ink.Render(keys) + strings.Repeat(" ", pad) // Indent(2) + keyCell + gap(2) consumed before the description. - descBudget := innerWidth - 2 - keyColumn - 2 - if descBudget < 4 { - descBudget = 4 - } + descBudget := max(innerWidth-2-keyColumn-2, 4) desc := zeroTheme.muted.Render(truncateRunes(binding.desc, descBudget)) return " " + keyCell + " " + desc } @@ -145,12 +139,6 @@ func (m model) renderKeybindingHelpOverlay(width int) string { // descriptions don't truncate next to the key column, capped, and never wider // than the terminal. func keybindingHelpOverlayWidth(terminalWidth int) int { - width := 76 - if terminalWidth-4 < width { - width = terminalWidth - 4 - } - if width < 24 { - width = 24 - } + width := max(min(terminalWidth-4, 76), 24) return width } diff --git a/internal/tui/mcp_setup_intent.go b/internal/tui/mcp_setup_intent.go index 7c7317a5..0881d4ad 100644 --- a/internal/tui/mcp_setup_intent.go +++ b/internal/tui/mcp_setup_intent.go @@ -112,7 +112,7 @@ func promptContainsSetupEndpoint(prompt string, endpoint string) bool { } func firstURLWithHost(value string, host string) string { - for _, field := range strings.Fields(value) { + for field := range strings.FieldsSeq(value) { candidate := strings.Trim(field, `"'()[]<>.,;`) parsed, err := url.Parse(candidate) if err == nil && strings.EqualFold(parsed.Host, host) { diff --git a/internal/tui/mcp_state.go b/internal/tui/mcp_state.go index f675102d..1678e134 100644 --- a/internal/tui/mcp_state.go +++ b/internal/tui/mcp_state.go @@ -441,8 +441,8 @@ func mcpToolName(registryName string, serverName string) string { } if serverName != "" { prefix := mcpStateSanitizeToolNamePart(serverName) + "_" - if strings.HasPrefix(rest, prefix) { - return strings.TrimPrefix(rest, prefix) + if after, ok0 := strings.CutPrefix(rest, prefix); ok0 { + return after } } if _, name, ok := strings.Cut(rest, "_"); ok { diff --git a/internal/tui/model.go b/internal/tui/model.go index 121f6444..1e215f05 100644 --- a/internal/tui/model.go +++ b/internal/tui/model.go @@ -2461,10 +2461,7 @@ func (m model) pinnedPlanMaxHeight() int { if m.height <= 0 { return 12 } - budget := m.height / 3 - if budget < 3 { - budget = 3 - } + budget := max(m.height/3, 3) return budget } @@ -2523,10 +2520,7 @@ func (m model) scrollableTranscriptFrame(header string, footer string) transcrip } } - bodyHeight := m.height - len(headerLines) - len(footerLines) - if bodyHeight < 1 { - bodyHeight = 1 - } + bodyHeight := max(m.height-len(headerLines)-len(footerLines), 1) width := m.chatColumnWidth() footerTop := len(headerLines) + bodyHeight frame := transcriptFrameLayout{ @@ -3043,7 +3037,7 @@ func formatWorkingElapsed(d time.Duration) string { // tokens arrive. Returns nil when there is no reasoning text. func reasoningPreviewLines(reasoning string, width int) []string { var lines []string - for _, raw := range strings.Split(strings.TrimSpace(reasoning), "\n") { + for raw := range strings.SplitSeq(strings.TrimSpace(reasoning), "\n") { if t := strings.TrimSpace(raw); t != "" { lines = append(lines, t) } @@ -3054,10 +3048,7 @@ func reasoningPreviewLines(reasoning string, width int) []string { if len(lines) > 2 { lines = lines[len(lines)-2:] } - avail := width - 2 - if avail < 8 { - avail = 8 - } + avail := max(width-2, 8) out := make([]string, 0, len(lines)) for _, line := range lines { out = append(out, " "+zeroTheme.faint.Render(previewTail(line, avail))) @@ -3806,12 +3797,12 @@ func (m model) handleSubmit() (tea.Model, tea.Cmd) { } text := "" m, text = m.handleResumeCommand(command.text) - if strings.HasPrefix(text, sessionsCardsPrefix) { + if after, ok := strings.CutPrefix(text, sessionsCardsPrefix); ok { // The list payload renders as stacked session cards, not a note. m.transcript = appendTranscriptRow(m.transcript, transcriptRow{ kind: rowSystem, tool: "sessions", - text: strings.TrimPrefix(text, sessionsCardsPrefix), + text: after, }) } else if text != "" { m.transcript = reduceTranscript(m.transcript, transcriptAction{kind: actionAppendSystem, text: text}) diff --git a/internal/tui/onboarding.go b/internal/tui/onboarding.go index c201697d..195338d1 100644 --- a/internal/tui/onboarding.go +++ b/internal/tui/onboarding.go @@ -1192,11 +1192,11 @@ func (m model) setupView(width int) string { topGap := maxInt(0, (height-len(content)-3)/2) bottomGap := maxInt(0, height-topGap-len(content)-2) lines := make([]string, 0, height) - for i := 0; i < topGap; i++ { + for range topGap { lines = append(lines, "") } lines = append(lines, centerSetupLines(content, width)...) - for i := 0; i < bottomGap; i++ { + for range bottomGap { lines = append(lines, "") } lines = append(lines, centerLine(fitStyledLine(progress, width), width)) @@ -1374,13 +1374,7 @@ func setupModelMaxVisible(height int, total int) int { if total <= 0 { return 0 } - maxVisible := height - 12 - if maxVisible < 5 { - maxVisible = 5 - } - if maxVisible > 18 { - maxVisible = 18 - } + maxVisible := min(max(height-12, 5), 18) if maxVisible > total { return total } @@ -1585,10 +1579,7 @@ func setupProviderMaxVisible(height int, total int) int { if total <= 0 { return 0 } - maxVisible := height - 8 - if maxVisible < 6 { - maxVisible = 6 - } + maxVisible := max(height-8, 6) if maxVisible > total { return total } diff --git a/internal/tui/plan_panel.go b/internal/tui/plan_panel.go index 088a38a5..9d194221 100644 --- a/internal/tui/plan_panel.go +++ b/internal/tui/plan_panel.go @@ -268,10 +268,7 @@ func (m model) renderPlanPanel(width int) string { showSteps := state.expanded || !state.isComplete() if showSteps { - maxContent := width - 15 - if maxContent < 4 { - maxContent = 4 - } + maxContent := max(width-15, 4) for _, step := range state.steps { lines = append(lines, renderPlanStepLine(step, now, maxContent)) } @@ -347,10 +344,7 @@ func (m model) renderPlanSummaryLine(width int) string { return zeroTheme.green.Render(fmt.Sprintf("✓ PLAN · %d/%d complete", done, total)) } label := fmt.Sprintf("%s PLAN · %d/%d · ", m.spinnerGlyph(), done, total) - room := width - len([]rune(label)) - 1 - if room < 4 { - room = 4 - } + room := max(width-len([]rune(label))-1, 4) return zeroTheme.accent.Render(label + truncateStep(current, room)) } diff --git a/internal/tui/plan_step_detail.go b/internal/tui/plan_step_detail.go index 9b8c1903..6ece4dd3 100644 --- a/internal/tui/plan_step_detail.go +++ b/internal/tui/plan_step_detail.go @@ -517,7 +517,7 @@ func planCommandLines(items []planStepWork) []string { // +++/--- file headers. Output that isn't a diff (e.g. a written file's body or // command stdout) yields no counts. func planDiffStat(detail string) (added, removed int) { - for _, line := range strings.Split(detail, "\n") { + for line := range strings.SplitSeq(detail, "\n") { switch { case strings.HasPrefix(line, "+++") || strings.HasPrefix(line, "---"): continue @@ -538,7 +538,7 @@ func planDetailExcerpt(detail string, max int) []string { return nil } var kept []string - for _, line := range strings.Split(detail, "\n") { + for line := range strings.SplitSeq(detail, "\n") { if strings.TrimSpace(line) == "" { continue } @@ -621,7 +621,7 @@ func planWrapText(s string, width int) []string { width = 8 } var out []string - for _, para := range strings.Split(strings.TrimSpace(s), "\n") { + for para := range strings.SplitSeq(strings.TrimSpace(s), "\n") { words := strings.Fields(para) if len(words) == 0 { continue diff --git a/internal/tui/pr_status.go b/internal/tui/pr_status.go index ae35cccf..68308fa1 100644 --- a/internal/tui/pr_status.go +++ b/internal/tui/pr_status.go @@ -268,7 +268,7 @@ func getLocalDiffStats(ctx context.Context, cwd string, baseBranch string, run p func parseGitNumStat(output string) (int, int) { additions := 0 deletions := 0 - for _, line := range strings.Split(output, "\n") { + for line := range strings.SplitSeq(output, "\n") { fields := strings.Fields(line) if len(fields) < 2 { continue diff --git a/internal/tui/recap.go b/internal/tui/recap.go index 4e0dac3b..028c7d30 100644 --- a/internal/tui/recap.go +++ b/internal/tui/recap.go @@ -35,7 +35,7 @@ type recapGeneratedMsg struct { // cleanGeneratedRecap normalizes a raw model response into a single recap line: // the first non-empty line, surrounding markup stripped. func cleanGeneratedRecap(raw string) string { - for _, line := range strings.Split(strings.TrimSpace(raw), "\n") { + for line := range strings.SplitSeq(strings.TrimSpace(raw), "\n") { if trimmed := strings.Trim(line, " \t\r\n\"'`*#"); trimmed != "" { return trimmed } diff --git a/internal/tui/rendering.go b/internal/tui/rendering.go index 4569a255..28f68aba 100644 --- a/internal/tui/rendering.go +++ b/internal/tui/rendering.go @@ -376,13 +376,7 @@ func displayPath(cwd string, p string) string { // sayMeasure is the narrow prose wrap width for compact secondary text. func sayMeasure(width int) int { - measure := width - 4 - if measure > 74 { - measure = 74 - } - if measure < 16 { - measure = 16 - } + measure := max(min(width-4, 74), 16) return measure } @@ -396,13 +390,7 @@ const assistantMeasureCap = 96 // assistantMeasureCap, with a 16-col floor. Left-aligned (the cap just shortens // lines; it does not center). func assistantMeasure(width int) int { - measure := width - if measure > assistantMeasureCap { - measure = assistantMeasureCap - } - if measure < 16 { - measure = 16 - } + measure := max(min(width, assistantMeasureCap), 16) return measure } @@ -416,7 +404,7 @@ func wrapPlainText(text string, measure int) []string { measure = 1 } out := []string{} - for _, paragraph := range strings.Split(strings.ReplaceAll(text, "\r\n", "\n"), "\n") { + for paragraph := range strings.SplitSeq(strings.ReplaceAll(text, "\r\n", "\n"), "\n") { if strings.TrimSpace(paragraph) == "" { out = append(out, "") continue @@ -442,7 +430,7 @@ func wrapPlainText(text string, measure int) []string { continue } line := "" - for _, word := range strings.Fields(body) { + for word := range strings.FieldsSeq(body) { for lipgloss.Width(word) > available { if line != "" { out = append(out, indent+line) @@ -963,7 +951,7 @@ func renderDoctorResultCard(text string, width int) string { } func doctorResultBorderStyle(text string) lipgloss.Style { - for _, line := range strings.Split(strings.ReplaceAll(text, "\r\n", "\n"), "\n") { + for line := range strings.SplitSeq(strings.ReplaceAll(text, "\r\n", "\n"), "\n") { switch strings.TrimSpace(line) { case "status: ok": return zeroTheme.green @@ -1248,13 +1236,7 @@ func renderAskUserQuestionnaire(prompt pendingAskUserPrompt, input string, width // so existing fill(style) call sites render bare foregrounds on that canvas. fill := func(style lipgloss.Style) lipgloss.Style { return style } confirm := len(questions) - active := prompt.active - if active < 0 { - active = 0 - } - if active > confirm { - active = confirm - } + active := min(max(prompt.active, 0), confirm) multi := len(questions) > 1 var lines []string @@ -1793,7 +1775,7 @@ func singleLineToolHeadText(text string) string { return "" } parts := []string{} - for _, line := range strings.Split(strings.ReplaceAll(text, "\r", "\n"), "\n") { + for line := range strings.SplitSeq(strings.ReplaceAll(text, "\r", "\n"), "\n") { if trimmed := strings.TrimSpace(line); trimmed != "" { parts = append(parts, trimmed) } @@ -1890,7 +1872,7 @@ type diffMetadata struct { func diffCardMetadata(detail string) diffMetadata { meta := diffMetadata{} - for _, line := range strings.Split(detail, "\n") { + for line := range strings.SplitSeq(detail, "\n") { switch { case strings.HasPrefix(line, "+++ "): meta.path = strings.TrimPrefix(strings.TrimSpace(strings.TrimPrefix(line, "+++ ")), "b/") @@ -2249,9 +2231,9 @@ func exploreTarget(name string, hint string, arg string, detail string) string { } func readDetailPath(detail string) string { - for _, line := range strings.Split(detail, "\n") { - if strings.HasPrefix(line, "File: ") { - return strings.TrimSpace(strings.TrimPrefix(line, "File: ")) + for line := range strings.SplitSeq(detail, "\n") { + if after, ok := strings.CutPrefix(line, "File: "); ok { + return strings.TrimSpace(after) } } return "" @@ -2285,7 +2267,7 @@ func localControlChildLine(text string, width int) string { func browserOpenSummary(detail string, target string) string { target = strings.TrimRight(strings.TrimSpace(target), "/") - for _, line := range strings.Split(detail, "\n") { + for line := range strings.SplitSeq(detail, "\n") { line = strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), "✓")) if line == "" { continue @@ -2303,7 +2285,7 @@ func browserOpenSummary(detail string, target string) string { } func firstLocalControlSummaryLine(detail string) string { - for _, line := range strings.Split(detail, "\n") { + for line := range strings.SplitSeq(detail, "\n") { line = strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), "✓")) switch { case line == "": @@ -2318,10 +2300,10 @@ func firstLocalControlSummaryLine(detail string) string { } func artifactCapturedPath(detail string) string { - for _, line := range strings.Split(detail, "\n") { + for line := range strings.SplitSeq(detail, "\n") { line = strings.TrimSpace(line) - if strings.HasPrefix(line, "Artifact captured:") { - return strings.TrimSpace(strings.TrimPrefix(line, "Artifact captured:")) + if after, ok := strings.CutPrefix(line, "Artifact captured:"); ok { + return strings.TrimSpace(after) } } return "" @@ -2331,7 +2313,7 @@ func bashCardBody(command string, detail string, width int, opts cardRenderOptio footer := "" output := []commandOutputLine{} section := "stdout" - for _, line := range strings.Split(detail, "\n") { + for line := range strings.SplitSeq(detail, "\n") { switch { case line == "stdout:": section = "stdout" @@ -2359,7 +2341,7 @@ func execCommandCardBody(command string, detail string, width int, opts cardRend output := []commandOutputLine{} section := "" interrupted := false - for _, line := range strings.Split(detail, "\n") { + for line := range strings.SplitSeq(detail, "\n") { switch { case line == "output:": section = "output" @@ -2446,7 +2428,7 @@ func truncateDisplayWidth(text string, budget int) string { // record without separators is a plain trailing hint. func renderSessionsCards(payload string, width int) string { blocks := []string{} - for _, record := range strings.Split(payload, "\n") { + for record := range strings.SplitSeq(payload, "\n") { fields := strings.Split(record, sessionsCardFieldSep) if len(fields) < 4 { blocks = append(blocks, fitStyledLine(zeroTheme.faint.Render(record), width)) diff --git a/internal/tui/ripple.go b/internal/tui/ripple.go index 872989de..be57f64d 100644 --- a/internal/tui/ripple.go +++ b/internal/tui/ripple.go @@ -68,13 +68,7 @@ func rippleText(text string, palette []lipgloss.Style, phase, waveLen int) strin var b strings.Builder for i, r := range text { - level := rippleLevel(i+phase, travel, waveLen) - if level < 0 { - level = 0 - } - if level > travel { - level = travel - } + level := min(max(rippleLevel(i+phase, travel, waveLen), 0), travel) b.WriteString(palette[level].Render(string(r))) } return b.String() diff --git a/internal/tui/sandbox_setup_command.go b/internal/tui/sandbox_setup_command.go index 8956a3da..b5f896f4 100644 --- a/internal/tui/sandbox_setup_command.go +++ b/internal/tui/sandbox_setup_command.go @@ -96,7 +96,7 @@ func sandboxSetupResultText(result SandboxSetupCommandResult) string { func sandboxSetupResultLines(result SandboxSetupCommandResult) []string { lines := []string{} for _, text := range []string{result.Output, result.Error} { - for _, line := range strings.Split(text, "\n") { + for line := range strings.SplitSeq(text, "\n") { line = strings.TrimSpace(line) if line != "" { lines = append(lines, line) diff --git a/internal/tui/session_controls.go b/internal/tui/session_controls.go index 4ce15fb5..36432939 100644 --- a/internal/tui/session_controls.go +++ b/internal/tui/session_controls.go @@ -3,6 +3,7 @@ package tui import ( "context" "fmt" + "slices" "strconv" "strings" @@ -175,12 +176,7 @@ func (m model) cycleReasoningEffort() (model, tea.Cmd) { } func reasoningEffortAllowed(efforts []modelregistry.ReasoningEffort, want modelregistry.ReasoningEffort) bool { - for _, effort := range efforts { - if effort == want { - return true - } - } - return false + return slices.Contains(efforts, want) } // reasoningEffortIndex returns the position of want in efforts, or -1. Sibling @@ -313,12 +309,7 @@ func (m model) selfCorrectText() string { } func responseStyleAllowed(value string) bool { - for _, style := range responseStyles { - if value == style { - return true - } - } - return false + return slices.Contains(responseStyles, value) } func (m model) handleCompactCommand(args string) (model, string, tea.Cmd) { diff --git a/internal/tui/sidebar.go b/internal/tui/sidebar.go index a4528145..c53f3130 100644 --- a/internal/tui/sidebar.go +++ b/internal/tui/sidebar.go @@ -560,7 +560,7 @@ func shortTaskName(task string) string { return "" } picked := make([]string, 0, 2) - for _, w := range strings.Fields(task) { + for w := range strings.FieldsSeq(task) { clean := strings.Trim(w, ".,:;!?\"'`()[]{}") if clean == "" { continue @@ -919,17 +919,14 @@ func (m model) sidebarTokenText() string { // to their column widths and to the same row count first, so every joined row // is exactly chatWidth + 1 + sidebarWidth cells and the columns stay aligned. func joinColumns(chat []string, sidebar []string, chatW, sidebarW int) []string { - rows := len(chat) - if len(sidebar) > rows { - rows = len(sidebar) - } + rows := max(len(sidebar), len(chat)) // A cell of air on each side of the rule (" │ ") so the columns don't butt // flush against it. The chat side gets its gutter from the leading space; the // sidebar side from the trailing space (plus items' own leading inset, which // nests them under the flush section headers). Budgeted by chatColumnWidth(-3). divider := " " + zeroTheme.line.Render("│") + " " out := make([]string, rows) - for i := 0; i < rows; i++ { + for i := range rows { left := "" if i < len(chat) { left = chat[i] diff --git a/internal/tui/specialist_card.go b/internal/tui/specialist_card.go index cf4624fb..7732a07e 100644 --- a/internal/tui/specialist_card.go +++ b/internal/tui/specialist_card.go @@ -284,10 +284,7 @@ func (m model) renderSpecialistCard(info specialistInfo, width int) string { // Description truncation. The header reserves room for the icon, the name, // the two " · " separators, the elapsed string, and a safety margin. Clamp // to zero so very long names never underflow. - descMax := width - len(info.name) - 25 - if descMax < 0 { - descMax = 0 - } + descMax := max(width-len(info.name)-25, 0) description := truncateRunes(info.description, descMax) // Header line: icon + name + " · " + description + " · " + elapsed. @@ -340,10 +337,7 @@ func (m model) renderSpecialistCard(info specialistInfo, width int) string { // Optional error detail line. if info.status == specialistError && strings.TrimSpace(info.errorMsg) != "" { - errMax := width - 4 - if errMax < 1 { - errMax = 1 - } + errMax := max(width-4, 1) errMsg := truncateRunes(strings.TrimSpace(info.errorMsg), errMax) lines = append(lines, zeroTheme.red.Render(" "+errMsg)) } diff --git a/internal/tui/startup.go b/internal/tui/startup.go index ae59a58f..a82fde00 100644 --- a/internal/tui/startup.go +++ b/internal/tui/startup.go @@ -213,18 +213,12 @@ func startupHeaderLine(width int, candidates []headerCandidate) string { } func centerLine(line string, width int) string { - padding := (width - lipgloss.Width(line)) / 2 - if padding < 0 { - padding = 0 - } + padding := max((width-lipgloss.Width(line))/2, 0) return strings.Repeat(" ", padding) + line } func rightAlignedLine(line string, width int) string { - padding := width - lipgloss.Width(line) - if padding < 0 { - padding = 0 - } + padding := max(width-lipgloss.Width(line), 0) return strings.Repeat(" ", padding) + line } diff --git a/internal/tui/streaming_fade.go b/internal/tui/streaming_fade.go index 676ffb9a..341295bd 100644 --- a/internal/tui/streaming_fade.go +++ b/internal/tui/streaming_fade.go @@ -155,12 +155,10 @@ func ageDimLine(line string, bornAt, now time.Time, base lipgloss.Style) string if bornAt.IsZero() { return base.Render(line) } - age := now.Sub(bornAt) - if age < 0 { + age := max(now.Sub(bornAt), // Clock skew (e.g. the test fixture hand-codes bornAt in the // future). Treat as freshest. - age = 0 - } + 0) if age >= streamingFadeDuration { return base.Render(line) } diff --git a/internal/tui/view.go b/internal/tui/view.go index b1a7e557..0b0f258a 100644 --- a/internal/tui/view.go +++ b/internal/tui/view.go @@ -467,8 +467,8 @@ func gitBranch(cwd string) string { return "" } ref := strings.TrimSpace(string(data)) - if strings.HasPrefix(ref, "ref: ") { - ref = strings.TrimPrefix(ref, "ref: ") + if after, ok := strings.CutPrefix(ref, "ref: "); ok { + ref = after return strings.TrimPrefix(ref, "refs/heads/") } if len(ref) >= 7 { @@ -966,7 +966,7 @@ func looksLikeDiff(text string) bool { return false } hasOld, hasNew := false, false - for _, line := range strings.Split(text, "\n") { + for line := range strings.SplitSeq(text, "\n") { switch { case hunkHeaderPattern.MatchString(line): return true From 701f74c80809fcd2229f9ebf491d3aa0ef5b40a4 Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:15:09 +0300 Subject: [PATCH 16/19] refactor(tui): use for range N, strings.Cut/SplitSeq/CutPrefix in tests and remaining files - Use for range N for test iteration loops - Use strings.CutPrefix instead of HasPrefix+TrimPrefix - Use strings.SplitSeq instead of strings.Split - Use slices.Contains for contains checks - session_title.go: use SplitSeq for title cleaning --- internal/tui/ask_user_test.go | 4 ++-- internal/tui/autocomplete_test.go | 12 ++++-------- internal/tui/coalesce_test.go | 2 +- internal/tui/commands_test.go | 8 ++------ internal/tui/doctor_view_test.go | 16 ++++++++-------- internal/tui/file_view_test.go | 2 +- internal/tui/health_command_test.go | 8 ++------ internal/tui/hover_test.go | 2 +- internal/tui/image_attach_test.go | 8 ++++---- internal/tui/model_test.go | 10 +++------- internal/tui/mouse_test.go | 8 ++++---- internal/tui/onboarding_test.go | 14 +++++++------- internal/tui/pr_status_test.go | 16 ++++++---------- internal/tui/provider_wizard_test.go | 10 +++------- internal/tui/rendering_lime_test.go | 10 +++++----- internal/tui/ripple_test.go | 2 +- internal/tui/scroll_test.go | 10 +++++----- internal/tui/session_controls_test.go | 4 ++-- internal/tui/session_test.go | 2 +- internal/tui/session_title.go | 2 +- internal/tui/specialist_card_test.go | 4 ++-- internal/tui/startup_test.go | 2 +- internal/tui/streaming_decoder_test.go | 2 +- internal/tui/subchat_regression_test.go | 2 +- internal/tui/theme_picker_test.go | 2 +- internal/tui/tool_render_registry.go | 2 +- internal/tui/transcript_view_test.go | 4 ++-- internal/tui/width_tiers_test.go | 2 +- 28 files changed, 73 insertions(+), 97 deletions(-) diff --git a/internal/tui/ask_user_test.go b/internal/tui/ask_user_test.go index b463021c..cae36caa 100644 --- a/internal/tui/ask_user_test.go +++ b/internal/tui/ask_user_test.go @@ -86,7 +86,7 @@ func TestAskUserSingleTypeMyOwnSubmitsTypedText(t *testing.T) { next := newAskUserModel(t, askUserSingle([]string{"Postgres", "SQLite", "MySQL"}, "SQLite"), &answers) // Move from SQLite (1) to the "type your own" entry (index 3). - for i := 0; i < 2; i++ { + for range 2 { updated, _ := next.Update(testKey(tea.KeyDown)) next = updated.(model) } @@ -168,7 +168,7 @@ func TestAskUserTypeMyOwnEscReturnsToPicker(t *testing.T) { var answers [][]string next := newAskUserModel(t, askUserSingle([]string{"Postgres", "SQLite", "MySQL"}, "SQLite"), &answers) - for i := 0; i < 2; i++ { // to type-your-own + for range 2 { // to type-your-own updated, _ := next.Update(testKey(tea.KeyDown)) next = updated.(model) } diff --git a/internal/tui/autocomplete_test.go b/internal/tui/autocomplete_test.go index 7a7ecf46..822ce798 100644 --- a/internal/tui/autocomplete_test.go +++ b/internal/tui/autocomplete_test.go @@ -4,6 +4,7 @@ import ( "context" "os" "path/filepath" + "slices" "strings" "testing" @@ -32,12 +33,7 @@ func suggestionNames(m model) []string { } func contains(names []string, want string) bool { - for _, name := range names { - if name == want { - return true - } - } - return false + return slices.Contains(names, want) } func TestSuggestionsSurfaceMatchingCommands(t *testing.T) { @@ -330,7 +326,7 @@ func TestSuggestionOverlayStaysVisibleWhenTranscriptScrolled(t *testing.T) { m.width, m.height = 96, 32 m.altScreen = true m.headerPrinted = true - for i := 0; i < 20; i++ { + for i := range 20 { m.transcript = appendRow(m.transcript, rowAssistant, "message "+string(rune('A'+i))) } m.chatScrollOffset = 12 @@ -810,7 +806,7 @@ func TestFileSuggestionsBoundCountsDirectories(t *testing.T) { root := t.TempDir() // Many empty directories sort before "zzz" lexically, so WalkDir visits them // first and exhausts the budget before reaching the matching file. - for i := 0; i < 50; i++ { + for i := range 50 { if err := os.MkdirAll(filepath.Join(root, "dir"+string(rune('a'+i%26))+string(rune('0'+i/26))), 0o755); err != nil { t.Fatal(err) } diff --git a/internal/tui/coalesce_test.go b/internal/tui/coalesce_test.go index 9157b3e9..082231fa 100644 --- a/internal/tui/coalesce_test.go +++ b/internal/tui/coalesce_test.go @@ -118,7 +118,7 @@ func TestCoalescerFlushesOnRunSwitch(t *testing.T) { // before a following non-text message, never after it. Run with -race to catch // the interleaving CodeRabbit flagged. func TestCoalescerOrdersTextBeforeNonTextUnderConcurrency(t *testing.T) { - for iter := 0; iter < 200; iter++ { + for iter := range 200 { rec := &recorder{} c := newTextCoalescer(rec.forward) diff --git a/internal/tui/commands_test.go b/internal/tui/commands_test.go index 6ce182f6..f963f969 100644 --- a/internal/tui/commands_test.go +++ b/internal/tui/commands_test.go @@ -4,6 +4,7 @@ import ( "context" "path/filepath" "reflect" + "slices" "strings" "testing" "time" @@ -915,12 +916,7 @@ func (tool commandTestMCPTool) MCPServerName() string { } func commandTestStringSliceContains(values []string, want string) bool { - for _, value := range values { - if value == want { - return true - } - } - return false + return slices.Contains(values, want) } func commandSuggestionNamesContain(suggestions []commandSuggestion, want string) bool { diff --git a/internal/tui/doctor_view_test.go b/internal/tui/doctor_view_test.go index a2fd9dc5..d5fde186 100644 --- a/internal/tui/doctor_view_test.go +++ b/internal/tui/doctor_view_test.go @@ -238,26 +238,26 @@ func doctorSection(output commandOutput, title string) *commandSection { func assertRowsContain(t *testing.T, rows []commandRow, wants ...string) { t.Helper() - text := "" + var text strings.Builder for _, row := range rows { - text += row.Text + "\n" + text.WriteString(row.Text);text.WriteString("\n") } for _, want := range wants { - if !strings.Contains(text, want) { - t.Fatalf("rows missing %q:\n%s", want, text) + if !strings.Contains(text.String(), want) { + t.Fatalf("rows missing %q:\n%s", want, text.String()) } } } func assertRowsDoNotContain(t *testing.T, rows []commandRow, wants ...string) { t.Helper() - text := "" + var text strings.Builder for _, row := range rows { - text += row.Text + "\n" + text.WriteString(row.Text);text.WriteString("\n") } for _, want := range wants { - if strings.Contains(text, want) { - t.Fatalf("rows unexpectedly contain %q:\n%s", want, text) + if strings.Contains(text.String(), want) { + t.Fatalf("rows unexpectedly contain %q:\n%s", want, text.String()) } } } diff --git a/internal/tui/file_view_test.go b/internal/tui/file_view_test.go index 3856ea6d..295f1dcc 100644 --- a/internal/tui/file_view_test.go +++ b/internal/tui/file_view_test.go @@ -243,7 +243,7 @@ func TestOpenFileViewSamePathIsNoOp(t *testing.T) { func TestFileViewFullBodyTruncatesLongFile(t *testing.T) { dir := t.TempDir() var b strings.Builder - for i := 0; i < fileViewMaxLines+50; i++ { + for i := range fileViewMaxLines + 50 { fmt.Fprintf(&b, "line %d\n", i) } if err := os.WriteFile(filepath.Join(dir, "big.txt"), []byte(b.String()), 0o644); err != nil { diff --git a/internal/tui/health_command_test.go b/internal/tui/health_command_test.go index 150cc6c0..9b7cd2bc 100644 --- a/internal/tui/health_command_test.go +++ b/internal/tui/health_command_test.go @@ -1,6 +1,7 @@ package tui import ( + "slices" "strings" "testing" ) @@ -49,12 +50,7 @@ func TestHealthAliasIsDiscoverableByNamesAndAutocomplete(t *testing.T) { } func healthCommandStringSliceContains(values []string, want string) bool { - for _, value := range values { - if value == want { - return true - } - } - return false + return slices.Contains(values, want) } func healthCommandSuggestionNamesContain(suggestions []commandSuggestion, want string) bool { diff --git a/internal/tui/hover_test.go b/internal/tui/hover_test.go index 3454300e..e0390bcb 100644 --- a/internal/tui/hover_test.go +++ b/internal/tui/hover_test.go @@ -231,7 +231,7 @@ func TestHoverChangesSidebarRenderOutput(t *testing.T) { func TestHoverClearsOnWheelScroll(t *testing.T) { m := mouseTestModel() - for i := 0; i < 80; i++ { + for range 80 { m.transcript = appendRow(m.transcript, rowUser, "line content") } m.hover = hoverTarget{kind: hoverTranscript, bodyY: 5} diff --git a/internal/tui/image_attach_test.go b/internal/tui/image_attach_test.go index d4754028..f3a19e9d 100644 --- a/internal/tui/image_attach_test.go +++ b/internal/tui/image_attach_test.go @@ -281,18 +281,18 @@ func writeTestPDF(t *testing.T, dir, name, text string) string { buf.WriteString("3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 5 0 R >> >> /Contents 4 0 R >>\nendobj\n") content := "BT /F1 24 Tf 72 700 Td (" + text + ") Tj ET" startObj() - buf.WriteString("4 0 obj\n<< /Length " + strconv.Itoa(len(content)) + " >>\nstream\n") + buf.WriteString("4 0 obj\n<< /Length ");buf.WriteString(strconv.Itoa(len(content)));buf.WriteString(" >>\nstream\n") buf.WriteString(content) buf.WriteString("\nendstream\nendobj\n") startObj() buf.WriteString("5 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n") xrefStart := buf.Len() - buf.WriteString("xref\n0 " + strconv.Itoa(len(offsets)+1) + "\n0000000000 65535 f \n") + buf.WriteString("xref\n0 ");buf.WriteString(strconv.Itoa(len(offsets) + 1));buf.WriteString("\n0000000000 65535 f \n") for _, off := range offsets { - buf.WriteString(fmt.Sprintf("%010d 00000 n \n", off)) + fmt.Fprintf(&buf, "%010d 00000 n \n", off) } - buf.WriteString("trailer\n<< /Size " + strconv.Itoa(len(offsets)+1) + " /Root 1 0 R >>\nstartxref\n" + strconv.Itoa(xrefStart) + "\n%%EOF\n") + buf.WriteString("trailer\n<< /Size ");buf.WriteString(strconv.Itoa(len(offsets) + 1));buf.WriteString(" /Root 1 0 R >>\nstartxref\n");buf.WriteString(strconv.Itoa(xrefStart));buf.WriteString("\n%%EOF\n") path := filepath.Join(dir, name) if err := os.WriteFile(path, buf.Bytes(), 0o644); err != nil { diff --git a/internal/tui/model_test.go b/internal/tui/model_test.go index 4c4efbfd..aaaa4c81 100644 --- a/internal/tui/model_test.go +++ b/internal/tui/model_test.go @@ -6,6 +6,7 @@ import ( "errors" "path/filepath" "reflect" + "slices" "strings" "testing" "time" @@ -2505,7 +2506,7 @@ func findTranscriptRow(rows []transcriptRow, kind rowKind) (transcriptRow, bool) func transcriptHasMarkedModelEntry(rows []transcriptRow) bool { for _, row := range rows { - for _, line := range strings.Split(row.text, "\n") { + for line := range strings.SplitSeq(row.text, "\n") { trimmed := strings.TrimSpace(line) if strings.HasPrefix(trimmed, "* ") && strings.Contains(trimmed, " (") { return true @@ -2516,12 +2517,7 @@ func transcriptHasMarkedModelEntry(rows []transcriptRow) bool { } func stringSliceContains(values []string, want string) bool { - for _, value := range values { - if value == want { - return true - } - } - return false + return slices.Contains(values, want) } func testPromptPermissionEvent() agent.PermissionEvent { diff --git a/internal/tui/mouse_test.go b/internal/tui/mouse_test.go index aac79b8a..283b6215 100644 --- a/internal/tui/mouse_test.go +++ b/internal/tui/mouse_test.go @@ -452,7 +452,7 @@ func TestTranscriptSelectionDragPastTopEdgeAutoScrolls(t *testing.T) { // between a local dev shell and CI's non-TTY runners — set it explicitly // so the test is deterministic regardless of environment. m.reducedMotion = false - for i := 0; i < 80; i++ { + for range 80 { m.transcript = appendRow(m.transcript, rowUser, "line content") } textY := topmostVisibleTranscriptMouseY(t, m) @@ -505,7 +505,7 @@ func TestTranscriptSelectionDragPastBottomEdgeAutoScrolls(t *testing.T) { m := mouseTestModel() m.mouseCapture = true m.reducedMotion = false // deterministic across local/CI TTY-detection differences - for i := 0; i < 80; i++ { + for range 80 { m.transcript = appendRow(m.transcript, rowUser, "line content") } m.chatScrollOffset = chatWheelScrollLines * 3 // scrolled up, away from the bottom @@ -554,7 +554,7 @@ func TestTranscriptSelectionEdgeGlideStopsWhenDragReturnsToBody(t *testing.T) { m := mouseTestModel() m.mouseCapture = true m.reducedMotion = false // deterministic across local/CI TTY-detection differences - for i := 0; i < 80; i++ { + for range 80 { m.transcript = appendRow(m.transcript, rowUser, "line content") } textY := topmostVisibleTranscriptMouseY(t, m) @@ -596,7 +596,7 @@ func TestTranscriptSelectionEdgeGlideRecoversAfterKeypressInterrupt(t *testing.T m := mouseTestModel() m.mouseCapture = true m.reducedMotion = false // deterministic across local/CI TTY-detection differences - for i := 0; i < 80; i++ { + for range 80 { m.transcript = appendRow(m.transcript, rowUser, "line content") } textY := topmostVisibleTranscriptMouseY(t, m) diff --git a/internal/tui/onboarding_test.go b/internal/tui/onboarding_test.go index 8a9ca7ee..87fa6890 100644 --- a/internal/tui/onboarding_test.go +++ b/internal/tui/onboarding_test.go @@ -1121,7 +1121,7 @@ func TestSetupProviderStepOmitsModelDetails(t *testing.T) { if strings.Contains(view, "Default model:") || strings.Contains(view, "gpt-4.1") || strings.Contains(view, "claude-sonnet-4.5") { t.Fatalf("provider step should not render model details:\n%s", view) } - for _, line := range strings.Split(view, "\n") { + for line := range strings.SplitSeq(view, "\n") { row := strings.TrimSpace(line) if strings.Contains(row, "Choose a provider") { titleColumn = displayColumn(line, "Choose a provider") @@ -1183,7 +1183,7 @@ func TestSetupProviderSelectionDoesNotShiftBlock(t *testing.T) { func TestSetupProviderLongCatalogUsesVisibleWindow(t *testing.T) { providers := make([]SetupProviderOption, 0, 14) - for index := 0; index < 14; index++ { + for index := range 14 { providers = append(providers, SetupProviderOption{ ID: "provider", Name: "Provider " + string(rune('A'+index)), @@ -1489,7 +1489,7 @@ func pressSetupContinueOnce(m model) model { func displayColumnForVisibleLine(t *testing.T, view any, marker string) int { t.Helper() rendered := plainRender(t, view) - for _, line := range strings.Split(rendered, "\n") { + for line := range strings.SplitSeq(rendered, "\n") { if strings.Contains(line, marker) { return displayColumn(line, marker) } @@ -1499,11 +1499,11 @@ func displayColumnForVisibleLine(t *testing.T, view any, marker string) int { } func displayColumn(line string, marker string) int { - index := strings.Index(line, marker) - if index < 0 { + before, _, ok := strings.Cut(line, marker) + if !ok { return -1 } - return lipgloss.Width(line[:index]) + return lipgloss.Width(before) } func assertSetupLineCentered(t *testing.T, view any, marker string, width int) { @@ -1521,7 +1521,7 @@ func assertSetupLineCentered(t *testing.T, view any, marker string, width int) { func visibleLineForMarker(t *testing.T, view any, marker string) string { t.Helper() rendered := plainRender(t, view) - for _, line := range strings.Split(rendered, "\n") { + for line := range strings.SplitSeq(rendered, "\n") { if strings.Contains(line, marker) { return line } diff --git a/internal/tui/pr_status_test.go b/internal/tui/pr_status_test.go index 4511e0b0..3ff4bd0e 100644 --- a/internal/tui/pr_status_test.go +++ b/internal/tui/pr_status_test.go @@ -3,6 +3,7 @@ package tui import ( "context" "errors" + "slices" "strings" "testing" ) @@ -102,15 +103,15 @@ func TestBuildPRSegments(t *testing.T) { } segments := BuildPRSegments(state, false) - gotText := "" + var gotText strings.Builder for _, segment := range segments { - gotText += segment.Text + gotText.WriteString(segment.Text) if segment.URL != state.PrURL { t.Fatalf("segment %#v should carry PR hyperlink", segment) } } - if gotText != "+42 -7 #1337" { - t.Fatalf("segments text = %q", gotText) + if gotText.String() != "+42 -7 #1337" { + t.Fatalf("segments text = %q", gotText.String()) } } @@ -166,10 +167,5 @@ func (runner *fakePRRunner) Run(ctx context.Context, cwd string, name string, ar } func (runner *fakePRRunner) called(call string) bool { - for _, actual := range runner.calls { - if actual == call { - return true - } - } - return false + return slices.Contains(runner.calls, call) } diff --git a/internal/tui/provider_wizard_test.go b/internal/tui/provider_wizard_test.go index ee832a29..38b68887 100644 --- a/internal/tui/provider_wizard_test.go +++ b/internal/tui/provider_wizard_test.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "path/filepath" + "slices" "strings" "testing" @@ -1068,7 +1069,7 @@ func finishProviderWizardModelDiscoveryForTest(t *testing.T, m model) model { func providerWizardManyModelsForTest(count int) []providerWizardModel { models := make([]providerWizardModel, 0, count) - for index := 0; index < count; index++ { + for index := range count { models = append(models, providerWizardModel{ ID: fmt.Sprintf("provider/model-%02d", index), Description: fmt.Sprintf("Model %02d", index), @@ -1247,12 +1248,7 @@ func readProviderWizardConfigFixture(t *testing.T, path string) config.FileConfi } func containsString(values []string, want string) bool { - for _, value := range values { - if value == want { - return true - } - } - return false + return slices.Contains(values, want) } // Applying the wizard switches the live provider, so it must export diff --git a/internal/tui/rendering_lime_test.go b/internal/tui/rendering_lime_test.go index a76ea030..d33afabf 100644 --- a/internal/tui/rendering_lime_test.go +++ b/internal/tui/rendering_lime_test.go @@ -450,7 +450,7 @@ func TestMarkdownTableConvertsHtmlBreaks(t *testing.T) { func countMarkdownRuleLines(rendered string) int { count := 0 - for _, line := range strings.Split(rendered, "\n") { + for line := range strings.SplitSeq(rendered, "\n") { if strings.Contains(line, "─┼─") { count++ } @@ -632,7 +632,7 @@ func TestSelectableExpandedReasoningRowsAreClamped(t *testing.T) { } rendered, _ := m.renderSelectableReasoningRow(0, row, width, 0) - for _, line := range strings.Split(rendered, "\n") { + for line := range strings.SplitSeq(rendered, "\n") { if got := lipgloss.Width(plainRender(t, line)); got > width { t.Fatalf("line width = %d, want <= %d:\n%s", got, width, rendered) } @@ -839,7 +839,7 @@ func TestNarrationSelectionGeometry(t *testing.T) { t.Errorf("meta %d copy text should be chrome-free, got %q", i, meta.text) } } - for _, line := range strings.Split(rendered, "\n") { + for line := range strings.SplitSeq(rendered, "\n") { if w := lipgloss.Width(line); w > 90 { t.Errorf("narration line width %d exceeds 90: %q", w, line) } @@ -942,7 +942,7 @@ func TestDiffCardBodyRendersCountsNumbersAndCap(t *testing.T) { // The 16-line cap keeps long diffs bounded. long := []string{"+++ b/big.go", "@@ -0,0 +1,40 @@"} - for i := 0; i < 40; i++ { + for range 40 { long = append(long, "+line") } row.detail = strings.Join(long, "\n") @@ -1535,7 +1535,7 @@ func TestToolCardLinesAllSameWidth(t *testing.T) { detail := "internal/cli/root.go:41: fs := flag.NewFlagSet" row := transcriptRow{kind: rowToolResult, id: "c", tool: "grep", status: tools.StatusOK, detail: detail} card := plainRender(t, m.renderRow(row, 60, buildRowContext(nil))) - for _, line := range strings.Split(card, "\n") { + for line := range strings.SplitSeq(card, "\n") { if got := len([]rune(line)); got != 60 { t.Fatalf("card line width %d, want 60: %q", got, line) } diff --git a/internal/tui/ripple_test.go b/internal/tui/ripple_test.go index 79fe5b61..a2d26232 100644 --- a/internal/tui/ripple_test.go +++ b/internal/tui/ripple_test.go @@ -95,7 +95,7 @@ func TestRipplePhaseShiftChangesBytesAcrossString(t *testing.T) { // phase must produce distinct outputs (the run-state text actually changes). func TestRippleAdvancesPhaseAcrossFrames(t *testing.T) { seen := map[string]bool{} - for phase := 0; phase < 12; phase++ { + for phase := range 12 { seen[rippleText("Working", ripplePalette(), phase, 6)] = true } if len(seen) < 2 { diff --git a/internal/tui/scroll_test.go b/internal/tui/scroll_test.go index 2a94729c..dc2eafda 100644 --- a/internal/tui/scroll_test.go +++ b/internal/tui/scroll_test.go @@ -15,7 +15,7 @@ func TestMouseWheelScrollsChatWithoutRecallingInputHistory(t *testing.T) { m.mouseCapture = true m.inputHistory = []string{"old prompt"} m.historyIdx = len(m.inputHistory) - for index := 0; index < 12; index++ { + for index := range 12 { m.transcript = appendRow(m.transcript, rowAssistant, "message "+string(rune('A'+index))) } @@ -36,7 +36,7 @@ func TestScrollChatClampsOffsetAtTranscriptTop(t *testing.T) { m := newModel(context.Background(), Options{AltScreen: true}) m.width = 90 m.height = 14 - for index := 0; index < 40; index++ { + for index := range 40 { m.transcript = appendRow(m.transcript, rowAssistant, "message "+string(rune('A'+index%26))) } maxOffset := m.chatMaxScrollOffset() @@ -116,7 +116,7 @@ func TestAltScreenTranscriptScrollKeepsFooterFixed(t *testing.T) { m.width = 90 m.height = 10 m.gitBranch = "feat/pinned-header" - for index := 0; index < 14; index++ { + for index := range 14 { m.transcript = appendRow(m.transcript, rowAssistant, "message "+string(rune('A'+index))) } @@ -161,7 +161,7 @@ func TestEmptySubmitKeepsChatScrollOffset(t *testing.T) { m := newModel(context.Background(), Options{AltScreen: true}) m.width = 90 m.height = 14 - for index := 0; index < 12; index++ { + for index := range 12 { m.transcript = appendRow(m.transcript, rowAssistant, "message "+string(rune('A'+index))) } @@ -189,7 +189,7 @@ func TestPageKeysScrollAltScreenTranscript(t *testing.T) { m := newModel(context.Background(), Options{AltScreen: true}) m.width = 90 m.height = 20 - for index := 0; index < 30; index++ { + for index := range 30 { m.transcript = appendRow(m.transcript, rowAssistant, "message "+string(rune('A'+index%26))) } diff --git a/internal/tui/session_controls_test.go b/internal/tui/session_controls_test.go index 7bfd37eb..47fa7236 100644 --- a/internal/tui/session_controls_test.go +++ b/internal/tui/session_controls_test.go @@ -31,8 +31,8 @@ func TestEffortCommandListsAndSetsSupportedEffort(t *testing.T) { // payload. var cardPayload string for _, row := range next.transcript { - if strings.HasPrefix(row.text, "\x00command-card\x00") { - cardPayload = strings.TrimPrefix(row.text, "\x00command-card\x00") + if after, ok := strings.CutPrefix(row.text, "\x00command-card\x00"); ok { + cardPayload = after break } } diff --git a/internal/tui/session_test.go b/internal/tui/session_test.go index 51f1f2b8..0ce2556b 100644 --- a/internal/tui/session_test.go +++ b/internal/tui/session_test.go @@ -463,7 +463,7 @@ func submitAndDrivePermissionRun(t *testing.T, m model, prompt string, key strin finalCh <- execCmd(cmd) }() - for received := 0; received < expectedRuntimeMessages; received++ { + for range expectedRuntimeMessages { runtimeMsg := receiveRuntimeMessage(t, runtimeMessages) updated, _ = next.Update(runtimeMsg) next = updated.(model) diff --git a/internal/tui/session_title.go b/internal/tui/session_title.go index 3705ac1b..aca62687 100644 --- a/internal/tui/session_title.go +++ b/internal/tui/session_title.go @@ -112,7 +112,7 @@ func cleanGeneratedTitle(raw string) string { } // Use the first line with real content after trimming markup — a model may // open with a bare code fence or blank line before the title itself. - for _, line := range strings.Split(title, "\n") { + for line := range strings.SplitSeq(title, "\n") { if trimmed := strings.Trim(line, titleTrimCutset); trimmed != "" { title = trimmed break diff --git a/internal/tui/specialist_card_test.go b/internal/tui/specialist_card_test.go index 26bbeafb..72090133 100644 --- a/internal/tui/specialist_card_test.go +++ b/internal/tui/specialist_card_test.go @@ -189,7 +189,7 @@ func TestRenderSpecialistCard(t *testing.T) { } plain := ansiPattern.ReplaceAllString(got, "") // Left-rule: every line starts with │. - for _, line := range strings.Split(plain, "\n") { + for line := range strings.SplitSeq(plain, "\n") { if !strings.HasPrefix(line, "│") { t.Errorf("card line %q should start with │ (left-rule)", line) } @@ -276,7 +276,7 @@ func TestRenderLeftRuleCard(t *testing.T) { } plain := ansiPattern.ReplaceAllString(got, "") // Left rule present on every line. - for _, line := range strings.Split(plain, "\n") { + for line := range strings.SplitSeq(plain, "\n") { if !strings.HasPrefix(line, "│") { t.Errorf("line %q should start with left rule │", line) } diff --git a/internal/tui/startup_test.go b/internal/tui/startup_test.go index c535c337..71faa330 100644 --- a/internal/tui/startup_test.go +++ b/internal/tui/startup_test.go @@ -79,7 +79,7 @@ func TestBorderedBlockFitsLongStyledLines(t *testing.T) { func assertRenderedLineWidths(t *testing.T, block string, width int) { t.Helper() - for _, line := range strings.Split(block, "\n") { + for line := range strings.SplitSeq(block, "\n") { if got := lipgloss.Width(line); got != width { t.Fatalf("expected line width %d, got %d for %q", width, got, line) } diff --git a/internal/tui/streaming_decoder_test.go b/internal/tui/streaming_decoder_test.go index b6c5be3c..84f7af86 100644 --- a/internal/tui/streaming_decoder_test.go +++ b/internal/tui/streaming_decoder_test.go @@ -92,7 +92,7 @@ func TestStreamingDecoderTailIsBounded(t *testing.T) { d := newStreamingDecoder() var b strings.Builder b.WriteString(`{"path":"x","content":"`) - for i := 0; i < 100; i++ { + for range 100 { b.WriteString("line\\n") // 100 escaped newlines } b.WriteString(`"}`) diff --git a/internal/tui/subchat_regression_test.go b/internal/tui/subchat_regression_test.go index 325a5bbd..cd8d9625 100644 --- a/internal/tui/subchat_regression_test.go +++ b/internal/tui/subchat_regression_test.go @@ -64,7 +64,7 @@ func TestTranscriptSelectionExtendsAcrossWheelScroll(t *testing.T) { // content, see transcriptViewport.window: start = totalLines-height-offset), so // with overflow content the topmost VISIBLE line is not the transcript's first // line — topmostVisibleTranscriptMouseY (window-aware) finds it correctly. - for i := 0; i < 80; i++ { + for range 80 { m.transcript = appendRow(m.transcript, rowUser, "line content") } textY := topmostVisibleTranscriptMouseY(t, m) diff --git a/internal/tui/theme_picker_test.go b/internal/tui/theme_picker_test.go index 14c337ce..dc53a1e0 100644 --- a/internal/tui/theme_picker_test.go +++ b/internal/tui/theme_picker_test.go @@ -151,7 +151,7 @@ func TestThemePickerPreviewsOnMove(t *testing.T) { m = updated.(model) // Each Down moves to the next theme and live-applies its palette; Up follows back. - for i := 0; i < 3; i++ { + for range 3 { updated, _ = m.Update(testKey(tea.KeyDown)) m = updated.(model) assertPreviewMatchesSelection(t, m) diff --git a/internal/tui/tool_render_registry.go b/internal/tui/tool_render_registry.go index cb027164..666e1f53 100644 --- a/internal/tui/tool_render_registry.go +++ b/internal/tui/tool_render_registry.go @@ -83,7 +83,7 @@ func newDefaultToolBodyRegistry() *toolBodyRegistry { // already renders. Falls back to the generic body if the text isn't a plan. func planSummaryCardBody(req toolBodyRequest) cardBody { total, done, active, failed := 0, 0, 0, 0 - for _, line := range strings.Split(req.detail, "\n") { + for line := range strings.SplitSeq(req.detail, "\n") { line = strings.TrimSpace(line) if !isNumberedPlanLine(line) { continue diff --git a/internal/tui/transcript_view_test.go b/internal/tui/transcript_view_test.go index b34a8270..1bc2c8ee 100644 --- a/internal/tui/transcript_view_test.go +++ b/internal/tui/transcript_view_test.go @@ -128,7 +128,7 @@ func TestPgUpDownScrollsDetailedTranscript(t *testing.T) { m := transcriptViewTestModel() m.altScreen = true // Fill the transcript enough to overflow the 30-row viewport. - for i := 0; i < 60; i++ { + for range 60 { m.transcript = append(m.transcript, transcriptRow{kind: rowUser, text: "line content", final: true}) } m.flushed = len(m.transcript) @@ -162,7 +162,7 @@ func TestPgUpDownScrollsDetailedTranscript(t *testing.T) { func TestUpDownArrowsScrollDetailedTranscript(t *testing.T) { m := transcriptViewTestModel() m.altScreen = true - for i := 0; i < 60; i++ { + for range 60 { m.transcript = append(m.transcript, transcriptRow{kind: rowUser, text: "line content", final: true}) } m.flushed = len(m.transcript) diff --git a/internal/tui/width_tiers_test.go b/internal/tui/width_tiers_test.go index f9345383..2a4edecd 100644 --- a/internal/tui/width_tiers_test.go +++ b/internal/tui/width_tiers_test.go @@ -112,7 +112,7 @@ func TestTinyTierSingleSegmentAndRailLessCards(t *testing.T) { row := transcriptRow{kind: rowToolResult, id: "c", tool: "grep", status: tools.StatusOK, detail: "a.go:1: x"} card := plainRender(t, m.renderRow(row, 40, buildRowContext(nil))) - for _, line := range strings.Split(card, "\n") { + for line := range strings.SplitSeq(card, "\n") { if strings.HasPrefix(line, "│") || strings.HasSuffix(line, "│") { t.Fatalf("tiny card keeps side borders: %q", line) } From 4a8a2fc727441748433e66ce122dcdc98874ac53 Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:15:15 +0300 Subject: [PATCH 17/19] refactor(update,usercommands,verify,workspaceseed): use strings.Cut, SplitSeq, slices.Contains - update: use strings.Cut instead of Index+slice for comma parsing - usercommands: use SplitSeq for frontmatter iteration - verify: use SplitSeq for output summarization, slices.Contains for test - workspaceseed: use slices.Contains for memoryFiles detection --- internal/update/update.go | 6 +++--- internal/usercommands/usercommands.go | 2 +- internal/verify/contracts.go | 2 +- internal/verify/verify.go | 2 +- internal/verify/verify_test.go | 10 +++------- internal/workspaceseed/workspaceseed.go | 8 +++----- internal/workspaceseed/workspaceseed_test.go | 8 ++------ 7 files changed, 14 insertions(+), 24 deletions(-) diff --git a/internal/update/update.go b/internal/update/update.go index e3eb5c9b..b158b4a7 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -270,11 +270,11 @@ func fetchRelease(ctx context.Context, endpoint string) (release Release, err er } func fetchDataRelease(endpoint string) (Release, error) { - comma := strings.Index(endpoint, ",") - if comma == -1 { + _, after, ok := strings.Cut(endpoint, ",") + if !ok { return Release{}, fmt.Errorf("invalid data update endpoint") } - payload, err := url.QueryUnescape(endpoint[comma+1:]) + payload, err := url.QueryUnescape(after) if err != nil { return Release{}, err } diff --git a/internal/usercommands/usercommands.go b/internal/usercommands/usercommands.go index 3cb969ca..6d15d961 100644 --- a/internal/usercommands/usercommands.go +++ b/internal/usercommands/usercommands.go @@ -202,7 +202,7 @@ func splitFrontmatter(normalized string) (string, string, bool) { func frontmatterValue(frontmatter, key string) string { prefix := strings.ToLower(key) + ":" - for _, line := range strings.Split(frontmatter, "\n") { + for line := range strings.SplitSeq(frontmatter, "\n") { trimmed := strings.TrimSpace(line) if strings.HasPrefix(strings.ToLower(trimmed), prefix) { return strings.Trim(strings.TrimSpace(trimmed[len(prefix):]), `"'`) diff --git a/internal/verify/contracts.go b/internal/verify/contracts.go index 0fe781a6..8e759cf3 100644 --- a/internal/verify/contracts.go +++ b/internal/verify/contracts.go @@ -61,7 +61,7 @@ type Event struct { Status Status `json:"status,omitempty"` ExitCode int `json:"exitCode,omitempty"` OK bool `json:"ok,omitempty"` - Summary Summary `json:"summary,omitempty"` + Summary Summary `json:"summary"` StartedAt string `json:"startedAt,omitempty"` EndedAt string `json:"endedAt,omitempty"` DurationMs int `json:"durationMs,omitempty"` diff --git a/internal/verify/verify.go b/internal/verify/verify.go index a1219e78..86a6a326 100644 --- a/internal/verify/verify.go +++ b/internal/verify/verify.go @@ -269,7 +269,7 @@ func summarizeOutput(values ...string) *OutputSummary { contextLines := 0 truncated := false for _, value := range values { - for _, line := range strings.Split(value, "\n") { + for line := range strings.SplitSeq(value, "\n") { trimmed := strings.TrimSpace(line) if trimmed == "" { continue diff --git a/internal/verify/verify_test.go b/internal/verify/verify_test.go index 5a929161..5b053a3e 100644 --- a/internal/verify/verify_test.go +++ b/internal/verify/verify_test.go @@ -5,6 +5,7 @@ import ( "errors" "os" "path/filepath" + "slices" "strings" "testing" "time" @@ -172,7 +173,7 @@ func TestRunSummarizesPlainCommandErrors(t *testing.T) { func TestRunFailureSummaryTruncatesOnlyOnOverflow(t *testing.T) { lines := []string{} - for index := 0; index < maxOutputSummaryLines; index++ { + for range maxOutputSummaryLines { lines = append(lines, "--- FAIL: TestExact (0.00s)") } exact := summarizeOutput(strings.Join(lines, "\n")) @@ -327,12 +328,7 @@ func checkIDs(checks []Check) []string { } func contains(values []string, want string) bool { - for _, value := range values { - if value == want { - return true - } - } - return false + return slices.Contains(values, want) } func checksByID(checks []Check) map[string]Check { diff --git a/internal/workspaceseed/workspaceseed.go b/internal/workspaceseed/workspaceseed.go index 14062037..92e14d7b 100644 --- a/internal/workspaceseed/workspaceseed.go +++ b/internal/workspaceseed/workspaceseed.go @@ -6,6 +6,7 @@ import ( "fmt" "path" "path/filepath" + "slices" "sort" "strings" @@ -249,11 +250,8 @@ func detectedProjectFiles(paths []string) []string { func memoryFiles(paths []string) []string { out := []string{} for _, candidate := range []string{"AGENTS.md", "ZERO.md"} { - for _, rel := range paths { - if rel == candidate { - out = append(out, candidate) - break - } + if slices.Contains(paths, candidate) { + out = append(out, candidate) } } return out diff --git a/internal/workspaceseed/workspaceseed_test.go b/internal/workspaceseed/workspaceseed_test.go index a7c04c21..59ae2b28 100644 --- a/internal/workspaceseed/workspaceseed_test.go +++ b/internal/workspaceseed/workspaceseed_test.go @@ -4,6 +4,7 @@ import ( "os" "path/filepath" "reflect" + "slices" "strings" "testing" ) @@ -184,10 +185,5 @@ func writeSeedFile(t *testing.T, root, rel, contents string) { } func contains(values []string, want string) bool { - for _, value := range values { - if value == want { - return true - } - } - return false + return slices.Contains(values, want) } From dd22ec6ad8286f2876a3323f370ec56e8cec5008 Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 15:15:19 +0300 Subject: [PATCH 18/19] refactor(zerogit,zeroruntime): use SplitSeq, strings.Cut, min builtin - zerogit diffstat: use SplitSeq instead of Split - zerogit zerogit.go: use SplitSeq, min() for truncation - zeroruntime types.go: use strings.Cut instead of IndexByte for media type parsing --- internal/zerogit/diffstat.go | 4 ++-- internal/zerogit/zerogit.go | 9 +++------ internal/zeroruntime/types.go | 4 ++-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/internal/zerogit/diffstat.go b/internal/zerogit/diffstat.go index a09016ad..4b28726f 100644 --- a/internal/zerogit/diffstat.go +++ b/internal/zerogit/diffstat.go @@ -27,7 +27,7 @@ func (stat DiffStat) NetLOC() int { // DiffStat without panicking. func ParseDiffStat(stat string) DiffStat { summary := "" - for _, line := range strings.Split(stat, "\n") { + for line := range strings.SplitSeq(stat, "\n") { trimmed := strings.TrimSpace(line) if strings.Contains(trimmed, "changed,") || strings.Contains(trimmed, "changed ") { if strings.Contains(trimmed, "file") { @@ -39,7 +39,7 @@ func ParseDiffStat(stat string) DiffStat { return DiffStat{} } result := DiffStat{} - for _, segment := range strings.Split(summary, ",") { + for segment := range strings.SplitSeq(summary, ",") { segment = strings.TrimSpace(segment) fields := strings.Fields(segment) if len(fields) < 2 { diff --git a/internal/zerogit/zerogit.go b/internal/zerogit/zerogit.go index f7bff7c1..c9fbd313 100644 --- a/internal/zerogit/zerogit.go +++ b/internal/zerogit/zerogit.go @@ -262,7 +262,7 @@ func parseStatus(status string) []FileChange { func parseNameStatus(output string) []FileChange { files := []FileChange{} - for _, line := range strings.Split(output, "\n") { + for line := range strings.SplitSeq(output, "\n") { if strings.TrimSpace(line) == "" { continue } @@ -469,10 +469,7 @@ func truncateString(value string, maxBytes int) (string, bool) { marker := "\n" + redaction.RedactedSecret budget := maxBytes - len(suffix) - len(marker) if budget <= 0 { - allowed := maxBytes - len(suffix) - if allowed > len(redaction.RedactedSecret) { - allowed = len(redaction.RedactedSecret) - } + allowed := min(maxBytes-len(suffix), len(redaction.RedactedSecret)) return redaction.RedactedSecret[:allowed] + suffix, true } return cutGitRuneBoundary(value, budget) + marker + suffix, true @@ -606,7 +603,7 @@ func Push(ctx context.Context, options PushOptions) (PushResult, error) { func isDefaultBranch(ctx context.Context, runGit Runner, dir, remote, branch string) bool { if out, err := gitOutput(ctx, runGit, dir, "ls-remote", "--symref", remote, "HEAD"); err == nil { - for _, line := range strings.Split(out, "\n") { + for line := range strings.SplitSeq(out, "\n") { line = strings.TrimSpace(line) if strings.HasPrefix(line, "ref: refs/heads/") && strings.HasSuffix(line, "\tHEAD") { symref := strings.TrimPrefix(line, "ref: refs/heads/") diff --git a/internal/zeroruntime/types.go b/internal/zeroruntime/types.go index b2487b68..caa5bcaf 100644 --- a/internal/zeroruntime/types.go +++ b/internal/zeroruntime/types.go @@ -233,8 +233,8 @@ func NormalizeImageMediaType(s string) string { } // Strip a data: URI wrapper, keeping the media type between "data:" and ";". if rest, ok := strings.CutPrefix(s, "data:"); ok { - if i := strings.IndexByte(rest, ';'); i >= 0 { - s = rest[:i] + if before, _, ok0 := strings.Cut(rest, ";"); ok0 { + s = before } else { s = rest } From 3a2eda2abc088b1cf862be04dabd2350b3d25c4b Mon Sep 17 00:00:00 2001 From: N1xev Date: Sun, 5 Jul 2026 18:05:11 +0300 Subject: [PATCH 19/19] formatting --- internal/tui/doctor_view_test.go | 6 ++++-- internal/tui/image_attach_test.go | 14 +++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/internal/tui/doctor_view_test.go b/internal/tui/doctor_view_test.go index d5fde186..a59e7b2e 100644 --- a/internal/tui/doctor_view_test.go +++ b/internal/tui/doctor_view_test.go @@ -240,7 +240,8 @@ func assertRowsContain(t *testing.T, rows []commandRow, wants ...string) { t.Helper() var text strings.Builder for _, row := range rows { - text.WriteString(row.Text);text.WriteString("\n") + text.WriteString(row.Text) + text.WriteString("\n") } for _, want := range wants { if !strings.Contains(text.String(), want) { @@ -253,7 +254,8 @@ func assertRowsDoNotContain(t *testing.T, rows []commandRow, wants ...string) { t.Helper() var text strings.Builder for _, row := range rows { - text.WriteString(row.Text);text.WriteString("\n") + text.WriteString(row.Text) + text.WriteString("\n") } for _, want := range wants { if strings.Contains(text.String(), want) { diff --git a/internal/tui/image_attach_test.go b/internal/tui/image_attach_test.go index f3a19e9d..ef30d727 100644 --- a/internal/tui/image_attach_test.go +++ b/internal/tui/image_attach_test.go @@ -281,18 +281,26 @@ func writeTestPDF(t *testing.T, dir, name, text string) string { buf.WriteString("3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 5 0 R >> >> /Contents 4 0 R >>\nendobj\n") content := "BT /F1 24 Tf 72 700 Td (" + text + ") Tj ET" startObj() - buf.WriteString("4 0 obj\n<< /Length ");buf.WriteString(strconv.Itoa(len(content)));buf.WriteString(" >>\nstream\n") + buf.WriteString("4 0 obj\n<< /Length ") + buf.WriteString(strconv.Itoa(len(content))) + buf.WriteString(" >>\nstream\n") buf.WriteString(content) buf.WriteString("\nendstream\nendobj\n") startObj() buf.WriteString("5 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n") xrefStart := buf.Len() - buf.WriteString("xref\n0 ");buf.WriteString(strconv.Itoa(len(offsets) + 1));buf.WriteString("\n0000000000 65535 f \n") + buf.WriteString("xref\n0 ") + buf.WriteString(strconv.Itoa(len(offsets) + 1)) + buf.WriteString("\n0000000000 65535 f \n") for _, off := range offsets { fmt.Fprintf(&buf, "%010d 00000 n \n", off) } - buf.WriteString("trailer\n<< /Size ");buf.WriteString(strconv.Itoa(len(offsets) + 1));buf.WriteString(" /Root 1 0 R >>\nstartxref\n");buf.WriteString(strconv.Itoa(xrefStart));buf.WriteString("\n%%EOF\n") + buf.WriteString("trailer\n<< /Size ") + buf.WriteString(strconv.Itoa(len(offsets) + 1)) + buf.WriteString(" /Root 1 0 R >>\nstartxref\n") + buf.WriteString(strconv.Itoa(xrefStart)) + buf.WriteString("\n%%EOF\n") path := filepath.Join(dir, name) if err := os.WriteFile(path, buf.Bytes(), 0o644); err != nil {