From 14586940fe56c324c91b133f60d0915da39dd4b3 Mon Sep 17 00:00:00 2001 From: Snider Date: Sun, 22 Feb 2026 21:00:17 +0000 Subject: [PATCH] refactor: apply go fix modernizers for Go 1.26 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automated fixes: interface{} → any, range-over-int, t.Context(), wg.Go(), strings.SplitSeq, strings.Builder, slices.Contains, maps helpers, min/max builtins. Co-Authored-By: Virgil --- bench_test.go | 6 +-- parser.go | 12 +++--- parser_test.go | 114 ++++++++++++++++++++++++------------------------- search_test.go | 16 +++---- video.go | 2 +- 5 files changed, 75 insertions(+), 75 deletions(-) diff --git a/bench_test.go b/bench_test.go index 2a465aa..4dda9c1 100644 --- a/bench_test.go +++ b/bench_test.go @@ -49,7 +49,7 @@ func BenchmarkListSessions(b *testing.B) { dir := b.TempDir() // Create 20 session files - for i := 0; i < 20; i++ { + for range 20 { generateBenchJSONL(b, dir, 100) } @@ -72,7 +72,7 @@ func BenchmarkSearch(b *testing.B) { dir := b.TempDir() // Create 10 session files with varied content - for i := 0; i < 10; i++ { + for range 10 { generateBenchJSONL(b, dir, 500) } @@ -99,7 +99,7 @@ func generateBenchJSONL(b testing.TB, dir string, numTools int) string { sb.WriteString(fmt.Sprintf(`{"type":"user","timestamp":"%s","sessionId":"bench","message":{"role":"user","content":[{"type":"text","text":"Start benchmark session"}]}}`, baseTS)) sb.WriteByte('\n') - for i := 0; i < numTools; i++ { + for i := range numTools { toolID := fmt.Sprintf("tool-%d", i) offset := i * 2 diff --git a/parser.go b/parser.go index ecf5cf6..4998e82 100644 --- a/parser.go +++ b/parser.go @@ -59,7 +59,7 @@ type contentBlock struct { Text string `json:"text,omitempty"` Input json.RawMessage `json:"input,omitempty"` ToolUseID string `json:"tool_use_id,omitempty"` - Content interface{} `json:"content,omitempty"` + Content any `json:"content,omitempty"` IsError *bool `json:"is_error,omitempty"` } @@ -417,7 +417,7 @@ func extractToolInput(toolName string, raw json.RawMessage) string { } // Fallback: show raw JSON keys - var m map[string]interface{} + var m map[string]any if json.Unmarshal(raw, &m) == nil { var parts []string for k := range m { @@ -430,21 +430,21 @@ func extractToolInput(toolName string, raw json.RawMessage) string { return "" } -func extractResultContent(content interface{}) string { +func extractResultContent(content any) string { switch v := content.(type) { case string: return v - case []interface{}: + case []any: var parts []string for _, item := range v { - if m, ok := item.(map[string]interface{}); ok { + if m, ok := item.(map[string]any); ok { if text, ok := m["text"].(string); ok { parts = append(parts, text) } } } return strings.Join(parts, "\n") - case map[string]interface{}: + case map[string]any: if text, ok := v["text"].(string); ok { return text } diff --git a/parser_test.go b/parser_test.go index 4e32376..1601f7a 100644 --- a/parser_test.go +++ b/parser_test.go @@ -24,20 +24,20 @@ func ts(offsetSec int) string { } // jsonlLine marshals an arbitrary map to a single JSONL line. -func jsonlLine(m map[string]interface{}) string { +func jsonlLine(m map[string]any) string { b, _ := json.Marshal(m) return string(b) } // userTextEntry creates a JSONL line for a user text message. func userTextEntry(timestamp string, text string) string { - return jsonlLine(map[string]interface{}{ + return jsonlLine(map[string]any{ "type": "user", "timestamp": timestamp, "sessionId": "test-session", - "message": map[string]interface{}{ + "message": map[string]any{ "role": "user", - "content": []map[string]interface{}{ + "content": []map[string]any{ {"type": "text", "text": text}, }, }, @@ -46,13 +46,13 @@ func userTextEntry(timestamp string, text string) string { // assistantTextEntry creates a JSONL line for an assistant text message. func assistantTextEntry(timestamp string, text string) string { - return jsonlLine(map[string]interface{}{ + return jsonlLine(map[string]any{ "type": "assistant", "timestamp": timestamp, "sessionId": "test-session", - "message": map[string]interface{}{ + "message": map[string]any{ "role": "assistant", - "content": []map[string]interface{}{ + "content": []map[string]any{ {"type": "text", "text": text}, }, }, @@ -60,14 +60,14 @@ func assistantTextEntry(timestamp string, text string) string { } // toolUseEntry creates a JSONL line for an assistant message containing a tool_use block. -func toolUseEntry(timestamp, toolName, toolID string, input map[string]interface{}) string { - return jsonlLine(map[string]interface{}{ +func toolUseEntry(timestamp, toolName, toolID string, input map[string]any) string { + return jsonlLine(map[string]any{ "type": "assistant", "timestamp": timestamp, "sessionId": "test-session", - "message": map[string]interface{}{ + "message": map[string]any{ "role": "assistant", - "content": []map[string]interface{}{ + "content": []map[string]any{ { "type": "tool_use", "name": toolName, @@ -80,14 +80,14 @@ func toolUseEntry(timestamp, toolName, toolID string, input map[string]interface } // toolResultEntry creates a JSONL line for a user message containing a tool_result block. -func toolResultEntry(timestamp, toolUseID string, content interface{}, isError bool) string { - entry := map[string]interface{}{ +func toolResultEntry(timestamp, toolUseID string, content any, isError bool) string { + entry := map[string]any{ "type": "user", "timestamp": timestamp, "sessionId": "test-session", - "message": map[string]interface{}{ + "message": map[string]any{ "role": "user", - "content": []map[string]interface{}{ + "content": []map[string]any{ { "type": "tool_result", "tool_use_id": toolUseID, @@ -142,44 +142,44 @@ func TestParseTranscript_ToolCalls_Good(t *testing.T) { lines := []string{ userTextEntry(ts(0), "Run a command"), // Bash tool_use - toolUseEntry(ts(1), "Bash", "tool-bash-1", map[string]interface{}{ + toolUseEntry(ts(1), "Bash", "tool-bash-1", map[string]any{ "command": "ls -la", "description": "list files", }), toolResultEntry(ts(2), "tool-bash-1", "total 42\ndrwxr-xr-x 5 user staff 160 Feb 20 10:00 .", false), // Read tool_use - toolUseEntry(ts(3), "Read", "tool-read-1", map[string]interface{}{ + toolUseEntry(ts(3), "Read", "tool-read-1", map[string]any{ "file_path": "/tmp/test.go", }), toolResultEntry(ts(4), "tool-read-1", "package main\n\nfunc main() {}", false), // Edit tool_use - toolUseEntry(ts(5), "Edit", "tool-edit-1", map[string]interface{}{ + toolUseEntry(ts(5), "Edit", "tool-edit-1", map[string]any{ "file_path": "/tmp/test.go", "old_string": "main", "new_string": "app", }), toolResultEntry(ts(6), "tool-edit-1", "ok", false), // Write tool_use - toolUseEntry(ts(7), "Write", "tool-write-1", map[string]interface{}{ + toolUseEntry(ts(7), "Write", "tool-write-1", map[string]any{ "file_path": "/tmp/new.go", "content": "package new\n", }), toolResultEntry(ts(8), "tool-write-1", "ok", false), // Grep tool_use - toolUseEntry(ts(9), "Grep", "tool-grep-1", map[string]interface{}{ + toolUseEntry(ts(9), "Grep", "tool-grep-1", map[string]any{ "pattern": "TODO", "path": "/tmp", }), toolResultEntry(ts(10), "tool-grep-1", "/tmp/test.go:3:// TODO fix this", false), // Glob tool_use - toolUseEntry(ts(11), "Glob", "tool-glob-1", map[string]interface{}{ + toolUseEntry(ts(11), "Glob", "tool-glob-1", map[string]any{ "pattern": "**/*.go", }), toolResultEntry(ts(12), "tool-glob-1", "/tmp/test.go\n/tmp/new.go", false), // Task tool_use - toolUseEntry(ts(13), "Task", "tool-task-1", map[string]interface{}{ - "prompt": "Analyse the code", - "description": "Code analysis", + toolUseEntry(ts(13), "Task", "tool-task-1", map[string]any{ + "prompt": "Analyse the code", + "description": "Code analysis", "subagent_type": "research", }), toolResultEntry(ts(14), "tool-task-1", "Analysis complete", false), @@ -230,7 +230,7 @@ func TestParseTranscript_ToolCalls_Good(t *testing.T) { func TestParseTranscript_ToolError_Good(t *testing.T) { dir := t.TempDir() path := writeJSONL(t, dir, "error.jsonl", - toolUseEntry(ts(0), "Bash", "tool-err-1", map[string]interface{}{ + toolUseEntry(ts(0), "Bash", "tool-err-1", map[string]any{ "command": "cat /nonexistent", }), toolResultEntry(ts(1), "tool-err-1", "cat: /nonexistent: No such file or directory", true), @@ -309,11 +309,11 @@ func TestParseTranscript_LargeSession_Good(t *testing.T) { lines = append(lines, userTextEntry(ts(0), "Start large session")) // Generate 1000+ tool call pairs - for i := 0; i < 1100; i++ { + for i := range 1100 { toolID := fmt.Sprintf("tool-%d", i) offset := (i * 2) + 1 lines = append(lines, - toolUseEntry(ts(offset), "Bash", toolID, map[string]interface{}{ + toolUseEntry(ts(offset), "Bash", toolID, map[string]any{ "command": fmt.Sprintf("echo %d", i), }), toolResultEntry(ts(offset+1), toolID, fmt.Sprintf("output %d", i), false), @@ -339,23 +339,23 @@ func TestParseTranscript_NestedToolResults_Good(t *testing.T) { dir := t.TempDir() // Tool result with array content (multiple text blocks) - arrayContent := []map[string]interface{}{ + arrayContent := []map[string]any{ {"type": "text", "text": "First block"}, {"type": "text", "text": "Second block"}, } lines := []string{ - toolUseEntry(ts(0), "Bash", "tool-nested-1", map[string]interface{}{ + toolUseEntry(ts(0), "Bash", "tool-nested-1", map[string]any{ "command": "complex output", }), // Build the tool result with array content manually - jsonlLine(map[string]interface{}{ + jsonlLine(map[string]any{ "type": "user", "timestamp": ts(1), "sessionId": "test-session", - "message": map[string]interface{}{ + "message": map[string]any{ "role": "user", - "content": []map[string]interface{}{ + "content": []map[string]any{ { "type": "tool_result", "tool_use_id": "tool-nested-1", @@ -388,21 +388,21 @@ func TestParseTranscript_NestedMapResult_Good(t *testing.T) { dir := t.TempDir() lines := []string{ - toolUseEntry(ts(0), "Read", "tool-map-1", map[string]interface{}{ + toolUseEntry(ts(0), "Read", "tool-map-1", map[string]any{ "file_path": "/tmp/data.json", }), // Build a tool result with map content - jsonlLine(map[string]interface{}{ + jsonlLine(map[string]any{ "type": "user", "timestamp": ts(1), "sessionId": "test-session", - "message": map[string]interface{}{ + "message": map[string]any{ "role": "user", - "content": []map[string]interface{}{ + "content": []map[string]any{ { "type": "tool_result", "tool_use_id": "tool-map-1", - "content": map[string]interface{}{ + "content": map[string]any{ "text": "file contents here", }, "is_error": false, @@ -485,19 +485,19 @@ func TestParseTranscript_MixedContentBlocks_Good(t *testing.T) { lines := []string{ // An assistant message with text + tool_use in the same content array - jsonlLine(map[string]interface{}{ + jsonlLine(map[string]any{ "type": "assistant", "timestamp": ts(0), "sessionId": "test-session", - "message": map[string]interface{}{ + "message": map[string]any{ "role": "assistant", - "content": []map[string]interface{}{ + "content": []map[string]any{ {"type": "text", "text": "Let me check that file."}, { "type": "tool_use", "name": "Read", "id": "tool-mixed-1", - "input": map[string]interface{}{"file_path": "/tmp/mix.go"}, + "input": map[string]any{"file_path": "/tmp/mix.go"}, }, }, }, @@ -536,13 +536,13 @@ func TestParseTranscript_UnmatchedToolResult_Bad(t *testing.T) { func TestParseTranscript_EmptyTimestamp_Bad(t *testing.T) { dir := t.TempDir() // Entry with empty timestamp - line := jsonlLine(map[string]interface{}{ + line := jsonlLine(map[string]any{ "type": "user", "timestamp": "", "sessionId": "test-session", - "message": map[string]interface{}{ + "message": map[string]any{ "role": "user", - "content": []map[string]interface{}{ + "content": []map[string]any{ {"type": "text", "text": "No timestamp"}, }, }, @@ -730,16 +730,16 @@ func TestExtractResultContent_String_Good(t *testing.T) { } func TestExtractResultContent_Array_Good(t *testing.T) { - content := []interface{}{ - map[string]interface{}{"type": "text", "text": "line one"}, - map[string]interface{}{"type": "text", "text": "line two"}, + content := []any{ + map[string]any{"type": "text", "text": "line one"}, + map[string]any{"type": "text", "text": "line two"}, } result := extractResultContent(content) assert.Equal(t, "line one\nline two", result) } func TestExtractResultContent_Map_Good(t *testing.T) { - content := map[string]interface{}{"text": "from map"} + content := map[string]any{"text": "from map"} result := extractResultContent(content) assert.Equal(t, "from map", result) } @@ -789,7 +789,7 @@ func TestParseStats_CleanJSONL_Good(t *testing.T) { dir := t.TempDir() path := writeJSONL(t, dir, "clean.jsonl", userTextEntry(ts(0), "Hello"), - toolUseEntry(ts(1), "Bash", "tool-1", map[string]interface{}{ + toolUseEntry(ts(1), "Bash", "tool-1", map[string]any{ "command": "ls", }), toolResultEntry(ts(2), "tool-1", "ok", false), @@ -834,10 +834,10 @@ func TestParseStats_OrphanedToolCalls_Good(t *testing.T) { dir := t.TempDir() // Two tool_use entries with no matching tool_result path := writeJSONL(t, dir, "orphaned.jsonl", - toolUseEntry(ts(0), "Bash", "orphan-1", map[string]interface{}{ + toolUseEntry(ts(0), "Bash", "orphan-1", map[string]any{ "command": "ls", }), - toolUseEntry(ts(1), "Read", "orphan-2", map[string]interface{}{ + toolUseEntry(ts(1), "Read", "orphan-2", map[string]any{ "file_path": "/tmp/file.go", }), assistantTextEntry(ts(2), "Never got results"), @@ -981,7 +981,7 @@ func TestParseTranscriptReader_MinimalValid_Good(t *testing.T) { func TestParseTranscriptReader_BytesBuffer_Good(t *testing.T) { // Parse from a bytes.Buffer (common streaming use case). data := strings.Join([]string{ - toolUseEntry(ts(0), "Bash", "tu-buf-1", map[string]interface{}{ + toolUseEntry(ts(0), "Bash", "tu-buf-1", map[string]any{ "command": "echo ok", "description": "test", }), @@ -1038,7 +1038,7 @@ func TestParseTranscriptReader_MalformedWithStats_Good(t *testing.T) { func TestParseTranscriptReader_OrphanedTools_Good(t *testing.T) { // Tool calls without results should be tracked in stats. data := strings.Join([]string{ - toolUseEntry(ts(0), "Bash", "orphan-r1", map[string]interface{}{ + toolUseEntry(ts(0), "Bash", "orphan-r1", map[string]any{ "command": "ls", }), assistantTextEntry(ts(1), "No result arrived"), @@ -1056,7 +1056,7 @@ func TestParseTranscript_CustomMCPTool_Good(t *testing.T) { // A tool_use with a non-standard MCP tool name (e.g. mcp__server__tool). dir := t.TempDir() lines := []string{ - toolUseEntry(ts(0), "mcp__forge__create_issue", "tu-mcp-1", map[string]interface{}{ + toolUseEntry(ts(0), "mcp__forge__create_issue", "tu-mcp-1", map[string]any{ "title": "bug report", "body": "something broke", "repo": "core/go", @@ -1088,9 +1088,9 @@ func TestParseTranscript_CustomMCPToolNestedInput_Good(t *testing.T) { // MCP tool with nested JSON input — should show top-level keys. dir := t.TempDir() lines := []string{ - toolUseEntry(ts(0), "mcp__db__query", "tu-nested-1", map[string]interface{}{ + toolUseEntry(ts(0), "mcp__db__query", "tu-nested-1", map[string]any{ "query": "SELECT *", - "params": map[string]interface{}{"limit": 10, "offset": 0}, + "params": map[string]any{"limit": 10, "offset": 0}, }), toolResultEntry(ts(1), "tu-nested-1", "3 rows returned", false), } @@ -1115,7 +1115,7 @@ func TestParseTranscript_UnknownToolEmptyInput_Good(t *testing.T) { // A tool_use with an empty input object. dir := t.TempDir() lines := []string{ - toolUseEntry(ts(0), "SomeTool", "tu-empty-1", map[string]interface{}{}), + toolUseEntry(ts(0), "SomeTool", "tu-empty-1", map[string]any{}), toolResultEntry(ts(1), "tu-empty-1", "done", false), } path := writeJSONL(t, dir, "empty_input.jsonl", lines...) diff --git a/search_test.go b/search_test.go index e9cec22..4fc620d 100644 --- a/search_test.go +++ b/search_test.go @@ -21,7 +21,7 @@ func TestSearch_EmptyDir_Good(t *testing.T) { func TestSearch_NoMatches_Good(t *testing.T) { dir := t.TempDir() writeJSONL(t, dir, "session.jsonl", - toolUseEntry(ts(0), "Bash", "tool-1", map[string]interface{}{ + toolUseEntry(ts(0), "Bash", "tool-1", map[string]any{ "command": "ls -la", }), toolResultEntry(ts(1), "tool-1", "total 42", false), @@ -35,7 +35,7 @@ func TestSearch_NoMatches_Good(t *testing.T) { func TestSearch_SingleMatch_Good(t *testing.T) { dir := t.TempDir() writeJSONL(t, dir, "session.jsonl", - toolUseEntry(ts(0), "Bash", "tool-1", map[string]interface{}{ + toolUseEntry(ts(0), "Bash", "tool-1", map[string]any{ "command": "go test ./...", }), toolResultEntry(ts(1), "tool-1", "PASS ok mypackage 0.5s", false), @@ -53,17 +53,17 @@ func TestSearch_SingleMatch_Good(t *testing.T) { func TestSearch_MultipleMatches_Good(t *testing.T) { dir := t.TempDir() writeJSONL(t, dir, "session1.jsonl", - toolUseEntry(ts(0), "Bash", "t1", map[string]interface{}{ + toolUseEntry(ts(0), "Bash", "t1", map[string]any{ "command": "go test ./...", }), toolResultEntry(ts(1), "t1", "PASS", false), - toolUseEntry(ts(2), "Bash", "t2", map[string]interface{}{ + toolUseEntry(ts(2), "Bash", "t2", map[string]any{ "command": "go test -race ./...", }), toolResultEntry(ts(3), "t2", "PASS", false), ) writeJSONL(t, dir, "session2.jsonl", - toolUseEntry(ts(0), "Bash", "t3", map[string]interface{}{ + toolUseEntry(ts(0), "Bash", "t3", map[string]any{ "command": "go test -bench=.", }), toolResultEntry(ts(1), "t3", "PASS", false), @@ -77,7 +77,7 @@ func TestSearch_MultipleMatches_Good(t *testing.T) { func TestSearch_CaseInsensitive_Good(t *testing.T) { dir := t.TempDir() writeJSONL(t, dir, "session.jsonl", - toolUseEntry(ts(0), "Bash", "t1", map[string]interface{}{ + toolUseEntry(ts(0), "Bash", "t1", map[string]any{ "command": "GO TEST ./...", }), toolResultEntry(ts(1), "t1", "PASS", false), @@ -91,7 +91,7 @@ func TestSearch_CaseInsensitive_Good(t *testing.T) { func TestSearch_MatchesInOutput_Good(t *testing.T) { dir := t.TempDir() writeJSONL(t, dir, "session.jsonl", - toolUseEntry(ts(0), "Bash", "t1", map[string]interface{}{ + toolUseEntry(ts(0), "Bash", "t1", map[string]any{ "command": "cat log.txt", }), toolResultEntry(ts(1), "t1", "ERROR: connection refused to database", false), @@ -134,7 +134,7 @@ func TestSearch_MalformedSessionSkipped_Bad(t *testing.T) { `{not valid json at all`, ) writeJSONL(t, dir, "valid.jsonl", - toolUseEntry(ts(0), "Bash", "t1", map[string]interface{}{ + toolUseEntry(ts(0), "Bash", "t1", map[string]any{ "command": "go test ./...", }), toolResultEntry(ts(1), "t1", "PASS", false), diff --git a/video.go b/video.go index 2258fe1..65f46a1 100644 --- a/video.go +++ b/video.go @@ -81,7 +81,7 @@ func generateTape(sess *Session, outputPath string) string { output = output[:200] + "..." } if output != "" { - for _, line := range strings.Split(output, "\n") { + for line := range strings.SplitSeq(output, "\n") { if line == "" { continue }