From 3b880eb4781aa6147dcce3ac939c5304e73ebb75 Mon Sep 17 00:00:00 2001 From: "pullfrog[bot]" <226033991+pullfrog[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 20:41:43 +0000 Subject: [PATCH] Implement GitHub suggestion format instructions (#79) * implement github suggestion format instructions add instructions for agents to use github's suggestion format (```suggestion blocks) when providing code suggestions in comments. this enables one-click apply for suggested changes. updated: - action/mcp/review.ts: added suggestion format guidance to create_pull_request_review tool description and comment body parameter - action/mcp/comment.ts: added suggestion format guidance to all comment tools with clarification that suggestions only work on pr line-level review comments - action/modes.ts: added detailed example in review mode and reminder in address reviews mode fixes #70 * Address PR review feedback - Remove suggestion format guidance from report_progress (not applicable) - De-duplicate description across Comment, EditComment, ReplyToReviewComment - Drop outer fence in suggestion format example - Clarify that suggestions only work for self-contained changes - Remove useless example comment from review tool description --------- Co-authored-by: pullfrog --- dispatch/entry | 23 ++++++++++++++++------- entry | 23 ++++++++++++++++------- mcp/comment.ts | 9 ++++++--- mcp/review.ts | 5 +++-- modes.ts | 12 ++++++++++-- run/entry | 23 ++++++++++++++++------- 6 files changed, 67 insertions(+), 28 deletions(-) diff --git a/dispatch/entry b/dispatch/entry index 736a9e8..5e7c512 100755 --- a/dispatch/entry +++ b/dispatch/entry @@ -100417,7 +100417,7 @@ ${dependencyInstallationGuidance} 4. Make the necessary code changes to address the feedback. Work through each review comment systematically. -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"). +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"). If suggesting a small, specific, self-contained code change, use GitHub's suggestion format with \`\`\`suggestion blocks. 6. Test your changes to ensure they work correctly. @@ -100442,7 +100442,15 @@ ${disableProgressComment ? "" : ` - Can you imagine a better approach? If so, explain. Make sure it's strictly better, not just different. - Are there bugs, edge cases, security issues, or usability issues? Use your imagination. -3. **DRAFT** - For each inline comment, find the line in the diff. Each code line shows: \`| OLD | NEW | TYPE | CODE\`. Use the NEW line number (second column). +3. **DRAFT** - For each inline comment, find the line in the diff. Each code line shows: \`| OLD | NEW | TYPE | CODE\`. Use the NEW line number (second column). When suggesting specific code changes, use GitHub's suggestion format with \`\`\`suggestion blocks to enable one-click apply. Example: + you could simplify this + \`\`\`suggestion + const result = data.map(x => x.value); + \`\`\` + or you could use reduce instead + \`\`\`suggestion + const result = data.reduce((acc, x) => [...acc, x.value], []); + \`\`\` 4. **FILTER COMMENTS** - Do not nitpick! Do not leave compliments that are not actionable. Do not critique the code hygiene or anything stylistic. @@ -106571,6 +106579,7 @@ async function buildCommentFooter({ } return buildPullfrogFooter(footerParams); } +var SUGGESTION_FORMAT_DESCRIPTION = "when suggesting code changes, use GitHub's suggestion format with ```suggestion blocks to enable one-click apply (e.g., 'you could do this\\n```suggestion\\nsuggested code here\\n```'). note: suggestions only work on pull request line-level review comments, not on issue/PR-level comments."; function buildImplementPlanLink(owner, repo, issueNumber, commentId) { const apiUrl = process.env.API_URL || "https://pullfrog.com"; return `[Implement plan \u2794](${apiUrl}/trigger/${owner}/${repo}/${issueNumber}?action=implement&comment_id=${commentId})`; @@ -106582,7 +106591,7 @@ async function addFooter(body, payload, octokit) { } var Comment = type({ issueNumber: type.number.describe("the issue number to comment on"), - body: type.string.describe("the comment body content") + body: type.string.describe(`the comment body content. ${SUGGESTION_FORMAT_DESCRIPTION}`) }); function CreateCommentTool(ctx) { return tool({ @@ -106608,7 +106617,7 @@ function CreateCommentTool(ctx) { } var EditComment = type({ commentId: type.number.describe("the ID of the comment to edit"), - body: type.string.describe("the new comment body content") + body: type.string.describe(`the new comment body content. ${SUGGESTION_FORMAT_DESCRIPTION}`) }); function EditCommentTool(ctx) { return tool({ @@ -106829,7 +106838,7 @@ var 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( - "extremely brief reply (1 sentence max) explaining what was fixed, e.g. 'Fixed by renaming to X' or 'Added null check'" + `extremely brief reply (1 sentence max) explaining what was fixed, e.g. 'Fixed by renaming to X' or 'Added null check'. ${SUGGESTION_FORMAT_DESCRIPTION}` ) }); function ReplyToReviewCommentTool(ctx) { @@ -137846,7 +137855,7 @@ var CreatePullRequestReview = type({ "Side of the diff: LEFT (old code, lines starting with -) or RIGHT (new code, lines starting with + or unchanged). Defaults to RIGHT." ).optional(), body: type.string.describe( - "The comment text for this specific line. For issues appearing multiple times, comment on the first occurrence and reference others." + "The comment text for this specific line. For issues appearing multiple times, comment on the first occurrence and reference others. When providing code suggestions, use GitHub's suggestion format with ```suggestion blocks to enable one-click apply. Only include explanatory text if the suggested code requires clarification." ), start_line: type.number.describe("Start line for multi-line comments (optional, for commenting on ranges)").optional() }).array().describe( @@ -137856,7 +137865,7 @@ var CreatePullRequestReview = type({ function CreatePullRequestReviewTool(ctx) { return tool({ name: "create_pull_request_review", - 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.", + 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. When suggesting code changes in comments, use GitHub's suggestion format (```suggestion blocks) to enable one-click apply.", parameters: CreatePullRequestReview, execute: execute(async ({ pull_number, body, commit_id, comments = [] }) => { ctx.toolState.prNumber = pull_number; diff --git a/entry b/entry index 2a3c7c1..d507e62 100755 --- a/entry +++ b/entry @@ -100417,7 +100417,7 @@ ${dependencyInstallationGuidance} 4. Make the necessary code changes to address the feedback. Work through each review comment systematically. -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"). +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"). If suggesting a small, specific, self-contained code change, use GitHub's suggestion format with \`\`\`suggestion blocks. 6. Test your changes to ensure they work correctly. @@ -100442,7 +100442,15 @@ ${disableProgressComment ? "" : ` - Can you imagine a better approach? If so, explain. Make sure it's strictly better, not just different. - Are there bugs, edge cases, security issues, or usability issues? Use your imagination. -3. **DRAFT** - For each inline comment, find the line in the diff. Each code line shows: \`| OLD | NEW | TYPE | CODE\`. Use the NEW line number (second column). +3. **DRAFT** - For each inline comment, find the line in the diff. Each code line shows: \`| OLD | NEW | TYPE | CODE\`. Use the NEW line number (second column). When suggesting specific code changes, use GitHub's suggestion format with \`\`\`suggestion blocks to enable one-click apply. Example: + you could simplify this + \`\`\`suggestion + const result = data.map(x => x.value); + \`\`\` + or you could use reduce instead + \`\`\`suggestion + const result = data.reduce((acc, x) => [...acc, x.value], []); + \`\`\` 4. **FILTER COMMENTS** - Do not nitpick! Do not leave compliments that are not actionable. Do not critique the code hygiene or anything stylistic. @@ -106571,6 +106579,7 @@ async function buildCommentFooter({ } return buildPullfrogFooter(footerParams); } +var SUGGESTION_FORMAT_DESCRIPTION = "when suggesting code changes, use GitHub's suggestion format with ```suggestion blocks to enable one-click apply (e.g., 'you could do this\\n```suggestion\\nsuggested code here\\n```'). note: suggestions only work on pull request line-level review comments, not on issue/PR-level comments."; function buildImplementPlanLink(owner, repo, issueNumber, commentId) { const apiUrl = process.env.API_URL || "https://pullfrog.com"; return `[Implement plan \u2794](${apiUrl}/trigger/${owner}/${repo}/${issueNumber}?action=implement&comment_id=${commentId})`; @@ -106582,7 +106591,7 @@ async function addFooter(body, payload, octokit) { } var Comment = type({ issueNumber: type.number.describe("the issue number to comment on"), - body: type.string.describe("the comment body content") + body: type.string.describe(`the comment body content. ${SUGGESTION_FORMAT_DESCRIPTION}`) }); function CreateCommentTool(ctx) { return tool({ @@ -106608,7 +106617,7 @@ function CreateCommentTool(ctx) { } var EditComment = type({ commentId: type.number.describe("the ID of the comment to edit"), - body: type.string.describe("the new comment body content") + body: type.string.describe(`the new comment body content. ${SUGGESTION_FORMAT_DESCRIPTION}`) }); function EditCommentTool(ctx) { return tool({ @@ -106829,7 +106838,7 @@ var 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( - "extremely brief reply (1 sentence max) explaining what was fixed, e.g. 'Fixed by renaming to X' or 'Added null check'" + `extremely brief reply (1 sentence max) explaining what was fixed, e.g. 'Fixed by renaming to X' or 'Added null check'. ${SUGGESTION_FORMAT_DESCRIPTION}` ) }); function ReplyToReviewCommentTool(ctx) { @@ -137846,7 +137855,7 @@ var CreatePullRequestReview = type({ "Side of the diff: LEFT (old code, lines starting with -) or RIGHT (new code, lines starting with + or unchanged). Defaults to RIGHT." ).optional(), body: type.string.describe( - "The comment text for this specific line. For issues appearing multiple times, comment on the first occurrence and reference others." + "The comment text for this specific line. For issues appearing multiple times, comment on the first occurrence and reference others. When providing code suggestions, use GitHub's suggestion format with ```suggestion blocks to enable one-click apply. Only include explanatory text if the suggested code requires clarification." ), start_line: type.number.describe("Start line for multi-line comments (optional, for commenting on ranges)").optional() }).array().describe( @@ -137856,7 +137865,7 @@ var CreatePullRequestReview = type({ function CreatePullRequestReviewTool(ctx) { return tool({ name: "create_pull_request_review", - 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.", + 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. When suggesting code changes in comments, use GitHub's suggestion format (```suggestion blocks) to enable one-click apply.", parameters: CreatePullRequestReview, execute: execute(async ({ pull_number, body, commit_id, comments = [] }) => { ctx.toolState.prNumber = pull_number; diff --git a/mcp/comment.ts b/mcp/comment.ts index 83a265f..2e03b03 100644 --- a/mcp/comment.ts +++ b/mcp/comment.ts @@ -77,6 +77,9 @@ async function buildCommentFooter({ return buildPullfrogFooter(footerParams); } +const SUGGESTION_FORMAT_DESCRIPTION = + "when suggesting code changes, use GitHub's suggestion format with ```suggestion blocks to enable one-click apply (e.g., 'you could do this\\n```suggestion\\nsuggested code here\\n```'). note: suggestions only work on pull request line-level review comments, not on issue/PR-level comments."; + function buildImplementPlanLink( owner: string, repo: string, @@ -99,7 +102,7 @@ async function addFooter( export const Comment = type({ issueNumber: type.number.describe("the issue number to comment on"), - body: type.string.describe("the comment body content"), + body: type.string.describe(`the comment body content. ${SUGGESTION_FORMAT_DESCRIPTION}`), }); export function CreateCommentTool(ctx: ToolContext) { @@ -130,7 +133,7 @@ export function CreateCommentTool(ctx: ToolContext) { export const EditComment = type({ commentId: type.number.describe("the ID of the comment to edit"), - body: type.string.describe("the new comment body content"), + body: type.string.describe(`the new comment body content. ${SUGGESTION_FORMAT_DESCRIPTION}`), }); export function EditCommentTool(ctx: ToolContext) { @@ -472,7 +475,7 @@ export const 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( - "extremely brief reply (1 sentence max) explaining what was fixed, e.g. 'Fixed by renaming to X' or 'Added null check'" + `extremely brief reply (1 sentence max) explaining what was fixed, e.g. 'Fixed by renaming to X' or 'Added null check'. ${SUGGESTION_FORMAT_DESCRIPTION}` ), }); diff --git a/mcp/review.ts b/mcp/review.ts index 4387bb5..30c2477 100644 --- a/mcp/review.ts +++ b/mcp/review.ts @@ -29,7 +29,7 @@ export const CreatePullRequestReview = type({ ) .optional(), body: type.string.describe( - "The comment text for this specific line. For issues appearing multiple times, comment on the first occurrence and reference others." + "The comment text for this specific line. For issues appearing multiple times, comment on the first occurrence and reference others. When providing code suggestions, use GitHub's suggestion format with ```suggestion blocks to enable one-click apply. Only include explanatory text if the suggested code requires clarification." ), start_line: type.number .describe("Start line for multi-line comments (optional, for commenting on ranges)") @@ -48,7 +48,8 @@ export function CreatePullRequestReviewTool(ctx: ToolContext) { 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.", + "Only use 'body' for a 1-2 sentence summary with urgency and critical callouts. " + + "When suggesting code changes in comments, use GitHub's suggestion format (```suggestion blocks) to enable one-click apply.", parameters: CreatePullRequestReview, execute: execute(async ({ pull_number, body, commit_id, comments = [] }) => { // set PR context diff --git a/modes.ts b/modes.ts index d8a786f..6fb1573 100644 --- a/modes.ts +++ b/modes.ts @@ -89,7 +89,7 @@ ${dependencyInstallationGuidance} 4. Make the necessary code changes to address the feedback. Work through each review comment systematically. -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"). +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"). If suggesting a small, specific, self-contained code change, use GitHub's suggestion format with \`\`\`suggestion blocks. 6. Test your changes to ensure they work correctly. @@ -119,7 +119,15 @@ ${ - Can you imagine a better approach? If so, explain. Make sure it's strictly better, not just different. - Are there bugs, edge cases, security issues, or usability issues? Use your imagination. -3. **DRAFT** - For each inline comment, find the line in the diff. Each code line shows: \`| OLD | NEW | TYPE | CODE\`. Use the NEW line number (second column). +3. **DRAFT** - For each inline comment, find the line in the diff. Each code line shows: \`| OLD | NEW | TYPE | CODE\`. Use the NEW line number (second column). When suggesting specific code changes, use GitHub's suggestion format with \`\`\`suggestion blocks to enable one-click apply. Example: + you could simplify this + \`\`\`suggestion + const result = data.map(x => x.value); + \`\`\` + or you could use reduce instead + \`\`\`suggestion + const result = data.reduce((acc, x) => [...acc, x.value], []); + \`\`\` 4. **FILTER COMMENTS** - Do not nitpick! Do not leave compliments that are not actionable. Do not critique the code hygiene or anything stylistic. diff --git a/run/entry b/run/entry index c65f31b..c7b7524 100755 --- a/run/entry +++ b/run/entry @@ -100417,7 +100417,7 @@ ${dependencyInstallationGuidance} 4. Make the necessary code changes to address the feedback. Work through each review comment systematically. -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"). +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"). If suggesting a small, specific, self-contained code change, use GitHub's suggestion format with \`\`\`suggestion blocks. 6. Test your changes to ensure they work correctly. @@ -100442,7 +100442,15 @@ ${disableProgressComment ? "" : ` - Can you imagine a better approach? If so, explain. Make sure it's strictly better, not just different. - Are there bugs, edge cases, security issues, or usability issues? Use your imagination. -3. **DRAFT** - For each inline comment, find the line in the diff. Each code line shows: \`| OLD | NEW | TYPE | CODE\`. Use the NEW line number (second column). +3. **DRAFT** - For each inline comment, find the line in the diff. Each code line shows: \`| OLD | NEW | TYPE | CODE\`. Use the NEW line number (second column). When suggesting specific code changes, use GitHub's suggestion format with \`\`\`suggestion blocks to enable one-click apply. Example: + you could simplify this + \`\`\`suggestion + const result = data.map(x => x.value); + \`\`\` + or you could use reduce instead + \`\`\`suggestion + const result = data.reduce((acc, x) => [...acc, x.value], []); + \`\`\` 4. **FILTER COMMENTS** - Do not nitpick! Do not leave compliments that are not actionable. Do not critique the code hygiene or anything stylistic. @@ -106571,6 +106579,7 @@ async function buildCommentFooter({ } return buildPullfrogFooter(footerParams); } +var SUGGESTION_FORMAT_DESCRIPTION = "when suggesting code changes, use GitHub's suggestion format with ```suggestion blocks to enable one-click apply (e.g., 'you could do this\\n```suggestion\\nsuggested code here\\n```'). note: suggestions only work on pull request line-level review comments, not on issue/PR-level comments."; function buildImplementPlanLink(owner, repo, issueNumber, commentId) { const apiUrl = process.env.API_URL || "https://pullfrog.com"; return `[Implement plan \u2794](${apiUrl}/trigger/${owner}/${repo}/${issueNumber}?action=implement&comment_id=${commentId})`; @@ -106582,7 +106591,7 @@ async function addFooter(body, payload, octokit) { } var Comment = type({ issueNumber: type.number.describe("the issue number to comment on"), - body: type.string.describe("the comment body content") + body: type.string.describe(`the comment body content. ${SUGGESTION_FORMAT_DESCRIPTION}`) }); function CreateCommentTool(ctx) { return tool({ @@ -106608,7 +106617,7 @@ function CreateCommentTool(ctx) { } var EditComment = type({ commentId: type.number.describe("the ID of the comment to edit"), - body: type.string.describe("the new comment body content") + body: type.string.describe(`the new comment body content. ${SUGGESTION_FORMAT_DESCRIPTION}`) }); function EditCommentTool(ctx) { return tool({ @@ -106829,7 +106838,7 @@ var 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( - "extremely brief reply (1 sentence max) explaining what was fixed, e.g. 'Fixed by renaming to X' or 'Added null check'" + `extremely brief reply (1 sentence max) explaining what was fixed, e.g. 'Fixed by renaming to X' or 'Added null check'. ${SUGGESTION_FORMAT_DESCRIPTION}` ) }); function ReplyToReviewCommentTool(ctx) { @@ -137846,7 +137855,7 @@ var CreatePullRequestReview = type({ "Side of the diff: LEFT (old code, lines starting with -) or RIGHT (new code, lines starting with + or unchanged). Defaults to RIGHT." ).optional(), body: type.string.describe( - "The comment text for this specific line. For issues appearing multiple times, comment on the first occurrence and reference others." + "The comment text for this specific line. For issues appearing multiple times, comment on the first occurrence and reference others. When providing code suggestions, use GitHub's suggestion format with ```suggestion blocks to enable one-click apply. Only include explanatory text if the suggested code requires clarification." ), start_line: type.number.describe("Start line for multi-line comments (optional, for commenting on ranges)").optional() }).array().describe( @@ -137856,7 +137865,7 @@ var CreatePullRequestReview = type({ function CreatePullRequestReviewTool(ctx) { return tool({ name: "create_pull_request_review", - 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.", + 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. When suggesting code changes in comments, use GitHub's suggestion format (```suggestion blocks) to enable one-click apply.", parameters: CreatePullRequestReview, execute: execute(async ({ pull_number, body, commit_id, comments = [] }) => { ctx.toolState.prNumber = pull_number;