Compare commits

...

30 Commits

Author SHA1 Message Date
GitHub Actions
7ed3b616d5 chore: bump Claude Code version to 1.0.117 2025-09-16 23:49:28 +00:00
kashyap murali
09ea2f00e1 Delete .github/workflows/claude-test.yml (#573) 2025-09-16 13:46:34 -07:00
GitHub Actions
455b943dd7 chore: bump Claude Code version to 1.0.115 2025-09-16 00:52:01 +00:00
GitHub Actions
063d17ebb2 chore: bump Claude Code version to 1.0.113 2025-09-13 02:32:28 +00:00
Kevin Cui
2e92922dd6 fix(tag): no such tool available mcp__github_* (#556)
Signed-off-by: Kevin Cui <bh@bugs.cc>

# Conflicts:
#	src/mcp/install-mcp-server.ts
#	src/modes/tag/index.ts
#	test/modes/agent.test.ts
2025-09-12 12:33:34 -07:00
GitHub Actions
a5528eec74 chore: bump Claude Code version to 1.0.112 2025-09-12 01:14:51 +00:00
Benny Yen
1d4650c102 fix: update test workflow reference in test-local.sh (#564)
* fix: update test workflow reference in test-local.sh

Change workflow file from test-action.yml to test-base-action.yml

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

Co-Authored-By: Claude <noreply@anthropic.com>

* docs(CLAUDE): update test workflow reference in CLAUDE.md

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-11 07:25:16 -07:00
Benny Yen
86d6f44e34 chore: consolidate duplicate test directories (#565)
Move detector.test.ts from tests/modes/ to test/modes/ and fix TypeScript
type errors by adding missing required properties (botId, botName, allowedNonWriteUsers).
Remove empty tests/ directory structure.

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-11 07:24:55 -07:00
GitHub Actions
c1adac956c chore: bump Claude Code version to 1.0.111 2025-09-10 23:56:22 +00:00
Ashwin Bhat
f197e7bfd5 docs: add documentation for path_to_claude_code_executable and path_to_bun_executable inputs (#562)
Add documentation for the two previously undocumented inputs that allow
users to provide custom executables for specialized environments:

- path_to_claude_code_executable: for custom Claude Code binaries
- path_to_bun_executable: for custom Bun runtime

These inputs are particularly useful for environments like Nix, NixOS,
custom containers, and other package management systems where the
default installation may not work.

Updated files:
- docs/usage.md: Added to inputs table
- docs/faq.md: Added FAQ entry with examples and use cases
- docs/configuration.md: Added dedicated section with examples

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-10 16:27:28 -07:00
Ashwin Bhat
89f9131f6c Add PostToolUse hook for automatic formatting (#563)
Added a PostToolUse hook that automatically runs `bun run format` after
Edit, Write, or MultiEdit operations, similar to the Python SDK's ruff
formatting hook. This ensures code is automatically formatted whenever
changes are made.

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-10 13:19:53 -07:00
Jimmy Utterström
b78e1c0244 feat: Add ANTHROPIC_CUSTOM_HEADERS environment variable support (#561) 2025-09-10 09:42:54 -07:00
GitHub Actions
abf075daf2 chore: bump Claude Code version to 1.0.110 2025-09-10 00:20:34 +00:00
Ashwin Bhat
a3ff61d47a enable track_progress for comments, fix mcp config (#558)
* 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>
2025-09-09 09:19:14 -07:00
Phantom
1b7eb924f1 fix: add missing githubContext (#547) 2025-09-08 22:47:46 -07:00
GitHub Actions
0f7dfed927 chore: bump Claude Code version to 1.0.109 2025-09-08 23:47:53 +00:00
Ashwin Bhat
11a01b7183 feat: update claude-review workflow to use slash command (#554)
* feat: update claude-review workflow to use progress tracking and slash command

- Rename workflow from "Auto review PRs" to "PR Review with Progress Tracking"
- Update trigger types to include synchronize, ready_for_review, reopened
- Add pull-requests: write permission for tracking comments
- Replace direct_prompt with /review-pr slash command using custom command file
- Update to use claude-code-action@v1
- Switch to inline comment tool for more precise PR feedback

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

Co-Authored-By: Claude <noreply@anthropic.com>

* agents

* refactor: standardize agent output format instructions

Unified the output format instructions across all reviewer agents to follow a consistent structure:
- Converted numbered sections to bold headers for better readability
- Standardized "Review Structure" sections across all agents
- Maintained distinct analysis areas specific to each reviewer type

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

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-08 07:06:52 -07:00
Ashwin Bhat
69dec299f8 feat: add allowed_non_write_users input to bypass permission checks (#550)
* chore: bump Claude Code version to 1.0.108

* triage fix

---------

Co-authored-by: GitHub Actions <actions@github.com>
2025-09-07 14:20:02 -07:00
Ashwin Bhat
1a8e7d330a fix: remove unnecessary GitHub comment server inclusion in agent mode (#549)
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>
2025-09-07 13:33:21 -07:00
kashyap murali
9975f36410 fix: use agent mode for issues events with explicit prompts (#530)
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>
2025-09-06 20:32:20 -07:00
GitHub Actions
c1ffc8a0e8 chore: bump Claude Code version to 1.0.108 2025-09-05 22:50:25 +00:00
bogini
13e47489f4 feat: add repository_dispatch event support (#546)
* 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>
2025-09-05 15:06:00 -07:00
Ashwin Bhat
765fadc6a6 fix: remove OIDC id-token permission and add github_token input (#545)
Removes the OIDC id-token permission requirement and adds explicit github_token input to both workflow files. This simplifies authentication by using the standard GITHUB_TOKEN instead of requiring OIDC token exchange.

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-05 11:58:05 -07:00
Ashwin Bhat
fd2c17f101 feat: enhance issue triage workflow with priority labeling and OIDC support (#540)
- Add id-token write permission for OIDC token exchange
- Update prompt to emphasize adding P1/P2/P3 priority labels based on label descriptions
- Ensure Claude selects appropriate priority labels from the available options

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-04 20:58:52 -07:00
GitHub Actions
a4a723b927 chore: bump Claude Code version to 1.0.107 2025-09-05 02:40:36 +00:00
GitHub Actions
d22fa6061b chore: bump Claude Code version to 1.0.106 2025-09-04 23:35:43 +00:00
Ashwin Bhat
63f1c772bd feat: add bot_id input to handle GitHub App authentication errors (#534)
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>
2025-09-04 14:55:25 -07:00
Ashwin Bhat
fb823f6dd6 fix: update action reference to claude-code-action in issue triage workflow (#537)
Changed from @anthropics/claude-code-base-action to @anthropics/claude-code-action
to use the correct action name in the issue triage workflow.

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-04 14:35:56 -07:00
Ashwin Bhat
9e9123239f docs: add timeout_minutes breaking change to migration guide (#529)
Add documentation for the timeout_minutes input removal that occurred in PR #482.
The input has been replaced with standard GitHub Actions timeout-minutes at job level.

Fixes #527

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Ashwin Bhat <ashwin-ant@users.noreply.github.com>
2025-09-04 12:39:22 -07:00
GitHub Actions
791fcb9fd1 chore: bump Claude Code version to 1.0.105 2025-09-04 16:13:45 +00:00
38 changed files with 1030 additions and 294 deletions

View File

@@ -0,0 +1,61 @@
---
name: code-quality-reviewer
description: Use this agent when you need to review code for quality, maintainability, and adherence to best practices. Examples:\n\n- After implementing a new feature or function:\n user: 'I've just written a function to process user authentication'\n assistant: 'Let me use the code-quality-reviewer agent to analyze the authentication function for code quality and best practices'\n\n- When refactoring existing code:\n user: 'I've refactored the payment processing module'\n assistant: 'I'll launch the code-quality-reviewer agent to ensure the refactored code maintains high quality standards'\n\n- Before committing significant changes:\n user: 'I've completed the API endpoint implementations'\n assistant: 'Let me use the code-quality-reviewer agent to review the endpoints for proper error handling and maintainability'\n\n- When uncertain about code quality:\n user: 'Can you check if this validation logic is robust enough?'\n assistant: 'I'll use the code-quality-reviewer agent to thoroughly analyze the validation logic'
tools: Glob, Grep, Read, WebFetch, TodoWrite, WebSearch, BashOutput, KillBash
model: inherit
---
You are an expert code quality reviewer with deep expertise in software engineering best practices, clean code principles, and maintainable architecture. Your role is to provide thorough, constructive code reviews focused on quality, readability, and long-term maintainability.
When reviewing code, you will:
**Clean Code Analysis:**
- Evaluate naming conventions for clarity and descriptiveness
- Assess function and method sizes for single responsibility adherence
- Check for code duplication and suggest DRY improvements
- Identify overly complex logic that could be simplified
- Verify proper separation of concerns
**Error Handling & Edge Cases:**
- Identify missing error handling for potential failure points
- Evaluate the robustness of input validation
- Check for proper handling of null/undefined values
- Assess edge case coverage (empty arrays, boundary conditions, etc.)
- Verify appropriate use of try-catch blocks and error propagation
**Readability & Maintainability:**
- Evaluate code structure and organization
- Check for appropriate use of comments (avoiding over-commenting obvious code)
- Assess the clarity of control flow
- Identify magic numbers or strings that should be constants
- Verify consistent code style and formatting
**TypeScript-Specific Considerations** (when applicable):
- Prefer `type` over `interface` as per project standards
- Avoid unnecessary use of underscores for unused variables
- Ensure proper type safety and avoid `any` types when possible
**Best Practices:**
- Evaluate adherence to SOLID principles
- Check for proper use of design patterns where appropriate
- Assess performance implications of implementation choices
- Verify security considerations (input sanitization, sensitive data handling)
**Review Structure:**
Provide your analysis in this format:
- Start with a brief summary of overall code quality
- Organize findings by severity (critical, important, minor)
- Provide specific examples with line references when possible
- Suggest concrete improvements with code examples
- Highlight positive aspects and good practices observed
- End with actionable recommendations prioritized by impact
Be constructive and educational in your feedback. When identifying issues, explain why they matter and how they impact code quality. Focus on teaching principles that will improve future code, not just fixing current issues.
If the code is well-written, acknowledge this and provide suggestions for potential enhancements rather than forcing criticism. Always maintain a professional, helpful tone that encourages continuous improvement.

View File

@@ -0,0 +1,56 @@
---
name: documentation-accuracy-reviewer
description: Use this agent when you need to verify that code documentation is accurate, complete, and up-to-date. Specifically use this agent after: implementing new features that require documentation updates, modifying existing APIs or functions, completing a logical chunk of code that needs documentation review, or when preparing code for review/release. Examples: 1) User: 'I just added a new authentication module with several public methods' → Assistant: 'Let me use the documentation-accuracy-reviewer agent to verify the documentation is complete and accurate for your new authentication module.' 2) User: 'Please review the documentation for the payment processing functions I just wrote' → Assistant: 'I'll launch the documentation-accuracy-reviewer agent to check your payment processing documentation.' 3) After user completes a feature implementation → Assistant: 'Now that the feature is complete, I'll use the documentation-accuracy-reviewer agent to ensure all documentation is accurate and up-to-date.'
tools: Glob, Grep, Read, WebFetch, TodoWrite, WebSearch, BashOutput, KillBash
model: inherit
---
You are an expert technical documentation reviewer with deep expertise in code documentation standards, API documentation best practices, and technical writing. Your primary responsibility is to ensure that code documentation accurately reflects implementation details and provides clear, useful information to developers.
When reviewing documentation, you will:
**Code Documentation Analysis:**
- Verify that all public functions, methods, and classes have appropriate documentation comments
- Check that parameter descriptions match actual parameter types and purposes
- Ensure return value documentation accurately describes what the code returns
- Validate that examples in documentation actually work with the current implementation
- Confirm that edge cases and error conditions are properly documented
- Check for outdated comments that reference removed or modified functionality
**README Verification:**
- Cross-reference README content with actual implemented features
- Verify installation instructions are current and complete
- Check that usage examples reflect the current API
- Ensure feature lists accurately represent available functionality
- Validate that configuration options documented in README match actual code
- Identify any new features missing from README documentation
**API Documentation Review:**
- Verify endpoint descriptions match actual implementation
- Check request/response examples for accuracy
- Ensure authentication requirements are correctly documented
- Validate parameter types, constraints, and default values
- Confirm error response documentation matches actual error handling
- Check that deprecated endpoints are properly marked
**Quality Standards:**
- Flag documentation that is vague, ambiguous, or misleading
- Identify missing documentation for public interfaces
- Note inconsistencies between documentation and implementation
- Suggest improvements for clarity and completeness
- Ensure documentation follows project-specific standards from CLAUDE.md
**Review Structure:**
Provide your analysis in this format:
- Start with a summary of overall documentation quality
- List specific issues found, categorized by type (code comments, README, API docs)
- For each issue, provide: file/location, current state, recommended fix
- Prioritize issues by severity (critical inaccuracies vs. minor improvements)
- End with actionable recommendations
You will be thorough but focused, identifying genuine documentation issues rather than stylistic preferences. When documentation is accurate and complete, acknowledge this clearly. If you need to examine specific files or code sections to verify documentation accuracy, request access to those resources. Always consider the target audience (developers using the code) and ensure documentation serves their needs effectively.

View File

@@ -0,0 +1,53 @@
---
name: performance-reviewer
description: Use this agent when you need to analyze code for performance issues, bottlenecks, and resource efficiency. Examples: After implementing database queries or API calls, when optimizing existing features, after writing data processing logic, when investigating slow application behavior, or when completing any code that involves loops, network requests, or memory-intensive operations.
tools: Glob, Grep, Read, WebFetch, TodoWrite, WebSearch, BashOutput, KillBash
model: inherit
---
You are an elite performance optimization specialist with deep expertise in identifying and resolving performance bottlenecks across all layers of software systems. Your mission is to conduct thorough performance reviews that uncover inefficiencies and provide actionable optimization recommendations.
When reviewing code, you will:
**Performance Bottleneck Analysis:**
- Examine algorithmic complexity and identify O(n²) or worse operations that could be optimized
- Detect unnecessary computations, redundant operations, or repeated work
- Identify blocking operations that could benefit from asynchronous execution
- Review loop structures for inefficient iterations or nested loops that could be flattened
- Check for premature optimization vs. legitimate performance concerns
**Network Query Efficiency:**
- Analyze database queries for N+1 problems and missing indexes
- Review API calls for batching opportunities and unnecessary round trips
- Check for proper use of pagination, filtering, and projection in data fetching
- Identify opportunities for caching, memoization, or request deduplication
- Examine connection pooling and resource reuse patterns
- Verify proper error handling that doesn't cause retry storms
**Memory and Resource Management:**
- Detect potential memory leaks from unclosed connections, event listeners, or circular references
- Review object lifecycle management and garbage collection implications
- Identify excessive memory allocation or large object creation in loops
- Check for proper cleanup in cleanup functions, destructors, or finally blocks
- Analyze data structure choices for memory efficiency
- Review file handles, database connections, and other resource cleanup
**Review Structure:**
Provide your analysis in this format:
1. **Critical Issues**: Immediate performance problems requiring attention
2. **Optimization Opportunities**: Improvements that would yield measurable benefits
3. **Best Practice Recommendations**: Preventive measures for future performance
4. **Code Examples**: Specific before/after snippets demonstrating improvements
For each issue identified:
- Specify the exact location (file, function, line numbers)
- Explain the performance impact with estimated complexity or resource usage
- Provide concrete, implementable solutions
- Prioritize recommendations by impact vs. effort
If code appears performant, confirm this explicitly and note any particularly well-optimized sections. Always consider the specific runtime environment and scale requirements when making recommendations.

View File

@@ -0,0 +1,59 @@
---
name: security-code-reviewer
description: Use this agent when you need to review code for security vulnerabilities, input validation issues, or authentication/authorization flaws. Examples: After implementing authentication logic, when adding user input handling, after writing API endpoints that process external data, or when integrating third-party libraries. The agent should be called proactively after completing security-sensitive code sections like login systems, data validation layers, or permission checks.
tools: Glob, Grep, Read, WebFetch, TodoWrite, WebSearch, BashOutput, KillBash
model: inherit
---
You are an elite security code reviewer with deep expertise in application security, threat modeling, and secure coding practices. Your mission is to identify and prevent security vulnerabilities before they reach production.
When reviewing code, you will:
**Security Vulnerability Assessment**
- Systematically scan for OWASP Top 10 vulnerabilities (injection flaws, broken authentication, sensitive data exposure, XXE, broken access control, security misconfiguration, XSS, insecure deserialization, using components with known vulnerabilities, insufficient logging)
- Identify potential SQL injection, NoSQL injection, and command injection vulnerabilities
- Check for cross-site scripting (XSS) vulnerabilities in any user-facing output
- Look for cross-site request forgery (CSRF) protection gaps
- Examine cryptographic implementations for weak algorithms or improper key management
- Identify potential race conditions and time-of-check-time-of-use (TOCTOU) vulnerabilities
**Input Validation and Sanitization**
- Verify all user inputs are properly validated against expected formats and ranges
- Ensure input sanitization occurs at appropriate boundaries (client-side validation is supplementary, never primary)
- Check for proper encoding when outputting user data
- Validate that file uploads have proper type checking, size limits, and content validation
- Ensure API parameters are validated for type, format, and business logic constraints
- Look for potential path traversal vulnerabilities in file operations
**Authentication and Authorization Review**
- Verify authentication mechanisms use secure, industry-standard approaches
- Check for proper session management (secure cookies, appropriate timeouts, session invalidation)
- Ensure passwords are properly hashed using modern algorithms (bcrypt, Argon2, PBKDF2)
- Validate that authorization checks occur at every protected resource access
- Look for privilege escalation opportunities
- Check for insecure direct object references (IDOR)
- Verify proper implementation of role-based or attribute-based access control
**Analysis Methodology**
1. First, identify the security context and attack surface of the code
2. Map data flows from untrusted sources to sensitive operations
3. Examine each security-critical operation for proper controls
4. Consider both common vulnerabilities and context-specific threats
5. Evaluate defense-in-depth measures
**Review Structure:**
Provide findings in order of severity (Critical, High, Medium, Low, Informational):
- **Vulnerability Description**: Clear explanation of the security issue
- **Location**: Specific file, function, and line numbers
- **Impact**: Potential consequences if exploited
- **Remediation**: Concrete steps to fix the vulnerability with code examples when helpful
- **References**: Relevant CWE numbers or security standards
If no security issues are found, provide a brief summary confirming the review was completed and highlighting any positive security practices observed.
Always consider the principle of least privilege, defense in depth, and fail securely. When uncertain about a potential vulnerability, err on the side of caution and flag it for further investigation.

View File

@@ -0,0 +1,52 @@
---
name: test-coverage-reviewer
description: Use this agent when you need to review testing implementation and coverage. Examples: After writing a new feature implementation, use this agent to verify test coverage. When refactoring code, use this agent to ensure tests still adequately cover all scenarios. After completing a module, use this agent to identify missing test cases and edge conditions.
tools: Glob, Grep, Read, WebFetch, TodoWrite, WebSearch, BashOutput, KillBash
model: inherit
---
You are an expert QA engineer and testing specialist with deep expertise in test-driven development, code coverage analysis, and quality assurance best practices. Your role is to conduct thorough reviews of test implementations to ensure comprehensive coverage and robust quality validation.
When reviewing code for testing, you will:
**Analyze Test Coverage:**
- Examine the ratio of test code to production code
- Identify untested code paths, branches, and edge cases
- Verify that all public APIs and critical functions have corresponding tests
- Check for coverage of error handling and exception scenarios
- Assess coverage of boundary conditions and input validation
**Evaluate Test Quality:**
- Review test structure and organization (arrange-act-assert pattern)
- Verify tests are isolated, independent, and deterministic
- Check for proper use of mocks, stubs, and test doubles
- Ensure tests have clear, descriptive names that document behavior
- Validate that assertions are specific and meaningful
- Identify brittle tests that may break with minor refactoring
**Identify Missing Test Scenarios:**
- List untested edge cases and boundary conditions
- Highlight missing integration test scenarios
- Point out uncovered error paths and failure modes
- Suggest performance and load testing opportunities
- Recommend security-related test cases where applicable
**Provide Actionable Feedback:**
- Prioritize findings by risk and impact
- Suggest specific test cases to add with example implementations
- Recommend refactoring opportunities to improve testability
- Identify anti-patterns and suggest corrections
**Review Structure:**
Provide your analysis in this format:
- **Coverage Analysis**: Summary of current test coverage with specific gaps
- **Quality Assessment**: Evaluation of existing test quality with examples
- **Missing Scenarios**: Prioritized list of untested cases
- **Recommendations**: Concrete actions to improve test suite
Be thorough but practical - focus on tests that provide real value and catch actual bugs. Consider the testing pyramid and ensure appropriate balance between unit, integration, and end-to-end tests.

View File

@@ -0,0 +1,60 @@
---
allowed-tools: Bash(gh label list:*),Bash(gh issue view:*),Bash(gh issue edit:*),Bash(gh search:*)
description: Apply labels to GitHub issues
---
You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list.
IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels.
Issue Information:
- REPO: ${{ github.repository }}
- ISSUE_NUMBER: ${{ github.event.issue.number }}
TASK OVERVIEW:
1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else.
2. Next, use gh commands to get context about the issue:
- Use `gh issue view ${{ github.event.issue.number }}` to retrieve the current issue's details
- Use `gh search issues` to find similar issues that might provide context for proper categorization
- You have access to these Bash commands:
- Bash(gh label list:\*) - to get available labels
- Bash(gh issue view:\*) - to view issue details
- Bash(gh issue edit:\*) - to apply labels to the issue
- Bash(gh search:\*) - to search for similar issues
3. Analyze the issue content, considering:
- The issue title and description
- The type of issue (bug report, feature request, question, etc.)
- Technical areas mentioned
- Severity or priority indicators
- User impact
- Components affected
4. Select appropriate labels from the available labels list provided above:
- Choose labels that accurately reflect the issue's nature
- Be specific but comprehensive
- IMPORTANT: Add a priority label (P1, P2, or P3) based on the label descriptions from gh label list
- Consider platform labels (android, ios) if applicable
- If you find similar issues using gh search, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
5. Apply the selected labels:
- Use `gh issue edit` to apply your selected labels
- DO NOT post any comments explaining your decision
- DO NOT communicate directly with users
- If no labels are clearly applicable, do not apply any labels
IMPORTANT GUIDELINES:
- Be thorough in your analysis
- Only select labels from the provided list above
- DO NOT post any comments to the issue
- Your ONLY action should be to apply labels using gh issue edit
- It's okay to not add any labels if none are clearly applicable
---

View File

@@ -0,0 +1,20 @@
---
allowed-tools: Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)
description: Review a pull request
---
Perform a comprehensive code review using subagents for key areas:
- code-quality-reviewer
- performance-reviewer
- test-coverage-reviewer
- documentation-accuracy-reviewer
- security-code-reviewer
Instruct each to only provide noteworthy feedback. Once they finish, review the feedback and post only the feedback that you also deem noteworthy.
Provide feedback using inline comments for specific issues.
Use top-level comments for general observations or praise.
Keep feedback concise.
---

15
.claude/settings.json Normal file
View File

@@ -0,0 +1,15 @@
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"type": "command",
"command": "bun run format"
}
],
"matcher": "Edit|Write|MultiEdit"
}
]
}
}

View File

@@ -1,33 +1,27 @@
name: Auto review PRs name: PR Review
on: on:
pull_request: pull_request:
types: [opened] types: [opened, synchronize, ready_for_review, reopened]
jobs: jobs:
auto-review: review:
runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
pull-requests: write
id-token: write id-token: write
runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 1 fetch-depth: 1
- name: Auto review PR - name: PR Review with Progress Tracking
uses: anthropics/claude-code-action@main uses: anthropics/claude-code-action@v1
with: with:
direct_prompt: |
Please review this PR. Look at the changes and provide thoughtful feedback on:
- Code quality and best practices
- Potential bugs or issues
- Suggestions for improvements
- Overall architecture and design decisions
- Documentation consistency: Verify that README.md and other documentation files are updated to reflect any code changes (especially new inputs, features, or configuration options)
Be constructive and specific in your feedback. Give inline comments where applicable.
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
allowed_tools: "mcp__github__create_pending_pull_request_review,mcp__github__add_comment_to_pending_review,mcp__github__submit_pending_pull_request_review,mcp__github__get_pull_request_diff"
prompt: "/review-pr REPO: ${{ github.repository }} PR_NUMBER: ${{ github.event.pull_request.number }}"
claude_args: |
--allowedTools "mcp__github_inline_comment__create_inline_comment"

View File

@@ -1,38 +0,0 @@
# Test workflow for km-anthropic fork (v1-dev branch)
# This tests the fork implementation, not the main repo
name: Claude Code (Fork Test)
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]
jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (
contains(github.event.issue.body, '@claude') ||
contains(github.event.issue.title, '@claude')
))
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write # Required for OIDC token exchange
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run Claude Code
uses: km-anthropic/claude-code-action@v1-dev
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}

View File

@@ -18,91 +18,10 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup GitHub MCP Server
run: |
mkdir -p /tmp/mcp-config
cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
{
"mcpServers": {
"github": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server:sha-efef8ae"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${{ secrets.GITHUB_TOKEN }}"
}
}
}
}
EOF
- name: Create triage prompt
run: |
mkdir -p /tmp/claude-prompts
cat > /tmp/claude-prompts/triage-prompt.txt << 'EOF'
You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list.
IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels.
Issue Information:
- REPO: ${{ github.repository }}
- ISSUE_NUMBER: ${{ github.event.issue.number }}
TASK OVERVIEW:
1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else.
2. Next, use the GitHub tools to get context about the issue:
- You have access to these tools:
- mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels
- mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments
- mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting)
- mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues
- mcp__github__list_issues: Use this to understand patterns in how other issues are labeled
- Start by using mcp__github__get_issue to get the issue details
3. Analyze the issue content, considering:
- The issue title and description
- The type of issue (bug report, feature request, question, etc.)
- Technical areas mentioned
- Severity or priority indicators
- User impact
- Components affected
4. Select appropriate labels from the available labels list provided above:
- Choose labels that accurately reflect the issue's nature
- Be specific but comprehensive
- Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority)
- Consider platform labels (android, ios) if applicable
- If you find similar issues using mcp__github__search_issues, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
5. Apply the selected labels:
- Use mcp__github__update_issue to apply your selected labels
- DO NOT post any comments explaining your decision
- DO NOT communicate directly with users
- If no labels are clearly applicable, do not apply any labels
IMPORTANT GUIDELINES:
- Be thorough in your analysis
- Only select labels from the provided list above
- DO NOT post any comments to the issue
- Your ONLY action should be to apply labels using mcp__github__update_issue
- It's okay to not add any labels if none are clearly applicable
EOF
- name: Run Claude Code for Issue Triage - name: Run Claude Code for Issue Triage
uses: anthropics/claude-code-base-action@v1 uses: anthropics/claude-code-action@main
with: with:
prompt: $(cat /tmp/claude-prompts/triage-prompt.txt) prompt: "/label-issue REPO: ${{ github.repository }} ISSUE_NUMBER${{ github.event.issue.number }}"
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: | allowed_non_write_users: "*" # Required for issue triage workflow, if users without repo write access create issues
--allowedTools Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues github_token: ${{ secrets.GITHUB_TOKEN }}
--mcp-config /tmp/mcp-config/mcp-servers.json
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -27,6 +27,10 @@ inputs:
description: "Comma-separated list of allowed bot usernames, or '*' to allow all bots. Empty string (default) allows no bots." description: "Comma-separated list of allowed bot usernames, or '*' to allow all bots. Empty string (default) allows no bots."
required: false required: false
default: "" default: ""
allowed_non_write_users:
description: "Comma-separated list of usernames to allow without write permissions, or '*' to allow all users. Only works when github_token input is provided. WARNING: Use with extreme caution - this bypasses security checks and should only be used for workflows with very limited permissions (e.g., issue labeling)."
required: false
default: ""
# Claude Code configuration # Claude Code configuration
prompt: prompt:
@@ -73,6 +77,14 @@ inputs:
description: "Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands" description: "Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands"
required: false required: false
default: "false" default: "false"
bot_id:
description: "GitHub user ID to use for git operations (defaults to Claude's bot ID)"
required: false
default: "41898282" # Claude's bot ID - see src/github/constants.ts
bot_name:
description: "GitHub username to use for git operations (defaults to Claude's bot name)"
required: false
default: "claude[bot]"
track_progress: track_progress:
description: "Force tag mode with tracking comments for pull_request and issue events. Only applicable to pull_request (opened, synchronize, ready_for_review, reopened) and issue (opened, edited, labeled, assigned) events." description: "Force tag mode with tracking comments for pull_request and issue events. Only applicable to pull_request (opened, synchronize, ready_for_review, reopened) and issue (opened, edited, labeled, assigned) events."
required: false required: false
@@ -140,10 +152,13 @@ runs:
BRANCH_PREFIX: ${{ inputs.branch_prefix }} BRANCH_PREFIX: ${{ inputs.branch_prefix }}
OVERRIDE_GITHUB_TOKEN: ${{ inputs.github_token }} OVERRIDE_GITHUB_TOKEN: ${{ inputs.github_token }}
ALLOWED_BOTS: ${{ inputs.allowed_bots }} ALLOWED_BOTS: ${{ inputs.allowed_bots }}
ALLOWED_NON_WRITE_USERS: ${{ inputs.allowed_non_write_users }}
GITHUB_RUN_ID: ${{ github.run_id }} GITHUB_RUN_ID: ${{ github.run_id }}
USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }} USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }}
DEFAULT_WORKFLOW_TOKEN: ${{ github.token }} DEFAULT_WORKFLOW_TOKEN: ${{ github.token }}
USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }} USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }}
BOT_ID: ${{ inputs.bot_id }}
BOT_NAME: ${{ inputs.bot_name }}
TRACK_PROGRESS: ${{ inputs.track_progress }} TRACK_PROGRESS: ${{ inputs.track_progress }}
ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }} ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }}
CLAUDE_ARGS: ${{ inputs.claude_args }} CLAUDE_ARGS: ${{ inputs.claude_args }}
@@ -162,7 +177,7 @@ runs:
# Install Claude Code if no custom executable is provided # Install Claude Code if no custom executable is provided
if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then
echo "Installing Claude Code..." echo "Installing Claude Code..."
curl -fsSL https://claude.ai/install.sh | bash -s 1.0.103 curl -fsSL https://claude.ai/install.sh | bash -s 1.0.117
echo "$HOME/.local/bin" >> "$GITHUB_PATH" echo "$HOME/.local/bin" >> "$GITHUB_PATH"
else else
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}" echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
@@ -208,6 +223,7 @@ runs:
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }} ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ inputs.claude_code_oauth_token }} CLAUDE_CODE_OAUTH_TOKEN: ${{ inputs.claude_code_oauth_token }}
ANTHROPIC_BASE_URL: ${{ env.ANTHROPIC_BASE_URL }} ANTHROPIC_BASE_URL: ${{ env.ANTHROPIC_BASE_URL }}
ANTHROPIC_CUSTOM_HEADERS: ${{ env.ANTHROPIC_CUSTOM_HEADERS }}
CLAUDE_CODE_USE_BEDROCK: ${{ inputs.use_bedrock == 'true' && '1' || '' }} CLAUDE_CODE_USE_BEDROCK: ${{ inputs.use_bedrock == 'true' && '1' || '' }}
CLAUDE_CODE_USE_VERTEX: ${{ inputs.use_vertex == 'true' && '1' || '' }} CLAUDE_CODE_USE_VERTEX: ${{ inputs.use_vertex == 'true' && '1' || '' }}

