Add a `display_report` input parameter (default: "true") that controls
whether the Claude Code Report is written to the GitHub Step Summary.
Setting it to "false" allows users with custom formatting solutions to
avoid duplicate output in the step summary.
Closes#206
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Ashwin Bhat <ashwin-ant@users.noreply.github.com>
Reverts the following commits:
- f669191 fix: use unique local branch names for PR checkout to avoid conflicts (#931)
- 21e3fe0 Fix PR checkout to support fork PRs (#851)
Simplifies PR branch checkout back to using headRefName directly instead
of the pr-{number} local branch naming scheme introduced in #931 and the
GitHub pull ref fetch approach introduced in #851.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The previous implementation used the PR's original branch name when
fetching, which could conflict with existing local or remote branches
of the same name. This caused checkout failures for PRs with common
branch names like 'main' or 'feature/xyz'.
Changes:
- Use 'pr-{number}' format for local branch names (e.g., pr-385)
- Preserve original branch name for logging purposes
- Add detailed logging showing original -> local branch mapping
This ensures uniqueness since PR numbers are unique per repository,
while maintaining support for both same-repo and fork PRs via
GitHub's pull/{number}/head refs.
Fixes the issue introduced in #851 where fork PR support was added.
Co-authored-by: Yi-Cheng Wang <yicheng.wang@heph-ai.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace `Bash(git add:*)` syntax with `Bash(git add *)` in default
tool permissions for tag mode and create-prompt. The colon-prefixed
wildcard syntax is deprecated and causes SDK validation errors.
Closes#856
Co-authored-by: Dave-London <hello@os4us.org>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Use GitHub's PR refs (pull/NUMBER/head) instead of fetching branch
by name. This works for both same-repo and fork PRs because GitHub
automatically creates these refs in the base repository for all PRs.
The branch name doesn't exist on origin for fork PRs, causing:
fatal: couldn't find remote ref <branch-name>
Using pull/${entityNumber}/head:${branchName} fetches the PR head
and creates a local branch with the correct name.
Fixes issues with tag mode failing on fork PRs.
Replace the over-engineered Mode interface/registry/detector pattern with
straightforward inline logic. There are only 2 modes (tag and agent) and
the complexity wasn't justified.
- Delete Mode interface, registry, and prepare pass-through modules
- Export prepareTagMode() and prepareAgentMode() as standalone functions
- Inline trigger checking and mode dispatch in run.ts/prepare.ts
- Change generatePrompt/createPrompt to take modeName string instead of Mode
- Remove dead code (extractGitHubContext, unused detector helpers)
- Update CLAUDE.md to reflect new architecture
* refactor: unify action into single composite step with run.ts entrypoint
Consolidate the prepare and base-action phases into a single composite
step that runs src/entrypoints/run.ts. This simplifies the action.yml
from multiple steps to one execution step, while keeping the same
behavior.
Key changes:
- Add src/entrypoints/run.ts as unified entrypoint
- Simplify action.yml to single 'Run Claude Code Action' step
- Pass all inputs via environment variables
- Update base-action to accept inputs via env vars
- Support agent mode auto-detection from prompt input
* refactor: keep SSH signing cleanup and token revocation as separate action steps
Move SSH signing key cleanup and app token revocation back to separate
composite action steps in action.yml with always() conditions, rather
than handling them inside run.ts. This keeps these cleanup concerns
as independently visible steps in the workflow.
* fix: address PR review feedback
- Use path.dirname() instead of manual string slicing for executable path
- Differentiate prepare vs execution errors in catch block so tracking
comment accurately reflects which phase failed
- Update CLAUDE.md architecture docs to reflect unified run.ts entrypoint
and four-phase design
* fix: address PR review feedback
- Use path.dirname() instead of manual string slicing for executable path
- Differentiate prepare vs execution errors in catch block so tracking
comment accurately reflects which phase failed
- Rewrite CLAUDE.md to focus on mental model, key concepts, and gotchas
instead of exhaustive file listings
- Introduced `include_comments_by_actor` and `exclude_comments_by_actor` inputs in action.yml to allow filtering of comments based on actor usernames.
- Updated context parsing to handle new input fields.
- Implemented `filterCommentsByActor` function to filter comments according to specified inclusion and exclusion patterns.
- Modified `fetchGitHubData` to apply actor filters when retrieving comments from pull requests and issues.
- Added comprehensive tests for the new filtering functionality.
This enhancement provides more control over which comments are processed based on the actor, improving the flexibility of the workflow.
* feat: send additional_permissions in token exchange request
Parse the ADDITIONAL_PERMISSIONS env var and send it as a JSON body
in the OIDC token exchange request. Permissions are merged on top of
the standard defaults (contents: write, pull_requests: write,
issues: write).
* docs: list specific available additional permissions
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.
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
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
* 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.
* 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>
Adds a shorter, more concise prompt for tag mode that trusts the model
to figure out details. Opt-in via USE_SIMPLE_PROMPT=true. The simplified
prompt keeps all context data but reduces instructions from ~250 to ~70 lines.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* fix: `commentBody` may be `null`
This handles the cases where `pull_request_review` events have no
comments (`commentBody` field is `null`). In those cases, the `null`
value is converted to the empty string.
The issue was testing `!commentBody` which was triggerring on empty
strings as well. This guard was removed (which is the fix), but for
clarity, the `commentBody` field was also made optional to make it clear
that the comment may be missing.
* fix: bun run format
* feat: add Agent SDK support with USE_AGENT_SDK feature flag
Add a feature-flagged code path that uses the Agent SDK instead of
spawning the CLI as a subprocess. When USE_AGENT_SDK=true is set,
the new SDK path is used; otherwise, existing CLI behavior is unchanged.
Changes:
- Add parse-sdk-options.ts for parsing ClaudeOptions into SDK format
- Add run-claude-sdk.ts for SDK execution with query() function
- Update run-claude.ts with feature flag check at entry point
- Update update-comment-link.ts to handle both cost_usd and total_cost_usd
- Add @anthropic-ai/claude-agent-sdk dependency
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: simplify SDK types by using @anthropic-ai/claude-agent-sdk types directly
- Remove duplicate SdkRunOptions and McpStdioServerConfig types
- Use SDK's Options and McpStdioServerConfig types directly
- Return { sdkOptions, showFullOutput, hasJsonSchema } from parseSdkOptions
- Remove unnecessary convertMcpServers function
- Net reduction of ~70 lines
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: use extraArgs for claudeArgs pass-through to CLI
Simplify option parsing by converting claudeArgs to extraArgs record
and letting the SDK/CLI handle --mcp-config, --json-schema, etc.
- Remove extractJsonSchema and parseMcpConfigs functions
- Add parseClaudeArgsToExtraArgs for simple flag parsing
- CLI handles complex args like --mcp-config directly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* ci
* refactor: remove hardcoded permission bypass flags
The SDK path should match CLI path behavior - permissions are handled
by the CLI itself, not hardcoded in the action.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: add logging for SDK vs CLI path selection
---------
Co-authored-by: Claude <noreply@anthropic.com>
Add trigger-time validation for issue/PR body content to prevent attackers
from exploiting a race condition where they edit the body between when an
authorized user triggers @claude and when Claude processes the request.
The existing filterCommentsToTriggerTime() already protected comments -
this extends the same pattern to the main issue/PR body via isBodySafeToUse().
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* chore: remove experimental allowed domains feature
Remove the experimental_allowed_domains feature which was used to
restrict network access via a Squid proxy. This removes:
- The input definition from action.yml
- The Network Restrictions workflow step
- The setup-network-restrictions.sh script
- Documentation from experimental.md, usage.md, and related files
- The input default from collect-inputs.ts
* chore: fix formatting with prettier
Co-authored-by: Ashwin Bhat <ashwin-ant@users.noreply.github.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Ashwin Bhat <ashwin-ant@users.noreply.github.com>
Add pull_request_target event support to enable Claude Code usage with forked
repositories while maintaining proper security boundaries. This resolves issues
with dependabot PRs and external contributions that require write permissions.
Changes:
- Add pull_request_target to supported GitHub events in context parsing
- Update type definitions to include PullRequestTargetEvent
- Modify IS_PR calculation to detect pull_request_target as PR context
- Add comprehensive test coverage for pull_request_target workflows
- Update documentation to reflect pull_request_target support
The pull_request_target event provides the same payload structure as
pull_request but runs with write permissions from the base repository,
making it ideal for secure automation of external contributions.
Fixes#347
- Update parseAllowedTools to accept both --allowedTools and --allowed-tools
- Add regex alternation to support both camelCase and kebab-case variants
- Add test cases for unquoted and quoted kebab-case formats
- All existing tests continue to pass
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: Claude <noreply@anthropic.com>
* enable track_progress for comments
* refactor: pass mode explicitly to prepareMcpConfig
Update prepareMcpConfig to receive the mode parameter from its callers
instead of detecting agent mode by checking context.inputs.prompt.
This makes mode determination explicit and controlled by the caller.
Also update all test cases to include the required mode parameter
and fix agent mode test expectations to match new behavior where
MCP config is only included when tools are explicitly allowed.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix test
---------
Co-authored-by: Claude <noreply@anthropic.com>
The GitHub comment MCP server was being included in agent mode even when no comment tools were explicitly allowed. This fix ensures the server is only included in tag mode where it's always needed for updating Claude comments.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: Claude <noreply@anthropic.com>
Fixes#528 - Issues events now correctly use agent mode when an explicit
prompt is provided in the workflow YAML, matching the behavior of PR events.
Previously, issues events would always use tag mode (with tracking comments)
even when a prompt was provided, creating inconsistent behavior compared to
pull request events which correctly used agent mode for automation.
The fix adds a check for explicit prompts before checking for triggers,
ensuring consistent mode selection across all event types.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: Claude <noreply@anthropic.com>
* feat: add repository_dispatch event support
Add support for repository_dispatch events in GitHub context parsing system. This enables the action to handle custom API-triggered events properly.
Changes:
- Add RepositoryDispatchEvent type definition
- Include repository_dispatch in automation event names
- Update context parsing to handle repository_dispatch events
- Update documentation to reflect repository_dispatch availability
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* style: format code with prettier
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* test: add comprehensive repository_dispatch event test coverage
- Add mockRepositoryDispatchContext with realistic payload structure
- Add repository_dispatch mode detection tests in registry.test.ts
- Add repository_dispatch trigger tests in agent.test.ts
- Ensure repository_dispatch events are properly handled as automation events
- Verify agent mode trigger behavior with and without prompts
- All 394 tests passing with new coverage
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* style: format test files with prettier
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Adds a new optional bot_id input parameter that defaults to the github-actions[bot] ID (41898282). This resolves the "403 Resource not accessible by integration" error that occurs when using GitHub App installation tokens, which cannot access the /user endpoint.
Changes:
- Add bot_id input to action.yml with default value
- Update context parsing to include bot_id from environment
- Modify agent mode to use bot_id when available, avoiding API calls that fail with GitHub App tokens
- Add clear error handling for GitHub App token limitations
- Update documentation in usage.md and faq.md
- Fix test mocks to include bot_id field
This allows users to specify a custom bot user ID or use the default github-actions[bot] ID automatically, preventing 403 errors in automation workflows.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
* feat: make MCP servers conditional in agent mode
In agent mode, MCP servers (github_comment, github_ci) are now only included
when explicitly requested via allowedTools, rather than being auto-provisioned.
This change gives agent mode workflows complete control over which MCP
servers are included, preventing unwanted automatic provisioning of GitHub
integration tools.
Changes:
- Add agent mode detection in prepareMcpConfig
- Make github_comment server conditional based on allowedTools in agent mode
- Make github_ci server conditional based on allowedTools in agent mode
- Tag mode behavior remains unchanged (auto-inclusion)
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* test: update agent mode test for conditional MCP behavior
Updated test expectation to match the new conditional MCP server behavior
where agent mode only includes MCP config when servers are actually needed.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Kashyap Murali <13315300+katchu11@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>