From dc144108bee64fe9e2aecb11b4fe8b90536ca14c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Sep 2025 23:25:31 +0000 Subject: [PATCH 1/3] Initial plan From 4a9642c295d3f8b29f74795113d65cfdc2a3fe86 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Sep 2025 23:38:39 +0000 Subject: [PATCH 2/3] Update Claude log parsers to handle mixed debug logs + JSONL format Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/ci-doctor.lock.yml | 38 +++++++- .github/workflows/dev.lock.yml | 38 +++++++- pkg/workflow/claude_engine.go | 47 +++++++++- pkg/workflow/js/parse_claude_log.cjs | 43 +++++++++- pkg/workflow/log_parser_test.go | 124 ++++++++++++++++++++++++++- 5 files changed, 272 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 7cb26f3ca45..cbce5b0ea3f 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -1738,11 +1738,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split('\n'); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === '') { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith('{')) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -1894,7 +1924,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index c87b69025e5..e5929f1633a 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -1734,11 +1734,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split('\n'); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === '') { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith('{')) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -1890,7 +1920,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/workflow/claude_engine.go b/pkg/workflow/claude_engine.go index 4aded7ebfe2..32f3ee3316b 100644 --- a/pkg/workflow/claude_engine.go +++ b/pkg/workflow/claude_engine.go @@ -817,17 +817,56 @@ func (e *ClaudeEngine) extractClaudeResultMetrics(line string) LogMetrics { return metrics } -// parseClaudeJSONLog parses Claude logs as a JSON array to find the result payload +// parseClaudeJSONLog parses Claude logs as a JSON array or mixed format (debug logs + JSONL) func (e *ClaudeEngine) parseClaudeJSONLog(logContent string, verbose bool) LogMetrics { var metrics LogMetrics - // Try to parse the entire log as a JSON array + // Try to parse the entire log as a JSON array first (old format) var logEntries []map[string]interface{} if err := json.Unmarshal([]byte(logContent), &logEntries); err != nil { + // If that fails, try to parse as mixed format (debug logs + JSONL) if verbose { - fmt.Printf("Failed to parse Claude log as JSON array: %v\n", err) + fmt.Printf("Failed to parse Claude log as JSON array, trying JSONL format: %v\n", err) + } + + logEntries = []map[string]interface{}{} + lines := strings.Split(logContent, "\n") + + for _, line := range lines { + trimmedLine := strings.TrimSpace(line) + if trimmedLine == "" { + continue // Skip empty lines + } + + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if !strings.HasPrefix(trimmedLine, "{") { + continue + } + + // Try to parse each line as JSON + var jsonEntry map[string]interface{} + if err := json.Unmarshal([]byte(trimmedLine), &jsonEntry); err != nil { + // Skip invalid JSON lines (could be partial debug output) + if verbose { + fmt.Printf("Skipping invalid JSON line: %s\n", trimmedLine[:min(50, len(trimmedLine))]) + } + continue + } + + logEntries = append(logEntries, jsonEntry) + } + + if len(logEntries) == 0 { + if verbose { + fmt.Printf("No valid JSON entries found in Claude log\n") + } + return metrics + } + + if verbose { + fmt.Printf("Extracted %d JSON entries from mixed format Claude log\n", len(logEntries)) } - return metrics } // Look for the result entry with type: "result" diff --git a/pkg/workflow/js/parse_claude_log.cjs b/pkg/workflow/js/parse_claude_log.cjs index cadbae70fb2..498e69c5432 100644 --- a/pkg/workflow/js/parse_claude_log.cjs +++ b/pkg/workflow/js/parse_claude_log.cjs @@ -38,11 +38,46 @@ function main() { */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split('\n'); + + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === '') { + continue; // Skip empty lines + } + + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith('{')) { + continue; + } + + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -213,7 +248,7 @@ function parseClaudeLog(logContent) { } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/workflow/log_parser_test.go b/pkg/workflow/log_parser_test.go index 8df3f968a01..d5d378289a4 100644 --- a/pkg/workflow/log_parser_test.go +++ b/pkg/workflow/log_parser_test.go @@ -136,7 +136,7 @@ func TestParseClaudeLogSmoke(t *testing.T) { if err != nil { t.Fatalf("Failed to parse invalid Claude log: %v", err) } - if !strings.Contains(result, "Error parsing Claude log") { + if !strings.Contains(result, "Log format not recognized") { t.Error("Expected error message for invalid JSON in Claude log") } @@ -145,7 +145,7 @@ func TestParseClaudeLogSmoke(t *testing.T) { if err != nil { t.Fatalf("Failed to parse empty Claude log: %v", err) } - if !strings.Contains(result, "Error parsing Claude log") { + if !strings.Contains(result, "Log format not recognized") { t.Error("Expected error message for empty Claude log") } } @@ -217,3 +217,123 @@ func TestParseClaudeLogInitialization(t *testing.T) { t.Error("Expected Claude log output to contain Slash Commands section") } } + +// Test parsing Claude logs in mixed format (debug logs + JSONL) +func TestParseClaudeMixedFormatLog(t *testing.T) { + script := GetLogParserScript("parse_claude_log") + if script == "" { + t.Skip("parse_claude_log script not available") + } + + // Test with mixed format log (debug logs + JSONL entries) + mixedFormatLog := `2025-09-15T23:22:45.123Z [DEBUG] Initializing Claude Code CLI +2025-09-15T23:22:45.125Z [INFO] Session started +{"type":"system","subtype":"init","session_id":"test-123","tools":["Bash","Read"],"model":"claude-sonnet-4-20250514"} +2025-09-15T23:22:45.130Z [DEBUG] Processing user prompt +{"type":"assistant","message":{"content":[{"type":"text","text":"I'll help you with this task."},{"type":"tool_use","id":"tool_123","name":"Bash","input":{"command":"echo 'Hello World'"}}]}} +2025-09-15T23:22:45.135Z [DEBUG] Executing bash command +{"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"tool_123","content":"Hello World"}]}} +2025-09-15T23:22:45.140Z [INFO] Workflow completed successfully +{"type":"result","total_cost_usd":0.0015,"usage":{"input_tokens":150,"output_tokens":50},"num_turns":1,"duration_ms":2000} +2025-09-15T23:22:45.145Z [DEBUG] Cleanup completed` + + result, err := runJSLogParser(script, mixedFormatLog) + if err != nil { + t.Fatalf("Failed to parse mixed format Claude log: %v", err) + } + + // Verify essential sections are present + if !strings.Contains(result, "🚀 Initialization") { + t.Error("Expected mixed format Claude log output to contain Initialization section") + } + if !strings.Contains(result, "🤖 Commands and Tools") { + t.Error("Expected mixed format Claude log output to contain Commands and Tools section") + } + if !strings.Contains(result, "🤖 Reasoning") { + t.Error("Expected mixed format Claude log output to contain Reasoning section") + } + if !strings.Contains(result, "echo 'Hello World'") { + t.Error("Expected mixed format Claude log output to contain the bash command") + } + if !strings.Contains(result, "Total Cost") { + t.Error("Expected mixed format Claude log output to contain cost information") + } + if !strings.Contains(result, "test-123") { + t.Error("Expected mixed format Claude log output to contain session ID") + } + + // Test backward compatibility with pure JSON array format + jsonArrayLog := `[ + {"type":"system","subtype":"init","session_id":"test-456","tools":["Bash","Read"],"model":"claude-sonnet-4-20250514"}, + {"type":"assistant","message":{"content":[{"type":"text","text":"Working on it."},{"type":"tool_use","id":"tool_456","name":"Bash","input":{"command":"ls -la"}}]}}, + {"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"tool_456","content":"total 0"}]}}, + {"type":"result","total_cost_usd":0.002,"usage":{"input_tokens":100,"output_tokens":40},"num_turns":1} + ]` + + result, err = runJSLogParser(script, jsonArrayLog) + if err != nil { + t.Fatalf("Failed to parse JSON array Claude log: %v", err) + } + + // Verify backward compatibility works + if !strings.Contains(result, "🚀 Initialization") { + t.Error("Expected JSON array Claude log output to contain Initialization section") + } + if !strings.Contains(result, "ls -la") { + t.Error("Expected JSON array Claude log output to contain the bash command") + } + if !strings.Contains(result, "test-456") { + t.Error("Expected JSON array Claude log output to contain session ID") + } +} + +// Test Go Claude engine mixed format parsing +func TestClaudeEngineMixedFormatParsing(t *testing.T) { + engine := NewClaudeEngine() + + // Test with mixed format log (debug logs + JSONL entries) + mixedFormatLog := `2025-09-15T23:22:45.123Z [DEBUG] Initializing Claude Code CLI +2025-09-15T23:22:45.125Z [INFO] Session started +{"type":"system","subtype":"init","session_id":"test-123","tools":["Bash","Read"],"model":"claude-sonnet-4-20250514"} +2025-09-15T23:22:45.130Z [DEBUG] Processing user prompt +{"type":"assistant","message":{"content":[{"type":"text","text":"I'll help you with this task."},{"type":"tool_use","id":"tool_123","name":"Bash","input":{"command":"echo 'Hello World'"}}]}} +2025-09-15T23:22:45.135Z [DEBUG] Executing bash command +{"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"tool_123","content":"Hello World"}]}} +2025-09-15T23:22:45.140Z [INFO] Workflow completed successfully +{"type":"result","total_cost_usd":0.0015,"usage":{"input_tokens":150,"output_tokens":50},"num_turns":1,"duration_ms":2000} +2025-09-15T23:22:45.145Z [DEBUG] Cleanup completed` + + metrics := engine.ParseLogMetrics(mixedFormatLog, true) + + // Verify that metrics were extracted + if metrics.TokenUsage != 200 { + t.Errorf("Expected token usage 200 (150+50), got %d", metrics.TokenUsage) + } + if metrics.EstimatedCost != 0.0015 { + t.Errorf("Expected cost 0.0015, got %f", metrics.EstimatedCost) + } + if metrics.Turns != 1 { + t.Errorf("Expected 1 turn, got %d", metrics.Turns) + } + + // Test backward compatibility with pure JSON array format + jsonArrayLog := `[ + {"type":"system","subtype":"init","session_id":"test-456","tools":["Bash","Read"],"model":"claude-sonnet-4-20250514"}, + {"type":"assistant","message":{"content":[{"type":"text","text":"Working on it."},{"type":"tool_use","id":"tool_456","name":"Bash","input":{"command":"ls -la"}}]}}, + {"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"tool_456","content":"total 0"}]}}, + {"type":"result","total_cost_usd":0.002,"usage":{"input_tokens":100,"output_tokens":40},"num_turns":1} + ]` + + metrics = engine.ParseLogMetrics(jsonArrayLog, true) + + // Verify backward compatibility + if metrics.TokenUsage != 140 { + t.Errorf("Expected token usage 140 (100+40), got %d", metrics.TokenUsage) + } + if metrics.EstimatedCost != 0.002 { + t.Errorf("Expected cost 0.002, got %f", metrics.EstimatedCost) + } + if metrics.Turns != 1 { + t.Errorf("Expected 1 turn, got %d", metrics.Turns) + } +} From d1a3dcc044c8d3f2f3e967948376b2fe974d11a6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Sep 2025 23:42:03 +0000 Subject: [PATCH 3/3] Final validation and testing completed - Claude log parsers updated successfully Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/ci-doctor.lock.yml | 8 ++-- .github/workflows/dev.lock.yml | 8 ++-- .../test-ai-inference-github-models.lock.yml | 38 +++++++++++++++++-- .../test-claude-add-issue-comment.lock.yml | 38 +++++++++++++++++-- .../test-claude-add-issue-labels.lock.yml | 38 +++++++++++++++++-- .../test-claude-cache-memory.lock.yml | 38 +++++++++++++++++-- .../workflows/test-claude-command.lock.yml | 38 +++++++++++++++++-- .../test-claude-create-issue.lock.yml | 38 +++++++++++++++++-- ...reate-pull-request-review-comment.lock.yml | 38 +++++++++++++++++-- .../test-claude-create-pull-request.lock.yml | 38 +++++++++++++++++-- ...eate-repository-security-advisory.lock.yml | 38 +++++++++++++++++-- pkg/cli/workflows/test-claude-mcp.lock.yml | 38 +++++++++++++++++-- .../test-claude-missing-tool.lock.yml | 38 +++++++++++++++++-- .../test-claude-push-to-pr-branch.lock.yml | 38 +++++++++++++++++-- .../test-claude-update-issue.lock.yml | 38 +++++++++++++++++-- ...playwright-accessibility-contrast.lock.yml | 38 +++++++++++++++++-- pkg/workflow/claude_engine.go | 14 +++---- pkg/workflow/js/parse_claude_log.cjs | 18 ++++----- 18 files changed, 500 insertions(+), 80 deletions(-) diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index cbce5b0ea3f..fd44623380a 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -1748,15 +1748,15 @@ jobs: } catch (jsonArrayError) { // If that fails, try to parse as mixed format (debug logs + JSONL) logEntries = []; - const lines = logContent.split('\n'); + const lines = logContent.split("\n"); for (const line of lines) { const trimmedLine = line.trim(); - if (trimmedLine === '') { + if (trimmedLine === "") { continue; // Skip empty lines } - // Skip debug log lines that don't start with { + // Skip debug log lines that don't start with { // (these are typically timestamped debug messages) - if (!trimmedLine.startsWith('{')) { + if (!trimmedLine.startsWith("{")) { continue; } // Try to parse each line as JSON diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index e5929f1633a..8cce21c582c 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -1744,15 +1744,15 @@ jobs: } catch (jsonArrayError) { // If that fails, try to parse as mixed format (debug logs + JSONL) logEntries = []; - const lines = logContent.split('\n'); + const lines = logContent.split("\n"); for (const line of lines) { const trimmedLine = line.trim(); - if (trimmedLine === '') { + if (trimmedLine === "") { continue; // Skip empty lines } - // Skip debug log lines that don't start with { + // Skip debug log lines that don't start with { // (these are typically timestamped debug messages) - if (!trimmedLine.startsWith('{')) { + if (!trimmedLine.startsWith("{")) { continue; } // Try to parse each line as JSON diff --git a/pkg/cli/workflows/test-ai-inference-github-models.lock.yml b/pkg/cli/workflows/test-ai-inference-github-models.lock.yml index e8ef3351209..5b073ba3f45 100644 --- a/pkg/cli/workflows/test-ai-inference-github-models.lock.yml +++ b/pkg/cli/workflows/test-ai-inference-github-models.lock.yml @@ -341,11 +341,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -497,7 +527,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-claude-add-issue-comment.lock.yml b/pkg/cli/workflows/test-claude-add-issue-comment.lock.yml index f1f790bc2c7..e0ff606b5da 100644 --- a/pkg/cli/workflows/test-claude-add-issue-comment.lock.yml +++ b/pkg/cli/workflows/test-claude-add-issue-comment.lock.yml @@ -338,11 +338,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -494,7 +524,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-claude-add-issue-labels.lock.yml b/pkg/cli/workflows/test-claude-add-issue-labels.lock.yml index c411b52e4c1..19dabb0f425 100644 --- a/pkg/cli/workflows/test-claude-add-issue-labels.lock.yml +++ b/pkg/cli/workflows/test-claude-add-issue-labels.lock.yml @@ -338,11 +338,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -494,7 +524,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-claude-cache-memory.lock.yml b/pkg/cli/workflows/test-claude-cache-memory.lock.yml index 8d89b9b642b..632427008e4 100644 --- a/pkg/cli/workflows/test-claude-cache-memory.lock.yml +++ b/pkg/cli/workflows/test-claude-cache-memory.lock.yml @@ -403,11 +403,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -559,7 +589,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-claude-command.lock.yml b/pkg/cli/workflows/test-claude-command.lock.yml index fc935a595b4..d770468793f 100644 --- a/pkg/cli/workflows/test-claude-command.lock.yml +++ b/pkg/cli/workflows/test-claude-command.lock.yml @@ -338,11 +338,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -494,7 +524,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-claude-create-issue.lock.yml b/pkg/cli/workflows/test-claude-create-issue.lock.yml index cb4651287e8..8bb4514eff8 100644 --- a/pkg/cli/workflows/test-claude-create-issue.lock.yml +++ b/pkg/cli/workflows/test-claude-create-issue.lock.yml @@ -338,11 +338,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -494,7 +524,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-claude-create-pull-request-review-comment.lock.yml b/pkg/cli/workflows/test-claude-create-pull-request-review-comment.lock.yml index 2796376dd4d..fb07829d0da 100644 --- a/pkg/cli/workflows/test-claude-create-pull-request-review-comment.lock.yml +++ b/pkg/cli/workflows/test-claude-create-pull-request-review-comment.lock.yml @@ -338,11 +338,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -494,7 +524,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-claude-create-pull-request.lock.yml b/pkg/cli/workflows/test-claude-create-pull-request.lock.yml index b5a26ed222e..47acdab6e9c 100644 --- a/pkg/cli/workflows/test-claude-create-pull-request.lock.yml +++ b/pkg/cli/workflows/test-claude-create-pull-request.lock.yml @@ -343,11 +343,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -499,7 +529,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-claude-create-repository-security-advisory.lock.yml b/pkg/cli/workflows/test-claude-create-repository-security-advisory.lock.yml index 1b9c8058cfd..9806b814f2a 100644 --- a/pkg/cli/workflows/test-claude-create-repository-security-advisory.lock.yml +++ b/pkg/cli/workflows/test-claude-create-repository-security-advisory.lock.yml @@ -341,11 +341,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -497,7 +527,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-claude-mcp.lock.yml b/pkg/cli/workflows/test-claude-mcp.lock.yml index 0330da402ca..6da0a40ceb5 100644 --- a/pkg/cli/workflows/test-claude-mcp.lock.yml +++ b/pkg/cli/workflows/test-claude-mcp.lock.yml @@ -341,11 +341,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -497,7 +527,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-claude-missing-tool.lock.yml b/pkg/cli/workflows/test-claude-missing-tool.lock.yml index 747e1179a81..7c9cc88221e 100644 --- a/pkg/cli/workflows/test-claude-missing-tool.lock.yml +++ b/pkg/cli/workflows/test-claude-missing-tool.lock.yml @@ -1699,11 +1699,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -1855,7 +1885,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-claude-push-to-pr-branch.lock.yml b/pkg/cli/workflows/test-claude-push-to-pr-branch.lock.yml index 6424ffc0975..4ca15b27362 100644 --- a/pkg/cli/workflows/test-claude-push-to-pr-branch.lock.yml +++ b/pkg/cli/workflows/test-claude-push-to-pr-branch.lock.yml @@ -343,11 +343,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -499,7 +529,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-claude-update-issue.lock.yml b/pkg/cli/workflows/test-claude-update-issue.lock.yml index 9f46ddf0c34..fd72e0a09fc 100644 --- a/pkg/cli/workflows/test-claude-update-issue.lock.yml +++ b/pkg/cli/workflows/test-claude-update-issue.lock.yml @@ -341,11 +341,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -497,7 +527,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/cli/workflows/test-playwright-accessibility-contrast.lock.yml b/pkg/cli/workflows/test-playwright-accessibility-contrast.lock.yml index 9b5330fc076..77608dacecb 100644 --- a/pkg/cli/workflows/test-playwright-accessibility-contrast.lock.yml +++ b/pkg/cli/workflows/test-playwright-accessibility-contrast.lock.yml @@ -1685,11 +1685,41 @@ jobs: */ function parseClaudeLog(logContent) { try { - const logEntries = JSON.parse(logContent); - if (!Array.isArray(logEntries)) { + let logEntries; + // First, try to parse as JSON array (old format) + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + // If that fails, try to parse as mixed format (debug logs + JSONL) + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; // Skip empty lines + } + // Skip debug log lines that don't start with { + // (these are typically timestamped debug messages) + if (!trimmedLine.startsWith("{")) { + continue; + } + // Try to parse each line as JSON + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + // Skip invalid JSON lines (could be partial debug output) + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: - "## Agent Log Summary\n\nLog format not recognized as Claude JSON array.\n", + "## Agent Log Summary\n\nLog format not recognized as Claude JSON array or JSONL.\n", mcpFailures: [], }; } @@ -1841,7 +1871,7 @@ jobs: } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { - markdown: `## Agent Log Summary\n\nError parsing Claude log: ${errorMessage}\n`, + markdown: `## Agent Log Summary\n\nError parsing Claude log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], }; } diff --git a/pkg/workflow/claude_engine.go b/pkg/workflow/claude_engine.go index 32f3ee3316b..06ecb44cb51 100644 --- a/pkg/workflow/claude_engine.go +++ b/pkg/workflow/claude_engine.go @@ -828,22 +828,22 @@ func (e *ClaudeEngine) parseClaudeJSONLog(logContent string, verbose bool) LogMe if verbose { fmt.Printf("Failed to parse Claude log as JSON array, trying JSONL format: %v\n", err) } - + logEntries = []map[string]interface{}{} lines := strings.Split(logContent, "\n") - + for _, line := range lines { trimmedLine := strings.TrimSpace(line) if trimmedLine == "" { continue // Skip empty lines } - + // Skip debug log lines that don't start with { // (these are typically timestamped debug messages) if !strings.HasPrefix(trimmedLine, "{") { continue } - + // Try to parse each line as JSON var jsonEntry map[string]interface{} if err := json.Unmarshal([]byte(trimmedLine), &jsonEntry); err != nil { @@ -853,17 +853,17 @@ func (e *ClaudeEngine) parseClaudeJSONLog(logContent string, verbose bool) LogMe } continue } - + logEntries = append(logEntries, jsonEntry) } - + if len(logEntries) == 0 { if verbose { fmt.Printf("No valid JSON entries found in Claude log\n") } return metrics } - + if verbose { fmt.Printf("Extracted %d JSON entries from mixed format Claude log\n", len(logEntries)) } diff --git a/pkg/workflow/js/parse_claude_log.cjs b/pkg/workflow/js/parse_claude_log.cjs index 498e69c5432..97984a6a5fa 100644 --- a/pkg/workflow/js/parse_claude_log.cjs +++ b/pkg/workflow/js/parse_claude_log.cjs @@ -39,7 +39,7 @@ function main() { function parseClaudeLog(logContent) { try { let logEntries; - + // First, try to parse as JSON array (old format) try { logEntries = JSON.parse(logContent); @@ -49,20 +49,20 @@ function parseClaudeLog(logContent) { } catch (jsonArrayError) { // If that fails, try to parse as mixed format (debug logs + JSONL) logEntries = []; - const lines = logContent.split('\n'); - + const lines = logContent.split("\n"); + for (const line of lines) { const trimmedLine = line.trim(); - if (trimmedLine === '') { + if (trimmedLine === "") { continue; // Skip empty lines } - - // Skip debug log lines that don't start with { + + // Skip debug log lines that don't start with { // (these are typically timestamped debug messages) - if (!trimmedLine.startsWith('{')) { + if (!trimmedLine.startsWith("{")) { continue; } - + // Try to parse each line as JSON try { const jsonEntry = JSON.parse(trimmedLine); @@ -73,7 +73,7 @@ function parseClaudeLog(logContent) { } } } - + if (!Array.isArray(logEntries) || logEntries.length === 0) { return { markdown: