Files
shockbot/mcp/reviewComments.ts

82 lines
3.4 KiB
TypeScript

import { writeFileSync } from "node:fs";
import { join } from "node:path";
import { type } from "arktype";
import { stripExistingFooter } from "../utils/buildShockbotFooter.ts";
import { log } from "../utils/log.ts";
import type { ToolContext } from "./server.ts";
import { execute, tool } from "./shared.ts";
interface GiteaReview { id: number; user?: { login: string }; state?: string; body?: string; submitted_at?: string; commit_id?: string }
interface GiteaReviewComment { id: number; body?: string; path?: string; diff_hunk?: string; original_position?: number; position?: number; pull_request_review_id?: number; user?: { login: string } }
export const GetReviewComments = type({ pull_number: type.number });
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;
const reviewsR = await ctx.gitea.request(
"GET /repos/{owner}/{repo}/pulls/{index}/reviews",
{ owner: ctx.repo.owner, repo: ctx.repo.name, index: pull_number, limit: 50 }
);
const reviews = reviewsR.data as GiteaReview[];
const allComments: GiteaReviewComment[] = [];
for (const review of reviews) {
if (!review.id) continue;
try {
const cr = await ctx.gitea.request(
"GET /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments",
{ owner: ctx.repo.owner, repo: ctx.repo.name, index: pull_number, id: review.id }
);
allComments.push(...(cr.data as GiteaReviewComment[]));
} 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,
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 });
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 r = await ctx.gitea.request(
"GET /repos/{owner}/{repo}/pulls/{index}/reviews",
{ owner: ctx.repo.owner, repo: ctx.repo.name, index: pull_number, limit: 50 }
);
const reviews = r.data as GiteaReview[];
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,
};
}),
});
}