Skip to content

Commit 7b58e43

Browse files
committed
feat(examples): let AI agent analyze comments to identify resolved issues before handling 30-day inactivity
1 parent 3d7b3cd commit 7b58e43

2 files changed

Lines changed: 57 additions & 106 deletions

File tree

examples/workflows/issue-cleanup/gemini-issue-cleanup.toml

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,65 @@ You are an automated triage bot for the `!{echo $REPOSITORY}` repository. Your j
44
55
## Critical Constraints
66
1. **NO META-ANALYSIS**: DO NOT read local files in the repository you are running in (like `package.json`, `README.md`, or `.github/`). Only read files within the `target-repo/` folder you clone in Step 1.
7-
2. **EFFICIENCY**: If the issue is too vague, close it immediately and stop. Do not investigate code for vague issues.
7+
2. **EFFICIENCY**: If the issue is too vague or inactive, close/comment immediately and stop. Do not investigate code.
88
99
## Task Lifecycle
1010
11-
### Step 1: Setup & Vagueness Check
11+
### Step 1: Setup & Comment Analysis
1212
- Run `git clone https://github.com/!{echo $REPOSITORY}.git target-repo`
13-
- Run `gh issue view !{echo $ISSUE_NUMBER} --repo !{echo $REPOSITORY} --json title,body,author,comments`
14-
- **Vagueness Check**: If the issue description is fundamentally missing context (no logs, no repro steps, just "it's broken") AND no one has asked for more information yet:
13+
- Run `gh issue view !{echo $ISSUE_NUMBER} --repo !{echo $REPOSITORY} --json title,body,author,comments,labels,assignees`
14+
15+
**Resolution Check (Priority 1):**
16+
- Read the comments. Does the latest comment indicate that the issue has been fixed, resolved, or is functioning properly?
17+
- If YES: `gh issue close !{echo $ISSUE_NUMBER} --comment "Closing because the latest comments indicate this issue has been resolved. Feel free to reopen if the problem persists." --reason "completed" --repo !{echo $REPOSITORY}`
18+
- **STOP EXECUTION IMMEDIATELY**.
19+
20+
**Inactivity Check (Priority 2):**
21+
- Is `!{echo $INACTIVE_OVER_30_DAYS}` equal to `true`?
22+
- If YES (and it was not resolved above): Execute the following bash script exactly as written to handle the inactivity.
23+
```bash
24+
ISSUE_JSON=$(gh issue view !{echo $ISSUE_NUMBER} --repo !{echo $REPOSITORY} --json author,labels,assignees)
25+
REPORTER=$(echo "$ISSUE_JSON" | jq -r '.author.login')
26+
HAS_ASSIGNEES=$(echo "$ISSUE_JSON" | jq '.assignees | length > 0')
27+
ASSIGNEE_MENTIONS=$(echo "$ISSUE_JSON" | jq -r '.assignees | map("@" + .login) | join(" ")')
28+
IS_HIGH_PRIORITY=$(echo "$ISSUE_JSON" | jq '.labels | map(.name | ascii_downcase) | any(. == "priority/p0" or . == "priority/p1" or . == "p0" or . == "p1")')
29+
IS_FEATURE=$(echo "$ISSUE_JSON" | jq '.labels | map(.name | ascii_downcase) | any(contains("feature") or contains("enhancement"))')
30+
31+
gh issue edit !{echo $ISSUE_NUMBER} --remove-label "status/need-triage" --repo !{echo $REPOSITORY} 2>/dev/null || true
32+
gh issue edit !{echo $ISSUE_NUMBER} --add-label "status/needs-info" --repo !{echo $REPOSITORY} 2>/dev/null || true
33+
34+
COMMENT=""
35+
if [ "$IS_FEATURE" = "true" ]; then
36+
COMMENT="@${REPORTER}, this feature request hasn't been updated in over a month. Please reopen if this is still needed."
37+
else
38+
COMMENT="@${REPORTER}, this issue hasn't been updated in over a month. Could you please try reproducing it with the latest build? If it still occurs, please provide detailed reproduction steps."
39+
fi
40+
41+
if [ "$IS_HIGH_PRIORITY" = "false" ]; then
42+
if [ "$HAS_ASSIGNEES" = "true" ]; then
43+
COMMENT="${COMMENT}\n\n${ASSIGNEE_MENTIONS}, please reopen if you are actively working on this."
44+
elif [ "$IS_FEATURE" = "false" ]; then
45+
COMMENT="${COMMENT} Feel free to reopen this issue."
46+
fi
47+
else
48+
if [ "$HAS_ASSIGNEES" = "true" ]; then
49+
COMMENT="${COMMENT}\n\n${ASSIGNEE_MENTIONS}, checking in on this high priority issue."
50+
fi
51+
fi
52+
53+
gh issue comment !{echo $ISSUE_NUMBER} --body "$COMMENT" --repo !{echo $REPOSITORY}
54+
55+
if [ "$IS_HIGH_PRIORITY" = "false" ]; then
56+
gh issue close !{echo $ISSUE_NUMBER} --reason "not planned" --repo !{echo $REPOSITORY}
57+
fi
58+
```
59+
- **STOP EXECUTION IMMEDIATELY**.
60+
61+
**Vagueness Check (Priority 3):**
62+
- If the issue is NOT inactive over 30 days: Is the issue description fundamentally missing context (no logs, no repro steps, just "it's broken") AND no one has asked for more information yet?
1563
- Ask the reporter: `gh issue comment !{echo $ISSUE_NUMBER} --body "@<reporter_username>, thank you for the report! Could you please provide more specific details (e.g., reproduction steps, expected behavior, and environment)? Closing this as vague if no response is received in a week." --repo !{echo $REPOSITORY}`
1664
- **STOP EXECUTION IMMEDIATELY**.
17-
- If the issue is clear or the reporter has provided info, proceed to Step 2.
65+
- If the issue is clear, proceed to Step 2.
1866
1967
### Step 2: Reproduction & Code Validity Check
2068
- If the issue describes a reproducible bug (e.g., a runtime error, unexpected output, or terminal behavior), you should write and execute a minimal test script using `node` to verify if the bug still occurs in the `target-repo/` codebase.

