Skip to content

feat(mcp): codegraph_review_context — structured PR-review context from a diff#26

Open
mschreib28 wants to merge 1 commit into
mainfrom
upstream/feat/review-context
Open

feat(mcp): codegraph_review_context — structured PR-review context from a diff#26
mschreib28 wants to merge 1 commit into
mainfrom
upstream/feat/review-context

Conversation

@mschreib28
Copy link
Copy Markdown
Owner

🔄 Rebased onto post-refactor patterns (review-context MCP tool)\n> This PR has been rebased to use the per-thing file layout established by refactor PRs colbymchenry#116/colbymchenry#117/colbymchenry#118/colbymchenry#119. Behavior shipped is identical to the original; only the internal code shape changed.\n>\n> What this PR now ships:\n>\n> - src/mcp/tools/review-context.tsREVIEW_CONTEXT_TOOL: ToolModule\n> - src/review/index.ts + src/review/diff-parser.ts — pure diff→context builder\n> - src/mcp/tools/registry.ts — 2 lines\n> - src/mcp/tools/types.ts — 1 HandlerKey entry\n> - src/mcp/tools.tshandleReviewContext method on ToolHandler + JSON-cap serializer\n> - src/index.tsbuildReviewContext public method\n> - __tests__/review-context.test.ts\n>\n> What you'll see in the diff: until colbymchenry#116-colbymchenry#119 land, the diff against main includes the refactor commits as part of the branch history. After they merge, GitHub auto-shrinks this PR's diff to just the files above. Full context: colbymchenry#120.\n>\n> ---\n\n## Summary\n\nAdds a new MCP tool codegraph_review_context that takes a unified diff and returns structured review context for an LLM-driven PR reviewer. Codegraph becomes a substrate for Greptile/CodeRabbit-style products without itself doing the synthesis — the LLM consumer (Claude Code, ultrareview, etc.) writes the review comments, decides severity, and posts to GitHub.\n\n## What it returns (JSON)\n\nPer changed file:\n- Status (added / modified / deleted / renamed)\n- Hunks (line ranges)\n- Affected symbols (line-range overlap with hunks)\n- Tests covering the file (via PR colbymchenry#106's tests edges; graceful no-op if those edges aren't present)\n\nPer affected symbol:\n- Signature, docstring\n- Top-N callers (PR colbymchenry#108's batch lookups make this fast)\n- Top-N callees\n- Impact-radius node count\n\nFor deleted files:\n- brokenIncomingRefs — every distinct caller of a vanishing symbol (deduped by source name + file + line)\n\nCo-change warnings (the genuinely novel signal):\n- For each changed file, surface up to N historical co-changers that were NOT touched in this PR\n- Catches "you changed schema.sql but didn't update migrations.ts" coupling violations that no static analyzer can see\n- Graceful degrade if PR colbymchenry#105's co_changes table isn't present\n\n## Components\n\n- src/review/diff-parser.ts — pure unified-diff parser. Handles modified, added (/dev/null in ---), deleted (/dev/null in +++), renamed (rename from/to), multi-hunk, single-line hunks, C-style-quoted paths with spaces, and hunk-less rename / mode-change files mid-diff.\n\n- src/review/index.tsbuildReviewContext(diff, queries, traverser, options). Iterates files, maps hunks to symbols, attaches per-symbol context, surfaces co-change warnings.\n\n- CodeGraph.buildReviewContext(diff, options) — public API method.\n\n- MCP tool in src/mcp/tools.ts — JSON output runs through serializeReviewContextWithinCap which progressively trims (docstrings → signatures → caller/callee caps → drop callers/callees → drop oldest files → summary-only) so the result stays valid JSON even when it would exceed MAX_OUTPUT_LENGTH. (Naive mid-string truncation would corrupt JSON parsing.)\n\n## Files changed\n\n| File | Change |\n|---|---|\n| src/review/diff-parser.ts (NEW) | Pure unified-diff parser |\n| src/review/index.ts (NEW) | buildReviewContext + co-change warnings |\n| src/index.ts | Add CodeGraph.buildReviewContext public API |\n| src/mcp/tools.ts | New tool + handler + JSON-safe truncation |\n| __tests__/review-context.test.ts (NEW) | 25 regression tests |\n\n## Example flow\n\nbash\n# Get the diff for a PR you want to review\ngh pr diff 42 > /tmp/pr-42.diff\n\n# Then the LLM (via MCP) calls:\ncodegraph_review_context({ diff: <diff text> })\n# → returns structured JSON; LLM writes review comments,\n# posts via gh pr comment / gh api\n\n\n## Test plan\n\n- [x] npm test: 405/405 pass on macOS\n- [x] npx tsc --noEmit clean\n- [x] Independent reviewer pass before pushing — addressed four findings:\n - Mid-diff hunk-less rename / mode-change was silently dropped (request_changes → fixed; flushHunkless on every header transition, flags reset after consumption to prevent phantom emits)\n - truncateOutput sliced JSON mid-string producing invalid JSON (request_changes → tier-and-trim helper, regression test asserts output stays parseable even with 200-symbol over-cap context)\n - brokenIncomingRefs could double-list the same caller with multiple edge types (info → dedup by name|filePath|line)\n - C-style-quoted paths in diff --git "a/x" "b/y" headers were not stripped (info → regex now matches both shapes; unquote helper handles the value)\n\n## Forward-compat with sibling PRs\n\nThis PR works on a clean upstream/main install today. When PRs colbymchenry#105 (co-change graph) and colbymchenry#106 (tests-as-edges) land, the output gets richer automatically — no further code changes required.\n\n🤖 Generated with Claude Code\n\n


