iterate on pr fix

This commit is contained in:
David Blass
2025-12-11 18:02:44 -05:00
parent 7959a51995
commit bb55216a6b
8 changed files with 312 additions and 79 deletions
+121 -36
View File
@@ -39243,6 +39243,47 @@ var init_api = __esm({
}
});
// utils/buildPullfrogFooter.ts
function buildPullfrogFooter(params) {
const parts = [];
if (params.triggeredBy) {
parts.push("Triggered by [Pullfrog](https://pullfrog.com)");
}
if (params.agent) {
parts.push(`Using [${params.agent.displayName}](${params.agent.url})`);
}
if (params.workflowRun) {
const { owner, repo, runId } = params.workflowRun;
parts.push(`[View workflow run](https://github.com/${owner}/${repo}/actions/runs/${runId})`);
}
if (params.customParts) {
parts.push(...params.customParts);
}
const allParts = [
...parts,
"[pullfrog.com](https://pullfrog.com)",
"[\u{1D54F}](https://x.com/pullfrogai)"
];
return `
${PULLFROG_DIVIDER}
<sup>${FROG_LOGO}&nbsp;&nbsp;\uFF5C ${allParts.join(" \uFF5C ")}</sup>`;
}
function stripExistingFooter(body) {
const dividerIndex = body.indexOf(PULLFROG_DIVIDER);
if (dividerIndex === -1) {
return body;
}
return body.substring(0, dividerIndex).trimEnd();
}
var PULLFROG_DIVIDER, FROG_LOGO;
var init_buildPullfrogFooter = __esm({
"utils/buildPullfrogFooter.ts"() {
"use strict";
PULLFROG_DIVIDER = "<!-- PULLFROG_DIVIDER_DO_NOT_REMOVE_PLZ -->";
FROG_LOGO = `<a href="https://pullfrog.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pullfrog.com/logos/frog-white-full-128px.png"><img src="https://pullfrog.com/logos/frog-green-full-128px.png" width="9px" height="9px" style="vertical-align: middle; " alt="Pullfrog"></picture></a>`;
}
});
// mcp/shared.ts
function initMcpContext(state) {
mcpInitContext = state;
@@ -39404,6 +39445,7 @@ __export(comment_exports, {
ReplyToReviewCommentTool: () => ReplyToReviewCommentTool,
ReportProgress: () => ReportProgress,
ReportProgressTool: () => ReportProgressTool,
deleteProgressComment: () => deleteProgressComment,
ensureProgressCommentUpdated: () => ensureProgressCommentUpdated,
reportProgress: () => reportProgress,
wasProgressCommentUpdated: () => wasProgressCommentUpdated
@@ -39413,19 +39455,14 @@ function buildCommentFooter(payload) {
const runId = process.env.GITHUB_RUN_ID;
const agentName = payload.agent;
const agentInfo = agentName ? agentsManifest[agentName] : null;
const agentDisplayName = agentInfo?.displayName || "Unknown agent";
const agentUrl = agentInfo?.url || "https://pullfrog.com";
const workflowRunPart = runId ? `[View workflow run](https://github.com/${repoContext.owner}/${repoContext.name}/actions/runs/${runId})` : "View workflow run";
return `
${PULLFROG_DIVIDER}
<sup><a href="https://pullfrog.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pullfrog.com/logos/frog-white-full-128px.png"><img src="https://pullfrog.com/logos/frog-green-full-128px.png" width="9px" height="9px" style="vertical-align: middle; " alt="Pullfrog"></picture></a>&nbsp;&nbsp;\uFF5C Triggered by [Pullfrog](https://pullfrog.com) \uFF5C Using [${agentDisplayName}](${agentUrl}) \uFF5C ${workflowRunPart} \uFF5C [\u{1D54F}](https://x.com/pullfrogai)</sup>`;
}
function stripExistingFooter(body) {
const dividerIndex = body.indexOf(PULLFROG_DIVIDER);
if (dividerIndex === -1) {
return body;
}
return body.substring(0, dividerIndex).trimEnd();
return buildPullfrogFooter({
triggeredBy: true,
agent: {
displayName: agentInfo?.displayName || "Unknown agent",
url: agentInfo?.url || "https://pullfrog.com"
},
workflowRun: runId ? { owner: repoContext.owner, repo: repoContext.name, runId } : void 0
});
}
function addFooter(body, payload) {
const bodyWithoutFooter = stripExistingFooter(body);
@@ -39494,6 +39531,22 @@ async function reportProgress({ body }) {
function wasProgressCommentUpdated() {
return progressCommentWasUpdated;
}
async function deleteProgressComment() {
const existingCommentId = getProgressCommentId();
if (!existingCommentId) {
return false;
}
const ctx = getMcpContext();
await ctx.octokit.rest.issues.deleteComment({
owner: ctx.owner,
repo: ctx.name,
comment_id: existingCommentId
});
progressCommentId = null;
progressCommentIdInitialized = true;
progressCommentWasUpdated = true;
return true;
}
async function ensureProgressCommentUpdated(payload) {
if (progressCommentWasUpdated) {
return;
@@ -39553,7 +39606,7 @@ The workflow encountered an error before any progress could be reported. Please
body
});
}
var PULLFROG_DIVIDER, LEAPING_INTO_ACTION_PREFIX, Comment, CreateCommentTool, EditComment, EditCommentTool, progressCommentId, progressCommentIdInitialized, progressCommentWasUpdated, ReportProgress, ReportProgressTool, ReplyToReviewComment, ReplyToReviewCommentTool;
var LEAPING_INTO_ACTION_PREFIX, Comment, CreateCommentTool, EditComment, EditCommentTool, progressCommentId, progressCommentIdInitialized, progressCommentWasUpdated, ReportProgress, ReportProgressTool, ReplyToReviewComment, ReplyToReviewCommentTool;
var init_comment = __esm({
"mcp/comment.ts"() {
"use strict";
@@ -39561,9 +39614,9 @@ var init_comment = __esm({
init_out4();
init_external();
init_api();
init_buildPullfrogFooter();
init_github();
init_shared3();
PULLFROG_DIVIDER = "<!-- PULLFROG_DIVIDER_DO_NOT_REMOVE_PLZ -->";
LEAPING_INTO_ACTION_PREFIX = "Leaping into action";
Comment = type({
issueNumber: type.number.describe("the issue number to comment on"),
@@ -39571,7 +39624,7 @@ var init_comment = __esm({
});
CreateCommentTool = tool({
name: "create_issue_comment",
description: "Create a comment on a GitHub issue",
description: "Create a comment on a GitHub issue. NOTE: Do NOT use this for progress updates or status summaries - use report_progress instead, which updates the existing progress comment.",
parameters: Comment,
execute: contextualize(async ({ issueNumber, body }, ctx) => {
const bodyWithFooter = addFooter(body, ctx.payload);
@@ -39635,11 +39688,13 @@ var init_comment = __esm({
ReplyToReviewComment = type({
pull_number: type.number.describe("the pull request number"),
comment_id: type.number.describe("the ID of the review comment to reply to"),
body: type.string.describe("the reply text explaining how the feedback was addressed")
body: type.string.describe(
"extremely brief reply (1 sentence max) explaining what was fixed, e.g. 'Fixed by renaming to X' or 'Added null check'"
)
});
ReplyToReviewCommentTool = tool({
name: "reply_to_review_comment",
description: "Reply to a PR review comment thread explaining how the feedback was addressed. Use this after addressing each review comment to provide specific context about the changes made.",
description: "Reply to a PR review comment thread. Call this for EACH comment you address. Keep replies extremely brief (1 sentence max).",
parameters: ReplyToReviewComment,
execute: contextualize(async ({ pull_number, comment_id, body }, ctx) => {
const bodyWithFooter = addFooter(body, ctx.payload);
@@ -97388,7 +97443,7 @@ function query({
// package.json
var package_default = {
name: "@pullfrog/action",
version: "0.0.131",
version: "0.0.132",
type: "module",
files: [
"index.js",
@@ -97901,16 +97956,15 @@ function getModes({ disableProgressComment }) {
4. Make the necessary code changes to address the feedback. Work through each review comment systematically.
5. After addressing each review comment, use ${ghPullfrogMcpName}/reply_to_review_comment to reply directly to that comment thread explaining what change was made (keep replies concise, 1-2 sentences).
5. **CRITICAL: Reply to EACH review comment individually.** After fixing each comment, use ${ghPullfrogMcpName}/reply_to_review_comment to reply directly to that comment thread. Keep replies extremely brief (1 sentence max, e.g., "Fixed by renaming to X" or "Added null check").
6. Test your changes to ensure they work correctly.${disableProgressComment ? "" : `
6. Test your changes to ensure they work correctly.
7. ${reportProgressInstruction}`}
7. When done, commit and push your changes to the existing PR branch. Do not create a new branch or PR - you are updating an existing one.
${disableProgressComment ? "" : `
8. ${reportProgressInstruction}
8. When done, commit and push your changes to the existing PR branch. Do not create a new branch or PR - you are updating an existing one.
9. Call report_progress one final time ONLY if you haven't already included a complete summary in a previous report_progress call. If you already called report_progress with complete information, you do NOT need to call it again. Only make a final call if you need to add missing information. **IMPORTANT**: Do NOT overwrite a good comment with details with a generic message.
`
**CRITICAL: Keep the progress comment extremely brief.** The summary should be 1-2 sentences max (e.g., "Fixed 3 review comments and pushed changes."). Almost all detail belongs in the individual reply_to_review_comment calls, NOT in the progress comment.`}`
},
{
name: "Review",
@@ -97920,13 +97974,16 @@ function getModes({ disableProgressComment }) {
2. View diff: git diff origin/<base>...origin/<head> (use line numbers from this for inline comments, replace <base> and <head> with 'base' and 'head' from PR info)
3. Read files from the checked-out PR branch to understand the implementation${disableProgressComment ? "" : `
3. Read files from the checked-out PR branch to understand the implementation
4. ${reportProgressInstruction}`}
4. Submit review using ${ghPullfrogMcpName}/submit_pull_request_review
5. When submitting review: use the 'comments' array for ALL specific code issues - include the file path and line position from the diff
6. Only use the 'body' field for a brief summary (1-2 sentences) or for feedback that doesn't apply to a specific code location`
**CRITICAL: Prioritize per-line feedback over summary text.**
- ALL specific feedback MUST go in the 'comments' array with file paths and line numbers from the diff
- for issues appearing in multiple places, comment on the FIRST occurrence and reference others (e.g., "also at lines X, Y" or "similar issue in otherFile.ts:42")
- the 'body' field is ONLY for: (1) a 1-2 sentence high-level overview, (2) urgency level (e.g., "minor suggestions" vs "blocking issues"), (3) critical security callouts (e.g., API key exposure)
- 95%+ of review content should be in per-line comments; the body should be just a couple sentences
- the review body will include quick action links for addressing feedback, so keep it concise`
},
{
name: "Plan",
@@ -124751,11 +124808,13 @@ var PullRequestInfoTool = tool({
// mcp/review.ts
init_out4();
init_buildPullfrogFooter();
init_comment();
init_shared3();
var Review = type({
pull_number: type.number.describe("The pull request number to review"),
body: type.string.describe(
"Brief summary or general feedback that doesn't apply to specific code locations. Keep it concise - most feedback should be in the 'comments' array."
"1-2 sentence high-level summary ONLY. Include urgency level and critical callouts (e.g., API key leak). ALL specific feedback MUST go in 'comments' array instead."
).optional(),
commit_id: type.string.describe("Optional SHA of the commit being reviewed. Defaults to latest.").optional(),
comments: type({
@@ -124766,15 +124825,17 @@ var Review = type({
side: type.enumerated("LEFT", "RIGHT").describe(
"Side of the diff: LEFT (old code) or RIGHT (new code). Defaults to RIGHT if not provided."
).optional(),
body: type.string.describe("The comment text for this specific line"),
body: type.string.describe(
"The comment text for this specific line. For issues appearing multiple times, comment on the first occurrence and reference others."
),
start_line: type.number.describe("Start line for multi-line comments (optional, for commenting on ranges)").optional()
}).array().describe(
"REQUIRED: Array of inline comments for specific code issues. Use this for all location-specific feedback. Use 'git diff origin/<base>...origin/<head>' to find the correct line numbers (typically use the line numbers shown on the RIGHT side for new code, LEFT side for old code)."
"PRIMARY location for ALL feedback. 95%+ of review content should be here. Use 'git diff origin/<base>...origin/<head>' to find correct line numbers (RIGHT side for new code, LEFT for old)."
).optional()
});
var ReviewTool = tool({
name: "submit_pull_request_review",
description: "Submit a review (approve, request changes, or comment) for an existing pull request. IMPORTANT: Use 'comments' array for ALL specific code issues at the line-level. Only use 'body' for a brief summary or feedback that doesn't apply to a specific location.",
description: "Submit a review for an existing pull request. IMPORTANT: 95%+ of feedback should be in 'comments' array with file paths and line numbers. Only use 'body' for a 1-2 sentence summary with urgency and critical callouts.",
parameters: Review,
execute: contextualize(async ({ pull_number, body, commit_id, comments = [] }, ctx) => {
const pr = await ctx.octokit.rest.pulls.get({
@@ -124808,9 +124869,25 @@ var ReviewTool = tool({
});
}
const result = await ctx.octokit.rest.pulls.createReview(params);
const reviewId = result.data.id;
const apiUrl = process.env.API_URL || "https://pullfrog.com";
const fixAllUrl = `${apiUrl}/trigger/${ctx.owner}/${ctx.name}/${pull_number}?action=fix&review_id=${reviewId}`;
const fixApprovedUrl = `${apiUrl}/trigger/${ctx.owner}/${ctx.name}/${pull_number}?action=fix-approved&review_id=${reviewId}`;
const footer = buildPullfrogFooter({
customParts: [`[Fix all \u2794](${fixAllUrl})`, `[Fix \u{1F44D}s \u2794](${fixApprovedUrl})`]
});
const updatedBody = (body || "") + footer;
await ctx.octokit.rest.pulls.updateReview({
owner: ctx.owner,
repo: ctx.name,
pull_number,
review_id: reviewId,
body: updatedBody
});
await deleteProgressComment();
return {
success: true,
reviewId: result.data.id,
reviewId,
html_url: result.data.html_url,
state: result.data.state,
user: result.data.user?.login,
@@ -125172,6 +125249,14 @@ async function main(inputs) {
await startMcpServer(ctx);
mcpServerClose = ctx.mcpServerClose;
timer.checkpoint("startMcpServer");
if (ctx.payload.event.trigger === "fix_review" && Array.isArray(ctx.payload.event.comment_ids) && ctx.payload.event.comment_ids.length === 0) {
await reportProgress({
body: `\u{1F44D} **No approved comments found**
To use "Fix \u{1F44D}s", add a \u{1F44D} reaction to one or more inline review comments you want fixed.`
});
return { success: true };
}
setupMcpServers(ctx);
await installAgentCli(ctx);
timer.checkpoint("installAgentCli");