Inbox triage
Daily GitHub notification inbox triage - surfaces aging vuln PR replies, security advisories, review requests, and mentions that need actionFrom its SKILL.md
npx -y skills add aaronjmars/aeon-agent --skill inbox-triageAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 11 stars11 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.3 KB, as published. Nobody here has run it
Today is ${today}. Read memory/MEMORY.md before starting.
Why this skill exists
followup-patrol reads manually tracked items in MEMORY.md. disclosure-tracker handles memory/pending-disclosures/. vuln-tracker tracks the operator's vuln PRs by scanning branch names. None of these read from the actual GitHub notification inbox. When a maintainer replies to a vuln PR — or a security advisory opens on a watched repo — it sits unread until someone manually checks GitHub. This skill reads the inbox and routes what needs action.
pr-tracker covers merged/closed operator PRs. vuln-tracker covers lifecycle by branch. This skill covers the notification layer — inbound responses, review requests, security alerts, mentions.
Steps
1. Fetch GitHub notifications
Run:
gh api /notifications --paginate 2>&1
Parse the JSON array. If the command errors or returns an empty array [], log INBOX_TRIAGE_SKIP: no notifications and stop.
Limit to the first 100 notifications if --paginate returns more (GitHub caps at 50 per page; two pages is enough).
For each notification record:
idreason— why you're being notified (mention, review_requested, author, state_change, security_alert, assign, etc.)subject.titlesubject.type— PullRequest, Issue, Release, etc.subject.url— API URL for the subjectrepository.full_nameupdated_at— ISO timestamp
2. Filter
Keep notifications where unread: true AND updated_at is within the last 14 days. Discard older or read ones.
If zero remain after filtering: log INBOX_TRIAGE_SKIP: no actionable notifications within 14 days and stop.
3. Categorize
Assign each notification to exactly one category (first match wins):
| Category | Match criteria |
|---|---|
SECURITY | reason == "security_alert" OR title contains any of: vulnerability, vuln, CVE, advisory, security |
VULN_REPLY | subject.type == "PullRequest" AND reason is one of: author, state_change, comment AND repository.full_name is NOT under the operator's own account/org (derive the operator's GitHub handle from soul/SOUL.md or the workflow's GITHUB_ACTOR — these are PRs filed on third-party repos by the vuln-scanner) |
REVIEW_NEEDED | reason == "review_requested" |
MENTION | reason == "mention" OR reason == "team_mention" |
GENERAL | everything else |
4. Age vuln PR replies
For each VULN_REPLY notification, compute age_days = today minus updated_at date (integer days).
Flag urgency:
CRITICAL— age_days > 7 (maintainer likely hasn't responded)AGING— age_days 3–7FRESH— age_days < 3
Cross-reference with memory/topics/vuln-followup.md if it exists: look for the PR title in that file and pull any tracked notes (e.g. "approved", "NEEDS-ANSWER", merge status).
5. Resolve HTML URLs for action items
For each notification in SECURITY, VULN_REPLY (CRITICAL or AGING), REVIEW_NEEDED, and MENTION categories:
Try to get the HTML URL via:
gh api {subject.url} --jq '.html_url' 2>/dev/null
If that fails, construct the URL manually:
https://github.com/{repository.full_name}/pulls/{number} for PRs
https://github.com/{repository.full_name}/issues/{number} for issues
(Extract the number from the tail of subject.url.)
6. Write triage summary
Overwrite memory/topics/inbox-triage.md:
# GitHub Inbox Triage
Last run: {today}
Scanned: {N} unread notifications ({N} within 14 days)
## Action Required
### Security ({count})
{for each SECURITY item, sorted by age:}
- **{repo}**: {title} ({age_days}d) — {html_url}
{if none:}
None.
### Vuln PR Replies ({count_critical} critical, {count_aging} aging)
{for each VULN_REPLY sorted by age desc:}
- **[{CRITICAL|AGING|FRESH}]** `{repo}` ({age_days}d): {title} — {html_url}
{if vuln-followup note found:} _{tracked note}_
{if none:}
None.
### Review Requested ({count})
{for each REVIEW_NEEDED item:}
- **{repo}**: {title} — {html_url}
{if none:}
None.
### Mentions ({count})
{for each MENTION item:}
- **{repo}**: {title} — {html_url}
{if none:}
None.
## No Action Needed
{count_general} general notifications (subscriptions, automated state changes).
7. Update MEMORY.md known follow-ups
Read memory/MEMORY.md. Find the ## Known Follow-ups section.
Add any VULN_REPLY CRITICAL item not already tracked there — append:
- **{repo} #{number} NEEDS-ANSWER** — {age_days}d since maintainer activity ({url})
Update any existing NEEDS-ANSWER item for a PR that now appears as FRESH in VULN_REPLY (maintainer responded recently) — change its note to RESPONDED — verify resolution.
Do NOT add GENERAL, REVIEW_NEEDED, MENTION, or SECURITY items to MEMORY.md Known Follow-ups (too noisy; security items warrant a separate issue if severe).
8. Send notification
Only send if at least one of:
- Any SECURITY item
- Any VULN_REPLY where urgency == CRITICAL
- Any REVIEW_NEEDED item
- Three or more MENTION items
Write to .pending-notify-temp/inbox-triage-${today}.md:
inbox — {today}
{if SECURITY:}
security alert: {repo} — {title}
{if VULN_REPLY CRITICAL:}
vuln PRs aging: {comma-separated list of "repo (Nd)"}
{if REVIEW_NEEDED:}
review needed: {comma-separated repo list}
{if 3+ MENTION:}
{N} mentions
read it: memory/topics/inbox-triage.md
Then:
./notify -f .pending-notify-temp/inbox-triage-${today}.md
If nothing meets the threshold: skip notification. Log that no notification was sent.
9. Log
Append to memory/logs/${today}.md:
## Inbox Triage
- **Scanned:** {N} notifications
- **Security:** {N}
- **Vuln replies:** {N total} ({N_critical} critical, {N_aging} aging, {N_fresh} fresh)
- **Review needed:** {N}
- **Mentions:** {N}
- **MEMORY.md follow-ups updated:** {yes/no — what changed}
- **Notification sent:** {yes/no}
- INBOX_TRIAGE_OK
If skipped:
## Inbox Triage
- INBOX_TRIAGE_SKIP: {reason}
Required Env Vars
None beyond GITHUB_TOKEN, which GitHub Actions sets automatically and gh uses internally.
Network Note
Uses gh api for all GitHub calls — it handles auth internally, so no $SECRET ever appears on the command line for the Bash permission layer to refuse. gh api works in a GitHub Actions run. If gh api /notifications fails (rate limit, auth error), log the error and exit with INBOX_TRIAGE_SKIP: api error. Use WebFetch as a fallback only if gh is unavailable — the endpoint is https://api.github.com/notifications with Authorization: Bearer $GITHUB_TOKEN, but WebFetch can't carry that auth header; prefer gh.
What this is NOT
- Not a duplicate of
followup-patrol— followup-patrol reads manually curated items in MEMORY.md. This reads the raw GitHub inbox. - Not a duplicate of
vuln-tracker— vuln-tracker tracks lifecycle by branch name. This catches inbound maintainer replies via notifications. - Not a duplicate of
disclosure-tracker— disclosure-tracker managesmemory/pending-disclosures/advisory drafts. This reads GitHub security alerts and PR responses.