View File

@@ -50,7 +50,7 @@ This is a GitHub Action that allows running Claude Code within GitHub workflows.
- Unit tests for configuration logic - Unit tests for configuration logic
- Integration tests for prompt preparation - Integration tests for prompt preparation
- Full workflow tests in `.github/workflows/test-action.yml` - Full workflow tests in `.github/workflows/test-base-action.yml`
## Important Technical Details ## Important Technical Details

View File

@@ -99,7 +99,7 @@ runs:
run: | run: |
if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then
echo "Installing Claude Code..." echo "Installing Claude Code..."
curl -fsSL https://claude.ai/install.sh | bash -s 1.0.103 curl -fsSL https://claude.ai/install.sh | bash -s 1.0.117
else else
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}" echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
# Add the directory containing the custom executable to PATH # Add the directory containing the custom executable to PATH
@@ -131,6 +131,7 @@ runs:
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }} ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ inputs.claude_code_oauth_token }} CLAUDE_CODE_OAUTH_TOKEN: ${{ inputs.claude_code_oauth_token }}
ANTHROPIC_BASE_URL: ${{ env.ANTHROPIC_BASE_URL }} ANTHROPIC_BASE_URL: ${{ env.ANTHROPIC_BASE_URL }}
ANTHROPIC_CUSTOM_HEADERS: ${{ env.ANTHROPIC_CUSTOM_HEADERS }}
# Only set provider flags if explicitly true, since any value (including "false") is truthy # Only set provider flags if explicitly true, since any value (including "false") is truthy
CLAUDE_CODE_USE_BEDROCK: ${{ inputs.use_bedrock == 'true' && '1' || '' }} CLAUDE_CODE_USE_BEDROCK: ${{ inputs.use_bedrock == 'true' && '1' || '' }}
CLAUDE_CODE_USE_VERTEX: ${{ inputs.use_vertex == 'true' && '1' || '' }} CLAUDE_CODE_USE_VERTEX: ${{ inputs.use_vertex == 'true' && '1' || '' }}

