Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c6c1210fa0 |
+8
-1
@@ -165,8 +165,15 @@ export class ClaudeAgent implements Agent {
|
|||||||
*/
|
*/
|
||||||
function processJSONChunk(chunk: string, agent?: ClaudeAgent): void {
|
function processJSONChunk(chunk: string, agent?: ClaudeAgent): void {
|
||||||
try {
|
try {
|
||||||
|
// Skip debug lines that start with [DEBUG] or [debug]
|
||||||
|
const trimmedChunk = chunk.trim();
|
||||||
|
if (trimmedChunk.startsWith("[DEBUG]") || trimmedChunk.startsWith("[debug]")) {
|
||||||
|
console.log(chunk);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log(chunk);
|
console.log(chunk);
|
||||||
const parsedChunk = JSON.parse(chunk.trim());
|
const parsedChunk = JSON.parse(trimmedChunk);
|
||||||
|
|
||||||
switch (parsedChunk.type) {
|
switch (parsedChunk.type) {
|
||||||
case "system":
|
case "system":
|
||||||
|
|||||||
+15
-41
@@ -1,5 +1,5 @@
|
|||||||
import { type } from "arktype";
|
import { type } from "arktype";
|
||||||
import { getMcpContext, tool } from "./shared.ts";
|
import { contextualize, tool } from "./shared.ts";
|
||||||
|
|
||||||
export const Comment = type({
|
export const Comment = type({
|
||||||
issueNumber: type.number.describe("the issue number to comment on"),
|
issueNumber: type.number.describe("the issue number to comment on"),
|
||||||
@@ -10,45 +10,19 @@ export const CommentTool = tool({
|
|||||||
name: "create_issue_comment",
|
name: "create_issue_comment",
|
||||||
description: "Create a comment on a GitHub issue",
|
description: "Create a comment on a GitHub issue",
|
||||||
parameters: Comment,
|
parameters: Comment,
|
||||||
execute: async ({ issueNumber, body }) => {
|
execute: contextualize(async ({ issueNumber, body }, ctx) => {
|
||||||
const ctx = getMcpContext();
|
const result = await ctx.octokit.rest.issues.createComment({
|
||||||
try {
|
owner: ctx.owner,
|
||||||
const result = await ctx.octokit.rest.issues.createComment({
|
repo: ctx.name,
|
||||||
owner: ctx.owner,
|
issue_number: issueNumber,
|
||||||
repo: ctx.name,
|
body: body,
|
||||||
issue_number: issueNumber,
|
});
|
||||||
body: body,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [
|
success: true,
|
||||||
{
|
commentId: result.data.id,
|
||||||
type: "text",
|
url: result.data.html_url,
|
||||||
text: JSON.stringify(
|
body: result.data.body,
|
||||||
{
|
};
|
||||||
success: true,
|
}),
|
||||||
commentId: result.data.id,
|
|
||||||
url: result.data.html_url,
|
|
||||||
body: result.data.body,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text: `Error creating comment: ${errorMessage}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
error: errorMessage,
|
|
||||||
isError: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
+21
-49
@@ -1,5 +1,5 @@
|
|||||||
import { type } from "arktype";
|
import { type } from "arktype";
|
||||||
import { getMcpContext, tool } from "./shared.ts";
|
import { contextualize, tool } from "./shared.ts";
|
||||||
|
|
||||||
export const Issue = type({
|
export const Issue = type({
|
||||||
title: type.string.describe("the title of the issue"),
|
title: type.string.describe("the title of the issue"),
|
||||||
@@ -18,53 +18,25 @@ export const IssueTool = tool({
|
|||||||
name: "create_issue",
|
name: "create_issue",
|
||||||
description: "Create a new GitHub issue",
|
description: "Create a new GitHub issue",
|
||||||
parameters: Issue,
|
parameters: Issue,
|
||||||
execute: async ({ title, body, labels, assignees }) => {
|
execute: contextualize(async ({ title, body, labels, assignees }, ctx) => {
|
||||||
const ctx = getMcpContext();
|
const result = await ctx.octokit.rest.issues.create({
|
||||||
try {
|
owner: ctx.owner,
|
||||||
const result = await ctx.octokit.rest.issues.create({
|
repo: ctx.name,
|
||||||
owner: ctx.owner,
|
title: title,
|
||||||
repo: ctx.name,
|
body: body,
|
||||||
title: title,
|
labels: labels ?? [],
|
||||||
body: body,
|
assignees: assignees ?? [],
|
||||||
labels: labels ?? [],
|
});
|
||||||
assignees: assignees ?? [],
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [
|
success: true,
|
||||||
{
|
issueId: result.data.id,
|
||||||
type: "text",
|
number: result.data.number,
|
||||||
text: JSON.stringify(
|
url: result.data.html_url,
|
||||||
{
|
title: result.data.title,
|
||||||
success: true,
|
state: result.data.state,
|
||||||
issueId: result.data.id,
|
labels: result.data.labels?.map((label) => (typeof label === "string" ? label : label.name)),
|
||||||
number: result.data.number,
|
assignees: result.data.assignees?.map((assignee) => assignee.login),
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import { type } from "arktype";
|
import { type } from "arktype";
|
||||||
import { getMcpContext, tool } from "./shared.ts";
|
import { contextualize, tool } from "./shared.ts";
|
||||||
|
|
||||||
export const PullRequest = type({
|
export const PullRequest = type({
|
||||||
title: type.string.describe("the title of the pull request"),
|
title: type.string.describe("the title of the pull request"),
|
||||||
@@ -12,57 +12,31 @@ export const PullRequestTool = tool({
|
|||||||
name: "create_pull_request",
|
name: "create_pull_request",
|
||||||
description: "Create a pull request from the current branch",
|
description: "Create a pull request from the current branch",
|
||||||
parameters: PullRequest,
|
parameters: PullRequest,
|
||||||
execute: async ({ title, body, base }) => {
|
execute: contextualize(async ({ title, body, base }, ctx) => {
|
||||||
const ctx = getMcpContext();
|
// Get the current branch name
|
||||||
try {
|
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", {
|
||||||
// Get the current branch name
|
encoding: "utf8",
|
||||||
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", {
|
}).trim();
|
||||||
encoding: "utf8",
|
|
||||||
}).trim();
|
|
||||||
|
|
||||||
console.log(`Current branch: ${currentBranch}`);
|
console.log(`Current branch: ${currentBranch}`);
|
||||||
|
|
||||||
const result = await ctx.octokit.rest.pulls.create({
|
const result = await ctx.octokit.rest.pulls.create({
|
||||||
owner: ctx.owner,
|
owner: ctx.owner,
|
||||||
repo: ctx.name,
|
repo: ctx.name,
|
||||||
title: title,
|
title: title,
|
||||||
body: body,
|
body: body,
|
||||||
head: currentBranch,
|
head: currentBranch,
|
||||||
base: base,
|
base: base,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [
|
success: true,
|
||||||
{
|
pullRequestId: result.data.id,
|
||||||
type: "text",
|
number: result.data.number,
|
||||||
text: JSON.stringify(
|
url: result.data.html_url,
|
||||||
{
|
title: result.data.title,
|
||||||
success: true,
|
head: result.data.head.ref,
|
||||||
pullRequestId: result.data.id,
|
base: result.data.base.ref,
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,6 +4,15 @@ import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|||||||
import type { FastMCP, Tool } from "fastmcp";
|
import type { FastMCP, Tool } from "fastmcp";
|
||||||
import { parseRepoContext, type RepoContext } from "../utils/github.ts";
|
import { parseRepoContext, type RepoContext } from "../utils/github.ts";
|
||||||
|
|
||||||
|
export interface ToolResult {
|
||||||
|
content: {
|
||||||
|
type: "text";
|
||||||
|
text: string;
|
||||||
|
}[];
|
||||||
|
error?: string;
|
||||||
|
isError?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const getMcpContext = cached((): McpContext => {
|
export const getMcpContext = cached((): McpContext => {
|
||||||
const githubInstallationToken = process.env.GITHUB_INSTALLATION_TOKEN;
|
const githubInstallationToken = process.env.GITHUB_INSTALLATION_TOKEN;
|
||||||
if (!githubInstallationToken) {
|
if (!githubInstallationToken) {
|
||||||
@@ -30,3 +39,40 @@ export const addTools = (server: FastMCP, tools: Tool<any, any>[]) => {
|
|||||||
}
|
}
|
||||||
return server;
|
return server;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const contextualize =
|
||||||
|
<T>(executor: (params: T, ctx: McpContext) => Promise<Record<string, any>>) =>
|
||||||
|
async (params: T): Promise<ToolResult> => {
|
||||||
|
try {
|
||||||
|
const ctx = getMcpContext();
|
||||||
|
const result = await executor(params, ctx);
|
||||||
|
return handleToolSuccess(result);
|
||||||
|
} catch (error) {
|
||||||
|
return handleToolError(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleToolSuccess = (data: Record<string, any>): ToolResult => {
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: JSON.stringify(data, null, 2),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleToolError = (error: unknown): ToolResult => {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `Error: ${errorMessage}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: errorMessage,
|
||||||
|
isError: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@pullfrog/action",
|
"name": "@pullfrog/action",
|
||||||
"version": "0.0.53",
|
"version": "0.0.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"index.js",
|
"index.js",
|
||||||
|
|||||||
Reference in New Issue
Block a user