inbox-triage
Daily GitHub notification inbox triage - surfaces aging vuln PR replies, security advisories, review requests, and mentions that need action
git clone --depth 1 https://github.com/aeonfun/aeon /tmp/inbox-triage && cp -r /tmp/inbox-triage/skills/inbox-triage ~/.claude/skills/inbox-triageSKILL.md
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:
```bash
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:
- `id`
- `reason` — why you're being notified (mention, review_requested, author, state_change, security_alert, assign, etc.)
- `subject.title`
- `subject.type` — PullRequest, Issue, Release, etc.
- `subject.url` — API URL for the subject
- `repository.full_name`
- `updated_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–7
- `FRESH` — 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:
```bash
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`:
```markdown
# 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:
```bash
./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`:
```markdown
### 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:
```markdown
### inbox-triage
- INBOX_TRIAGE_SKIP: {reason}
```
##Set up and run an Aeon agent instance — get started from scratch, pick which skills to turn on or install more from packs, reschedule or change what runs, edit what an existing skill does, fix a skill that isn't firing, set the STRATEGY.md north star and soul/ voice, turn a coding-agent chat into a scheduled Aeon skill, and mine past coding-agent conversations for recurring work worth automating as a skill. Use when the user mentions Aeon, aeon.yml, an Aeon skill / instance / routine / pack, asks to schedule, enable, edit, or debug an agent that runs on a cron, or asks what of their repeated/manual work Aeon could take over.
Mention/keyword sweep on social platforms for [REPLACE: KEYWORDS] — trends, sentiment, top posts
5 concrete real-life actions, leverage-scored against open loops with specificity and anti-fluff gates
Static config-correctness linter for this instance - catches the silent-failure class (unquoted schedules, duplicate keys, unconfigured skills, mode typos, broken requires/MCP refs) that no run-based health skill can see. Notifies only on problems.
Pull framework updates from the upstream Aeon repo into this instance - 3-way merges canon's new commits into a PR, never clobbering operator config.
Write a publication-ready article in one of three angles - a trending long-form piece, a watched-repo thesis, or a project-through-a-lens essay. Optional Replicate hero image with --visual.
Automatically merge open PRs that have passing CI, no blocking reviews, and no conflicts
Two-mode aeon.yml workflow builder - analyze inspects URLs and emits a tiered, signal-verified skill-enablement plan plus an aeon.yml diff; enable flips slugs to enabled:true and opens a PR.