diff --git a/entry b/entry index 68b3837..5ac2a88 100755 --- a/entry +++ b/entry @@ -119602,21 +119602,21 @@ function computeModes() { 2. ${dependencyInstallationStep} -3. Review the feedback provided. Understand each review comment and what changes are being requested. - - **EVENT DATA may contain review comment details**: If available, \`approved_comments\` are comments to address, \`unapproved_comments\` are for context only. The \`triggerer\` field indicates who initiated this action - prioritize their replies when deciding how to implement fixes. - - You can use ${ghPullfrogMcpName}/get_pull_request to get PR metadata if needed. +3. Fetch review comments using ${ghPullfrogMcpName}/get_review_comments with \`pull_number\` and \`review_id\` from EVENT DATA. This returns \`commentsPath\` - read that file for full comment details with diff context. If EVENT DATA contains a \`triggerer\` field (indicating who requested fixes), you can pass \`approved_by\` to filter to only comments they approved with \u{1F44D}. -4. If the request requires understanding the codebase structure or conventions, gather relevant context. Read AGENTS.md if it exists. +4. Review the feedback provided. Understand each review comment and what changes are being requested. -5. Make the necessary code changes to address the feedback. Work through each review comment systematically. +5. If the request requires understanding the codebase structure or conventions, gather relevant context. Read AGENTS.md if it exists. -6. **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. Make the necessary code changes to address the feedback. Work through each review comment systematically. -7. Test your changes to ensure they work correctly. +7. **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. -8. When done, commit your changes with ${ghPullfrogMcpName}/commit_files, then push with ${ghPullfrogMcpName}/push_branch. The push will automatically go to the correct remote (including fork repos). Do not create a new branch or PR - you are updating an existing one. +8. Test your changes to ensure they work correctly. -9. ${reportProgressInstruction} +9. When done, commit your changes with ${ghPullfrogMcpName}/commit_files, then push with ${ghPullfrogMcpName}/push_branch. The push will automatically go to the correct remote (including fork repos). Do not create a new branch or PR - you are updating an existing one. + +10. ${reportProgressInstruction} **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.` }, @@ -138074,17 +138074,25 @@ ${ctx.error}` : ctx.error; // utils/instructions.ts import { execSync } from "node:child_process"; function buildRuntimeContext(ctx) { - const lines = []; - lines.push(`working_directory: ${process.cwd()}`); - lines.push(`log_level: ${process.env.LOG_LEVEL}`); + const { + "~pullfrog": _, + prompt: _p, + repoInstructions: _r, + event: _e, + ...payloadRest + } = ctx.payload; + let gitStatus; try { - const gitStatus = execSync("git status --short", { encoding: "utf-8", stdio: "pipe" }).trim(); - lines.push(`git_status: ${gitStatus || "(clean)"}`); + gitStatus = execSync("git status --short", { encoding: "utf-8", stdio: "pipe" }).trim() || "(clean)"; } catch { } - lines.push(`repo: ${ctx.repoData.owner}/${ctx.repoData.name}`); - lines.push(`default_branch: ${ctx.repoData.repo.default_branch}`); - const ghVars = { + const data = { + ...payloadRest, + repo: `${ctx.repoData.owner}/${ctx.repoData.name}`, + default_branch: ctx.repoData.repo.default_branch, + working_directory: process.cwd(), + log_level: process.env.LOG_LEVEL, + git_status: gitStatus, github_event_name: process.env.GITHUB_EVENT_NAME, github_ref: process.env.GITHUB_REF, github_sha: process.env.GITHUB_SHA?.slice(0, 7), @@ -138092,12 +138100,25 @@ function buildRuntimeContext(ctx) { github_run_id: process.env.GITHUB_RUN_ID, github_workflow: process.env.GITHUB_WORKFLOW }; - for (const [key, value2] of Object.entries(ghVars)) { - if (value2) { - lines.push(`${key}: ${value2}`); - } + const filtered = Object.fromEntries(Object.entries(data).filter(([_2, v]) => v !== void 0)); + return encode(filtered); +} +function buildEventData(event) { + const { title, body, ...rest } = event; + const sections = []; + const trimmedTitle = typeof title === "string" ? title.trim() : ""; + const trimmedBody = typeof body === "string" ? body.trim() : ""; + if (trimmedTitle) { + sections.push(`# ${trimmedTitle}`); } - return lines.join("\n"); + if (trimmedBody) { + sections.push(trimmedBody); + } + if (Object.keys(rest).length > 0) { + if (sections.length > 0) sections.push("---"); + sections.push(encode(rest)); + } + return sections.join("\n\n"); } function getShellInstructions(bash) { const backgroundInstructions = `For long-running processes (dev servers, watchers), use \`bash({ command, background: true })\` which returns a handle. Use \`${ghPullfrogMcpName}/kill_background\` to stop background processes by handle.`; @@ -138115,17 +138136,7 @@ function getShellInstructions(bash) { } } function resolveInstructions(ctx) { - const event = encode({ - agent: ctx.payload.agent, - effort: ctx.payload.effort, - permissions: { - web: ctx.payload.web, - search: ctx.payload.search, - write: ctx.payload.write, - bash: ctx.payload.bash - }, - event: ctx.payload.event - }); + const event = buildEventData(ctx.payload.event); const runtime = buildRuntimeContext(ctx); const user = ctx.payload.prompt; const repo = ctx.payload.repoInstructions ?? ""; @@ -138219,12 +138230,12 @@ ${repoSection} ************* EVENT DATA ************* -The following is structured data about the context of this run (agent, effort level, permissions, and the GitHub event that triggered it). Use this context to understand the full situation. - ${event} ************* RUNTIME CONTEXT ************* +The following contains the agent configuration (agent, effort, permissions) and runtime environment details. + ${runtime}`; return { full, system, user, repo, event, runtime }; } diff --git a/external.ts b/external.ts index daedf78..99dc337 100644 --- a/external.ts +++ b/external.ts @@ -69,17 +69,13 @@ interface BasePayloadEvent { issue_number?: number; is_pr?: boolean; branch?: string; - pr_title?: string; - pr_body?: string | null; - issue_title?: string; - issue_body?: string | null; + /** title of the issue/PR (or contextual title for comments) */ + title?: string; + /** primary content for this trigger (issue body, PR body, comment body, review body, etc.) */ + body?: string | null; comment_id?: number; - comment_body?: string; review_id?: number; - review_body?: string | null; review_state?: string; - review_comments?: any[]; - context?: any; thread?: any; pull_request?: any; check_suite?: { @@ -102,8 +98,8 @@ interface PullRequestOpenedEvent extends BasePayloadEvent { trigger: "pull_request_opened"; issue_number: number; is_pr: true; - pr_title: string; - pr_body: string | null; + title: string; + body: string | null; branch: string; } @@ -111,8 +107,8 @@ interface PullRequestReadyForReviewEvent extends BasePayloadEvent { trigger: "pull_request_ready_for_review"; issue_number: number; is_pr: true; - pr_title: string; - pr_body: string | null; + title: string; + body: string | null; branch: string; } @@ -120,8 +116,8 @@ interface PullRequestReviewRequestedEvent extends BasePayloadEvent { trigger: "pull_request_review_requested"; issue_number: number; is_pr: true; - pr_title: string; - pr_body: string | null; + title: string; + body: string | null; branch: string; } @@ -130,10 +126,9 @@ interface PullRequestReviewSubmittedEvent extends BasePayloadEvent { issue_number: number; is_pr: true; review_id: number; - review_body: string | null; + /** review body is the primary content */ + body: string | null; review_state: string; - review_comments: any[]; - context: any; branch: string; } @@ -141,9 +136,10 @@ interface PullRequestReviewCommentCreatedEvent extends BasePayloadEvent { trigger: "pull_request_review_comment_created"; issue_number: number; is_pr: true; - pr_title: string; + title: string; comment_id: number; - comment_body: string; + /** comment body is the primary content */ + body: string; thread?: any; branch: string; } @@ -151,42 +147,42 @@ interface PullRequestReviewCommentCreatedEvent extends BasePayloadEvent { interface IssuesOpenedEvent extends BasePayloadEvent { trigger: "issues_opened"; issue_number: number; - issue_title: string; - issue_body: string | null; + title: string; + body: string | null; } interface IssuesAssignedEvent extends BasePayloadEvent { trigger: "issues_assigned"; issue_number: number; - issue_title: string; - issue_body: string | null; + title: string; + body: string | null; } interface IssuesLabeledEvent extends BasePayloadEvent { trigger: "issues_labeled"; issue_number: number; - issue_title: string; - issue_body: string | null; + title: string; + body: string | null; } interface IssueCommentCreatedEvent extends BasePayloadEvent { trigger: "issue_comment_created"; comment_id: number; - comment_body: string; + /** comment body is the primary content */ + body: string; issue_number: number; // PR-specific fields (only present when is_pr is true) is_pr?: true; branch?: string; - pr_title?: string; - pr_body?: string | null; + title?: string; } interface CheckSuiteCompletedEvent extends BasePayloadEvent { trigger: "check_suite_completed"; issue_number: number; is_pr: true; - pr_title: string; - pr_body: string | null; + title: string; + body: string | null; pull_request: any; branch: string; check_suite: { @@ -216,7 +212,8 @@ interface ImplementPlanEvent extends BasePayloadEvent { trigger: "implement_plan"; issue_number: number; plan_comment_id: number; - plan_content: string; + /** plan content is the primary content */ + body: string; } interface UnknownEvent extends BasePayloadEvent { diff --git a/modes.ts b/modes.ts index 4ac4fad..4d877fa 100644 --- a/modes.ts +++ b/modes.ts @@ -73,21 +73,21 @@ export function computeModes(): Mode[] { 2. ${dependencyInstallationStep} -3. Review the feedback provided. Understand each review comment and what changes are being requested. - - **EVENT DATA may contain review comment details**: If available, \`approved_comments\` are comments to address, \`unapproved_comments\` are for context only. The \`triggerer\` field indicates who initiated this action - prioritize their replies when deciding how to implement fixes. - - You can use ${ghPullfrogMcpName}/get_pull_request to get PR metadata if needed. +3. Fetch review comments using ${ghPullfrogMcpName}/get_review_comments with \`pull_number\` and \`review_id\` from EVENT DATA. This returns \`commentsPath\` - read that file for full comment details with diff context. If EVENT DATA contains a \`triggerer\` field (indicating who requested fixes), you can pass \`approved_by\` to filter to only comments they approved with 👍. -4. If the request requires understanding the codebase structure or conventions, gather relevant context. Read AGENTS.md if it exists. +4. Review the feedback provided. Understand each review comment and what changes are being requested. -5. Make the necessary code changes to address the feedback. Work through each review comment systematically. +5. If the request requires understanding the codebase structure or conventions, gather relevant context. Read AGENTS.md if it exists. -6. **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. Make the necessary code changes to address the feedback. Work through each review comment systematically. -7. Test your changes to ensure they work correctly. +7. **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. -8. When done, commit your changes with ${ghPullfrogMcpName}/commit_files, then push with ${ghPullfrogMcpName}/push_branch. The push will automatically go to the correct remote (including fork repos). Do not create a new branch or PR - you are updating an existing one. +8. Test your changes to ensure they work correctly. -9. ${reportProgressInstruction} +9. When done, commit your changes with ${ghPullfrogMcpName}/commit_files, then push with ${ghPullfrogMcpName}/push_branch. The push will automatically go to the correct remote (including fork repos). Do not create a new branch or PR - you are updating an existing one. + +10. ${reportProgressInstruction} **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.`, }, diff --git a/utils/instructions.ts b/utils/instructions.ts index ce9b179..a0b2812 100644 --- a/utils/instructions.ts +++ b/utils/instructions.ts @@ -1,6 +1,6 @@ import { execSync } from "node:child_process"; import { encode as toonEncode } from "@toon-format/toon"; -import { ghPullfrogMcpName } from "../external.ts"; +import { ghPullfrogMcpName, type PayloadEvent } from "../external.ts"; import type { Mode } from "../modes.ts"; import type { ResolvedPayload } from "./payload.ts"; import type { RepoData } from "./repoData.ts"; @@ -12,22 +12,30 @@ interface InstructionsContext { } function buildRuntimeContext(ctx: InstructionsContext): string { - const lines: string[] = []; - - lines.push(`working_directory: ${process.cwd()}`); - lines.push(`log_level: ${process.env.LOG_LEVEL}`); + // extract payload fields excluding prompt/repoInstructions/event + const { + "~pullfrog": _, + prompt: _p, + repoInstructions: _r, + event: _e, + ...payloadRest + } = ctx.payload; + let gitStatus: string | undefined; try { - const gitStatus = execSync("git status --short", { encoding: "utf-8", stdio: "pipe" }).trim(); - lines.push(`git_status: ${gitStatus || "(clean)"}`); + gitStatus = + execSync("git status --short", { encoding: "utf-8", stdio: "pipe" }).trim() || "(clean)"; } catch { // git not available or not in a repo } - lines.push(`repo: ${ctx.repoData.owner}/${ctx.repoData.name}`); - lines.push(`default_branch: ${ctx.repoData.repo.default_branch}`); - - const ghVars: Record = { + const data: Record = { + ...payloadRest, + repo: `${ctx.repoData.owner}/${ctx.repoData.name}`, + default_branch: ctx.repoData.repo.default_branch, + working_directory: process.cwd(), + log_level: process.env.LOG_LEVEL, + git_status: gitStatus, github_event_name: process.env.GITHUB_EVENT_NAME, github_ref: process.env.GITHUB_REF, github_sha: process.env.GITHUB_SHA?.slice(0, 7), @@ -35,13 +43,36 @@ function buildRuntimeContext(ctx: InstructionsContext): string { github_run_id: process.env.GITHUB_RUN_ID, github_workflow: process.env.GITHUB_WORKFLOW, }; - for (const [key, value] of Object.entries(ghVars)) { - if (value) { - lines.push(`${key}: ${value}`); - } + + // filter out undefined values + const filtered = Object.fromEntries(Object.entries(data).filter(([_, v]) => v !== undefined)); + + return toonEncode(filtered); +} + +function buildEventData(event: PayloadEvent): string { + const { title, body, ...rest } = event; + const sections: string[] = []; + + // render title + body as markdown + const trimmedTitle = typeof title === "string" ? title.trim() : ""; + const trimmedBody = typeof body === "string" ? body.trim() : ""; + + if (trimmedTitle) { + sections.push(`# ${trimmedTitle}`); } - return lines.join("\n"); + if (trimmedBody) { + sections.push(trimmedBody); + } + + // separator and toon-encoded remaining fields + if (Object.keys(rest).length > 0) { + if (sections.length > 0) sections.push("---"); + sections.push(toonEncode(rest)); + } + + return sections.join("\n\n"); } function getShellInstructions(bash: ResolvedPayload["bash"]): string { @@ -71,18 +102,7 @@ export interface ResolvedInstructions { } export function resolveInstructions(ctx: InstructionsContext): ResolvedInstructions { - const event = toonEncode({ - agent: ctx.payload.agent, - effort: ctx.payload.effort, - permissions: { - web: ctx.payload.web, - search: ctx.payload.search, - write: ctx.payload.write, - bash: ctx.payload.bash, - }, - event: ctx.payload.event, - }); - + const event = buildEventData(ctx.payload.event); const runtime = buildRuntimeContext(ctx); // user prompt is constructed server-side (body if @pullfrog tagged + per-trigger instructions) @@ -190,12 +210,12 @@ ${repoSection} ************* EVENT DATA ************* -The following is structured data about the context of this run (agent, effort level, permissions, and the GitHub event that triggered it). Use this context to understand the full situation. - ${event} ************* RUNTIME CONTEXT ************* +The following contains the agent configuration (agent, effort, permissions) and runtime environment details. + ${runtime}`; return { full, system, user, repo, event, runtime };