Attempt to make this work

This commit is contained in:
Mark Wylde
2025-05-31 00:56:07 +01:00
parent ea134ca929
commit 5171232878
8 changed files with 300 additions and 137 deletions

View File

@@ -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). - 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). - 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 - 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. - 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) - 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). - 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 - 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 eventData.claudeBranch
? `- Provide a URL to create a PR manually in this format: ? `- 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}. - 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." : ""} - 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. - You communicate exclusively by editing your single comment - not through any other means.
- Use this spinner HTML when work is in progress: <img src="https://github.com/user-attachments/assets/5ac382c7-e004-429b-8e35-7feb3e8f9c6f" width="14px" height="14px" style="vertical-align: middle; margin-left: 4px;" /> - Use this spinner HTML when work is in progress: <img src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wjRLKLkHZx6EaNgeFJBAC7F9YoI5FqYZEtfkGdVBNMAKg3DiC0Kx/BqFgBAA7" width="14px" height="14px" style="vertical-align: middle; margin-left: 4px;" />
${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.`} ${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. - 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: 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) - 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: 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. 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.

View File

@@ -12,7 +12,12 @@ import {
} from "../github/context"; } from "../github/context";
import { GITEA_SERVER_URL } from "../github/api/config"; import { GITEA_SERVER_URL } from "../github/api/config";
import { checkAndDeleteEmptyBranch } from "../github/operations/branch-cleanup"; 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() { async function run() {
try { try {
@@ -108,14 +113,17 @@ async function run() {
if (!containsPRUrl) { if (!containsPRUrl) {
// Check if we're using Gitea or GitHub // Check if we're using Gitea or GitHub
const giteaApiUrl = process.env.GITEA_API_URL?.trim(); const giteaApiUrl = process.env.GITEA_API_URL?.trim();
const isGitea = giteaApiUrl && const isGitea =
giteaApiUrl &&
giteaApiUrl !== "" && giteaApiUrl !== "" &&
!giteaApiUrl.includes("api.github.com") && !giteaApiUrl.includes("api.github.com") &&
!giteaApiUrl.includes("github.com"); !giteaApiUrl.includes("github.com");
if (isGitea) { if (isGitea) {
// Use local git commands for Gitea // 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 { try {
// Fetch latest changes from remote // Fetch latest changes from remote
@@ -123,7 +131,10 @@ async function run() {
await fetchBranch(baseBranch); await fetchBranch(baseBranch);
// Check if branch exists and has changes // 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 (branchSha && baseSha) {
if (hasChanges) { if (hasChanges) {
@@ -150,7 +161,9 @@ async function run() {
const remoteExists = await remoteBranchExists(claudeBranch); const remoteExists = await remoteBranchExists(claudeBranch);
if (localExists || remoteExists) { 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 entityType = context.isPR ? "PR" : "Issue";
const prTitle = encodeURIComponent( const prTitle = encodeURIComponent(
`${entityType} #${context.entityNumber}: Changes from Claude`, `${entityType} #${context.entityNumber}: Changes from Claude`,

View File

@@ -1,6 +1,11 @@
import type { GitHubClient } from "../api/client"; import type { GitHubClient } from "../api/client";
import { GITEA_SERVER_URL } from "../api/config"; 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( export async function checkAndDeleteEmptyBranch(
client: GitHubClient, client: GitHubClient,
@@ -15,7 +20,8 @@ export async function checkAndDeleteEmptyBranch(
if (claudeBranch) { if (claudeBranch) {
// Check if we're using Gitea or GitHub // Check if we're using Gitea or GitHub
const giteaApiUrl = process.env.GITEA_API_URL?.trim(); const giteaApiUrl = process.env.GITEA_API_URL?.trim();
const isGitea = giteaApiUrl && const isGitea =
giteaApiUrl &&
giteaApiUrl !== "" && giteaApiUrl !== "" &&
!giteaApiUrl.includes("api.github.com") && !giteaApiUrl.includes("api.github.com") &&
!giteaApiUrl.includes("github.com"); !giteaApiUrl.includes("github.com");
@@ -30,7 +36,10 @@ export async function checkAndDeleteEmptyBranch(
await fetchBranch(baseBranch); await fetchBranch(baseBranch);
// Check if branch exists and has changes // 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 (branchSha && baseSha) {
if (hasChanges) { if (hasChanges) {
@@ -51,7 +60,9 @@ export async function checkAndDeleteEmptyBranch(
const remoteExists = await remoteBranchExists(claudeBranch); const remoteExists = await remoteBranchExists(claudeBranch);
if (localExists || remoteExists) { 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}`; const branchUrl = `${GITEA_SERVER_URL}/${owner}/${repo}/tree/${claudeBranch}`;
branchLink = `\n[View branch](${branchUrl})`; branchLink = `\n[View branch](${branchUrl})`;
} else { } else {
@@ -81,7 +92,11 @@ export async function checkAndDeleteEmptyBranch(
); );
// Get base branch info for comparison // 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 branchSha = branchResponse.data.commit.sha;
const baseSha = baseResponse.data.commit.sha; const baseSha = baseResponse.data.commit.sha;

View File

@@ -1,7 +1,7 @@
import { GITEA_SERVER_URL } from "../../api/config"; import { GITEA_SERVER_URL } from "../../api/config";
export const SPINNER_HTML = export const SPINNER_HTML =
'<img src="https://github.com/user-attachments/assets/5ac382c7-e004-429b-8e35-7feb3e8f9c6f" width="14px" height="14px" style="vertical-align: middle; margin-left: 4px;" />'; '<img src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wjRLKLkHZx6EaNgeFJBAC7F9YoI5FqYZEtfkGdVBNMAKg3DiC0Kx/BqFgBAA7" width="14px" height="14px" style="vertical-align: middle; margin-left: 4px;" />';
export function createJobRunLink( export function createJobRunLink(
owner: string, owner: string,

View File

@@ -53,7 +53,14 @@ export async function getBranchSha(branchName: string): Promise<string | null> {
/** /**
* Check if a branch has commits different from base branch * 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 { try {
const branchSha = await getBranchSha(branchName); const branchSha = await getBranchSha(branchName);
const baseSha = await getBranchSha(baseBranch); const baseSha = await getBranchSha(baseBranch);
@@ -65,7 +72,10 @@ export async function branchHasChanges(branchName: string, baseBranch: string):
const hasChanges = branchSha !== baseSha; const hasChanges = branchSha !== baseSha;
return { hasChanges, branchSha, baseSha }; return { hasChanges, branchSha, baseSha };
} catch (error) { } 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 }; return { hasChanges: false, branchSha: null, baseSha: null };
} }
} }
@@ -78,7 +88,9 @@ export async function fetchBranch(branchName: string): Promise<boolean> {
await $`git fetch origin ${branchName}`; await $`git fetch origin ${branchName}`;
return true; return true;
} catch (error) { } 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; return false;
} }
} }

View File

@@ -17,7 +17,7 @@ console.log(`[GITEA-MCP] REPO_OWNER: ${REPO_OWNER}`);
console.log(`[GITEA-MCP] REPO_NAME: ${REPO_NAME}`); console.log(`[GITEA-MCP] REPO_NAME: ${REPO_NAME}`);
console.log(`[GITEA-MCP] BRANCH_NAME: ${BRANCH_NAME}`); console.log(`[GITEA-MCP] BRANCH_NAME: ${BRANCH_NAME}`);
console.log(`[GITEA-MCP] GITEA_API_URL: ${GITEA_API_URL}`); 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) { if (!REPO_OWNER || !REPO_NAME || !GITHUB_TOKEN) {
console.error( console.error(
@@ -41,8 +41,8 @@ async function giteaRequest(
console.log(`[GITEA-MCP] Making ${method} request to: ${url}`); console.log(`[GITEA-MCP] Making ${method} request to: ${url}`);
const headers: Record<string, string> = { const headers: Record<string, string> = {
"Authorization": `token ${GITHUB_TOKEN}`, Authorization: `token ${GITHUB_TOKEN}`,
"Accept": "application/json", Accept: "application/json",
}; };
if (body) { if (body) {
@@ -60,7 +60,9 @@ async function giteaRequest(
console.log(`[GITEA-MCP] Response: ${responseText.substring(0, 500)}...`); console.log(`[GITEA-MCP] Response: ${responseText.substring(0, 500)}...`);
if (!response.ok) { 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; return responseText ? JSON.parse(responseText) : null;
@@ -75,7 +77,9 @@ server.tool(
}, },
async ({ issue_number }) => { async ({ issue_number }) => {
try { 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 { return {
content: [ content: [
@@ -86,7 +90,8 @@ server.tool(
], ],
}; };
} catch (error) { } 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}`); console.error(`[GITEA-MCP] Error getting issue: ${errorMessage}`);
return { return {
content: [ content: [
@@ -108,8 +113,16 @@ server.tool(
"Get all comments for a specific issue", "Get all comments for a specific issue",
{ {
issue_number: z.number().describe("The issue number to fetch comments for"), 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)"), since: z
before: z.string().optional().describe("Only show comments updated before this time (ISO 8601 format)"), .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 }) => { async ({ issue_number, since, before }) => {
try { try {
@@ -134,8 +147,11 @@ server.tool(
], ],
}; };
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error); const errorMessage =
console.error(`[GITEA-MCP] Error getting issue comments: ${errorMessage}`); error instanceof Error ? error.message : String(error);
console.error(
`[GITEA-MCP] Error getting issue comments: ${errorMessage}`,
);
return { return {
content: [ content: [
{ {
@@ -175,7 +191,8 @@ server.tool(
], ],
}; };
} catch (error) { } 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}`); console.error(`[GITEA-MCP] Error adding issue comment: ${errorMessage}`);
return { return {
content: [ content: [
@@ -216,8 +233,11 @@ server.tool(
], ],
}; };
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error); const errorMessage =
console.error(`[GITEA-MCP] Error updating issue comment: ${errorMessage}`); error instanceof Error ? error.message : String(error);
console.error(
`[GITEA-MCP] Error updating issue comment: ${errorMessage}`,
);
return { return {
content: [ content: [
{ {
@@ -255,8 +275,11 @@ server.tool(
], ],
}; };
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error); const errorMessage =
console.error(`[GITEA-MCP] Error deleting issue comment: ${errorMessage}`); error instanceof Error ? error.message : String(error);
console.error(
`[GITEA-MCP] Error deleting issue comment: ${errorMessage}`,
);
return { return {
content: [ content: [
{ {
@@ -280,7 +303,9 @@ server.tool(
}, },
async ({ comment_id }) => { async ({ comment_id }) => {
try { 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 { return {
content: [ content: [
@@ -291,7 +316,8 @@ server.tool(
], ],
}; };
} catch (error) { } 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}`); console.error(`[GITEA-MCP] Error getting comment: ${errorMessage}`);
return { return {
content: [ content: [
@@ -312,16 +338,40 @@ server.tool(
"list_issues", "list_issues",
"List issues in the repository", "List issues in the repository",
{ {
state: z.enum(["open", "closed", "all"]).optional().describe("Issue state filter"), state: z
labels: z.string().optional().describe("Comma-separated list of label names"), .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"), milestone: z.string().optional().describe("Milestone title to filter by"),
assignee: z.string().optional().describe("Username to filter issues assigned to"), assignee: z
creator: z.string().optional().describe("Username to filter issues created by"), .string()
mentioned: z.string().optional().describe("Username to filter issues that mention"), .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"), page: z.number().optional().describe("Page number for pagination"),
limit: z.number().optional().describe("Number of items per page"), 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 { try {
let endpoint = `/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/issues`; let endpoint = `/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/issues`;
const params = new URLSearchParams(); const params = new URLSearchParams();
@@ -350,7 +400,8 @@ server.tool(
], ],
}; };
} catch (error) { } 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}`); console.error(`[GITEA-MCP] Error listing issues: ${errorMessage}`);
return { return {
content: [ content: [
@@ -374,9 +425,18 @@ server.tool(
title: z.string().describe("Issue title"), title: z.string().describe("Issue title"),
body: z.string().optional().describe("Issue body content"), body: z.string().optional().describe("Issue body content"),
assignee: z.string().optional().describe("Username to assign the issue to"), 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"), assignees: z
milestone: z.number().optional().describe("Milestone ID to associate with the issue"), .array(z.string())
labels: z.array(z.string()).optional().describe("Array of label names to apply to the issue"), .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 }) => { async ({ title, body, assignee, assignees, milestone, labels }) => {
try { try {
@@ -403,7 +463,8 @@ server.tool(
], ],
}; };
} catch (error) { } 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}`); console.error(`[GITEA-MCP] Error creating issue: ${errorMessage}`);
return { return {
content: [ content: [
@@ -428,12 +489,30 @@ server.tool(
title: z.string().optional().describe("New issue title"), title: z.string().optional().describe("New issue title"),
body: z.string().optional().describe("New issue body content"), body: z.string().optional().describe("New issue body content"),
assignee: z.string().optional().describe("Username to assign the issue to"), 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"), assignees: z
milestone: z.number().optional().describe("Milestone ID to associate with the issue"), .array(z.string())
labels: z.array(z.string()).optional().describe("Array of label names to apply to the issue"), .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"), 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 { try {
const updateData: any = {}; const updateData: any = {};
@@ -460,7 +539,8 @@ server.tool(
], ],
}; };
} catch (error) { } 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}`); console.error(`[GITEA-MCP] Error updating issue: ${errorMessage}`);
return { return {
content: [ content: [
@@ -477,45 +557,43 @@ server.tool(
); );
// Get repository information // Get repository information
server.tool( server.tool("get_repository", "Get repository information", {}, async () => {
"get_repository", try {
"Get repository information", const repo = await giteaRequest(`/api/v1/repos/${REPO_OWNER}/${REPO_NAME}`);
{},
async () => {
try {
const repo = await giteaRequest(`/api/v1/repos/${REPO_OWNER}/${REPO_NAME}`);
return { return {
content: [ content: [
{ {
type: "text", type: "text",
text: JSON.stringify(repo, null, 2), text: JSON.stringify(repo, null, 2),
}, },
], ],
}; };
} catch (error) { } 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 repository: ${errorMessage}`); console.error(`[GITEA-MCP] Error getting repository: ${errorMessage}`);
return { return {
content: [ content: [
{ {
type: "text", type: "text",
text: `Error getting repository: ${errorMessage}`, text: `Error getting repository: ${errorMessage}`,
}, },
], ],
error: errorMessage, error: errorMessage,
isError: true, isError: true,
}; };
} }
}, });
);
// Get pull requests // Get pull requests
server.tool( server.tool(
"list_pull_requests", "list_pull_requests",
"List pull requests in the repository", "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"), head: z.string().optional().describe("Head branch name"),
base: z.string().optional().describe("Base branch name"), base: z.string().optional().describe("Base branch name"),
page: z.number().optional().describe("Page number for pagination"), page: z.number().optional().describe("Page number for pagination"),
@@ -547,7 +625,8 @@ server.tool(
], ],
}; };
} catch (error) { } 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}`); console.error(`[GITEA-MCP] Error listing pull requests: ${errorMessage}`);
return { return {
content: [ content: [
@@ -572,7 +651,9 @@ server.tool(
}, },
async ({ pull_number }) => { async ({ pull_number }) => {
try { 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 { return {
content: [ content: [
@@ -583,7 +664,8 @@ server.tool(
], ],
}; };
} catch (error) { } 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}`); console.error(`[GITEA-MCP] Error getting pull request: ${errorMessage}`);
return { return {
content: [ content: [
@@ -608,12 +690,33 @@ server.tool(
body: z.string().optional().describe("Pull request body/description"), body: z.string().optional().describe("Pull request body/description"),
head: z.string().describe("Head branch name"), head: z.string().describe("Head branch name"),
base: z.string().describe("Base branch name"), base: z.string().describe("Base branch name"),
assignee: z.string().optional().describe("Username to assign the pull request to"), assignee: z
assignees: z.array(z.string()).optional().describe("Array of usernames to assign the pull request to"), .string()
milestone: z.number().optional().describe("Milestone ID to associate with the pull request"), .optional()
labels: z.array(z.string()).optional().describe("Array of label names to apply to the pull request"), .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 { try {
const pullData: any = { title, head, base }; const pullData: any = { title, head, base };
@@ -638,7 +741,8 @@ server.tool(
], ],
}; };
} catch (error) { } 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}`); console.error(`[GITEA-MCP] Error creating pull request: ${errorMessage}`);
return { return {
content: [ content: [

View File

@@ -10,9 +10,15 @@ export async function prepareMcpConfig(
console.log(`[MCP-INSTALL] Owner: ${owner}`); console.log(`[MCP-INSTALL] Owner: ${owner}`);
console.log(`[MCP-INSTALL] Repo: ${repo}`); console.log(`[MCP-INSTALL] Repo: ${repo}`);
console.log(`[MCP-INSTALL] Branch: ${branch}`); console.log(`[MCP-INSTALL] Branch: ${branch}`);
console.log(`[MCP-INSTALL] GitHub token: ${githubToken ? '***' : 'undefined'}`); console.log(
console.log(`[MCP-INSTALL] GITHUB_ACTION_PATH: ${process.env.GITHUB_ACTION_PATH}`); `[MCP-INSTALL] GitHub token: ${githubToken ? "***" : "undefined"}`,
console.log(`[MCP-INSTALL] GITHUB_WORKSPACE: ${process.env.GITHUB_WORKSPACE}`); );
console.log(
`[MCP-INSTALL] GITHUB_ACTION_PATH: ${process.env.GITHUB_ACTION_PATH}`,
);
console.log(
`[MCP-INSTALL] GITHUB_WORKSPACE: ${process.env.GITHUB_WORKSPACE}`,
);
try { try {
const mcpConfig = { const mcpConfig = {
@@ -29,7 +35,8 @@ export async function prepareMcpConfig(
REPO_NAME: repo, REPO_NAME: repo,
BRANCH_NAME: branch, BRANCH_NAME: branch,
REPO_DIR: process.env.GITHUB_WORKSPACE || process.cwd(), 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: { local_git_ops: {

View File

@@ -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] BRANCH_NAME: ${BRANCH_NAME}`);
console.log(`[LOCAL-GIT-MCP] REPO_DIR: ${REPO_DIR}`); 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] 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) { if (!REPO_OWNER || !REPO_NAME || !BRANCH_NAME) {
console.error( console.error(
@@ -63,7 +65,9 @@ function ensureGitUserConfigured(): void {
runGitCommand("git config user.email"); runGitCommand("git config user.email");
console.log(`[LOCAL-GIT-MCP] Git user.email already configured`); console.log(`[LOCAL-GIT-MCP] Git user.email already configured`);
} catch (error) { } 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"'); runGitCommand('git config user.email "claude@anthropic.com"');
} }
@@ -72,7 +76,9 @@ function ensureGitUserConfigured(): void {
runGitCommand("git config user.name"); runGitCommand("git config user.name");
console.log(`[LOCAL-GIT-MCP] Git user.name already configured`); console.log(`[LOCAL-GIT-MCP] Git user.name already configured`);
} catch (error) { } 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"'); runGitCommand('git config user.name "Claude"');
} }
} }
@@ -134,7 +140,9 @@ server.tool(
message: z.string().describe("Commit message"), message: z.string().describe("Commit message"),
}, },
async ({ files, 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 { try {
// Ensure git user is configured before committing // Ensure git user is configured before committing
ensureGitUserConfigured(); ensureGitUserConfigured();
@@ -151,7 +159,9 @@ server.tool(
console.log(`[LOCAL-GIT-MCP] Committing with message: ${message}`); console.log(`[LOCAL-GIT-MCP] Committing with message: ${message}`);
runGitCommand(`git commit -m "${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 { return {
content: [ content: [
{ {
@@ -353,7 +363,9 @@ server.tool("git_status", "Get the current git status", {}, async () => {
const currentBranch = runGitCommand("git rev-parse --abbrev-ref HEAD"); const currentBranch = runGitCommand("git rev-parse --abbrev-ref HEAD");
console.log(`[LOCAL-GIT-MCP] Current branch: ${currentBranch}`); 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 { return {
content: [ content: [