Clustered, signal-scored digest of Farcaster casts with conversation-shape lead and insight-first editorial notes
Install
npx skillscat add atrium-hermes/atrium-lighthouse/farcaster-digest Install via the SkillsCat registry.
${var} — Topic filter or channel name (e.g. "prediction-markets", "base", "ai-agents"). If empty, uses default interest areas.
If ${var} is set, focus curation on that topic or channel.
Read memory/MEMORY.md for current interests.
Read the last 2 days of memory/logs/ for recency context.
Load persistent dedup state from memory/state/farcaster-seen-hashes.json (auto-created; safe to delete to reset dedup). The file starts as {"hashes": [], "updated": null} and stores cast hashes seen in the last 7 days — casts present here are skipped even if they re-appear in algorithmic feeds.
Thesis
Curation is the biggest lever, not fetching. Raw engagement counts rank by popularity, not conversation. This skill ranks by a signal score, clusters casts into 2–3 sub-narratives, leads with a one-line conversation-shape header, and demands an original insight per cast — not a paraphrase of the cast text.
Steps
1. Fetch casts from three complementary sources
For every endpoint, prefer WebFetch over curl (sandbox often blocks curl — see CLAUDE.md). Auth header: x-api-key: $NEYNAR_API_KEY.
(a) Topic search — literal + semantic.
Run each default topic query twice: once with mode=literal (default), once with mode=semantic to catch thematic matches the keyword query misses.
GET https://api.neynar.com/v2/farcaster/cast/search/?q=QUERY&sort_type=algorithmic&mode=literal&limit=20
GET https://api.neynar.com/v2/farcaster/cast/search/?q=QUERY&sort_type=algorithmic&mode=semantic&limit=20Default topics (override with ${var} or MEMORY.md interests):
"prediction markets" | "coordination markets" | polymarket | kalshi | futarchy"AI agents" | "autonomous agents" | agentic | "agent frameworks"hyperstitions | "mechanism design" | "network states" | "public goods"
If ${var} is set, replace all default topics with a single search on ${var}.
(b) Channel feed — high-signal channels.
Sample recent casts from 3–4 channels relevant to Aeon's interests:
GET https://api.neynar.com/v2/farcaster/feed/channels?channel_ids=ai-agents,base,founders,ethereum&with_recasts=false&with_replies=false&limit=30(If ${var} looks like a channel slug, query that channel alone.)
(c) Global trending — network-wide context.
GET https://api.neynar.com/v2/farcaster/feed/trending?limit=25&time_window=24hRecord per-source status (ok | fail | empty) as you go — you'll emit it in the footer.
2. Filter and deduplicate
- Drop any cast whose
hashis infarcaster-seen-hashes.json. - Drop any cast older than 48h (use
timestampfield). - Drop casts whose
author.follower_countis below 500 — removes most spam/fresh-acct noise. - Drop near-duplicates (same author posting near-identical text within the window).
- Drop pure shilling: casts that are almost entirely
$TICKER+ a link and no commentary.
3. Score and rank
Compute a signal score for each surviving cast:
signal = reactions.likes_count + 2 × reactions.recasts_count + 0.5 × replies.countRationale: recasts signal endorsement (worth ~2 likes); replies signal controversy/noise (halved). Raw likes remain the base. Take the top 15 by signal score into the editorial pass.
Engagement floor: exclude anything with signal < 10. If fewer than 3 casts clear the floor, lower it to 5 rather than returning nothing.
4. Cluster into 2–3 sub-narratives
Read the top 15 and group them into 2 or 3 themed clusters. Examples of good cluster labels:
- "prediction market volume chasing the election cycle"
- "agent frameworks debating memory vs. tools"
- "base mini-apps shipping faster than the analytics can track"
Avoid generic labels like "crypto news" or "AI". A cluster label should name a conversation, not a topic.
From each cluster, select the 2–3 casts that best represent the argument — diversity of voices over highest-engagement if they overlap.
Target total: 5–8 casts across clusters.
5. Format the digest
Keep under 4000 chars. Lead with a one-line conversation-shape header describing what's happening on Farcaster today, then one block per cluster.
farcaster digest — ${today}
shape: <one line, ≤20 words, what's the conversation today>
—— <cluster 1 label> ——
@username — "cast text here (truncate >240 chars)"
↳ 142 likes · 31 recasts · 18 replies | warpcast.com/username/0xabc123
insight: <one line, ≤25 words, something a reader couldn't get from the cast text alone>
@username2 — "..."
↳ ... | warpcast.com/...
insight: ...
—— <cluster 2 label> ——
...
sources: search=ok trending=ok channels=ok | new casts: 7 | seen-cache: 142 hashesInsight discipline: prefer casts where the insight: line adds something — implication, counter-argument, context the cast assumes, or a connection to another cast in the digest — over casts where the best you can write is a paraphrase. When choosing between casts of similar signal, pick the one that yields a non-paraphrase insight.
6. Send via ./notify
Pass the formatted digest to ./notify "<digest>".
7. Persist state and log
- Append every included cast's hash to
memory/state/farcaster-seen-hashes.json. Prune entries older than 7 days before writing. - Log to
memory/logs/${today}.mdunder### farcaster-digestwith: search queries used, source-status line, cluster labels chosen, cast count included, and any notable edge cases (e.g. engagement-floor lowered).
Empty vs error
- Empty run (
FARCASTER_DIGEST_OK): sources succeeded, but nothing cleared the engagement floor or all surviving casts were already in the seen cache. Notify:farcaster digest: quiet day — <N> casts fetched, 0 passed signal floor. Log the source-status line. - Error run (
FARCASTER_DIGEST_ERROR): every source returned fail/empty before filtering. Notify:farcaster digest skipped — sources: search=X trending=Y channels=Z. Do not update the seen cache.
Sandbox note
The sandbox may block outbound curl. Default to WebFetch; fall back to curl only if WebFetch hits an auth wall. For the auth-required Neynar API, WebFetch works because it doesn't rely on shell env expansion — pass the key inline via the prompt or use the pre-fetch pattern (see CLAUDE.md) if you need to shell out.
Environment Variables Required
NEYNAR_API_KEY— Neynar API key for Farcaster data access (get one at neynar.com).