repo-pulse
$
npx mdskill add aaronjmars/aeon/repo-pulse<!-- autoresearch: variation B — sharper output: /events primary input + notable-stargazer enrichment + QUIET/STEADY/ACTIVE/SURGE verdict --> > **${var}** — Repo (`owner/repo`) to check. If empty, checks all watched repos.
SKILL.md
.github/skills/repo-pulseView on GitHub ↗
---
name: repo-pulse
description: Daily report on new stars, forks, and releases for watched repos — with notable-stargazer enrichment and a one-line growth verdict
var: ""
tags: [dev]
---
<!-- autoresearch: variation B — sharper output: /events primary input + notable-stargazer enrichment + QUIET/STEADY/ACTIVE/SURGE verdict -->
> **${var}** — Repo (`owner/repo`) to check. If empty, checks all watched repos.
## Config
Reads repos from `memory/watched-repos.md`. Skip any repo whose name ends with `-aeon` or contains `aeon-agent` — those are agent repos, not project repos.
If `${var}` is set and matches `owner/repo`, check only that repo.
## Context
Read `memory/MEMORY.md` and the last **7 days** of `memory/logs/` for previous `stargazers_count` / `forks_count` per repo. Parse lines matching `**owner/repo**: stargazers_count=N, forks_count=M` to reconstruct a per-day series — you'll need it for the rolling-average baseline used in step 5.
## Steps
### 1. Compute the 24h cutoff FIRST
```bash
CUTOFF=$(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-24H +%Y-%m-%dT%H:%M:%SZ)
export CUTOFF
```
All time filtering uses exactly this timestamp — never "today's date" or "since midnight".
### 2. Fetch current counts (1 call per repo)
```bash
gh api repos/owner/repo --jq '{stargazers_count, forks_count, subscribers_count}'
```
If this call returns non-2xx (404, 403, rate limit), record `source=fail` with the reason and continue to the next repo. Do **not** abort the batch.
### 3. Fetch recent events — primary input
One call per repo covers stargazers, forks, **and releases** for the last ~90 days, newest-first:
```bash
gh api "repos/owner/repo/events?per_page=100" \
--jq '[.[] | select(.created_at >= env.CUTOFF) | {type, actor: .actor.login, created_at, tag: (.payload.release.tag_name // null), action: (.payload.action // null)}]'
```
Parse the filtered events:
- `WatchEvent` → new stargazer (`actor`). Deduplicate by actor (GitHub only fires one per user).
- `ForkEvent` → new fork. Fork URL = `github.com/{actor}/{repo}`.
- `ReleaseEvent` with `action == "published"` → new release (`tag`).
Record `source=events` for this repo.
**Why `/events` over paginated stargazers?** One call instead of two, and it captures forks + releases in the same response. Events API returns 300 events over 10 pages for up to 90 days — more than enough for a 24h window on typical repos.
### 4. Fallback (rate limit or error)
If step 3 returns non-2xx, fall back to the stargazers two-last-pages technique (events emptiness is NOT a fallback trigger — empty genuinely means no activity):
```bash
STARS=$(gh api repos/owner/repo --jq '.stargazers_count')
LAST_PAGE=$(( (STARS + 99) / 100 ))
PREV_PAGE=$(( LAST_PAGE > 1 ? LAST_PAGE - 1 : 1 ))
gh api "repos/owner/repo/stargazers?per_page=100&page=$PREV_PAGE" \
-H "Accept: application/vnd.github.star+json" \
--jq '.[] | select(.starred_at >= env.CUTOFF) | {user: .user.login, starred_at}'
gh api "repos/owner/repo/stargazers?per_page=100&page=$LAST_PAGE" \
-H "Accept: application/vnd.github.star+json" \
--jq '.[] | select(.starred_at >= env.CUTOFF) | {user: .user.login, starred_at}'
```
Deduplicate by user. Forks in the fallback path come from:
```bash
gh api "repos/owner/repo/forks?sort=newest&per_page=10" \
--jq '.[] | select(.created_at >= env.CUTOFF) | {owner: .owner.login, full_name, created_at}'
```
Record `source=stargazers-fallback` for this repo. Releases are skipped in fallback (not critical).
### 5. Enrich stargazers and compute the verdict
**Notable-stargazer lookup** — for each new stargazer in the 24h window, cap **10** lookups per repo to respect rate limits:
```bash
gh api users/{login} --jq '{login, followers, public_repos, bio}'
```
Mark as **notable** if `followers >= 100` OR `public_repos >= 20`. Logins ending in `[bot]` or `-bot` are never notable and are excluded from the handle list entirely.
**Growth verdict** — reconstruct the last 7 days of `stargazers_count` from logs and compute per-day deltas. Let `avg7` = mean of the available daily deltas (use `avg7 = 1` if fewer than 3 days are logged). Let `today_stars` = new stargazers in the last 24h.
| Verdict | Rule (first matching row wins) |
|---------|--------------------------------|
| `SURGE` | `today_stars >= 10` OR `today_stars > 3 * avg7` |
| `ACTIVE` | `today_stars > 1.5 * avg7` |
| `STEADY` | `today_stars >= 1` OR any new fork OR any new release |
| `QUIET` | zero stars, zero forks, zero releases in 24h |
Record the rule that fired so it shows up in the log.
### 6. Decide whether to notify
Send a notification if ANY of:
- ≥1 new stargazer in the last 24h (unstars do not cancel this)
- ≥1 new fork
- ≥1 new release
- First run for this repo (no previous count in logs)
Otherwise print `REPO_PULSE_QUIET` and skip `./notify`.
### 7. Notification — via `./notify`
Format (omit any empty section entirely):
```
*Repo Pulse — ${today}* — [VERDICT]
[owner/repo] — stars X (+N) · forks Y (+M) · releases +R
Notable new stargazers:
github.com/user1 (1.2k followers) | github.com/user2 (450 followers)
Other new stargazers:
github.com/user3 | github.com/user4
New forks:
github.com/user5/repo | github.com/user6/repo
New releases:
v1.2.3 | v1.2.4
Source: events
```
Rules:
- `[VERDICT]` is uppercased, in square brackets, on the header line.
- Handles joined by ` | ` on **one line** — never one per line.
- Round follower counts: `<1000` → raw number, `1000+` → `1.2k` form.
- Omit `Notable new stargazers`, `Other new stargazers`, `New forks`, `New releases`, or `Source` lines if they would be empty.
- **Never include traffic, watchers, or open issues** — they don't belong in a pulse.
- One message per repo if multiple repos have activity. Batch into a single message only when combined length stays under 1500 chars.
### 8. Log to `memory/logs/${today}.md`
Always include the exact current counts so tomorrow's run can compute deltas:
```
## Repo Pulse
- **owner/repo**: stargazers_count=X, forks_count=Y, source=events
- **New stars (24h):** N (verdict=ACTIVE, avg7=1.4)
- **New forks (24h):** M
- **New releases (24h):** R
- **Notable stargazers:** user1(1200), user2(450)
- **Notification sent:** yes
```
If the repo lookup failed, log:
```
- **owner/repo:** FAILED (<reason>) — counts unchanged
```
## Sandbox note
- `gh api` handles auth internally; prefer it over curl.
- `/repos/{owner}/{repo}/traffic/*` endpoints require **admin** permission and return 403 for the default workflow `GITHUB_TOKEN`. Do **not** attempt them from this skill.
- If `gh api` fails on one repo, log the failure and continue — never abort the whole batch.
## Constraints
- A day with zero stars, zero forks, zero releases is `QUIET` — print `REPO_PULSE_QUIET` and do not notify.
- Never promote a bot account to "notable", even if it clears the follower threshold.
- Keep the verdict vocabulary fixed to `QUIET / STEADY / ACTIVE / SURGE` so downstream skills can grep for it.
More from aaronjmars/aeon
- [REPLACE: SKILL_NAME]Daily digest of the most interesting new posts on [REPLACE: TOPIC] from RSS feeds and the open web
- Action Converter5 concrete real-life actions for today, leverage-scored against open loops with specificity and anti-fluff gates
- Agent BuzzCurated AI-agent tweets, clustered into narratives with insight summaries
- agent-displacementWeekly tracker of AI agent substitution signals — which roles, companies, and industries show real headcount displacement. Named roles + real deployments only.
- AI Framework WatchWeekly competitive-intelligence digest on the AI agent framework space — momentum, releases, breaking changes across a curated watchlist
- AIXBT PulseCross-domain market pulse from AIXBT's free grounding endpoint — crypto, macro, tradfi, geopolitics. Refreshes taxonomy references (clusters, chains) as a bonus.
- api-health-probeDaily pre-batch API provider health check — detects credit exhaustion or auth failure for every configured provider key before the morning batch runs, giving the operator a window to act before skills degrade
- Approval AuditList a wallet's live ERC-20 token approvals on Base and flag unlimited / risky spender grants. Keyless via Base RPC (eth_getLogs + eth_call) — no explorer key needed.
- article-queueWeekly article idea synthesizer — ranks signals from topic-momentum, beat-tracker, and narrative-tracker into a prioritized queue the article skill reads on next run
- atrium-catalog-watcherWeekly diff of the Atrium marketplace catalog at https://atriumhermes.tech/.well-known/skills/index.json against the prior snapshot — surfaces newly-published skills, removed skills, and updated descriptions. Supply-side complement to sparkleware-catalog (curated skill-packs.json registry) and skill-update-check (version drift of installed skills).