mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-23 15:04:13 +08:00
Compare commits
4 Commits
testbranch
...
claude/iss
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0ca8faf6b | ||
|
|
e8d2b8d5df | ||
|
|
a8a36ced96 | ||
|
|
180a1b6680 |
1
.github/workflows/claude.yml
vendored
1
.github/workflows/claude.yml
vendored
@@ -36,3 +36,4 @@ jobs:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
allowed_tools: "Bash(bun install),Bash(bun test:*),Bash(bun run format),Bash(bun typecheck)"
|
||||
custom_instructions: "You have also been granted tools for editing files and running bun commands (install, run, test, typecheck) for testing your changes: bun install, bun test, bun run format, bun typecheck."
|
||||
model: "claude-opus-4-20250514"
|
||||
|
||||
2
FAQ.md
2
FAQ.md
@@ -6,7 +6,7 @@ This FAQ addresses common questions and gotchas when using the Claude Code GitHu
|
||||
|
||||
### Why doesn't tagging @claude from my automated workflow work?
|
||||
|
||||
The `github-actions` user (and other GitHub Apps/bots) cannot trigger subsequent GitHub Actions workflows. This is a GitHub security feature to prevent infinite loops. To make this work, you need to use a Personal Access Token (PAT) instead, which will act as a regular user. When posting a comment on an issue or PR from your workflow, use your PAT instead of the `GITHUB_TOKEN` generated in your workflow.
|
||||
The `github-actions` user cannot trigger subsequent GitHub Actions workflows. This is a GitHub security feature to prevent infinite loops. To make this work, you need to use a Personal Access Token (PAT) instead, which will act as a regular user, or use a separate app token of your own. When posting a comment on an issue or PR from your workflow, use your PAT instead of the `GITHUB_TOKEN` generated in your workflow.
|
||||
|
||||
### Why does Claude say I don't have permission to trigger it?
|
||||
|
||||
|
||||
@@ -31,13 +31,24 @@ const BASE_ALLOWED_TOOLS = [
|
||||
"Write",
|
||||
"mcp__github_file_ops__commit_files",
|
||||
"mcp__github_file_ops__delete_files",
|
||||
"mcp__github_file_ops__update_claude_comment",
|
||||
];
|
||||
const DISALLOWED_TOOLS = ["WebSearch", "WebFetch"];
|
||||
|
||||
export function buildAllowedToolsString(customAllowedTools?: string): string {
|
||||
export function buildAllowedToolsString(
|
||||
eventData: EventData,
|
||||
customAllowedTools?: string,
|
||||
): string {
|
||||
let baseTools = [...BASE_ALLOWED_TOOLS];
|
||||
|
||||
// Add the appropriate comment tool based on event type
|
||||
if (eventData.eventName === "pull_request_review_comment") {
|
||||
// For inline PR review comments, only use PR comment tool
|
||||
baseTools.push("mcp__github__update_pull_request_comment");
|
||||
} else {
|
||||
// For all other events (issue comments, PR reviews, issues), use issue comment tool
|
||||
baseTools.push("mcp__github__update_issue_comment");
|
||||
}
|
||||
|
||||
let allAllowedTools = baseTools.join(",");
|
||||
if (customAllowedTools) {
|
||||
allAllowedTools = `${allAllowedTools},${customAllowedTools}`;
|
||||
@@ -350,6 +361,7 @@ export function getEventTypeAndContext(envVars: PreparedContext): {
|
||||
export function generatePrompt(
|
||||
context: PreparedContext,
|
||||
githubData: FetchDataResult,
|
||||
isReusedBranch?: boolean,
|
||||
): string {
|
||||
const {
|
||||
contextData,
|
||||
@@ -436,15 +448,33 @@ ${sanitizeContent(context.directPrompt)}
|
||||
</direct_prompt>`
|
||||
: ""
|
||||
}
|
||||
${`<comment_tool_info>
|
||||
IMPORTANT: You have been provided with the mcp__github_file_ops__update_claude_comment tool to update your comment. This tool automatically handles both issue and PR comments.
|
||||
${
|
||||
eventData.eventName === "pull_request_review_comment"
|
||||
? `<comment_tool_info>
|
||||
IMPORTANT: For this inline PR review comment, you have been provided with ONLY the mcp__github__update_pull_request_comment tool to update this specific review comment.
|
||||
|
||||
Tool usage example for mcp__github_file_ops__update_claude_comment:
|
||||
Tool usage example for mcp__github__update_pull_request_comment:
|
||||
{
|
||||
"owner": "${context.repository.split("/")[0]}",
|
||||
"repo": "${context.repository.split("/")[1]}",
|
||||
"commentId": ${eventData.commentId || context.claudeCommentId},
|
||||
"body": "Your comment text here"
|
||||
}
|
||||
Only the body parameter is required - the tool automatically knows which comment to update.
|
||||
</comment_tool_info>`}
|
||||
All four parameters (owner, repo, commentId, body) are required.
|
||||
</comment_tool_info>`
|
||||
: `<comment_tool_info>
|
||||
IMPORTANT: For this event type, you have been provided with ONLY the mcp__github__update_issue_comment tool to update comments.
|
||||
|
||||
Tool usage example for mcp__github__update_issue_comment:
|
||||
{
|
||||
"owner": "${context.repository.split("/")[0]}",
|
||||
"repo": "${context.repository.split("/")[1]}",
|
||||
"commentId": ${context.claudeCommentId},
|
||||
"body": "Your comment text here"
|
||||
}
|
||||
All four parameters (owner, repo, commentId, body) are required.
|
||||
</comment_tool_info>`
|
||||
}
|
||||
|
||||
Your task is to analyze the context, understand the request, and provide helpful responses and/or implement code changes as needed.
|
||||
|
||||
@@ -458,7 +488,7 @@ Follow these steps:
|
||||
1. Create a Todo List:
|
||||
- Use your GitHub comment to maintain a detailed task list based on the request.
|
||||
- Format todos as a checklist (- [ ] for incomplete, - [x] for complete).
|
||||
- Update the comment using mcp__github_file_ops__update_claude_comment with each task completion.
|
||||
- Update the comment using ${eventData.eventName === "pull_request_review_comment" ? "mcp__github__update_pull_request_comment" : "mcp__github__update_issue_comment"} with each task completion.
|
||||
|
||||
2. Gather Context:
|
||||
- Analyze the pre-fetched data provided above.
|
||||
@@ -488,11 +518,11 @@ ${context.directPrompt ? ` - DIRECT INSTRUCTION: A direct instruction was prov
|
||||
- Look for bugs, security issues, performance problems, and other issues
|
||||
- Suggest improvements for readability and maintainability
|
||||
- Check for best practices and coding standards
|
||||
- Reference specific code sections with file paths and line numbers${eventData.isPR ? "\n - AFTER reading files and analyzing code, you MUST call mcp__github_file_ops__update_claude_comment to post your review" : ""}
|
||||
- Reference specific code sections with file paths and line numbers${eventData.isPR ? "\n - AFTER reading files and analyzing code, you MUST call mcp__github__update_issue_comment to post your review" : ""}
|
||||
- Formulate a concise, technical, and helpful response based on the context.
|
||||
- Reference specific code with inline formatting or code blocks.
|
||||
- Include relevant file paths and line numbers when applicable.
|
||||
- ${eventData.isPR ? "IMPORTANT: Submit your review feedback by updating the Claude comment using mcp__github_file_ops__update_claude_comment. This will be displayed as your PR review." : "Remember that this feedback must be posted to the GitHub comment using mcp__github_file_ops__update_claude_comment."}
|
||||
- ${eventData.isPR ? "IMPORTANT: Submit your review feedback by updating the Claude comment. This will be displayed as your PR review." : "Remember that this feedback must be posted to the GitHub comment."}
|
||||
|
||||
B. For Straightforward Changes:
|
||||
- Use file system tools to make the change locally.
|
||||
@@ -505,7 +535,7 @@ ${context.directPrompt ? ` - DIRECT INSTRUCTION: A direct instruction was prov
|
||||
- Use mcp__github_file_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.
|
||||
- You are already on the correct branch (${eventData.claudeBranch || "the PR branch"}). Do not create a new branch.${isReusedBranch ? `\n - NOTE: This branch (${eventData.claudeBranch}) was reused from a previous Claude invocation on this issue. It may already contain some work.` : ''}
|
||||
- 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).
|
||||
- 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.
|
||||
@@ -547,8 +577,8 @@ ${context.directPrompt ? ` - DIRECT INSTRUCTION: A direct instruction was prov
|
||||
|
||||
Important Notes:
|
||||
- All communication must happen through GitHub PR comments.
|
||||
- Never create new comments. Only update the existing comment using mcp__github_file_ops__update_claude_comment.
|
||||
- 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_file_ops__update_claude_comment. Do NOT just respond with a normal response, the user will not see it." : ""}
|
||||
- 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;" />
|
||||
${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.`}
|
||||
@@ -612,6 +642,7 @@ export async function createPrompt(
|
||||
claudeBranch: string | undefined,
|
||||
githubData: FetchDataResult,
|
||||
context: ParsedGitHubContext,
|
||||
isReusedBranch?: boolean,
|
||||
) {
|
||||
try {
|
||||
const preparedContext = prepareContext(
|
||||
@@ -624,7 +655,7 @@ export async function createPrompt(
|
||||
await mkdir("/tmp/claude-prompts", { recursive: true });
|
||||
|
||||
// Generate the prompt
|
||||
const promptContent = generatePrompt(preparedContext, githubData);
|
||||
const promptContent = generatePrompt(preparedContext, githubData, isReusedBranch);
|
||||
|
||||
// Log the final prompt to console
|
||||
console.log("===== FINAL PROMPT =====");
|
||||
@@ -636,6 +667,7 @@ export async function createPrompt(
|
||||
|
||||
// Set allowed tools
|
||||
const allAllowedTools = buildAllowedToolsString(
|
||||
preparedContext.eventData,
|
||||
preparedContext.allowedTools,
|
||||
);
|
||||
const allDisallowedTools = buildDisallowedToolsString(
|
||||
|
||||
@@ -81,6 +81,7 @@ async function run() {
|
||||
branchInfo.claudeBranch,
|
||||
githubData,
|
||||
context,
|
||||
branchInfo.isReusedBranch,
|
||||
);
|
||||
|
||||
// Step 11: Get MCP configuration
|
||||
@@ -89,7 +90,6 @@ async function run() {
|
||||
context.repository.owner,
|
||||
context.repository.repo,
|
||||
branchInfo.currentBranch,
|
||||
commentId.toString(),
|
||||
);
|
||||
core.setOutput("mcp_config", mcpConfig);
|
||||
} catch (error) {
|
||||
|
||||
@@ -87,13 +87,29 @@ async function run() {
|
||||
const currentBody = comment.body ?? "";
|
||||
|
||||
// Check if we need to add branch link for new branches
|
||||
const { shouldDeleteBranch, branchLink } = await checkAndDeleteEmptyBranch(
|
||||
octokit,
|
||||
owner,
|
||||
repo,
|
||||
claudeBranch,
|
||||
baseBranch,
|
||||
);
|
||||
// For issues, we don't delete branches anymore to allow reuse
|
||||
const skipBranchDeletion = !context.isPR && claudeBranch;
|
||||
|
||||
let shouldDeleteBranch = false;
|
||||
let branchLink = "";
|
||||
|
||||
if (skipBranchDeletion) {
|
||||
// For issue branches, just add the branch link without checking for deletion
|
||||
const branchUrl = `${GITHUB_SERVER_URL}/${owner}/${repo}/tree/${claudeBranch}`;
|
||||
branchLink = `\n[View branch](${branchUrl})`;
|
||||
console.log(`Keeping issue branch ${claudeBranch} for potential reuse`);
|
||||
} else {
|
||||
// For PR branches, use the existing cleanup logic
|
||||
const result = await checkAndDeleteEmptyBranch(
|
||||
octokit,
|
||||
owner,
|
||||
repo,
|
||||
claudeBranch,
|
||||
baseBranch,
|
||||
);
|
||||
shouldDeleteBranch = result.shouldDeleteBranch;
|
||||
branchLink = result.branchLink;
|
||||
}
|
||||
|
||||
// Check if we need to add PR URL when we have a new branch
|
||||
let prLink = "";
|
||||
|
||||
@@ -17,6 +17,7 @@ export type BranchInfo = {
|
||||
baseBranch: string;
|
||||
claudeBranch?: string;
|
||||
currentBranch: string;
|
||||
isReusedBranch?: boolean;
|
||||
};
|
||||
|
||||
export async function setupBranch(
|
||||
@@ -79,57 +80,127 @@ export async function setupBranch(
|
||||
|
||||
// Creating a new branch for either an issue or closed/merged PR
|
||||
const entityType = isPR ? "pr" : "issue";
|
||||
console.log(
|
||||
`Creating new branch for ${entityType} #${entityNumber} from source branch: ${sourceBranch}...`,
|
||||
);
|
||||
|
||||
const timestamp = new Date()
|
||||
.toISOString()
|
||||
.replace(/[:-]/g, "")
|
||||
.replace(/\.\d{3}Z/, "")
|
||||
.split("T")
|
||||
.join("_");
|
||||
|
||||
const newBranch = `claude/${entityType}-${entityNumber}-${timestamp}`;
|
||||
|
||||
try {
|
||||
// Get the SHA of the source branch
|
||||
const sourceBranchRef = await octokits.rest.git.getRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `heads/${sourceBranch}`,
|
||||
});
|
||||
|
||||
const currentSHA = sourceBranchRef.data.object.sha;
|
||||
|
||||
console.log(`Current SHA: ${currentSHA}`);
|
||||
|
||||
// Create branch using GitHub API
|
||||
await octokits.rest.git.createRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `refs/heads/${newBranch}`,
|
||||
sha: currentSHA,
|
||||
});
|
||||
|
||||
// Checkout the new branch (shallow fetch for performance)
|
||||
await $`git fetch origin --depth=1 ${newBranch}`;
|
||||
await $`git checkout ${newBranch}`;
|
||||
|
||||
|
||||
// For issues, check if a Claude branch already exists
|
||||
let branchToUse: string | null = null;
|
||||
let isReusedBranch = false;
|
||||
|
||||
if (!isPR) {
|
||||
// Check for existing Claude branches for this issue
|
||||
try {
|
||||
// Use GraphQL to efficiently search for branches with a specific prefix
|
||||
const query = `
|
||||
query($owner: String!, $repo: String!, $prefix: String!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
refs(refPrefix: "refs/heads/", query: $prefix, first: 100) {
|
||||
nodes {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const response = await octokits.graphql<{
|
||||
repository: {
|
||||
refs: {
|
||||
nodes: Array<{ name: string }>;
|
||||
};
|
||||
};
|
||||
}>(query, {
|
||||
owner,
|
||||
repo,
|
||||
prefix: `claude/issue-${entityNumber}-`,
|
||||
});
|
||||
|
||||
const branches = response.repository.refs.nodes;
|
||||
|
||||
if (branches.length > 0) {
|
||||
// Use the first matching branch (could be sorted by date in future)
|
||||
branchToUse = branches[0].name;
|
||||
isReusedBranch = true;
|
||||
console.log(`Found existing Claude branch for issue #${entityNumber}: ${branchToUse}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error checking for existing branches:", error);
|
||||
// Continue with new branch creation if check fails
|
||||
}
|
||||
}
|
||||
|
||||
// If no existing branch found or this is a PR, create a new branch
|
||||
if (!branchToUse) {
|
||||
console.log(
|
||||
`Successfully created and checked out new branch: ${newBranch}`,
|
||||
`Creating new branch for ${entityType} #${entityNumber} from source branch: ${sourceBranch}...`,
|
||||
);
|
||||
|
||||
const timestamp = new Date()
|
||||
.toISOString()
|
||||
.replace(/[:-]/g, "")
|
||||
.replace(/\.\d{3}Z/, "")
|
||||
.split("T")
|
||||
.join("_");
|
||||
|
||||
branchToUse = `claude/${entityType}-${entityNumber}-${timestamp}`;
|
||||
}
|
||||
|
||||
try {
|
||||
if (isReusedBranch) {
|
||||
// For existing branches, just checkout
|
||||
console.log(`Checking out existing branch: ${branchToUse}`);
|
||||
|
||||
// Fetch the branch with more depth to allow for context
|
||||
await $`git fetch origin --depth=20 ${branchToUse}`;
|
||||
await $`git checkout ${branchToUse}`;
|
||||
|
||||
console.log(
|
||||
`Successfully checked out existing branch: ${branchToUse}`,
|
||||
);
|
||||
console.log(
|
||||
`Note: This is a reused branch from a previous Claude invocation on issue #${entityNumber}`,
|
||||
);
|
||||
} else {
|
||||
// Get the SHA of the source branch
|
||||
const sourceBranchRef = await octokits.rest.git.getRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `heads/${sourceBranch}`,
|
||||
});
|
||||
|
||||
const currentSHA = sourceBranchRef.data.object.sha;
|
||||
|
||||
console.log(`Current SHA: ${currentSHA}`);
|
||||
|
||||
// Create branch using GitHub API
|
||||
await octokits.rest.git.createRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `refs/heads/${branchToUse}`,
|
||||
sha: currentSHA,
|
||||
});
|
||||
|
||||
// Checkout the new branch (shallow fetch for performance)
|
||||
await $`git fetch origin --depth=1 ${branchToUse}`;
|
||||
await $`git checkout ${branchToUse}`;
|
||||
|
||||
console.log(
|
||||
`Successfully created and checked out new branch: ${branchToUse}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Set outputs for GitHub Actions
|
||||
core.setOutput("CLAUDE_BRANCH", newBranch);
|
||||
core.setOutput("CLAUDE_BRANCH", branchToUse);
|
||||
core.setOutput("BASE_BRANCH", sourceBranch);
|
||||
if (isReusedBranch) {
|
||||
core.setOutput("IS_REUSED_BRANCH", "true");
|
||||
}
|
||||
return {
|
||||
baseBranch: sourceBranch,
|
||||
claudeBranch: newBranch,
|
||||
currentBranch: newBranch,
|
||||
claudeBranch: branchToUse,
|
||||
currentBranch: branchToUse,
|
||||
isReusedBranch,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error creating branch:", error);
|
||||
console.error("Error setting up branch:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import { readFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import fetch from "node-fetch";
|
||||
import { GITHUB_API_URL } from "../github/api/config";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
|
||||
type GitHubRef = {
|
||||
object: {
|
||||
@@ -440,106 +439,6 @@ server.tool(
|
||||
},
|
||||
);
|
||||
|
||||
// Update Claude comment tool
|
||||
server.tool(
|
||||
"update_claude_comment",
|
||||
"Update the Claude comment with progress and results (automatically handles both issue and PR comments)",
|
||||
{
|
||||
body: z.string().describe("The updated comment content"),
|
||||
},
|
||||
async ({ body }) => {
|
||||
try {
|
||||
const githubToken = process.env.GITHUB_TOKEN;
|
||||
const claudeCommentId = process.env.CLAUDE_COMMENT_ID;
|
||||
const eventName = process.env.GITHUB_EVENT_NAME;
|
||||
|
||||
if (!githubToken) {
|
||||
throw new Error("GITHUB_TOKEN environment variable is required");
|
||||
}
|
||||
if (!claudeCommentId) {
|
||||
throw new Error("CLAUDE_COMMENT_ID environment variable is required");
|
||||
}
|
||||
|
||||
const owner = REPO_OWNER;
|
||||
const repo = REPO_NAME;
|
||||
const commentId = parseInt(claudeCommentId, 10);
|
||||
|
||||
// Create Octokit instance
|
||||
const octokit = new Octokit({
|
||||
auth: githubToken,
|
||||
});
|
||||
|
||||
// Determine if this is a PR review comment based on event type
|
||||
const isPullRequestReviewComment =
|
||||
eventName === "pull_request_review_comment";
|
||||
|
||||
let response;
|
||||
|
||||
try {
|
||||
if (isPullRequestReviewComment) {
|
||||
// Try PR review comment API first
|
||||
response = await octokit.rest.pulls.updateReviewComment({
|
||||
owner,
|
||||
repo,
|
||||
comment_id: commentId,
|
||||
body,
|
||||
});
|
||||
} else {
|
||||
// Use issue comment API (works for both issues and PR general comments)
|
||||
response = await octokit.rest.issues.updateComment({
|
||||
owner,
|
||||
repo,
|
||||
comment_id: commentId,
|
||||
body,
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
// If PR review comment update fails with 404, fall back to issue comment API
|
||||
if (isPullRequestReviewComment && error.status === 404) {
|
||||
response = await octokit.rest.issues.updateComment({
|
||||
owner,
|
||||
repo,
|
||||
comment_id: commentId,
|
||||
body,
|
||||
});
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{
|
||||
id: response.data.id,
|
||||
html_url: response.data.html_url,
|
||||
updated_at: response.data.updated_at,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error: ${errorMessage}`,
|
||||
},
|
||||
],
|
||||
error: errorMessage,
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
async function runServer() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
|
||||
@@ -5,7 +5,6 @@ export async function prepareMcpConfig(
|
||||
owner: string,
|
||||
repo: string,
|
||||
branch: string,
|
||||
claudeCommentId?: string,
|
||||
): Promise<string> {
|
||||
try {
|
||||
const mcpConfig = {
|
||||
@@ -36,9 +35,6 @@ export async function prepareMcpConfig(
|
||||
REPO_NAME: repo,
|
||||
BRANCH_NAME: branch,
|
||||
REPO_DIR: process.env.GITHUB_WORKSPACE || process.cwd(),
|
||||
...(claudeCommentId && { CLAUDE_COMMENT_ID: claudeCommentId }),
|
||||
GITHUB_EVENT_NAME: process.env.GITHUB_EVENT_NAME || "",
|
||||
IS_PR: process.env.IS_PR || "false",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
buildDisallowedToolsString,
|
||||
} from "../src/create-prompt";
|
||||
import type { PreparedContext } from "../src/create-prompt";
|
||||
import type { EventData } from "../src/create-prompt/types";
|
||||
|
||||
describe("generatePrompt", () => {
|
||||
const mockGitHubData = {
|
||||
@@ -618,7 +619,15 @@ describe("getEventTypeAndContext", () => {
|
||||
|
||||
describe("buildAllowedToolsString", () => {
|
||||
test("should return issue comment tool for regular events", () => {
|
||||
const result = buildAllowedToolsString();
|
||||
const mockEventData: EventData = {
|
||||
eventName: "issue_comment",
|
||||
commentId: "123",
|
||||
isPR: true,
|
||||
prNumber: "456",
|
||||
commentBody: "Test comment",
|
||||
};
|
||||
|
||||
const result = buildAllowedToolsString(mockEventData);
|
||||
|
||||
// The base tools should be in the result
|
||||
expect(result).toContain("Edit");
|
||||
@@ -627,15 +636,22 @@ describe("buildAllowedToolsString", () => {
|
||||
expect(result).toContain("LS");
|
||||
expect(result).toContain("Read");
|
||||
expect(result).toContain("Write");
|
||||
expect(result).toContain("mcp__github_file_ops__update_claude_comment");
|
||||
expect(result).not.toContain("mcp__github__update_issue_comment");
|
||||
expect(result).toContain("mcp__github__update_issue_comment");
|
||||
expect(result).not.toContain("mcp__github__update_pull_request_comment");
|
||||
expect(result).toContain("mcp__github_file_ops__commit_files");
|
||||
expect(result).toContain("mcp__github_file_ops__delete_files");
|
||||
});
|
||||
|
||||
test("should return PR comment tool for inline review comments", () => {
|
||||
const result = buildAllowedToolsString();
|
||||
const mockEventData: EventData = {
|
||||
eventName: "pull_request_review_comment",
|
||||
isPR: true,
|
||||
prNumber: "456",
|
||||
commentBody: "Test review comment",
|
||||
commentId: "789",
|
||||
};
|
||||
|
||||
const result = buildAllowedToolsString(mockEventData);
|
||||
|
||||
// The base tools should be in the result
|
||||
expect(result).toContain("Edit");
|
||||
@@ -644,16 +660,23 @@ describe("buildAllowedToolsString", () => {
|
||||
expect(result).toContain("LS");
|
||||
expect(result).toContain("Read");
|
||||
expect(result).toContain("Write");
|
||||
expect(result).toContain("mcp__github_file_ops__update_claude_comment");
|
||||
expect(result).not.toContain("mcp__github__update_issue_comment");
|
||||
expect(result).not.toContain("mcp__github__update_pull_request_comment");
|
||||
expect(result).toContain("mcp__github__update_pull_request_comment");
|
||||
expect(result).toContain("mcp__github_file_ops__commit_files");
|
||||
expect(result).toContain("mcp__github_file_ops__delete_files");
|
||||
});
|
||||
|
||||
test("should append custom tools when provided", () => {
|
||||
const mockEventData: EventData = {
|
||||
eventName: "issue_comment",
|
||||
commentId: "123",
|
||||
isPR: true,
|
||||
prNumber: "456",
|
||||
commentBody: "Test comment",
|
||||
};
|
||||
|
||||
const customTools = "Tool1,Tool2,Tool3";
|
||||
const result = buildAllowedToolsString(customTools);
|
||||
const result = buildAllowedToolsString(mockEventData, customTools);
|
||||
|
||||
// Base tools should be present
|
||||
expect(result).toContain("Edit");
|
||||
|
||||
Reference in New Issue
Block a user