This commit is contained in:
Overtorment 2026-06-20 10:53:35 +01:00
parent 0d3e992aec
commit 13850607a4
3 changed files with 29 additions and 1 deletions

View File

@ -53,6 +53,7 @@ cli/notifications.ts
→ filter reason === "review_requested"
→ review/process.processReviewRequest() (per notification)
→ github/pr.parsePullRequest()
→ github/pr.isReviewRequestedForUser() # skip if not on pending reviewer list
→ git/workspace.preparePrWorkspace() # /tmp/glados-*/<repo>
→ review/agent.runAgentReview() # local Agent.prompt
→ review/payload.buildGithubReview() # APPROVE vs REQUEST_CHANGES
@ -60,6 +61,8 @@ cli/notifications.ts
→ rm temp workspace
```
**Duplicate protection:** before cloning, `isReviewRequestedForUser()` calls `GET .../pulls/{n}/requested_reviewers`. GitHub only lists users with a **pending** review request — once you submit a review you drop off; if someone re-requests you, you're back. Review only when the GLADOS user is on that list.
**Tests:** GLaDOS does not run the test suite or install deps to execute tests. The PR's CI runs tests; the agent reviews test *code* by reading files only. This is enforced in `buildReviewPrompt()`.
**Approve vs request changes:** `critical` or `high` findings → `REQUEST_CHANGES`; otherwise `APPROVE`.

View File

@ -1,3 +1,4 @@
import * as github from "@actions/github";
import type { NotificationThread, PullRequestRef } from "../types.js";
export function parsePullRequest(
@ -23,3 +24,21 @@ export function subjectUrlToWebUrl(subjectUrl: string): string {
.replace("https://api.github.com/repos/", "https://github.com/")
.replace("/pulls/", "/pull/");
}
/** Whether the authenticated user is on the PR's pending reviewer list. */
export async function isReviewRequestedForUser(
githubToken: string,
pr: Pick<PullRequestRef, "owner" | "repo" | "prNumber">,
): Promise<boolean> {
const octokit = github.getOctokit(githubToken);
const [{ data: user }, { data: requested }] = await Promise.all([
octokit.rest.users.getAuthenticated(),
octokit.rest.pulls.listRequestedReviewers({
owner: pr.owner,
repo: pr.repo,
pull_number: pr.prNumber,
}),
]);
return requested.users.some((u) => u.login === user.login);
}

View File

@ -1,7 +1,7 @@
import { CursorAgentError } from "@cursor/sdk";
import { rm } from "node:fs/promises";
import { preparePrWorkspace } from "../git/workspace.js";
import { parsePullRequest } from "../github/pr.js";
import { parsePullRequest, isReviewRequestedForUser } from "../github/pr.js";
import { postGithubReview } from "../github/reviews.js";
import type { NotificationThread } from "../types.js";
import { runAgentReview } from "./agent.js";
@ -21,6 +21,12 @@ export async function processReviewRequest(
let workDir: string | undefined;
try {
const requested = await isReviewRequestedForUser(options.githubToken, pr);
if (!requested) {
console.log(" Skipping: review not requested for this user");
return true;
}
console.log(` Cloning ${pr.owner}/${pr.repo}...`);
const workspace = await preparePrWorkspace(
pr.owner,