fix: address pr review follow-ups

This commit is contained in:
harshitsinghbhandari 2026-04-18 17:43:11 +05:30
parent 4dac245b81
commit 7bc98aa387
6 changed files with 87 additions and 7 deletions

View File

@ -203,6 +203,15 @@ describe("parsePrFromUrl", () => {
});
});
it("parses GitHub pull request URLs with trailing path segments", () => {
expect(parsePrFromUrl("https://github.com/foo/bar/pull/123/files")).toEqual({
owner: "foo",
repo: "bar",
number: 123,
url: "https://github.com/foo/bar/pull/123/files",
});
});
it("returns null when the URL has no PR number", () => {
expect(parsePrFromUrl("https://example.com/foo/bar/pull/not-a-number")).toBeNull();
});

View File

@ -9,7 +9,7 @@ export function parsePrFromUrl(prUrl: string): ParsedPrUrl | null {
const pathSegments = parsedUrl?.pathname.split("/").filter(Boolean) ?? [];
const githubStylePullIndex = pathSegments.findIndex((segment) => segment === "pull");
if (githubStylePullIndex >= 2 && githubStylePullIndex === pathSegments.length - 2) {
if (githubStylePullIndex >= 2 && githubStylePullIndex + 1 < pathSegments.length) {
const owner = pathSegments[githubStylePullIndex - 2];
const repo = pathSegments[githubStylePullIndex - 1];
const prNumber = pathSegments[githubStylePullIndex + 1];

View File

@ -226,7 +226,8 @@ export function SessionDetailPRCard({
{pr.ciChecks.length > 0 ? (
<>
<div className="session-detail-pr-sep" />
{pr.ciChecks.map((check) => {
{pr.ciChecks.map((check, index) => {
const key = check.url ?? `${check.name}-${index}`;
const chip = (
<span
className={cn(
@ -252,7 +253,7 @@ export function SessionDetailPRCard({
);
return check.url ? (
<a
key={check.name}
key={key}
href={check.url}
target="_blank"
rel="noopener noreferrer"
@ -262,7 +263,7 @@ export function SessionDetailPRCard({
{chip}
</a>
) : (
<span key={check.name}>{chip}</span>
<span key={key}>{chip}</span>
);
})}
</>

View File

@ -1,6 +1,7 @@
import { act, fireEvent, render, screen } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { SessionDetail } from "../SessionDetail";
import { buildAgentFixMessage } from "../session-detail-agent-actions";
import { makePR, makeSession } from "../../__tests__/helpers";
vi.mock("next/navigation", () => ({
@ -243,6 +244,41 @@ describe("SessionDetail desktop layout", () => {
expect(screen.getByRole("button", { name: "Ask Agent to Fix" })).toBeInTheDocument();
});
it("builds branch links from the PR host for GitHub Enterprise repos", () => {
render(
<SessionDetail
session={makeSession({
id: "worker-ghe",
projectId: "my-app",
branch: "feat/ghe-detail",
pr: makePR({
number: 312,
url: "https://github.enterprise.local/acme/app/pull/312",
owner: "acme",
repo: "app",
branch: "feat/ghe-detail",
}),
})}
/>,
);
expect(screen.getByRole("link", { name: "feat/ghe-detail" })).toHaveAttribute(
"href",
"https://github.enterprise.local/acme/app/tree/feat/ghe-detail",
);
});
it("truncates review-comment messages below the API payload cap", () => {
const message = buildAgentFixMessage({
url: "https://github.com/acme/app/pull/311#discussion_r2",
path: `packages/web/${"deep/".repeat(200)}component.tsx`,
body: `### ${"T".repeat(500)}\n<!-- DESCRIPTION START -->${"D".repeat(15_000)}<!-- DESCRIPTION END -->`,
});
expect(message.length).toBeLessThanOrEqual(9_500);
expect(message).toContain("Resolve the comment at https://github.com/acme/app/pull/311#discussion_r2");
});
it("shows terminal-ended placeholder for exited desktop sessions", () => {
render(
<SessionDetail

View File

@ -1,5 +1,30 @@
import { cleanBugbotComment } from "./session-detail-utils";
const MAX_AGENT_MESSAGE_LENGTH = 9_500;
const MAX_TITLE_LENGTH = 240;
const MAX_DESCRIPTION_LENGTH = 7_500;
function truncate(value: string, maxLength: number): string {
const trimmed = value.trim();
if (trimmed.length <= maxLength) return trimmed;
return `${trimmed.slice(0, Math.max(0, maxLength - 1)).trimEnd()}`;
}
function buildAgentFixMessage(comment: { url: string; path: string; body: string }): string {
const { title, description } = cleanBugbotComment(comment.body);
const message = [
"Please address this review comment:",
"",
`File: ${truncate(comment.path, 500)}`,
`Comment: ${truncate(title, MAX_TITLE_LENGTH)}`,
`Description: ${truncate(description, MAX_DESCRIPTION_LENGTH)}`,
"",
`Resolve the comment at ${comment.url} after fixing it.`,
].join("\n");
return truncate(message, MAX_AGENT_MESSAGE_LENGTH);
}
export async function askAgentToFix(
sessionId: string,
comment: { url: string; path: string; body: string },
@ -7,8 +32,7 @@ export async function askAgentToFix(
onError: () => void,
) {
try {
const { title, description } = cleanBugbotComment(comment.body);
const message = `Please address this review comment:\n\nFile: ${comment.path}\nComment: ${title}\nDescription: ${description}\n\nComment URL: ${comment.url}\n\nAfter fixing, mark the comment as resolved at ${comment.url}`;
const message = buildAgentFixMessage(comment);
const response = await fetch(`/api/sessions/${encodeURIComponent(sessionId)}/message`, {
method: "POST",
headers: { "Content-Type": "application/json" },
@ -21,3 +45,5 @@ export async function askAgentToFix(
onError();
}
}
export { buildAgentFixMessage };

View File

@ -52,7 +52,15 @@ export function cleanBugbotComment(body: string): { title: string; description:
}
export function buildGitHubBranchUrl(pr: DashboardPR): string {
return `https://github.com/${pr.owner}/${pr.repo}/tree/${pr.branch}`;
let origin = "https://github.com";
try {
origin = new URL(pr.url).origin;
} catch {
// Fall back to the public GitHub host if the PR URL is missing or invalid.
}
return `${origin}/${pr.owner}/${pr.repo}/tree/${pr.branch}`;
}
export function activityStateClass(activityLabel: string): string {