From 517123287854e96b455c6b053b8ef68a98ec4bf8 Mon Sep 17 00:00:00 2001 From: Mark Wylde Date: Sat, 31 May 2025 00:56:07 +0100 Subject: [PATCH] Attempt to make this work --- src/create-prompt/index.ts | 8 +- src/entrypoints/update-comment-link.ts | 27 +- src/github/operations/branch-cleanup.ts | 29 ++- src/github/operations/comments/common.ts | 2 +- src/github/utils/local-git.ts | 28 ++- src/mcp/gitea-mcp-server.ts | 300 +++++++++++++++-------- src/mcp/install-mcp-server.ts | 19 +- src/mcp/local-git-ops-server.ts | 24 +- 8 files changed, 300 insertions(+), 137 deletions(-) diff --git a/src/create-prompt/index.ts b/src/create-prompt/index.ts index c7a5e18..a0d74e9 100644 --- a/src/create-prompt/index.ts +++ b/src/create-prompt/index.ts @@ -536,13 +536,13 @@ ${context.directPrompt ? ` - DIRECT INSTRUCTION: A direct instruction was prov - Commit changes using mcp__local_git_ops__commit_files to the existing branch (works for both new and existing files). - Use mcp__local_git_ops__commit_files to commit files atomically in a single commit (supports single or multiple files). - CRITICAL: After committing, you MUST push the branch to the remote repository using mcp__local_git_ops__push_branch - - When pushing changes with this tool and TRIGGER_USERNAME is not "Unknown", include a "Co-authored-by: ${context.triggerUsername} <${context.triggerUsername}@users.noreply.github.com>" line in the commit message.` + - When pushing changes with this tool and TRIGGER_USERNAME is not "Unknown", include a "Co-authored-by: ${context.triggerUsername} <${context.triggerUsername}@users.noreply.local>" line in the commit message.` : ` - You are already on the correct branch (${eventData.claudeBranch || "the PR branch"}). Do not create a new branch. - Commit changes using mcp__local_git_ops__commit_files (works for both new and existing files) - Use mcp__local_git_ops__commit_files to commit files atomically in a single commit (supports single or multiple files). - CRITICAL: After committing, you MUST push the branch to the remote repository using mcp__local_git_ops__push_branch - - When pushing changes and TRIGGER_USERNAME is not "Unknown", include a "Co-authored-by: ${context.triggerUsername} <${context.triggerUsername}@users.noreply.github.com>" line in the commit message. + - When pushing changes and TRIGGER_USERNAME is not "Unknown", include a "Co-authored-by: ${context.triggerUsername} <${context.triggerUsername}@users.noreply.local>" line in the commit message. ${ eventData.claudeBranch ? `- Provide a URL to create a PR manually in this format: @@ -584,7 +584,7 @@ Important Notes: - Never create new comments. Only update the existing comment using ${eventData.eventName === "pull_request_review_comment" ? "mcp__github__update_pull_request_comment" : "mcp__github__update_issue_comment"} with comment_id: ${context.claudeCommentId}. - This includes ALL responses: code reviews, answers to questions, progress updates, and final results.${eventData.isPR ? "\n- PR CRITICAL: After reading files and forming your response, you MUST post it by calling mcp__github__update_issue_comment. Do NOT just respond with a normal response, the user will not see it." : ""} - You communicate exclusively by editing your single comment - not through any other means. -- Use this spinner HTML when work is in progress: +- Use this spinner HTML when work is in progress: ${eventData.isPR && !eventData.claudeBranch ? `- Always push to the existing branch when triggered on a PR.` : `- IMPORTANT: You are already on the correct branch (${eventData.claudeBranch || "the created branch"}). Never create new branches when triggered on issues or closed/merged PRs.`} - Use mcp__local_git_ops__commit_files for making commits (works for both new and existing files, single or multiple). Use mcp__local_git_ops__delete_files for deleting files (supports deleting single or multiple files atomically), or mcp__github__delete_file for deleting a single file. Edit files locally, and the tool will read the content from the same path on disk. Tool usage examples: @@ -621,7 +621,7 @@ What You CANNOT Do: - View CI/CD results or workflow run outputs (cannot access GitHub Actions logs or test results) When users ask you to perform actions you cannot do, politely explain the limitation and, when applicable, direct them to the FAQ for more information and workarounds: -"I'm unable to [specific action] due to [reason]. You can find more information and potential workarounds in the [FAQ](https://github.com/anthropics/claude-code-action/blob/main/FAQ.md)." +"I'm unable to [specific action] due to [reason]. Please check the documentation for more information and potential workarounds." If a user asks for something outside these capabilities (and you have no other tools provided), politely explain that you cannot perform that action and suggest an alternative approach if possible. diff --git a/src/entrypoints/update-comment-link.ts b/src/entrypoints/update-comment-link.ts index b30fca7..d00fcc4 100644 --- a/src/entrypoints/update-comment-link.ts +++ b/src/entrypoints/update-comment-link.ts @@ -12,7 +12,12 @@ import { } from "../github/context"; import { GITEA_SERVER_URL } from "../github/api/config"; import { checkAndDeleteEmptyBranch } from "../github/operations/branch-cleanup"; -import { branchHasChanges, fetchBranch, branchExists, remoteBranchExists } from "../github/utils/local-git"; +import { + branchHasChanges, + fetchBranch, + branchExists, + remoteBranchExists, +} from "../github/utils/local-git"; async function run() { try { @@ -108,14 +113,17 @@ async function run() { if (!containsPRUrl) { // Check if we're using Gitea or GitHub const giteaApiUrl = process.env.GITEA_API_URL?.trim(); - const isGitea = giteaApiUrl && - giteaApiUrl !== "" && + const isGitea = + giteaApiUrl && + giteaApiUrl !== "" && !giteaApiUrl.includes("api.github.com") && !giteaApiUrl.includes("github.com"); if (isGitea) { // Use local git commands for Gitea - console.log("Using local git commands for PR link check (Gitea mode)"); + console.log( + "Using local git commands for PR link check (Gitea mode)", + ); try { // Fetch latest changes from remote @@ -123,7 +131,10 @@ async function run() { await fetchBranch(baseBranch); // Check if branch exists and has changes - const { hasChanges, branchSha, baseSha } = await branchHasChanges(claudeBranch, baseBranch); + const { hasChanges, branchSha, baseSha } = await branchHasChanges( + claudeBranch, + baseBranch, + ); if (branchSha && baseSha) { if (hasChanges) { @@ -148,9 +159,11 @@ async function run() { // If we can't get SHAs, check if branch exists at all const localExists = await branchExists(claudeBranch); const remoteExists = await remoteBranchExists(claudeBranch); - + if (localExists || remoteExists) { - console.log(`Branch ${claudeBranch} exists but SHA comparison failed, adding PR link to be safe`); + console.log( + `Branch ${claudeBranch} exists but SHA comparison failed, adding PR link to be safe`, + ); const entityType = context.isPR ? "PR" : "Issue"; const prTitle = encodeURIComponent( `${entityType} #${context.entityNumber}: Changes from Claude`, diff --git a/src/github/operations/branch-cleanup.ts b/src/github/operations/branch-cleanup.ts index 4280145..e8ab547 100644 --- a/src/github/operations/branch-cleanup.ts +++ b/src/github/operations/branch-cleanup.ts @@ -1,6 +1,11 @@ import type { GitHubClient } from "../api/client"; import { GITEA_SERVER_URL } from "../api/config"; -import { branchHasChanges, fetchBranch, branchExists, remoteBranchExists } from "../utils/local-git"; +import { + branchHasChanges, + fetchBranch, + branchExists, + remoteBranchExists, +} from "../utils/local-git"; export async function checkAndDeleteEmptyBranch( client: GitHubClient, @@ -15,8 +20,9 @@ export async function checkAndDeleteEmptyBranch( if (claudeBranch) { // Check if we're using Gitea or GitHub const giteaApiUrl = process.env.GITEA_API_URL?.trim(); - const isGitea = giteaApiUrl && - giteaApiUrl !== "" && + const isGitea = + giteaApiUrl && + giteaApiUrl !== "" && !giteaApiUrl.includes("api.github.com") && !giteaApiUrl.includes("github.com"); @@ -30,7 +36,10 @@ export async function checkAndDeleteEmptyBranch( await fetchBranch(baseBranch); // Check if branch exists and has changes - const { hasChanges, branchSha, baseSha } = await branchHasChanges(claudeBranch, baseBranch); + const { hasChanges, branchSha, baseSha } = await branchHasChanges( + claudeBranch, + baseBranch, + ); if (branchSha && baseSha) { if (hasChanges) { @@ -49,9 +58,11 @@ export async function checkAndDeleteEmptyBranch( // If we can't get SHAs, check if branch exists at all const localExists = await branchExists(claudeBranch); const remoteExists = await remoteBranchExists(claudeBranch); - + if (localExists || remoteExists) { - console.log(`Branch ${claudeBranch} exists but SHA comparison failed, assuming it has commits`); + console.log( + `Branch ${claudeBranch} exists but SHA comparison failed, assuming it has commits`, + ); const branchUrl = `${GITEA_SERVER_URL}/${owner}/${repo}/tree/${claudeBranch}`; branchLink = `\n[View branch](${branchUrl})`; } else { @@ -81,7 +92,11 @@ export async function checkAndDeleteEmptyBranch( ); // Get base branch info for comparison - const baseResponse = await client.api.getBranch(owner, repo, baseBranch); + const baseResponse = await client.api.getBranch( + owner, + repo, + baseBranch, + ); const branchSha = branchResponse.data.commit.sha; const baseSha = baseResponse.data.commit.sha; diff --git a/src/github/operations/comments/common.ts b/src/github/operations/comments/common.ts index 37bf6a1..2b318f2 100644 --- a/src/github/operations/comments/common.ts +++ b/src/github/operations/comments/common.ts @@ -1,7 +1,7 @@ import { GITEA_SERVER_URL } from "../../api/config"; export const SPINNER_HTML = - ''; + ''; export function createJobRunLink( owner: string, diff --git a/src/github/utils/local-git.ts b/src/github/utils/local-git.ts index 551831e..2b36f2b 100644 --- a/src/github/utils/local-git.ts +++ b/src/github/utils/local-git.ts @@ -36,13 +36,13 @@ export async function getBranchSha(branchName: string): Promise { const result = await $`git rev-parse refs/heads/${branchName}`; return result.text().trim(); } - + // Try remote branch if local doesn't exist if (await remoteBranchExists(branchName)) { const result = await $`git rev-parse refs/remotes/origin/${branchName}`; return result.text().trim(); } - + return null; } catch (error) { console.error(`Error getting SHA for branch ${branchName}:`, error); @@ -53,19 +53,29 @@ export async function getBranchSha(branchName: string): Promise { /** * Check if a branch has commits different from base branch */ -export async function branchHasChanges(branchName: string, baseBranch: string): Promise<{ hasChanges: boolean; branchSha: string | null; baseSha: string | null }> { +export async function branchHasChanges( + branchName: string, + baseBranch: string, +): Promise<{ + hasChanges: boolean; + branchSha: string | null; + baseSha: string | null; +}> { try { const branchSha = await getBranchSha(branchName); const baseSha = await getBranchSha(baseBranch); - + if (!branchSha || !baseSha) { return { hasChanges: false, branchSha, baseSha }; } - + const hasChanges = branchSha !== baseSha; return { hasChanges, branchSha, baseSha }; } catch (error) { - console.error(`Error comparing branches ${branchName} and ${baseBranch}:`, error); + console.error( + `Error comparing branches ${branchName} and ${baseBranch}:`, + error, + ); return { hasChanges: false, branchSha: null, baseSha: null }; } } @@ -78,7 +88,9 @@ export async function fetchBranch(branchName: string): Promise { await $`git fetch origin ${branchName}`; return true; } catch (error) { - console.log(`Could not fetch branch ${branchName} from remote (may not exist yet)`); + console.log( + `Could not fetch branch ${branchName} from remote (may not exist yet)`, + ); return false; } -} \ No newline at end of file +} diff --git a/src/mcp/gitea-mcp-server.ts b/src/mcp/gitea-mcp-server.ts index 27e71ff..8b2832b 100644 --- a/src/mcp/gitea-mcp-server.ts +++ b/src/mcp/gitea-mcp-server.ts @@ -17,7 +17,7 @@ console.log(`[GITEA-MCP] REPO_OWNER: ${REPO_OWNER}`); console.log(`[GITEA-MCP] REPO_NAME: ${REPO_NAME}`); console.log(`[GITEA-MCP] BRANCH_NAME: ${BRANCH_NAME}`); console.log(`[GITEA-MCP] GITEA_API_URL: ${GITEA_API_URL}`); -console.log(`[GITEA-MCP] GITHUB_TOKEN: ${GITHUB_TOKEN ? '***' : 'undefined'}`); +console.log(`[GITEA-MCP] GITHUB_TOKEN: ${GITHUB_TOKEN ? "***" : "undefined"}`); if (!REPO_OWNER || !REPO_NAME || !GITHUB_TOKEN) { console.error( @@ -39,10 +39,10 @@ async function giteaRequest( ): Promise { const url = `${GITEA_API_URL}${endpoint}`; console.log(`[GITEA-MCP] Making ${method} request to: ${url}`); - + const headers: Record = { - "Authorization": `token ${GITHUB_TOKEN}`, - "Accept": "application/json", + Authorization: `token ${GITHUB_TOKEN}`, + Accept: "application/json", }; if (body) { @@ -60,7 +60,9 @@ async function giteaRequest( console.log(`[GITEA-MCP] Response: ${responseText.substring(0, 500)}...`); if (!response.ok) { - throw new Error(`Gitea API request failed: ${response.status} ${responseText}`); + throw new Error( + `Gitea API request failed: ${response.status} ${responseText}`, + ); } return responseText ? JSON.parse(responseText) : null; @@ -75,8 +77,10 @@ server.tool( }, async ({ issue_number }) => { try { - const issue = await giteaRequest(`/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/issues/${issue_number}`); - + const issue = await giteaRequest( + `/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/issues/${issue_number}`, + ); + return { content: [ { @@ -86,7 +90,8 @@ server.tool( ], }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = + error instanceof Error ? error.message : String(error); console.error(`[GITEA-MCP] Error getting issue: ${errorMessage}`); return { content: [ @@ -108,23 +113,31 @@ server.tool( "Get all comments for a specific issue", { issue_number: z.number().describe("The issue number to fetch comments for"), - since: z.string().optional().describe("Only show comments updated after this time (ISO 8601 format)"), - before: z.string().optional().describe("Only show comments updated before this time (ISO 8601 format)"), + since: z + .string() + .optional() + .describe("Only show comments updated after this time (ISO 8601 format)"), + before: z + .string() + .optional() + .describe( + "Only show comments updated before this time (ISO 8601 format)", + ), }, async ({ issue_number, since, before }) => { try { let endpoint = `/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/issues/${issue_number}/comments`; const params = new URLSearchParams(); - + if (since) params.append("since", since); if (before) params.append("before", before); - + if (params.toString()) { endpoint += `?${params.toString()}`; } const comments = await giteaRequest(endpoint); - + return { content: [ { @@ -134,8 +147,11 @@ server.tool( ], }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - console.error(`[GITEA-MCP] Error getting issue comments: ${errorMessage}`); + const errorMessage = + error instanceof Error ? error.message : String(error); + console.error( + `[GITEA-MCP] Error getting issue comments: ${errorMessage}`, + ); return { content: [ { @@ -165,7 +181,7 @@ server.tool( "POST", { body }, ); - + return { content: [ { @@ -175,7 +191,8 @@ server.tool( ], }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = + error instanceof Error ? error.message : String(error); console.error(`[GITEA-MCP] Error adding issue comment: ${errorMessage}`); return { content: [ @@ -206,7 +223,7 @@ server.tool( "PATCH", { body }, ); - + return { content: [ { @@ -216,8 +233,11 @@ server.tool( ], }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - console.error(`[GITEA-MCP] Error updating issue comment: ${errorMessage}`); + const errorMessage = + error instanceof Error ? error.message : String(error); + console.error( + `[GITEA-MCP] Error updating issue comment: ${errorMessage}`, + ); return { content: [ { @@ -245,7 +265,7 @@ server.tool( `/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/issues/comments/${comment_id}`, "DELETE", ); - + return { content: [ { @@ -255,8 +275,11 @@ server.tool( ], }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - console.error(`[GITEA-MCP] Error deleting issue comment: ${errorMessage}`); + const errorMessage = + error instanceof Error ? error.message : String(error); + console.error( + `[GITEA-MCP] Error deleting issue comment: ${errorMessage}`, + ); return { content: [ { @@ -280,8 +303,10 @@ server.tool( }, async ({ comment_id }) => { try { - const comment = await giteaRequest(`/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/issues/comments/${comment_id}`); - + const comment = await giteaRequest( + `/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/issues/comments/${comment_id}`, + ); + return { content: [ { @@ -291,7 +316,8 @@ server.tool( ], }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = + error instanceof Error ? error.message : String(error); console.error(`[GITEA-MCP] Error getting comment: ${errorMessage}`); return { content: [ @@ -312,20 +338,44 @@ server.tool( "list_issues", "List issues in the repository", { - state: z.enum(["open", "closed", "all"]).optional().describe("Issue state filter"), - labels: z.string().optional().describe("Comma-separated list of label names"), + state: z + .enum(["open", "closed", "all"]) + .optional() + .describe("Issue state filter"), + labels: z + .string() + .optional() + .describe("Comma-separated list of label names"), milestone: z.string().optional().describe("Milestone title to filter by"), - assignee: z.string().optional().describe("Username to filter issues assigned to"), - creator: z.string().optional().describe("Username to filter issues created by"), - mentioned: z.string().optional().describe("Username to filter issues that mention"), + assignee: z + .string() + .optional() + .describe("Username to filter issues assigned to"), + creator: z + .string() + .optional() + .describe("Username to filter issues created by"), + mentioned: z + .string() + .optional() + .describe("Username to filter issues that mention"), page: z.number().optional().describe("Page number for pagination"), limit: z.number().optional().describe("Number of items per page"), }, - async ({ state, labels, milestone, assignee, creator, mentioned, page, limit }) => { + async ({ + state, + labels, + milestone, + assignee, + creator, + mentioned, + page, + limit, + }) => { try { let endpoint = `/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/issues`; const params = new URLSearchParams(); - + if (state) params.append("state", state); if (labels) params.append("labels", labels); if (milestone) params.append("milestone", milestone); @@ -334,13 +384,13 @@ server.tool( if (mentioned) params.append("mentioned", mentioned); if (page) params.append("page", page.toString()); if (limit) params.append("limit", limit.toString()); - + if (params.toString()) { endpoint += `?${params.toString()}`; } const issues = await giteaRequest(endpoint); - + return { content: [ { @@ -350,7 +400,8 @@ server.tool( ], }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = + error instanceof Error ? error.message : String(error); console.error(`[GITEA-MCP] Error listing issues: ${errorMessage}`); return { content: [ @@ -374,14 +425,23 @@ server.tool( title: z.string().describe("Issue title"), body: z.string().optional().describe("Issue body content"), assignee: z.string().optional().describe("Username to assign the issue to"), - assignees: z.array(z.string()).optional().describe("Array of usernames to assign the issue to"), - milestone: z.number().optional().describe("Milestone ID to associate with the issue"), - labels: z.array(z.string()).optional().describe("Array of label names to apply to the issue"), + assignees: z + .array(z.string()) + .optional() + .describe("Array of usernames to assign the issue to"), + milestone: z + .number() + .optional() + .describe("Milestone ID to associate with the issue"), + labels: z + .array(z.string()) + .optional() + .describe("Array of label names to apply to the issue"), }, async ({ title, body, assignee, assignees, milestone, labels }) => { try { const issueData: any = { title }; - + if (body) issueData.body = body; if (assignee) issueData.assignee = assignee; if (assignees) issueData.assignees = assignees; @@ -393,7 +453,7 @@ server.tool( "POST", issueData, ); - + return { content: [ { @@ -403,7 +463,8 @@ server.tool( ], }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = + error instanceof Error ? error.message : String(error); console.error(`[GITEA-MCP] Error creating issue: ${errorMessage}`); return { content: [ @@ -428,15 +489,33 @@ server.tool( title: z.string().optional().describe("New issue title"), body: z.string().optional().describe("New issue body content"), assignee: z.string().optional().describe("Username to assign the issue to"), - assignees: z.array(z.string()).optional().describe("Array of usernames to assign the issue to"), - milestone: z.number().optional().describe("Milestone ID to associate with the issue"), - labels: z.array(z.string()).optional().describe("Array of label names to apply to the issue"), + assignees: z + .array(z.string()) + .optional() + .describe("Array of usernames to assign the issue to"), + milestone: z + .number() + .optional() + .describe("Milestone ID to associate with the issue"), + labels: z + .array(z.string()) + .optional() + .describe("Array of label names to apply to the issue"), state: z.enum(["open", "closed"]).optional().describe("Issue state"), }, - async ({ issue_number, title, body, assignee, assignees, milestone, labels, state }) => { + async ({ + issue_number, + title, + body, + assignee, + assignees, + milestone, + labels, + state, + }) => { try { const updateData: any = {}; - + if (title) updateData.title = title; if (body !== undefined) updateData.body = body; if (assignee) updateData.assignee = assignee; @@ -450,7 +529,7 @@ server.tool( "PATCH", updateData, ); - + return { content: [ { @@ -460,7 +539,8 @@ server.tool( ], }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = + error instanceof Error ? error.message : String(error); console.error(`[GITEA-MCP] Error updating issue: ${errorMessage}`); return { content: [ @@ -477,45 +557,43 @@ server.tool( ); // Get repository information -server.tool( - "get_repository", - "Get repository information", - {}, - async () => { - try { - const repo = await giteaRequest(`/api/v1/repos/${REPO_OWNER}/${REPO_NAME}`); - - return { - content: [ - { - type: "text", - text: JSON.stringify(repo, null, 2), - }, - ], - }; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - console.error(`[GITEA-MCP] Error getting repository: ${errorMessage}`); - return { - content: [ - { - type: "text", - text: `Error getting repository: ${errorMessage}`, - }, - ], - error: errorMessage, - isError: true, - }; - } - }, -); +server.tool("get_repository", "Get repository information", {}, async () => { + try { + const repo = await giteaRequest(`/api/v1/repos/${REPO_OWNER}/${REPO_NAME}`); + + return { + content: [ + { + type: "text", + text: JSON.stringify(repo, null, 2), + }, + ], + }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + console.error(`[GITEA-MCP] Error getting repository: ${errorMessage}`); + return { + content: [ + { + type: "text", + text: `Error getting repository: ${errorMessage}`, + }, + ], + error: errorMessage, + isError: true, + }; + } +}); // Get pull requests server.tool( "list_pull_requests", "List pull requests in the repository", { - state: z.enum(["open", "closed", "all"]).optional().describe("Pull request state filter"), + state: z + .enum(["open", "closed", "all"]) + .optional() + .describe("Pull request state filter"), head: z.string().optional().describe("Head branch name"), base: z.string().optional().describe("Base branch name"), page: z.number().optional().describe("Page number for pagination"), @@ -525,19 +603,19 @@ server.tool( try { let endpoint = `/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/pulls`; const params = new URLSearchParams(); - + if (state) params.append("state", state); if (head) params.append("head", head); if (base) params.append("base", base); if (page) params.append("page", page.toString()); if (limit) params.append("limit", limit.toString()); - + if (params.toString()) { endpoint += `?${params.toString()}`; } const pulls = await giteaRequest(endpoint); - + return { content: [ { @@ -547,7 +625,8 @@ server.tool( ], }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = + error instanceof Error ? error.message : String(error); console.error(`[GITEA-MCP] Error listing pull requests: ${errorMessage}`); return { content: [ @@ -572,8 +651,10 @@ server.tool( }, async ({ pull_number }) => { try { - const pull = await giteaRequest(`/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/pulls/${pull_number}`); - + const pull = await giteaRequest( + `/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/pulls/${pull_number}`, + ); + return { content: [ { @@ -583,7 +664,8 @@ server.tool( ], }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = + error instanceof Error ? error.message : String(error); console.error(`[GITEA-MCP] Error getting pull request: ${errorMessage}`); return { content: [ @@ -608,15 +690,36 @@ server.tool( body: z.string().optional().describe("Pull request body/description"), head: z.string().describe("Head branch name"), base: z.string().describe("Base branch name"), - assignee: z.string().optional().describe("Username to assign the pull request to"), - assignees: z.array(z.string()).optional().describe("Array of usernames to assign the pull request to"), - milestone: z.number().optional().describe("Milestone ID to associate with the pull request"), - labels: z.array(z.string()).optional().describe("Array of label names to apply to the pull request"), + assignee: z + .string() + .optional() + .describe("Username to assign the pull request to"), + assignees: z + .array(z.string()) + .optional() + .describe("Array of usernames to assign the pull request to"), + milestone: z + .number() + .optional() + .describe("Milestone ID to associate with the pull request"), + labels: z + .array(z.string()) + .optional() + .describe("Array of label names to apply to the pull request"), }, - async ({ title, body, head, base, assignee, assignees, milestone, labels }) => { + async ({ + title, + body, + head, + base, + assignee, + assignees, + milestone, + labels, + }) => { try { const pullData: any = { title, head, base }; - + if (body) pullData.body = body; if (assignee) pullData.assignee = assignee; if (assignees) pullData.assignees = assignees; @@ -628,7 +731,7 @@ server.tool( "POST", pullData, ); - + return { content: [ { @@ -638,7 +741,8 @@ server.tool( ], }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = + error instanceof Error ? error.message : String(error); console.error(`[GITEA-MCP] Error creating pull request: ${errorMessage}`); return { content: [ @@ -670,4 +774,4 @@ console.log(`[GITEA-MCP] Calling runServer()...`); runServer().catch((error) => { console.error(`[GITEA-MCP] Server startup failed:`, error); process.exit(1); -}); \ No newline at end of file +}); diff --git a/src/mcp/install-mcp-server.ts b/src/mcp/install-mcp-server.ts index 4373639..3b44b23 100644 --- a/src/mcp/install-mcp-server.ts +++ b/src/mcp/install-mcp-server.ts @@ -10,10 +10,16 @@ export async function prepareMcpConfig( console.log(`[MCP-INSTALL] Owner: ${owner}`); console.log(`[MCP-INSTALL] Repo: ${repo}`); console.log(`[MCP-INSTALL] Branch: ${branch}`); - console.log(`[MCP-INSTALL] GitHub token: ${githubToken ? '***' : 'undefined'}`); - console.log(`[MCP-INSTALL] GITHUB_ACTION_PATH: ${process.env.GITHUB_ACTION_PATH}`); - console.log(`[MCP-INSTALL] GITHUB_WORKSPACE: ${process.env.GITHUB_WORKSPACE}`); - + console.log( + `[MCP-INSTALL] GitHub token: ${githubToken ? "***" : "undefined"}`, + ); + console.log( + `[MCP-INSTALL] GITHUB_ACTION_PATH: ${process.env.GITHUB_ACTION_PATH}`, + ); + console.log( + `[MCP-INSTALL] GITHUB_WORKSPACE: ${process.env.GITHUB_WORKSPACE}`, + ); + try { const mcpConfig = { mcpServers: { @@ -29,7 +35,8 @@ export async function prepareMcpConfig( REPO_NAME: repo, BRANCH_NAME: branch, REPO_DIR: process.env.GITHUB_WORKSPACE || process.cwd(), - GITEA_API_URL: process.env.GITEA_API_URL || "https://api.github.com", + GITEA_API_URL: + process.env.GITEA_API_URL || "https://api.github.com", }, }, local_git_ops: { @@ -55,7 +62,7 @@ export async function prepareMcpConfig( console.log("[MCP-INSTALL] Generated MCP configuration:"); console.log(configString); console.log("[MCP-INSTALL] MCP config generation completed successfully"); - + return configString; } catch (error) { console.error("[MCP-INSTALL] MCP config generation failed:", error); diff --git a/src/mcp/local-git-ops-server.ts b/src/mcp/local-git-ops-server.ts index 011c92f..11876fb 100644 --- a/src/mcp/local-git-ops-server.ts +++ b/src/mcp/local-git-ops-server.ts @@ -21,7 +21,9 @@ console.log(`[LOCAL-GIT-MCP] REPO_NAME: ${REPO_NAME}`); console.log(`[LOCAL-GIT-MCP] BRANCH_NAME: ${BRANCH_NAME}`); console.log(`[LOCAL-GIT-MCP] REPO_DIR: ${REPO_DIR}`); console.log(`[LOCAL-GIT-MCP] GITEA_API_URL: ${GITEA_API_URL}`); -console.log(`[LOCAL-GIT-MCP] GITHUB_TOKEN: ${GITHUB_TOKEN ? '***' : 'undefined'}`); +console.log( + `[LOCAL-GIT-MCP] GITHUB_TOKEN: ${GITHUB_TOKEN ? "***" : "undefined"}`, +); if (!REPO_OWNER || !REPO_NAME || !BRANCH_NAME) { console.error( @@ -63,7 +65,9 @@ function ensureGitUserConfigured(): void { runGitCommand("git config user.email"); console.log(`[LOCAL-GIT-MCP] Git user.email already configured`); } catch (error) { - console.log(`[LOCAL-GIT-MCP] Git user.email not configured, setting default`); + console.log( + `[LOCAL-GIT-MCP] Git user.email not configured, setting default`, + ); runGitCommand('git config user.email "claude@anthropic.com"'); } @@ -72,7 +76,9 @@ function ensureGitUserConfigured(): void { runGitCommand("git config user.name"); console.log(`[LOCAL-GIT-MCP] Git user.name already configured`); } catch (error) { - console.log(`[LOCAL-GIT-MCP] Git user.name not configured, setting default`); + console.log( + `[LOCAL-GIT-MCP] Git user.name not configured, setting default`, + ); runGitCommand('git config user.name "Claude"'); } } @@ -134,7 +140,9 @@ server.tool( message: z.string().describe("Commit message"), }, async ({ files, message }) => { - console.log(`[LOCAL-GIT-MCP] commit_files called with files: ${JSON.stringify(files)}, message: ${message}`); + console.log( + `[LOCAL-GIT-MCP] commit_files called with files: ${JSON.stringify(files)}, message: ${message}`, + ); try { // Ensure git user is configured before committing ensureGitUserConfigured(); @@ -151,7 +159,9 @@ server.tool( console.log(`[LOCAL-GIT-MCP] Committing with message: ${message}`); runGitCommand(`git commit -m "${message}"`); - console.log(`[LOCAL-GIT-MCP] Successfully committed ${files.length} files`); + console.log( + `[LOCAL-GIT-MCP] Successfully committed ${files.length} files`, + ); return { content: [ { @@ -353,7 +363,9 @@ server.tool("git_status", "Get the current git status", {}, async () => { const currentBranch = runGitCommand("git rev-parse --abbrev-ref HEAD"); console.log(`[LOCAL-GIT-MCP] Current branch: ${currentBranch}`); - console.log(`[LOCAL-GIT-MCP] Git status: ${status || "Working tree clean"}`); + console.log( + `[LOCAL-GIT-MCP] Git status: ${status || "Working tree clean"}`, + ); return { content: [