Compare commits

..

1 Commits

Author SHA1 Message Date
Claude
68df0f8000 feat: standardize PR creation instructions to match BashTool format
Update PR creation instructions in generateDefaultPrompt and generateSimplePrompt
to be consistent with the comprehensive BashTool PR workflow:

- Add steps to run git log and git diff to understand full commit history
- Require analyzing ALL commits, not just the latest
- Enforce structured PR body format with ## Summary (1-3 bullet points)
  and ## Test plan (checklist) sections
2025-12-15 20:07:15 +00:00
10 changed files with 58 additions and 116 deletions

View File

@@ -36,4 +36,4 @@ jobs:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: |
--allowedTools "Bash(bun install),Bash(bun test:*),Bash(bun run format),Bash(bun typecheck)"
--model "claude-opus-4-5"
--model "claude-opus-4-1-20250805"

View File

@@ -198,7 +198,7 @@ runs:
# Install Claude Code if no custom executable is provided
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
CLAUDE_CODE_VERSION="2.0.70"
CLAUDE_CODE_VERSION="2.0.69"
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
for attempt in 1 2 3; do
echo "Installation attempt $attempt..."

View File

@@ -124,7 +124,7 @@ runs:
PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
run: |
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
CLAUDE_CODE_VERSION="2.0.70"
CLAUDE_CODE_VERSION="2.0.69"
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
for attempt in 1 2 3; do
echo "Installation attempt $attempt..."

View File

@@ -1,12 +1,11 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "@anthropic-ai/claude-code-base-action",
"dependencies": {
"@actions/core": "^1.10.1",
"@anthropic-ai/claude-agent-sdk": "^0.1.70",
"@anthropic-ai/claude-agent-sdk": "^0.1.52",
"shell-quote": "^1.8.3",
},
"devDependencies": {
@@ -27,7 +26,7 @@
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.1.70", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^3.24.1" } }, "sha512-4jpFPDX8asys6skO1r3Pzh0Fe9nbND2ASYTWuyFB5iN9bWEL6WScTFyGokjql3M2TkEp9ZGuB2YYpTCdaqT9Sw=="],
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.1.52", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^3.24.1" } }, "sha512-yF8N05+9NRbqYA/h39jQ726HTQFrdXXp7pEfDNKIJ2c4FdWvEjxBA/8ciZIebN6/PyvGDcbEp3yq2Co4rNpg6A=="],
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],

View File

@@ -11,7 +11,7 @@
},
"dependencies": {
"@actions/core": "^1.10.1",
"@anthropic-ai/claude-agent-sdk": "^0.1.70",
"@anthropic-ai/claude-agent-sdk": "^0.1.52",
"shell-quote": "^1.8.3"
},
"devDependencies": {

View File

@@ -1,13 +1,12 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "@anthropic-ai/claude-code-action",
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/github": "^6.0.1",
"@anthropic-ai/claude-agent-sdk": "^0.1.70",
"@anthropic-ai/claude-agent-sdk": "^0.1.52",
"@modelcontextprotocol/sdk": "^1.11.0",
"@octokit/graphql": "^8.2.2",
"@octokit/rest": "^21.1.1",
@@ -37,7 +36,7 @@
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.1.70", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^3.24.1" } }, "sha512-4jpFPDX8asys6skO1r3Pzh0Fe9nbND2ASYTWuyFB5iN9bWEL6WScTFyGokjql3M2TkEp9ZGuB2YYpTCdaqT9Sw=="],
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.1.52", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^3.24.1" } }, "sha512-yF8N05+9NRbqYA/h39jQ726HTQFrdXXp7pEfDNKIJ2c4FdWvEjxBA/8ciZIebN6/PyvGDcbEp3yq2Co4rNpg6A=="],
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],

View File

@@ -12,7 +12,7 @@
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/github": "^6.0.1",
"@anthropic-ai/claude-agent-sdk": "^0.1.70",
"@anthropic-ai/claude-agent-sdk": "^0.1.52",
"@modelcontextprotocol/sdk": "^1.11.0",
"@octokit/graphql": "^8.2.2",
"@octokit/rest": "^21.1.1",

