[DISCUSSION] Reconsider src/ directory requirement? #51
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Codebase Agent (agentready-dev) | |
| on: | |
| issue_comment: | |
| types: [created] | |
| pull_request_review_comment: | |
| types: [created] | |
| issues: | |
| types: [opened, assigned] | |
| pull_request_review: | |
| types: [submitted] | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| id-token: write | |
| actions: read | |
| jobs: | |
| agentready-dev: | |
| # Trigger on @agentready-dev mentions in issues, PRs, and comments | |
| if: | | |
| (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@agentready-dev')) || | |
| (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@agentready-dev')) || | |
| (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@agentready-dev')) || | |
| (github.event_name == 'issues' && (contains(github.event.issue.body, '@agentready-dev') || contains(github.event.issue.title, '@agentready-dev'))) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Determine event context | |
| id: event-context | |
| run: | | |
| EVENT_NAME="${{ github.event_name }}" | |
| # Determine if this is a PR-related event and get the PR number | |
| { | |
| if [ "$EVENT_NAME" == "pull_request_review_comment" ]; then | |
| # pull_request_review_comment events have github.event.pull_request | |
| PR_NUMBER="${{ github.event.pull_request.number }}" | |
| echo "is_pr=true" | |
| echo "pr_number=$PR_NUMBER" | |
| echo "issue_number=$PR_NUMBER" | |
| elif [ "$EVENT_NAME" == "pull_request_review" ]; then | |
| # pull_request_review events have github.event.pull_request | |
| PR_NUMBER="${{ github.event.pull_request.number }}" | |
| echo "is_pr=true" | |
| echo "pr_number=$PR_NUMBER" | |
| echo "issue_number=$PR_NUMBER" | |
| elif [ "$EVENT_NAME" == "issue_comment" ]; then | |
| # issue_comment events have github.event.issue | |
| ISSUE_NUMBER="${{ github.event.issue.number }}" | |
| # Check if this issue is actually a PR by checking if pull_request.url exists | |
| # GitHub Actions will return empty string if pull_request doesn't exist | |
| PULL_REQUEST_URL="${{ github.event.issue.pull_request.url || '' }}" | |
| if [ -n "$PULL_REQUEST_URL" ] && [ "$PULL_REQUEST_URL" != "null" ] && [ "$PULL_REQUEST_URL" != "undefined" ]; then | |
| echo "is_pr=true" | |
| echo "pr_number=$ISSUE_NUMBER" | |
| else | |
| echo "is_pr=false" | |
| fi | |
| echo "issue_number=$ISSUE_NUMBER" | |
| elif [ "$EVENT_NAME" == "issues" ]; then | |
| # issues events have github.event.issue | |
| ISSUE_NUMBER="${{ github.event.issue.number }}" | |
| # Check if this issue is actually a PR by checking if pull_request.url exists | |
| # GitHub Actions will return empty string if pull_request doesn't exist | |
| PULL_REQUEST_URL="${{ github.event.issue.pull_request.url || '' }}" | |
| if [ -n "$PULL_REQUEST_URL" ] && [ "$PULL_REQUEST_URL" != "null" ] && [ "$PULL_REQUEST_URL" != "undefined" ]; then | |
| echo "is_pr=true" | |
| echo "pr_number=$ISSUE_NUMBER" | |
| else | |
| echo "is_pr=false" | |
| fi | |
| echo "issue_number=$ISSUE_NUMBER" | |
| else | |
| echo "is_pr=false" | |
| echo "issue_number=" | |
| fi | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Get PR info for fork support | |
| if: steps.event-context.outputs.is_pr == 'true' | |
| id: pr-info | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| REPOSITORY: ${{ github.repository }} | |
| PR_NUMBER: ${{ steps.event-context.outputs.pr_number }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| PR_HEAD_SHA_INPUT: ${{ github.event.pull_request.head.sha }} | |
| PR_HEAD_REF_INPUT: ${{ github.event.pull_request.head.ref }} | |
| PR_HEAD_OWNER_INPUT: ${{ github.event.pull_request.head.repo.owner.login }} | |
| PR_HEAD_REPO_INPUT: ${{ github.event.pull_request.head.repo.name }} | |
| PR_HEAD_FORK_INPUT: ${{ github.event.pull_request.head.repo.fork }} | |
| run: | | |
| # For pull_request_review_comment and pull_request_review events, we have direct access | |
| if [ "$EVENT_NAME" == "pull_request_review_comment" ] || [ "$EVENT_NAME" == "pull_request_review" ]; then | |
| PR_HEAD_SHA="$PR_HEAD_SHA_INPUT" | |
| PR_HEAD_REF="$PR_HEAD_REF_INPUT" | |
| PR_HEAD_OWNER="$PR_HEAD_OWNER_INPUT" | |
| PR_HEAD_REPO="$PR_HEAD_REPO_INPUT" | |
| IS_FORK="$PR_HEAD_FORK_INPUT" | |
| else | |
| # For issue_comment and issues events on PRs, fetch from API | |
| PR_DATA=$(gh api "repos/$REPOSITORY/pulls/$PR_NUMBER") | |
| PR_HEAD_SHA=$(echo "$PR_DATA" | jq -r '.head.sha') | |
| PR_HEAD_REF=$(echo "$PR_DATA" | jq -r '.head.ref') | |
| PR_HEAD_OWNER=$(echo "$PR_DATA" | jq -r '.head.repo.owner.login') | |
| PR_HEAD_REPO=$(echo "$PR_DATA" | jq -r '.head.repo.name') | |
| IS_FORK=$(echo "$PR_DATA" | jq -r '.head.repo.fork') | |
| fi | |
| { | |
| echo "pr_head_owner=$PR_HEAD_OWNER" | |
| echo "pr_head_repo=$PR_HEAD_REPO" | |
| echo "pr_head_ref=$PR_HEAD_REF" | |
| echo "pr_head_sha=$PR_HEAD_SHA" | |
| echo "is_fork=$IS_FORK" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Extract user request | |
| id: extract-request | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| COMMENT_BODY: ${{ github.event.comment.body || '' }} | |
| REVIEW_BODY: ${{ github.event.review.body || '' }} | |
| ISSUE_BODY: ${{ github.event.issue.body || '' }} | |
| ISSUE_TITLE: ${{ github.event.issue.title || '' }} | |
| run: | | |
| case "$EVENT_NAME" in | |
| issue_comment|pull_request_review_comment) | |
| REQUEST="$COMMENT_BODY" | |
| ;; | |
| pull_request_review) | |
| REQUEST="$REVIEW_BODY" | |
| ;; | |
| issues) | |
| # Use body if available, otherwise use title | |
| if [ -n "$ISSUE_BODY" ]; then | |
| REQUEST="$ISSUE_BODY" | |
| else | |
| REQUEST="$ISSUE_TITLE" | |
| fi | |
| ;; | |
| *) | |
| REQUEST="No request body found" | |
| ;; | |
| esac | |
| # Remove @agentready-dev mention from the request | |
| REQUEST=$(echo "$REQUEST" | sed 's/@agentready-dev//g' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') | |
| { | |
| echo "request<<EOF" | |
| echo "$REQUEST" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Determine checkout ref | |
| id: checkout-ref | |
| run: | | |
| if [ "${{ steps.event-context.outputs.is_pr }}" == "true" ]; then | |
| if [ "${{ steps.pr-info.outputs.is_fork }}" == "true" ]; then | |
| # Fork: checkout the fork repository at the PR branch | |
| echo "repository=${{ steps.pr-info.outputs.pr_head_owner }}/${{ steps.pr-info.outputs.pr_head_repo }}" >> "$GITHUB_OUTPUT" | |
| echo "ref=${{ steps.pr-info.outputs.pr_head_ref }}" >> "$GITHUB_OUTPUT" | |
| else | |
| # Same repo PR: checkout at PR head SHA | |
| echo "repository=${{ github.repository }}" >> "$GITHUB_OUTPUT" | |
| echo "ref=${{ steps.pr-info.outputs.pr_head_sha }}" >> "$GITHUB_OUTPUT" | |
| fi | |
| else | |
| # Regular issue: checkout default branch (checkout action will use default if ref is empty) | |
| echo "repository=${{ github.repository }}" >> "$GITHUB_OUTPUT" | |
| # github.ref may not be set for issue events, so leave ref empty to use default branch | |
| if [ -n "${{ github.ref }}" ] && [ "${{ github.ref }}" != "null" ]; then | |
| echo "ref=${{ github.ref }}" >> "$GITHUB_OUTPUT" | |
| fi | |
| # If ref is empty, checkout action will use repository's default branch | |
| fi | |
| - name: Checkout repository (fork-compatible) | |
| uses: actions/checkout@v6 | |
| with: | |
| repository: ${{ steps.checkout-ref.outputs.repository }} | |
| ref: ${{ steps.checkout-ref.outputs.ref || '' }} | |
| fetch-depth: 0 | |
| - name: Debug event context | |
| run: | | |
| echo "Event name: ${{ github.event_name }}" | |
| echo "Issue number from step: ${{ steps.event-context.outputs.issue_number }}" | |
| echo "Is PR: ${{ steps.event-context.outputs.is_pr }}" | |
| echo "PR number: ${{ steps.event-context.outputs.pr_number }}" | |
| echo "GitHub context issue: ${{ github.event.issue.number }}" | |
| - name: Claude Code Action | |
| id: claude-code | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| prompt: | | |
| You are responding as the @agentready-dev agent. Please analyze the request and post your response as a comment on this issue/PR. | |
| Request: ${{ steps.extract-request.outputs.request }} | |
| Important: Make sure to post your response as a comment and clearly indicate you are responding as the @agentready-dev agent. | |
| - name: Debug event context | |
| run: | | |
| echo "Event name: ${{ github.event_name }}" | |
| echo "Issue number from step: ${{ steps.event-context.outputs.issue_number }}" | |
| echo "Is PR: ${{ steps.event-context.outputs.is_pr }}" | |
| echo "PR number: ${{ steps.event-context.outputs.pr_number }}" | |
| echo "GitHub context issue: ${{ github.event.issue.number }}" | |
| echo "GitHub context issue (alt): ${{ github.event.issue.number || 'not set' }}" | |
| - name: Post @agentready-dev response | |
| uses: actions/github-script@v8 | |
| env: | |
| ISSUE_NUMBER: ${{ steps.event-context.outputs.issue_number }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| with: | |
| script: | | |
| const issueNumber = process.env.ISSUE_NUMBER; | |
| const eventName = process.env.EVENT_NAME; | |
| console.log('ISSUE_NUMBER from env:', issueNumber); | |
| console.log('EVENT_NAME from env:', eventName); | |
| console.log('Event name from context:', context.eventName); | |
| console.log('Context payload keys:', Object.keys(context.payload || {})); | |
| // Get the issue/PR number from context | |
| // For issue_comment events, context.issue.number is the most reliable | |
| let targetNumber = issueNumber; | |
| // Try multiple fallback methods | |
| if (!targetNumber || targetNumber === '' || targetNumber === 'undefined') { | |
| console.log('Trying fallback methods...'); | |
| // Method 1: context.issue.number (most reliable for issue_comment events) | |
| if (context.issue && context.issue.number) { | |
| targetNumber = context.issue.number; | |
| console.log('Found from context.issue.number:', targetNumber); | |
| } | |
| // Method 2: context.payload.issue.number | |
| else if (context.payload && context.payload.issue && context.payload.issue.number) { | |
| targetNumber = context.payload.issue.number; | |
| console.log('Found from context.payload.issue.number:', targetNumber); | |
| } | |
| // Method 3: context.payload.pull_request.number (for PR comments) | |
| else if (context.payload && context.payload.pull_request && context.payload.pull_request.number) { | |
| targetNumber = context.payload.pull_request.number; | |
| console.log('Found from context.payload.pull_request.number:', targetNumber); | |
| } | |
| // Method 4: Extract from comment issue_url | |
| else if (context.payload && context.payload.comment && context.payload.comment.issue_url) { | |
| const urlParts = context.payload.comment.issue_url.split('/'); | |
| targetNumber = urlParts[urlParts.length - 1]; | |
| console.log('Found from comment.issue_url:', targetNumber); | |
| } | |
| } | |
| // Convert to number if it's a string | |
| if (targetNumber) { | |
| targetNumber = parseInt(targetNumber, 10); | |
| } | |
| console.log('Final targetNumber:', targetNumber); | |
| if (!targetNumber || isNaN(targetNumber)) { | |
| console.log('ERROR: Could not determine issue/PR number'); | |
| console.log('Available context keys:', Object.keys(context)); | |
| console.log('Available payload keys:', Object.keys(context.payload || {})); | |
| throw new Error('Could not determine issue/PR number'); | |
| } | |
| // Wait a moment for Claude Code Action to post its comment | |
| console.log('Waiting 3 seconds for Claude Code Action to post comment...'); | |
| await new Promise(resolve => setTimeout(resolve, 3000)); | |
| // Get recent comments | |
| console.log('Fetching comments for issue/PR:', targetNumber); | |
| let comments; | |
| try { | |
| comments = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: targetNumber, | |
| }); | |
| console.log(`Found ${comments.data.length} total comments`); | |
| } catch (error) { | |
| console.error('Error fetching comments:', error); | |
| throw error; | |
| } | |
| // Find the most recent comment from github-actions[bot] (Claude Code Action) | |
| const recentComments = comments.data | |
| .filter(comment => { | |
| const commentTime = new Date(comment.created_at); | |
| const twoMinutesAgo = new Date(Date.now() - 2 * 60 * 1000); | |
| return commentTime > twoMinutesAgo; | |
| }) | |
| .sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); | |
| console.log(`Found ${recentComments.length} recent comments (within 2 minutes)`); | |
| const claudeComment = recentComments.find(comment => | |
| comment.user.login === 'github-actions[bot]' && | |
| !comment.body.includes('@agentready-dev') | |
| ); | |
| if (claudeComment) { | |
| console.log('Found Claude Code Action comment, updating with attribution...'); | |
| // Update Claude's comment to add @agentready-dev attribution | |
| const updatedBody = `🤖 **Response from @agentready-dev agent:**\n\n---\n\n${claudeComment.body}\n\n---\n*This response was generated by the @agentready-dev workflow.*`; | |
| try { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: claudeComment.id, | |
| body: updatedBody | |
| }); | |
| console.log('Successfully updated Claude comment with attribution'); | |
| } catch (error) { | |
| console.error('Error updating comment:', error); | |
| throw error; | |
| } | |
| } else { | |
| console.log('No Claude comment found, posting status comment...'); | |
| // If no comment from Claude, post our own status comment | |
| const jobStatus = '${{ job.status }}' === 'success' ? '✅' : '❌'; | |
| const statusText = '${{ job.status }}' === 'success' ? 'completed' : 'failed'; | |
| const body = `🤖 **@agentready-dev Agent**\n\n` + | |
| `${jobStatus} Analysis ${statusText}.\n\n` + | |
| `The @agentready-dev agent has processed your request. ` + | |
| `Please check the [workflow logs](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${{ github.run_id }}) for details.\n\n` + | |
| `---\n` + | |
| `*This comment was automatically posted by the @agentready-dev workflow.*`; | |
| try { | |
| const result = await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: targetNumber, | |
| body: body | |
| }); | |
| console.log('Successfully posted comment:', result.data.html_url); | |
| } catch (error) { | |
| console.error('Error creating comment:', error); | |
| console.error('Error details:', JSON.stringify(error, null, 2)); | |
| throw error; | |
| } | |
| } |