From 49e1c700edef672d305376a31a40dfd05d8cf6b1 Mon Sep 17 00:00:00 2001 From: kite Date: Wed, 3 Jun 2026 15:38:22 +0800 Subject: [PATCH] fix(cli): embed summary into JSON output to avoid breaking JSON parsing When using --audience agent --format json, the text summary line was printed before the JSON body, causing parse failures for integrations. Move summary data into the JSON "summary" field and skip PrintTraceSummary in JSON mode. --- cmd/opencodereview/output.go | 22 +++++++++++++++++++++- cmd/opencodereview/review_cmd.go | 10 ++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/cmd/opencodereview/output.go b/cmd/opencodereview/output.go index 18c3995..603a7ea 100644 --- a/cmd/opencodereview/output.go +++ b/cmd/opencodereview/output.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "strings" + "time" "github.com/open-code-review/open-code-review/internal/agent" "github.com/open-code-review/open-code-review/internal/model" @@ -161,9 +162,19 @@ func buildDiffLines(comment model.LlmComment) []suggestdiff.DiffLine { return suggestdiff.ComputeLineDiff(oldLines, newLines) } +type jsonSummary struct { + FilesReviewed int64 `json:"files_reviewed"` + Comments int64 `json:"comments"` + TotalTokens int64 `json:"total_tokens"` + InputTokens int64 `json:"input_tokens"` + OutputTokens int64 `json:"output_tokens"` + Elapsed string `json:"elapsed"` +} + type jsonOutput struct { Status string `json:"status"` Message string `json:"message,omitempty"` + Summary *jsonSummary `json:"summary,omitempty"` Comments []model.LlmComment `json:"comments"` Warnings []agent.AgentWarning `json:"warnings,omitempty"` } @@ -181,10 +192,19 @@ func outputJSON(comments []model.LlmComment) error { return enc.Encode(out) } -func outputJSONWithWarnings(comments []model.LlmComment, warnings []agent.AgentWarning) error { +func outputJSONWithWarnings(comments []model.LlmComment, warnings []agent.AgentWarning, + filesReviewed, inputTokens, outputTokens, totalTokens int64, duration time.Duration) error { out := jsonOutput{ Status: "success", Comments: comments, + Summary: &jsonSummary{ + FilesReviewed: filesReviewed, + Comments: int64(len(comments)), + TotalTokens: totalTokens, + InputTokens: inputTokens, + OutputTokens: outputTokens, + Elapsed: duration.Round(time.Second).String(), + }, } if len(comments) == 0 { if hasSubtaskErrors(warnings) { diff --git a/cmd/opencodereview/review_cmd.go b/cmd/opencodereview/review_cmd.go index 92e2d3a..ac75562 100644 --- a/cmd/opencodereview/review_cmd.go +++ b/cmd/opencodereview/review_cmd.go @@ -153,16 +153,18 @@ func runReview(args []string) error { return outputJSONNoFiles() } - // In agent mode, restore stdout so Summary reaches the terminal. - if opts.audience == "agent" && unsilence != nil { + // In agent mode (text output), restore stdout so Summary reaches the terminal. + if opts.audience == "agent" && opts.outputFormat != "json" && unsilence != nil { unsilence() unsilence = nil } - telemetry.PrintTraceSummary(ag.FilesReviewed(), int64(len(comments)), ag.TotalInputTokens(), ag.TotalOutputTokens(), ag.TotalTokensUsed(), duration) + if opts.outputFormat != "json" { + telemetry.PrintTraceSummary(ag.FilesReviewed(), int64(len(comments)), ag.TotalInputTokens(), ag.TotalOutputTokens(), ag.TotalTokensUsed(), duration) + } if opts.outputFormat == "json" { - return outputJSONWithWarnings(comments, ag.Warnings()) + return outputJSONWithWarnings(comments, ag.Warnings(), ag.FilesReviewed(), ag.TotalInputTokens(), ag.TotalOutputTokens(), ag.TotalTokensUsed(), duration) } if opts.audience == "agent" { outputTextWithWarnings(comments, ag.Warnings())