View File

@@ -563,9 +563,22 @@ ${getCommitInstructions(eventData, githubData, context, useCommitSigning)}
${
eventData.claudeBranch
? `
When done with changes, provide a PR link:
When done with changes:
1. Run git log origin/${eventData.baseBranch}..HEAD and git diff origin/${eventData.baseBranch}...HEAD to understand ALL commits
2. Draft a PR summary analyzing ALL changes (not just the latest commit)
3. Provide a PR link:
[Create a PR](${GITHUB_SERVER_URL}/${context.repository}/compare/${eventData.baseBranch}...${eventData.claudeBranch}?quick_pull=1&title=<url-encoded-title>&body=<url-encoded-body>)
Use THREE dots (...) between branches. URL-encode all parameters.`
Use THREE dots (...) between branches. URL-encode all parameters.
PR body format:
## Summary
<1-3 bullet points>
## Test plan
<Checklist of testing TODOs>
Fixes #<issue-number>
Generated with [Claude Code](https://claude.ai/code)`
: ""
}
@@ -743,8 +756,13 @@ ${eventData.eventName === "issue_comment" || eventData.eventName === "pull_reque
- Mark each subtask as completed as you progress.${getCommitInstructions(eventData, githubData, context, useCommitSigning)}
${
eventData.claudeBranch
? `- Provide a URL to create a PR manually in this format:
[Create a PR](${GITHUB_SERVER_URL}/${context.repository}/compare/${eventData.baseBranch}...<branch-name>?quick_pull=1&title=<url-encoded-title>&body=<url-encoded-body>)
? `- When creating a pull request, follow these steps:
1. Use git log and git diff to understand the full commit history for the current branch (from the time it diverged from the base branch):
- Run: git log origin/${eventData.baseBranch}..HEAD
- Run: git diff origin/${eventData.baseBranch}...HEAD
2. Analyze ALL changes that will be included in the pull request, making sure to look at all relevant commits (NOT just the latest commit, but ALL commits that will be included in the pull request), and draft a pull request summary
3. Provide a URL to create a PR manually in this format:
[Create a PR](${GITHUB_SERVER_URL}/${context.repository}/compare/${eventData.baseBranch}...<branch-name>?quick_pull=1&title=<url-encoded-title>&body=<url-encoded-body>)
- IMPORTANT: Use THREE dots (...) between branch names, not two (..)
Example: ${GITHUB_SERVER_URL}/${context.repository}/compare/main...feature-branch (correct)
NOT: ${GITHUB_SERVER_URL}/${context.repository}/compare/main..feature-branch (incorrect)
@@ -752,10 +770,16 @@ ${eventData.eventName === "issue_comment" || eventData.eventName === "pull_reque
Example: Instead of "fix: update welcome message", use "fix%3A%20update%20welcome%20message"
- The target-branch should be '${eventData.baseBranch}'.
- The branch-name is the current branch: ${eventData.claudeBranch}
- The body should include:
- A clear description of the changes
- Reference to the original ${eventData.isPR ? "PR" : "issue"}
- The signature: "Generated with [Claude Code](https://claude.ai/code)"
- The PR body MUST follow this format:
## Summary
<1-3 bullet points summarizing the changes>
## Test plan
<Bulleted markdown checklist of TODOs for testing the pull request>
Fixes #<issue-number>
Generated with [Claude Code](https://claude.ai/code)
- Just include the markdown link with text "Create a PR" - do not add explanatory text before it like "You can create a PR using this link"`
: ""
}

View File

@@ -1,55 +1,22 @@
import { parse as parseShellArgs, type ParseEntry } from "shell-quote";
/**
* Extract the string value from a shell-quote ParseEntry.
* Handles both plain strings and glob patterns (which are returned as objects).
*/
function entryToString(entry: ParseEntry): string | null {
if (typeof entry === "string") {
return entry;
}
// Handle glob patterns - shell-quote returns { op: "glob", pattern: "..." }
if (typeof entry === "object" && "op" in entry && entry.op === "glob") {
return (entry as { op: "glob"; pattern: string }).pattern;
}
return null;
}
export function parseAllowedTools(claudeArgs: string): string[] {
if (!claudeArgs?.trim()) return [];
// Match --allowedTools or --allowed-tools followed by the value
// Handle both quoted and unquoted values
const patterns = [
/--(?:allowedTools|allowed-tools)\s+"([^"]+)"/, // Double quoted
/--(?:allowedTools|allowed-tools)\s+'([^']+)'/, // Single quoted
/--(?:allowedTools|allowed-tools)\s+([^\s]+)/, // Unquoted
];
const result: string[] = [];
// Use shell-quote to properly tokenize the arguments
// This handles quoted strings, escaped characters, etc.
const rawArgs = parseShellArgs(claudeArgs);
for (let i = 0; i < rawArgs.length; i++) {
const entry = rawArgs[i];
if (!entry) continue;
const arg = entryToString(entry);
if (!arg) continue;
// Match both --allowedTools and --allowed-tools
if (arg === "--allowedTools" || arg === "--allowed-tools") {
// Collect all subsequent non-flag values as tools
while (i + 1 < rawArgs.length) {
const nextEntry = rawArgs[i + 1];
if (!nextEntry) break;
const toolArg = entryToString(nextEntry);
// Stop if we hit another flag or a non-parseable entry
if (!toolArg || toolArg.startsWith("--")) {
break;
}
// Split by comma in case tools are comma-separated within a single value
const tools = toolArg.split(",").map((t) => t.trim());
result.push(...tools.filter((t) => t.length > 0));
i++;
for (const pattern of patterns) {
const match = claudeArgs.match(pattern);
if (match && match[1]) {
// Don't return if the value starts with -- (another flag)
if (match[1].startsWith("--")) {
return [];
}
return match[1].split(",").map((t) => t.trim());
}
}
return result;
return [];
}

View File

@@ -37,9 +37,8 @@ describe("parseAllowedTools", () => {
test("handles duplicate --allowedTools flags", () => {
const args = "--allowedTools --allowedTools mcp__github__*";
// Should skip the first one since the value is another flag
// and parse the second one correctly
expect(parseAllowedTools(args)).toEqual(["mcp__github__*"]);
// Should not match the first one since the value is another flag
expect(parseAllowedTools(args)).toEqual([]);
});
test("handles typo --alloedTools", () => {
@@ -85,50 +84,4 @@ describe("parseAllowedTools", () => {
"mcp__github_comment__*",
]);
});
test("parses multiple space-separated quoted tools (issue #746)", () => {
// This is the exact format from the bug report
const args =
'--allowed-tools "Bash(git log:*)" "Bash(git diff:*)" "Bash(git fetch:*)" "Bash(gh pr:*)"';
expect(parseAllowedTools(args)).toEqual([
"Bash(git log:*)",
"Bash(git diff:*)",
"Bash(git fetch:*)",
"Bash(gh pr:*)",
]);
});
test("parses multiple --allowedTools flags with different tools", () => {
const args =
'--allowedTools "Edit,Read" --model "claude-3" --allowedTools "Bash(npm install)"';
expect(parseAllowedTools(args)).toEqual([
"Edit",
"Read",
"Bash(npm install)",
]);
});
test("parses mix of comma-separated and space-separated tools", () => {
const args =
'--allowed-tools "Bash(git log:*),Bash(git diff:*)" "Bash(git fetch:*)"';
expect(parseAllowedTools(args)).toEqual([
"Bash(git log:*)",
"Bash(git diff:*)",
"Bash(git fetch:*)",
]);
});
test("handles complex workflow example from issue #746", () => {
const args =
'--allowed-tools "Bash(git log:*)" "Bash(git diff:*)" "Bash(git fetch:*)" "Bash(git reflog:*)" "Bash(git merge-tree:*)" "Bash(gh pr:*)" "Bash(gh api:*)"';
expect(parseAllowedTools(args)).toEqual([
"Bash(git log:*)",
"Bash(git diff:*)",
"Bash(git fetch:*)",
"Bash(git reflog:*)",
"Bash(git merge-tree:*)",
"Bash(gh pr:*)",
"Bash(gh api:*)",
]);
});
});