mirror of
https://github.com/markwylde/claude-code-gitea-action.git
synced 2026-02-20 02:22:49 +08:00
Attempt to make this work
This commit is contained in:
@@ -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: <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.`}
|
||||
- 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.
|
||||
|
||||
|
||||
@@ -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`,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GITEA_SERVER_URL } from "../../api/config";
|
||||
|
||||
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(
|
||||
owner: string,
|
||||
|
||||
@@ -36,13 +36,13 @@ export async function getBranchSha(branchName: string): Promise<string | null> {
|
||||
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<string | null> {
|
||||
/**
|
||||
* 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<boolean> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<any> {
|
||||
const url = `${GITEA_API_URL}${endpoint}`;
|
||||
console.log(`[GITEA-MCP] Making ${method} request to: ${url}`);
|
||||
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
"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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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: [
|
||||
|
||||
Reference in New Issue
Block a user