Implement Plan button
This commit is contained in:
@@ -98415,7 +98415,11 @@ var addTools = (ctx, server, tools) => {
|
||||
|
||||
// mcp/comment.ts
|
||||
var LEAPING_INTO_ACTION_PREFIX = "Leaping into action";
|
||||
async function buildCommentFooter(payload, octokit) {
|
||||
async function buildCommentFooter({
|
||||
payload,
|
||||
octokit,
|
||||
customParts
|
||||
}) {
|
||||
const repoContext = parseRepoContext();
|
||||
const runId = process.env.GITHUB_RUN_ID;
|
||||
const agentName = payload.agent;
|
||||
@@ -98432,7 +98436,7 @@ async function buildCommentFooter(payload, octokit) {
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
return buildPullfrogFooter({
|
||||
const footerParams = {
|
||||
triggeredBy: true,
|
||||
agent: {
|
||||
displayName: agentInfo?.displayName || "Unknown agent",
|
||||
@@ -98444,11 +98448,19 @@ async function buildCommentFooter(payload, octokit) {
|
||||
runId,
|
||||
...workflowRunHtmlUrl ? { htmlUrl: workflowRunHtmlUrl } : {}
|
||||
} : void 0
|
||||
});
|
||||
};
|
||||
if (customParts && customParts.length > 0) {
|
||||
return buildPullfrogFooter({ ...footerParams, customParts });
|
||||
}
|
||||
return buildPullfrogFooter(footerParams);
|
||||
}
|
||||
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})`;
|
||||
}
|
||||
async function addFooter(body, payload, octokit) {
|
||||
const bodyWithoutFooter = stripExistingFooter(body);
|
||||
const footer = await buildCommentFooter(payload, octokit);
|
||||
const footer = await buildCommentFooter({ payload, octokit });
|
||||
return `${bodyWithoutFooter}${footer}`;
|
||||
}
|
||||
var Comment = type({
|
||||
@@ -98532,9 +98544,18 @@ var ReportProgress = type({
|
||||
body: type.string.describe("the progress update content to share")
|
||||
});
|
||||
async function reportProgress(ctx, { body }) {
|
||||
const bodyWithFooter = await addFooter(body, ctx.payload, ctx.octokit);
|
||||
const existingCommentId = getProgressCommentId();
|
||||
const issueNumber = ctx.toolState.prNumber ?? ctx.toolState.issueNumber ?? ctx.payload.event.issue_number;
|
||||
const isPlanMode = ctx.toolState.selectedMode === "Plan";
|
||||
if (existingCommentId) {
|
||||
const customParts = isPlanMode && issueNumber !== void 0 ? [buildImplementPlanLink(ctx.owner, ctx.name, issueNumber, existingCommentId)] : void 0;
|
||||
const bodyWithoutFooter = stripExistingFooter(body);
|
||||
const footer = await buildCommentFooter({
|
||||
payload: ctx.payload,
|
||||
octokit: ctx.octokit,
|
||||
customParts
|
||||
});
|
||||
const bodyWithFooter = `${bodyWithoutFooter}${footer}`;
|
||||
const result2 = await ctx.octokit.rest.issues.updateComment({
|
||||
owner: ctx.owner,
|
||||
repo: ctx.name,
|
||||
@@ -98549,18 +98570,40 @@ async function reportProgress(ctx, { body }) {
|
||||
action: "updated"
|
||||
};
|
||||
}
|
||||
const issueNumber = ctx.toolState.prNumber ?? ctx.toolState.issueNumber ?? ctx.payload.event.issue_number;
|
||||
if (issueNumber === void 0) {
|
||||
return void 0;
|
||||
}
|
||||
const initialBody = await addFooter(body, ctx.payload, ctx.octokit);
|
||||
const result = await ctx.octokit.rest.issues.createComment({
|
||||
owner: ctx.owner,
|
||||
repo: ctx.name,
|
||||
issue_number: issueNumber,
|
||||
body: bodyWithFooter
|
||||
body: initialBody
|
||||
});
|
||||
setProgressCommentId(result.data.id);
|
||||
progressCommentWasUpdated = true;
|
||||
if (isPlanMode) {
|
||||
const customParts = [buildImplementPlanLink(ctx.owner, ctx.name, issueNumber, result.data.id)];
|
||||
const bodyWithoutFooter = stripExistingFooter(body);
|
||||
const footer = await buildCommentFooter({
|
||||
payload: ctx.payload,
|
||||
octokit: ctx.octokit,
|
||||
customParts
|
||||
});
|
||||
const bodyWithPlanLink = `${bodyWithoutFooter}${footer}`;
|
||||
const updateResult = await ctx.octokit.rest.issues.updateComment({
|
||||
owner: ctx.owner,
|
||||
repo: ctx.name,
|
||||
comment_id: result.data.id,
|
||||
body: bodyWithPlanLink
|
||||
});
|
||||
return {
|
||||
commentId: updateResult.data.id,
|
||||
url: updateResult.data.html_url,
|
||||
body: updateResult.data.body || "",
|
||||
action: "created"
|
||||
};
|
||||
}
|
||||
return {
|
||||
commentId: result.data.id,
|
||||
url: result.data.html_url,
|
||||
@@ -124718,6 +124761,7 @@ function SelectModeTool(ctx) {
|
||||
availableModes: ctx.modes.map((m) => ({ name: m.name, description: m.description }))
|
||||
};
|
||||
}
|
||||
ctx.toolState.selectedMode = selectedMode.name;
|
||||
return {
|
||||
modeName: selectedMode.name,
|
||||
description: selectedMode.description,
|
||||
|
||||
@@ -212,6 +212,13 @@ interface FixReviewEvent extends BasePayloadEvent {
|
||||
unapproved_comments: ReviewCommentData[];
|
||||
}
|
||||
|
||||
interface ImplementPlanEvent extends BasePayloadEvent {
|
||||
trigger: "implement_plan";
|
||||
issue_number: number;
|
||||
plan_comment_id: number;
|
||||
plan_content: string;
|
||||
}
|
||||
|
||||
interface UnknownEvent extends BasePayloadEvent {
|
||||
trigger: "unknown";
|
||||
}
|
||||
@@ -231,6 +238,7 @@ export type PayloadEvent =
|
||||
| CheckSuiteCompletedEvent
|
||||
| WorkflowDispatchEvent
|
||||
| FixReviewEvent
|
||||
| ImplementPlanEvent
|
||||
| UnknownEvent;
|
||||
|
||||
export interface DispatchOptions {
|
||||
|
||||
@@ -337,6 +337,7 @@ export interface DependencyInstallationState {
|
||||
export interface ToolState {
|
||||
prNumber?: number;
|
||||
issueNumber?: number;
|
||||
selectedMode?: string;
|
||||
review?: {
|
||||
id: number; // REST API database ID (for fix URLs)
|
||||
nodeId: string; // GraphQL node ID (for mutations)
|
||||
|
||||
+75
-8
@@ -15,7 +15,17 @@ import { execute, tool } from "./shared.ts";
|
||||
*/
|
||||
export const LEAPING_INTO_ACTION_PREFIX = "Leaping into action";
|
||||
|
||||
async function buildCommentFooter(payload: Payload, octokit?: Octokit): Promise<string> {
|
||||
interface BuildCommentFooterParams {
|
||||
payload: Payload;
|
||||
octokit?: Octokit | undefined;
|
||||
customParts?: string[] | undefined;
|
||||
}
|
||||
|
||||
async function buildCommentFooter({
|
||||
payload,
|
||||
octokit,
|
||||
customParts,
|
||||
}: BuildCommentFooterParams): Promise<string> {
|
||||
const repoContext = parseRepoContext();
|
||||
const runId = process.env.GITHUB_RUN_ID;
|
||||
|
||||
@@ -38,7 +48,7 @@ async function buildCommentFooter(payload: Payload, octokit?: Octokit): Promise<
|
||||
}
|
||||
}
|
||||
|
||||
return buildPullfrogFooter({
|
||||
const footerParams = {
|
||||
triggeredBy: true,
|
||||
agent: {
|
||||
displayName: agentInfo?.displayName || "Unknown agent",
|
||||
@@ -52,12 +62,27 @@ async function buildCommentFooter(payload: Payload, octokit?: Octokit): Promise<
|
||||
...(workflowRunHtmlUrl ? { htmlUrl: workflowRunHtmlUrl } : {}),
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
if (customParts && customParts.length > 0) {
|
||||
return buildPullfrogFooter({ ...footerParams, customParts });
|
||||
}
|
||||
return buildPullfrogFooter(footerParams);
|
||||
}
|
||||
|
||||
function buildImplementPlanLink(
|
||||
owner: string,
|
||||
repo: string,
|
||||
issueNumber: number,
|
||||
commentId: number
|
||||
): string {
|
||||
const apiUrl = process.env.API_URL || "https://pullfrog.com";
|
||||
return `[Implement plan ➔](${apiUrl}/trigger/${owner}/${repo}/${issueNumber}?action=implement&comment_id=${commentId})`;
|
||||
}
|
||||
|
||||
async function addFooter(body: string, payload: Payload, octokit?: Octokit): Promise<string> {
|
||||
const bodyWithoutFooter = stripExistingFooter(body);
|
||||
const footer = await buildCommentFooter(payload, octokit);
|
||||
const footer = await buildCommentFooter({ payload, octokit });
|
||||
return `${bodyWithoutFooter}${footer}`;
|
||||
}
|
||||
|
||||
@@ -181,11 +206,26 @@ export async function reportProgress(
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
const bodyWithFooter = await addFooter(body, ctx.payload, ctx.octokit);
|
||||
const existingCommentId = getProgressCommentId();
|
||||
const issueNumber =
|
||||
ctx.toolState.prNumber ?? ctx.toolState.issueNumber ?? ctx.payload.event.issue_number;
|
||||
const isPlanMode = ctx.toolState.selectedMode === "Plan";
|
||||
|
||||
// if we already have a progress comment, update it
|
||||
if (existingCommentId) {
|
||||
const customParts =
|
||||
isPlanMode && issueNumber !== undefined
|
||||
? [buildImplementPlanLink(ctx.owner, ctx.name, issueNumber, existingCommentId)]
|
||||
: undefined;
|
||||
|
||||
const bodyWithoutFooter = stripExistingFooter(body);
|
||||
const footer = await buildCommentFooter({
|
||||
payload: ctx.payload,
|
||||
octokit: ctx.octokit,
|
||||
customParts,
|
||||
});
|
||||
const bodyWithFooter = `${bodyWithoutFooter}${footer}`;
|
||||
|
||||
const result = await ctx.octokit.rest.issues.updateComment({
|
||||
owner: ctx.owner,
|
||||
repo: ctx.name,
|
||||
@@ -205,24 +245,51 @@ export async function reportProgress(
|
||||
|
||||
// no existing comment - create one
|
||||
// use fallback chain: dynamically set context > event payload
|
||||
const issueNumber =
|
||||
ctx.toolState.prNumber ?? ctx.toolState.issueNumber ?? ctx.payload.event.issue_number;
|
||||
if (issueNumber === undefined) {
|
||||
// cannot create comment without issue_number (e.g., workflow_dispatch events)
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// for new comments, we need to create first, then update with Plan link if in Plan mode
|
||||
const initialBody = await addFooter(body, ctx.payload, ctx.octokit);
|
||||
|
||||
const result = await ctx.octokit.rest.issues.createComment({
|
||||
owner: ctx.owner,
|
||||
repo: ctx.name,
|
||||
issue_number: issueNumber,
|
||||
body: bodyWithFooter,
|
||||
body: initialBody,
|
||||
});
|
||||
|
||||
// store the comment ID for future updates
|
||||
setProgressCommentId(result.data.id);
|
||||
progressCommentWasUpdated = true;
|
||||
|
||||
// if Plan mode, update the comment to add the "Implement plan" link
|
||||
if (isPlanMode) {
|
||||
const customParts = [buildImplementPlanLink(ctx.owner, ctx.name, issueNumber, result.data.id)];
|
||||
const bodyWithoutFooter = stripExistingFooter(body);
|
||||
const footer = await buildCommentFooter({
|
||||
payload: ctx.payload,
|
||||
octokit: ctx.octokit,
|
||||
customParts,
|
||||
});
|
||||
const bodyWithPlanLink = `${bodyWithoutFooter}${footer}`;
|
||||
|
||||
const updateResult = await ctx.octokit.rest.issues.updateComment({
|
||||
owner: ctx.owner,
|
||||
repo: ctx.name,
|
||||
comment_id: result.data.id,
|
||||
body: bodyWithPlanLink,
|
||||
});
|
||||
|
||||
return {
|
||||
commentId: updateResult.data.id,
|
||||
url: updateResult.data.html_url,
|
||||
body: updateResult.data.body || "",
|
||||
action: "created",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
commentId: result.data.id,
|
||||
url: result.data.html_url,
|
||||
|
||||
@@ -25,6 +25,9 @@ export function SelectModeTool(ctx: ToolContext) {
|
||||
};
|
||||
}
|
||||
|
||||
// store selected mode in toolState for use by other tools (e.g., report_progress)
|
||||
ctx.toolState.selectedMode = selectedMode.name;
|
||||
|
||||
return {
|
||||
modeName: selectedMode.name,
|
||||
description: selectedMode.description,
|
||||
|
||||
Reference in New Issue
Block a user