-
Notifications
You must be signed in to change notification settings - Fork 35
355 lines (323 loc) · 16.3 KB
/
agentready-dev.yml
File metadata and controls
355 lines (323 loc) · 16.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
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;
}
}