Add additional tools
This commit is contained in:
@@ -44,9 +44,6 @@ Do not try to handle github auth- treat ${ghPullfrogMcpName} as a black box that
|
|||||||
When using ${ghPullfrogMcpName}, use the tools to comment and interact in a way that a real member of the team would.
|
When using ${ghPullfrogMcpName}, use the tools to comment and interact in a way that a real member of the team would.
|
||||||
Ensure after your edits are done, your final comments do not contain intermediate reasoning or context, e.g. "I'll respond to the question."
|
Ensure after your edits are done, your final comments do not contain intermediate reasoning or context, e.g. "I'll respond to the question."
|
||||||
|
|
||||||
For CI failures:
|
|
||||||
- Use ${ghPullfrogMcpName}/get_check_suite_logs with check_suite.id to get all failed workflow logs
|
|
||||||
|
|
||||||
## Mode Selection
|
## Mode Selection
|
||||||
|
|
||||||
Before starting any work, you must first determine which mode to use by examining the request and calling ${ghPullfrogMcpName}/select_mode.
|
Before starting any work, you must first determine which mode to use by examining the request and calling ${ghPullfrogMcpName}/select_mode.
|
||||||
|
|||||||
@@ -27,6 +27,53 @@ await mcp.call("gh-pullfrog/get_check_suite_logs", {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### review tools
|
||||||
|
|
||||||
|
#### `get_review_comments`
|
||||||
|
get all line-by-line comments for a specific pull request review.
|
||||||
|
|
||||||
|
**parameters:**
|
||||||
|
- `pull_number` (number): the pull request number
|
||||||
|
- `review_id` (number): the id from review.id in the webhook payload
|
||||||
|
|
||||||
|
**replaces:** `gh api repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments`
|
||||||
|
|
||||||
|
**returns:**
|
||||||
|
array of review comments including:
|
||||||
|
- file path, line number, comment body
|
||||||
|
- side (LEFT/RIGHT) and position in diff
|
||||||
|
- user, timestamps, html_url
|
||||||
|
- in_reply_to_id for threaded comments
|
||||||
|
|
||||||
|
**example:**
|
||||||
|
```typescript
|
||||||
|
// when handling a pull_request_review_submitted webhook
|
||||||
|
await mcp.call("gh-pullfrog/get_review_comments", {
|
||||||
|
pull_number: 47,
|
||||||
|
review_id: review.id
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `list_pull_request_reviews`
|
||||||
|
list all reviews for a pull request.
|
||||||
|
|
||||||
|
**parameters:**
|
||||||
|
- `pull_number` (number): the pull request number
|
||||||
|
|
||||||
|
**replaces:** `gh api repos/{owner}/{repo}/pulls/{pull_number}/reviews`
|
||||||
|
|
||||||
|
**returns:**
|
||||||
|
array of reviews with:
|
||||||
|
- review id, body, state (approved/changes_requested/commented)
|
||||||
|
- user, commit_id, submitted_at, html_url
|
||||||
|
|
||||||
|
**example:**
|
||||||
|
```typescript
|
||||||
|
await mcp.call("gh-pullfrog/list_pull_request_reviews", {
|
||||||
|
pull_number: 47
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### other tools
|
### other tools
|
||||||
|
|
||||||
see individual files for documentation on other tools:
|
see individual files for documentation on other tools:
|
||||||
|
|||||||
+12
-11
@@ -12,17 +12,18 @@ export const GetCheckSuiteLogsTool = tool({
|
|||||||
parameters: GetCheckSuiteLogs,
|
parameters: GetCheckSuiteLogs,
|
||||||
execute: contextualize(async ({ check_suite_id }, ctx) => {
|
execute: contextualize(async ({ check_suite_id }, ctx) => {
|
||||||
// get workflow runs for this specific check suite
|
// get workflow runs for this specific check suite
|
||||||
const runsResponse = await ctx.octokit.rest.actions.listWorkflowRunsForRepo({
|
const workflowRuns = await ctx.octokit.paginate(
|
||||||
owner: ctx.owner,
|
ctx.octokit.rest.actions.listWorkflowRunsForRepo,
|
||||||
repo: ctx.name,
|
{
|
||||||
check_suite_id,
|
owner: ctx.owner,
|
||||||
per_page: 100,
|
repo: ctx.name,
|
||||||
});
|
check_suite_id,
|
||||||
|
per_page: 100,
|
||||||
const failedRuns = runsResponse.data.workflow_runs.filter(
|
}
|
||||||
(run) => run.conclusion === "failure"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const failedRuns = workflowRuns.filter((run) => run.conclusion === "failure");
|
||||||
|
|
||||||
if (failedRuns.length === 0) {
|
if (failedRuns.length === 0) {
|
||||||
return {
|
return {
|
||||||
check_suite_id,
|
check_suite_id,
|
||||||
@@ -34,14 +35,14 @@ export const GetCheckSuiteLogsTool = tool({
|
|||||||
// get logs for each failed run
|
// get logs for each failed run
|
||||||
const logsForRuns = await Promise.all(
|
const logsForRuns = await Promise.all(
|
||||||
failedRuns.map(async (run) => {
|
failedRuns.map(async (run) => {
|
||||||
const jobsResponse = await ctx.octokit.rest.actions.listJobsForWorkflowRun({
|
const jobs = await ctx.octokit.paginate(ctx.octokit.rest.actions.listJobsForWorkflowRun, {
|
||||||
owner: ctx.owner,
|
owner: ctx.owner,
|
||||||
repo: ctx.name,
|
repo: ctx.name,
|
||||||
run_id: run.id,
|
run_id: run.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const jobLogs = await Promise.all(
|
const jobLogs = await Promise.all(
|
||||||
jobsResponse.data.jobs.map(async (job) => {
|
jobs.map(async (job) => {
|
||||||
try {
|
try {
|
||||||
const logsResponse = await ctx.octokit.rest.actions.downloadJobLogsForWorkflowRun({
|
const logsResponse = await ctx.octokit.rest.actions.downloadJobLogsForWorkflowRun({
|
||||||
owner: ctx.owner,
|
owner: ctx.owner,
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { execSync } from "node:child_process";
|
||||||
|
import { type } from "arktype";
|
||||||
|
import { contextualize, tool } from "./shared.ts";
|
||||||
|
|
||||||
|
export const ListFiles = type({
|
||||||
|
path: type.string
|
||||||
|
.describe("The path to list files from (defaults to current directory)")
|
||||||
|
.default("."),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ListFilesTool = tool({
|
||||||
|
name: "list_files",
|
||||||
|
description:
|
||||||
|
"List files in the repository using git ls-files. Useful for discovering the file structure and locating files.",
|
||||||
|
parameters: ListFiles,
|
||||||
|
execute: contextualize(async ({ path }) => {
|
||||||
|
try {
|
||||||
|
// Use git ls-files to list tracked files
|
||||||
|
// This respects .gitignore and gives a clean list of source files
|
||||||
|
const command = path === "." ? "git ls-files" : `git ls-files ${path}`;
|
||||||
|
const output = execSync(command, { encoding: "utf-8" });
|
||||||
|
const files = output.split("\n").filter((f) => f.trim() !== "");
|
||||||
|
|
||||||
|
if (files.length === 0) {
|
||||||
|
// Fallback for non-git environments or untracked files
|
||||||
|
const findCmd = `find ${path} -maxdepth 3 -not -path '*/.*' -type f`;
|
||||||
|
const findOutput = execSync(findCmd, { encoding: "utf-8" });
|
||||||
|
return {
|
||||||
|
files: findOutput.split("\n").filter((f) => f.trim() !== ""),
|
||||||
|
method: "find",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { files, method: "git" };
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
return {
|
||||||
|
error: `Failed to list files: ${errorMessage}`,
|
||||||
|
hint: "Try using a specific path if the repository root is not the current directory.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
+2
-1
@@ -36,7 +36,8 @@ export const PullRequestInfoTool = tool({
|
|||||||
execSync(`git fetch origin ${headBranch}`, { stdio: "inherit" });
|
execSync(`git fetch origin ${headBranch}`, { stdio: "inherit" });
|
||||||
|
|
||||||
log.info(`Checking out PR branch: origin/${headBranch}`);
|
log.info(`Checking out PR branch: origin/${headBranch}`);
|
||||||
execSync(`git checkout origin/${headBranch}`, { stdio: "inherit" });
|
// check out a local branch tracking the remote branch so we can push changes
|
||||||
|
execSync(`git checkout -B ${headBranch} origin/${headBranch}`, { stdio: "inherit" });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
number: data.number,
|
number: data.number,
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { type } from "arktype";
|
||||||
|
import { contextualize, tool } from "./shared.ts";
|
||||||
|
|
||||||
|
export const GetReviewComments = type({
|
||||||
|
pull_number: type.number.describe("The pull request number"),
|
||||||
|
review_id: type.number.describe("The review ID to get comments for"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetReviewCommentsTool = tool({
|
||||||
|
name: "get_review_comments",
|
||||||
|
description:
|
||||||
|
"Get all review comments for a specific pull request review. Returns line-by-line comments that were left on specific code locations.",
|
||||||
|
parameters: GetReviewComments,
|
||||||
|
execute: contextualize(async ({ pull_number, review_id }, ctx) => {
|
||||||
|
const comments = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listCommentsForReview, {
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
pull_number,
|
||||||
|
review_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
review_id,
|
||||||
|
pull_number,
|
||||||
|
comments: comments.map((comment) => ({
|
||||||
|
id: comment.id,
|
||||||
|
body: comment.body,
|
||||||
|
path: comment.path,
|
||||||
|
line: comment.line,
|
||||||
|
side: comment.side,
|
||||||
|
start_line: comment.start_line,
|
||||||
|
start_side: comment.start_side,
|
||||||
|
user: typeof comment.user === "string" ? comment.user : comment.user?.login,
|
||||||
|
created_at: comment.created_at,
|
||||||
|
updated_at: comment.updated_at,
|
||||||
|
html_url: comment.html_url,
|
||||||
|
in_reply_to_id: comment.in_reply_to_id,
|
||||||
|
diff_hunk: comment.diff_hunk,
|
||||||
|
reactions: comment.reactions,
|
||||||
|
})),
|
||||||
|
count: comments.length,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ListPullRequestReviews = type({
|
||||||
|
pull_number: type.number.describe("The pull request number to list reviews for"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ListPullRequestReviewsTool = tool({
|
||||||
|
name: "list_pull_request_reviews",
|
||||||
|
description:
|
||||||
|
"List all reviews for a pull request. Returns all reviews including approvals, request changes, and comments.",
|
||||||
|
parameters: ListPullRequestReviews,
|
||||||
|
execute: contextualize(async ({ pull_number }, ctx) => {
|
||||||
|
const reviews = await ctx.octokit.paginate(ctx.octokit.rest.pulls.listReviews, {
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
pull_number,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
pull_number,
|
||||||
|
reviews: reviews.map((review) => ({
|
||||||
|
id: review.id,
|
||||||
|
body: review.body,
|
||||||
|
state: review.state,
|
||||||
|
user: review.user?.login,
|
||||||
|
commit_id: review.commit_id,
|
||||||
|
submitted_at: review.submitted_at,
|
||||||
|
html_url: review.html_url,
|
||||||
|
})),
|
||||||
|
count: reviews.length,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -6,10 +6,12 @@ import {
|
|||||||
EditCommentTool,
|
EditCommentTool,
|
||||||
UpdateWorkingCommentTool,
|
UpdateWorkingCommentTool,
|
||||||
} from "./comment.ts";
|
} from "./comment.ts";
|
||||||
|
// import { ListFilesTool } from "./files.ts";
|
||||||
import { IssueTool } from "./issue.ts";
|
import { IssueTool } from "./issue.ts";
|
||||||
import { PullRequestTool } from "./pr.ts";
|
import { PullRequestTool } from "./pr.ts";
|
||||||
import { PullRequestInfoTool } from "./prInfo.ts";
|
import { PullRequestInfoTool } from "./prInfo.ts";
|
||||||
import { ReviewTool } from "./review.ts";
|
import { ReviewTool } from "./review.ts";
|
||||||
|
import { GetReviewCommentsTool, ListPullRequestReviewsTool } from "./reviewComments.ts";
|
||||||
import { SelectModeTool } from "./selectMode.ts";
|
import { SelectModeTool } from "./selectMode.ts";
|
||||||
import { addTools } from "./shared.ts";
|
import { addTools } from "./shared.ts";
|
||||||
|
|
||||||
@@ -25,9 +27,12 @@ addTools(server, [
|
|||||||
CreateWorkingCommentTool,
|
CreateWorkingCommentTool,
|
||||||
UpdateWorkingCommentTool,
|
UpdateWorkingCommentTool,
|
||||||
IssueTool,
|
IssueTool,
|
||||||
|
// ListFilesTool,
|
||||||
PullRequestTool,
|
PullRequestTool,
|
||||||
ReviewTool,
|
ReviewTool,
|
||||||
PullRequestInfoTool,
|
PullRequestInfoTool,
|
||||||
|
GetReviewCommentsTool,
|
||||||
|
ListPullRequestReviewsTool,
|
||||||
GetCheckSuiteLogsTool,
|
GetCheckSuiteLogsTool,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user