View File

@@ -9,4 +9,4 @@ fi
# Run the test workflow locally # Run the test workflow locally
# You'll need to provide your ANTHROPIC_API_KEY # You'll need to provide your ANTHROPIC_API_KEY
echo "Running action locally with act..." echo "Running action locally with act..."
act push --secret ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" -W .github/workflows/test-action.yml --container-architecture linux/amd64 act push --secret ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" -W .github/workflows/test-base-action.yml --container-architecture linux/amd64

View File

@@ -343,3 +343,31 @@ Many individual input parameters have been consolidated into `claude_args` or `s
| `mcp_config` | Use `claude_args: "--mcp-config '{...}'"` | | `mcp_config` | Use `claude_args: "--mcp-config '{...}'"` |
| `direct_prompt` | Use `prompt` input instead | | `direct_prompt` | Use `prompt` input instead |
| `override_prompt` | Use `prompt` with GitHub context variables | | `override_prompt` | Use `prompt` with GitHub context variables |
## Custom Executables for Specialized Environments
For specialized environments like Nix, custom container setups, or other package management systems where the default installation doesn't work, you can provide your own executables:
### Custom Claude Code Executable
Use `path_to_claude_code_executable` to provide your own Claude Code binary instead of using the automatically installed version:
```yaml
- uses: anthropics/claude-code-action@v1
with:
path_to_claude_code_executable: "/path/to/custom/claude"
# ... other inputs
```
### Custom Bun Executable
Use `path_to_bun_executable` to provide your own Bun runtime instead of the default installation:
```yaml
- uses: anthropics/claude-code-action@v1
with:
path_to_bun_executable: "/path/to/custom/bun"
# ... other inputs
```
**Important**: Using incompatible versions may cause the action to fail. Ensure your custom executables are compatible with the action's requirements.

View File

@@ -21,7 +21,7 @@ This action supports the following GitHub events ([learn more GitHub event trigg
- `issues` - When issues are opened or assigned - `issues` - When issues are opened or assigned
- `pull_request_review` - When PR reviews are submitted - `pull_request_review` - When PR reviews are submitted
- `pull_request_review_comment` - When comments are made on PR reviews - `pull_request_review_comment` - When comments are made on PR reviews
- `repository_dispatch` - Custom events triggered via API (coming soon) - `repository_dispatch` - Custom events triggered via API
- `workflow_dispatch` - Manual workflow triggers (coming soon) - `workflow_dispatch` - Manual workflow triggers (coming soon)
## Automated Documentation Updates ## Automated Documentation Updates

View File

@@ -28,6 +28,33 @@ permissions:
The OIDC token is required in order for the Claude GitHub app to function. If you wish to not use the GitHub app, you can instead provide a `github_token` input to the action for Claude to operate with. See the [Claude Code permissions documentation][perms] for more. The OIDC token is required in order for the Claude GitHub app to function. If you wish to not use the GitHub app, you can instead provide a `github_token` input to the action for Claude to operate with. See the [Claude Code permissions documentation][perms] for more.
### Why am I getting '403 Resource not accessible by integration' errors?
This error occurs when the action tries to fetch the authenticated user information using a GitHub App installation token. GitHub App tokens have limited access and cannot access the `/user` endpoint, which causes this 403 error.
**Solution**: The action now includes `bot_id` and `bot_name` inputs that default to Claude's bot credentials. This avoids the need to fetch user information from the API.
For the default claude[bot]:
```yaml
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
# bot_id and bot_name have sensible defaults, no need to specify
```
For custom bots, specify both:
```yaml
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
bot_id: "12345678" # Your bot's GitHub user ID
bot_name: "my-bot" # Your bot's username
```
This issue typically only affects agent/automation mode workflows. Interactive workflows (with @claude mentions) don't encounter this issue as they use the comment author's information.
## Claude's Capabilities and Limitations ## Claude's Capabilities and Limitations
### Why won't Claude update workflow files when I ask it to? ### Why won't Claude update workflow files when I ask it to?
@@ -186,6 +213,44 @@ Check the GitHub Action log for Claude's run for the full execution trace.
The trigger uses word boundaries, so `@claude` must be a complete word. Variations like `@claude-bot`, `@claude!`, or `claude@mention` won't work unless you customize the `trigger_phrase`. The trigger uses word boundaries, so `@claude` must be a complete word. Variations like `@claude-bot`, `@claude!`, or `claude@mention` won't work unless you customize the `trigger_phrase`.
### How can I use custom executables in specialized environments?
For specialized environments like Nix, NixOS, or custom container setups where you need to provide your own executables:
**Using a custom Claude Code executable:**
```yaml
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
path_to_claude_code_executable: "/path/to/custom/claude"
# ... other inputs
```
**Using a custom Bun executable:**
```yaml
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
path_to_bun_executable: "/path/to/custom/bun"
# ... other inputs
```
**Common use cases:**
- Nix/NixOS environments where packages are managed differently
- Docker containers with pre-installed executables
- Custom build environments with specific version requirements
- Debugging specific issues with particular versions
**Important notes:**
- Using an older Claude Code version may cause problems if the action uses newer features
- Using an incompatible Bun version may cause runtime errors
- The action will skip automatic installation when custom paths are provided
- Ensure the custom executables are available in your GitHub Actions environment
## Best Practices ## Best Practices
1. **Always specify permissions explicitly** in your workflow file 1. **Always specify permissions explicitly** in your workflow file

View File

@@ -15,7 +15,7 @@ This guide helps you migrate from Claude Code Action v0.x to v1.0. The new versi
The following inputs have been deprecated and replaced: The following inputs have been deprecated and replaced:
| Deprecated Input | Replacement | Notes | | Deprecated Input | Replacement | Notes |
| --------------------- | -------------------------------- | --------------------------------------------- | | --------------------- | ------------------------------------ | --------------------------------------------- |
| `mode` | Auto-detected | Action automatically chooses based on context | | `mode` | Auto-detected | Action automatically chooses based on context |
| `direct_prompt` | `prompt` | Direct drop-in replacement | | `direct_prompt` | `prompt` | Direct drop-in replacement |
| `override_prompt` | `prompt` | Use GitHub context variables instead | | `override_prompt` | `prompt` | Use GitHub context variables instead |
@@ -26,6 +26,7 @@ The following inputs have been deprecated and replaced:
| `disallowed_tools` | `claude_args: --disallowedTools` | Use CLI format | | `disallowed_tools` | `claude_args: --disallowedTools` | Use CLI format |
| `claude_env` | `settings` with env object | Use settings JSON | | `claude_env` | `settings` with env object | Use settings JSON |
| `mcp_config` | `claude_args: --mcp-config` | Pass MCP config via CLI arguments | | `mcp_config` | `claude_args: --mcp-config` | Pass MCP config via CLI arguments |
| `timeout_minutes` | Use GitHub Actions `timeout-minutes` | Configure at job level instead of input level |
## Migration Examples ## Migration Examples
@@ -198,6 +199,30 @@ The `track_progress` input only works with these GitHub events:
} }
``` ```
### Timeout Configuration
**Before (v0.x):**
```yaml
- uses: anthropics/claude-code-action@beta
with:
timeout_minutes: 30
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
```
**After (v1.0):**
```yaml
jobs:
claude-task:
runs-on: ubuntu-latest
timeout-minutes: 30 # Moved to job level
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
```
## How Mode Detection Works ## How Mode Detection Works
The action now automatically detects the appropriate mode: The action now automatically detects the appropriate mode:
@@ -312,6 +337,7 @@ You can also pass MCP configuration from a file:
- [ ] Convert `disallowed_tools` to `claude_args` with `--disallowedTools` - [ ] Convert `disallowed_tools` to `claude_args` with `--disallowedTools`
- [ ] Move `claude_env` to `settings` JSON format - [ ] Move `claude_env` to `settings` JSON format
- [ ] Move `mcp_config` to `claude_args` with `--mcp-config` - [ ] Move `mcp_config` to `claude_args` with `--mcp-config`
- [ ] Replace `timeout_minutes` with GitHub Actions `timeout-minutes` at job level
- [ ] **Optional**: Add `track_progress: true` if you need tracking comments in automation mode - [ ] **Optional**: Add `track_progress: true` if you need tracking comments in automation mode
- [ ] Test workflow in a non-production environment - [ ] Test workflow in a non-production environment

View File

@@ -4,6 +4,11 @@
- **Repository Access**: The action can only be triggered by users with write access to the repository - **Repository Access**: The action can only be triggered by users with write access to the repository
- **Bot User Control**: By default, GitHub Apps and bots cannot trigger this action for security reasons. Use the `allowed_bots` parameter to enable specific bots or all bots - **Bot User Control**: By default, GitHub Apps and bots cannot trigger this action for security reasons. Use the `allowed_bots` parameter to enable specific bots or all bots
- **⚠️ Non-Write User Access (RISKY)**: The `allowed_non_write_users` parameter allows bypassing the write permission requirement. **This is a significant security risk and should only be used for workflows with extremely limited permissions** (e.g., issue labeling workflows that only have `issues: write` permission). This feature:
- Only works when `github_token` is provided as input (not with GitHub App authentication)
- Accepts either a comma-separated list of specific usernames or `*` to allow all users
- **Should be used with extreme caution** as it bypasses the primary security mechanism of this action
- Is designed for automation workflows where user permissions are already restricted by the workflow's permission scope
- **Token Permissions**: The GitHub app receives only a short-lived token scoped specifically to the repository it's operating in - **Token Permissions**: The GitHub app receives only a short-lived token scoped specifically to the repository it's operating in
- **No Cross-Repository Access**: Each action invocation is limited to the repository where it was triggered - **No Cross-Repository Access**: Each action invocation is limited to the repository where it was triggered
- **Limited Scope**: The token cannot access other repositories or perform actions beyond the configured permissions - **Limited Scope**: The token cannot access other repositories or perform actions beyond the configured permissions

View File

@@ -48,7 +48,7 @@ jobs:
## Inputs ## Inputs
| Input | Description | Required | Default | | Input | Description | Required | Default |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------- | -------- | --------- | | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------------- |
| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - | | `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - |
| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - | | `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - |
| `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - | | `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - |
@@ -68,7 +68,12 @@ jobs:
| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" | | `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" |
| `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" | | `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" |
| `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` | | `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` |
| `bot_id` | GitHub user ID to use for git operations (defaults to Claude's bot ID) | No | `41898282` |
| `bot_name` | GitHub username to use for git operations (defaults to Claude's bot name) | No | `claude[bot]` |
| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots | No | "" | | `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots | No | "" |
| `allowed_non_write_users` | **⚠️ RISKY**: Comma-separated list of usernames to allow without write permissions, or '\*' for all users. Only works with `github_token` input. See [Security](./security.md) | No | "" |
| `path_to_claude_code_executable` | Optional path to a custom Claude Code executable. Skips automatic installation. Useful for Nix, custom containers, or specialized environments | No | "" |
| `path_to_bun_executable` | Optional path to a custom Bun executable. Skips automatic Bun installation. Useful for Nix, custom containers, or specialized environments | No | "" |
### Deprecated Inputs ### Deprecated Inputs

View File

@@ -1,4 +1,5 @@
name: Issue Triage name: Claude Issue Triage
description: Run Claude Code for issue triage in GitHub Actions
on: on:
issues: issues:
types: [opened] types: [opened]
@@ -10,7 +11,6 @@ jobs:
permissions: permissions:
contents: read contents: read
issues: write issues: write
id-token: write
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -18,59 +18,12 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Triage issue with Claude - name: Run Claude Code for Issue Triage
uses: anthropics/claude-code-action@v1 uses: anthropics/claude-code-action@v1
with: with:
prompt: | # NOTE: /label-issue here requires a .claude/commands/label-issue.md file in your repo (see this repo's .claude directory for an example)
You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list. prompt: "/label-issue REPO: ${{ github.repository }} ISSUE_NUMBER${{ github.event.issue.number }}"
IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels.
Issue Information:
- REPO: ${{ github.repository }}
- ISSUE_NUMBER: ${{ github.event.issue.number }}
TASK OVERVIEW:
1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else.
2. Next, use the GitHub tools to get context about the issue:
- You have access to these tools:
- mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels
- mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments
- mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting)
- mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues
- mcp__github__list_issues: Use this to understand patterns in how other issues are labeled
- Start by using mcp__github__get_issue to get the issue details
3. Analyze the issue content, considering:
- The issue title and description
- The type of issue (bug report, feature request, question, etc.)
- Technical areas mentioned
- Severity or priority indicators
- User impact
- Components affected
4. Select appropriate labels from the available labels list provided above:
- Choose labels that accurately reflect the issue's nature
- Be specific but comprehensive
- Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority)
- Consider platform labels (android, ios) if applicable
- If you find similar issues using mcp__github__search_issues, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
5. Apply the selected labels:
- Use mcp__github__update_issue to apply your selected labels
- DO NOT post any comments explaining your decision
- DO NOT communicate directly with users
- If no labels are clearly applicable, do not apply any labels
IMPORTANT GUIDELINES:
- Be thorough in your analysis
- Only select labels from the provided list above
- DO NOT post any comments to the issue
- Your ONLY action should be to apply labels using mcp__github__update_issue
- It's okay to not add any labels if none are clearly applicable
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: | allowed_non_write_users: "*" # Required for issue triage workflow, if users without repo write access create issues
--allowedTools "Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues" github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -335,6 +335,7 @@ export function prepareContext(
return { return {
...commonFields, ...commonFields,
eventData, eventData,
githubContext: context,
}; };
} }

View File

@@ -30,9 +30,13 @@ async function run() {
// Step 3: Check write permissions (only for entity contexts) // Step 3: Check write permissions (only for entity contexts)
if (isEntityContext(context)) { if (isEntityContext(context)) {
// Check if github_token was provided as input (not from app)
const githubTokenProvided = !!process.env.OVERRIDE_GITHUB_TOKEN;
const hasWritePermissions = await checkWritePermissions( const hasWritePermissions = await checkWritePermissions(
octokit.rest, octokit.rest,
context, context,
context.inputs.allowedNonWriteUsers,
githubTokenProvided,
); );
if (!hasWritePermissions) { if (!hasWritePermissions) {
throw new Error( throw new Error(

13
src/github/constants.ts Normal file
View File

@@ -0,0 +1,13 @@
/**
* GitHub-related constants used throughout the application
*/
/**
* Claude App bot user ID
*/
export const CLAUDE_APP_BOT_ID = 41898282;
/**
* Claude bot username
*/
export const CLAUDE_BOT_LOGIN = "claude[bot]";

View File

@@ -8,6 +8,7 @@ import type {
PullRequestReviewCommentEvent, PullRequestReviewCommentEvent,
WorkflowRunEvent, WorkflowRunEvent,
} from "@octokit/webhooks-types"; } from "@octokit/webhooks-types";
import { CLAUDE_APP_BOT_ID, CLAUDE_BOT_LOGIN } from "./constants";
// Custom types for GitHub Actions events that aren't webhooks // Custom types for GitHub Actions events that aren't webhooks
export type WorkflowDispatchEvent = { export type WorkflowDispatchEvent = {
action?: never; action?: never;
@@ -25,6 +26,20 @@ export type WorkflowDispatchEvent = {
workflow: string; workflow: string;
}; };
export type RepositoryDispatchEvent = {
action: string;
client_payload?: Record<string, any>;
repository: {
name: string;
owner: {
login: string;
};
};
sender: {
login: string;
};
};
export type ScheduleEvent = { export type ScheduleEvent = {
action?: never; action?: never;
schedule?: string; schedule?: string;
@@ -47,6 +62,7 @@ const ENTITY_EVENT_NAMES = [
const AUTOMATION_EVENT_NAMES = [ const AUTOMATION_EVENT_NAMES = [
"workflow_dispatch", "workflow_dispatch",
"repository_dispatch",
"schedule", "schedule",
"workflow_run", "workflow_run",
] as const; ] as const;
@@ -74,7 +90,10 @@ type BaseContext = {
branchPrefix: string; branchPrefix: string;
useStickyComment: boolean; useStickyComment: boolean;
useCommitSigning: boolean; useCommitSigning: boolean;
botId: string;
botName: string;
allowedBots: string; allowedBots: string;
allowedNonWriteUsers: string;
trackProgress: boolean; trackProgress: boolean;
}; };
}; };
@@ -92,10 +111,14 @@ export type ParsedGitHubContext = BaseContext & {
isPR: boolean; isPR: boolean;
}; };
// Context for automation events (workflow_dispatch, schedule, workflow_run) // Context for automation events (workflow_dispatch, repository_dispatch, schedule, workflow_run)
export type AutomationContext = BaseContext & { export type AutomationContext = BaseContext & {
eventName: AutomationEventName; eventName: AutomationEventName;
payload: WorkflowDispatchEvent | ScheduleEvent | WorkflowRunEvent; payload:
| WorkflowDispatchEvent
| RepositoryDispatchEvent
| ScheduleEvent
| WorkflowRunEvent;
}; };
// Union type for all contexts // Union type for all contexts
@@ -122,7 +145,10 @@ export function parseGitHubContext(): GitHubContext {
branchPrefix: process.env.BRANCH_PREFIX ?? "claude/", branchPrefix: process.env.BRANCH_PREFIX ?? "claude/",
useStickyComment: process.env.USE_STICKY_COMMENT === "true", useStickyComment: process.env.USE_STICKY_COMMENT === "true",
useCommitSigning: process.env.USE_COMMIT_SIGNING === "true", useCommitSigning: process.env.USE_COMMIT_SIGNING === "true",
botId: process.env.BOT_ID ?? String(CLAUDE_APP_BOT_ID),
botName: process.env.BOT_NAME ?? CLAUDE_BOT_LOGIN,
allowedBots: process.env.ALLOWED_BOTS ?? "", allowedBots: process.env.ALLOWED_BOTS ?? "",
allowedNonWriteUsers: process.env.ALLOWED_NON_WRITE_USERS ?? "",
trackProgress: process.env.TRACK_PROGRESS === "true", trackProgress: process.env.TRACK_PROGRESS === "true",
}, },
}; };
@@ -185,6 +211,13 @@ export function parseGitHubContext(): GitHubContext {
payload: context.payload as unknown as WorkflowDispatchEvent, payload: context.payload as unknown as WorkflowDispatchEvent,
}; };
} }
case "repository_dispatch": {
return {
...commonFields,
eventName: "repository_dispatch",
payload: context.payload as unknown as RepositoryDispatchEvent,
};
}
case "schedule": { case "schedule": {
return { return {
...commonFields, ...commonFields,

View File

@@ -17,7 +17,7 @@ type GitUser = {
export async function configureGitAuth( export async function configureGitAuth(
githubToken: string, githubToken: string,
context: GitHubContext, context: GitHubContext,
user: GitUser | null, user: GitUser,
) { ) {
console.log("Configuring git authentication for non-signing mode"); console.log("Configuring git authentication for non-signing mode");
@@ -28,20 +28,14 @@ export async function configureGitAuth(
? "users.noreply.github.com" ? "users.noreply.github.com"
: `users.noreply.${serverUrl.hostname}`; : `users.noreply.${serverUrl.hostname}`;
// Configure git user based on the comment creator // Configure git user
console.log("Configuring git user..."); console.log("Configuring git user...");
if (user) {
const botName = user.login; const botName = user.login;
const botId = user.id; const botId = user.id;
console.log(`Setting git user as ${botName}...`); console.log(`Setting git user as ${botName}...`);
await $`git config user.name "${botName}"`; await $`git config user.name "${botName}"`;
await $`git config user.email "${botId}+${botName}@${noreplyDomain}"`; await $`git config user.email "${botId}+${botName}@${noreplyDomain}"`;
console.log(`✓ Set git user as ${botName}`); console.log(`✓ Set git user as ${botName}`);
} else {
console.log("No user data in comment, using default bot user");
await $`git config user.name "github-actions[bot]"`;
await $`git config user.email "41898282+github-actions[bot]@${noreplyDomain}"`;
}
// Remove the authorization header that actions/checkout sets // Remove the authorization header that actions/checkout sets
console.log("Removing existing git authentication headers..."); console.log("Removing existing git authentication headers...");

View File

@@ -6,17 +6,43 @@ import type { Octokit } from "@octokit/rest";
* Check if the actor has write permissions to the repository * Check if the actor has write permissions to the repository
* @param octokit - The Octokit REST client * @param octokit - The Octokit REST client
* @param context - The GitHub context * @param context - The GitHub context
* @param allowedNonWriteUsers - Comma-separated list of users allowed without write permissions, or '*' for all
* @param githubTokenProvided - Whether github_token was provided as input (not from app)
* @returns true if the actor has write permissions, false otherwise * @returns true if the actor has write permissions, false otherwise
*/ */
export async function checkWritePermissions( export async function checkWritePermissions(
octokit: Octokit, octokit: Octokit,
context: ParsedGitHubContext, context: ParsedGitHubContext,
allowedNonWriteUsers?: string,
githubTokenProvided?: boolean,
): Promise<boolean> { ): Promise<boolean> {
const { repository, actor } = context; const { repository, actor } = context;
try { try {
core.info(`Checking permissions for actor: ${actor}`); core.info(`Checking permissions for actor: ${actor}`);
// Check if we should bypass permission checks for this user
if (allowedNonWriteUsers && githubTokenProvided) {
const allowedUsers = allowedNonWriteUsers.trim();
if (allowedUsers === "*") {
core.warning(
`⚠️ SECURITY WARNING: Bypassing write permission check for ${actor} due to allowed_non_write_users='*'. This should only be used for workflows with very limited permissions.`,
);
return true;
} else if (allowedUsers) {
const allowedUserList = allowedUsers
.split(",")
.map((u) => u.trim())
.filter((u) => u.length > 0);
if (allowedUserList.includes(actor)) {
core.warning(
`⚠️ SECURITY WARNING: Bypassing write permission check for ${actor} due to allowed_non_write_users configuration. This should only be used for workflows with very limited permissions.`,
);
return true;
}
}
}
// Check if the actor is a GitHub App (bot user) // Check if the actor is a GitHub App (bot user)
if (actor.endsWith("[bot]")) { if (actor.endsWith("[bot]")) {
core.info(`Actor is a GitHub App: ${actor}`); core.info(`Actor is a GitHub App: ${actor}`);

View File

@@ -3,6 +3,7 @@ import { GITHUB_API_URL, GITHUB_SERVER_URL } from "../github/api/config";
import type { GitHubContext } from "../github/context"; import type { GitHubContext } from "../github/context";
import { isEntityContext } from "../github/context"; import { isEntityContext } from "../github/context";
import { Octokit } from "@octokit/rest"; import { Octokit } from "@octokit/rest";
import type { AutoDetectedMode } from "../modes/detector";
type PrepareConfigParams = { type PrepareConfigParams = {
githubToken: string; githubToken: string;
@@ -12,6 +13,7 @@ type PrepareConfigParams = {
baseBranch: string; baseBranch: string;
claudeCommentId?: string; claudeCommentId?: string;
allowedTools: string[]; allowedTools: string[];
mode: AutoDetectedMode;
context: GitHubContext; context: GitHubContext;
}; };
@@ -59,12 +61,17 @@ export async function prepareMcpConfig(
claudeCommentId, claudeCommentId,
allowedTools, allowedTools,
context, context,
mode,
} = params; } = params;
try { try {
const allowedToolsList = allowedTools || []; const allowedToolsList = allowedTools || [];
// Detect if we're in agent mode (explicit prompt provided) // Detect if we're in agent mode (explicit prompt provided)
const isAgentMode = !!context.inputs?.prompt; const isAgentMode = mode === "agent";
const hasGitHubCommentTools = allowedToolsList.some((tool) =>
tool.startsWith("mcp__github_comment__"),
);
const hasGitHubMcpTools = allowedToolsList.some((tool) => const hasGitHubMcpTools = allowedToolsList.some((tool) =>
tool.startsWith("mcp__github__"), tool.startsWith("mcp__github__"),
@@ -74,10 +81,6 @@ export async function prepareMcpConfig(
tool.startsWith("mcp__github_inline_comment__"), tool.startsWith("mcp__github_inline_comment__"),
); );
const hasGitHubCommentTools = allowedToolsList.some((tool) =>
tool.startsWith("mcp__github_comment__"),
);
const hasGitHubCITools = allowedToolsList.some((tool) => const hasGitHubCITools = allowedToolsList.some((tool) =>
tool.startsWith("mcp__github_ci__"), tool.startsWith("mcp__github_ci__"),
); );

View File

@@ -77,22 +77,16 @@ export const agentMode: Mode = {
return false; return false;
}, },
async prepare({ async prepare({ context, githubToken }: ModeOptions): Promise<ModeResult> {
context,
githubToken,
octokit,
}: ModeOptions): Promise<ModeResult> {
// Configure git authentication for agent mode (same as tag mode) // Configure git authentication for agent mode (same as tag mode)
if (!context.inputs.useCommitSigning) { if (!context.inputs.useCommitSigning) {
try { // Use bot_id and bot_name from inputs directly
// Get the authenticated user (will be claude[bot] when using Claude App token)
const { data: authenticatedUser } =
await octokit.rest.users.getAuthenticated();
const user = { const user = {
login: authenticatedUser.login, login: context.inputs.botName,
id: authenticatedUser.id, id: parseInt(context.inputs.botId),
}; };
try {
// Use the shared git configuration function // Use the shared git configuration function
await configureGitAuth(githubToken, context, user); await configureGitAuth(githubToken, context, user);
} catch (error) { } catch (error) {
@@ -141,6 +135,7 @@ export const agentMode: Mode = {
baseBranch: baseBranch, baseBranch: baseBranch,
claudeCommentId: undefined, // No tracking comment in agent mode claudeCommentId: undefined, // No tracking comment in agent mode
allowedTools, allowedTools,
mode: "agent",
context, context,
}); });

View File

@@ -19,7 +19,13 @@ export function detectMode(context: GitHubContext): AutoDetectedMode {
// If track_progress is set for PR/issue events, force tag mode // If track_progress is set for PR/issue events, force tag mode
if (context.inputs.trackProgress && isEntityContext(context)) { if (context.inputs.trackProgress && isEntityContext(context)) {
if (isPullRequestEvent(context) || isIssuesEvent(context)) { if (
isPullRequestEvent(context) ||
isIssuesEvent(context) ||
isIssueCommentEvent(context) ||
isPullRequestReviewCommentEvent(context) ||
isPullRequestReviewEvent(context)
) {
return "tag"; return "tag";
} }
} }
@@ -44,6 +50,10 @@ export function detectMode(context: GitHubContext): AutoDetectedMode {
// Issue events // Issue events
if (isEntityContext(context) && isIssuesEvent(context)) { if (isEntityContext(context) && isIssuesEvent(context)) {
// If prompt is provided, use agent mode (same as PR events)
if (context.inputs.prompt) {
return "agent";
}
// Check for @claude mentions or labels/assignees // Check for @claude mentions or labels/assignees
if (checkContainsTrigger(context)) { if (checkContainsTrigger(context)) {
return "tag"; return "tag";
@@ -83,10 +93,16 @@ export function getModeDescription(mode: AutoDetectedMode): string {
function validateTrackProgressEvent(context: GitHubContext): void { function validateTrackProgressEvent(context: GitHubContext): void {
// track_progress is only valid for pull_request and issue events // track_progress is only valid for pull_request and issue events
const validEvents = ["pull_request", "issues"]; const validEvents = [
"pull_request",
"issues",
"issue_comment",
"pull_request_review_comment",
"pull_request_review",
];
if (!validEvents.includes(context.eventName)) { if (!validEvents.includes(context.eventName)) {
throw new Error( throw new Error(
`track_progress is only supported for pull_request and issue events. ` + `track_progress is only supported for events: ${validEvents.join(", ")}. ` +
`Current event: ${context.eventName}`, `Current event: ${context.eventName}`,
); );
} }

View File

@@ -14,6 +14,7 @@ import { createPrompt, generateDefaultPrompt } from "../../create-prompt";
import { isEntityContext } from "../../github/context"; import { isEntityContext } from "../../github/context";
import type { PreparedContext } from "../../create-prompt/types"; import type { PreparedContext } from "../../create-prompt/types";
import type { FetchDataResult } from "../../github/data/fetcher"; import type { FetchDataResult } from "../../github/data/fetcher";
import { parseAllowedTools } from "../agent/parse-tools";
/** /**
* Tag mode implementation. * Tag mode implementation.
@@ -89,8 +90,14 @@ export const tagMode: Mode = {
// Configure git authentication if not using commit signing // Configure git authentication if not using commit signing
if (!context.inputs.useCommitSigning) { if (!context.inputs.useCommitSigning) {
// Use bot_id and bot_name from inputs directly
const user = {
login: context.inputs.botName,
id: parseInt(context.inputs.botId),
};
try { try {
await configureGitAuth(githubToken, context, commentData.user); await configureGitAuth(githubToken, context, user);
} catch (error) { } catch (error) {
console.error("Failed to configure git authentication:", error); console.error("Failed to configure git authentication:", error);
throw error; throw error;
@@ -106,19 +113,10 @@ export const tagMode: Mode = {
await createPrompt(tagMode, modeContext, githubData, context); await createPrompt(tagMode, modeContext, githubData, context);
// Get our GitHub MCP servers configuration const userClaudeArgs = process.env.CLAUDE_ARGS || "";
const ourMcpConfig = await prepareMcpConfig({ const userAllowedMCPTools = parseAllowedTools(userClaudeArgs).filter(
githubToken, (tool) => tool.startsWith("mcp__github_"),
owner: context.repository.owner, );
repo: context.repository.repo,
branch: branchInfo.claudeBranch || branchInfo.currentBranch,
baseBranch: branchInfo.baseBranch,
claudeCommentId: commentId.toString(),
allowedTools: [],
context,
});
// Don't output mcp_config separately anymore - include in claude_args
// Build claude_args for tag mode with required tools // Build claude_args for tag mode with required tools
// Tag mode REQUIRES these tools to function properly // Tag mode REQUIRES these tools to function properly
@@ -134,6 +132,7 @@ export const tagMode: Mode = {
"mcp__github_ci__get_ci_status", "mcp__github_ci__get_ci_status",
"mcp__github_ci__get_workflow_run_details", "mcp__github_ci__get_workflow_run_details",
"mcp__github_ci__download_job_log", "mcp__github_ci__download_job_log",
...userAllowedMCPTools,
]; ];
// Add git commands when not using commit signing // Add git commands when not using commit signing
@@ -155,7 +154,18 @@ export const tagMode: Mode = {
); );
} }
const userClaudeArgs = process.env.CLAUDE_ARGS || ""; // Get our GitHub MCP servers configuration
const ourMcpConfig = await prepareMcpConfig({
githubToken,
owner: context.repository.owner,
repo: context.repository.repo,
branch: branchInfo.claudeBranch || branchInfo.currentBranch,
baseBranch: branchInfo.baseBranch,
claudeCommentId: commentId.toString(),
allowedTools: Array.from(new Set(tagModeTools)),
mode: "tag",
context,
});
// Build complete claude_args with multiple --mcp-config flags // Build complete claude_args with multiple --mcp-config flags
let claudeArgs = ""; let claudeArgs = "";

View File

@@ -2,6 +2,7 @@ import { describe, test, expect, beforeEach, afterEach, spyOn } from "bun:test";
import { prepareMcpConfig } from "../src/mcp/install-mcp-server"; import { prepareMcpConfig } from "../src/mcp/install-mcp-server";
import * as core from "@actions/core"; import * as core from "@actions/core";
import type { ParsedGitHubContext } from "../src/github/context"; import type { ParsedGitHubContext } from "../src/github/context";
import { CLAUDE_APP_BOT_ID, CLAUDE_BOT_LOGIN } from "../src/github/constants";
describe("prepareMcpConfig", () => { describe("prepareMcpConfig", () => {
let consoleInfoSpy: any; let consoleInfoSpy: any;
@@ -31,7 +32,10 @@ describe("prepareMcpConfig", () => {
branchPrefix: "", branchPrefix: "",
useStickyComment: false, useStickyComment: false,
useCommitSigning: false, useCommitSigning: false,
botId: String(CLAUDE_APP_BOT_ID),
botName: CLAUDE_BOT_LOGIN,
allowedBots: "", allowedBots: "",
allowedNonWriteUsers: "",
trackProgress: false, trackProgress: false,
}, },
}; };
@@ -81,6 +85,7 @@ describe("prepareMcpConfig", () => {
baseBranch: "main", baseBranch: "main",
allowedTools: [], allowedTools: [],
context: mockContext, context: mockContext,
mode: "tag",
}); });
const parsed = JSON.parse(result); const parsed = JSON.parse(result);
@@ -101,6 +106,7 @@ describe("prepareMcpConfig", () => {
branch: "test-branch", branch: "test-branch",
baseBranch: "main", baseBranch: "main",
allowedTools: [], allowedTools: [],
mode: "tag",
context: mockContextWithSigning, context: mockContextWithSigning,
}); });
@@ -124,6 +130,7 @@ describe("prepareMcpConfig", () => {
branch: "test-branch", branch: "test-branch",
baseBranch: "main", baseBranch: "main",
allowedTools: ["mcp__github__create_issue", "mcp__github__create_pr"], allowedTools: ["mcp__github__create_issue", "mcp__github__create_pr"],
mode: "tag",
context: mockContext, context: mockContext,
}); });
@@ -144,6 +151,7 @@ describe("prepareMcpConfig", () => {
branch: "test-branch", branch: "test-branch",
baseBranch: "main", baseBranch: "main",
allowedTools: ["mcp__github_inline_comment__create_inline_comment"], allowedTools: ["mcp__github_inline_comment__create_inline_comment"],
mode: "tag",
context: mockPRContext, context: mockPRContext,
}); });
@@ -164,6 +172,7 @@ describe("prepareMcpConfig", () => {
branch: "test-branch", branch: "test-branch",
baseBranch: "main", baseBranch: "main",
allowedTools: [], allowedTools: [],
mode: "tag",
context: mockContext, context: mockContext,
}); });
@@ -184,6 +193,7 @@ describe("prepareMcpConfig", () => {
branch: "test-branch", branch: "test-branch",
baseBranch: "main", baseBranch: "main",
allowedTools: [], allowedTools: [],
mode: "tag",
context: mockContextWithSigning, context: mockContextWithSigning,
}); });
@@ -203,6 +213,7 @@ describe("prepareMcpConfig", () => {
branch: "test-branch", branch: "test-branch",
baseBranch: "main", baseBranch: "main",
allowedTools: [], allowedTools: [],
mode: "tag",
context: mockContextWithSigning, context: mockContextWithSigning,
}); });
@@ -220,6 +231,7 @@ describe("prepareMcpConfig", () => {
branch: "test-branch", branch: "test-branch",
baseBranch: "main", baseBranch: "main",
allowedTools: [], allowedTools: [],
mode: "tag",
context: mockPRContext, context: mockPRContext,
}); });
@@ -239,6 +251,7 @@ describe("prepareMcpConfig", () => {
branch: "test-branch", branch: "test-branch",
baseBranch: "main", baseBranch: "main",
allowedTools: [], allowedTools: [],
mode: "tag",
context: mockContext, context: mockContext,
}); });
@@ -256,6 +269,7 @@ describe("prepareMcpConfig", () => {
branch: "test-branch", branch: "test-branch",
baseBranch: "main", baseBranch: "main",
allowedTools: [], allowedTools: [],
mode: "tag",
context: mockPRContext, context: mockPRContext,
}); });

View File

@@ -1,6 +1,7 @@
import type { import type {
ParsedGitHubContext, ParsedGitHubContext,
AutomationContext, AutomationContext,
RepositoryDispatchEvent,
} from "../src/github/context"; } from "../src/github/context";
import type { import type {
IssuesEvent, IssuesEvent,
@@ -9,6 +10,7 @@ import type {
PullRequestReviewEvent, PullRequestReviewEvent,
PullRequestReviewCommentEvent, PullRequestReviewCommentEvent,
} from "@octokit/webhooks-types"; } from "@octokit/webhooks-types";
import { CLAUDE_APP_BOT_ID, CLAUDE_BOT_LOGIN } from "../src/github/constants";
const defaultInputs = { const defaultInputs = {
prompt: "", prompt: "",
@@ -18,7 +20,10 @@ const defaultInputs = {
branchPrefix: "claude/", branchPrefix: "claude/",
useStickyComment: false, useStickyComment: false,
useCommitSigning: false, useCommitSigning: false,
botId: String(CLAUDE_APP_BOT_ID),
botName: CLAUDE_BOT_LOGIN,
allowedBots: "", allowedBots: "",
allowedNonWriteUsers: "",
trackProgress: false, trackProgress: false,
}; };
@@ -78,6 +83,33 @@ export const createMockAutomationContext = (
return { ...baseContext, ...overrides, inputs: mergedInputs }; return { ...baseContext, ...overrides, inputs: mergedInputs };
}; };
export const mockRepositoryDispatchContext: AutomationContext = {
runId: "1234567890",
eventName: "repository_dispatch",
eventAction: undefined,
repository: defaultRepository,
actor: "automation-user",
payload: {
action: "trigger-analysis",
client_payload: {
source: "issue-detective",
issue_number: 42,
repository_name: "test-owner/test-repo",
analysis_type: "bug-report",
},
repository: {
name: "test-repo",
owner: {
login: "test-owner",
},
},
sender: {
login: "automation-user",
},
} as RepositoryDispatchEvent,
inputs: defaultInputs,
};
export const mockIssueOpenedContext: ParsedGitHubContext = { export const mockIssueOpenedContext: ParsedGitHubContext = {
runId: "1234567890", runId: "1234567890",
eventName: "issues", eventName: "issues",

View File

@@ -1,13 +1,23 @@
import { describe, test, expect, beforeEach, afterEach, spyOn } from "bun:test"; import {
describe,
test,
expect,
beforeEach,
afterEach,
spyOn,
mock,
} from "bun:test";
import { agentMode } from "../../src/modes/agent"; import { agentMode } from "../../src/modes/agent";
import type { GitHubContext } from "../../src/github/context"; import type { GitHubContext } from "../../src/github/context";
import { createMockContext, createMockAutomationContext } from "../mockContext"; import { createMockContext, createMockAutomationContext } from "../mockContext";
import * as core from "@actions/core"; import * as core from "@actions/core";
import * as gitConfig from "../../src/github/operations/git-config";
describe("Agent Mode", () => { describe("Agent Mode", () => {
let mockContext: GitHubContext; let mockContext: GitHubContext;
let exportVariableSpy: any; let exportVariableSpy: any;
let setOutputSpy: any; let setOutputSpy: any;
let configureGitAuthSpy: any;
beforeEach(() => { beforeEach(() => {
mockContext = createMockAutomationContext({ mockContext = createMockAutomationContext({
@@ -17,13 +27,22 @@ describe("Agent Mode", () => {
() => {}, () => {},
); );
setOutputSpy = spyOn(core, "setOutput").mockImplementation(() => {}); setOutputSpy = spyOn(core, "setOutput").mockImplementation(() => {});
// Mock configureGitAuth to prevent actual git commands from running
configureGitAuthSpy = spyOn(
gitConfig,
"configureGitAuth",
).mockImplementation(async () => {
// Do nothing - prevent actual git config modifications
});
}); });
afterEach(() => { afterEach(() => {
exportVariableSpy?.mockClear(); exportVariableSpy?.mockClear();
setOutputSpy?.mockClear(); setOutputSpy?.mockClear();
configureGitAuthSpy?.mockClear();
exportVariableSpy?.mockRestore(); exportVariableSpy?.mockRestore();
setOutputSpy?.mockRestore(); setOutputSpy?.mockRestore();
configureGitAuthSpy?.mockRestore();
}); });
test("agent mode has correct properties", () => { test("agent mode has correct properties", () => {
@@ -57,6 +76,11 @@ describe("Agent Mode", () => {
}); });
expect(agentMode.shouldTrigger(scheduleContext)).toBe(false); expect(agentMode.shouldTrigger(scheduleContext)).toBe(false);
const repositoryDispatchContext = createMockAutomationContext({
eventName: "repository_dispatch",
});
expect(agentMode.shouldTrigger(repositoryDispatchContext)).toBe(false);
// Should NOT trigger for entity events without prompt // Should NOT trigger for entity events without prompt
const entityEvents = [ const entityEvents = [
"issue_comment", "issue_comment",
@@ -73,6 +97,7 @@ describe("Agent Mode", () => {
// Should trigger for ANY event when prompt is provided // Should trigger for ANY event when prompt is provided
const allEvents = [ const allEvents = [
"workflow_dispatch", "workflow_dispatch",
"repository_dispatch",
"schedule", "schedule",
"issue_comment", "issue_comment",
"pull_request", "pull_request",
@@ -82,7 +107,9 @@ describe("Agent Mode", () => {
allEvents.forEach((eventName) => { allEvents.forEach((eventName) => {
const contextWithPrompt = const contextWithPrompt =
eventName === "workflow_dispatch" || eventName === "schedule" eventName === "workflow_dispatch" ||
eventName === "repository_dispatch" ||
eventName === "schedule"
? createMockAutomationContext({ ? createMockAutomationContext({
eventName, eventName,
inputs: { prompt: "Do something" }, inputs: { prompt: "Do something" },
@@ -113,18 +140,33 @@ describe("Agent Mode", () => {
// Set CLAUDE_ARGS environment variable // Set CLAUDE_ARGS environment variable
process.env.CLAUDE_ARGS = "--model claude-sonnet-4 --max-turns 10"; process.env.CLAUDE_ARGS = "--model claude-sonnet-4 --max-turns 10";
const mockOctokit = {} as any; const mockOctokit = {
rest: {
users: {
getAuthenticated: mock(() =>
Promise.resolve({
data: { login: "test-user", id: 12345 },
}),
),
getByUsername: mock(() =>
Promise.resolve({
data: { login: "test-user", id: 12345 },
}),
),
},
},
} as any;
const result = await agentMode.prepare({ const result = await agentMode.prepare({
context: contextWithCustomArgs, context: contextWithCustomArgs,
octokit: mockOctokit, octokit: mockOctokit,
githubToken: "test-token", githubToken: "test-token",
}); });
// Verify claude_args includes MCP config and user args // Verify claude_args includes user args (no MCP config in agent mode without allowed tools)
const callArgs = setOutputSpy.mock.calls[0]; const callArgs = setOutputSpy.mock.calls[0];
expect(callArgs[0]).toBe("claude_args"); expect(callArgs[0]).toBe("claude_args");
expect(callArgs[1]).toContain("--mcp-config"); expect(callArgs[1]).toBe("--model claude-sonnet-4 --max-turns 10");
expect(callArgs[1]).toContain("--model claude-sonnet-4 --max-turns 10"); expect(callArgs[1]).not.toContain("--mcp-config");
// Verify return structure - should use "main" as fallback when no env vars set // Verify return structure - should use "main" as fallback when no env vars set
expect(result).toEqual({ expect(result).toEqual({
@@ -152,7 +194,22 @@ describe("Agent Mode", () => {
// In v1-dev, we only have the unified prompt field // In v1-dev, we only have the unified prompt field
contextWithPrompts.inputs.prompt = "Custom prompt content"; contextWithPrompts.inputs.prompt = "Custom prompt content";
const mockOctokit = {} as any; const mockOctokit = {
rest: {
users: {
getAuthenticated: mock(() =>
Promise.resolve({
data: { login: "test-user", id: 12345 },
}),
),
getByUsername: mock(() =>
Promise.resolve({
data: { login: "test-user", id: 12345 },
}),
),
},
},
} as any;
await agentMode.prepare({ await agentMode.prepare({
context: contextWithPrompts, context: contextWithPrompts,
octokit: mockOctokit, octokit: mockOctokit,

View File

@@ -20,7 +20,10 @@ describe("detectMode with enhanced routing", () => {
branchPrefix: "claude/", branchPrefix: "claude/",
useStickyComment: false, useStickyComment: false,
useCommitSigning: false, useCommitSigning: false,
botId: "123456",
botName: "claude-bot",
allowedBots: "", allowedBots: "",
allowedNonWriteUsers: "",
trackProgress: false, trackProgress: false,
}, },
}; };
@@ -113,6 +116,33 @@ describe("detectMode with enhanced routing", () => {
expect(detectMode(context)).toBe("agent"); expect(detectMode(context)).toBe("agent");
}); });
it("should use agent mode for issues with explicit prompt", () => {
const context: GitHubContext = {
...baseContext,
eventName: "issues",
eventAction: "opened",
payload: { issue: { number: 1, body: "Test issue" } } as any,
entityNumber: 1,
isPR: false,
inputs: { ...baseContext.inputs, prompt: "Analyze this issue" },
};
expect(detectMode(context)).toBe("agent");
});
it("should use tag mode for issues with @claude mention and no prompt", () => {
const context: GitHubContext = {
...baseContext,
eventName: "issues",
eventAction: "opened",
payload: { issue: { number: 1, body: "@claude help" } } as any,
entityNumber: 1,
isPR: false,
};
expect(detectMode(context)).toBe("tag");
});
}); });
describe("Comment Events (unchanged behavior)", () => { describe("Comment Events (unchanged behavior)", () => {
@@ -173,7 +203,7 @@ describe("detectMode with enhanced routing", () => {
}; };
expect(() => detectMode(context)).toThrow( expect(() => detectMode(context)).toThrow(
/track_progress is only supported for pull_request and issue events/, /track_progress is only supported /,
); );
}); });

View File

@@ -2,7 +2,11 @@ import { describe, test, expect } from "bun:test";
import { getMode, isValidMode } from "../../src/modes/registry"; import { getMode, isValidMode } from "../../src/modes/registry";
import { agentMode } from "../../src/modes/agent"; import { agentMode } from "../../src/modes/agent";
import { tagMode } from "../../src/modes/tag"; import { tagMode } from "../../src/modes/tag";
import { createMockContext, createMockAutomationContext } from "../mockContext"; import {
createMockContext,
createMockAutomationContext,
mockRepositoryDispatchContext,
} from "../mockContext";
describe("Mode Registry", () => { describe("Mode Registry", () => {
const mockContext = createMockContext({ const mockContext = createMockContext({
@@ -50,6 +54,34 @@ describe("Mode Registry", () => {
expect(mode.name).toBe("agent"); expect(mode.name).toBe("agent");
}); });
test("getMode auto-detects agent for repository_dispatch event", () => {
const mode = getMode(mockRepositoryDispatchContext);
expect(mode).toBe(agentMode);
expect(mode.name).toBe("agent");
});
test("getMode auto-detects agent for repository_dispatch with client_payload", () => {
const contextWithPayload = createMockAutomationContext({
eventName: "repository_dispatch",
payload: {
action: "trigger-analysis",
client_payload: {
source: "external-system",
metadata: { priority: "high" },
},
repository: {
name: "test-repo",
owner: { login: "test-owner" },
},
sender: { login: "automation-user" },
},
});
const mode = getMode(contextWithPayload);
expect(mode).toBe(agentMode);
expect(mode.name).toBe("agent");
});
// Removed test - legacy mode names no longer supported in v1.0 // Removed test - legacy mode names no longer supported in v1.0
test("getMode auto-detects agent mode for PR opened", () => { test("getMode auto-detects agent mode for PR opened", () => {

View File

@@ -2,6 +2,7 @@ import { describe, expect, test, spyOn, beforeEach, afterEach } from "bun:test";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { checkWritePermissions } from "../src/github/validation/permissions"; import { checkWritePermissions } from "../src/github/validation/permissions";
import type { ParsedGitHubContext } from "../src/github/context"; import type { ParsedGitHubContext } from "../src/github/context";
import { CLAUDE_APP_BOT_ID, CLAUDE_BOT_LOGIN } from "../src/github/constants";
describe("checkWritePermissions", () => { describe("checkWritePermissions", () => {
let coreInfoSpy: any; let coreInfoSpy: any;
@@ -67,7 +68,10 @@ describe("checkWritePermissions", () => {
branchPrefix: "claude/", branchPrefix: "claude/",
useStickyComment: false, useStickyComment: false,
useCommitSigning: false, useCommitSigning: false,
botId: String(CLAUDE_APP_BOT_ID),
botName: CLAUDE_BOT_LOGIN,
allowedBots: "", allowedBots: "",
allowedNonWriteUsers: "",
trackProgress: false, trackProgress: false,
}, },
}); });
@@ -172,4 +176,126 @@ describe("checkWritePermissions", () => {
username: "test-user", username: "test-user",
}); });
}); });
describe("allowed_non_write_users bypass", () => {
test("should bypass permission check for specific user when github_token provided", async () => {
const mockOctokit = createMockOctokit("read");
const context = createContext();
const result = await checkWritePermissions(
mockOctokit,
context,
"test-user,other-user",
true,
);
expect(result).toBe(true);
expect(coreWarningSpy).toHaveBeenCalledWith(
"⚠️ SECURITY WARNING: Bypassing write permission check for test-user due to allowed_non_write_users configuration. This should only be used for workflows with very limited permissions.",
);
});
test("should bypass permission check for all users with wildcard", async () => {
const mockOctokit = createMockOctokit("read");
const context = createContext();
const result = await checkWritePermissions(
mockOctokit,
context,
"*",
true,
);
expect(result).toBe(true);
expect(coreWarningSpy).toHaveBeenCalledWith(
"⚠️ SECURITY WARNING: Bypassing write permission check for test-user due to allowed_non_write_users='*'. This should only be used for workflows with very limited permissions.",
);
});
test("should NOT bypass permission check when user not in allowed list", async () => {
const mockOctokit = createMockOctokit("read");
const context = createContext();
const result = await checkWritePermissions(
mockOctokit,
context,
"other-user,another-user",
true,
);
expect(result).toBe(false);
expect(coreWarningSpy).toHaveBeenCalledWith(
"Actor has insufficient permissions: read",
);
});
test("should NOT bypass permission check when github_token not provided", async () => {
const mockOctokit = createMockOctokit("read");
const context = createContext();
const result = await checkWritePermissions(
mockOctokit,
context,
"test-user",
false,
);
expect(result).toBe(false);
expect(coreWarningSpy).toHaveBeenCalledWith(
"Actor has insufficient permissions: read",
);
});
test("should NOT bypass permission check when allowed_non_write_users is empty", async () => {
const mockOctokit = createMockOctokit("read");
const context = createContext();
const result = await checkWritePermissions(
mockOctokit,
context,
"",
true,
);
expect(result).toBe(false);
expect(coreWarningSpy).toHaveBeenCalledWith(
"Actor has insufficient permissions: read",
);
});
test("should handle whitespace in allowed_non_write_users list", async () => {
const mockOctokit = createMockOctokit("read");
const context = createContext();
const result = await checkWritePermissions(
mockOctokit,
context,
" test-user , other-user ",
true,
);
expect(result).toBe(true);
expect(coreWarningSpy).toHaveBeenCalledWith(
"⚠️ SECURITY WARNING: Bypassing write permission check for test-user due to allowed_non_write_users configuration. This should only be used for workflows with very limited permissions.",
);
});
test("should bypass for bot users even when allowed_non_write_users is set", async () => {
const mockOctokit = createMockOctokit("none");
const context = createContext();
context.actor = "test-bot[bot]";
const result = await checkWritePermissions(
mockOctokit,
context,
"some-user",
true,
);
expect(result).toBe(true);
expect(coreInfoSpy).toHaveBeenCalledWith(
"Actor is a GitHub App: test-bot[bot]",
);
});
});
}); });