Files
shockbot/mcp/reviewComments.ts
T

112 lines
3.5 KiB
TypeScript

import { writeFileSync } from "node:fs";
import { join } from "node:path";
import { type } from "arktype";
import type { PullReviewComment } from "@go-gitea/sdk.js";
import { stripExistingFooter } from "../utils/buildShockbotFooter.ts";
import { log } from "../utils/log.ts";
import type { ToolContext } from "./server.ts";
import { execute, tool } from "./shared.ts";
export const GetReviewComments = type({
pull_number: type.number.describe("The pull request number to get review comments for"),
});
export function GetReviewCommentsTool(ctx: ToolContext) {
return tool({
name: "get_review_comments",
description:
"Get all inline review comments for a pull request. " +
"Example: `get_review_comments({ pull_number: 1234 })`.",
parameters: GetReviewComments,
execute: execute(async ({ pull_number }) => {
ctx.toolState.issueNumber = pull_number;
// Get all reviews, then collect their comments
const reviews = await ctx.gitea.paginate(ctx.gitea.rest.repository.repoListPullReviews, {
owner: ctx.repo.owner,
repo: ctx.repo.name,
index: pull_number,
});
const allComments: PullReviewComment[] = [];
for (const review of reviews) {
if (!review.id) continue;
try {
const commentsResult = await ctx.gitea.rest.repository.repoGetPullReviewComments({
owner: ctx.repo.owner,
repo: ctx.repo.name,
index: pull_number,
id: review.id,
});
allComments.push(...commentsResult.data);
} catch {
// best-effort
}
}
const tempDir = process.env.SHOCKBOT_TEMP_DIR;
let filePath: string | undefined;
const rendered = allComments.map((c) => ({
id: c.id,
path: c.path,
// PullReviewComment uses `position` for the diff offset; Gitea doesn't
// expose separate new_position/old_position on GET — use original_position as fallback
line: c.original_position ?? c.position,
side: "RIGHT" as const,
body: stripExistingFooter(c.body ?? ""),
author: c.user?.login,
diffHunk: c.diff_hunk,
reviewId: c.pull_request_review_id,
}));
if (tempDir && rendered.length > 0) {
filePath = join(tempDir, `pr-${pull_number}-review-comments.json`);
writeFileSync(filePath, JSON.stringify(rendered, null, 2));
log.debug(`wrote review comments to ${filePath}`);
}
return {
pull_number,
comments: rendered,
count: rendered.length,
...(filePath ? { filePath } : {}),
};
}),
});
}
export const ListPullRequestReviews = type({
pull_number: type.number.describe("The pull request number"),
});
export function ListPullRequestReviewsTool(ctx: ToolContext) {
return tool({
name: "list_pull_request_reviews",
description:
"List all reviews submitted on a pull request. " +
"Example: `list_pull_request_reviews({ pull_number: 1234 })`.",
parameters: ListPullRequestReviews,
execute: execute(async ({ pull_number }) => {
const reviews = await ctx.gitea.paginate(ctx.gitea.rest.repository.repoListPullReviews, {
owner: ctx.repo.owner,
repo: ctx.repo.name,
index: pull_number,
});
return {
pull_number,
reviews: reviews.map((r) => ({
id: r.id,
user: r.user?.login,
state: r.state,
body: r.body,
submitted_at: r.submitted_at,
commit_id: r.commit_id,
})),
count: reviews.length,
};
}),
});
}