Merge pull request #6 from pullfrog/fix-setup-git-auth-order

fix: move origin URL auth setup before git fetch in setupGit
This commit is contained in:
Colin McDonnell
2025-12-16 18:00:54 -08:00
committed by GitHub
7 changed files with 8175 additions and 7254 deletions
+4 -1
View File
@@ -204,7 +204,10 @@ If you cannot complete a task due to missing information, ambiguity, or an unrec
************* USER PROMPT ************* ************* USER PROMPT *************
${payload.prompt} ${payload.prompt
.split("\n")
.map((line) => `> ${line}`)
.join("\n")}
${ ${
encodedEvent encodedEvent
+8111 -7237
View File
File diff suppressed because one or more lines are too long
+17
View File
@@ -186,13 +186,30 @@ interface WorkflowDispatchEvent extends BasePayloadEvent {
trigger: "workflow_dispatch"; trigger: "workflow_dispatch";
} }
/** simplified review comment data for payload */
export interface ReviewCommentData {
id: number;
body: string;
path: string;
line: number | null;
user: string | null;
html_url: string;
in_reply_to_id: number | null;
}
interface FixReviewEvent extends BasePayloadEvent { interface FixReviewEvent extends BasePayloadEvent {
trigger: "fix_review"; trigger: "fix_review";
issue_number: number; issue_number: number;
is_pr: true; is_pr: true;
review_id: number; review_id: number;
/** username of the person who triggered this action */
triggerer: string;
/** "all" to fix all comments, or specific comment IDs to fix */ /** "all" to fix all comments, or specific comment IDs to fix */
comment_ids: number[] | "all"; comment_ids: number[] | "all";
/** comments the triggerer approved (via thumbs up) - these should be addressed */
approved_comments: ReviewCommentData[];
/** other comments in the review - for context only, do not address unless asked */
unapproved_comments: ReviewCommentData[];
} }
interface UnknownEvent extends BasePayloadEvent { interface UnknownEvent extends BasePayloadEvent {
+32 -9
View File
@@ -15,26 +15,49 @@ import { execute, tool } from "./shared.ts";
*/ */
export const LEAPING_INTO_ACTION_PREFIX = "Leaping into action"; export const LEAPING_INTO_ACTION_PREFIX = "Leaping into action";
function buildCommentFooter(payload: Payload): string { async function buildCommentFooter(payload: Payload, octokit?: Octokit): Promise<string> {
const repoContext = parseRepoContext(); const repoContext = parseRepoContext();
const runId = process.env.GITHUB_RUN_ID; const runId = process.env.GITHUB_RUN_ID;
const agentName = payload.agent; const agentName = payload.agent;
const agentInfo = agentName ? agentsManifest[agentName] : null; const agentInfo = agentName ? agentsManifest[agentName] : null;
let workflowRunHtmlUrl: string | undefined;
if (runId && octokit) {
try {
// fetch jobs to get the job URL for deep linking
const { data: jobs } = await octokit.rest.actions.listJobsForWorkflowRun({
owner: repoContext.owner,
repo: repoContext.name,
run_id: parseInt(runId, 10),
});
// use the first job's URL if available
workflowRunHtmlUrl = jobs.jobs[0]?.html_url ?? undefined;
} catch {
// fall back to building URL from runId if jobs can't be fetched
}
}
return buildPullfrogFooter({ return buildPullfrogFooter({
triggeredBy: true, triggeredBy: true,
agent: { agent: {
displayName: agentInfo?.displayName || "Unknown agent", displayName: agentInfo?.displayName || "Unknown agent",
url: agentInfo?.url || "https://pullfrog.com", url: agentInfo?.url || "https://pullfrog.com",
}, },
workflowRun: runId ? { owner: repoContext.owner, repo: repoContext.name, runId } : undefined, workflowRun: runId
? {
owner: repoContext.owner,
repo: repoContext.name,
runId,
...(workflowRunHtmlUrl ? { htmlUrl: workflowRunHtmlUrl } : {}),
}
: undefined,
}); });
} }
function addFooter(body: string, payload: Payload): string { async function addFooter(body: string, payload: Payload, octokit?: Octokit): Promise<string> {
const bodyWithoutFooter = stripExistingFooter(body); const bodyWithoutFooter = stripExistingFooter(body);
const footer = buildCommentFooter(payload); const footer = await buildCommentFooter(payload, octokit);
return `${bodyWithoutFooter}${footer}`; return `${bodyWithoutFooter}${footer}`;
} }
@@ -50,7 +73,7 @@ export function CreateCommentTool(ctx: Context) {
"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.", "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, parameters: Comment,
execute: execute(ctx, async ({ issueNumber, body }) => { execute: execute(ctx, async ({ issueNumber, body }) => {
const bodyWithFooter = addFooter(body, ctx.payload); const bodyWithFooter = await addFooter(body, ctx.payload, ctx.octokit);
const result = await ctx.octokit.rest.issues.createComment({ const result = await ctx.octokit.rest.issues.createComment({
owner: ctx.owner, owner: ctx.owner,
@@ -80,7 +103,7 @@ export function EditCommentTool(ctx: Context) {
description: "Edit a GitHub issue comment by its ID", description: "Edit a GitHub issue comment by its ID",
parameters: EditComment, parameters: EditComment,
execute: execute(ctx, async ({ commentId, body }) => { execute: execute(ctx, async ({ commentId, body }) => {
const bodyWithFooter = addFooter(body, ctx.payload); const bodyWithFooter = await addFooter(body, ctx.payload, ctx.octokit);
const result = await ctx.octokit.rest.issues.updateComment({ const result = await ctx.octokit.rest.issues.updateComment({
owner: ctx.owner, owner: ctx.owner,
@@ -158,7 +181,7 @@ export async function reportProgress(
} }
| undefined | undefined
> { > {
const bodyWithFooter = addFooter(body, ctx.payload); const bodyWithFooter = await addFooter(body, ctx.payload, ctx.octokit);
const existingCommentId = getProgressCommentId(); const existingCommentId = getProgressCommentId();
// if we already have a progress comment, update it // if we already have a progress comment, update it
@@ -337,7 +360,7 @@ export async function ensureProgressCommentUpdated(payload?: Payload): Promise<v
The workflow encountered an error before any progress could be reported. Please check the ${workflowRunLink} for details.`; The workflow encountered an error before any progress could be reported. Please check the ${workflowRunLink} for details.`;
// add footer if we have payload, otherwise use plain message // add footer if we have payload, otherwise use plain message
const body = payload ? addFooter(errorMessage, payload) : errorMessage; const body = payload ? await addFooter(errorMessage, payload, octokit) : errorMessage;
await octokit.rest.issues.updateComment({ await octokit.rest.issues.updateComment({
owner: repoContext.owner, owner: repoContext.owner,
@@ -362,7 +385,7 @@ export function ReplyToReviewCommentTool(ctx: Context) {
"Reply to a PR review comment thread. Call this for EACH comment you address. Keep replies extremely brief (1 sentence max).", "Reply to a PR review comment thread. Call this for EACH comment you address. Keep replies extremely brief (1 sentence max).",
parameters: ReplyToReviewComment, parameters: ReplyToReviewComment,
execute: execute(ctx, async ({ pull_number, comment_id, body }) => { execute: execute(ctx, async ({ pull_number, comment_id, body }) => {
const bodyWithFooter = addFooter(body, ctx.payload); const bodyWithFooter = await addFooter(body, ctx.payload, ctx.octokit);
const result = await ctx.octokit.rest.pulls.createReplyForReviewComment({ const result = await ctx.octokit.rest.pulls.createReplyForReviewComment({
owner: ctx.owner, owner: ctx.owner,
+1
View File
@@ -67,6 +67,7 @@ export function getModes({
1. Get PR info with ${ghPullfrogMcpName}/get_pull_request (this automatically fetches and checks out the PR branch) 1. Get PR info with ${ghPullfrogMcpName}/get_pull_request (this automatically fetches and checks out the PR branch)
2. Review the feedback provided. Understand each review comment and what changes are being requested. 2. 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.
3. If the request requires understanding the codebase structure, dependencies, or conventions, gather relevant context. Read AGENTS.md if it exists. 3. If the request requires understanding the codebase structure, dependencies, or conventions, gather relevant context. Read AGENTS.md if it exists.
+4 -3
View File
@@ -11,6 +11,8 @@ export interface WorkflowRunInfo {
owner: string; owner: string;
repo: string; repo: string;
runId: string; runId: string;
/** optional job URL - if provided, will be used instead of building from runId */
htmlUrl?: string;
} }
export interface BuildPullfrogFooterParams { export interface BuildPullfrogFooterParams {
@@ -40,9 +42,8 @@ export function buildPullfrogFooter(params: BuildPullfrogFooterParams): string {
} }
if (params.workflowRun) { if (params.workflowRun) {
parts.push( const url = params.workflowRun.htmlUrl ?? `https://github.com/${params.workflowRun.owner}/${params.workflowRun.repo}/actions/runs/${params.workflowRun.runId}`;
`[View workflow run](https://github.com/${params.workflowRun.owner}/${params.workflowRun.repo}/actions/runs/${params.workflowRun.runId})` parts.push(`[View workflow run](${url})`);
);
} }
if (params.customParts) { if (params.customParts) {
+6 -4
View File
@@ -87,11 +87,13 @@ export async function setupGit(ctx: Context): Promise<SetupGitResult> {
log.debug("No existing authentication headers to remove"); log.debug("No existing authentication headers to remove");
} }
// non-PR events: set up origin with token, stay on default branch // set up origin with token (needed for fetch operations)
const originUrl = `https://x-access-token:${ctx.githubInstallationToken}@github.com/${ctx.owner}/${ctx.name}.git`;
$("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir });
log.info("✓ Updated origin URL with authentication token");
// non-PR events: stay on default branch
if (ctx.payload.event.is_pr !== true || !ctx.payload.event.issue_number) { if (ctx.payload.event.is_pr !== true || !ctx.payload.event.issue_number) {
const originUrl = `https://x-access-token:${ctx.githubInstallationToken}@github.com/${ctx.owner}/${ctx.name}.git`;
$("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir });
log.info("✓ Updated origin URL with authentication token");
return { pushRemote: "origin" }; return { pushRemote: "origin" };
} }