From 13850607a4fc07df912efd09166d523426894ab2 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Sat, 20 Jun 2026 10:53:35 +0100 Subject: [PATCH] init --- AGENTS.md | 3 +++ src/github/pr.ts | 19 +++++++++++++++++++ src/review/process.ts | 8 +++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 2008aab..af6fd15 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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-*/ → 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`. diff --git a/src/github/pr.ts b/src/github/pr.ts index 28bf7af..0ef9f14 100644 --- a/src/github/pr.ts +++ b/src/github/pr.ts @@ -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, +): Promise { + 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); +} diff --git a/src/review/process.ts b/src/review/process.ts index 42979b6..3588edf 100644 --- a/src/review/process.ts +++ b/src/review/process.ts @@ -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,