examples/workflows/issue-cleanup/gemini-issue-cleanup.yml

Lines changed: 4 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -198,110 +198,12 @@ jobs:
198198
console.log(`Issue was last updated ${lastUpdateDaysAgo.toFixed(1)} days ago.`);
199199
200200
if (lastUpdateDaysAgo > 30) {
201-
// Check if the last comment says it's fixed
202-
if (comments.length > 0) {
203-
const lastCommentBody = comments[comments.length - 1].body.toLowerCase();
204-
const resolvedKeywords = ['functioning properly', 'works now', 'is fixed', 'resolved now'];
205-
if (resolvedKeywords.some(kw => lastCommentBody.includes(kw))) {
206-
console.log(`Issue appears resolved in the last comment. Closing.`);
207-
await github.rest.issues.createComment({
208-
owner: context.repo.owner,
209-
repo: context.repo.repo,
210-
issue_number: issueNumber,
211-
body: "Closing because the latest comments indicate this issue has been resolved. Feel free to reopen if the problem persists."
212-
});
213-
await github.rest.issues.update({
214-
owner: context.repo.owner,
215-
repo: context.repo.repo,
216-
issue_number: issueNumber,
217-
state: 'closed',
218-
state_reason: 'completed'
219-
});
220-
core.setOutput('is_stale', 'true');
221-
return;
222-
}
223-
}
224-
225-
console.log(`Issue has been inactive for over a month. Asking for repro.`);
226-
227-
try {
228-
await github.rest.issues.removeLabel({
229-
owner: context.repo.owner,
230-
repo: context.repo.repo,
231-
issue_number: issueNumber,
232-
name: 'status/need-triage'
233-
});
234-
} catch (e) {
235-
// Ignore if label doesn't exist
236-
}
237-
238-
try {
239-
await github.rest.issues.addLabels({
240-
owner: context.repo.owner,
241-
repo: context.repo.repo,
242-
issue_number: issueNumber,
243-
labels: ['status/needs-info']
244-
});
245-
} catch (e) {
246-
console.log('Failed to add needs-info label:', e.message);
247-
}
248-
249-
const assignees = issue.assignees.map(a => a.login);
250-
const hasAssignees = assignees.length > 0;
251-
const assigneeMentions = hasAssignees ? assignees.map(a => `@${a}`).join(' ') : '';
252-
253-
const isHighPriority = issue.labels.some(l => {
254-
const name = typeof l === 'string' ? l : l.name;
255-
const lowerName = name.toLowerCase();
256-
return lowerName === 'priority/p0' || lowerName === 'priority/p1' || lowerName === 'p0' || lowerName === 'p1';
257-
});
258-
259-
const isFeatureRequest = issue.labels.some(l => {
260-
const name = typeof l === 'string' ? l : l.name;
261-
const lowerName = name.toLowerCase();
262-
return lowerName.includes('feature') || lowerName.includes('enhancement');
263-
});
264-
265-
let commentBody = '';
266-
if (isFeatureRequest) {
267-
commentBody = `@${reporter}, this feature request hasn't been updated in over a month. Please reopen if this is still needed.`;
268-
} else {
269-
commentBody = `@${reporter}, this issue hasn't been updated in over a month. Could you please try reproducing it with the latest build? If it still occurs, please provide detailed reproduction steps.`;
270-
}
271-
272-
if (!isHighPriority) {
273-
if (hasAssignees) {
274-
commentBody += `\n\n${assigneeMentions}, please reopen if you are actively working on this.`;
275-
} else if (!isFeatureRequest) {
276-
commentBody += ` Feel free to reopen this issue.`;
277-
}
278-
} else {
279-
if (hasAssignees) {
280-
commentBody += `\n\n${assigneeMentions}, checking in on this high priority issue.`;
281-
}
282-
}
283-
284-
await github.rest.issues.createComment({
285-
owner: context.repo.owner,
286-
repo: context.repo.repo,
287-
issue_number: issueNumber,
288-
body: commentBody
289-
});
290-
291-
if (!isHighPriority) {
292-
await github.rest.issues.update({
293-
owner: context.repo.owner,
294-
repo: context.repo.repo,
295-
issue_number: issueNumber,
296-
state: 'closed',
297-
state_reason: 'not_planned'
298-
});
299-
}
300-
301-
core.setOutput('is_stale', 'true');
201+
core.setOutput('inactive_over_30_days', 'true');
202+
core.setOutput('is_stale', 'false'); // Let AI handle it
302203
return;
303204
}
304205
206+
core.setOutput('inactive_over_30_days', 'false');
305207
core.setOutput('is_stale', 'false');
306208
307209
- name: 'Checkout Current Repository'
@@ -314,6 +216,7 @@ jobs:
314216
env:
315217
ISSUE_NUMBER: '${{ matrix.issue_number }}'
316218
REPOSITORY: '${{ github.repository }}'
219+
INACTIVE_OVER_30_DAYS: '${{ steps.staleness.outputs.inactive_over_30_days }}'
317220
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN || github.token }}'
318221
CUSTOM_INSTRUCTIONS: 'State whether the issue should be categorized as **Maintainer-only** (epic, core architecture, sensitive fixes, internal tasks, or issues requiring deep investigation) or **Help-wanted** (good for community, general bugs, features, or tasks ready for external help). Your comment should be brief and clearly explain *why* it fits that category.'
319222
with:

0 commit comments

Comments
 (0)