Install
npx skillscat add atrium-hermes/atrium-lighthouse/github-releases Install via the SkillsCat registry.
${var} — Comma-separated list of repos (e.g.
anthropics/anthropic-sdk-python,anza-xyz/agave). If empty, uses the default watch list.
Read memory/MEMORY.md for context.
Read the last 2 days of memory/logs/ and memory/github-releases-state.json (if present) to avoid reporting the same tag twice.
Goal
Turn a list of "$N$ new releases" into $M$ upgrade decisions. Every release earns a triage verdict from semver delta + release-notes content, so the reader acts rather than skims.
Steps
1. Build the repo list
If ${var} is set, split on commas and use that. Otherwise use this default watch list:
AI / LLM
- anthropics/anthropic-sdk-python
- anthropics/anthropic-sdk-typescript
- anthropics/claude-code
- anthropics/claude-agent-sdk-python
- openai/openai-python
- openai/openai-node
- openai/openai-agents-python
- BerriAI/litellm
- langchain-ai/langchain
- run-llama/llama_index
Infra / Dev
- vercel/next.js
- supabase/supabase
- ggerganov/llama.cpp
- huggingface/transformers
Crypto / DeFi
- anza-xyz/agave
- ethereum/go-ethereum
- uniswap/v4-core
- aave/aave-v3-core
(solana-labs/solana was archived 2025-01-22 — replaced with anza-xyz/agave.)
2. Fetch releases per repo
Use WebFetch against the list endpoint, not /releases/latest:
https://api.github.com/repos/{owner}/{repo}/releases?per_page=5/releases/latest silently drops prereleases and drafts, so repos that ship only prereleases look silent. The list endpoint shows everything; we decide what to do with each in step 4.
Extract per release: tag_name, name, published_at, html_url, prerelease, draft, body (first 800 chars).
Fallback chain:
- On 404 (repo has no releases ever): fetch
https://api.github.com/repos/{owner}/{repo}/tags?per_page=3and treat the newest tag as a bare release (tag only, no body). - On 403/429 (rate-limit): record
ratelimitedfor that repo and skip. Do not retry. - On any other error: record
errorand skip.
If GITHUB_TOKEN is in env, include Authorization: Bearer $GITHUB_TOKEN. In GitHub Actions the token is auto-injected — the workflow must pass env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}. Anonymous rate limit is 60 req/hr; authenticated is 5000.
3. Filter by window + dedup
Keep a release iff either:
published_atis within the last 25 hours (1h overlap absorbs cron drift), ortag_nameis not present inmemory/github-releases-state.json[repo].last_tagand is newer than the stored entry.
Drop draft=true. Keep prerelease=true — they feed the SKIP tier.
4. Triage — classify each kept release into one tier
Semver delta. Strip a leading v. Parse MAJOR.MINOR.PATCH[-pre] against the prior tag (from state, or from the previous release in the list). If unparseable (e.g. release-2024-11-15), treat delta as unknown and rely on keywords alone.
Body keyword scan (case-insensitive, on body + name):
securityfamily:security,CVE-,vulnerability,critical fix,RCE,auth bypass,patch releasebreakingfamily:breaking change,BREAKING,migration required,deprecat,removedfeaturefamily:add,introduce,new,support for,now supports
Decision ladder — first match wins:
| Tier | Emoji | Trigger |
|---|---|---|
| UPGRADE ASAP | 🔴 | Any security keyword match, regardless of semver. |
| UPGRADE SOON | 🟡 | MAJOR bump, or any breaking keyword match. |
| FYI | 🔵 | MINOR or PATCH bump, no breaking/security keywords. |
| SKIP | ⚪ | prerelease=true, or tag matches -rc|-alpha|-beta|-canary|-nightly|-dev. |
A prerelease that also has a security keyword promotes to 🔴 (security always wins).
5. Compose output (under 4000 chars)
Always emit a lead line:
*GitHub Releases — ${today}* — N updates · 🔴 A asap · 🟡 B soon · 🔵 C fyi · ⚪ D skippedIf every tier is empty (N=0), log GITHUB_RELEASES_NONE and end — no notification.
Otherwise emit tiers in order 🔴 → 🟡 → 🔵 → ⚪. Omit empty tiers. Within a tier, sort by published_at descending.
Each item is one line:
🔴 [owner/repo v1.2.3](html_url) — <triage reason ≤15 words>Triage-reason rules:
- Lead with a concrete verb:
Patches,Breaks,Adds,Deprecates,Removes,Fixes. - Name the specific thing:
auth bypass in /session,JSON streaming for tools,v2 response schema. No generic filler (various bugs,improvements,stability). - Never echo the version, the repo name, or the release title. Never end with
…. - Strip markdown, emojis, and
Full Changelog:links before scanning. - If the body is empty or pure noise, fall back to the release
name— but only if it contains a concrete noun (notv1.2.3).
Truncate the ⚪ SKIP tier to the first 3 items, then … +N more.
Append a blank line and the source-status footer:
_sources: ok=12 notfound=2 ratelimited=0 error=0_6. Update state
Write memory/github-releases-state.json:
{
"updated_at": "<ISO 8601>",
"repos": {
"owner/repo": { "last_tag": "v1.2.3", "last_published_at": "<ISO 8601>" }
}
}Only update entries for repos that returned at least one release or tag this run. Preserve existing entries for ratelimited / error / notfound repos — don't clobber good history with a bad fetch.
7. Send via ./notify
Send the full composed message (lead line + tier sections + footer) via ./notify. Keep total under 4000 chars — if over, truncate the 🔵 FYI tier first, then ⚪ SKIP, never 🔴 or 🟡.
Distinct end states:
GITHUB_RELEASES_NONE— every source succeeded, zero fresh releases (quiet day).GITHUB_RELEASES_ERROR— every source failed (all 404 / ratelimited / error). Notify with the error state so a net problem doesn't masquerade as a quiet day.
8. Log
Append to memory/logs/${today}.md:
### github-releases
- Tiers: 🔴 A · 🟡 B · 🔵 C · ⚪ D
- Reported: <owner/repo@tag>, ...
- Sources: ok=X notfound=Y ratelimited=Z error=WSandbox note
Use WebFetch for every GitHub API call — curl is unreliable from the sandbox, and WebFetch bypasses the block. Pass the Authorization: Bearer $GITHUB_TOKEN header via WebFetch when the token is present. If WebFetch is slow across the full watch list, create scripts/prefetch-github-releases.sh to cache responses into .github-releases-cache/{owner}__{repo}.json before Claude runs; the workflow executes all scripts/prefetch-*.sh with full env access.
Constraints
- Never invent a tier. If
bodyis empty and semver delta is unknown, default to 🔵 FYI. - Never report the same
owner/repo@tagtwice across runs — the state file is the source of truth. If state is missing, fall back to scanning the last 2 days ofmemory/logs/. - Don't add env vars beyond
GITHUB_TOKEN(it's already standard in GitHub Actions).
Environment Variables
| Variable | Required | Description |
|---|---|---|
GITHUB_TOKEN |
Recommended | Auto-injected in GH Actions; pass via env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}. Raises rate limit 60 → 5000 req/hr. |