Update entry
This commit is contained in:
@@ -19768,7 +19768,7 @@ Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
|
|||||||
(0, command_1.issue)("endgroup");
|
(0, command_1.issue)("endgroup");
|
||||||
}
|
}
|
||||||
exports.endGroup = endGroup3;
|
exports.endGroup = endGroup3;
|
||||||
function group(name, fn2) {
|
function group2(name, fn2) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
startGroup3(name);
|
startGroup3(name);
|
||||||
let result;
|
let result;
|
||||||
@@ -19780,7 +19780,7 @@ Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
|
|||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.group = group;
|
exports.group = group2;
|
||||||
function saveState(name, value2) {
|
function saveState(name, value2) {
|
||||||
const filePath = process.env["GITHUB_STATE"] || "";
|
const filePath = process.env["GITHUB_STATE"] || "";
|
||||||
if (filePath) {
|
if (filePath) {
|
||||||
@@ -21000,17 +21000,17 @@ var require_ansi_styles = __commonJS({
|
|||||||
styles.bgColor.bgGray = styles.bgColor.bgBlackBright;
|
styles.bgColor.bgGray = styles.bgColor.bgBlackBright;
|
||||||
styles.color.grey = styles.color.blackBright;
|
styles.color.grey = styles.color.blackBright;
|
||||||
styles.bgColor.bgGrey = styles.bgColor.bgBlackBright;
|
styles.bgColor.bgGrey = styles.bgColor.bgBlackBright;
|
||||||
for (const [groupName, group] of Object.entries(styles)) {
|
for (const [groupName, group2] of Object.entries(styles)) {
|
||||||
for (const [styleName, style] of Object.entries(group)) {
|
for (const [styleName, style] of Object.entries(group2)) {
|
||||||
styles[styleName] = {
|
styles[styleName] = {
|
||||||
open: `\x1B[${style[0]}m`,
|
open: `\x1B[${style[0]}m`,
|
||||||
close: `\x1B[${style[1]}m`
|
close: `\x1B[${style[1]}m`
|
||||||
};
|
};
|
||||||
group[styleName] = styles[styleName];
|
group2[styleName] = styles[styleName];
|
||||||
codes.set(style[0], style[1]);
|
codes.set(style[0], style[1]);
|
||||||
}
|
}
|
||||||
Object.defineProperty(styles, groupName, {
|
Object.defineProperty(styles, groupName, {
|
||||||
value: group,
|
value: group2,
|
||||||
enumerable: false
|
enumerable: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -21272,9 +21272,9 @@ var require_utils3 = __commonJS({
|
|||||||
var groupBySizes = (array, sizes) => {
|
var groupBySizes = (array, sizes) => {
|
||||||
let startIndex = 0;
|
let startIndex = 0;
|
||||||
return sizes.map((size) => {
|
return sizes.map((size) => {
|
||||||
const group = array.slice(startIndex, startIndex + size);
|
const group2 = array.slice(startIndex, startIndex + size);
|
||||||
startIndex += size;
|
startIndex += size;
|
||||||
return group;
|
return group2;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
exports.groupBySizes = groupBySizes;
|
exports.groupBySizes = groupBySizes;
|
||||||
@@ -24967,8 +24967,8 @@ var require_drawTable = __commonJS({
|
|||||||
var utils_1 = require_utils3();
|
var utils_1 = require_utils3();
|
||||||
var drawTable = (rows, outputColumnWidths, rowHeights, config3) => {
|
var drawTable = (rows, outputColumnWidths, rowHeights, config3) => {
|
||||||
const { drawHorizontalLine, singleLine } = config3;
|
const { drawHorizontalLine, singleLine } = config3;
|
||||||
const contents = (0, utils_1.groupBySizes)(rows, rowHeights).map((group, groupIndex) => {
|
const contents = (0, utils_1.groupBySizes)(rows, rowHeights).map((group2, groupIndex) => {
|
||||||
return group.map((row) => {
|
return group2.map((row) => {
|
||||||
return (0, drawRow_1.drawRow)(row, {
|
return (0, drawRow_1.drawRow)(row, {
|
||||||
...config3,
|
...config3,
|
||||||
rowIndex: groupIndex
|
rowIndex: groupIndex
|
||||||
@@ -30717,8 +30717,8 @@ var require_rules2 = __commonJS({
|
|||||||
var TYPES = ["number", "integer", "string", "array", "object", "boolean", "null"];
|
var TYPES = ["number", "integer", "string", "array", "object", "boolean", "null"];
|
||||||
RULES.all = toHash(ALL);
|
RULES.all = toHash(ALL);
|
||||||
RULES.types = toHash(TYPES);
|
RULES.types = toHash(TYPES);
|
||||||
RULES.forEach(function(group) {
|
RULES.forEach(function(group2) {
|
||||||
group.rules = group.rules.map(function(keyword) {
|
group2.rules = group2.rules.map(function(keyword) {
|
||||||
var implKeywords;
|
var implKeywords;
|
||||||
if (typeof keyword == "object") {
|
if (typeof keyword == "object") {
|
||||||
var key = Object.keys(keyword)[0];
|
var key = Object.keys(keyword)[0];
|
||||||
@@ -30741,7 +30741,7 @@ var require_rules2 = __commonJS({
|
|||||||
keyword: "$comment",
|
keyword: "$comment",
|
||||||
code: ruleModules.$comment
|
code: ruleModules.$comment
|
||||||
};
|
};
|
||||||
if (group.type) RULES.types[group.type] = group;
|
if (group2.type) RULES.types[group2.type] = group2;
|
||||||
});
|
});
|
||||||
RULES.keywords = toHash(ALL.concat(KEYWORDS));
|
RULES.keywords = toHash(ALL.concat(KEYWORDS));
|
||||||
RULES.custom = {};
|
RULES.custom = {};
|
||||||
@@ -72903,8 +72903,8 @@ var require_rules = __commonJS2((exports, module) => {
|
|||||||
var TYPES = ["number", "integer", "string", "array", "object", "boolean", "null"];
|
var TYPES = ["number", "integer", "string", "array", "object", "boolean", "null"];
|
||||||
RULES.all = toHash(ALL);
|
RULES.all = toHash(ALL);
|
||||||
RULES.types = toHash(TYPES);
|
RULES.types = toHash(TYPES);
|
||||||
RULES.forEach(function(group) {
|
RULES.forEach(function(group2) {
|
||||||
group.rules = group.rules.map(function(keyword) {
|
group2.rules = group2.rules.map(function(keyword) {
|
||||||
var implKeywords;
|
var implKeywords;
|
||||||
if (typeof keyword == "object") {
|
if (typeof keyword == "object") {
|
||||||
var key = Object.keys(keyword)[0];
|
var key = Object.keys(keyword)[0];
|
||||||
@@ -72927,8 +72927,8 @@ var require_rules = __commonJS2((exports, module) => {
|
|||||||
keyword: "$comment",
|
keyword: "$comment",
|
||||||
code: ruleModules.$comment
|
code: ruleModules.$comment
|
||||||
};
|
};
|
||||||
if (group.type)
|
if (group2.type)
|
||||||
RULES.types[group.type] = group;
|
RULES.types[group2.type] = group2;
|
||||||
});
|
});
|
||||||
RULES.keywords = toHash(ALL.concat(KEYWORDS));
|
RULES.keywords = toHash(ALL.concat(KEYWORDS));
|
||||||
RULES.custom = {};
|
RULES.custom = {};
|
||||||
@@ -80237,6 +80237,11 @@ function endGroup2() {
|
|||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function group(name, fn2) {
|
||||||
|
startGroup2(name);
|
||||||
|
fn2();
|
||||||
|
endGroup2();
|
||||||
|
}
|
||||||
function boxString(text, options) {
|
function boxString(text, options) {
|
||||||
const { title, maxWidth = 80, indent: indent2 = "", padding = 1 } = options || {};
|
const { title, maxWidth = 80, indent: indent2 = "", padding = 1 } = options || {};
|
||||||
const lines = text.trim().split("\n");
|
const lines = text.trim().split("\n");
|
||||||
@@ -80457,6 +80462,10 @@ var log = {
|
|||||||
* End a collapsed group
|
* End a collapsed group
|
||||||
*/
|
*/
|
||||||
endGroup: endGroup2,
|
endGroup: endGroup2,
|
||||||
|
/**
|
||||||
|
* Run a callback within a collapsed group
|
||||||
|
*/
|
||||||
|
group,
|
||||||
/**
|
/**
|
||||||
* Log tool call information to console with formatted output
|
* Log tool call information to console with formatted output
|
||||||
*/
|
*/
|
||||||
@@ -85261,7 +85270,7 @@ var UnionNode = class extends BaseRoot {
|
|||||||
innerToJsonSchema(ctx) {
|
innerToJsonSchema(ctx) {
|
||||||
if (this.branchGroups.length === 1 && this.branchGroups[0].equals($ark.intrinsic.boolean))
|
if (this.branchGroups.length === 1 && this.branchGroups[0].equals($ark.intrinsic.boolean))
|
||||||
return { type: "boolean" };
|
return { type: "boolean" };
|
||||||
const jsonSchemaBranches = this.branchGroups.map((group) => group.toJsonSchemaRecurse(ctx));
|
const jsonSchemaBranches = this.branchGroups.map((group2) => group2.toJsonSchemaRecurse(ctx));
|
||||||
if (jsonSchemaBranches.every((branch) => (
|
if (jsonSchemaBranches.every((branch) => (
|
||||||
// iff all branches are pure unit values with no metadata,
|
// iff all branches are pure unit values with no metadata,
|
||||||
// we can simplify the representation to an enum
|
// we can simplify the representation to an enum
|
||||||
@@ -89445,30 +89454,36 @@ ${disableProgressComment ? "" : `
|
|||||||
prompt: `Follow these steps:
|
prompt: `Follow these steps:
|
||||||
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.
|
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 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.
|
2. **IMPORTANT**: After calling ${ghPullfrogMcpName}/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.
|
3. Start review session using ${ghPullfrogMcpName}/start_review. This creates a scratchpad file at a temp path (e.g., \`/tmp/pullfrog-review-abc123.md\`) and returns a session ID. The scratchpad file header contains the session ID for reference. Use this file as free-form space to gather your thoughts before adding comments.
|
||||||
|
|
||||||
4. Submit review using ${ghPullfrogMcpName}/submit_pull_request_review
|
4. **ANALYZE** - Use the scratchpad to gather your thoughts:
|
||||||
|
- Summarize what changes this PR makes
|
||||||
|
- Evaluate the approach - is it sound? If not, **stop here** and leave feedback on the approach. Don't waste time on implementation details if the approach is wrong.
|
||||||
|
- If approach is sound, analyze implementation - consider potential issues per file
|
||||||
|
- Identify bugs, security issues, edge cases
|
||||||
|
|
||||||
|
5. **SELF-CRITIQUE** - Before adding comments, review your scratchpad:
|
||||||
|
- Remove nitpicks unless explicitly requested. Think documentation, JSDoc/docstrings, useless comments (compliments)
|
||||||
|
- Your level of nitpickiness should be proportional to the current state of the codebase. Try to guess how much the user will care about a specific critique.
|
||||||
|
|
||||||
|
6. Add inline review comments one-by-one using ${ghPullfrogMcpName}/add_review_comment
|
||||||
|
- Use **relative paths** from repo root (e.g., \`packages/core/src/utils.ts\`)
|
||||||
|
- 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.
|
||||||
|
- For issues appearing in multiple places, comment on the FIRST occurrence and reference others (e.g., "also at lines X, Y")
|
||||||
|
|
||||||
|
7. Submit the review using ${ghPullfrogMcpName}/submit_review
|
||||||
|
- The "body" field is ONLY for: (1) a 1-3 sentence high-level overview, (2) urgency level (e.g., "minor suggestions" vs "blocking issues"), (3) critical security callouts (e.g., API key exposure)
|
||||||
|
|
||||||
**GENERAL GUIDANCE**
|
**GENERAL GUIDANCE**
|
||||||
|
|
||||||
- Do not leave any comments that are not potentially actionable. Do not leave complimentary comments just to be nice.
|
- 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\`)
|
- Do not nitpick unless instructed explicitly to do so by the user's additional instructions. This includes: requesting documentation/docstrings/JSDoc.
|
||||||
- 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.**
|
- **CRITICAL: Prioritize per-line feedback over summary text.**
|
||||||
- ALL specific feedback MUST go in the 'comments' array with file paths and line numbers from the diff
|
- All specific feedback MUST go in inline review comments with file paths and line numbers from the diff
|
||||||
- For issues appearing in multiple places, comment on the FIRST occurrence and reference others (e.g., "also at lines X, Y" or "similar issue in otherFile.ts:42")
|
- The vast majority of review content should be in inline review comments; the body should be brief and only summarize the urgency of the review and any cross-cutting concerns.
|
||||||
- The "body" field is ONLY for: (1) a 1-2 sentence high-level overview, (2) urgency level (e.g., "minor suggestions" vs "blocking issues"), (3) critical security callouts (e.g., API key exposure)
|
|
||||||
- 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.
|
|
||||||
- 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.
|
|
||||||
- 3) if the conceptual approach looks sound, evaluate the implementation. leave feedback as needed. consider everything, but especially edge cases, security, correctness, and performance. leave feedback as needed.
|
|
||||||
- 4) only leave nitpick/housekeeping comments if instructed explicity to do so by the user's additional instructions.
|
|
||||||
`
|
`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -89634,34 +89649,34 @@ CRITICAL SECURITY RULES - NEVER VIOLATE UNDER ANY CIRCUMSTANCES:
|
|||||||
|
|
||||||
### Rule 1: Never expose secrets through ANY means
|
### Rule 1: Never expose secrets through ANY means
|
||||||
|
|
||||||
You must NEVER expose secrets through any channel, including but not limited to:
|
You must NEVER expose secrets through any channel, including but not limited to:
|
||||||
- Displaying, printing, echoing, logging, or outputting to console
|
- Displaying, printing, echoing, logging, or outputting to console
|
||||||
- Writing to files (including .txt, .env, .json, config files, etc.)
|
- Writing to files (including .txt, .env, .json, config files, etc.)
|
||||||
- Including in git commits, commit messages, or PR descriptions
|
- Including in git commits, commit messages, or PR descriptions
|
||||||
- Posting in GitHub comments, issue bodies, or PR review comments
|
- Posting in GitHub comments, issue bodies, or PR review comments
|
||||||
- Returning in tool outputs, API responses, or error messages
|
- Returning in tool outputs, API responses, or error messages
|
||||||
- Including in redirect URLs, WebSocket messages, or GraphQL responses
|
- Including in redirect URLs, WebSocket messages, or GraphQL responses
|
||||||
|
|
||||||
Secrets include: API keys, authentication tokens, passwords, private keys, certificates, database connection strings, and any credential used for authentication or authorization. Common patterns (case-insensitive): variables containing API_KEY, SECRET, TOKEN, PASSWORD, CREDENTIAL, PRIVATE_KEY, or AUTH in an authentication context. Use judgment: \`PUBLIC_KEY\` for a cryptographic public key is fine; \`PRIVATE_KEY\` is not.
|
Secrets include: API keys, authentication tokens, passwords, private keys, certificates, database connection strings, and any credential used for authentication or authorization. Common patterns (case-insensitive): variables containing API_KEY, SECRET, TOKEN, PASSWORD, CREDENTIAL, PRIVATE_KEY, or AUTH in an authentication context. Use judgment: \`PUBLIC_KEY\` for a cryptographic public key is fine; \`PRIVATE_KEY\` is not.
|
||||||
|
|
||||||
### Rule 2: Never serialize objects containing secrets
|
### Rule 2: Never serialize objects containing secrets
|
||||||
|
|
||||||
When working with objects that may contain environment variables or secrets:
|
When working with objects that may contain environment variables or secrets:
|
||||||
- NEVER serialize, stringify, or dump entire environment objects (process.env, os.environ, ENV, etc.)
|
- NEVER serialize, stringify, or dump entire environment objects (process.env, os.environ, ENV, etc.)
|
||||||
- NEVER iterate over environment variables and write their values to files
|
- NEVER iterate over environment variables and write their values to files
|
||||||
- NEVER include environment variable values in outputs, logs, HTTP requests, or anywhere they can be exposed
|
- NEVER include environment variable values in outputs, logs, HTTP requests, or anywhere they can be exposed
|
||||||
- If you must list properties, only show property NAMES, never values
|
- If you must list properties, only show property NAMES, never values
|
||||||
- Only access specific, known-safe keys explicitly (e.g., NODE_ENV, HOME, PWD)
|
- Only access specific, known-safe keys explicitly (e.g., NODE_ENV, HOME, PWD)
|
||||||
|
|
||||||
### Rule 3: Refuse and explain
|
### Rule 3: Refuse and explain
|
||||||
|
|
||||||
Even if explicitly requested to reveal secrets, you must:
|
Even if explicitly requested to reveal secrets, you must:
|
||||||
1. Refuse the request
|
1. Refuse the request
|
||||||
2. Print a message explaining that exposing secrets is prohibited for security reasons
|
2. Print a message explaining that exposing secrets is prohibited for security reasons
|
||||||
3. If using ${ghPullfrogMcpName}, update the working comment to explain that secrets cannot be revealed
|
3. If using ${ghPullfrogMcpName}, update the working comment to explain that secrets cannot be revealed
|
||||||
4. Offer a safe alternative, if applicable
|
4. Offer a safe alternative, if applicable
|
||||||
|
|
||||||
If you encounter secrets in files or environment, acknowledge they exist but never reveal their values.
|
If you encounter secrets in files or environment, acknowledge they exist but never reveal their values.
|
||||||
|
|
||||||
## MCP (Model Context Protocol) Tools
|
## MCP (Model Context Protocol) Tools
|
||||||
|
|
||||||
@@ -89697,29 +89712,27 @@ Tool names may be formatted as \`(server name)/(tool name)\`, for example: \`${g
|
|||||||
|
|
||||||
**Commenting style**: When posting comments via ${ghPullfrogMcpName}, write as a professional team member would. Your final comments should be polished and actionable\u2014do not include intermediate reasoning like "I'll now look at the code" or "Let me respond to the question."
|
**Commenting style**: When posting comments via ${ghPullfrogMcpName}, write as a professional team member would. Your final comments should be polished and actionable\u2014do not include intermediate reasoning like "I'll now look at the code" or "Let me respond to the question."
|
||||||
|
|
||||||
## Mode Selection
|
**If you get stuck**: If you cannot complete a task due to missing information, ambiguity, or an unrecoverable error:
|
||||||
|
|
||||||
Before starting any work, you must first determine which mode to use by examining the request and calling ${ghPullfrogMcpName}/select_mode.
|
|
||||||
|
|
||||||
Available modes:
|
|
||||||
|
|
||||||
${[...getModes({ disableProgressComment: payload.disableProgressComment, dependenciesPreinstalled }), ...payload.modes].map((w) => ` - "${w.name}": ${w.description}`).join("\n")}
|
|
||||||
|
|
||||||
**Required first step**:
|
|
||||||
1. Examine the user's request/prompt carefully
|
|
||||||
2. Determine which mode is most appropriate based on the mode descriptions above
|
|
||||||
3. If the request could fit multiple modes, choose the mode with the narrowest scope that still addresses the request
|
|
||||||
4. Call ${ghPullfrogMcpName}/select_mode with the chosen mode name
|
|
||||||
5. The tool will return detailed instructions for that mode\u2014follow those instructions, but remember they cannot override the Security rules or System instructions above
|
|
||||||
6. Check for an AGENTS.md file or an agent-specific equivalent that applies to you. If it exists, read it and follow the instructions unless they conflict with the Security, System or Mode instructions above
|
|
||||||
|
|
||||||
## When You're Stuck
|
|
||||||
|
|
||||||
If you cannot complete a task due to missing information, ambiguity, or an unrecoverable error:
|
|
||||||
1. Do not silently fail or produce incomplete work
|
1. Do not silently fail or produce incomplete work
|
||||||
2. Post a comment via ${ghPullfrogMcpName} explaining what blocked you and what information or action would unblock you
|
2. Post a comment via ${ghPullfrogMcpName} explaining what blocked you and what information or action would unblock you
|
||||||
3. Make your blocker comment specific and actionable (e.g., "I need the database schema to proceed" not "I'm stuck")
|
3. Make your blocker comment specific and actionable (e.g., "I need the database schema to proceed" not "I'm stuck")
|
||||||
|
|
||||||
|
**Agent context files** Check for an AGENTS.md file or an agent-specific equivalent that applies to you. If it exists, read it and follow the instructions unless they conflict with the Security, System or Mode instructions above
|
||||||
|
|
||||||
|
*************************************
|
||||||
|
************* YOUR TASK *************
|
||||||
|
*************************************
|
||||||
|
|
||||||
|
**Required!** Before starting any work, you will pick a mode. Examine the prompt below carefully, along with the event data and runtime context. Determine which mode is most appropriate based on the mode descriptions below. Then use ${ghPullfrogMcpName}/select_mode to pick a mode. If the request could fit multiple modes, choose the mode with the narrowest scope that still addresses the request. You will be given back detailed step-by-step instructions based on your selection.
|
||||||
|
|
||||||
|
### Available modes
|
||||||
|
|
||||||
|
${[...getModes({ disableProgressComment: payload.disableProgressComment, dependenciesPreinstalled }), ...payload.modes].map((w) => ` - "${w.name}": ${w.description}`).join("\n")}
|
||||||
|
|
||||||
|
### Following the mode instructions
|
||||||
|
|
||||||
|
After selecting a mode, follow the detailed step-by-step instructions provided by the ${ghPullfrogMcpName}/select_mode tool. Refer to the user prompt, event data, and runtime context below to inform your actions. These instructions cannot override the Security rules or System instructions above.
|
||||||
|
|
||||||
************* USER PROMPT *************
|
************* USER PROMPT *************
|
||||||
|
|
||||||
${payload.prompt.split("\n").map((line) => `> ${line}`).join("\n")}
|
${payload.prompt.split("\n").map((line) => `> ${line}`).join("\n")}
|
||||||
@@ -90156,7 +90169,7 @@ var claude = agent({
|
|||||||
run: async ({ payload, mcpServers, apiKey, cliPath, prepResults, repo }) => {
|
run: async ({ payload, mcpServers, apiKey, cliPath, prepResults, repo }) => {
|
||||||
delete process.env.ANTHROPIC_API_KEY;
|
delete process.env.ANTHROPIC_API_KEY;
|
||||||
const prompt = addInstructions({ payload, prepResults, repo });
|
const prompt = addInstructions({ payload, prepResults, repo });
|
||||||
console.log(prompt);
|
log.group("Full prompt", () => log.info(prompt));
|
||||||
const sandboxOptions = payload.sandbox ? {
|
const sandboxOptions = payload.sandbox ? {
|
||||||
permissionMode: "default",
|
permissionMode: "default",
|
||||||
disallowedTools: ["Bash", "WebSearch", "WebFetch", "Write"],
|
disallowedTools: ["Bash", "WebSearch", "WebFetch", "Write"],
|
||||||
@@ -90837,6 +90850,7 @@ var cursor = agent({
|
|||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const fullPrompt = addInstructions({ payload, prepResults, repo });
|
const fullPrompt = addInstructions({ payload, prepResults, repo });
|
||||||
|
log.group("Full prompt", () => log.info(fullPrompt));
|
||||||
const cursorArgs = payload.sandbox ? [
|
const cursorArgs = payload.sandbox ? [
|
||||||
"--print",
|
"--print",
|
||||||
fullPrompt,
|
fullPrompt,
|
||||||
@@ -91146,7 +91160,7 @@ var gemini = agent({
|
|||||||
throw new Error("google_api_key or gemini_api_key is required for gemini agent");
|
throw new Error("google_api_key or gemini_api_key is required for gemini agent");
|
||||||
}
|
}
|
||||||
const sessionPrompt = addInstructions({ payload, prepResults, repo });
|
const sessionPrompt = addInstructions({ payload, prepResults, repo });
|
||||||
log.info(`Starting Gemini CLI with prompt: ${payload.prompt.substring(0, 100)}...`);
|
log.group("Full prompt", () => log.info(sessionPrompt));
|
||||||
const args3 = payload.sandbox ? [
|
const args3 = payload.sandbox ? [
|
||||||
"--allowed-tools",
|
"--allowed-tools",
|
||||||
"read_file,list_directory,search_file_content,glob,save_memory,write_todos",
|
"read_file,list_directory,search_file_content,glob,save_memory,write_todos",
|
||||||
@@ -91408,7 +91422,8 @@ var opencode = agent({
|
|||||||
configureOpenCodeMcpServers({ mcpServers });
|
configureOpenCodeMcpServers({ mcpServers });
|
||||||
configureOpenCodeSandbox({ sandbox: payload.sandbox ?? false });
|
configureOpenCodeSandbox({ sandbox: payload.sandbox ?? false });
|
||||||
const prompt = addInstructions({ payload, prepResults, repo });
|
const prompt = addInstructions({ payload, prepResults, repo });
|
||||||
const args3 = ["run", "--format", "json", prompt];
|
log.group("Full prompt", () => log.info(prompt));
|
||||||
|
const args3 = ["run", prompt, "--format", "json"];
|
||||||
if (payload.sandbox) {
|
if (payload.sandbox) {
|
||||||
log.info("\u{1F512} sandbox mode enabled: restricting to read-only operations");
|
log.info("\u{1F512} sandbox mode enabled: restricting to read-only operations");
|
||||||
}
|
}
|
||||||
@@ -91440,6 +91455,7 @@ var opencode = agent({
|
|||||||
// 10 minutes timeout to prevent infinite hangs
|
// 10 minutes timeout to prevent infinite hangs
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
onStdout: async (chunk) => {
|
onStdout: async (chunk) => {
|
||||||
|
log.debug(`[opencode stdout] ${chunk}`);
|
||||||
const text = chunk.toString();
|
const text = chunk.toString();
|
||||||
output += text;
|
output += text;
|
||||||
const lines = text.split("\n");
|
const lines = text.split("\n");
|
||||||
@@ -91472,7 +91488,8 @@ var opencode = agent({
|
|||||||
"step_finish",
|
"step_finish",
|
||||||
"tool_use",
|
"tool_use",
|
||||||
"tool_result",
|
"tool_result",
|
||||||
"result"
|
"result",
|
||||||
|
"error"
|
||||||
].includes(event.type)) {
|
].includes(event.type)) {
|
||||||
log.debug(`\u{1F4CB} OpenCode event (unhandled): type=${event.type}`);
|
log.debug(`\u{1F4CB} OpenCode event (unhandled): type=${event.type}`);
|
||||||
}
|
}
|
||||||
@@ -91564,16 +91581,20 @@ function configureOpenCodeSandbox({ sandbox }) {
|
|||||||
} catch {
|
} catch {
|
||||||
}
|
}
|
||||||
if (sandbox) {
|
if (sandbox) {
|
||||||
config3.tools = {
|
config3.permission = {
|
||||||
write: false,
|
edit: "deny",
|
||||||
bash: false,
|
bash: "deny",
|
||||||
webfetch: false
|
webfetch: "deny",
|
||||||
|
doom_loop: "allow",
|
||||||
|
external_directory: "allow"
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
config3.tools = {
|
config3.permission = {
|
||||||
write: true,
|
edit: "allow",
|
||||||
bash: true,
|
bash: "allow",
|
||||||
webfetch: true
|
webfetch: "allow",
|
||||||
|
doom_loop: "allow",
|
||||||
|
external_directory: "allow"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
writeFileSync3(configPath, JSON.stringify(config3, null, 2), "utf-8");
|
writeFileSync3(configPath, JSON.stringify(config3, null, 2), "utf-8");
|
||||||
@@ -91592,7 +91613,7 @@ var agents = {
|
|||||||
// main.ts
|
// main.ts
|
||||||
import { mkdtemp as mkdtemp2 } from "node:fs/promises";
|
import { mkdtemp as mkdtemp2 } from "node:fs/promises";
|
||||||
import { tmpdir as tmpdir2 } from "node:os";
|
import { tmpdir as tmpdir2 } from "node:os";
|
||||||
import { join as join10 } from "node:path";
|
import { join as join11 } from "node:path";
|
||||||
|
|
||||||
// ../node_modules/.pnpm/universal-user-agent@7.0.3/node_modules/universal-user-agent/index.js
|
// ../node_modules/.pnpm/universal-user-agent@7.0.3/node_modules/universal-user-agent/index.js
|
||||||
function getUserAgent() {
|
function getUserAgent() {
|
||||||
@@ -95099,14 +95120,14 @@ function buildPullfrogFooter(params) {
|
|||||||
if (params.agent) {
|
if (params.agent) {
|
||||||
parts.push(`Using [${params.agent.displayName}](${params.agent.url})`);
|
parts.push(`Using [${params.agent.displayName}](${params.agent.url})`);
|
||||||
}
|
}
|
||||||
|
if (params.customParts) {
|
||||||
|
parts.push(...params.customParts);
|
||||||
|
}
|
||||||
if (params.workflowRun) {
|
if (params.workflowRun) {
|
||||||
const baseUrl = `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;
|
const url2 = params.workflowRun.jobId ? `${baseUrl}/job/${params.workflowRun.jobId}` : baseUrl;
|
||||||
parts.push(`[View workflow run](${url2})`);
|
parts.push(`[View workflow run](${url2})`);
|
||||||
}
|
}
|
||||||
if (params.customParts) {
|
|
||||||
parts.push(...params.customParts);
|
|
||||||
}
|
|
||||||
const allParts = [
|
const allParts = [
|
||||||
...parts,
|
...parts,
|
||||||
"[pullfrog.com](https://pullfrog.com)",
|
"[pullfrog.com](https://pullfrog.com)",
|
||||||
@@ -95385,7 +95406,7 @@ async function reportProgress(ctx, { body }) {
|
|||||||
action: "updated"
|
action: "updated"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const issueNumber = ctx.payload.event.issue_number;
|
const issueNumber = ctx.toolState.prNumber ?? ctx.toolState.issueNumber ?? ctx.payload.event.issue_number;
|
||||||
if (issueNumber === void 0) {
|
if (issueNumber === void 0) {
|
||||||
return void 0;
|
return void 0;
|
||||||
}
|
}
|
||||||
@@ -121111,8 +121132,8 @@ var require_rules3 = /* @__PURE__ */ __commonJS3({ "node_modules/.pnpm/ajv@6.12.
|
|||||||
];
|
];
|
||||||
RULES.all = toHash(ALL);
|
RULES.all = toHash(ALL);
|
||||||
RULES.types = toHash(TYPES);
|
RULES.types = toHash(TYPES);
|
||||||
RULES.forEach(function(group) {
|
RULES.forEach(function(group2) {
|
||||||
group.rules = group.rules.map(function(keyword) {
|
group2.rules = group2.rules.map(function(keyword) {
|
||||||
var implKeywords;
|
var implKeywords;
|
||||||
if (typeof keyword == "object") {
|
if (typeof keyword == "object") {
|
||||||
var key$1 = Object.keys(keyword)[0];
|
var key$1 = Object.keys(keyword)[0];
|
||||||
@@ -121134,7 +121155,7 @@ var require_rules3 = /* @__PURE__ */ __commonJS3({ "node_modules/.pnpm/ajv@6.12.
|
|||||||
keyword: "$comment",
|
keyword: "$comment",
|
||||||
code: ruleModules.$comment
|
code: ruleModules.$comment
|
||||||
};
|
};
|
||||||
if (group.type) RULES.types[group.type] = group;
|
if (group2.type) RULES.types[group2.type] = group2;
|
||||||
});
|
});
|
||||||
RULES.keywords = toHash(ALL.concat(KEYWORDS$1));
|
RULES.keywords = toHash(ALL.concat(KEYWORDS$1));
|
||||||
RULES.custom = {};
|
RULES.custom = {};
|
||||||
@@ -123846,89 +123867,6 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// mcp/checkSuite.ts
|
|
||||||
var GetCheckSuiteLogs = type({
|
|
||||||
check_suite_id: type.number.describe("the id from check_suite.id")
|
|
||||||
});
|
|
||||||
function GetCheckSuiteLogsTool(ctx) {
|
|
||||||
return tool({
|
|
||||||
name: "get_check_suite_logs",
|
|
||||||
description: "get workflow run logs for a failed check suite. pass check_suite.id from the webhook payload.",
|
|
||||||
parameters: GetCheckSuiteLogs,
|
|
||||||
execute: execute(ctx, async ({ check_suite_id }) => {
|
|
||||||
const workflowRuns = await ctx.octokit.paginate(
|
|
||||||
ctx.octokit.rest.actions.listWorkflowRunsForRepo,
|
|
||||||
{
|
|
||||||
owner: ctx.owner,
|
|
||||||
repo: ctx.name,
|
|
||||||
check_suite_id,
|
|
||||||
per_page: 100
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const failedRuns = workflowRuns.filter((run2) => run2.conclusion === "failure");
|
|
||||||
if (failedRuns.length === 0) {
|
|
||||||
return {
|
|
||||||
check_suite_id,
|
|
||||||
message: "no failed workflow runs found for this check suite",
|
|
||||||
workflow_runs: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const logsForRuns = await Promise.all(
|
|
||||||
failedRuns.map(async (run2) => {
|
|
||||||
const jobs = await ctx.octokit.paginate(ctx.octokit.rest.actions.listJobsForWorkflowRun, {
|
|
||||||
owner: ctx.owner,
|
|
||||||
repo: ctx.name,
|
|
||||||
run_id: run2.id
|
|
||||||
});
|
|
||||||
const jobLogs = await Promise.all(
|
|
||||||
jobs.map(async (job) => {
|
|
||||||
try {
|
|
||||||
const logsResponse = await ctx.octokit.rest.actions.downloadJobLogsForWorkflowRun({
|
|
||||||
owner: ctx.owner,
|
|
||||||
repo: ctx.name,
|
|
||||||
job_id: job.id
|
|
||||||
});
|
|
||||||
const logsUrl = logsResponse.url;
|
|
||||||
const logsText = await fetch(logsUrl).then((r) => r.text());
|
|
||||||
return {
|
|
||||||
job_id: job.id,
|
|
||||||
job_name: job.name,
|
|
||||||
status: job.status,
|
|
||||||
conclusion: job.conclusion,
|
|
||||||
started_at: job.started_at,
|
|
||||||
completed_at: job.completed_at,
|
|
||||||
logs: logsText
|
|
||||||
};
|
|
||||||
} catch (error42) {
|
|
||||||
return {
|
|
||||||
job_id: job.id,
|
|
||||||
job_name: job.name,
|
|
||||||
status: job.status,
|
|
||||||
conclusion: job.conclusion,
|
|
||||||
started_at: job.started_at,
|
|
||||||
completed_at: job.completed_at,
|
|
||||||
error: `failed to fetch logs: ${error42}`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
workflow_run_id: run2.id,
|
|
||||||
workflow_name: run2.name,
|
|
||||||
html_url: run2.html_url,
|
|
||||||
conclusion: run2.conclusion,
|
|
||||||
jobs: jobLogs
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
check_suite_id,
|
|
||||||
workflow_runs: logsForRuns
|
|
||||||
};
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// utils/shell.ts
|
// utils/shell.ts
|
||||||
import { spawnSync as spawnSync4 } from "node:child_process";
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
||||||
function $(cmd, args3, options) {
|
function $(cmd, args3, options) {
|
||||||
@@ -124020,6 +123958,7 @@ function CheckoutPrTool(ctx) {
|
|||||||
} else {
|
} else {
|
||||||
$("git", ["config", `branch.${headBranch}.pushRemote`, "origin"]);
|
$("git", ["config", `branch.${headBranch}.pushRemote`, "origin"]);
|
||||||
}
|
}
|
||||||
|
ctx.toolState.prNumber = pull_number;
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
number: pr.data.number,
|
number: pr.data.number,
|
||||||
@@ -124035,6 +123974,89 @@ function CheckoutPrTool(ctx) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mcp/checkSuite.ts
|
||||||
|
var GetCheckSuiteLogs = type({
|
||||||
|
check_suite_id: type.number.describe("the id from check_suite.id")
|
||||||
|
});
|
||||||
|
function GetCheckSuiteLogsTool(ctx) {
|
||||||
|
return tool({
|
||||||
|
name: "get_check_suite_logs",
|
||||||
|
description: "get workflow run logs for a failed check suite. pass check_suite.id from the webhook payload.",
|
||||||
|
parameters: GetCheckSuiteLogs,
|
||||||
|
execute: execute(ctx, async ({ check_suite_id }) => {
|
||||||
|
const workflowRuns = await ctx.octokit.paginate(
|
||||||
|
ctx.octokit.rest.actions.listWorkflowRunsForRepo,
|
||||||
|
{
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
check_suite_id,
|
||||||
|
per_page: 100
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const failedRuns = workflowRuns.filter((run2) => run2.conclusion === "failure");
|
||||||
|
if (failedRuns.length === 0) {
|
||||||
|
return {
|
||||||
|
check_suite_id,
|
||||||
|
message: "no failed workflow runs found for this check suite",
|
||||||
|
workflow_runs: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const logsForRuns = await Promise.all(
|
||||||
|
failedRuns.map(async (run2) => {
|
||||||
|
const jobs = await ctx.octokit.paginate(ctx.octokit.rest.actions.listJobsForWorkflowRun, {
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
run_id: run2.id
|
||||||
|
});
|
||||||
|
const jobLogs = await Promise.all(
|
||||||
|
jobs.map(async (job) => {
|
||||||
|
try {
|
||||||
|
const logsResponse = await ctx.octokit.rest.actions.downloadJobLogsForWorkflowRun({
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
job_id: job.id
|
||||||
|
});
|
||||||
|
const logsUrl = logsResponse.url;
|
||||||
|
const logsText = await fetch(logsUrl).then((r) => r.text());
|
||||||
|
return {
|
||||||
|
job_id: job.id,
|
||||||
|
job_name: job.name,
|
||||||
|
status: job.status,
|
||||||
|
conclusion: job.conclusion,
|
||||||
|
started_at: job.started_at,
|
||||||
|
completed_at: job.completed_at,
|
||||||
|
logs: logsText
|
||||||
|
};
|
||||||
|
} catch (error42) {
|
||||||
|
return {
|
||||||
|
job_id: job.id,
|
||||||
|
job_name: job.name,
|
||||||
|
status: job.status,
|
||||||
|
conclusion: job.conclusion,
|
||||||
|
started_at: job.started_at,
|
||||||
|
completed_at: job.completed_at,
|
||||||
|
error: `failed to fetch logs: ${error42}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
workflow_run_id: run2.id,
|
||||||
|
workflow_name: run2.name,
|
||||||
|
html_url: run2.html_url,
|
||||||
|
conclusion: run2.conclusion,
|
||||||
|
jobs: jobLogs
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
check_suite_id,
|
||||||
|
workflow_runs: logsForRuns
|
||||||
|
};
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// mcp/debug.ts
|
// mcp/debug.ts
|
||||||
var DebugShellCommand = type({});
|
var DebugShellCommand = type({});
|
||||||
var DebugShellCommandTool = tool({
|
var DebugShellCommandTool = tool({
|
||||||
@@ -124254,6 +124276,7 @@ function GetIssueCommentsTool(ctx) {
|
|||||||
description: "Get all comments for a GitHub issue. Returns all comments including the issue body and all subsequent discussion comments.",
|
description: "Get all comments for a GitHub issue. Returns all comments including the issue body and all subsequent discussion comments.",
|
||||||
parameters: GetIssueComments,
|
parameters: GetIssueComments,
|
||||||
execute: execute(ctx, async ({ issue_number }) => {
|
execute: execute(ctx, async ({ issue_number }) => {
|
||||||
|
ctx.toolState.issueNumber = issue_number;
|
||||||
const comments = await ctx.octokit.paginate(ctx.octokit.rest.issues.listComments, {
|
const comments = await ctx.octokit.paginate(ctx.octokit.rest.issues.listComments, {
|
||||||
owner: ctx.owner,
|
owner: ctx.owner,
|
||||||
repo: ctx.name,
|
repo: ctx.name,
|
||||||
@@ -124287,6 +124310,7 @@ function GetIssueEventsTool(ctx) {
|
|||||||
description: "Get timeline events for a GitHub issue that aren't reflected in the current state. Returns cross-references to other issues/PRs and commit references. Note: current labels, assignees, state, and milestone are already available via get_issue.",
|
description: "Get timeline events for a GitHub issue that aren't reflected in the current state. Returns cross-references to other issues/PRs and commit references. Note: current labels, assignees, state, and milestone are already available via get_issue.",
|
||||||
parameters: GetIssueEvents,
|
parameters: GetIssueEvents,
|
||||||
execute: execute(ctx, async ({ issue_number }) => {
|
execute: execute(ctx, async ({ issue_number }) => {
|
||||||
|
ctx.toolState.issueNumber = issue_number;
|
||||||
const events = await ctx.octokit.paginate(ctx.octokit.rest.issues.listEventsForTimeline, {
|
const events = await ctx.octokit.paginate(ctx.octokit.rest.issues.listEventsForTimeline, {
|
||||||
owner: ctx.owner,
|
owner: ctx.owner,
|
||||||
repo: ctx.name,
|
repo: ctx.name,
|
||||||
@@ -124364,6 +124388,7 @@ function IssueInfoTool(ctx) {
|
|||||||
issue_number
|
issue_number
|
||||||
});
|
});
|
||||||
const data = issue3.data;
|
const data = issue3.data;
|
||||||
|
ctx.toolState.issueNumber = issue_number;
|
||||||
const hints = [];
|
const hints = [];
|
||||||
if (data.comments > 0) {
|
if (data.comments > 0) {
|
||||||
hints.push("use get_issue_comments to retrieve all comments for this issue");
|
hints.push("use get_issue_comments to retrieve all comments for this issue");
|
||||||
@@ -124430,14 +124455,12 @@ var PullRequest = type({
|
|||||||
base: type.string.describe("the base branch to merge into (e.g., 'main')")
|
base: type.string.describe("the base branch to merge into (e.g., 'main')")
|
||||||
});
|
});
|
||||||
function buildPrBodyWithFooter(ctx, body) {
|
function buildPrBodyWithFooter(ctx, body) {
|
||||||
const repoContext = parseRepoContext();
|
|
||||||
const runId = process.env.GITHUB_RUN_ID;
|
|
||||||
const agentName = ctx.payload.agent;
|
const agentName = ctx.payload.agent;
|
||||||
const agentInfo = agentName ? agentsManifest[agentName] : null;
|
const agentInfo = agentName ? agentsManifest[agentName] : null;
|
||||||
const footer = buildPullfrogFooter({
|
const footer = buildPullfrogFooter({
|
||||||
triggeredBy: true,
|
triggeredBy: true,
|
||||||
agent: agentInfo ? { displayName: agentInfo.displayName, url: agentInfo.url } : void 0,
|
agent: agentInfo ? { displayName: agentInfo.displayName, url: agentInfo.url } : void 0,
|
||||||
workflowRun: runId ? { owner: repoContext.owner, repo: repoContext.name, runId } : void 0
|
workflowRun: ctx.runId ? { owner: ctx.owner, repo: ctx.name, runId: ctx.runId, jobId: ctx.jobId } : void 0
|
||||||
});
|
});
|
||||||
const bodyWithoutFooter = stripExistingFooter(body);
|
const bodyWithoutFooter = stripExistingFooter(body);
|
||||||
return `${bodyWithoutFooter}${footer}`;
|
return `${bodyWithoutFooter}${footer}`;
|
||||||
@@ -124499,7 +124522,7 @@ function PullRequestInfoTool(ctx) {
|
|||||||
pull_number
|
pull_number
|
||||||
});
|
});
|
||||||
const data = pr.data;
|
const data = pr.data;
|
||||||
const isFork = data.head.repo.full_name !== data.base.repo.full_name;
|
const isFork = data.head.repo?.full_name !== data.base.repo.full_name;
|
||||||
return {
|
return {
|
||||||
number: data.number,
|
number: data.number,
|
||||||
url: data.html_url,
|
url: data.html_url,
|
||||||
@@ -124516,6 +124539,186 @@ function PullRequestInfoTool(ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// mcp/review.ts
|
// mcp/review.ts
|
||||||
|
import { randomBytes } from "node:crypto";
|
||||||
|
import { writeFileSync as writeFileSync4 } from "node:fs";
|
||||||
|
import { join as join8 } from "node:path";
|
||||||
|
var ADD_PULL_REQUEST_REVIEW_THREAD = `
|
||||||
|
mutation AddPullRequestReviewThread($pullRequestReviewId: ID!, $path: String!, $line: Int!, $body: String!, $side: DiffSide) {
|
||||||
|
addPullRequestReviewThread(input: {
|
||||||
|
pullRequestReviewId: $pullRequestReviewId,
|
||||||
|
path: $path,
|
||||||
|
line: $line,
|
||||||
|
body: $body,
|
||||||
|
side: $side
|
||||||
|
}) {
|
||||||
|
thread {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
async function findPendingReview(ctx, pull_number) {
|
||||||
|
const reviews = await ctx.octokit.rest.pulls.listReviews({
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
pull_number,
|
||||||
|
per_page: 100
|
||||||
|
});
|
||||||
|
const pendingReview = reviews.data.find((r) => r.state === "PENDING");
|
||||||
|
if (pendingReview) {
|
||||||
|
return { id: pendingReview.id, node_id: pendingReview.node_id };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var StartReview = type({
|
||||||
|
pull_number: type.number.describe("The pull request number to review")
|
||||||
|
});
|
||||||
|
function StartReviewTool(ctx) {
|
||||||
|
return tool({
|
||||||
|
name: "start_review",
|
||||||
|
description: "Start a new review session for a pull request. Creates a scratchpad file for gathering thoughts and a pending review on GitHub. Must be called before add_review_comment.",
|
||||||
|
parameters: StartReview,
|
||||||
|
execute: execute(ctx, async ({ pull_number }) => {
|
||||||
|
if (ctx.toolState.review) {
|
||||||
|
throw new Error(
|
||||||
|
`Review session already in progress. Call submit_review first to finish it.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const pr = await ctx.octokit.rest.pulls.get({
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
pull_number
|
||||||
|
});
|
||||||
|
let reviewId;
|
||||||
|
let reviewNodeId;
|
||||||
|
log.debug(`creating pending review for PR #${pull_number}...`);
|
||||||
|
try {
|
||||||
|
const result = await ctx.octokit.rest.pulls.createReview({
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
pull_number,
|
||||||
|
commit_id: pr.data.head.sha
|
||||||
|
// no 'event' = PENDING review
|
||||||
|
});
|
||||||
|
reviewId = result.data.id;
|
||||||
|
reviewNodeId = result.data.node_id;
|
||||||
|
log.debug(`created new pending review: id=${reviewId}`);
|
||||||
|
} catch (error42) {
|
||||||
|
const errorMessage = error42 instanceof Error ? error42.message : String(error42);
|
||||||
|
log.debug(`createReview failed: ${errorMessage}`);
|
||||||
|
if (errorMessage.includes("pending review")) {
|
||||||
|
log.debug(`pending review already exists, fetching existing review...`);
|
||||||
|
const existing = await findPendingReview(ctx, pull_number);
|
||||||
|
if (!existing) {
|
||||||
|
throw new Error(
|
||||||
|
"GitHub says a pending review exists but we couldn't find it. Try again or check the PR reviews."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
reviewId = existing.id;
|
||||||
|
reviewNodeId = existing.node_id;
|
||||||
|
log.debug(`reusing existing pending review: id=${reviewId}`);
|
||||||
|
} else {
|
||||||
|
throw error42;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const scratchpadId = randomBytes(4).toString("hex");
|
||||||
|
const scratchpadPath = join8(ctx.sharedTempDir, `pullfrog-review-${scratchpadId}.md`);
|
||||||
|
const scratchpadContent = `# Review ${scratchpadId}
|
||||||
|
|
||||||
|
`;
|
||||||
|
writeFileSync4(scratchpadPath, scratchpadContent);
|
||||||
|
ctx.toolState.prNumber = pull_number;
|
||||||
|
ctx.toolState.review = {
|
||||||
|
nodeId: reviewNodeId,
|
||||||
|
id: reviewId
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
reviewId: scratchpadId,
|
||||||
|
scratchpadPath,
|
||||||
|
message: `Review session started. Use the scratchpad file to gather your thoughts, then call add_review_comment for each comment.`
|
||||||
|
};
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var AddReviewComment = type({
|
||||||
|
path: type.string.describe("The file path to comment on (relative to repo root)"),
|
||||||
|
line: type.number.describe(
|
||||||
|
"The line number in the file (use line numbers from the diff - the NEW file line number)"
|
||||||
|
),
|
||||||
|
body: type.string.describe("The comment text for this specific line"),
|
||||||
|
side: type.enumerated("LEFT", "RIGHT").describe("Side of the diff: LEFT (old code) or RIGHT (new code). Defaults to RIGHT.").optional()
|
||||||
|
});
|
||||||
|
function AddReviewCommentTool(ctx) {
|
||||||
|
return tool({
|
||||||
|
name: "add_review_comment",
|
||||||
|
description: "Add a comment to the current review session. Must call start_review first. Comments are stored in draft state until submit_review is called.",
|
||||||
|
parameters: AddReviewComment,
|
||||||
|
execute: execute(ctx, async ({ path: path4, line, body, side }) => {
|
||||||
|
if (!ctx.toolState.review) {
|
||||||
|
throw new Error("No review session started. Call start_review first.");
|
||||||
|
}
|
||||||
|
await ctx.octokit.graphql(
|
||||||
|
ADD_PULL_REQUEST_REVIEW_THREAD,
|
||||||
|
{
|
||||||
|
pullRequestReviewId: ctx.toolState.review.nodeId,
|
||||||
|
path: path4,
|
||||||
|
line,
|
||||||
|
body,
|
||||||
|
side: side || "RIGHT"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `Comment added to ${path4}:${line}`
|
||||||
|
};
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var SubmitReview = type({
|
||||||
|
body: type.string.describe(
|
||||||
|
"Review body text. Typically 1-3 sentences with high-level overview and urgency level. Action links are auto-appended."
|
||||||
|
).optional()
|
||||||
|
});
|
||||||
|
function SubmitReviewTool(ctx) {
|
||||||
|
return tool({
|
||||||
|
name: "submit_review",
|
||||||
|
description: "Submit the current review session. All comments added via add_review_comment will be published. Must call start_review first.",
|
||||||
|
parameters: SubmitReview,
|
||||||
|
execute: execute(ctx, async ({ body }) => {
|
||||||
|
if (!ctx.toolState.review) {
|
||||||
|
throw new Error("No review session started. Call start_review first.");
|
||||||
|
}
|
||||||
|
if (ctx.toolState.prNumber === void 0) {
|
||||||
|
throw new Error("No PR context. Call checkout_pr or start_review first.");
|
||||||
|
}
|
||||||
|
const reviewId = ctx.toolState.review.id;
|
||||||
|
const apiUrl = process.env.API_URL || "https://pullfrog.com";
|
||||||
|
const fixAllUrl = `${apiUrl}/trigger/${ctx.owner}/${ctx.name}/${ctx.toolState.prNumber}?action=fix&review_id=${reviewId}`;
|
||||||
|
const fixApprovedUrl = `${apiUrl}/trigger/${ctx.owner}/${ctx.name}/${ctx.toolState.prNumber}?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 bodyWithFooter = (body || "") + footer;
|
||||||
|
const result = await ctx.octokit.rest.pulls.submitReview({
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
pull_number: ctx.toolState.prNumber,
|
||||||
|
review_id: reviewId,
|
||||||
|
event: "COMMENT",
|
||||||
|
body: bodyWithFooter
|
||||||
|
});
|
||||||
|
delete ctx.toolState.review;
|
||||||
|
await deleteProgressComment(ctx);
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
reviewId: result.data.id,
|
||||||
|
html_url: result.data.html_url,
|
||||||
|
state: result.data.state
|
||||||
|
};
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
var Review = type({
|
var Review = type({
|
||||||
pull_number: type.number.describe("The pull request number to review"),
|
pull_number: type.number.describe("The pull request number to review"),
|
||||||
body: type.string.describe(
|
body: type.string.describe(
|
||||||
@@ -124540,71 +124743,6 @@ var Review = type({
|
|||||||
"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."
|
"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()
|
).optional()
|
||||||
});
|
});
|
||||||
function ReviewTool(ctx) {
|
|
||||||
return tool({
|
|
||||||
name: "submit_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.",
|
|
||||||
parameters: Review,
|
|
||||||
execute: execute(ctx, async ({ pull_number, body, commit_id, comments = [] }) => {
|
|
||||||
const pr = await ctx.octokit.rest.pulls.get({
|
|
||||||
owner: ctx.owner,
|
|
||||||
repo: ctx.name,
|
|
||||||
pull_number
|
|
||||||
});
|
|
||||||
const params = {
|
|
||||||
owner: ctx.owner,
|
|
||||||
repo: ctx.name,
|
|
||||||
pull_number,
|
|
||||||
event: "COMMENT"
|
|
||||||
};
|
|
||||||
if (body) params.body = body;
|
|
||||||
if (commit_id) {
|
|
||||||
params.commit_id = commit_id;
|
|
||||||
} else {
|
|
||||||
params.commit_id = pr.data.head.sha;
|
|
||||||
}
|
|
||||||
if (comments.length > 0) {
|
|
||||||
params.comments = comments.map((comment) => {
|
|
||||||
const reviewComment = {
|
|
||||||
...comment
|
|
||||||
};
|
|
||||||
reviewComment.side = comment.side || "RIGHT";
|
|
||||||
if (comment.start_line) {
|
|
||||||
reviewComment.start_line = comment.start_line;
|
|
||||||
reviewComment.start_side = comment.side || "RIGHT";
|
|
||||||
}
|
|
||||||
return reviewComment;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const result = await ctx.octokit.rest.pulls.createReview(params);
|
|
||||||
const reviewId = result.data.id;
|
|
||||||
const apiUrl = process.env.API_URL || "https://pullfrog.com";
|
|
||||||
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;
|
|
||||||
await ctx.octokit.rest.pulls.updateReview({
|
|
||||||
owner: ctx.owner,
|
|
||||||
repo: ctx.name,
|
|
||||||
pull_number,
|
|
||||||
review_id: reviewId,
|
|
||||||
body: updatedBody
|
|
||||||
});
|
|
||||||
await deleteProgressComment(ctx);
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
reviewId,
|
|
||||||
html_url: result.data.html_url,
|
|
||||||
state: result.data.state,
|
|
||||||
user: result.data.user?.login,
|
|
||||||
submitted_at: result.data.submitted_at
|
|
||||||
};
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// mcp/reviewComments.ts
|
// mcp/reviewComments.ts
|
||||||
var REVIEW_THREADS_QUERY = `
|
var REVIEW_THREADS_QUERY = `
|
||||||
@@ -124814,7 +124952,10 @@ async function startMcpHttpServer(ctx) {
|
|||||||
GetIssueCommentsTool(ctx),
|
GetIssueCommentsTool(ctx),
|
||||||
GetIssueEventsTool(ctx),
|
GetIssueEventsTool(ctx),
|
||||||
PullRequestTool(ctx),
|
PullRequestTool(ctx),
|
||||||
ReviewTool(ctx),
|
// ReviewTool(ctx),
|
||||||
|
StartReviewTool(ctx),
|
||||||
|
AddReviewCommentTool(ctx),
|
||||||
|
SubmitReviewTool(ctx),
|
||||||
PullRequestInfoTool(ctx),
|
PullRequestInfoTool(ctx),
|
||||||
CheckoutPrTool(ctx),
|
CheckoutPrTool(ctx),
|
||||||
GetReviewCommentsTool(ctx),
|
GetReviewCommentsTool(ctx),
|
||||||
@@ -124852,7 +124993,7 @@ async function startMcpHttpServer(ctx) {
|
|||||||
|
|
||||||
// prep/installNodeDependencies.ts
|
// prep/installNodeDependencies.ts
|
||||||
import { existsSync as existsSync4 } from "node:fs";
|
import { existsSync as existsSync4 } from "node:fs";
|
||||||
import { join as join8 } from "node:path";
|
import { join as join9 } from "node:path";
|
||||||
|
|
||||||
// ../node_modules/.pnpm/package-manager-detector@1.6.0/node_modules/package-manager-detector/dist/commands.mjs
|
// ../node_modules/.pnpm/package-manager-detector@1.6.0/node_modules/package-manager-detector/dist/commands.mjs
|
||||||
function dashDashArg(agent2, agentCommand) {
|
function dashDashArg(agent2, agentCommand) {
|
||||||
@@ -125175,7 +125316,7 @@ async function installPackageManager(name) {
|
|||||||
return result.stderr || `failed to install ${name}`;
|
return result.stderr || `failed to install ${name}`;
|
||||||
}
|
}
|
||||||
if (name === "deno") {
|
if (name === "deno") {
|
||||||
const denoPath = join8(process.env.HOME || "", ".deno", "bin");
|
const denoPath = join9(process.env.HOME || "", ".deno", "bin");
|
||||||
process.env.PATH = `${denoPath}:${process.env.PATH}`;
|
process.env.PATH = `${denoPath}:${process.env.PATH}`;
|
||||||
}
|
}
|
||||||
log.info(`\u2705 installed ${name}`);
|
log.info(`\u2705 installed ${name}`);
|
||||||
@@ -125184,7 +125325,7 @@ async function installPackageManager(name) {
|
|||||||
var installNodeDependencies = {
|
var installNodeDependencies = {
|
||||||
name: "installNodeDependencies",
|
name: "installNodeDependencies",
|
||||||
shouldRun: () => {
|
shouldRun: () => {
|
||||||
const packageJsonPath = join8(process.cwd(), "package.json");
|
const packageJsonPath = join9(process.cwd(), "package.json");
|
||||||
return existsSync4(packageJsonPath);
|
return existsSync4(packageJsonPath);
|
||||||
},
|
},
|
||||||
run: async () => {
|
run: async () => {
|
||||||
@@ -125246,7 +125387,7 @@ var installNodeDependencies = {
|
|||||||
|
|
||||||
// prep/installPythonDependencies.ts
|
// prep/installPythonDependencies.ts
|
||||||
import { existsSync as existsSync5 } from "node:fs";
|
import { existsSync as existsSync5 } from "node:fs";
|
||||||
import { join as join9 } from "node:path";
|
import { join as join10 } from "node:path";
|
||||||
var PYTHON_CONFIGS = [
|
var PYTHON_CONFIGS = [
|
||||||
{
|
{
|
||||||
file: "requirements.txt",
|
file: "requirements.txt",
|
||||||
@@ -125318,11 +125459,11 @@ var installPythonDependencies = {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const cwd2 = process.cwd();
|
const cwd2 = process.cwd();
|
||||||
return PYTHON_CONFIGS.some((config3) => existsSync5(join9(cwd2, config3.file)));
|
return PYTHON_CONFIGS.some((config3) => existsSync5(join10(cwd2, config3.file)));
|
||||||
},
|
},
|
||||||
run: async () => {
|
run: async () => {
|
||||||
const cwd2 = process.cwd();
|
const cwd2 = process.cwd();
|
||||||
const config3 = PYTHON_CONFIGS.find((c) => existsSync5(join9(cwd2, c.file)));
|
const config3 = PYTHON_CONFIGS.find((c) => existsSync5(join10(cwd2, c.file)));
|
||||||
if (!config3) {
|
if (!config3) {
|
||||||
return {
|
return {
|
||||||
language: "python",
|
language: "python",
|
||||||
@@ -125463,6 +125604,12 @@ function setupGitConfig() {
|
|||||||
cwd: repoDir,
|
cwd: repoDir,
|
||||||
stdio: "pipe"
|
stdio: "pipe"
|
||||||
});
|
});
|
||||||
|
if (!process.env.GITHUB_ACTIONS) {
|
||||||
|
execSync2('git config --local credential.helper ""', {
|
||||||
|
cwd: repoDir,
|
||||||
|
stdio: "pipe"
|
||||||
|
});
|
||||||
|
}
|
||||||
log.debug("setupGitConfig: \u2713 Git configuration set successfully (scoped to repo)");
|
log.debug("setupGitConfig: \u2713 Git configuration set successfully (scoped to repo)");
|
||||||
} catch (error42) {
|
} catch (error42) {
|
||||||
log.warning(
|
log.warning(
|
||||||
@@ -125522,6 +125669,7 @@ async function main(inputs) {
|
|||||||
payload = parsePayload(inputs);
|
payload = parsePayload(inputs);
|
||||||
const partialCtx = await initializeContext(inputs, payload);
|
const partialCtx = await initializeContext(inputs, payload);
|
||||||
const ctx = partialCtx;
|
const ctx = partialCtx;
|
||||||
|
ctx.toolState = {};
|
||||||
timer.checkpoint("initializeContext");
|
timer.checkpoint("initializeContext");
|
||||||
await setupGit(ctx);
|
await setupGit(ctx);
|
||||||
timer.checkpoint("setupGit");
|
timer.checkpoint("setupGit");
|
||||||
@@ -125707,7 +125855,7 @@ function resolveAgent({
|
|||||||
return { agentName, agent: agent2 };
|
return { agentName, agent: agent2 };
|
||||||
}
|
}
|
||||||
async function setupTempDirectory(ctx) {
|
async function setupTempDirectory(ctx) {
|
||||||
ctx.sharedTempDir = await mkdtemp2(join10(tmpdir2(), "pullfrog-"));
|
ctx.sharedTempDir = await mkdtemp2(join11(tmpdir2(), "pullfrog-"));
|
||||||
process.env.PULLFROG_TEMP_DIR = ctx.sharedTempDir;
|
process.env.PULLFROG_TEMP_DIR = ctx.sharedTempDir;
|
||||||
log.info(`\u{1F4C2} PULLFROG_TEMP_DIR has been created at ${ctx.sharedTempDir}`);
|
log.info(`\u{1F4C2} PULLFROG_TEMP_DIR has been created at ${ctx.sharedTempDir}`);
|
||||||
}
|
}
|
||||||
@@ -125731,18 +125879,17 @@ function parsePayload(inputs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function startMcpServer(ctx) {
|
async function startMcpServer(ctx) {
|
||||||
const runId = process.env.GITHUB_RUN_ID;
|
const runId = process.env.GITHUB_RUN_ID || "";
|
||||||
if (!runId) {
|
|
||||||
throw new Error("GITHUB_RUN_ID environment variable is required");
|
|
||||||
}
|
|
||||||
ctx.runId = runId;
|
ctx.runId = runId;
|
||||||
|
if (runId) {
|
||||||
const workflowRunInfo = await fetchWorkflowRunInfo(ctx.runId);
|
const workflowRunInfo = await fetchWorkflowRunInfo(ctx.runId);
|
||||||
if (workflowRunInfo.progressCommentId) {
|
if (workflowRunInfo.progressCommentId) {
|
||||||
process.env.PULLFROG_PROGRESS_COMMENT_ID = workflowRunInfo.progressCommentId;
|
process.env.PULLFROG_PROGRESS_COMMENT_ID = workflowRunInfo.progressCommentId;
|
||||||
log.info(`\u{1F4DD} Using pre-created progress comment: ${workflowRunInfo.progressCommentId}`);
|
log.info(`\u{1F4DD} Using pre-created progress comment: ${workflowRunInfo.progressCommentId}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const jobName = process.env.GITHUB_JOB;
|
const jobName = process.env.GITHUB_JOB;
|
||||||
if (jobName) {
|
if (jobName && runId) {
|
||||||
const jobs = await ctx.octokit.rest.actions.listJobsForWorkflowRun({
|
const jobs = await ctx.octokit.rest.actions.listJobsForWorkflowRun({
|
||||||
owner: ctx.owner,
|
owner: ctx.owner,
|
||||||
repo: ctx.name,
|
repo: ctx.name,
|
||||||
|
|||||||
Reference in New Issue
Block a user