idk
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import type { ActionConfig, PRContext, ChunkReview } from "./types.ts";
|
||||
import { getPR, getPRDiff, addReaction, removeReaction, postReviewComment } from "./gitea.ts";
|
||||
import { parseDiff, chunkFile } from "./diff.ts";
|
||||
import { createMcpServer } from "./mcp.ts";
|
||||
import { reviewChunk, aggregateFindings } from "./review.ts";
|
||||
import { formatReview } from "./comment.ts";
|
||||
|
||||
function readConfig(): ActionConfig {
|
||||
return {
|
||||
prompt: process.env.INPUT_PROMPT ?? "Review this pull request",
|
||||
model: process.env.INPUT_MODEL ?? "qwen3.6:35b",
|
||||
contextWindow: parseInt(process.env.INPUT_CONTEXT_WINDOW ?? "4096", 10),
|
||||
maxToolCalls: parseInt(process.env.INPUT_MAX_TOOL_CALLS ?? "10", 10),
|
||||
};
|
||||
}
|
||||
|
||||
function readEvent(): Record<string, unknown> {
|
||||
const path = process.env.GITHUB_EVENT_PATH;
|
||||
if (!path) return {};
|
||||
try {
|
||||
return JSON.parse(readFileSync(path, "utf8")) as Record<string, unknown>;
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const config = readConfig();
|
||||
const eventName = process.env.GITHUB_EVENT_NAME ?? "";
|
||||
const event = readEvent();
|
||||
|
||||
const repository = process.env.GITHUB_REPOSITORY ?? "";
|
||||
const slashIdx = repository.indexOf("/");
|
||||
if (slashIdx === -1) throw new Error(`Invalid GITHUB_REPOSITORY: ${repository}`);
|
||||
const owner = repository.slice(0, slashIdx);
|
||||
const repo = repository.slice(slashIdx + 1);
|
||||
|
||||
let prNumber: number;
|
||||
let prompt: string;
|
||||
let triggerCommentId: number | null = null;
|
||||
|
||||
if (eventName === "pull_request") {
|
||||
prNumber = event.number as number;
|
||||
prompt = config.prompt;
|
||||
} else if (eventName === "issue_comment") {
|
||||
const issue = event.issue as Record<string, unknown>;
|
||||
if (!issue?.pull_request) {
|
||||
console.log("issue_comment on non-PR issue, skipping");
|
||||
return;
|
||||
}
|
||||
const comment = event.comment as Record<string, unknown>;
|
||||
const commentBody = String(comment?.body ?? "");
|
||||
if (!commentBody.toLowerCase().includes("@shockbot")) {
|
||||
console.log("comment does not mention @shockbot, skipping");
|
||||
return;
|
||||
}
|
||||
prNumber = issue.number as number;
|
||||
triggerCommentId = comment.id as number;
|
||||
prompt = commentBody.replace(/@shockbot\s*/gi, "").trim() || config.prompt;
|
||||
} else {
|
||||
console.log(`Unsupported event: ${eventName}, skipping`);
|
||||
return;
|
||||
}
|
||||
|
||||
const prData = await getPR(owner, repo, prNumber);
|
||||
const headSha = prData.head?.sha;
|
||||
if (!headSha) throw new Error(`Could not get head SHA for PR #${prNumber}`);
|
||||
|
||||
const pr: PRContext = { owner, repo, prNumber, headSha };
|
||||
|
||||
if (triggerCommentId !== null) {
|
||||
await addReaction(pr, triggerCommentId, "eyes").catch(() => {});
|
||||
}
|
||||
|
||||
try {
|
||||
const rawDiff = await getPRDiff(pr);
|
||||
if (!rawDiff.trim()) {
|
||||
await postReviewComment(pr, "shockbot: no diff found for this PR.");
|
||||
return;
|
||||
}
|
||||
|
||||
const files = parseDiff(rawDiff);
|
||||
if (files.length === 0) {
|
||||
await postReviewComment(pr, "shockbot: no reviewable files in this PR (all files skipped).");
|
||||
return;
|
||||
}
|
||||
|
||||
const chunks = files.flatMap((f) => chunkFile(f, config.contextWindow));
|
||||
const mcpServer = createMcpServer(pr, config.maxToolCalls);
|
||||
|
||||
const results: ChunkReview[] = [];
|
||||
for (const chunk of chunks) {
|
||||
const result = await reviewChunk(
|
||||
chunk,
|
||||
prompt,
|
||||
config.model,
|
||||
config.contextWindow,
|
||||
mcpServer,
|
||||
);
|
||||
results.push(result);
|
||||
}
|
||||
|
||||
const aggregated = aggregateFindings(results);
|
||||
const body = formatReview(aggregated, files.map((f) => f.filename), config.model);
|
||||
await postReviewComment(pr, body);
|
||||
|
||||
if (triggerCommentId !== null) {
|
||||
await removeReaction(pr, triggerCommentId, "eyes").catch(() => {});
|
||||
await addReaction(pr, triggerCommentId, "+1").catch(() => {});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Review failed:", err);
|
||||
if (triggerCommentId !== null) {
|
||||
await removeReaction(pr, triggerCommentId, "eyes").catch(() => {});
|
||||
await addReaction(pr, triggerCommentId, "-1").catch(() => {});
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user