Skip to content

Add line-level annotations to summarize_diff plugin - #205

Merged
C-Hipple merged 1 commit into
mainfrom
claude/summarize-diff-plugin-syntax-4ngocz
Jul 31, 2026
Merged

Add line-level annotations to summarize_diff plugin#205
C-Hipple merged 1 commit into
mainfrom
claude/summarize-diff-plugin-syntax-4ngocz

Conversation

@C-Hipple

Copy link
Copy Markdown
Owner

Summary

Enhance the summarize_diff plugin to emit structured JSON responses with line-level annotations, matching the plugin response contract defined in docs/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

  • Add plugin response contract types: PluginBody, PluginAnnotation, and PluginResponse structs to match the documented contract
  • Implement diff numbering: numberedDiff() parses unified diffs and annotates each line with its head-side line number, enabling reviewers to anchor comments to specific lines
  • Add response schema generation: responseSchema() constrains Gemini's reply to summary + annotations via OpenAPI schema validation
  • Enhance prompt: buildPrompt() now instructs the model to provide both a summary and up to 6 line-level annotations, with file paths and line numbers
  • Add response parsing: responseFor() parses Gemini's JSON reply, handles legacy plain-text fallback, and sanitizeAnnotations() validates and normalizes annotations
  • Update Gemini request: Include GenerationConfig with responseMimeType: "application/json" and the response schema
  • Output structured JSON: encodeResponse() writes the plugin response contract to stdout with readable formatting
  • Add comprehensive tests: main_test.go covers diff numbering, prompt building, response parsing, and contract compliance

Test Plan

  • go build ./... passes
  • go test ./... passes — new unit tests verify diff numbering, prompt generation, response parsing, and contract compliance

https://claude.ai/code/session_01YDJ423RNG6NkeTFPM7LU16

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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_diff to (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.

Comment on lines 425 to 428
if err != nil {
fmt.Printf("Error calling Gemini: %v\n", err)
os.Exit(1)
}
Comment on lines +430 to +433
if err := encodeResponse(os.Stdout, responseFor(summary)); err != nil {
fmt.Printf("Error encoding plugin response: %v\n", err)
os.Exit(1)
}
Comment on lines +345 to +360
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
}
@C-Hipple
C-Hipple merged commit c3deb3c into main Jul 31, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants