Add line-level annotations to summarize_diff plugin - #205
Conversation
The plugin wrote its summary to stdout as plain text, which clients render as a legacy markdown body. It now emits the response contract from docs/plugins.md: a markdown body holding the summary, plus line-level annotations that render inline in the diff. Gemini is asked for the summary and the annotations in one structured response (responseMimeType + responseSchema), so the reply parses instead of being scraped out of prose. Annotations anchor to head-side line numbers, so the diff is rendered for the prompt with each line's post-merge line number — removed lines get none, since there is nothing on the head side to anchor to — and the file list is spelled out so the model copies paths rather than inventing them. Annotations that could not be anchored (no filename, no positive line) or carry no text are dropped before they are emitted, and a reply that isn't the JSON we asked for is passed through as the body verbatim, which is what the plugin produced before. The binaries in .gitignore were matched by bare name, which also ignored the cmd/ directories they are built from; anchoring them to the repo root keeps the intent and lets the new test file be tracked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDJ423RNG6NkeTFPM7LU16
There was a problem hiding this comment.
🟡 Not ready to approve
The updated plugin output path still needs small contract-alignment fixes (e.g., enforcing the max-annotations cap and ensuring errors don’t mix with stdout response output).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
Enhances the cmd/summarize_diff plugin so it can emit the documented plugin response contract (JSON with a renderable body plus line-level annotations) and updates docs/tests accordingly, enabling clients to render inline, diff-anchored guidance.
Changes:
- Document that Summarize Diff emits the plugin response contract (summary + up to six annotations).
- Update
summarize_diffto (a) number unified diffs with head-side line numbers, (b) request structured JSON from Gemini via a response schema, and (c) encode the contract JSON to stdout. - Add unit tests for diff numbering, prompt construction, response parsing/sanitization, and contract compatibility with the server parser.
File summaries
| File | Description |
|---|---|
| docs/plugins.md | Updates plugin documentation to state that Summarize Diff emits the response contract and line-level annotations. |
| cmd/summarize_diff/main.go | Implements diff numbering, schema-constrained Gemini responses, JSON parsing/sanitization, and contract encoding to stdout. |
| cmd/summarize_diff/main_test.go | Adds tests covering numbering, prompt generation, response parsing, and server contract parsing compatibility. |
| .gitignore | Anchors ignored binary names to repo root so plugin source directories aren’t accidentally ignored. |
Review details
- Files reviewed: 3/4 changed files
- Comments generated: 3
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
| if err != nil { | ||
| fmt.Printf("Error calling Gemini: %v\n", err) | ||
| os.Exit(1) | ||
| } |
| if err := encodeResponse(os.Stdout, responseFor(summary)); err != nil { | ||
| fmt.Printf("Error encoding plugin response: %v\n", err) | ||
| os.Exit(1) | ||
| } |
| func sanitizeAnnotations(annotations []PluginAnnotation) []PluginAnnotation { | ||
| cleaned := []PluginAnnotation{} | ||
| for _, a := range annotations { | ||
| a.Filename = strings.TrimPrefix(strings.TrimSpace(a.Filename), "./") | ||
| a.Content = strings.TrimSpace(a.Content) | ||
| a.Severity = strings.ToLower(strings.TrimSpace(a.Severity)) | ||
| if a.Filename == "" || a.Line < 1 || a.Content == "" { | ||
| continue | ||
| } | ||
| if a.Severity == "" { | ||
| a.Severity = severityInfo | ||
| } | ||
| cleaned = append(cleaned, a) | ||
| } | ||
| return cleaned | ||
| } |
Summary
Enhance the
summarize_diffplugin to emit structured JSON responses with line-level annotations, matching the plugin response contract defined indocs/plugins.md. The plugin now uses Gemini's response schema to request both a summary and specific line-level remarks anchored to the diff.Changes
PluginBody,PluginAnnotation, andPluginResponsestructs to match the documented contractnumberedDiff()parses unified diffs and annotates each line with its head-side line number, enabling reviewers to anchor comments to specific linesresponseSchema()constrains Gemini's reply to summary + annotations via OpenAPI schema validationbuildPrompt()now instructs the model to provide both a summary and up to 6 line-level annotations, with file paths and line numbersresponseFor()parses Gemini's JSON reply, handles legacy plain-text fallback, andsanitizeAnnotations()validates and normalizes annotationsGenerationConfigwithresponseMimeType: "application/json"and the response schemaencodeResponse()writes the plugin response contract to stdout with readable formattingmain_test.gocovers diff numbering, prompt building, response parsing, and contract complianceTest Plan
go build ./...passesgo test ./...passes — new unit tests verify diff numbering, prompt generation, response parsing, and contract compliancehttps://claude.ai/code/session_01YDJ423RNG6NkeTFPM7LU16