Farcaster Digest
Farcaster Digest curates and ranks Farcaster casts by signal strength rather than raw engagement, clustering conversations into 2–3 narratives with conversation-shape headers and original insights per cast. Use this when you need a filtered, editorially refined summary of Farcaster activity around specific topics like prediction markets, AI agents, or mechanism design, with automatic deduplication across a rolling 7-day window and support for topic or channel filtering.
git clone --depth 1 https://github.com/aaronjmars/aeon /tmp/farcaster-digest && cp -r /tmp/farcaster-digest/skills/farcaster-digest ~/.claude/skills/farcaster-digestSKILL.md
<!-- autoresearch: variation B — sharper output via cluster + signal score + insight extraction; folds in A's semantic/channel inputs and C's source-status footer + persistent dedup -->
> **${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=20
```
Default 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=24h
```
Record per-source status (`ok` | `fail` | `empty`) as you go — you'll emit it in the footer.
### 2. Filter and deduplicate
- Drop any cast whose `hash` is in `farcaster-seen-hashes.json`.
- Drop any cast older than 48h (use `timestamp` field).
- Drop casts whose `author.follower_count` is 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.count
```
Rationale: 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 hashes
```
**Insight 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}.md` under `### farcaster-digest` with: 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 retuMention/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
Curated AI-agent tweets, clustered into narratives with insight summaries
Tracker of AI agent substitution signals — which roles, companies, and industries show real headcount displacement. Named roles + real deployments only.
Competitive-intelligence digest on the AI agent framework space — momentum, releases, breaking changes across a curated watchlist
Cross-domain market pulse from AIXBT's free grounding endpoint — crypto, macro, tradfi, geopolitics. Refreshes taxonomy references (clusters, chains) as a bonus.
Pre-batch API provider health check — detects credit exhaustion or auth failure for every configured provider key before the scheduled batch runs, giving the operator a window to act before skills degrade
List 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.