diff --git a/src/agent/agent-system-prompt.ts b/src/agent/agent-system-prompt.ts
index dbddabc..74b6b62 100644
--- a/src/agent/agent-system-prompt.ts
+++ b/src/agent/agent-system-prompt.ts
@@ -204,6 +204,49 @@ export function getInteractiveSystemPrompt(
If you cannot verify a claim, do not make it.
+
+
+ When other reviewers have already commented on the PR (shown in ):
+
+ 1. **DO NOT DUPLICATE**: Never repeat feedback that another reviewer has already given.
+ If someone already pointed out an issue, do not post the same comment.
+
+ 2. **USE EMOJI REACTIONS**: When you agree with an existing comment but have nothing
+ substantial to add, use an emoji reaction instead of posting a new comment.
+ This is a lightweight way to show your agreement without adding noise.
+
+ The comment ID is provided in each element's \`\` field.
+ Use it to add a reaction:
+ \`gh api repos/OWNER/REPO/pulls/comments/COMMENT_ID/reactions -f content="+1"\`
+
+ Available reactions:
+ • \`+1\` (👍) - Agree with the comment
+ • \`-1\` (👎) - Disagree (prefer posting a counter-opinion with explanation)
+ • \`heart\` (❤️) - Great catch/suggestion
+ • \`rocket\` (🚀) - Excellent improvement
+ • \`eyes\` (👀) - Interesting point, needs attention
+ • \`confused\` (😕) - Unclear or questionable suggestion
+
+ 3. **REINFORCE when valuable**: If you strongly agree with an existing comment and have
+ additional context or a stronger argument, you MAY add a supporting comment.
+ Example: "I agree with @reviewer's point about X. Additionally, this could cause Y..."
+
+ 4. **RESPECTFULLY DISAGREE**: If you believe an existing comment is incorrect or
+ the suggested change would be harmful, you may respectfully provide a counter-opinion.
+ Be constructive and explain your reasoning with evidence.
+ Example: "I have a different perspective on @reviewer's suggestion about X.
+ The current approach is actually preferred because..."
+
+ 5. **FACTOR INTO ASSESSMENT**: Consider existing comments when deciding your overall
+ review verdict. If issues have already been raised that warrant changes, acknowledge
+ them in your review summary even if you don't add new comments about them.
+
+ 6. **STILL DO A FULL REVIEW**: You should review ALL the code in the diff and form your
+ own judgement about everything. Existing comments don't mean you skip those areas.
+ Just avoid posting duplicate feedback for issues already raised - use emoji reactions
+ or add to the discussion with new insights instead.
+
+
Don't just read the diff - actively investigate using your tools:
diff --git a/src/agent/index.ts b/src/agent/index.ts
index bd742da..f50bac3 100644
--- a/src/agent/index.ts
+++ b/src/agent/index.ts
@@ -2,7 +2,7 @@ import { Agent, Runner } from "@openai/agents";
import { aisdk } from "@openai/agents-extensions";
import { debugError, debugLog } from "../debug";
import { createModel } from "../model-factory";
-import { ModelConfig } from "../types";
+import { ExistingReviewComment, ModelConfig } from "../types";
import { getInteractiveSystemPrompt } from "./agent-system-prompt";
import { allTools, resetTodoList } from "./tools";
@@ -38,6 +38,41 @@ export interface PRContext {
commitSha: string;
}
+/**
+ * Formats existing review comments into a readable string for the agent.
+ */
+function formatExistingComments(comments: ExistingReviewComment[]): string {
+ if (comments.length === 0) {
+ return "";
+ }
+
+ const formattedComments = comments.map((comment, index) => {
+ const lineInfo = comment.line ? `Line ${comment.line}` : "File-level";
+ return ``;
+ });
+
+ return `
+
+ These are review comments made by other reviewers on this PR.
+ Consider them in your review:
+ - DO NOT duplicate feedback that has already been given
+ - If you agree with a comment, you may reinforce it or add additional context
+ - If you disagree with a comment, you may respectfully provide a counter-opinion
+ - Factor these comments into your overall assessment of the PR
+
+${formattedComments.join("\n\n")}
+`;
+}
+
/**
* Reviews an entire PR diff using a single interactive agent.
* The agent has full autonomy to:
@@ -53,6 +88,7 @@ export async function reviewFullDiff(
prContext: PRContext,
maxTurns: number = 75,
blockingOnly: boolean = false,
+ existingComments: ExistingReviewComment[] = [],
): Promise {
// Reset todo list for fresh review
resetTodoList();
@@ -76,6 +112,12 @@ export async function reviewFullDiff(
);
}
+ // Format existing comments for inclusion
+ const existingCommentsSection = formatExistingComments(existingComments);
+ if (existingComments.length > 0) {
+ debugLog(`📝 Including ${existingComments.length} existing review comments in context`);
+ }
+
const initialMessage = `
You are reviewing PR #${prContext.prNumber} in repository ${prContext.repo}.
Commit SHA: ${prContext.commitSha}
@@ -87,6 +129,7 @@ ${fileList}
${fullDiff}
+${existingCommentsSection}
Please review this pull request. You have the complete diff above.
@@ -98,7 +141,7 @@ Please review this pull request. You have the complete diff above.
- Run \`gh pr view ${prContext.prNumber} --json body -q '.body'\` to check if description is blank
- **If body is empty/blank, you MUST update it immediately:**
\`gh pr edit ${prContext.prNumber} --body "## Summary\\n\\n\\n\\n## Changes\\n\\n- "\`
- - Run \`gh api repos/${prContext.repo}/pulls/${prContext.prNumber}/comments\` to check existing comments (avoid duplicates)
+ - Review the section above (if present) to understand what other reviewers have already commented on
2. **Deep review each changed file:**
For each file in the diff:
diff --git a/src/index.ts b/src/index.ts
index 43a69ca..df7faf0 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,6 +2,7 @@ import * as core from "@actions/core";
import * as github from "@actions/github";
import { writeFileSync } from "node:fs";
import { resolve } from "node:path";
+import { ExistingReviewComment } from "./types";
interface TriggerConfig {
runOnPrOpened: boolean;
@@ -327,6 +328,48 @@ async function run(): Promise {
core.info(
`Generated diff file: ${diffFile} (${diffOutput.length} bytes)`,
);
+
+ // Fetch existing review comments from other reviewers
+ const existingCommentsFile = resolve("pr-comments.json");
+ try {
+ const { data: reviewComments } =
+ await octokit.rest.pulls.listReviewComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: prNumber,
+ });
+
+ // Filter out bot comments and map to our interface
+ const existingComments: ExistingReviewComment[] = reviewComments
+ .filter(
+ (comment) =>
+ comment.user &&
+ !comment.user.login.includes("[bot]") &&
+ comment.user.login !== "github-actions",
+ )
+ .map((comment) => ({
+ id: comment.id,
+ author: comment.user?.login || "unknown",
+ body: comment.body,
+ path: comment.path,
+ line: comment.line ?? comment.original_line ?? null,
+ diffHunk: comment.diff_hunk,
+ createdAt: comment.created_at,
+ }));
+
+ writeFileSync(
+ existingCommentsFile,
+ JSON.stringify(existingComments, null, 2),
+ );
+ core.info(
+ `Found ${existingComments.length} existing review comments from other reviewers`,
+ );
+ } catch (commentError) {
+ core.warning(
+ `Failed to fetch existing comments (continuing without them): ${commentError}`,
+ );
+ writeFileSync(existingCommentsFile, "[]");
+ }
} catch (error) {
core.setFailed(`Failed to fetch diff from GitHub API: ${error}`);
return;
diff --git a/src/review-service.ts b/src/review-service.ts
index 3f0a741..df5e2ab 100644
--- a/src/review-service.ts
+++ b/src/review-service.ts
@@ -5,7 +5,7 @@ import { resolve } from "path";
import { PRContext, reviewFullDiff } from "./agent";
import { getModelConfig } from "./config";
import { debugLog } from "./debug";
-import type { ReviewConfig } from "./types";
+import type { ExistingReviewComment, ReviewConfig } from "./types";
/**
* Service class that orchestrates the review process.
@@ -81,6 +81,20 @@ export class ReviewService {
console.error("COMMIT_SHA not set - agent will not be able to post inline comments");
}
+ // Load existing review comments from other reviewers
+ const commentsFile = resolve("pr-comments.json");
+ let existingComments: ExistingReviewComment[] = [];
+ if (existsSync(commentsFile)) {
+ try {
+ existingComments = JSON.parse(readFileSync(commentsFile, "utf8"));
+ if (existingComments.length > 0) {
+ debugLog(`📝 Found ${existingComments.length} existing review comments from other reviewers`);
+ }
+ } catch (error) {
+ debugLog("⚠️ Failed to parse existing comments file, continuing without them");
+ }
+ }
+
// Run the autonomous agent review
debugLog("🚀 Starting autonomous PR review...");
const modelConfig = getModelConfig();
@@ -93,6 +107,7 @@ export class ReviewService {
prContext,
this.config.maxTurns,
this.config.blockingOnly,
+ existingComments,
);
debugLog("✅ Review completed!");
} catch (error: unknown) {
diff --git a/src/types.ts b/src/types.ts
index 3e08c53..12dabcd 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -29,3 +29,24 @@ export interface ParsedArgs {
diff: string;
pr: number;
}
+
+/**
+ * Represents an existing review comment on the PR.
+ * These are comments made by other reviewers that the bot should be aware of.
+ */
+export interface ExistingReviewComment {
+ /** GitHub comment ID (for adding reactions) */
+ id: number;
+ /** GitHub username of the commenter */
+ author: string;
+ /** The comment body/text */
+ body: string;
+ /** File path the comment is on */
+ path: string;
+ /** Line number in the new version of the file (may be null for file-level comments) */
+ line: number | null;
+ /** The diff hunk context around the comment */
+ diffHunk: string;
+ /** ISO timestamp when the comment was created */
+ createdAt: string;
+}
diff --git a/test/review-service.test.ts b/test/review-service.test.ts
index f7a1333..cbbae1b 100644
--- a/test/review-service.test.ts
+++ b/test/review-service.test.ts
@@ -89,6 +89,7 @@ diff --git a/src/test.ts b/src/test.ts
}), // prContext
20, // maxTurns
false, // blockingOnly
+ [], // existingComments (empty when no pr-comments.json)
);
});
@@ -107,6 +108,7 @@ diff --git a/src/test.ts b/src/test.ts
expect.any(Object),
20,
true, // blockingOnly should be true
+ [], // existingComments
);
});