mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-24 07:24:12 +08:00
Compare commits
48 Commits
v1.0.3
...
ashwin/bra
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b12957001c | ||
|
|
60112e2967 | ||
|
|
ddb3ef092b | ||
|
|
35c0ca2406 | ||
|
|
1a49e00b3e | ||
|
|
e758a32de7 | ||
|
|
6246ecdcb5 | ||
|
|
32e7aeee0e | ||
|
|
40c2c1e7b4 | ||
|
|
9c4bc5dc35 | ||
|
|
b5de2b9913 | ||
|
|
4991c0c947 | ||
|
|
726d5808b5 | ||
|
|
063d17ebb2 | ||
|
|
2e92922dd6 | ||
|
|
a5528eec74 | ||
|
|
1d4650c102 | ||
|
|
86d6f44e34 | ||
|
|
c1adac956c | ||
|
|
f197e7bfd5 | ||
|
|
89f9131f6c | ||
|
|
b78e1c0244 | ||
|
|
abf075daf2 | ||
|
|
a3ff61d47a | ||
|
|
1b7eb924f1 | ||
|
|
0f7dfed927 | ||
|
|
11a01b7183 | ||
|
|
69dec299f8 | ||
|
|
1a8e7d330a | ||
|
|
9975f36410 | ||
|
|
c1ffc8a0e8 | ||
|
|
13e47489f4 | ||
|
|
765fadc6a6 | ||
|
|
fd2c17f101 | ||
|
|
a4a723b927 | ||
|
|
d22fa6061b | ||
|
|
63f1c772bd | ||
|
|
fb823f6dd6 | ||
|
|
9e9123239f | ||
|
|
791fcb9fd1 | ||
|
|
9365bbe4af | ||
|
|
2e6fc44bd4 | ||
|
|
a6ca65328b | ||
|
|
ce697c0d4c | ||
|
|
b60e3f0e60 | ||
|
|
3ed14485f8 | ||
|
|
45408b4058 | ||
|
|
1f8cfe7658 |
61
.claude/agents/code-quality-reviewer.md
Normal file
61
.claude/agents/code-quality-reviewer.md
Normal 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.
|
||||||
56
.claude/agents/documentation-accuracy-reviewer.md
Normal file
56
.claude/agents/documentation-accuracy-reviewer.md
Normal 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.
|
||||||
53
.claude/agents/performance-reviewer.md
Normal file
53
.claude/agents/performance-reviewer.md
Normal 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.
|
||||||
59
.claude/agents/security-code-reviewer.md
Normal file
59
.claude/agents/security-code-reviewer.md
Normal 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.
|
||||||
52
.claude/agents/test-coverage-reviewer.md
Normal file
52
.claude/agents/test-coverage-reviewer.md
Normal 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.
|
||||||
60
.claude/commands/label-issue.md
Normal file
60
.claude/commands/label-issue.md
Normal 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
|
||||||
|
|
||||||
|
---
|
||||||
20
.claude/commands/review-pr.md
Normal file
20
.claude/commands/review-pr.md
Normal 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
15
.claude/settings.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"PostToolUse": [
|
||||||
|
{
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "bun run format"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"matcher": "Edit|Write|MultiEdit"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
28
.github/workflows/claude-review.yml
vendored
28
.github/workflows/claude-review.yml
vendored
@@ -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"
|
||||||
|
|||||||
89
.github/workflows/issue-triage.yml
vendored
89
.github/workflows/issue-triage.yml
vendored
@@ -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 }}
|
|
||||||
|
|||||||
17
README.md
17
README.md
@@ -31,8 +31,25 @@ This command will guide you through setting up the GitHub app and required secre
|
|||||||
- You must be a repository admin to install the GitHub app and add secrets
|
- You must be a repository admin to install the GitHub app and add secrets
|
||||||
- This quickstart method is only available for direct Anthropic API users. For AWS Bedrock or Google Vertex AI setup, see [docs/cloud-providers.md](./docs/cloud-providers.md).
|
- This quickstart method is only available for direct Anthropic API users. For AWS Bedrock or Google Vertex AI setup, see [docs/cloud-providers.md](./docs/cloud-providers.md).
|
||||||
|
|
||||||
|
## 📚 Solutions & Use Cases
|
||||||
|
|
||||||
|
Looking for specific automation patterns? Check our **[Solutions Guide](./docs/solutions.md)** for complete working examples including:
|
||||||
|
|
||||||
|
- **🔍 Automatic PR Code Review** - Full review automation
|
||||||
|
- **📂 Path-Specific Reviews** - Trigger on critical file changes
|
||||||
|
- **👥 External Contributor Reviews** - Special handling for new contributors
|
||||||
|
- **📝 Custom Review Checklists** - Enforce team standards
|
||||||
|
- **🔄 Scheduled Maintenance** - Automated repository health checks
|
||||||
|
- **🏷️ Issue Triage & Labeling** - Automatic categorization
|
||||||
|
- **📖 Documentation Sync** - Keep docs updated with code changes
|
||||||
|
- **🔒 Security-Focused Reviews** - OWASP-aligned security analysis
|
||||||
|
- **📊 DIY Progress Tracking** - Create tracking comments in automation mode
|
||||||
|
|
||||||
|
Each solution includes complete working examples, configuration details, and expected outcomes.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
- **[Solutions Guide](./docs/solutions.md)** - **🎯 Ready-to-use automation patterns**
|
||||||
- **[Migration Guide](./docs/migration-guide.md)** - **⭐ Upgrading from v0.x to v1.0**
|
- **[Migration Guide](./docs/migration-guide.md)** - **⭐ Upgrading from v0.x to v1.0**
|
||||||
- [Setup Guide](./docs/setup.md) - Manual setup, custom GitHub apps, and security best practices
|
- [Setup Guide](./docs/setup.md) - Manual setup, custom GitHub apps, and security best practices
|
||||||
- [Usage Guide](./docs/usage.md) - Basic usage, workflow configuration, and input parameters
|
- [Usage Guide](./docs/usage.md) - Basic usage, workflow configuration, and input parameters
|
||||||
|
|||||||
23
action.yml
23
action.yml
@@ -23,10 +23,18 @@ inputs:
|
|||||||
description: "The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format)"
|
description: "The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format)"
|
||||||
required: false
|
required: false
|
||||||
default: "claude/"
|
default: "claude/"
|
||||||
|
branch_name_template:
|
||||||
|
description: "Template for branch naming. Available variables: {{prefix}}, {{entityType}}, {{entityNumber}}, {{timestamp}}, {{sha}}, {{label}}, {{description}}. {{label}} will be first label from the issue/PR, or {{entityType}} as a fallback. {{description}} will be the first 3 words of the issue/PR title in kebab-case. Default: '{{prefix}}{{entityType}}-{{entityNumber}}-{{timestamp}}'"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
allowed_bots:
|
allowed_bots:
|
||||||
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 +81,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
|
||||||
@@ -138,12 +154,16 @@ runs:
|
|||||||
LABEL_TRIGGER: ${{ inputs.label_trigger }}
|
LABEL_TRIGGER: ${{ inputs.label_trigger }}
|
||||||
BASE_BRANCH: ${{ inputs.base_branch }}
|
BASE_BRANCH: ${{ inputs.base_branch }}
|
||||||
BRANCH_PREFIX: ${{ inputs.branch_prefix }}
|
BRANCH_PREFIX: ${{ inputs.branch_prefix }}
|
||||||
|
BRANCH_NAME_TEMPLATE: ${{ inputs.branch_name_template }}
|
||||||
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 +182,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.96
|
curl -fsSL https://claude.ai/install.sh | bash -s 1.0.113
|
||||||
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 +228,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' || '' }}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.96
|
curl -fsSL https://claude.ai/install.sh | bash -s 1.0.113
|
||||||
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' || '' }}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -2,6 +2,15 @@
|
|||||||
|
|
||||||
These examples show how to configure Claude to act automatically based on GitHub events. When you provide a `prompt` input, the action automatically runs in agent mode without requiring manual @mentions. Without a `prompt`, it runs in interactive mode, responding to @claude mentions.
|
These examples show how to configure Claude to act automatically based on GitHub events. When you provide a `prompt` input, the action automatically runs in agent mode without requiring manual @mentions. Without a `prompt`, it runs in interactive mode, responding to @claude mentions.
|
||||||
|
|
||||||
|
## Mode Detection & Tracking Comments
|
||||||
|
|
||||||
|
The action automatically detects which mode to use based on your configuration:
|
||||||
|
|
||||||
|
- **Interactive Mode** (no `prompt` input): Responds to @claude mentions, creates tracking comments with progress indicators
|
||||||
|
- **Automation Mode** (with `prompt` input): Executes immediately, **does not create tracking comments**
|
||||||
|
|
||||||
|
> **Note**: In v1, automation mode intentionally does not create tracking comments by default to reduce noise in automated workflows. If you need progress tracking, use the `track_progress: true` input parameter.
|
||||||
|
|
||||||
## Supported GitHub Events
|
## Supported GitHub Events
|
||||||
|
|
||||||
This action supports the following GitHub events ([learn more GitHub event triggers](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows)):
|
This action supports the following GitHub events ([learn more GitHub event triggers](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows)):
|
||||||
@@ -12,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
|
||||||
|
|||||||
65
docs/faq.md
65
docs/faq.md
@@ -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
|
||||||
|
|||||||
@@ -14,18 +14,19 @@ 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 |
|
||||||
| `custom_instructions` | `claude_args: --system-prompt` | Move to CLI arguments |
|
| `custom_instructions` | `claude_args: --system-prompt` | Move to CLI arguments |
|
||||||
| `max_turns` | `claude_args: --max-turns` | Use CLI format |
|
| `max_turns` | `claude_args: --max-turns` | Use CLI format |
|
||||||
| `model` | `claude_args: --model` | Specify via CLI |
|
| `model` | `claude_args: --model` | Specify via CLI |
|
||||||
| `allowed_tools` | `claude_args: --allowedTools` | Use CLI format |
|
| `allowed_tools` | `claude_args: --allowedTools` | Use CLI format |
|
||||||
| `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
|
||||||
|
|
||||||
@@ -74,13 +75,75 @@ The following inputs have been deprecated and replaced:
|
|||||||
```yaml
|
```yaml
|
||||||
- uses: anthropics/claude-code-action@v1
|
- uses: anthropics/claude-code-action@v1
|
||||||
with:
|
with:
|
||||||
prompt: "Review this PR for security issues"
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
Review this PR for security issues
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
claude_args: |
|
claude_args: |
|
||||||
--model claude-4-0-sonnet-20250805
|
--model claude-4-0-sonnet-20250805
|
||||||
--allowedTools Edit,Read,Write
|
--allowedTools Edit,Read,Write
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **⚠️ Important**: For PR reviews, always include the repository and PR context in your prompt. This ensures Claude knows which PR to review.
|
||||||
|
|
||||||
|
### Automation with Progress Tracking (New in v1.0)
|
||||||
|
|
||||||
|
**Missing the tracking comments from v0.x agent mode?** The new `track_progress` input brings them back!
|
||||||
|
|
||||||
|
In v1.0, automation mode (with `prompt` input) doesn't create tracking comments by default to reduce noise. However, if you need progress visibility, you can use the `track_progress` feature:
|
||||||
|
|
||||||
|
**Before (v0.x with tracking):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: anthropics/claude-code-action@beta
|
||||||
|
with:
|
||||||
|
mode: "agent"
|
||||||
|
direct_prompt: "Review this PR for security issues"
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
```
|
||||||
|
|
||||||
|
**After (v1.0 with tracking):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
track_progress: true # Forces tag mode with tracking comments
|
||||||
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
Review this PR for security issues
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Benefits of `track_progress`
|
||||||
|
|
||||||
|
1. **Preserves GitHub Context**: Automatically includes all PR/issue details, comments, and attachments
|
||||||
|
2. **Brings Back Tracking Comments**: Creates progress indicators just like v0.x agent mode
|
||||||
|
3. **Works with Custom Prompts**: Your `prompt` is injected as custom instructions while maintaining context
|
||||||
|
|
||||||
|
#### Supported Events for `track_progress`
|
||||||
|
|
||||||
|
The `track_progress` input only works with these GitHub events:
|
||||||
|
|
||||||
|
**Pull Request Events:**
|
||||||
|
|
||||||
|
- `opened` - New PR created
|
||||||
|
- `synchronize` - PR updated with new commits
|
||||||
|
- `ready_for_review` - Draft PR marked as ready
|
||||||
|
- `reopened` - Previously closed PR reopened
|
||||||
|
|
||||||
|
**Issue Events:**
|
||||||
|
|
||||||
|
- `opened` - New issue created
|
||||||
|
- `edited` - Issue title or body modified
|
||||||
|
- `labeled` - Label added to issue
|
||||||
|
- `assigned` - Issue assigned to user
|
||||||
|
|
||||||
|
> **Note**: Using `track_progress: true` with unsupported events will cause an error.
|
||||||
|
|
||||||
### Custom Template with Variables
|
### Custom Template with Variables
|
||||||
|
|
||||||
**Before (v0.x):**
|
**Before (v0.x):**
|
||||||
@@ -100,10 +163,16 @@ The following inputs have been deprecated and replaced:
|
|||||||
- uses: anthropics/claude-code-action@v1
|
- uses: anthropics/claude-code-action@v1
|
||||||
with:
|
with:
|
||||||
prompt: |
|
prompt: |
|
||||||
Analyze PR #${{ github.event.pull_request.number }} in ${{ github.repository }}
|
REPO: ${{ github.repository }}
|
||||||
Focus on security vulnerabilities in the changed files
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
Analyze this pull request focusing on security vulnerabilities in the changed files.
|
||||||
|
|
||||||
|
Note: The PR branch is already checked out in the current working directory.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **💡 Tip**: While you can access GitHub context variables in your prompt, it's recommended to use the standard `REPO:` and `PR NUMBER:` format for consistency.
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
**Before (v0.x):**
|
**Before (v0.x):**
|
||||||
@@ -130,6 +199,30 @@ The following inputs have been deprecated and replaced:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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:
|
||||||
@@ -244,6 +337,8 @@ 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
|
||||||
- [ ] Test workflow in a non-production environment
|
- [ ] Test workflow in a non-production environment
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
591
docs/solutions.md
Normal file
591
docs/solutions.md
Normal file
@@ -0,0 +1,591 @@
|
|||||||
|
# Solutions & Use Cases
|
||||||
|
|
||||||
|
This guide provides complete, ready-to-use solutions for common automation scenarios with Claude Code Action. Each solution includes working examples, configuration details, and expected outcomes.
|
||||||
|
|
||||||
|
## 📋 Table of Contents
|
||||||
|
|
||||||
|
- [Automatic PR Code Review](#automatic-pr-code-review)
|
||||||
|
- [Review Only Specific File Paths](#review-only-specific-file-paths)
|
||||||
|
- [Review PRs from External Contributors](#review-prs-from-external-contributors)
|
||||||
|
- [Custom PR Review Checklist](#custom-pr-review-checklist)
|
||||||
|
- [Scheduled Repository Maintenance](#scheduled-repository-maintenance)
|
||||||
|
- [Issue Auto-Triage and Labeling](#issue-auto-triage-and-labeling)
|
||||||
|
- [Documentation Sync on API Changes](#documentation-sync-on-api-changes)
|
||||||
|
- [Security-Focused PR Reviews](#security-focused-pr-reviews)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Automatic PR Code Review
|
||||||
|
|
||||||
|
**When to use:** Automatically review every PR opened or updated in your repository.
|
||||||
|
|
||||||
|
### Basic Example (No Tracking)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Claude Auto Review
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
Please review this pull request with a focus on:
|
||||||
|
- Code quality and best practices
|
||||||
|
- Potential bugs or issues
|
||||||
|
- Security implications
|
||||||
|
- Performance considerations
|
||||||
|
|
||||||
|
Note: The PR branch is already checked out in the current working directory.
|
||||||
|
|
||||||
|
Use `gh pr comment` for top-level feedback.
|
||||||
|
Use `mcp__github_inline_comment__create_inline_comment` to highlight specific code issues.
|
||||||
|
Only post GitHub comments - don't submit review text as messages.
|
||||||
|
|
||||||
|
claude_args: |
|
||||||
|
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Configuration:**
|
||||||
|
|
||||||
|
- Triggers on `opened` and `synchronize` (new commits)
|
||||||
|
- Always include `REPO` and `PR NUMBER` for context
|
||||||
|
- Specify tools for commenting and reviewing
|
||||||
|
- PR branch is pre-checked out
|
||||||
|
|
||||||
|
**Expected Output:** Claude posts review comments directly to the PR with inline annotations where appropriate.
|
||||||
|
|
||||||
|
### Enhanced Example (With Progress Tracking)
|
||||||
|
|
||||||
|
Want visual progress tracking for PR reviews? Use `track_progress: true` to get tracking comments like in v0.x:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Claude Auto Review with Tracking
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, ready_for_review, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
track_progress: true # ✨ Enables tracking comments
|
||||||
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
Please review this pull request with a focus on:
|
||||||
|
- Code quality and best practices
|
||||||
|
- Potential bugs or issues
|
||||||
|
- Security implications
|
||||||
|
- Performance considerations
|
||||||
|
|
||||||
|
Provide detailed feedback using inline comments for specific issues.
|
||||||
|
|
||||||
|
claude_args: |
|
||||||
|
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Benefits of Progress Tracking:**
|
||||||
|
|
||||||
|
- **Visual Progress Indicators**: Shows "In progress" status with checkboxes
|
||||||
|
- **Preserves Full Context**: Automatically includes all PR details, comments, and attachments
|
||||||
|
- **Migration-Friendly**: Perfect for teams moving from v0.x who miss tracking comments
|
||||||
|
- **Works with Custom Prompts**: Your prompt becomes custom instructions while maintaining GitHub context
|
||||||
|
|
||||||
|
**Expected Output:**
|
||||||
|
|
||||||
|
1. Claude creates a tracking comment: "Claude Code is reviewing this pull request..."
|
||||||
|
2. Updates the comment with progress checkboxes as it works
|
||||||
|
3. Posts detailed review feedback with inline annotations
|
||||||
|
4. Updates tracking comment to "Completed" when done
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Review Only Specific File Paths
|
||||||
|
|
||||||
|
**When to use:** Review PRs only when specific critical files change.
|
||||||
|
|
||||||
|
**Complete Example:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Review Critical Files
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
paths:
|
||||||
|
- "src/auth/**"
|
||||||
|
- "src/api/**"
|
||||||
|
- "config/security.yml"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
security-review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
This PR modifies critical authentication or API files.
|
||||||
|
|
||||||
|
Please provide a security-focused review with emphasis on:
|
||||||
|
- Authentication and authorization flows
|
||||||
|
- Input validation and sanitization
|
||||||
|
- SQL injection or XSS vulnerabilities
|
||||||
|
- API security best practices
|
||||||
|
|
||||||
|
Note: The PR branch is already checked out.
|
||||||
|
|
||||||
|
Post detailed security findings as PR comments.
|
||||||
|
|
||||||
|
claude_args: |
|
||||||
|
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*)"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Configuration:**
|
||||||
|
|
||||||
|
- `paths:` filter triggers only for specific file changes
|
||||||
|
- Custom prompt emphasizes security for sensitive areas
|
||||||
|
- Useful for compliance or security reviews
|
||||||
|
|
||||||
|
**Expected Output:** Security-focused review when critical files are modified.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Review PRs from External Contributors
|
||||||
|
|
||||||
|
**When to use:** Apply stricter review criteria for external or new contributors.
|
||||||
|
|
||||||
|
**Complete Example:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: External Contributor Review
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
external-review:
|
||||||
|
if: github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
CONTRIBUTOR: ${{ github.event.pull_request.user.login }}
|
||||||
|
|
||||||
|
This is a first-time contribution from @${{ github.event.pull_request.user.login }}.
|
||||||
|
|
||||||
|
Please provide a comprehensive review focusing on:
|
||||||
|
- Compliance with project coding standards
|
||||||
|
- Proper test coverage (unit and integration)
|
||||||
|
- Documentation for new features
|
||||||
|
- Potential breaking changes
|
||||||
|
- License header requirements
|
||||||
|
|
||||||
|
Be welcoming but thorough in your review. Use inline comments for code-specific feedback.
|
||||||
|
|
||||||
|
claude_args: |
|
||||||
|
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr view:*)"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Configuration:**
|
||||||
|
|
||||||
|
- `if:` condition targets specific contributor types
|
||||||
|
- Includes contributor username in context
|
||||||
|
- Emphasis on onboarding and standards
|
||||||
|
|
||||||
|
**Expected Output:** Detailed review helping new contributors understand project standards.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Custom PR Review Checklist
|
||||||
|
|
||||||
|
**When to use:** Enforce specific review criteria for your team's workflow.
|
||||||
|
|
||||||
|
**Complete Example:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: PR Review Checklist
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
checklist-review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
Review this PR against our team checklist:
|
||||||
|
|
||||||
|
## Code Quality
|
||||||
|
- [ ] Code follows our style guide
|
||||||
|
- [ ] No commented-out code
|
||||||
|
- [ ] Meaningful variable names
|
||||||
|
- [ ] DRY principle followed
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
- [ ] Unit tests for new functions
|
||||||
|
- [ ] Integration tests for new endpoints
|
||||||
|
- [ ] Edge cases covered
|
||||||
|
- [ ] Test coverage > 80%
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
- [ ] README updated if needed
|
||||||
|
- [ ] API docs updated
|
||||||
|
- [ ] Inline comments for complex logic
|
||||||
|
- [ ] CHANGELOG.md updated
|
||||||
|
|
||||||
|
## Security
|
||||||
|
- [ ] No hardcoded credentials
|
||||||
|
- [ ] Input validation implemented
|
||||||
|
- [ ] Proper error handling
|
||||||
|
- [ ] No sensitive data in logs
|
||||||
|
|
||||||
|
For each item, check if it's satisfied and comment on any that need attention.
|
||||||
|
Post a summary comment with checklist results.
|
||||||
|
|
||||||
|
claude_args: |
|
||||||
|
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*)"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Configuration:**
|
||||||
|
|
||||||
|
- Structured checklist in prompt
|
||||||
|
- Systematic review approach
|
||||||
|
- Team-specific criteria
|
||||||
|
|
||||||
|
**Expected Output:** Systematic review with checklist results and specific feedback.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Scheduled Repository Maintenance
|
||||||
|
|
||||||
|
**When to use:** Regular automated maintenance tasks.
|
||||||
|
|
||||||
|
**Complete Example:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Weekly Maintenance
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * 0" # Every Sunday at midnight
|
||||||
|
workflow_dispatch: # Manual trigger option
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
maintenance:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
|
||||||
|
Perform weekly repository maintenance:
|
||||||
|
|
||||||
|
1. Check for outdated dependencies in package.json
|
||||||
|
2. Scan for security vulnerabilities using `npm audit`
|
||||||
|
3. Review open issues older than 90 days
|
||||||
|
4. Check for TODO comments in recent commits
|
||||||
|
5. Verify README.md examples still work
|
||||||
|
|
||||||
|
Create a single issue summarizing any findings.
|
||||||
|
If critical security issues are found, also comment on open PRs.
|
||||||
|
|
||||||
|
claude_args: |
|
||||||
|
--allowedTools "Read,Bash(npm:*),Bash(gh issue:*),Bash(git:*)"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Configuration:**
|
||||||
|
|
||||||
|
- `schedule:` for automated runs
|
||||||
|
- `workflow_dispatch:` for manual triggering
|
||||||
|
- Comprehensive tool permissions for analysis
|
||||||
|
|
||||||
|
**Expected Output:** Weekly maintenance report as GitHub issue.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Issue Auto-Triage and Labeling
|
||||||
|
|
||||||
|
**When to use:** Automatically categorize and prioritize new issues.
|
||||||
|
|
||||||
|
**Complete Example:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Issue Triage
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
triage:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
ISSUE NUMBER: ${{ github.event.issue.number }}
|
||||||
|
TITLE: ${{ github.event.issue.title }}
|
||||||
|
BODY: ${{ github.event.issue.body }}
|
||||||
|
AUTHOR: ${{ github.event.issue.user.login }}
|
||||||
|
|
||||||
|
Analyze this new issue and:
|
||||||
|
1. Determine if it's a bug report, feature request, or question
|
||||||
|
2. Assess priority (critical, high, medium, low)
|
||||||
|
3. Suggest appropriate labels
|
||||||
|
4. Check if it duplicates existing issues
|
||||||
|
|
||||||
|
Based on your analysis, add the appropriate labels using:
|
||||||
|
`gh issue edit [number] --add-label "label1,label2"`
|
||||||
|
|
||||||
|
If it appears to be a duplicate, post a comment mentioning the original issue.
|
||||||
|
|
||||||
|
claude_args: |
|
||||||
|
--allowedTools "Bash(gh issue:*),Bash(gh search:*)"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Configuration:**
|
||||||
|
|
||||||
|
- Triggered on new issues
|
||||||
|
- Issue context in prompt
|
||||||
|
- Label management capabilities
|
||||||
|
|
||||||
|
**Expected Output:** Automatically labeled and categorized issues.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Documentation Sync on API Changes
|
||||||
|
|
||||||
|
**When to use:** Keep docs up-to-date when API code changes.
|
||||||
|
|
||||||
|
**Complete Example:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Sync API Documentation
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
paths:
|
||||||
|
- "src/api/**/*.ts"
|
||||||
|
- "src/routes/**/*.ts"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
doc-sync:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.ref }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
This PR modifies API endpoints. Please:
|
||||||
|
|
||||||
|
1. Review the API changes in src/api and src/routes
|
||||||
|
2. Update API.md to document any new or changed endpoints
|
||||||
|
3. Ensure OpenAPI spec is updated if needed
|
||||||
|
4. Update example requests/responses
|
||||||
|
|
||||||
|
Use standard REST API documentation format.
|
||||||
|
Commit any documentation updates to this PR branch.
|
||||||
|
|
||||||
|
claude_args: |
|
||||||
|
--allowedTools "Read,Write,Edit,Bash(git:*)"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Configuration:**
|
||||||
|
|
||||||
|
- Path-specific trigger
|
||||||
|
- Write permissions for doc updates
|
||||||
|
- Git tools for committing
|
||||||
|
|
||||||
|
**Expected Output:** API documentation automatically updated with code changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security-Focused PR Reviews
|
||||||
|
|
||||||
|
**When to use:** Deep security analysis for sensitive repositories.
|
||||||
|
|
||||||
|
**Complete Example:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Security Review
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
security:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
security-events: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
# Optional: Add track_progress: true for visual progress tracking during security reviews
|
||||||
|
# track_progress: true
|
||||||
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
Perform a comprehensive security review:
|
||||||
|
|
||||||
|
## OWASP Top 10 Analysis
|
||||||
|
- SQL Injection vulnerabilities
|
||||||
|
- Cross-Site Scripting (XSS)
|
||||||
|
- Broken Authentication
|
||||||
|
- Sensitive Data Exposure
|
||||||
|
- XML External Entities (XXE)
|
||||||
|
- Broken Access Control
|
||||||
|
- Security Misconfiguration
|
||||||
|
- Cross-Site Request Forgery (CSRF)
|
||||||
|
- Using Components with Known Vulnerabilities
|
||||||
|
- Insufficient Logging & Monitoring
|
||||||
|
|
||||||
|
## Additional Security Checks
|
||||||
|
- Hardcoded secrets or credentials
|
||||||
|
- Insecure cryptographic practices
|
||||||
|
- Unsafe deserialization
|
||||||
|
- Server-Side Request Forgery (SSRF)
|
||||||
|
- Race conditions or TOCTOU issues
|
||||||
|
|
||||||
|
Rate severity as: CRITICAL, HIGH, MEDIUM, LOW, or NONE.
|
||||||
|
Post detailed findings with recommendations.
|
||||||
|
|
||||||
|
claude_args: |
|
||||||
|
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*)"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Configuration:**
|
||||||
|
|
||||||
|
- Security-focused prompt structure
|
||||||
|
- OWASP alignment
|
||||||
|
- Severity rating system
|
||||||
|
|
||||||
|
**Expected Output:** Detailed security analysis with prioritized findings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tips for All Solutions
|
||||||
|
|
||||||
|
### Always Include GitHub Context
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
[Your specific instructions]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Tool Permissions
|
||||||
|
|
||||||
|
- **PR Comments**: `Bash(gh pr comment:*)`
|
||||||
|
- **Inline Comments**: `mcp__github_inline_comment__create_inline_comment`
|
||||||
|
- **File Operations**: `Read,Write,Edit`
|
||||||
|
- **Git Operations**: `Bash(git:*)`
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
|
||||||
|
- Be specific in your prompts
|
||||||
|
- Include expected output format
|
||||||
|
- Set clear success criteria
|
||||||
|
- Provide context about the repository
|
||||||
|
- Use inline comments for code-specific feedback
|
||||||
@@ -47,27 +47,33 @@ 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 | - |
|
||||||
| `claude_args` | Additional arguments to pass directly to Claude CLI (e.g., `--max-turns 10 --model claude-4-0-sonnet-20250805`) | No | "" |
|
| `track_progress` | Force tag mode with tracking comments. Only works with specific PR/issue events. Preserves GitHub context | No | `false` |
|
||||||
| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - |
|
| `claude_args` | Additional arguments to pass directly to Claude CLI (e.g., `--max-turns 10 --model claude-4-0-sonnet-20250805`) | No | "" |
|
||||||
| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` |
|
| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - |
|
||||||
| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - |
|
| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` |
|
||||||
| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` |
|
| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - |
|
||||||
| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` |
|
| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` |
|
||||||
| `mcp_config` | Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers | No | "" |
|
| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` |
|
||||||
| `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - |
|
| `mcp_config` | Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers | No | "" |
|
||||||
| `label_trigger` | The label name that triggers the action when applied to an issue (e.g. "claude") | No | - |
|
| `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - |
|
||||||
| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` |
|
| `label_trigger` | The label name that triggers the action when applied to an issue (e.g. "claude") | No | - |
|
||||||
| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` |
|
| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` |
|
||||||
| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" |
|
| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` |
|
||||||
| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" |
|
| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" |
|
||||||
| `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" |
|
| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" |
|
||||||
| `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` |
|
| `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" |
|
||||||
| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots | No | "" |
|
| `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_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
|
||||||
|
|
||||||
@@ -139,7 +145,11 @@ For a comprehensive guide on migrating from v0.x to v1.0, including step-by-step
|
|||||||
```yaml
|
```yaml
|
||||||
- uses: anthropics/claude-code-action@v1
|
- uses: anthropics/claude-code-action@v1
|
||||||
with:
|
with:
|
||||||
prompt: "Update the API documentation"
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
Update the API documentation to reflect changes in this PR
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
claude_args: |
|
claude_args: |
|
||||||
--model claude-4-0-sonnet-20250805
|
--model claude-4-0-sonnet-20250805
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
name: Auto Fix CI Failures (Signed Commits)
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows: ["CI"]
|
|
||||||
types:
|
|
||||||
- completed
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
actions: read
|
|
||||||
issues: write
|
|
||||||
id-token: write # Required for OIDC token exchange
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
auto-fix-signed:
|
|
||||||
if: |
|
|
||||||
github.event.workflow_run.conclusion == 'failure' &&
|
|
||||||
github.event.workflow_run.pull_requests[0] &&
|
|
||||||
!startsWith(github.event.workflow_run.head_branch, 'claude-auto-fix-ci-signed-')
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.event.workflow_run.head_branch }}
|
|
||||||
fetch-depth: 0
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Generate fix branch name
|
|
||||||
id: branch
|
|
||||||
run: |
|
|
||||||
BRANCH_NAME="claude-auto-fix-ci-signed-${{ github.event.workflow_run.head_branch }}-${{ github.run_id }}"
|
|
||||||
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
|
||||||
# Don't create branch locally - MCP tools will create it via API
|
|
||||||
echo "Generated branch name: $BRANCH_NAME (will be created by MCP tools)"
|
|
||||||
|
|
||||||
- name: Get CI failure details
|
|
||||||
id: failure_details
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const run = await github.rest.actions.getWorkflowRun({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
run_id: ${{ github.event.workflow_run.id }}
|
|
||||||
});
|
|
||||||
|
|
||||||
const jobs = await github.rest.actions.listJobsForWorkflowRun({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
run_id: ${{ github.event.workflow_run.id }}
|
|
||||||
});
|
|
||||||
|
|
||||||
const failedJobs = jobs.data.jobs.filter(job => job.conclusion === 'failure');
|
|
||||||
|
|
||||||
let errorLogs = [];
|
|
||||||
for (const job of failedJobs) {
|
|
||||||
const logs = await github.rest.actions.downloadJobLogsForWorkflowRun({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
job_id: job.id
|
|
||||||
});
|
|
||||||
errorLogs.push({
|
|
||||||
jobName: job.name,
|
|
||||||
logs: logs.data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
runUrl: run.data.html_url,
|
|
||||||
failedJobs: failedJobs.map(j => j.name),
|
|
||||||
errorLogs: errorLogs
|
|
||||||
};
|
|
||||||
|
|
||||||
- name: Fix CI failures with Claude (Signed Commits)
|
|
||||||
id: claude
|
|
||||||
uses: anthropics/claude-code-action@v1-dev
|
|
||||||
env:
|
|
||||||
CLAUDE_BRANCH: ${{ steps.branch.outputs.branch_name }}
|
|
||||||
BASE_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
|
||||||
with:
|
|
||||||
prompt: |
|
|
||||||
/fix-ci-signed
|
|
||||||
Failed CI Run: ${{ fromJSON(steps.failure_details.outputs.result).runUrl }}
|
|
||||||
Failed Jobs: ${{ join(fromJSON(steps.failure_details.outputs.result).failedJobs, ', ') }}
|
|
||||||
PR Number: ${{ github.event.workflow_run.pull_requests[0].number }}
|
|
||||||
Branch Name: ${{ steps.branch.outputs.branch_name }}
|
|
||||||
Base Branch: ${{ github.event.workflow_run.head_branch }}
|
|
||||||
Repository: ${{ github.repository }}
|
|
||||||
|
|
||||||
Error logs:
|
|
||||||
${{ toJSON(fromJSON(steps.failure_details.outputs.result).errorLogs) }}
|
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
||||||
use_commit_signing: true
|
|
||||||
claude_args: "--allowedTools 'Edit,MultiEdit,Write,Read,Glob,Grep,LS,Bash(bun:*),Bash(npm:*),Bash(npx:*),Bash(gh:*),mcp__github_file_ops__commit_files,mcp__github_file_ops__delete_files'"
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
---
|
|
||||||
description: Analyze and fix CI failures with signed commits using MCP tools
|
|
||||||
allowed_tools: Edit,MultiEdit,Write,Read,Glob,Grep,LS,Bash(bun:*),Bash(npm:*),Bash(npx:*),Bash(gh:*),mcp__github_file_ops__commit_files,mcp__github_file_ops__delete_files
|
|
||||||
---
|
|
||||||
|
|
||||||
# Fix CI Failures with Signed Commits
|
|
||||||
|
|
||||||
You are tasked with analyzing CI failure logs and fixing the issues using MCP tools for signed commits. Follow these steps:
|
|
||||||
|
|
||||||
## Context Provided
|
|
||||||
|
|
||||||
$ARGUMENTS
|
|
||||||
|
|
||||||
## Important Context Information
|
|
||||||
|
|
||||||
Look for these key pieces of information in the arguments:
|
|
||||||
|
|
||||||
- **Failed CI Run URL**: Link to the failed CI run
|
|
||||||
- **Failed Jobs**: List of jobs that failed
|
|
||||||
- **PR Number**: The PR number to comment on
|
|
||||||
- **Branch Name**: The fix branch you're working on
|
|
||||||
- **Base Branch**: The original PR branch
|
|
||||||
- **Error logs**: Detailed logs from failed jobs
|
|
||||||
|
|
||||||
## CRITICAL: Use MCP Tools for Git Operations
|
|
||||||
|
|
||||||
**IMPORTANT**: You MUST use MCP tools for all git operations to ensure commits are properly signed. DO NOT use `git` commands directly via Bash.
|
|
||||||
|
|
||||||
- Use `mcp__github_file_ops__commit_files` to commit and push changes
|
|
||||||
- Use `mcp__github_file_ops__delete_files` to delete files
|
|
||||||
|
|
||||||
## Step 1: Analyze the Failure
|
|
||||||
|
|
||||||
Parse the provided CI failure information to understand:
|
|
||||||
|
|
||||||
- Which jobs failed and why
|
|
||||||
- The specific error messages and stack traces
|
|
||||||
- Whether failures are test-related, build-related, or linting issues
|
|
||||||
|
|
||||||
## Step 2: Search and Understand the Codebase
|
|
||||||
|
|
||||||
Use MCP search tools to locate the failing code:
|
|
||||||
|
|
||||||
- Use `mcp_github_file_ops_server__search_files` or `mcp_github_file_ops_server__file_search` to find failing test names or functions
|
|
||||||
- Use `mcp_github_file_ops_server__read_file` to read source files mentioned in error messages
|
|
||||||
- Review related configuration files (package.json, tsconfig.json, etc.)
|
|
||||||
|
|
||||||
## Step 3: Apply Targeted Fixes
|
|
||||||
|
|
||||||
Make minimal, focused changes:
|
|
||||||
|
|
||||||
- **For test failures**: Determine if the test or implementation needs fixing
|
|
||||||
- **For type errors**: Fix type definitions or correct the code logic
|
|
||||||
- **For linting issues**: Apply formatting using the project's tools
|
|
||||||
- **For build errors**: Resolve dependency or configuration issues
|
|
||||||
- **For missing imports**: Add the necessary imports or install packages
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
|
|
||||||
- Only fix the actual CI failures, avoid unrelated changes
|
|
||||||
- Follow existing code patterns and conventions
|
|
||||||
- Ensure changes are production-ready, not temporary hacks
|
|
||||||
- Preserve existing functionality while fixing issues
|
|
||||||
|
|
||||||
## Step 4: Verify Fixes Locally
|
|
||||||
|
|
||||||
Run available verification commands using Bash:
|
|
||||||
|
|
||||||
- Execute the failing tests locally to confirm they pass
|
|
||||||
- Run the project's lint command (check package.json for scripts)
|
|
||||||
- Run type checking if available
|
|
||||||
- Execute any build commands to ensure compilation succeeds
|
|
||||||
|
|
||||||
## Step 5: Commit and Push Changes Using MCP
|
|
||||||
|
|
||||||
**CRITICAL**: You MUST use MCP tools for committing and pushing:
|
|
||||||
|
|
||||||
1. Prepare all your file changes (using Edit/MultiEdit/Write tools as needed)
|
|
||||||
2. **Use `mcp__github_file_ops__commit_files` to commit and push all changes**
|
|
||||||
- Pass the file paths you've edited in the `files` array
|
|
||||||
- Set `message` to describe the specific fixes (e.g., "Fix CI failures: remove syntax errors and format code")
|
|
||||||
- The MCP tool will automatically create the branch specified in "Branch Name:" from the context and push signed commits
|
|
||||||
|
|
||||||
**IMPORTANT**: The MCP tool will create the branch from the context automatically. The branch name from "Branch Name:" in the context will be used.
|
|
||||||
|
|
||||||
Example usage:
|
|
||||||
|
|
||||||
```
|
|
||||||
mcp__github_file_ops__commit_files with:
|
|
||||||
- files: ["src/utils/retry.ts", "src/other/file.ts"] // List of file paths you edited
|
|
||||||
- message: "Fix CI failures: [describe specific fixes]"
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: The branch will be created from the Base Branch specified in the context.
|
|
||||||
|
|
||||||
## Step 6: Create PR Comment (REQUIRED - DO NOT SKIP)
|
|
||||||
|
|
||||||
**CRITICAL: You MUST create a PR comment after pushing. This step is MANDATORY.**
|
|
||||||
|
|
||||||
After successfully pushing the fixes, you MUST create a comment on the original PR to notify about the auto-fix. DO NOT end the task without completing this step.
|
|
||||||
|
|
||||||
1. Extract the PR number from the context provided in arguments (look for "PR Number:" in the context)
|
|
||||||
2. **MANDATORY**: Execute the gh CLI command below to create the comment
|
|
||||||
3. Verify the comment was created successfully
|
|
||||||
|
|
||||||
**YOU MUST RUN THIS COMMAND** (replace placeholders with actual values from context):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gh pr comment PR_NUMBER --body "## 🤖 CI Auto-Fix Available (Signed Commits)
|
|
||||||
|
|
||||||
Claude has analyzed the CI failures and prepared fixes with signed commits.
|
|
||||||
|
|
||||||
[**→ Create pull request to fix CI**](https://github.com/OWNER/REPO/compare/BASE_BRANCH...FIX_BRANCH?quick_pull=1)
|
|
||||||
|
|
||||||
_This fix was generated automatically based on the [failed CI run](FAILED_CI_RUN_URL)._"
|
|
||||||
```
|
|
||||||
|
|
||||||
**IMPORTANT REPLACEMENTS YOU MUST MAKE:**
|
|
||||||
|
|
||||||
- Replace `PR_NUMBER` with the actual PR number from "PR Number:" in context
|
|
||||||
- Replace `OWNER/REPO` with the repository from "Repository:" in context
|
|
||||||
- Replace `BASE_BRANCH` with the branch from "Base Branch:" in context
|
|
||||||
- Replace `FIX_BRANCH` with the branch from "Branch Name:" in context
|
|
||||||
- Replace `FAILED_CI_RUN_URL` with the URL from "Failed CI Run:" in context
|
|
||||||
|
|
||||||
**DO NOT SKIP THIS STEP. The task is NOT complete until the PR comment is created.**
|
|
||||||
|
|
||||||
## Step 7: Final Verification
|
|
||||||
|
|
||||||
**BEFORE CONSIDERING THE TASK COMPLETE**, verify you have:
|
|
||||||
|
|
||||||
1. ✅ Fixed all CI failures
|
|
||||||
2. ✅ Committed the changes using `mcp_github_file_ops_server__push_files`
|
|
||||||
3. ✅ Verified the branch was pushed successfully
|
|
||||||
4. ✅ **CREATED THE PR COMMENT using `gh pr comment` command from Step 6**
|
|
||||||
|
|
||||||
If you have NOT created the PR comment, go back to Step 6 and execute the command.
|
|
||||||
|
|
||||||
## Important Guidelines
|
|
||||||
|
|
||||||
- Always use MCP tools for git operations to ensure proper commit signing
|
|
||||||
- Focus exclusively on fixing the reported CI failures
|
|
||||||
- Maintain code quality and follow the project's established patterns
|
|
||||||
- If a fix requires significant refactoring, document why it's necessary
|
|
||||||
- When multiple solutions exist, choose the simplest one that maintains code quality
|
|
||||||
- **THE TASK IS NOT COMPLETE WITHOUT THE PR COMMENT**
|
|
||||||
|
|
||||||
Begin by analyzing the failure details provided above.
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
---
|
|
||||||
description: Analyze and fix CI failures by examining logs and making targeted fixes
|
|
||||||
allowed_tools: Edit,MultiEdit,Write,Read,Glob,Grep,LS,Bash(git:*),Bash(bun:*),Bash(npm:*),Bash(npx:*),Bash(gh:*)
|
|
||||||
---
|
|
||||||
|
|
||||||
# Fix CI Failures
|
|
||||||
|
|
||||||
You are tasked with analyzing CI failure logs and fixing the issues. Follow these steps:
|
|
||||||
|
|
||||||
## Context Provided
|
|
||||||
|
|
||||||
$ARGUMENTS
|
|
||||||
|
|
||||||
## Important Context Information
|
|
||||||
|
|
||||||
Look for these key pieces of information in the arguments:
|
|
||||||
|
|
||||||
- **Failed CI Run URL**: Link to the failed CI run
|
|
||||||
- **Failed Jobs**: List of jobs that failed
|
|
||||||
- **PR Number**: The PR number to comment on
|
|
||||||
- **Branch Name**: The fix branch you're working on
|
|
||||||
- **Base Branch**: The original PR branch
|
|
||||||
- **Error logs**: Detailed logs from failed jobs
|
|
||||||
|
|
||||||
## Step 1: Analyze the Failure
|
|
||||||
|
|
||||||
Parse the provided CI failure information to understand:
|
|
||||||
|
|
||||||
- Which jobs failed and why
|
|
||||||
- The specific error messages and stack traces
|
|
||||||
- Whether failures are test-related, build-related, or linting issues
|
|
||||||
|
|
||||||
## Step 2: Search and Understand the Codebase
|
|
||||||
|
|
||||||
Use search tools to locate the failing code:
|
|
||||||
|
|
||||||
- Search for the failing test names or functions
|
|
||||||
- Find the source files mentioned in error messages
|
|
||||||
- Review related configuration files (package.json, tsconfig.json, etc.)
|
|
||||||
|
|
||||||
## Step 3: Apply Targeted Fixes
|
|
||||||
|
|
||||||
Make minimal, focused changes:
|
|
||||||
|
|
||||||
- **For test failures**: Determine if the test or implementation needs fixing
|
|
||||||
- **For type errors**: Fix type definitions or correct the code logic
|
|
||||||
- **For linting issues**: Apply formatting using the project's tools
|
|
||||||
- **For build errors**: Resolve dependency or configuration issues
|
|
||||||
- **For missing imports**: Add the necessary imports or install packages
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
|
|
||||||
- Only fix the actual CI failures, avoid unrelated changes
|
|
||||||
- Follow existing code patterns and conventions
|
|
||||||
- Ensure changes are production-ready, not temporary hacks
|
|
||||||
- Preserve existing functionality while fixing issues
|
|
||||||
|
|
||||||
## Step 4: Verify Fixes Locally
|
|
||||||
|
|
||||||
Run available verification commands:
|
|
||||||
|
|
||||||
- Execute the failing tests locally to confirm they pass
|
|
||||||
- Run the project's lint command (check package.json for scripts)
|
|
||||||
- Run type checking if available
|
|
||||||
- Execute any build commands to ensure compilation succeeds
|
|
||||||
|
|
||||||
## Step 5: Commit and Push Changes
|
|
||||||
|
|
||||||
After applying ALL fixes:
|
|
||||||
|
|
||||||
1. Stage all modified files with `git add -A`
|
|
||||||
2. Commit with: `git commit -m "Fix CI failures: [describe specific fixes]"`
|
|
||||||
3. Document which CI jobs/tests were addressed
|
|
||||||
4. **CRITICAL**: Push the branch with `git push origin HEAD` - You MUST push the branch after committing
|
|
||||||
|
|
||||||
## Step 6: Create PR Comment (REQUIRED - DO NOT SKIP)
|
|
||||||
|
|
||||||
**CRITICAL: You MUST create a PR comment after pushing. This step is MANDATORY.**
|
|
||||||
|
|
||||||
After successfully pushing the fixes, you MUST create a comment on the original PR to notify about the auto-fix. DO NOT end the task without completing this step.
|
|
||||||
|
|
||||||
1. Extract the PR number from the context provided in arguments (look for "PR Number:" in the context)
|
|
||||||
2. **MANDATORY**: Execute the gh CLI command below to create the comment
|
|
||||||
3. Verify the comment was created successfully
|
|
||||||
|
|
||||||
**YOU MUST RUN THIS COMMAND** (replace placeholders with actual values from context):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gh pr comment PR_NUMBER --body "## 🤖 CI Auto-Fix Available
|
|
||||||
|
|
||||||
Claude has analyzed the CI failures and prepared fixes.
|
|
||||||
|
|
||||||
[**→ Create pull request to fix CI**](https://github.com/OWNER/REPO/compare/BASE_BRANCH...FIX_BRANCH?quick_pull=1)
|
|
||||||
|
|
||||||
_This fix was generated automatically based on the [failed CI run](FAILED_CI_RUN_URL)._"
|
|
||||||
```
|
|
||||||
|
|
||||||
**IMPORTANT REPLACEMENTS YOU MUST MAKE:**
|
|
||||||
|
|
||||||
- Replace `PR_NUMBER` with the actual PR number from "PR Number:" in context
|
|
||||||
- Replace `OWNER/REPO` with the repository from "Repository:" in context
|
|
||||||
- Replace `BASE_BRANCH` with the branch from "Base Branch:" in context
|
|
||||||
- Replace `FIX_BRANCH` with the branch from "Branch Name:" in context
|
|
||||||
- Replace `FAILED_CI_RUN_URL` with the URL from "Failed CI Run:" in context
|
|
||||||
|
|
||||||
**DO NOT SKIP THIS STEP. The task is NOT complete until the PR comment is created.**
|
|
||||||
|
|
||||||
## Step 7: Final Verification
|
|
||||||
|
|
||||||
**BEFORE CONSIDERING THE TASK COMPLETE**, verify you have:
|
|
||||||
|
|
||||||
1. ✅ Fixed all CI failures
|
|
||||||
2. ✅ Committed the changes
|
|
||||||
3. ✅ Pushed the branch with `git push origin HEAD`
|
|
||||||
4. ✅ **CREATED THE PR COMMENT using `gh pr comment` command from Step 6**
|
|
||||||
|
|
||||||
If you have NOT created the PR comment, go back to Step 6 and execute the command.
|
|
||||||
|
|
||||||
## Important Guidelines
|
|
||||||
|
|
||||||
- Focus exclusively on fixing the reported CI failures
|
|
||||||
- Maintain code quality and follow the project's established patterns
|
|
||||||
- If a fix requires significant refactoring, document why it's necessary
|
|
||||||
- When multiple solutions exist, choose the simplest one that maintains code quality
|
|
||||||
- **THE TASK IS NOT COMPLETE WITHOUT THE PR COMMENT**
|
|
||||||
|
|
||||||
Begin by analyzing the failure details provided above.
|
|
||||||
@@ -80,7 +80,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Fix CI failures with Claude
|
- name: Fix CI failures with Claude
|
||||||
id: claude
|
id: claude
|
||||||
uses: anthropics/claude-code-action@v1-dev
|
uses: anthropics/claude-code-action@v1
|
||||||
with:
|
with:
|
||||||
prompt: |
|
prompt: |
|
||||||
/fix-ci
|
/fix-ci
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
name: Claude Args Example
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
prompt:
|
|
||||||
description: "Prompt for Claude"
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
claude-with-custom-args:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Run Claude with custom arguments
|
|
||||||
uses: anthropics/claude-code-action@v1-dev
|
|
||||||
with:
|
|
||||||
prompt: ${{ github.event.inputs.prompt }}
|
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
||||||
|
|
||||||
# claude_args provides direct CLI argument control
|
|
||||||
# This allows full customization of Claude's behavior
|
|
||||||
claude_args: |
|
|
||||||
--max-turns 15
|
|
||||||
--model claude-opus-4-1-20250805
|
|
||||||
--allowedTools Edit,Read,Write,Bash
|
|
||||||
--disallowedTools WebSearch
|
|
||||||
--system-prompt "You are a senior engineer focused on code quality"
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
name: Claude PR Auto Review
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
auto-review:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: read
|
|
||||||
id-token: write
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
|
|
||||||
- name: Automatic PR Review
|
|
||||||
uses: anthropics/claude-code-action@v1-dev
|
|
||||||
with:
|
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
||||||
prompt: |
|
|
||||||
REPO: ${{ github.repository }}
|
|
||||||
PR NUMBER: ${{ github.event.pull_request.number }}
|
|
||||||
|
|
||||||
Please review this pull request.
|
|
||||||
|
|
||||||
Note: The PR branch is already checked out in the current working directory.
|
|
||||||
|
|
||||||
Focus on:
|
|
||||||
- Code quality and best practices
|
|
||||||
- Potential bugs or issues
|
|
||||||
- Performance considerations
|
|
||||||
- Security implications
|
|
||||||
- Test coverage
|
|
||||||
- Documentation updates if needed
|
|
||||||
- Verify that README.md and docs are updated for any new features or config changes
|
|
||||||
|
|
||||||
Provide constructive feedback with specific suggestions for improvement.
|
|
||||||
Use `gh pr comment:*` for top-level comments.
|
|
||||||
Use `mcp__github_inline_comment__create_inline_comment` to highlight specific areas of concern.
|
|
||||||
Only your GitHub comments that you post will be seen, so don't submit your review as a normal message, just as comments.
|
|
||||||
If the PR has already been reviewed, or there are no noteworthy changes, don't post anything.
|
|
||||||
|
|
||||||
claude_args: |
|
|
||||||
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*), Bash(gh pr diff:*), Bash(gh pr view:*)"
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
name: Claude Automatic Mode Detection Examples
|
|
||||||
|
|
||||||
on:
|
|
||||||
# Events for interactive mode (responds to @claude mentions)
|
|
||||||
issue_comment:
|
|
||||||
types: [created]
|
|
||||||
issues:
|
|
||||||
types: [opened, labeled]
|
|
||||||
pull_request:
|
|
||||||
types: [opened]
|
|
||||||
# Events for automation mode (runs with explicit prompt)
|
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 0 * * 0" # Weekly on Sunday
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# Interactive Mode - Activated automatically when no prompt is provided
|
|
||||||
interactive-mode-example:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
issues: write
|
|
||||||
id-token: write
|
|
||||||
steps:
|
|
||||||
- uses: anthropics/claude-code-action@v1-dev
|
|
||||||
with:
|
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
||||||
# Interactive mode (auto-detected when no prompt):
|
|
||||||
# - Scans for @claude mentions in comments, issues, and PRs
|
|
||||||
# - Only acts when trigger phrase is found
|
|
||||||
# - Creates tracking comments with progress checkboxes
|
|
||||||
# - Perfect for: Interactive Q&A, on-demand code changes
|
|
||||||
|
|
||||||
# Automation Mode - Activated automatically when prompt is provided
|
|
||||||
automation-mode-scheduled-task:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
issues: write
|
|
||||||
id-token: write
|
|
||||||
steps:
|
|
||||||
- uses: anthropics/claude-code-action@v1-dev
|
|
||||||
with:
|
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
||||||
prompt: |
|
|
||||||
Check for outdated dependencies and security vulnerabilities.
|
|
||||||
Create an issue if any critical problems are found.
|
|
||||||
# Automation mode (auto-detected when prompt provided):
|
|
||||||
# - Works with any GitHub event
|
|
||||||
# - Executes immediately without waiting for @claude mentions
|
|
||||||
# - No tracking comments created
|
|
||||||
# - Perfect for: scheduled maintenance, automated reviews, CI/CD tasks
|
|
||||||
@@ -32,14 +32,10 @@ jobs:
|
|||||||
|
|
||||||
- name: Run Claude Code
|
- name: Run Claude Code
|
||||||
id: claude
|
id: claude
|
||||||
uses: anthropics/claude-code-action@v1-dev
|
uses: anthropics/claude-code-action@v1
|
||||||
with:
|
with:
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
|
||||||
# This is an optional setting that allows Claude to read CI results on PRs
|
|
||||||
additional_permissions: |
|
|
||||||
actions: read
|
|
||||||
|
|
||||||
# Optional: Customize the trigger phrase (default: @claude)
|
# Optional: Customize the trigger phrase (default: @claude)
|
||||||
# trigger_phrase: "/claude"
|
# trigger_phrase: "/claude"
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Check for duplicate issues
|
- name: Check for duplicate issues
|
||||||
uses: anthropics/claude-code-action@v1-dev
|
uses: anthropics/claude-code-action@v1
|
||||||
with:
|
with:
|
||||||
prompt: |
|
prompt: |
|
||||||
Analyze this new issue and check if it's a duplicate of existing issues in the repository.
|
Analyze this new issue and check if it's a duplicate of existing issues in the repository.
|
||||||
|
|||||||
@@ -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]
|
||||||
@@ -17,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-dev
|
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 }}
|
||||||
|
|||||||
@@ -28,10 +28,13 @@ jobs:
|
|||||||
fetch-depth: 2 # Need at least 2 commits to analyze the latest
|
fetch-depth: 2 # Need at least 2 commits to analyze the latest
|
||||||
|
|
||||||
- name: Run Claude Analysis
|
- name: Run Claude Analysis
|
||||||
uses: anthropics/claude-code-action@v1-dev
|
uses: anthropics/claude-code-action@v1
|
||||||
with:
|
with:
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
prompt: |
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
BRANCH: ${{ github.ref_name }}
|
||||||
|
|
||||||
Analyze the latest commit in this repository.
|
Analyze the latest commit in this repository.
|
||||||
|
|
||||||
${{ github.event.inputs.analysis_type == 'summarize-commit' && 'Task: Provide a clear, concise summary of what changed in the latest commit. Include the commit message, files changed, and the purpose of the changes.' || '' }}
|
${{ github.event.inputs.analysis_type == 'summarize-commit' && 'Task: Provide a clear, concise summary of what changed in the latest commit. Include the commit message, files changed, and the purpose of the changes.' || '' }}
|
||||||
74
examples/pr-review-comprehensive.yml
Normal file
74
examples/pr-review-comprehensive.yml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
name: PR Review with Progress Tracking
|
||||||
|
|
||||||
|
# This example demonstrates how to use the track_progress feature to get
|
||||||
|
# visual progress tracking for PR reviews, similar to v0.x agent mode.
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, ready_for_review, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
review-with-tracking:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- name: PR Review with Progress Tracking
|
||||||
|
uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
|
||||||
|
# Enable progress tracking
|
||||||
|
track_progress: true
|
||||||
|
|
||||||
|
# Your custom review instructions
|
||||||
|
prompt: |
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
Perform a comprehensive code review with the following focus areas:
|
||||||
|
|
||||||
|
1. **Code Quality**
|
||||||
|
- Clean code principles and best practices
|
||||||
|
- Proper error handling and edge cases
|
||||||
|
- Code readability and maintainability
|
||||||
|
|
||||||
|
2. **Security**
|
||||||
|
- Check for potential security vulnerabilities
|
||||||
|
- Validate input sanitization
|
||||||
|
- Review authentication/authorization logic
|
||||||
|
|
||||||
|
3. **Performance**
|
||||||
|
- Identify potential performance bottlenecks
|
||||||
|
- Review database queries for efficiency
|
||||||
|
- Check for memory leaks or resource issues
|
||||||
|
|
||||||
|
4. **Testing**
|
||||||
|
- Verify adequate test coverage
|
||||||
|
- Review test quality and edge cases
|
||||||
|
- Check for missing test scenarios
|
||||||
|
|
||||||
|
5. **Documentation**
|
||||||
|
- Ensure code is properly documented
|
||||||
|
- Verify README updates for new features
|
||||||
|
- Check API documentation accuracy
|
||||||
|
|
||||||
|
Provide detailed feedback using inline comments for specific issues.
|
||||||
|
Use top-level comments for general observations or praise.
|
||||||
|
|
||||||
|
# Tools for comprehensive PR review
|
||||||
|
claude_args: |
|
||||||
|
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)"
|
||||||
|
|
||||||
|
# When track_progress is enabled:
|
||||||
|
# - Creates a tracking comment with progress checkboxes
|
||||||
|
# - Includes all PR context (comments, attachments, images)
|
||||||
|
# - Updates progress as the review proceeds
|
||||||
|
# - Marks as completed when done
|
||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Review PR from Specific Author
|
- name: Review PR from Specific Author
|
||||||
uses: anthropics/claude-code-action@v1-dev
|
uses: anthropics/claude-code-action@v1
|
||||||
with:
|
with:
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
prompt: |
|
prompt: |
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Claude Code Review
|
- name: Claude Code Review
|
||||||
uses: anthropics/claude-code-action@v1-dev
|
uses: anthropics/claude-code-action@v1
|
||||||
with:
|
with:
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
prompt: |
|
prompt: |
|
||||||
@@ -335,6 +335,7 @@ export function prepareContext(
|
|||||||
return {
|
return {
|
||||||
...commonFields,
|
...commonFields,
|
||||||
eventData,
|
eventData,
|
||||||
|
githubContext: context,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ export const PR_QUERY = `
|
|||||||
additions
|
additions
|
||||||
deletions
|
deletions
|
||||||
state
|
state
|
||||||
|
labels(first: 1) {
|
||||||
|
nodes {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
commits(first: 100) {
|
commits(first: 100) {
|
||||||
totalCount
|
totalCount
|
||||||
nodes {
|
nodes {
|
||||||
@@ -97,6 +102,11 @@ export const ISSUE_QUERY = `
|
|||||||
}
|
}
|
||||||
createdAt
|
createdAt
|
||||||
state
|
state
|
||||||
|
labels(first: 1) {
|
||||||
|
nodes {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
comments(first: 100) {
|
comments(first: 100) {
|
||||||
nodes {
|
nodes {
|
||||||
id
|
id
|
||||||
|
|||||||
13
src/github/constants.ts
Normal file
13
src/github/constants.ts
Normal 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]";
|
||||||
@@ -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;
|
||||||
@@ -72,9 +88,13 @@ type BaseContext = {
|
|||||||
labelTrigger: string;
|
labelTrigger: string;
|
||||||
baseBranch?: string;
|
baseBranch?: string;
|
||||||
branchPrefix: string;
|
branchPrefix: string;
|
||||||
|
branchNameTemplate?: 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 +112,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
|
||||||
@@ -120,9 +144,13 @@ export function parseGitHubContext(): GitHubContext {
|
|||||||
labelTrigger: process.env.LABEL_TRIGGER ?? "",
|
labelTrigger: process.env.LABEL_TRIGGER ?? "",
|
||||||
baseBranch: process.env.BASE_BRANCH,
|
baseBranch: process.env.BASE_BRANCH,
|
||||||
branchPrefix: process.env.BRANCH_PREFIX ?? "claude/",
|
branchPrefix: process.env.BRANCH_PREFIX ?? "claude/",
|
||||||
|
branchNameTemplate: process.env.BRANCH_NAME_TEMPLATE,
|
||||||
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 +213,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,
|
||||||
|
|||||||
@@ -12,6 +12,15 @@ import type { ParsedGitHubContext } from "../context";
|
|||||||
import type { GitHubPullRequest } from "../types";
|
import type { GitHubPullRequest } from "../types";
|
||||||
import type { Octokits } from "../api/client";
|
import type { Octokits } from "../api/client";
|
||||||
import type { FetchDataResult } from "../data/fetcher";
|
import type { FetchDataResult } from "../data/fetcher";
|
||||||
|
import { generateBranchName } from "../../utils/branch-template";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the first label from GitHub data, or returns undefined if no labels exist
|
||||||
|
*/
|
||||||
|
function extractFirstLabel(githubData: FetchDataResult): string | undefined {
|
||||||
|
const labels = githubData.contextData.labels?.nodes;
|
||||||
|
return labels && labels.length > 0 ? labels[0]?.name : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export type BranchInfo = {
|
export type BranchInfo = {
|
||||||
baseBranch: string;
|
baseBranch: string;
|
||||||
@@ -26,7 +35,7 @@ export async function setupBranch(
|
|||||||
): Promise<BranchInfo> {
|
): Promise<BranchInfo> {
|
||||||
const { owner, repo } = context.repository;
|
const { owner, repo } = context.repository;
|
||||||
const entityNumber = context.entityNumber;
|
const entityNumber = context.entityNumber;
|
||||||
const { baseBranch, branchPrefix } = context.inputs;
|
const { baseBranch, branchPrefix, branchNameTemplate } = context.inputs;
|
||||||
const isPR = context.isPR;
|
const isPR = context.isPR;
|
||||||
|
|
||||||
if (isPR) {
|
if (isPR) {
|
||||||
@@ -87,17 +96,8 @@ export async function setupBranch(
|
|||||||
// Generate branch name for either an issue or closed/merged PR
|
// Generate branch name for either an issue or closed/merged PR
|
||||||
const entityType = isPR ? "pr" : "issue";
|
const entityType = isPR ? "pr" : "issue";
|
||||||
|
|
||||||
// Create Kubernetes-compatible timestamp: lowercase, hyphens only, shorter format
|
// Get the SHA of the source branch to use in template
|
||||||
const now = new Date();
|
let sourceSHA: string | undefined;
|
||||||
const timestamp = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}-${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`;
|
|
||||||
|
|
||||||
// Ensure branch name is Kubernetes-compatible:
|
|
||||||
// - Lowercase only
|
|
||||||
// - Alphanumeric with hyphens
|
|
||||||
// - No underscores
|
|
||||||
// - Max 50 chars (to allow for prefixes)
|
|
||||||
const branchName = `${branchPrefix}${entityType}-${entityNumber}-${timestamp}`;
|
|
||||||
const newBranch = branchName.toLowerCase().substring(0, 50);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get the SHA of the source branch to verify it exists
|
// Get the SHA of the source branch to verify it exists
|
||||||
@@ -107,8 +107,46 @@ export async function setupBranch(
|
|||||||
ref: `heads/${sourceBranch}`,
|
ref: `heads/${sourceBranch}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentSHA = sourceBranchRef.data.object.sha;
|
sourceSHA = sourceBranchRef.data.object.sha;
|
||||||
console.log(`Source branch SHA: ${currentSHA}`);
|
console.log(`Source branch SHA: ${sourceSHA}`);
|
||||||
|
|
||||||
|
// Extract first label from GitHub data
|
||||||
|
const firstLabel = extractFirstLabel(githubData);
|
||||||
|
|
||||||
|
// Extract title from GitHub data
|
||||||
|
const title = githubData.contextData.title;
|
||||||
|
|
||||||
|
// Generate branch name using template or default format
|
||||||
|
let newBranch = generateBranchName(
|
||||||
|
branchNameTemplate,
|
||||||
|
branchPrefix,
|
||||||
|
entityType,
|
||||||
|
entityNumber,
|
||||||
|
sourceSHA,
|
||||||
|
firstLabel,
|
||||||
|
title,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if generated branch already exists on remote
|
||||||
|
try {
|
||||||
|
await $`git ls-remote --exit-code origin refs/heads/${newBranch}`.quiet();
|
||||||
|
|
||||||
|
// If we get here, branch exists (exit code 0)
|
||||||
|
console.log(
|
||||||
|
`Branch '${newBranch}' already exists, falling back to default format`,
|
||||||
|
);
|
||||||
|
newBranch = generateBranchName(
|
||||||
|
undefined, // Force default template
|
||||||
|
branchPrefix,
|
||||||
|
entityType,
|
||||||
|
entityNumber,
|
||||||
|
sourceSHA,
|
||||||
|
firstLabel,
|
||||||
|
title,
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
// Branch doesn't exist (non-zero exit code), continue with generated name
|
||||||
|
}
|
||||||
|
|
||||||
// For commit signing, defer branch creation to the file ops server
|
// For commit signing, defer branch creation to the file ops server
|
||||||
if (context.inputs.useCommitSigning) {
|
if (context.inputs.useCommitSigning) {
|
||||||
|
|||||||
@@ -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...");
|
||||||
|
|||||||
@@ -61,6 +61,11 @@ export type GitHubPullRequest = {
|
|||||||
additions: number;
|
additions: number;
|
||||||
deletions: number;
|
deletions: number;
|
||||||
state: string;
|
state: string;
|
||||||
|
labels: {
|
||||||
|
nodes: Array<{
|
||||||
|
name: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
commits: {
|
commits: {
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
nodes: Array<{
|
nodes: Array<{
|
||||||
@@ -84,6 +89,11 @@ export type GitHubIssue = {
|
|||||||
author: GitHubAuthor;
|
author: GitHubAuthor;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
state: string;
|
state: string;
|
||||||
|
labels: {
|
||||||
|
nodes: Array<{
|
||||||
|
name: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
comments: {
|
comments: {
|
||||||
nodes: GitHubComment[];
|
nodes: GitHubComment[];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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}`);
|
||||||
|
|||||||
@@ -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,10 +61,18 @@ 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)
|
||||||
|
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__"),
|
||||||
);
|
);
|
||||||
@@ -71,26 +81,36 @@ export async function prepareMcpConfig(
|
|||||||
tool.startsWith("mcp__github_inline_comment__"),
|
tool.startsWith("mcp__github_inline_comment__"),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const hasGitHubCITools = allowedToolsList.some((tool) =>
|
||||||
|
tool.startsWith("mcp__github_ci__"),
|
||||||
|
);
|
||||||
|
|
||||||
const baseMcpConfig: { mcpServers: Record<string, unknown> } = {
|
const baseMcpConfig: { mcpServers: Record<string, unknown> } = {
|
||||||
mcpServers: {},
|
mcpServers: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Always include comment server for updating Claude comments
|
// Include comment server:
|
||||||
baseMcpConfig.mcpServers.github_comment = {
|
// - Always in tag mode (for updating Claude comments)
|
||||||
command: "bun",
|
// - Only with explicit tools in agent mode
|
||||||
args: [
|
const shouldIncludeCommentServer = !isAgentMode || hasGitHubCommentTools;
|
||||||
"run",
|
|
||||||
`${process.env.GITHUB_ACTION_PATH}/src/mcp/github-comment-server.ts`,
|
if (shouldIncludeCommentServer) {
|
||||||
],
|
baseMcpConfig.mcpServers.github_comment = {
|
||||||
env: {
|
command: "bun",
|
||||||
GITHUB_TOKEN: githubToken,
|
args: [
|
||||||
REPO_OWNER: owner,
|
"run",
|
||||||
REPO_NAME: repo,
|
`${process.env.GITHUB_ACTION_PATH}/src/mcp/github-comment-server.ts`,
|
||||||
...(claudeCommentId && { CLAUDE_COMMENT_ID: claudeCommentId }),
|
],
|
||||||
GITHUB_EVENT_NAME: process.env.GITHUB_EVENT_NAME || "",
|
env: {
|
||||||
GITHUB_API_URL: GITHUB_API_URL,
|
GITHUB_TOKEN: githubToken,
|
||||||
},
|
REPO_OWNER: owner,
|
||||||
};
|
REPO_NAME: repo,
|
||||||
|
...(claudeCommentId && { CLAUDE_COMMENT_ID: claudeCommentId }),
|
||||||
|
GITHUB_EVENT_NAME: process.env.GITHUB_EVENT_NAME || "",
|
||||||
|
GITHUB_API_URL: GITHUB_API_URL,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Include file ops server when commit signing is enabled
|
// Include file ops server when commit signing is enabled
|
||||||
if (context.inputs.useCommitSigning) {
|
if (context.inputs.useCommitSigning) {
|
||||||
@@ -136,10 +156,17 @@ export async function prepareMcpConfig(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// CI server is included when we have a workflow token and context is a PR
|
// CI server is included when:
|
||||||
|
// - In tag mode: when we have a workflow token and context is a PR
|
||||||
|
// - In agent mode: same conditions PLUS explicit CI tools in allowedTools
|
||||||
const hasWorkflowToken = !!process.env.DEFAULT_WORKFLOW_TOKEN;
|
const hasWorkflowToken = !!process.env.DEFAULT_WORKFLOW_TOKEN;
|
||||||
|
const shouldIncludeCIServer =
|
||||||
|
(!isAgentMode || hasGitHubCITools) &&
|
||||||
|
isEntityContext(context) &&
|
||||||
|
context.isPR &&
|
||||||
|
hasWorkflowToken;
|
||||||
|
|
||||||
if (isEntityContext(context) && context.isPR && hasWorkflowToken) {
|
if (shouldIncludeCIServer) {
|
||||||
// Verify the token actually has actions:read permission
|
// Verify the token actually has actions:read permission
|
||||||
const actuallyHasPermission = await checkActionsReadPermission(
|
const actuallyHasPermission = await checkActionsReadPermission(
|
||||||
process.env.DEFAULT_WORKFLOW_TOKEN || "",
|
process.env.DEFAULT_WORKFLOW_TOKEN || "",
|
||||||
|
|||||||
@@ -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 user = {
|
||||||
const { data: authenticatedUser } =
|
login: context.inputs.botName,
|
||||||
await octokit.rest.users.getAuthenticated();
|
id: parseInt(context.inputs.botId),
|
||||||
const user = {
|
};
|
||||||
login: authenticatedUser.login,
|
|
||||||
id: authenticatedUser.id,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
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,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 = "";
|
||||||
|
|||||||
94
src/utils/branch-template.ts
Normal file
94
src/utils/branch-template.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Branch name template parsing and variable substitution utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the first three words from a title and converts them to kebab-case
|
||||||
|
*/
|
||||||
|
function extractDescription(title: string): string {
|
||||||
|
if (!title || title.trim() === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return title
|
||||||
|
.trim() // Remove leading/trailing whitespace
|
||||||
|
.split(/\s+/) // Split on whitespace
|
||||||
|
.slice(0, 3) // Take first 3 words
|
||||||
|
.join("-") // Join with hyphens
|
||||||
|
.toLowerCase() // Convert to lowercase
|
||||||
|
.replace(/[^a-z0-9-]/g, "") // Remove non-alphanumeric except hyphens
|
||||||
|
.replace(/-+/g, "-") // Replace multiple hyphens with single
|
||||||
|
.replace(/^-|-$/g, ""); // Remove leading/trailing hyphens
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BranchTemplateVariables {
|
||||||
|
prefix: string;
|
||||||
|
entityType: string;
|
||||||
|
entityNumber: number;
|
||||||
|
timestamp: string;
|
||||||
|
sha?: string;
|
||||||
|
label?: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces template variables in a branch name template
|
||||||
|
* Template format: {{variableName}}
|
||||||
|
*/
|
||||||
|
export function applyBranchTemplate(
|
||||||
|
template: string,
|
||||||
|
variables: BranchTemplateVariables,
|
||||||
|
): string {
|
||||||
|
let result = template;
|
||||||
|
|
||||||
|
// Replace each variable
|
||||||
|
Object.entries(variables).forEach(([key, value]) => {
|
||||||
|
const placeholder = `{{${key}}}`;
|
||||||
|
const replacement = value ? String(value) : "";
|
||||||
|
result = result.replaceAll(placeholder, replacement);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a branch name from the provided `template` and set of `variables`. Uses a default format if the template is empty or produces an empty result.
|
||||||
|
*/
|
||||||
|
export function generateBranchName(
|
||||||
|
template: string | undefined,
|
||||||
|
branchPrefix: string,
|
||||||
|
entityType: string,
|
||||||
|
entityNumber: number,
|
||||||
|
sha?: string,
|
||||||
|
label?: string,
|
||||||
|
title?: string,
|
||||||
|
): string {
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
const variables: BranchTemplateVariables = {
|
||||||
|
prefix: branchPrefix,
|
||||||
|
entityType,
|
||||||
|
entityNumber,
|
||||||
|
timestamp: `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}-${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`,
|
||||||
|
sha: sha?.substring(0, 8), // First 8 characters of SHA
|
||||||
|
label: label || entityType, // Fall back to entityType if no label
|
||||||
|
description: title ? extractDescription(title) : undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (template?.trim()) {
|
||||||
|
const branchName = applyBranchTemplate(template, variables);
|
||||||
|
|
||||||
|
// Some templates could produce empty results- validate
|
||||||
|
if (branchName.trim().length > 0) return branchName;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Branch template '${template}' generated empty result, falling back to default format`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const branchName = `${branchPrefix}${entityType}-${entityNumber}-${variables.timestamp}`;
|
||||||
|
// Kubernetes compatible: lowercase, max 50 chars, alphanumeric and hyphens only
|
||||||
|
return branchName.toLowerCase().substring(0, 50);
|
||||||
|
}
|
||||||
232
test/branch-template.test.ts
Normal file
232
test/branch-template.test.ts
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
|
||||||
|
import { describe, it, expect } from "bun:test";
|
||||||
|
import {
|
||||||
|
applyBranchTemplate,
|
||||||
|
generateBranchName,
|
||||||
|
} from "../src/utils/branch-template";
|
||||||
|
|
||||||
|
describe("branch template utilities", () => {
|
||||||
|
describe("applyBranchTemplate", () => {
|
||||||
|
it("should replace all template variables", () => {
|
||||||
|
const template =
|
||||||
|
"{{prefix}}{{entityType}}-{{entityNumber}}-{{timestamp}}";
|
||||||
|
const variables = {
|
||||||
|
prefix: "feat/",
|
||||||
|
entityType: "issue",
|
||||||
|
entityNumber: 123,
|
||||||
|
timestamp: "20240301-1430",
|
||||||
|
sha: "abcd1234",
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = applyBranchTemplate(template, variables);
|
||||||
|
expect(result).toBe("feat/issue-123-20240301-1430");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle custom templates with multiple variables", () => {
|
||||||
|
const template =
|
||||||
|
"{{prefix}}fix/{{entityType}}_{{entityNumber}}_{{timestamp}}_{{sha}}";
|
||||||
|
const variables = {
|
||||||
|
prefix: "claude-",
|
||||||
|
entityType: "pr",
|
||||||
|
entityNumber: 456,
|
||||||
|
timestamp: "20240301-1430",
|
||||||
|
sha: "abcd1234",
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = applyBranchTemplate(template, variables);
|
||||||
|
expect(result).toBe("claude-fix/pr_456_20240301-1430_abcd1234");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle templates with missing variables gracefully", () => {
|
||||||
|
const template = "{{prefix}}{{entityType}}-{{missing}}-{{entityNumber}}";
|
||||||
|
const variables = {
|
||||||
|
prefix: "feat/",
|
||||||
|
entityType: "issue",
|
||||||
|
entityNumber: 123,
|
||||||
|
timestamp: "20240301-1430",
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = applyBranchTemplate(template, variables);
|
||||||
|
expect(result).toBe("feat/issue-{{missing}}-123");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("generateBranchName", () => {
|
||||||
|
it("should use custom template when provided", () => {
|
||||||
|
const template = "{{prefix}}custom-{{entityType}}_{{entityNumber}}";
|
||||||
|
const result = generateBranchName(template, "feature/", "issue", 123);
|
||||||
|
|
||||||
|
expect(result).toBe("feature/custom-issue_123");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use default format when template is empty", () => {
|
||||||
|
const result = generateBranchName("", "claude/", "issue", 123);
|
||||||
|
|
||||||
|
expect(result).toMatch(/^claude\/issue-123-\d{8}-\d{4}$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use default format when template is undefined", () => {
|
||||||
|
const result = generateBranchName(undefined, "claude/", "pr", 456);
|
||||||
|
|
||||||
|
expect(result).toMatch(/^claude\/pr-456-\d{8}-\d{4}$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should preserve custom template formatting (no automatic lowercase/truncation)", () => {
|
||||||
|
const template = "{{prefix}}UPPERCASE_Branch-Name_{{entityNumber}}";
|
||||||
|
const result = generateBranchName(template, "Feature/", "issue", 123);
|
||||||
|
|
||||||
|
expect(result).toBe("Feature/UPPERCASE_Branch-Name_123");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not truncate custom template results", () => {
|
||||||
|
const template =
|
||||||
|
"{{prefix}}very-long-branch-name-that-exceeds-the-maximum-allowed-length-{{entityNumber}}";
|
||||||
|
const result = generateBranchName(template, "feature/", "issue", 123);
|
||||||
|
|
||||||
|
expect(result).toBe(
|
||||||
|
"feature/very-long-branch-name-that-exceeds-the-maximum-allowed-length-123",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply Kubernetes-compatible transformations to default template only", () => {
|
||||||
|
const result = generateBranchName(undefined, "Feature/", "issue", 123);
|
||||||
|
|
||||||
|
expect(result).toMatch(/^feature\/issue-123-\d{8}-\d{4}$/);
|
||||||
|
expect(result.length).toBeLessThanOrEqual(50);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle SHA in template", () => {
|
||||||
|
const template = "{{prefix}}{{entityType}}-{{entityNumber}}-{{sha}}";
|
||||||
|
const result = generateBranchName(
|
||||||
|
template,
|
||||||
|
"fix/",
|
||||||
|
"pr",
|
||||||
|
789,
|
||||||
|
"abcdef123456",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe("fix/pr-789-abcdef12");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use label in template when provided", () => {
|
||||||
|
const template = "{{prefix}}{{label}}/{{entityNumber}}";
|
||||||
|
const result = generateBranchName(
|
||||||
|
template,
|
||||||
|
"feature/",
|
||||||
|
"issue",
|
||||||
|
123,
|
||||||
|
undefined,
|
||||||
|
"bug",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe("feature/bug/123");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fallback to entityType when label template is used but no label provided", () => {
|
||||||
|
const template = "{{prefix}}{{label}}-{{entityNumber}}";
|
||||||
|
const result = generateBranchName(template, "fix/", "pr", 456);
|
||||||
|
|
||||||
|
expect(result).toBe("fix/pr-456");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle template with both label and entityType", () => {
|
||||||
|
const template = "{{prefix}}{{label}}-{{entityType}}_{{entityNumber}}";
|
||||||
|
const result = generateBranchName(
|
||||||
|
template,
|
||||||
|
"dev/",
|
||||||
|
"issue",
|
||||||
|
789,
|
||||||
|
undefined,
|
||||||
|
"enhancement",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe("dev/enhancement-issue_789");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use description in template when provided", () => {
|
||||||
|
const template = "{{prefix}}{{description}}/{{entityNumber}}";
|
||||||
|
const result = generateBranchName(
|
||||||
|
template,
|
||||||
|
"feature/",
|
||||||
|
"issue",
|
||||||
|
123,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
"Fix login bug with OAuth",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe("feature/fix-login-bug/123");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle template with multiple variables including description", () => {
|
||||||
|
const template =
|
||||||
|
"{{prefix}}{{label}}/{{description}}-{{entityType}}_{{entityNumber}}";
|
||||||
|
const result = generateBranchName(
|
||||||
|
template,
|
||||||
|
"dev/",
|
||||||
|
"issue",
|
||||||
|
456,
|
||||||
|
undefined,
|
||||||
|
"bug",
|
||||||
|
"User authentication fails completely",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe("dev/bug/user-authentication-fails-issue_456");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle description with special characters in template", () => {
|
||||||
|
const template = "{{prefix}}{{description}}-{{entityNumber}}";
|
||||||
|
const result = generateBranchName(
|
||||||
|
template,
|
||||||
|
"fix/",
|
||||||
|
"pr",
|
||||||
|
789,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
"Add: User Registration & Email Validation",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe("fix/add-user-registration-789");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle empty description in template", () => {
|
||||||
|
const template = "{{prefix}}{{description}}-{{entityNumber}}";
|
||||||
|
const result = generateBranchName(
|
||||||
|
template,
|
||||||
|
"test/",
|
||||||
|
"issue",
|
||||||
|
101,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe("test/-101");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fallback to default format when template produces empty result", () => {
|
||||||
|
const template = "{{description}}"; // Will be empty if no title provided
|
||||||
|
const result = generateBranchName(template, "claude/", "issue", 123);
|
||||||
|
|
||||||
|
expect(result).toMatch(/^claude\/issue-123-\d{8}-\d{4}$/);
|
||||||
|
expect(result.length).toBeLessThanOrEqual(50);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fallback to default format when template produces only whitespace", () => {
|
||||||
|
const template = " {{description}} "; // Will be " " if description is empty
|
||||||
|
const result = generateBranchName(
|
||||||
|
template,
|
||||||
|
"fix/",
|
||||||
|
"pr",
|
||||||
|
456,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toMatch(/^fix\/pr-456-\d{8}-\d{4}$/);
|
||||||
|
expect(result.length).toBeLessThanOrEqual(50);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -61,6 +61,7 @@ describe("generatePrompt", () => {
|
|||||||
body: "This is a test PR",
|
body: "This is a test PR",
|
||||||
author: { login: "testuser" },
|
author: { login: "testuser" },
|
||||||
state: "OPEN",
|
state: "OPEN",
|
||||||
|
labels: { nodes: [] },
|
||||||
createdAt: "2023-01-01T00:00:00Z",
|
createdAt: "2023-01-01T00:00:00Z",
|
||||||
additions: 15,
|
additions: 15,
|
||||||
deletions: 5,
|
deletions: 5,
|
||||||
@@ -475,6 +476,7 @@ describe("generatePrompt", () => {
|
|||||||
body: "The login form is not working",
|
body: "The login form is not working",
|
||||||
author: { login: "testuser" },
|
author: { login: "testuser" },
|
||||||
state: "OPEN",
|
state: "OPEN",
|
||||||
|
labels: { nodes: [] },
|
||||||
createdAt: "2023-01-01T00:00:00Z",
|
createdAt: "2023-01-01T00:00:00Z",
|
||||||
comments: {
|
comments: {
|
||||||
nodes: [],
|
nodes: [],
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ describe("formatContext", () => {
|
|||||||
additions: 50,
|
additions: 50,
|
||||||
deletions: 30,
|
deletions: 30,
|
||||||
state: "OPEN",
|
state: "OPEN",
|
||||||
|
labels: {
|
||||||
|
nodes: [],
|
||||||
|
},
|
||||||
commits: {
|
commits: {
|
||||||
totalCount: 3,
|
totalCount: 3,
|
||||||
nodes: [],
|
nodes: [],
|
||||||
@@ -63,6 +66,9 @@ Changed Files: 2 files`,
|
|||||||
author: { login: "test-user" },
|
author: { login: "test-user" },
|
||||||
createdAt: "2023-01-01T00:00:00Z",
|
createdAt: "2023-01-01T00:00:00Z",
|
||||||
state: "OPEN",
|
state: "OPEN",
|
||||||
|
labels: {
|
||||||
|
nodes: [],
|
||||||
|
},
|
||||||
comments: {
|
comments: {
|
||||||
nodes: [],
|
nodes: [],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -161,9 +218,11 @@ describe("Agent Mode", () => {
|
|||||||
|
|
||||||
// Note: We can't easily test file creation in this unit test,
|
// Note: We can't easily test file creation in this unit test,
|
||||||
// but we can verify the method completes without errors
|
// but we can verify the method completes without errors
|
||||||
// Agent mode now includes MCP config even with empty user args
|
// With our conditional MCP logic, agent mode with no allowed tools
|
||||||
|
// should not include any MCP config
|
||||||
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");
|
// Should be empty or just whitespace when no MCP servers are included
|
||||||
|
expect(callArgs[1]).not.toContain("--mcp-config");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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 /,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -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", () => {
|
||||||
|
|||||||
@@ -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]",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user