From 4b69e8485ab4982a406b7578c1ed81bfbb54e8ab Mon Sep 17 00:00:00 2001 From: Mark Wylde Date: Fri, 30 May 2025 22:41:48 +0100 Subject: [PATCH] Attempt to make this work --- README.md | 6 ++--- action.yml | 2 +- src/create-prompt/index.ts | 23 +++++++++------- src/github/operations/branch.ts | 32 +++++++++++++++++++--- src/mcp/local-git-ops-server.ts | 48 +++++++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 5315d74..72b8e51 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ jobs: | `direct_prompt` | Direct prompt for Claude to execute automatically without needing a trigger (for automated workflows) | No | - | | `timeout_minutes` | Timeout in minutes for execution | No | `30` | | `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - | -| `gitea_api_url` | Gitea API URL (e.g., `https://gitea.example.com/api/v1`) for Gitea installations. Leave empty for GitHub. | No | GitHub API | +| `gitea_api_url` | Gitea server URL (e.g., `https://gitea.example.com`) for Gitea installations. Leave empty for GitHub. | No | GitHub API | | `model` | Model to use (provider-specific format required for Bedrock/Vertex) | No | - | | `anthropic_model` | **DEPRECATED**: Use `model` instead. Kept for backward compatibility. | No | - | | `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` | @@ -96,7 +96,7 @@ This action has been enhanced to work with Gitea installations. The main differe 1. **Local Git Operations**: Instead of using API-based file operations (which have limited support in Gitea), this action uses local git commands to create branches, commit files, and push changes. -2. **API URL Configuration**: You must specify your Gitea API URL using the `gitea_api_url` input. +2. **API URL Configuration**: You must specify your Gitea server URL using the `gitea_api_url` input. ### Example Gitea Workflow @@ -114,7 +114,7 @@ jobs: - uses: anthropics/claude-code-action@beta with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - gitea_api_url: "https://gitea.example.com/api/v1" + gitea_api_url: "https://gitea.example.com" github_token: ${{ secrets.GITEA_TOKEN }} ``` diff --git a/action.yml b/action.yml index 9815ae6..48db405 100644 --- a/action.yml +++ b/action.yml @@ -48,7 +48,7 @@ inputs: description: "GitHub token with repo and pull request permissions (defaults to GITHUB_TOKEN)" required: false gitea_api_url: - description: "Gitea API URL (e.g., https://gitea.example.com/api/v1, defaults to GitHub API)" + description: "Gitea server URL (e.g., https://gitea.example.com, defaults to GitHub API)" required: false use_bedrock: description: "Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API" diff --git a/src/create-prompt/index.ts b/src/create-prompt/index.ts index b866efb..1d155a8 100644 --- a/src/create-prompt/index.ts +++ b/src/create-prompt/index.ts @@ -29,8 +29,11 @@ const BASE_ALLOWED_TOOLS = [ "LS", "Read", "Write", - "mcp__github_file_ops__commit_files", - "mcp__github_file_ops__delete_files", + "mcp__local_git_ops__commit_files", + "mcp__local_git_ops__delete_files", + "mcp__local_git_ops__push_branch", + "mcp__local_git_ops__create_pull_request", + "mcp__local_git_ops__git_status", ]; const DISALLOWED_TOOLS = ["WebSearch", "WebFetch"]; @@ -530,13 +533,13 @@ ${context.directPrompt ? ` - DIRECT INSTRUCTION: A direct instruction was prov ${ eventData.isPR && !eventData.claudeBranch ? ` - - Push directly using mcp__github_file_ops__commit_files to the existing branch (works for both new and existing files). - - Use mcp__github_file_ops__commit_files to commit files atomically in a single commit (supports single or multiple files). + - Push directly 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). - 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.` : ` - You are already on the correct branch (${eventData.claudeBranch || "the PR branch"}). Do not create a new branch. - - Push changes directly to the current branch using mcp__github_file_ops__commit_files (works for both new and existing files) - - Use mcp__github_file_ops__commit_files to commit files atomically in a single commit (supports single or multiple files). + - Push changes directly to the current branch 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). - 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. ${ eventData.claudeBranch @@ -571,7 +574,7 @@ ${context.directPrompt ? ` - DIRECT INSTRUCTION: A direct instruction was prov - Always update the GitHub comment to reflect the current todo state. - When all todos are completed, remove the spinner and add a brief summary of what was accomplished, and what was not done. - Note: If you see previous Claude comments with headers like "**Claude finished @user's task**" followed by "---", do not include this in your comment. The system adds this automatically. - - If you changed any files locally, you must update them in the remote branch via mcp__github_file_ops__commit_files before saying that you're done. + - If you changed any files locally, you must update them in the remote branch via mcp__local_git_ops__commit_files before saying that you're done. ${eventData.claudeBranch ? `- If you created anything in your branch, your comment must include the PR URL with prefilled title and body mentioned above.` : ""} Important Notes: @@ -581,10 +584,10 @@ Important Notes: - You communicate exclusively by editing your single comment - not through any other means. - 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__github_file_ops__commit_files for making commits (works for both new and existing files, single or multiple). Use mcp__github_file_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: - - mcp__github_file_ops__commit_files: {"files": ["path/to/file1.js", "path/to/file2.py"], "message": "feat: add new feature"} - - mcp__github_file_ops__delete_files: {"files": ["path/to/old.js"], "message": "chore: remove deprecated file"} + - mcp__local_git_ops__commit_files: {"files": ["path/to/file1.js", "path/to/file2.py"], "message": "feat: add new feature"} + - mcp__local_git_ops__delete_files: {"files": ["path/to/old.js"], "message": "chore: remove deprecated file"} - Display the todo list as a checklist in the GitHub comment and mark things off as you go. - REPOSITORY SETUP INSTRUCTIONS: The repository's CLAUDE.md file(s) contain critical repo-specific setup instructions, development guidelines, and preferences. Always read and follow these files, particularly the root CLAUDE.md, as they provide essential context for working with the codebase effectively. - Use h3 headers (###) for section titles in your comments, not h1 headers (#). diff --git a/src/github/operations/branch.ts b/src/github/operations/branch.ts index 571d476..ea421d6 100644 --- a/src/github/operations/branch.ts +++ b/src/github/operations/branch.ts @@ -100,6 +100,10 @@ export async function setupBranch( console.log(`Working in directory: ${repoDir}`); try { + // Check if we're in a git repository + console.log(`Checking if we're in a git repository...`); + await $`git status`; + // Ensure we have the latest version of the source branch console.log(`Fetching latest ${sourceBranch}...`); await $`git fetch origin ${sourceBranch}`; @@ -109,18 +113,38 @@ export async function setupBranch( await $`git checkout ${sourceBranch}`; // Pull latest changes + console.log(`Pulling latest changes for ${sourceBranch}...`); await $`git pull origin ${sourceBranch}`; // Create and checkout the new branch console.log(`Creating new branch: ${newBranch}`); await $`git checkout -b ${newBranch}`; - console.log(`Successfully created and checked out branch: ${newBranch}`); - } catch (gitError: any) { + // Verify the branch was created + const currentBranch = await $`git branch --show-current`; console.log( - `Local git operations completed. Branch ${newBranch} ready for use.`, + `Current branch after creation: ${currentBranch.stdout.trim()}`, + ); + + if (currentBranch.stdout.trim() === newBranch) { + console.log( + `✅ Successfully created and checked out branch: ${newBranch}`, + ); + } else { + throw new Error( + `Branch creation failed. Expected ${newBranch}, got ${currentBranch.stdout.trim()}`, + ); + } + } catch (gitError: any) { + console.error(`❌ Git operations failed:`, gitError); + console.error(`Error message: ${gitError.message}`); + console.error(`Error stdout: ${gitError.stdout}`); + console.error(`Error stderr: ${gitError.stderr}`); + + // This is a critical failure - the branch MUST be created for Claude to work + throw new Error( + `Failed to create branch ${newBranch}: ${gitError.message}`, ); - // Don't fail here - the branch will be created when files are committed } console.log(`Branch setup completed for: ${newBranch}`); diff --git a/src/mcp/local-git-ops-server.ts b/src/mcp/local-git-ops-server.ts index c361f49..ed285a3 100644 --- a/src/mcp/local-git-ops-server.ts +++ b/src/mcp/local-git-ops-server.ts @@ -258,6 +258,54 @@ server.tool( }, ); +// Delete files tool +server.tool( + "delete_files", + "Delete one or more files and commit the deletion using local git operations", + { + files: z + .array(z.string()) + .describe( + 'Array of file paths relative to repository root (e.g. ["src/old-file.js", "docs/deprecated.md"])', + ), + message: z.string().describe("Commit message for the deletion"), + }, + async ({ files, message }) => { + try { + // Remove the specified files + for (const file of files) { + const filePath = file.startsWith("/") ? file.slice(1) : file; + runGitCommand(`git rm "${filePath}"`); + } + + // Commit the deletions + runGitCommand(`git commit -m "${message}"`); + + return { + content: [ + { + type: "text", + text: `Successfully deleted and committed ${files.length} file(s): ${files.join(", ")}`, + }, + ], + }; + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + return { + content: [ + { + type: "text", + text: `Error deleting files: ${errorMessage}`, + }, + ], + error: errorMessage, + isError: true, + }; + } + }, +); + // Get git status tool server.tool("git_status", "Get the current git status", {}, async () => { try {