diff --git a/agents/shared.ts b/agents/shared.ts index 572f8c0..7769a90 100644 --- a/agents/shared.ts +++ b/agents/shared.ts @@ -3,4 +3,6 @@ import { mcpServerName } from "../mcp/config.ts"; export const instructions = `- use the ${mcpServerName} MCP server to interact with github - if ${mcpServerName} is not available or doesn't include the functionality you need, describe why and bail - do not under any circumstances use the gh cli +- if prompted by a comment to respond to create a new issue, pr or anything else, after succeeding, + also respond to the original comment with a very brief message containing a link to it `; diff --git a/fixtures/basic.txt b/fixtures/basic.txt index a554619..4aef6c4 100644 --- a/fixtures/basic.txt +++ b/fixtures/basic.txt @@ -1 +1 @@ -add a comment containing your best frog joke to GitHub issue https://github.com/pullfrogai/scratch/issues/2 +create a PR implementing bogosort to https://github.com/pullfrogai/scratch/ diff --git a/mcp/issue.ts b/mcp/issue.ts new file mode 100644 index 0000000..20a108d --- /dev/null +++ b/mcp/issue.ts @@ -0,0 +1,70 @@ +import { type } from "arktype"; +import { getMcpContext, tool } from "./shared.ts"; + +export const Issue = type({ + title: type.string.describe("the title of the issue"), + body: type.string.describe("the body content of the issue"), + labels: type.string + .array() + .describe("optional array of label names to apply to the issue") + .optional(), + assignees: type.string + .array() + .describe("optional array of usernames to assign to the issue") + .optional(), +}); + +export const IssueTool = tool({ + name: "create_issue", + description: "Create a new GitHub issue", + parameters: Issue, + execute: async ({ title, body, labels, assignees }) => { + const ctx = getMcpContext(); + try { + const result = await ctx.octokit.rest.issues.create({ + owner: ctx.owner, + repo: ctx.name, + title: title, + body: body, + labels: labels ?? [], + assignees: assignees ?? [], + }); + + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: true, + issueId: result.data.id, + number: result.data.number, + url: result.data.html_url, + title: result.data.title, + state: result.data.state, + labels: result.data.labels?.map((label) => + typeof label === "string" ? label : label.name + ), + assignees: result.data.assignees?.map((assignee) => assignee.login), + }, + null, + 2 + ), + }, + ], + }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + return { + content: [ + { + type: "text", + text: `Error creating issue: ${errorMessage}`, + }, + ], + error: errorMessage, + isError: true, + }; + } + }, +}); diff --git a/mcp/pr.ts b/mcp/pr.ts new file mode 100644 index 0000000..7eba331 --- /dev/null +++ b/mcp/pr.ts @@ -0,0 +1,68 @@ +import { execSync } from "node:child_process"; +import { type } from "arktype"; +import { getMcpContext, tool } from "./shared.ts"; + +export const PullRequest = type({ + title: type.string.describe("the title of the pull request"), + body: type.string.describe("the body content of the pull request"), + base: type.string.describe("the base branch to merge into (e.g., 'main')"), +}); + +export const PullRequestTool = tool({ + name: "create_pull_request", + description: "Create a pull request from the current branch", + parameters: PullRequest, + execute: async ({ title, body, base }) => { + const ctx = getMcpContext(); + try { + // Get the current branch name + const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + }).trim(); + + console.log(`Current branch: ${currentBranch}`); + + const result = await ctx.octokit.rest.pulls.create({ + owner: ctx.owner, + repo: ctx.name, + title: title, + body: body, + head: currentBranch, + base: base, + }); + + return { + content: [ + { + type: "text", + text: JSON.stringify( + { + success: true, + pullRequestId: result.data.id, + number: result.data.number, + url: result.data.html_url, + title: result.data.title, + head: result.data.head.ref, + base: result.data.base.ref, + }, + null, + 2 + ), + }, + ], + }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + return { + content: [ + { + type: "text", + text: `Error creating pull request: ${errorMessage}`, + }, + ], + error: errorMessage, + isError: true, + }; + } + }, +}); diff --git a/mcp/server.ts b/mcp/server.ts index 9cc4306..debb792 100644 --- a/mcp/server.ts +++ b/mcp/server.ts @@ -2,6 +2,8 @@ // Minimal GitHub Issue Comment MCP Server import { FastMCP } from "fastmcp"; import { CommentTool } from "./comment.ts"; +import { IssueTool } from "./issue.ts"; +import { PullRequestTool } from "./pr.ts"; import { addTools } from "./shared.ts"; const server = new FastMCP({ @@ -9,6 +11,6 @@ const server = new FastMCP({ version: "0.0.1", }); -addTools(server, [CommentTool]); +addTools(server, [CommentTool, IssueTool, PullRequestTool]); server.start(); diff --git a/package.json b/package.json index 9d2c35b..09a0cd3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@pullfrog/action", - "version": "0.0.52", + "version": "0.0.53", "type": "module", "files": [ "index.js",