feat(review): PR context panel — summary, comments, and checks (#350)

* feat(review): add PR context panel with summary, comments, and checks tabs

When reviewing a GitHub PR, the ReviewPanel now shows icon tabs for
Summary (PR body, labels, linked issues), Comments (chronological
thread of comments + reviews), and Checks (CI status, merge readiness).
Tabs only appear in PR mode; local review is unchanged.

Data fetched lazily via new /api/pr-context endpoint on first tab click.
Uses DOMPurify for safe HTML rendering of GitHub markdown content.
Inline markdown renderer extended with link and bare URL support.

Closes #348

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(review): reset fetch guard on error and detect inline HTML in PR bodies

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Ramos
2026-03-19 23:14:21 -07:00
committed by GitHub
parent 0908e234e1
commit 1821943d06
12 changed files with 924 additions and 121 deletions
+25 -1
View File
@@ -15,7 +15,7 @@ import { getRepoInfo } from "./repo";
import { handleImage, handleUpload, handleAgents, handleServerReady, handleDraftSave, handleDraftLoad, handleDraftDelete, handleFavicon, type OpencodeClient } from "./shared-handlers";
import { contentHash, deleteDraft } from "./draft";
import { createEditorAnnotationHandler } from "./editor-annotations";
import { type PRMetadata, fetchPRFileContent } from "./pr";
import { type PRMetadata, fetchPRFileContent, fetchPRContext } from "./pr";
// Re-export utilities
export { isRemoteSession, getServerPort } from "./remote";
@@ -195,6 +195,30 @@ export async function startReviewServer(
}
}
// API: Fetch PR context (comments, checks, merge status) — PR mode only
if (url.pathname === "/api/pr-context" && req.method === "GET") {
if (!isPRMode) {
return Response.json(
{ error: "Not in PR mode" },
{ status: 400 },
);
}
try {
const ref = {
platform: "github" as const,
owner: prMetadata!.owner,
repo: prMetadata!.repo,
number: prMetadata!.number,
};
const context = await fetchPRContext(ref);
return Response.json(context);
} catch (err) {
const message =
err instanceof Error ? err.message : "Failed to fetch PR context";
return Response.json({ error: message }, { status: 500 });
}
}
// API: Get file content for expandable diff context
if (url.pathname === "/api/file-content" && req.method === "GET") {
const filePath = url.searchParams.get("path");