Copied from colbymchenry/codegraph#110

…om a diff

Adds a new MCP tool that takes a unified diff and returns structured
review context for an LLM-driven PR reviewer. Codegraph becomes a
substrate for Greptile/CodeRabbit-style products without itself doing
the synthesis.

## What it returns

Per changed file:
  - status (added / modified / deleted / renamed)
  - hunks (line ranges)
  - affected symbols (line-range overlap with hunks)
  - tests covering the file (via PR colbymchenry#106 `tests` edges; graceful no-op
    if those edges aren't present)

Per affected symbol:
  - signature, docstring
  - top-N callers (PR colbymchenry#108's batch lookups make this fast)
  - top-N callees
  - impact-radius node count

For deleted files:
  - brokenIncomingRefs — every distinct caller of a vanishing symbol
    (deduped by source name + file + line)

Co-change warnings (the genuinely novel signal):
  - For each changed file, surface up to N historical co-changers
    that were NOT touched in this PR (catches "you changed schema.sql
    but didn't update migrations.ts" coupling violations)
  - Graceful degrade if PR colbymchenry#105's co_changes table isn't present

Output is JSON; the LLM consumer does the synthesis (writes review
comments, decides severity, posts to GitHub).

## Components

- src/review/diff-parser.ts — pure unified-diff parser. Handles
  modified, added (/dev/null in ---), deleted (/dev/null in +++),
  renamed (rename from/to), multi-hunk files, single-line hunks
  (no comma in @@), C-style-quoted paths with spaces, and hunk-less
  rename / mode-change files mid-diff.

- src/review/index.ts — buildReviewContext(diff, queries, traverser).
  Iterates files, maps hunks to symbols, attaches per-symbol context,
  surfaces co-change warnings.

- src/index.ts — CodeGraph.buildReviewContext(diff, options) public API.

- src/mcp/tools.ts — codegraph_review_context tool definition + handler.
  JSON output runs through serializeReviewContextWithinCap which
  progressively trims (docstrings → signatures → caller/callee caps →
  drop callers/callees → drop oldest files → summary-only) so the
  result stays valid JSON even when it would exceed MAX_OUTPUT_LENGTH.

## Files changed

| File | Change |
|---|---|
| src/review/diff-parser.ts (NEW) | Pure unified-diff parser |
| src/review/index.ts (NEW) | buildReviewContext + co-change warnings |
| src/index.ts | Add CodeGraph.buildReviewContext public API |
| src/mcp/tools.ts | New tool + handler + JSON-safe truncation |
| __tests__/review-context.test.ts (NEW) | 25 regression tests |

## Test plan

- [x] npm test: 405/405 pass on macOS
- [x] npx tsc --noEmit clean
- [x] Independent reviewer pass before pushing — addressed four findings:
  - Mid-diff hunk-less rename / mode-change was silently dropped
    (request_changes → fixed; flushHunkless on every header transition,
    flags reset after consumption to prevent phantom emits)
  - truncateOutput sliced JSON mid-string producing invalid JSON
    (request_changes → tier-and-trim helper, regression test asserts
    output stays parseable)
  - brokenIncomingRefs could double-list the same caller with multiple
    edge types (info → dedup by name|filePath|line, regression test added)
  - C-style-quoted paths in `diff --git "a/x" "b/y"` headers were not
    stripped (info → regex now matches both shapes, regression test added)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

2 participants