overhaul git setup
This commit is contained in:
@@ -80489,6 +80489,9 @@ function formatIndentedField(label, content) {
|
||||
return formatted;
|
||||
}
|
||||
|
||||
// agents/instructions.ts
|
||||
import { execSync } from "node:child_process";
|
||||
|
||||
// ../node_modules/.pnpm/@toon-format+toon@1.0.0/node_modules/@toon-format/toon/dist/index.js
|
||||
var LIST_ITEM_MARKER = "-";
|
||||
var LIST_ITEM_PREFIX = "- ";
|
||||
@@ -89416,10 +89419,11 @@ function getModes({
|
||||
name: "Address Reviews",
|
||||
description: "Address PR review feedback; respond to reviewer comments; make requested changes to an existing PR",
|
||||
prompt: `Follow these steps:
|
||||
1. Get PR info with ${ghPullfrogMcpName}/get_pull_request (this automatically fetches and checks out the PR branch)
|
||||
1. Checkout the PR using ${ghPullfrogMcpName}/checkout_pr with the PR number. This fetches the PR branch and configures push settings (including for fork PRs).
|
||||
|
||||
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.
|
||||
- You can use ${ghPullfrogMcpName}/get_pull_request to get PR metadata if needed.
|
||||
|
||||
3. If the request requires understanding the codebase structure, dependencies, or conventions, gather relevant context. Read AGENTS.md if it exists.
|
||||
|
||||
@@ -89429,7 +89433,7 @@ function getModes({
|
||||
|
||||
6. Test your changes to ensure they work correctly.
|
||||
|
||||
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.
|
||||
7. 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.
|
||||
${disableProgressComment ? "" : `
|
||||
8. ${reportProgressInstruction}
|
||||
|
||||
@@ -89439,9 +89443,9 @@ ${disableProgressComment ? "" : `
|
||||
name: "Review",
|
||||
description: "Review code, PRs, or implementations; provide feedback or suggestions; identify issues; or check code quality, style, and correctness",
|
||||
prompt: `Follow these steps:
|
||||
1. Get PR info with ${ghPullfrogMcpName}/get_pull_request (this automatically prepares the repository by fetching and checking out the PR branch)
|
||||
1. Checkout the PR using ${ghPullfrogMcpName}/checkout_pr with the PR number. This fetches the PR branch and base branch, preparing the repo for review.
|
||||
|
||||
2. **IMPORTANT**: After calling ${ghPullfrogMcpName}/get_pull_request, the PR branch is already checked out locally. View diff using: \`git diff origin/<base>...HEAD\` (replace <base> with 'base' from PR info). Do NOT use \`origin/<head>\` - the branch is checked out locally, not as a remote tracking branch.
|
||||
2. **IMPORTANT**: After calling checkout_pr, the PR branch is checked out locally. View diff using: \`git diff origin/<base>..HEAD\` (replace <base> with 'base' from checkout_pr result, e.g., \`git diff origin/main..HEAD\`). Use two dots (..) not three dots (...) for reliable diffs. Do NOT use \`origin/<head>\` - the branch is checked out locally, not as a remote tracking branch. This works for both same-repo and fork PRs.
|
||||
|
||||
3. Read files from the checked-out PR branch to understand the implementation. Always use **relative paths** from repo root (e.g., \`src/index.ts\`), never absolute paths.
|
||||
|
||||
@@ -89449,7 +89453,8 @@ ${disableProgressComment ? "" : `
|
||||
|
||||
**GENERAL GUIDANCE**
|
||||
|
||||
- *CRITICAL* \u2014\xA0Use **relative paths** from repo root (e.g., \`packages/core/src/utils.ts\`)
|
||||
- Do not leave any comments that are not potentially actionable. Do not leave complimentary comments just to be nice.
|
||||
- *CRITICAL* \u2014 Use **relative paths** from repo root (e.g., \`packages/core/src/utils.ts\`)
|
||||
- For line numbers, use the NEW file line number from the diff (shown after \`+\` in hunk headers like \`@@ -10,5 +12,8 @@\` means new file starts at line 12)
|
||||
- Only comment on lines that appear in the diff. GitHub will reject comments on unchanged lines.
|
||||
- **CRITICAL: Prioritize per-line feedback over summary text.**
|
||||
@@ -89459,7 +89464,6 @@ ${disableProgressComment ? "" : `
|
||||
- 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
|
||||
- Do not nitpick unless instructed explicity to do so by the user's additional instructions. This includes: requesting documentation/docstrings/JSDoc.
|
||||
- Do not leave any comments that are not potentially actionable.
|
||||
- The review should be thoughtful. When evaluating complex changes, consider the following conceptual approach:
|
||||
- 1) conceptualize the changes made. make sure you understand it.
|
||||
- 2) evaluate conceptual approach. leave feedback as needed.
|
||||
@@ -89554,20 +89558,47 @@ function formatPrepResults(results) {
|
||||
if (lines.length === 0) {
|
||||
return "";
|
||||
}
|
||||
return `************* ENVIRONMENT SETUP *************
|
||||
|
||||
${lines.join("\n")}
|
||||
|
||||
`;
|
||||
return lines.join("\n");
|
||||
}
|
||||
var addInstructions = ({ payload, prepResults }) => {
|
||||
function buildRuntimeContext({ repo, prepResults }) {
|
||||
const lines = [];
|
||||
lines.push(`working_directory: ${process.cwd()}`);
|
||||
try {
|
||||
const gitStatus = execSync("git status --short", { encoding: "utf-8", stdio: "pipe" }).trim();
|
||||
lines.push(`git_status: ${gitStatus || "(clean)"}`);
|
||||
} catch {
|
||||
}
|
||||
lines.push(`repo: ${repo.owner}/${repo.name}`);
|
||||
lines.push(`default_branch: ${repo.defaultBranch}`);
|
||||
const ghVars = {
|
||||
github_event_name: process.env.GITHUB_EVENT_NAME,
|
||||
github_ref: process.env.GITHUB_REF,
|
||||
github_sha: process.env.GITHUB_SHA?.slice(0, 7),
|
||||
github_actor: process.env.GITHUB_ACTOR,
|
||||
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 envSetup = formatPrepResults(prepResults);
|
||||
if (envSetup) {
|
||||
lines.push("");
|
||||
lines.push("environment_setup:");
|
||||
lines.push(envSetup);
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
var addInstructions = ({ payload, prepResults, repo }) => {
|
||||
let encodedEvent = "";
|
||||
const eventKeys = Object.keys(payload.event);
|
||||
if (eventKeys.length === 1 && eventKeys[0] === "trigger") {
|
||||
} else {
|
||||
encodedEvent = encode(payload.event);
|
||||
}
|
||||
const envSetup = formatPrepResults(prepResults);
|
||||
const runtimeContext = buildRuntimeContext({ repo, prepResults });
|
||||
const dependenciesPreinstalled = prepResults.every((r) => r.dependenciesInstalled) || void 0;
|
||||
return `
|
||||
***********************************************
|
||||
@@ -89643,14 +89674,21 @@ Tool names may be formatted as \`(server name)/(tool name)\`, for example: \`${g
|
||||
**Git operations**: All git operations must use ${ghPullfrogMcpName} MCP tools to ensure proper authentication and commit attribution. Do NOT use git commands directly (e.g., \`git commit\`, \`git push\`, \`git checkout\`, \`git branch\`) - these will use incorrect credentials and attribute commits to the wrong author.
|
||||
|
||||
**Available git MCP tools**:
|
||||
- \`${ghPullfrogMcpName}/checkout_pr\` - Checkout an existing PR branch locally (handles fork PRs automatically)
|
||||
- \`${ghPullfrogMcpName}/create_branch\` - Create a new branch from a base branch
|
||||
- \`${ghPullfrogMcpName}/commit_files\` - Stage and commit files with proper authentication
|
||||
- \`${ghPullfrogMcpName}/push_branch\` - Push a branch to the remote repository
|
||||
- \`${ghPullfrogMcpName}/push_branch\` - Push a branch to the remote (automatically uses correct remote for fork PRs)
|
||||
- \`${ghPullfrogMcpName}/create_pull_request\` - Create a PR from the current branch
|
||||
|
||||
**Workflow for making code changes**:
|
||||
1. Use file operations to create/modify files
|
||||
2. Use \`${ghPullfrogMcpName}/create_branch\` to create a new branch
|
||||
**Workflow for working on an existing PR**:
|
||||
1. Use \`${ghPullfrogMcpName}/checkout_pr\` to checkout the PR branch
|
||||
2. Make your changes using file operations
|
||||
3. Use \`${ghPullfrogMcpName}/commit_files\` to commit your changes
|
||||
4. Use \`${ghPullfrogMcpName}/push_branch\` to push (automatically pushes to fork for fork PRs)
|
||||
|
||||
**Workflow for creating new changes**:
|
||||
1. Use \`${ghPullfrogMcpName}/create_branch\` to create a new branch
|
||||
2. Make your changes using file operations
|
||||
3. Use \`${ghPullfrogMcpName}/commit_files\` to commit your changes
|
||||
4. Use \`${ghPullfrogMcpName}/push_branch\` to push the branch
|
||||
5. Use \`${ghPullfrogMcpName}/create_pull_request\` to create a PR
|
||||
@@ -89694,9 +89732,7 @@ ${encodedEvent}` : ""}
|
||||
|
||||
************* RUNTIME CONTEXT *************
|
||||
|
||||
working_directory: ${process.cwd()}
|
||||
|
||||
${envSetup}`;
|
||||
${runtimeContext}`;
|
||||
};
|
||||
|
||||
// agents/shared.ts
|
||||
@@ -90117,9 +90153,9 @@ var claude = agent({
|
||||
executablePath: "cli.js"
|
||||
});
|
||||
},
|
||||
run: async ({ payload, mcpServers, apiKey, cliPath, prepResults }) => {
|
||||
run: async ({ payload, mcpServers, apiKey, cliPath, prepResults, repo }) => {
|
||||
delete process.env.ANTHROPIC_API_KEY;
|
||||
const prompt = addInstructions({ payload, prepResults });
|
||||
const prompt = addInstructions({ payload, prepResults, repo });
|
||||
console.log(prompt);
|
||||
const sandboxOptions = payload.sandbox ? {
|
||||
permissionMode: "default",
|
||||
@@ -90575,7 +90611,7 @@ var codex = agent({
|
||||
executablePath: "bin/codex.js"
|
||||
});
|
||||
},
|
||||
run: async ({ payload, mcpServers, apiKey, cliPath, prepResults }) => {
|
||||
run: async ({ payload, mcpServers, apiKey, cliPath, prepResults, repo }) => {
|
||||
const tempHome = process.env.PULLFROG_TEMP_DIR;
|
||||
const configDir = join5(tempHome, ".config", "codex");
|
||||
mkdirSync2(configDir, { recursive: true });
|
||||
@@ -90605,7 +90641,7 @@ var codex = agent({
|
||||
}
|
||||
);
|
||||
try {
|
||||
const streamedTurn = await thread.runStreamed(addInstructions({ payload, prepResults }));
|
||||
const streamedTurn = await thread.runStreamed(addInstructions({ payload, prepResults, repo }));
|
||||
let finalOutput2 = "";
|
||||
for await (const event of streamedTurn.events) {
|
||||
const handler2 = messageHandlers2[event.type];
|
||||
@@ -90747,7 +90783,7 @@ var cursor = agent({
|
||||
executableName: "cursor-agent"
|
||||
});
|
||||
},
|
||||
run: async ({ payload, apiKey, cliPath, mcpServers, prepResults }) => {
|
||||
run: async ({ payload, apiKey, cliPath, mcpServers, prepResults, repo }) => {
|
||||
configureCursorMcpServers({ mcpServers, cliPath });
|
||||
configureCursorSandbox({ sandbox: payload.sandbox ?? false });
|
||||
const loggedModelCallIds = /* @__PURE__ */ new Set();
|
||||
@@ -90800,7 +90836,7 @@ var cursor = agent({
|
||||
}
|
||||
};
|
||||
try {
|
||||
const fullPrompt = addInstructions({ payload, prepResults });
|
||||
const fullPrompt = addInstructions({ payload, prepResults, repo });
|
||||
const cursorArgs = payload.sandbox ? [
|
||||
"--print",
|
||||
fullPrompt,
|
||||
@@ -91104,12 +91140,12 @@ var gemini = agent({
|
||||
...githubInstallationToken2 && { githubInstallationToken: githubInstallationToken2 }
|
||||
});
|
||||
},
|
||||
run: async ({ payload, apiKey, mcpServers, cliPath, prepResults }) => {
|
||||
run: async ({ payload, apiKey, mcpServers, cliPath, prepResults, repo }) => {
|
||||
configureGeminiMcpServers({ mcpServers, cliPath });
|
||||
if (!apiKey) {
|
||||
throw new Error("google_api_key or gemini_api_key is required for gemini agent");
|
||||
}
|
||||
const sessionPrompt = addInstructions({ payload, prepResults });
|
||||
const sessionPrompt = addInstructions({ payload, prepResults, repo });
|
||||
log.info(`Starting Gemini CLI with prompt: ${payload.prompt.substring(0, 100)}...`);
|
||||
const args3 = payload.sandbox ? [
|
||||
"--allowed-tools",
|
||||
@@ -91365,13 +91401,13 @@ var opencode = agent({
|
||||
installDependencies: true
|
||||
});
|
||||
},
|
||||
run: async ({ payload, apiKey: _apiKey, apiKeys, mcpServers, cliPath, prepResults }) => {
|
||||
run: async ({ payload, apiKey: _apiKey, apiKeys, mcpServers, cliPath, prepResults, repo }) => {
|
||||
const tempHome = process.env.PULLFROG_TEMP_DIR;
|
||||
const configDir = join7(tempHome, ".config", "opencode");
|
||||
mkdirSync4(configDir, { recursive: true });
|
||||
configureOpenCodeMcpServers({ mcpServers });
|
||||
configureOpenCodeSandbox({ sandbox: payload.sandbox ?? false });
|
||||
const prompt = addInstructions({ payload, prepResults });
|
||||
const prompt = addInstructions({ payload, prepResults, repo });
|
||||
const args3 = ["run", "--format", "json", prompt];
|
||||
if (payload.sandbox) {
|
||||
log.info("\u{1F512} sandbox mode enabled: restricting to read-only operations");
|
||||
@@ -95064,7 +95100,8 @@ function buildPullfrogFooter(params) {
|
||||
parts.push(`Using [${params.agent.displayName}](${params.agent.url})`);
|
||||
}
|
||||
if (params.workflowRun) {
|
||||
const url2 = params.workflowRun.htmlUrl ?? `https://github.com/${params.workflowRun.owner}/${params.workflowRun.repo}/actions/runs/${params.workflowRun.runId}`;
|
||||
const baseUrl = `https://github.com/${params.workflowRun.owner}/${params.workflowRun.repo}/actions/runs/${params.workflowRun.runId}`;
|
||||
const url2 = params.workflowRun.jobId ? `${baseUrl}/job/${params.workflowRun.jobId}` : baseUrl;
|
||||
parts.push(`[View workflow run](${url2})`);
|
||||
}
|
||||
if (params.customParts) {
|
||||
@@ -123934,6 +123971,70 @@ function $(cmd, args3, options) {
|
||||
return stdout.trim();
|
||||
}
|
||||
|
||||
// mcp/checkout.ts
|
||||
var CheckoutPr = type({
|
||||
pull_number: type.number.describe("the pull request number to checkout")
|
||||
});
|
||||
function CheckoutPrTool(ctx) {
|
||||
return tool({
|
||||
name: "checkout_pr",
|
||||
description: "Checkout a pull request branch locally. This fetches the PR branch and sets up push configuration for fork PRs. Use this when you need to work on an existing PR.",
|
||||
parameters: CheckoutPr,
|
||||
execute: execute(ctx, async ({ pull_number }) => {
|
||||
log.info(`\u{1F500} checking out PR #${pull_number}...`);
|
||||
const pr = await ctx.octokit.rest.pulls.get({
|
||||
owner: ctx.owner,
|
||||
repo: ctx.name,
|
||||
pull_number
|
||||
});
|
||||
const headRepo = pr.data.head.repo;
|
||||
if (!headRepo) {
|
||||
throw new Error(`PR #${pull_number} source repository was deleted`);
|
||||
}
|
||||
const isFork = headRepo.full_name !== pr.data.base.repo.full_name;
|
||||
const baseBranch = pr.data.base.ref;
|
||||
const headBranch = pr.data.head.ref;
|
||||
log.info(`\u{1F4E5} fetching base branch (${baseBranch})...`);
|
||||
$("git", ["fetch", "--no-tags", "origin", baseBranch]);
|
||||
log.info(`\u{1F33F} fetching PR #${pull_number} (${headBranch})...`);
|
||||
$("git", ["fetch", "--no-tags", "origin", `pull/${pull_number}/head:${headBranch}`]);
|
||||
$("git", ["checkout", headBranch]);
|
||||
log.info(`\u2713 checked out PR #${pull_number}`);
|
||||
if (isFork) {
|
||||
const remoteName = `pr-${pull_number}`;
|
||||
const forkUrl = `https://x-access-token:${ctx.githubInstallationToken}@github.com/${headRepo.full_name}.git`;
|
||||
try {
|
||||
$("git", ["remote", "add", remoteName, forkUrl]);
|
||||
log.info(`\u{1F4CC} added remote '${remoteName}' for fork ${headRepo.full_name}`);
|
||||
} catch {
|
||||
$("git", ["remote", "set-url", remoteName, forkUrl]);
|
||||
log.info(`\u{1F4CC} updated remote '${remoteName}' for fork ${headRepo.full_name}`);
|
||||
}
|
||||
$("git", ["config", `branch.${headBranch}.pushRemote`, remoteName]);
|
||||
log.info(`\u{1F4CC} configured branch '${headBranch}' to push to '${remoteName}'`);
|
||||
if (!pr.data.maintainer_can_modify) {
|
||||
log.warning(
|
||||
`\u26A0\uFE0F fork PR has maintainer_can_modify=false - push operations will fail. ask the PR author to enable "Allow edits from maintainers" or the fork may be owned by an organization.`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$("git", ["config", `branch.${headBranch}.pushRemote`, "origin"]);
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
number: pr.data.number,
|
||||
title: pr.data.title,
|
||||
base: baseBranch,
|
||||
head: headBranch,
|
||||
isFork,
|
||||
maintainerCanModify: pr.data.maintainer_can_modify,
|
||||
url: pr.data.html_url,
|
||||
headRepo: headRepo.full_name
|
||||
};
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// mcp/debug.ts
|
||||
var DebugShellCommand = type({});
|
||||
var DebugShellCommandTool = tool({
|
||||
@@ -124079,27 +124180,30 @@ var PushBranch = type({
|
||||
branchName: type.string.describe("The branch name to push (defaults to current branch)").optional(),
|
||||
force: type.boolean.describe("Force push (use with caution)").default(false)
|
||||
});
|
||||
function PushBranchTool(ctx) {
|
||||
const remote = ctx.pushRemote;
|
||||
function PushBranchTool(_ctx) {
|
||||
return tool({
|
||||
name: "push_branch",
|
||||
description: "Push the current branch (or specified branch) to the remote repository. Never force push unless explicitly requested.",
|
||||
description: "Push the current branch (or specified branch) to the remote repository. Git automatically determines the correct remote based on branch config (set by checkout_pr for fork PRs). Never force push unless explicitly requested.",
|
||||
parameters: PushBranch,
|
||||
execute: execute(ctx, async ({ branchName, force }) => {
|
||||
execute: execute(_ctx, async ({ branchName, force }) => {
|
||||
const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
|
||||
const isUrl = remote.startsWith("https://");
|
||||
const args3 = force ? ["push", "--force", ...isUrl ? [] : ["-u"], remote, branch] : ["push", ...isUrl ? [] : ["-u"], remote, branch];
|
||||
log.info(`Pushing branch ${branch} to ${isUrl ? "(fork URL)" : remote}`);
|
||||
let remote = "origin";
|
||||
try {
|
||||
remote = $("git", ["config", `branch.${branch}.pushRemote`], { log: false }).trim();
|
||||
} catch {
|
||||
}
|
||||
const args3 = force ? ["push", "--force", "-u", remote, branch] : ["push", "-u", remote, branch];
|
||||
log.info(`pushing branch ${branch} to ${remote}`);
|
||||
if (force) {
|
||||
log.warning(`Force pushing - this will overwrite remote history`);
|
||||
log.warning(`force pushing - this will overwrite remote history`);
|
||||
}
|
||||
$("git", args3);
|
||||
return {
|
||||
success: true,
|
||||
branch,
|
||||
remote: isUrl ? "(fork URL)" : remote,
|
||||
remote,
|
||||
force,
|
||||
message: `Successfully pushed branch ${branch}`
|
||||
message: `successfully pushed branch ${branch}`
|
||||
};
|
||||
})
|
||||
});
|
||||
@@ -124325,6 +124429,19 @@ var PullRequest = type({
|
||||
body: type.string.describe("the body content of the pull request"),
|
||||
base: type.string.describe("the base branch to merge into (e.g., 'main')")
|
||||
});
|
||||
function buildPrBodyWithFooter(ctx, body) {
|
||||
const repoContext = parseRepoContext();
|
||||
const runId = process.env.GITHUB_RUN_ID;
|
||||
const agentName = ctx.payload.agent;
|
||||
const agentInfo = agentName ? agentsManifest[agentName] : null;
|
||||
const footer = buildPullfrogFooter({
|
||||
triggeredBy: true,
|
||||
agent: agentInfo ? { displayName: agentInfo.displayName, url: agentInfo.url } : void 0,
|
||||
workflowRun: runId ? { owner: repoContext.owner, repo: repoContext.name, runId } : void 0
|
||||
});
|
||||
const bodyWithoutFooter = stripExistingFooter(body);
|
||||
return `${bodyWithoutFooter}${footer}`;
|
||||
}
|
||||
function PullRequestTool(ctx) {
|
||||
return tool({
|
||||
name: "create_pull_request",
|
||||
@@ -124338,17 +124455,18 @@ function PullRequestTool(ctx) {
|
||||
"PR creation blocked: secrets detected in PR title or body. Please remove any sensitive information (API keys, tokens, passwords) before creating a PR."
|
||||
);
|
||||
}
|
||||
const diff = $("git", ["diff", `origin/${base}...HEAD`], { log: false });
|
||||
const diff = $("git", ["diff", `origin/${base}..HEAD`], { log: false });
|
||||
if (containsSecrets(diff)) {
|
||||
throw new Error(
|
||||
"PR creation blocked: secrets detected in changes. Please remove any sensitive information (API keys, tokens, passwords) before creating a PR."
|
||||
);
|
||||
}
|
||||
const bodyWithFooter = buildPrBodyWithFooter(ctx, body);
|
||||
const result = await ctx.octokit.rest.pulls.create({
|
||||
owner: ctx.owner,
|
||||
repo: ctx.name,
|
||||
title,
|
||||
body,
|
||||
body: bodyWithFooter,
|
||||
head: currentBranch,
|
||||
base
|
||||
});
|
||||
@@ -124372,7 +124490,7 @@ var PullRequestInfo = type({
|
||||
function PullRequestInfoTool(ctx) {
|
||||
return tool({
|
||||
name: "get_pull_request",
|
||||
description: "Retrieve PR information (metadata only). PR branch is already checked out during setup.",
|
||||
description: "Retrieve PR metadata (number, title, state, base/head branches, fork status). To checkout a PR branch locally, use checkout_pr instead.",
|
||||
parameters: PullRequestInfo,
|
||||
execute: execute(ctx, async ({ pull_number }) => {
|
||||
const pr = await ctx.octokit.rest.pulls.get({
|
||||
@@ -124417,7 +124535,9 @@ var Review = type({
|
||||
),
|
||||
start_line: type.number.describe("Start line for multi-line comments (optional, for commenting on ranges)").optional()
|
||||
}).array().describe(
|
||||
"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)."
|
||||
// FORK PR NOTE: use HEAD not origin/<head> - for fork PRs, origin/<head> doesn't exist
|
||||
// because the head branch is in a different repo (the fork). HEAD is the locally checked out PR branch.
|
||||
"PRIMARY location for ALL feedback. 95%+ of review content should be here. Use 'git diff origin/<base>..HEAD' to find correct line numbers (RIGHT side for new code, LEFT for old). Works for both fork and same-repo PRs."
|
||||
).optional()
|
||||
});
|
||||
function ReviewTool(ctx) {
|
||||
@@ -124462,6 +124582,7 @@ function ReviewTool(ctx) {
|
||||
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({
|
||||
workflowRun: { owner: ctx.owner, repo: ctx.name, runId: ctx.runId, jobId: ctx.jobId },
|
||||
customParts: [`[Fix all \u2794](${fixAllUrl})`, `[Fix \u{1F44D}s \u2794](${fixApprovedUrl})`]
|
||||
});
|
||||
const updatedBody = (body || "") + footer;
|
||||
@@ -124695,6 +124816,7 @@ async function startMcpHttpServer(ctx) {
|
||||
PullRequestTool(ctx),
|
||||
ReviewTool(ctx),
|
||||
PullRequestInfoTool(ctx),
|
||||
CheckoutPrTool(ctx),
|
||||
GetReviewCommentsTool(ctx),
|
||||
ListPullRequestReviewsTool(ctx),
|
||||
GetCheckSuiteLogsTool(ctx),
|
||||
@@ -125328,16 +125450,16 @@ ${error42}` : `\u274C ${error42}`;
|
||||
}
|
||||
|
||||
// utils/setup.ts
|
||||
import { execSync } from "node:child_process";
|
||||
import { execSync as execSync2 } from "node:child_process";
|
||||
function setupGitConfig() {
|
||||
const repoDir = process.cwd();
|
||||
log.info("\u{1F527} Setting up git configuration...");
|
||||
try {
|
||||
execSync('git config --local user.email "team@pullfrog.com"', {
|
||||
execSync2('git config --local user.email "team@pullfrog.com"', {
|
||||
cwd: repoDir,
|
||||
stdio: "pipe"
|
||||
});
|
||||
execSync('git config --local user.name "pullfrog"', {
|
||||
execSync2('git config --local user.name "pullfrog"', {
|
||||
cwd: repoDir,
|
||||
stdio: "pipe"
|
||||
});
|
||||
@@ -125350,44 +125472,19 @@ function setupGitConfig() {
|
||||
}
|
||||
async function setupGit(ctx) {
|
||||
const repoDir = process.cwd();
|
||||
log.info("\u{1F527} Setting up git authentication...");
|
||||
log.info("\u{1F527} setting up git authentication...");
|
||||
try {
|
||||
execSync("git config --local --unset-all http.https://github.com/.extraheader", {
|
||||
execSync2("git config --local --unset-all http.https://github.com/.extraheader", {
|
||||
cwd: repoDir,
|
||||
stdio: "pipe"
|
||||
});
|
||||
log.info("\u2713 Removed existing authentication headers");
|
||||
log.info("\u2713 removed existing authentication headers");
|
||||
} catch {
|
||||
log.debug("No existing authentication headers to remove");
|
||||
log.debug("no existing authentication headers to remove");
|
||||
}
|
||||
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("\u2713 Updated origin URL with authentication token");
|
||||
if (ctx.payload.event.is_pr !== true || !ctx.payload.event.issue_number) {
|
||||
return { pushRemote: "origin" };
|
||||
}
|
||||
const prNumber = ctx.payload.event.issue_number;
|
||||
const pr = await ctx.octokit.rest.pulls.get({
|
||||
owner: ctx.owner,
|
||||
repo: ctx.name,
|
||||
pull_number: prNumber
|
||||
});
|
||||
const headRepo = pr.data.head.repo;
|
||||
if (!headRepo) {
|
||||
throw new Error(`PR #${prNumber} source repository was deleted`);
|
||||
}
|
||||
const branch = pr.data.head.ref;
|
||||
log.info(`\u{1F33F} Checking out PR #${prNumber} (${branch})...`);
|
||||
$("git", ["fetch", "origin", `pull/${prNumber}/head:${branch}`], { cwd: repoDir });
|
||||
$("git", ["checkout", branch], { cwd: repoDir });
|
||||
log.info(`\u2713 Successfully checked out PR #${prNumber}`);
|
||||
const token = process.env.GITHUB_TOKEN || ctx.githubInstallationToken;
|
||||
const pushUrl = `https://x-access-token:${token}@github.com/${headRepo.full_name}.git`;
|
||||
const isFork = headRepo.full_name !== pr.data.base.repo.full_name;
|
||||
if (isFork) {
|
||||
log.info(`\u{1F374} Fork PR detected, will push to: ${headRepo.full_name}`);
|
||||
}
|
||||
return { pushRemote: pushUrl };
|
||||
log.info("\u2713 updated origin URL with authentication token");
|
||||
}
|
||||
|
||||
// utils/timer.ts
|
||||
@@ -125426,8 +125523,7 @@ async function main(inputs) {
|
||||
const partialCtx = await initializeContext(inputs, payload);
|
||||
const ctx = partialCtx;
|
||||
timer.checkpoint("initializeContext");
|
||||
const { pushRemote } = await setupGit(ctx);
|
||||
ctx.pushRemote = pushRemote;
|
||||
await setupGit(ctx);
|
||||
timer.checkpoint("setupGit");
|
||||
await setupTempDirectory(ctx);
|
||||
timer.checkpoint("setupTempDirectory");
|
||||
@@ -125636,11 +125732,26 @@ function parsePayload(inputs) {
|
||||
}
|
||||
async function startMcpServer(ctx) {
|
||||
const runId = process.env.GITHUB_RUN_ID;
|
||||
if (runId) {
|
||||
const workflowRunInfo = await fetchWorkflowRunInfo(runId);
|
||||
if (workflowRunInfo.progressCommentId) {
|
||||
process.env.PULLFROG_PROGRESS_COMMENT_ID = workflowRunInfo.progressCommentId;
|
||||
log.info(`\u{1F4DD} Using pre-created progress comment: ${workflowRunInfo.progressCommentId}`);
|
||||
if (!runId) {
|
||||
throw new Error("GITHUB_RUN_ID environment variable is required");
|
||||
}
|
||||
ctx.runId = runId;
|
||||
const workflowRunInfo = await fetchWorkflowRunInfo(ctx.runId);
|
||||
if (workflowRunInfo.progressCommentId) {
|
||||
process.env.PULLFROG_PROGRESS_COMMENT_ID = workflowRunInfo.progressCommentId;
|
||||
log.info(`\u{1F4DD} Using pre-created progress comment: ${workflowRunInfo.progressCommentId}`);
|
||||
}
|
||||
const jobName = process.env.GITHUB_JOB;
|
||||
if (jobName) {
|
||||
const jobs = await ctx.octokit.rest.actions.listJobsForWorkflowRun({
|
||||
owner: ctx.owner,
|
||||
repo: ctx.name,
|
||||
run_id: parseInt(ctx.runId, 10)
|
||||
});
|
||||
const matchingJob = jobs.data.jobs.find((job) => job.name === jobName);
|
||||
if (matchingJob) {
|
||||
ctx.jobId = String(matchingJob.id);
|
||||
log.info(`\u{1F4CB} Found job ID: ${ctx.jobId}`);
|
||||
}
|
||||
}
|
||||
const { url: url2, close } = await startMcpHttpServer(ctx);
|
||||
@@ -125695,7 +125806,12 @@ ${encode(eventWithoutContext)}`;
|
||||
apiKey: ctx.apiKey,
|
||||
apiKeys: ctx.apiKeys,
|
||||
cliPath: ctx.cliPath,
|
||||
prepResults: ctx.prepResults
|
||||
prepResults: ctx.prepResults,
|
||||
repo: {
|
||||
owner: ctx.owner,
|
||||
name: ctx.name,
|
||||
defaultBranch: ctx.repo.default_branch
|
||||
}
|
||||
});
|
||||
}
|
||||
async function handleAgentResult(result) {
|
||||
|
||||
Reference in New Issue
Block a user