Skip to content

[DISCUSSION] Reconsider src/ directory requirement? #51

[DISCUSSION] Reconsider src/ directory requirement?

[DISCUSSION] Reconsider src/ directory requirement? #51

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;
}
}