* refactor: consolidate CI workflows with ci-all.yml orchestrator
- Add ci-all.yml to orchestrate all CI workflows on push to main
- Update individual workflows to use workflow_call for reusability
- Remove redundant push triggers from individual test workflows
- Update release.yml to trigger on CI All workflow completion
- Auto-release on version bump commits after CI passes
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 8
Claude-Permission-Prompts: 1
Claude-Escapes: 0
* address security review comments
---------
Co-authored-by: Claude <noreply@anthropic.com>
ssh-keygen requires a trailing newline to parse private keys correctly.
Without it, git signing fails with the confusing error:
'Couldn't load public key: No such file or directory?'
This normalizes the key to always end with a newline before writing.
Temporarily disable the release-base-action job that syncs releases
to the claude-code-base-action repository. The job checkout step
and subsequent tag/release creation steps are now commented out.
Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 2
Claude-Permission-Prompts: 2
Claude-Escapes: 0
Co-authored-by: Claude <noreply@anthropic.com>
Fixes issue #641 where users were getting banned due to rapid successive
Claude runs triggered by the synchronize event.
Changes:
- Add checkHumanActor call to agent mode's prepare() method to reject
bot-triggered workflows unless explicitly allowed via allowed_bots
- Update checkHumanActor to accept GitHubContext (union type) instead
of just ParsedGitHubContext
- Add tests for bot rejection/allowance in agent mode
Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 1
Claude-Permission-Prompts: 3
Claude-Escapes: 0
Add a new section to security.md explaining that in the default
configuration, Claude commits to a branch and provides a link for
the user to create the PR themselves, ensuring human oversight.
Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 2
Claude-Permission-Prompts: 2
Claude-Escapes: 0
Co-authored-by: Claude <noreply@anthropic.com>
The parseAllowedTools() function previously used .match() which only
returns the first match. This caused tools specified in subsequent
--allowed-tools flags to be ignored during MCP server initialization.
Changes:
- Add /g flag to regex patterns for global matching
- Use matchAll() to find all occurrences
- Deduplicate tools while preserving order
- Make unquoted pattern not match quoted values
Fixes#800
#vibe
Co-authored-by: Claude <noreply@anthropic.com>
* Add branch-name-template config option
* Logging
* Use branch name template
* Add label to template variables
* Add description template variable
* More concise description for branch_name_template
* Remove more granular time template variables
* Only fetch first label
* Add check for empty template-generated name
* Clean up comments, docstrings
* Merge createBranchTemplateVariables into generateBranchName
* Still replace undefined values
* Fall back to default on duplicate branch
* Parameterize description wordcount
* Remove some over-explanatory comments
* NUM_DESCRIPTION_WORDS: 3 -> 5
Add validatePathWithinRepo helper to ensure file paths resolve within the repository root directory. This hardens the commit_files tool by validating paths before file operations.
Changes:
- Add src/mcp/path-validation.ts with async path validation using realpath
- Update commit_files to validate all paths before reading files
- Prevent symlink-based path escapes by resolving real paths
- Add comprehensive test coverage including symlink attack scenarios
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* fix: use original title from webhook payload instead of fetched title
- Add extractOriginalTitle() helper to extract title from webhook payload
- Add originalTitle parameter to fetchGitHubData()
- Update tag mode to pass original title from webhook context
- Add tests for extractOriginalTitle and originalTitle parameter
This ensures the title used in prompts is the one that existed when the
trigger event occurred, rather than a potentially modified title fetched
later via GraphQL.
* fix: add title sanitization and explicit TOCTOU test
- Apply sanitizeContent() to titles in formatContext() for defense-in-depth
- Add explicit test documenting TOCTOU prevention for title handling
Previously, the SDK path would result in the CLI setting the entrypoint
to 'sdk-ts' internally, while the non-SDK (CLI) path would correctly
set it to 'claude-code-github-action' based on the CLAUDE_CODE_ACTION
env var.
This change explicitly sets CLAUDE_CODE_ENTRYPOINT in both:
1. The action.yml env block (for consistency)
2. The SDK options env (to override the CLI's internal default)
The CLI respects pre-set entrypoint values, so this ensures consistent
user agent reporting for both execution paths.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: Claude <noreply@anthropic.com>
* fix: prevent orphaned installer processes from blocking retries
When the `timeout` command expires during Claude Code installation, it only
kills the direct child bash process, not the grandchild installer processes.
These orphaned processes continue holding a lock file, causing retry attempts
to fail with "another process is currently installing Claude".
Add `--foreground` flag to run the command in a foreground process group so
all child processes are killed on timeout. Add `--kill-after=10` to send
SIGKILL if SIGTERM doesn't terminate processes within 10 seconds.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: apply same timeout fix to root action.yml
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: support local plugin marketplace paths
Enable installing plugins from local directories in addition to remote
Git URLs. This allows users to use local plugin marketplaces within their
repository without requiring them to be hosted in a separate Git repo.
Example usage:
plugin_marketplaces: "./my-local-marketplace"
plugins: "my-plugin@my-local-marketplace"
Supported path formats:
- Relative paths: ./plugins, ../shared-plugins
- Absolute Unix paths: /home/user/plugins
- Absolute Windows paths: C:\Users\user\plugins
Fixes#664
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* support hidden folders
* Revert "support hidden folders"
This reverts commit a55626c9f1.
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: send user request as separate content block for slash command support
When in tag mode with the SDK path, extracts the user's request from the
trigger comment (text after @claude) and sends it as a separate content
block. This enables the CLI to process slash commands like "/review-pr".
- Add extract-user-request utility to parse trigger comments
- Write user request to separate file during prompt generation
- Send multi-block SDKUserMessage when user request file exists
- Add tests for the extraction utility
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: address PR feedback
- Fix potential ReDoS vulnerability by using string operations instead of regex
- Remove unused extractUserRequestFromEvent function and tests
- Extract USER_REQUEST_FILENAME to shared constants
- Conditionally log user request based on showFullOutput setting
- Add JSDoc documentation to extractUserRequestFromContext
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat: add ssh_signing_key input for SSH commit signing
Add a new ssh_signing_key input that allows passing an SSH signing key
for commit signing, as an alternative to the existing use_commit_signing
(which uses GitHub API-based commits).
When ssh_signing_key is provided:
- Git is configured to use SSH signing (gpg.format=ssh, commit.gpgsign=true)
- The key is written to ~/.ssh/claude_signing_key with 0600 permissions
- Git CLI commands are used (not MCP file ops)
- The key is cleaned up in a post step for security
Behavior matrix:
| ssh_signing_key | use_commit_signing | Result |
|-----------------|-------------------|--------|
| not set | false | Regular git, no signing |
| not set | true | GitHub API (MCP), verified commits |
| set | false | Git CLI with SSH signing |
| set | true | Git CLI with SSH signing (ssh_signing_key takes precedence)
* docs: add SSH signing key documentation
- Update security.md with detailed setup instructions for both signing options
- Explain that ssh_signing_key enables full git CLI operations (rebasing, etc.)
- Add ssh_signing_key to inputs table in usage.md
- Update bot_id/bot_name descriptions to note they're needed for verified commits
* fix: address security review feedback for SSH signing
- Write SSH key atomically with mode 0o600 (fixes TOCTOU race condition)
- Create .ssh directory with mode 0o700 (SSH best practices)
- Add input validation for SSH key format
- Remove unused chmod import
- Add tests for validation logic
* feat: add "Fix this" links to PR code reviews
When Claude reviews PRs and identifies fixable issues, it now includes
inline links that open Claude Code with the fix request pre-loaded.
Format: [Fix this →](https://claude.ai/code?q=<URI_ENCODED_INSTRUCTIONS>&repo=<REPO>)
This enables one-click fix requests directly from code review comments.
* feat: add include_fix_links input to control Fix this links
Adds a configurable input to enable/disable the "Fix this →" links
in PR code reviews. Defaults to true for backwards compatibility.
Update the AWS Bedrock documentation link to point to the new
code.claude.com domain.
Fixes#756
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Ashwin Bhat <ashwin-ant@users.noreply.github.com>
The gh CLI prefers GH_TOKEN over GITHUB_TOKEN. When a calling workflow
sets GH_TOKEN in env, the action's GITHUB_TOKEN was being ignored,
causing the gh CLI to use the wrong token (e.g., the default workflow
token instead of an App token).
This ensures Claude's gh CLI commands use the action's prepared token.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
When users specify --setting-sources in claude_args (e.g., '--setting-sources user'),
the action now respects that value instead of overriding it with all three sources.
This fixes an issue where users who wanted to avoid in-repo configs would still
have them loaded because the settingSources was hardcoded to ['user', 'project', 'local'].
Fixes#749
Co-authored-by: Ashwin Bhat <ashwin-ant@users.noreply.github.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
* fix: merge multiple --mcp-config flags instead of overwriting
When users provide their own --mcp-config in claude_args, the action's
built-in MCP servers (github_comment, github_ci, etc.) were being lost
because multiple --mcp-config flags were overwriting each other.
This fix:
- Adds mcp-config to ACCUMULATING_FLAGS to collect all values
- Changes delimiter to null character to avoid conflicts with JSON
- Adds mergeMcpConfigs() to combine mcpServers objects from multiple configs
- Merges inline JSON configs while preserving file path configs
Fixes#745🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ashwin Bhat <ashwin-ant@users.noreply.github.com>
* fix: support hyphenated --allowed-tools flag and multiple values
The --allowed-tools flag was not being parsed correctly when:
1. Using the hyphenated form (--allowed-tools) instead of camelCase (--allowedTools)
2. Passing multiple space-separated values after a single flag
(e.g., --allowed-tools "Tool1" "Tool2" "Tool3")
This fix:
- Adds hyphenated variants (allowed-tools, disallowed-tools) to ACCUMULATING_FLAGS
- Updates parsing to consume all consecutive non-flag values for accumulating flags
- Merges values from both camelCase and hyphenated variants
Fixes#746🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ashwin Bhat <ashwin-ant@users.noreply.github.com>
Add a new `session_id` output that exposes the Claude Code session ID,
allowing other workflows or Claude Code instances to resume the
conversation using `--resume <session_id>`.
Changes:
- Add parseAndSetSessionId() function to extract session_id from
the system.init message in execution output
- Add session_id output to both action.yml and base-action/action.yml
- Add comprehensive tests for the new functionality
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Change USE_AGENT_SDK to default to true instead of false. The Agent SDK
path is now used by default; set USE_AGENT_SDK=false to use the CLI path.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* fix: Prevent command injection in branch operations
Replace Bun shell template literals with Node.js execFileSync to prevent
command injection attacks via malicious branch names. Branch names from
PR data (headRefName) are now validated against a strict whitelist pattern
before use in git commands.
Changes:
- Add validateBranchName() function with strict character whitelist
- Replace $`git ...` shell templates with execGit() using execFileSync
- Validate all branch names before use in git operations
* fix: Address review comments for branch validation security
- Enhanced execGit JSDoc to explain security benefits of execFileSync
- Added comprehensive branch name validation:
- Leading dash check (prevents option injection)
- Control characters and special git characters (~^:?*[\])
- Leading/trailing period checks
- Trailing slash and consecutive slash checks
- Added -- separator to git checkout commands
- Added 30 unit tests for validateBranchName covering:
- Valid branch names
- Command injection attempts
- Option injection attempts
- Path traversal attempts
- Git-specific invalid patterns
- Control characters and edge cases
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Replace direct template expansion of user inputs in shell scripts with
environment variables to prevent potential command injection attacks.
Changes:
- sync-base-action.yml: Use $GITHUB_EVENT_NAME and $GITHUB_ACTOR instead of template expansion
- action.yml: Pass path_to_bun_executable and path_to_claude_code_executable through env vars
- base-action/action.yml: Same env var changes for path inputs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Two fixes for the Agent SDK path (USE_AGENT_SDK=true):
1. Add settingSources to load filesystem settings
- Without this, CLI-installed plugins aren't available to the SDK
- Also needed to load CLAUDE.md files from the project
2. Default systemPrompt to claude_code preset
- Without an explicit systemPrompt, the SDK would use no system prompt
- Now defaults to { type: "preset", preset: "claude_code" } to match CLI behavior
Also adds logging of SDK options (excluding env) for debugging.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* fix: merge allowedTools from claudeArgs when using Agent SDK
When USE_AGENT_SDK=true, the allowedTools from claudeArgs (which contains
tag mode's required tools like mcp__github_comment__update_claude_comment)
were being lost because parseClaudeArgsToExtraArgs converts args to a
Record<string, string>, and the SDK was using sdkOptions.allowedTools
(from direct options) instead of merging with extraArgs.allowedTools.
This fix:
- Extracts allowedTools/disallowedTools from extraArgs after parsing
- Merges them with any direct options.allowedTools/disallowedTools
- Removes them from extraArgs to prevent duplicate CLI flags
- Passes the merged list as sdkOptions.allowedTools
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: accumulate multiple --allowedTools flags in claudeArgs
When tag mode adds its --allowedTools (with MCP tools) and the user also
provides --allowedTools in their claude_args, the parseClaudeArgsToExtraArgs
function was only keeping the last value. This caused tag mode's required
tools like mcp__github_comment__update_claude_comment to be lost.
Now allowedTools and disallowedTools flags accumulate their values when
they appear multiple times in claudeArgs, so both tag mode's tools and
user's tools are preserved.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>