Compare commits

...

8 Commits

Author SHA1 Message Date
Ashwin Bhat
32a6163092 tmp 2025-07-15 15:51:39 -07:00
Ashwin Bhat
a9d9ad3612 feat: add settings input support (#276)
- Add settings input to action.yml that accepts JSON string or file path
- Pass settings parameter to claude-code-base-action
- Update README with comprehensive settings documentation
- Add link to official Claude Code settings documentation
- Document precedence rules for model and tool permissions

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-07-15 14:00:26 -07:00
GitHub Actions
4824494f4d chore: update claude-code-base-action to v0.0.35 2025-07-15 18:54:33 +00:00
Ashwin Bhat
c09fc691c5 docs: add custom GitHub App setup instructions (#267)
Add comprehensive section explaining how to create and use a custom GitHub App
instead of the official Claude app. This is particularly useful for users with
restrictive organization policies or those using AWS Bedrock/Google Vertex AI.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-07-14 17:17:56 -07:00
GitHub Actions
b3c6de94ea chore: update claude-code-base-action to v0.0.34 2025-07-14 15:59:55 +00:00
Jay Derinbogaz
b92e56a96b refactor: update branch naming convention for Kubernetes compatibility (#249)
* refactor: update branch naming convention for Kubernetes compatibility

- Changed timestamp format in branch names to a shorter, Kubernetes-compatible style (lowercase, hyphens only).
- Updated related tests to reflect new branch name format.
- Ensured branch names are limited to a maximum of 50 characters to comply with Kubernetes naming requirements.

* refactor: clean up timestamp formatting in branch naming logic

- Removed unnecessary whitespace and standardized string formatting for the Kubernetes-compatible timestamp in branch names.
- Ensured consistency in the use of double quotes for string literals.
2025-07-12 11:30:49 -07:00
David Wells
b6868bfc27 Expose the created branch for downstream usage (#237)
* Expose the created branch for downstream usage

* run bun format
2025-07-11 10:15:41 -07:00
Allen Li
0f9a2c4dc3 fix: add GITHUB_API_URL to all Octokit client instantiations (#243)
Not all Octokit client instantiations were respecting GITHUB_API_URL, so
these tools would fail on enterprise.
2025-07-11 07:46:23 -07:00
10 changed files with 240 additions and 92 deletions

140
README.md
View File

@@ -35,6 +35,86 @@ This command will guide you through setting up the GitHub app and required secre
- Or `CLAUDE_CODE_OAUTH_TOKEN` for OAuth token authentication (Pro and Max users can generate this by running `claude setup-token` locally)
3. Copy the workflow file from [`examples/claude.yml`](./examples/claude.yml) into your repository's `.github/workflows/`
### Using a Custom GitHub App
If you prefer not to install the official Claude app, you can create your own GitHub App to use with this action. This gives you complete control over permissions and access.
**When you may want to use a custom GitHub App:**
- You need more restrictive permissions than the official app
- Organization policies prevent installing third-party apps
- You're using AWS Bedrock or Google Vertex AI
**Steps to create and use a custom GitHub App:**
1. **Create a new GitHub App:**
- Go to https://github.com/settings/apps (for personal apps) or your organization's settings
- Click "New GitHub App"
- Configure the app with these minimum permissions:
- **Repository permissions:**
- Contents: Read & Write
- Issues: Read & Write
- Pull requests: Read & Write
- **Account permissions:** None required
- Set "Where can this GitHub App be installed?" to your preference
- Create the app
2. **Generate and download a private key:**
- After creating the app, scroll down to "Private keys"
- Click "Generate a private key"
- Download the `.pem` file (keep this secure!)
3. **Install the app on your repository:**
- Go to the app's settings page
- Click "Install App"
- Select the repositories where you want to use Claude
4. **Add the app credentials to your repository secrets:**
- Go to your repository's Settings → Secrets and variables → Actions
- Add these secrets:
- `APP_ID`: Your GitHub App's ID (found in the app settings)
- `APP_PRIVATE_KEY`: The contents of the downloaded `.pem` file
5. **Update your workflow to use the custom app:**
```yaml
name: Claude with Custom App
on:
issue_comment:
types: [created]
# ... other triggers
jobs:
claude-response:
runs-on: ubuntu-latest
steps:
# Generate a token from your custom app
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
# Use Claude with your custom app's token
- uses: anthropics/claude-code-action@beta
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ steps.app-token.outputs.token }}
# ... other configuration
```
**Important notes:**
- The custom app must have read/write permissions for Issues, Pull Requests, and Contents
- Your app's token will have the exact permissions you configured, nothing more
For more information on creating GitHub Apps, see the [GitHub documentation](https://docs.github.com/en/apps/creating-github-apps).
## 📚 FAQ
Having issues or questions? Check out our [Frequently Asked Questions](./FAQ.md) for solutions to common problems and detailed explanations of Claude's capabilities and limitations.
@@ -109,6 +189,7 @@ jobs:
| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` |
| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` |
| `claude_env` | Custom environment variables to pass to Claude Code execution (YAML format) | No | "" |
| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" |
| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" |
\*Required when using direct Anthropic API (default and when not using Bedrock or Vertex)
@@ -491,6 +572,65 @@ Use a specific Claude model:
# ... other inputs
```
### Claude Code Settings
You can provide Claude Code settings to customize behavior such as model selection, environment variables, permissions, and hooks. Settings can be provided either as a JSON string or a path to a settings file.
#### Option 1: Settings File
```yaml
- uses: anthropics/claude-code-action@beta
with:
settings: "path/to/settings.json"
# ... other inputs
```
#### Option 2: Inline Settings
```yaml
- uses: anthropics/claude-code-action@beta
with:
settings: |
{
"model": "claude-opus-4-20250514",
"env": {
"DEBUG": "true",
"API_URL": "https://api.example.com"
},
"permissions": {
"allow": ["Bash", "Read"],
"deny": ["WebFetch"]
},
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "echo Running bash command..."
}]
}]
}
}
# ... other inputs
```
The settings support all Claude Code settings options including:
- `model`: Override the default model
- `env`: Environment variables for the session
- `permissions`: Tool usage permissions
- `hooks`: Pre/post tool execution hooks
- And more...
For a complete list of available settings and their descriptions, see the [Claude Code settings documentation](https://docs.anthropic.com/en/docs/claude-code/settings).
**Notes**:
- The `enableAllProjectMcpServers` setting is always set to `true` by this action to ensure MCP servers work correctly.
- If both the `model` input parameter and a `model` in settings are provided, the `model` input parameter takes precedence.
- The `allowed_tools` and `disallowed_tools` input parameters take precedence over `permissions` in settings.
- In a future version, we may deprecate individual input parameters in favor of using the settings file for all configuration.
## Cloud Providers
You can authenticate with Claude using any of these three methods:

View File

@@ -60,6 +60,10 @@ inputs:
description: "Custom environment variables to pass to Claude Code execution (YAML format)"
required: false
default: ""
settings:
description: "Claude Code settings as JSON string or path to settings JSON file"
required: false
default: ""
# Auth configuration
anthropic_api_key:
@@ -101,6 +105,9 @@ outputs:
execution_file:
description: "Path to the Claude Code execution output file"
value: ${{ steps.claude-code.outputs.execution_file }}
branch_name:
description: "The branch created by Claude Code for this execution"
value: ${{ steps.prepare.outputs.CLAUDE_BRANCH }}
runs:
using: "composite"
@@ -142,7 +149,7 @@ runs:
- name: Run Claude Code
id: claude-code
if: steps.prepare.outputs.contains_trigger == 'true'
uses: anthropics/claude-code-base-action@0f7a229cb06f840f77f49df0b711ee0060868c2c # v0.0.33
uses: anthropics/claude-code-base-action@503cc7080e62d63d2cc1d80035ed04617d5efb47 # v0.0.35
with:
prompt_file: ${{ runner.temp }}/claude-prompts/claude-prompt.txt
allowed_tools: ${{ env.ALLOWED_TOOLS }}
@@ -157,6 +164,7 @@ runs:
anthropic_api_key: ${{ inputs.anthropic_api_key }}
claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }}
claude_env: ${{ inputs.claude_env }}
settings: ${{ inputs.settings }}
env:
# Model configuration
ANTHROPIC_MODEL: ${{ inputs.model || inputs.anthropic_model }}

View File

@@ -86,14 +86,18 @@ export async function setupBranch(
// Generate branch name for either an issue or closed/merged PR
const entityType = isPR ? "pr" : "issue";
const timestamp = new Date()
.toISOString()
.replace(/[:-]/g, "")
.replace(/\.\d{3}Z/, "")
.split("T")
.join("_");
const newBranch = `${branchPrefix}${entityType}-${entityNumber}-${timestamp}`;
// Create Kubernetes-compatible timestamp: lowercase, hyphens only, shorter format
const now = new Date();
const timestamp = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}-${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`;
// Ensure branch name is Kubernetes-compatible:
// - Lowercase only
// - Alphanumeric with hyphens
// - No underscores
// - Max 50 chars (to allow for prefixes)
const branchName = `${branchPrefix}${entityType}-${entityNumber}-${timestamp}`;
const newBranch = branchName.toLowerCase().substring(0, 50);
try {
// Get the SHA of the source branch to verify it exists

View File

@@ -3,6 +3,7 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { GITHUB_API_URL } from "../github/api/config";
import { mkdir, writeFile } from "fs/promises";
import { Octokit } from "@octokit/rest";
@@ -54,6 +55,7 @@ server.tool(
try {
const client = new Octokit({
auth: GITHUB_TOKEN,
baseUrl: GITHUB_API_URL,
});
// Get the PR to find the head SHA
@@ -142,6 +144,7 @@ server.tool(
try {
const client = new Octokit({
auth: GITHUB_TOKEN,
baseUrl: GITHUB_API_URL,
});
// Get jobs for this workflow run
@@ -209,6 +212,7 @@ server.tool(
try {
const client = new Octokit({
auth: GITHUB_TOKEN,
baseUrl: GITHUB_API_URL,
});
const response = await client.actions.downloadJobLogsForWorkflowRun({

View File

@@ -8,6 +8,7 @@ import { join } from "path";
import fetch from "node-fetch";
import { GITHUB_API_URL } from "../github/api/config";
import { retryWithBackoff } from "../utils/retry";
import { Octokit } from "@octokit/rest";
type GitHubRef = {
object: {
@@ -59,6 +60,12 @@ async function getOrCreateBranchRef(
branch: string,
githubToken: string,
): Promise<string> {
// Create Octokit instance
const octokit = new Octokit({
auth: githubToken,
baseUrl: GITHUB_API_URL,
});
// Try to get the branch reference
const refUrl = `${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs/heads/${branch}`;
const refResponse = await fetch(refUrl, {
@@ -78,9 +85,6 @@ async function getOrCreateBranchRef(
throw new Error(`Failed to get branch reference: ${refResponse.status}`);
}
// Branch doesn't exist, need to create it
console.log(`Branch ${branch} does not exist, creating it...`);
// Get base branch from environment or determine it
const baseBranch = process.env.BASE_BRANCH || "main";
@@ -139,30 +143,19 @@ async function getOrCreateBranchRef(
baseSha = baseRefData.object.sha;
}
// Create the new branch
const createRefUrl = `${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs`;
const createRefResponse = await fetch(createRefUrl, {
method: "POST",
headers: {
Accept: "application/vnd.github+json",
Authorization: `Bearer ${githubToken}`,
"X-GitHub-Api-Version": "2022-11-28",
"Content-Type": "application/json",
},
body: JSON.stringify({
// Create the new branch using Octokit
try {
await octokit.rest.git.createRef({
owner,
repo,
ref: `refs/heads/${branch}`,
sha: baseSha,
}),
});
if (!createRefResponse.ok) {
const errorText = await createRefResponse.text();
throw new Error(
`Failed to create branch: ${createRefResponse.status} - ${errorText}`,
);
});
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
throw new Error(`Failed to create branch: ${errorMessage}`);
}
console.log(`Successfully created branch ${branch}`);
return baseSha;
}
@@ -569,7 +562,6 @@ server.tool(
// Only retry on 403 errors - these are the intermittent failures we're targeting
if (updateRefResponse.status === 403) {
console.log("Received 403 error, will retry...");
throw error;
}

View File

@@ -20,7 +20,7 @@ async function checkActionsReadPermission(
repo: string,
): Promise<boolean> {
try {
const client = new Octokit({ auth: token });
const client = new Octokit({ auth: token, baseUrl: GITHUB_API_URL });
// Try to list workflow runs - this requires actions:read
// We use per_page=1 to minimize the response size

View File

@@ -72,7 +72,7 @@ describe("checkAndCommitOrDeleteBranch", () => {
mockOctokit,
"owner",
"repo",
"claude/issue-123-20240101_123456",
"claude/issue-123-20240101-1234",
"main",
true, // commit signing enabled
);
@@ -80,7 +80,7 @@ describe("checkAndCommitOrDeleteBranch", () => {
expect(result.shouldDeleteBranch).toBe(true);
expect(result.branchLink).toBe("");
expect(consoleLogSpy).toHaveBeenCalledWith(
"Branch claude/issue-123-20240101_123456 has no commits from Claude, will delete it",
"Branch claude/issue-123-20240101-1234 has no commits from Claude, will delete it",
);
});
@@ -90,14 +90,14 @@ describe("checkAndCommitOrDeleteBranch", () => {
mockOctokit,
"owner",
"repo",
"claude/issue-123-20240101_123456",
"claude/issue-123-20240101-1234",
"main",
false,
);
expect(result.shouldDeleteBranch).toBe(false);
expect(result.branchLink).toBe(
`\n[View branch](${GITHUB_SERVER_URL}/owner/repo/tree/claude/issue-123-20240101_123456)`,
`\n[View branch](${GITHUB_SERVER_URL}/owner/repo/tree/claude/issue-123-20240101-1234)`,
);
expect(consoleLogSpy).not.toHaveBeenCalledWith(
expect.stringContaining("has no commits"),
@@ -123,14 +123,14 @@ describe("checkAndCommitOrDeleteBranch", () => {
mockOctokit,
"owner",
"repo",
"claude/issue-123-20240101_123456",
"claude/issue-123-20240101-1234",
"main",
false,
);
expect(result.shouldDeleteBranch).toBe(false);
expect(result.branchLink).toBe(
`\n[View branch](${GITHUB_SERVER_URL}/owner/repo/tree/claude/issue-123-20240101_123456)`,
`\n[View branch](${GITHUB_SERVER_URL}/owner/repo/tree/claude/issue-123-20240101-1234)`,
);
expect(consoleErrorSpy).toHaveBeenCalledWith(
"Error comparing commits on Claude branch:",
@@ -146,7 +146,7 @@ describe("checkAndCommitOrDeleteBranch", () => {
mockOctokit,
"owner",
"repo",
"claude/issue-123-20240101_123456",
"claude/issue-123-20240101-1234",
"main",
true, // commit signing enabled - will try to delete
);
@@ -154,7 +154,7 @@ describe("checkAndCommitOrDeleteBranch", () => {
expect(result.shouldDeleteBranch).toBe(true);
expect(result.branchLink).toBe("");
expect(consoleErrorSpy).toHaveBeenCalledWith(
"Failed to delete branch claude/issue-123-20240101_123456:",
"Failed to delete branch claude/issue-123-20240101-1234:",
deleteError,
);
});
@@ -170,7 +170,7 @@ describe("checkAndCommitOrDeleteBranch", () => {
mockOctokit,
"owner",
"repo",
"claude/issue-123-20240101_123456",
"claude/issue-123-20240101-1234",
"main",
false,
);
@@ -178,10 +178,10 @@ describe("checkAndCommitOrDeleteBranch", () => {
expect(result.shouldDeleteBranch).toBe(false);
expect(result.branchLink).toBe("");
expect(consoleLogSpy).toHaveBeenCalledWith(
"Branch claude/issue-123-20240101_123456 does not exist remotely",
"Branch claude/issue-123-20240101-1234 does not exist remotely",
);
expect(consoleLogSpy).toHaveBeenCalledWith(
"Branch claude/issue-123-20240101_123456 does not exist remotely, no branch link will be added",
"Branch claude/issue-123-20240101-1234 does not exist remotely, no branch link will be added",
);
});
});

View File

@@ -103,12 +103,12 @@ describe("updateCommentBody", () => {
it("adds branch name with link to header when provided", () => {
const input = {
...baseInput,
branchName: "claude/issue-123-20240101_120000",
branchName: "claude/issue-123-20240101-1200",
};
const result = updateCommentBody(input);
expect(result).toContain(
"• [`claude/issue-123-20240101_120000`](https://github.com/owner/repo/tree/claude/issue-123-20240101_120000)",
"• [`claude/issue-123-20240101-1200`](https://github.com/owner/repo/tree/claude/issue-123-20240101-1200)",
);
});
@@ -384,9 +384,9 @@ describe("updateCommentBody", () => {
const input = {
...baseInput,
currentBody: "Claude Code is working… <img src='spinner.gif' />",
branchName: "claude/pr-456-20240101_120000",
branchName: "claude/pr-456-20240101-1200",
prLink:
"\n[Create a PR](https://github.com/owner/repo/compare/main...claude/pr-456-20240101_120000)",
"\n[Create a PR](https://github.com/owner/repo/compare/main...claude/pr-456-20240101-1200)",
triggerUsername: "jane-doe",
};
@@ -394,7 +394,7 @@ describe("updateCommentBody", () => {
// Should include the PR link in the formatted style
expect(result).toContain(
"• [Create PR ➔](https://github.com/owner/repo/compare/main...claude/pr-456-20240101_120000)",
"• [Create PR ➔](https://github.com/owner/repo/compare/main...claude/pr-456-20240101-1200)",
);
expect(result).toContain("**Claude finished @jane-doe's task**");
});
@@ -403,21 +403,21 @@ describe("updateCommentBody", () => {
const input = {
...baseInput,
currentBody: "Claude Code is working…",
branchName: "claude/issue-123-20240101_120000",
branchName: "claude/issue-123-20240101-1200",
branchLink:
"\n[View branch](https://github.com/owner/repo/tree/claude/issue-123-20240101_120000)",
"\n[View branch](https://github.com/owner/repo/tree/claude/issue-123-20240101-1200)",
prLink:
"\n[Create a PR](https://github.com/owner/repo/compare/main...claude/issue-123-20240101_120000)",
"\n[Create a PR](https://github.com/owner/repo/compare/main...claude/issue-123-20240101-1200)",
};
const result = updateCommentBody(input);
// Should include both links in formatted style
expect(result).toContain(
"• [`claude/issue-123-20240101_120000`](https://github.com/owner/repo/tree/claude/issue-123-20240101_120000)",
"• [`claude/issue-123-20240101-1200`](https://github.com/owner/repo/tree/claude/issue-123-20240101-1200)",
);
expect(result).toContain(
"• [Create PR ➔](https://github.com/owner/repo/compare/main...claude/issue-123-20240101_120000)",
"• [Create PR ➔](https://github.com/owner/repo/compare/main...claude/issue-123-20240101-1200)",
);
});

View File

@@ -127,7 +127,7 @@ describe("generatePrompt", () => {
commentId: "67890",
isPR: false,
baseBranch: "main",
claudeBranch: "claude/issue-67890-20240101_120000",
claudeBranch: "claude/issue-67890-20240101-1200",
issueNumber: "67890",
commentBody: "@claude please fix this",
},
@@ -183,7 +183,7 @@ describe("generatePrompt", () => {
isPR: false,
issueNumber: "789",
baseBranch: "main",
claudeBranch: "claude/issue-789-20240101_120000",
claudeBranch: "claude/issue-789-20240101-1200",
},
};
@@ -210,7 +210,7 @@ describe("generatePrompt", () => {
isPR: false,
issueNumber: "999",
baseBranch: "develop",
claudeBranch: "claude/issue-999-20240101_120000",
claudeBranch: "claude/issue-999-20240101-1200",
assigneeTrigger: "claude-bot",
},
};
@@ -237,7 +237,7 @@ describe("generatePrompt", () => {
isPR: false,
issueNumber: "888",
baseBranch: "main",
claudeBranch: "claude/issue-888-20240101_120000",
claudeBranch: "claude/issue-888-20240101-1200",
labelTrigger: "claude-task",
},
};
@@ -265,7 +265,7 @@ describe("generatePrompt", () => {
isPR: false,
issueNumber: "789",
baseBranch: "main",
claudeBranch: "claude/issue-789-20240101_120000",
claudeBranch: "claude/issue-789-20240101-1200",
},
};
@@ -312,7 +312,7 @@ describe("generatePrompt", () => {
isPR: false,
issueNumber: "123",
baseBranch: "main",
claudeBranch: "claude/issue-67890-20240101_120000",
claudeBranch: "claude/issue-67890-20240101-1200",
commentBody: "@claude please fix this",
},
};
@@ -334,7 +334,7 @@ describe("generatePrompt", () => {
isPR: false,
issueNumber: "123",
baseBranch: "main",
claudeBranch: "claude/issue-67890-20240101_120000",
claudeBranch: "claude/issue-67890-20240101-1200",
commentBody: "@claude please fix this",
},
};
@@ -388,7 +388,7 @@ describe("generatePrompt", () => {
isPR: false,
issueNumber: "789",
baseBranch: "main",
claudeBranch: "claude/issue-789-20240101_120000",
claudeBranch: "claude/issue-789-20240101-1200",
},
};
@@ -396,10 +396,10 @@ describe("generatePrompt", () => {
// Should contain Issue-specific instructions
expect(prompt).toContain(
"You are already on the correct branch (claude/issue-789-20240101_120000)",
"You are already on the correct branch (claude/issue-789-20240101-1200)",
);
expect(prompt).toContain(
"IMPORTANT: You are already on the correct branch (claude/issue-789-20240101_120000)",
"IMPORTANT: You are already on the correct branch (claude/issue-789-20240101-1200)",
);
expect(prompt).toContain("Create a PR](https://github.com/");
expect(prompt).toContain(
@@ -426,7 +426,7 @@ describe("generatePrompt", () => {
isPR: false,
issueNumber: "123",
baseBranch: "main",
claudeBranch: "claude/issue-123-20240101_120000",
claudeBranch: "claude/issue-123-20240101-1200",
commentBody: "@claude please fix this",
},
};
@@ -435,13 +435,13 @@ describe("generatePrompt", () => {
// Should contain the actual branch name with timestamp
expect(prompt).toContain(
"You are already on the correct branch (claude/issue-123-20240101_120000)",
"You are already on the correct branch (claude/issue-123-20240101-1200)",
);
expect(prompt).toContain(
"IMPORTANT: You are already on the correct branch (claude/issue-123-20240101_120000)",
"IMPORTANT: You are already on the correct branch (claude/issue-123-20240101-1200)",
);
expect(prompt).toContain(
"The branch-name is the current branch: claude/issue-123-20240101_120000",
"The branch-name is the current branch: claude/issue-123-20240101-1200",
);
});
@@ -456,7 +456,7 @@ describe("generatePrompt", () => {
isPR: true,
prNumber: "456",
commentBody: "@claude please fix this",
claudeBranch: "claude/pr-456-20240101_120000",
claudeBranch: "claude/pr-456-20240101-1200",
baseBranch: "main",
},
};
@@ -465,13 +465,13 @@ describe("generatePrompt", () => {
// Should contain branch-specific instructions like issues
expect(prompt).toContain(
"You are already on the correct branch (claude/pr-456-20240101_120000)",
"You are already on the correct branch (claude/pr-456-20240101-1200)",
);
expect(prompt).toContain(
"Create a PR](https://github.com/owner/repo/compare/main",
);
expect(prompt).toContain(
"The branch-name is the current branch: claude/pr-456-20240101_120000",
"The branch-name is the current branch: claude/pr-456-20240101-1200",
);
expect(prompt).toContain("Reference to the original PR");
expect(prompt).toContain(
@@ -525,7 +525,7 @@ describe("generatePrompt", () => {
isPR: true,
prNumber: "789",
commentBody: "@claude please update this",
claudeBranch: "claude/pr-789-20240101_123000",
claudeBranch: "claude/pr-789-20240101-1230",
baseBranch: "develop",
},
};
@@ -534,7 +534,7 @@ describe("generatePrompt", () => {
// Should contain new branch instructions
expect(prompt).toContain(
"You are already on the correct branch (claude/pr-789-20240101_123000)",
"You are already on the correct branch (claude/pr-789-20240101-1230)",
);
expect(prompt).toContain(
"Create a PR](https://github.com/owner/repo/compare/develop",
@@ -553,7 +553,7 @@ describe("generatePrompt", () => {
prNumber: "999",
commentId: "review-comment-123",
commentBody: "@claude fix this issue",
claudeBranch: "claude/pr-999-20240101_140000",
claudeBranch: "claude/pr-999-20240101-1400",
baseBranch: "main",
},
};
@@ -562,7 +562,7 @@ describe("generatePrompt", () => {
// Should contain new branch instructions
expect(prompt).toContain(
"You are already on the correct branch (claude/pr-999-20240101_140000)",
"You are already on the correct branch (claude/pr-999-20240101-1400)",
);
expect(prompt).toContain("Create a PR](https://github.com/");
expect(prompt).toContain("Reference to the original PR");
@@ -581,7 +581,7 @@ describe("generatePrompt", () => {
eventAction: "closed",
isPR: true,
prNumber: "555",
claudeBranch: "claude/pr-555-20240101_150000",
claudeBranch: "claude/pr-555-20240101-1500",
baseBranch: "main",
},
};
@@ -590,7 +590,7 @@ describe("generatePrompt", () => {
// Should contain new branch instructions
expect(prompt).toContain(
"You are already on the correct branch (claude/pr-555-20240101_150000)",
"You are already on the correct branch (claude/pr-555-20240101-1500)",
);
expect(prompt).toContain("Create a PR](https://github.com/");
expect(prompt).toContain("Reference to the original PR");
@@ -683,7 +683,7 @@ describe("getEventTypeAndContext", () => {
isPR: false,
issueNumber: "999",
baseBranch: "main",
claudeBranch: "claude/issue-999-20240101_120000",
claudeBranch: "claude/issue-999-20240101-1200",
assigneeTrigger: "claude-bot",
},
};
@@ -705,7 +705,7 @@ describe("getEventTypeAndContext", () => {
isPR: false,
issueNumber: "888",
baseBranch: "main",
claudeBranch: "claude/issue-888-20240101_120000",
claudeBranch: "claude/issue-888-20240101-1200",
labelTrigger: "claude-task",
},
};
@@ -728,7 +728,7 @@ describe("getEventTypeAndContext", () => {
isPR: false,
issueNumber: "999",
baseBranch: "main",
claudeBranch: "claude/issue-999-20240101_120000",
claudeBranch: "claude/issue-999-20240101-1200",
// No assigneeTrigger when using directPrompt
},
};

View File

@@ -35,7 +35,7 @@ describe("parseEnvVarsWithContext", () => {
process.env = {
...BASE_ENV,
BASE_BRANCH: "main",
CLAUDE_BRANCH: "claude/issue-67890-20240101_120000",
CLAUDE_BRANCH: "claude/issue-67890-20240101-1200",
};
});
@@ -44,7 +44,7 @@ describe("parseEnvVarsWithContext", () => {
mockIssueCommentContext,
"12345",
"main",
"claude/issue-67890-20240101_120000",
"claude/issue-67890-20240101-1200",
);
expect(result.repository).toBe("test-owner/test-repo");
@@ -60,7 +60,7 @@ describe("parseEnvVarsWithContext", () => {
expect(result.eventData.issueNumber).toBe("55");
expect(result.eventData.commentId).toBe("12345678");
expect(result.eventData.claudeBranch).toBe(
"claude/issue-67890-20240101_120000",
"claude/issue-67890-20240101-1200",
);
expect(result.eventData.baseBranch).toBe("main");
expect(result.eventData.commentBody).toBe(
@@ -81,7 +81,7 @@ describe("parseEnvVarsWithContext", () => {
mockIssueCommentContext,
"12345",
undefined,
"claude/issue-67890-20240101_120000",
"claude/issue-67890-20240101-1200",
),
).toThrow("BASE_BRANCH is required for issue_comment event");
});
@@ -152,7 +152,7 @@ describe("parseEnvVarsWithContext", () => {
process.env = {
...BASE_ENV,
BASE_BRANCH: "main",
CLAUDE_BRANCH: "claude/issue-42-20240101_120000",
CLAUDE_BRANCH: "claude/issue-42-20240101-1200",
};
});
@@ -161,7 +161,7 @@ describe("parseEnvVarsWithContext", () => {
mockIssueOpenedContext,
"12345",
"main",
"claude/issue-42-20240101_120000",
"claude/issue-42-20240101-1200",
);
expect(result.eventData.eventName).toBe("issues");
@@ -174,7 +174,7 @@ describe("parseEnvVarsWithContext", () => {
expect(result.eventData.issueNumber).toBe("42");
expect(result.eventData.baseBranch).toBe("main");
expect(result.eventData.claudeBranch).toBe(
"claude/issue-42-20240101_120000",
"claude/issue-42-20240101-1200",
);
}
});
@@ -184,7 +184,7 @@ describe("parseEnvVarsWithContext", () => {
mockIssueAssignedContext,
"12345",
"main",
"claude/issue-123-20240101_120000",
"claude/issue-123-20240101-1200",
);
expect(result.eventData.eventName).toBe("issues");
@@ -197,7 +197,7 @@ describe("parseEnvVarsWithContext", () => {
expect(result.eventData.issueNumber).toBe("123");
expect(result.eventData.baseBranch).toBe("main");
expect(result.eventData.claudeBranch).toBe(
"claude/issue-123-20240101_120000",
"claude/issue-123-20240101-1200",
);
expect(result.eventData.assigneeTrigger).toBe("@claude-bot");
}
@@ -215,7 +215,7 @@ describe("parseEnvVarsWithContext", () => {
mockIssueOpenedContext,
"12345",
undefined,
"claude/issue-42-20240101_120000",
"claude/issue-42-20240101-1200",
),
).toThrow("BASE_BRANCH is required for issues event");
});
@@ -234,7 +234,7 @@ describe("parseEnvVarsWithContext", () => {
contextWithDirectPrompt,
"12345",
"main",
"claude/issue-123-20240101_120000",
"claude/issue-123-20240101-1200",
);
expect(result.eventData.eventName).toBe("issues");
@@ -264,7 +264,7 @@ describe("parseEnvVarsWithContext", () => {
contextWithoutTriggers,
"12345",
"main",
"claude/issue-123-20240101_120000",
"claude/issue-123-20240101-1200",
),
).toThrow("ASSIGNEE_TRIGGER is required for issue assigned event");
});