A daily AI-signal digest. It collects high-signal AI posts from Reddit, filters
out noise, ranks by practical usefulness, summarizes the best ones with the
Gemini API, and publishes a clean Markdown digest to a local output/ folder
and to a Daily AI Signal page in Notion.
Built as a sibling to Daily-Youtube-Digest and Daily-News-Digest, reusing the
same Gemini + Notion conventions.
- Collect — pulls top posts from configured subreddits (Reddit public JSON API, no auth).
- Filter & rank — matches posts to your AI topics, drops noise, scores usefulness.
- Summarize — Gemini writes a concise, per-topic Markdown section.
- Monitor — checks Artificial Analysis for newly published articles and summarizes what changed.
- Render — assembles a dated Markdown digest, grouped by topic.
- Publish — saves
output/<date>.mdand creates a Notion child page (newest on top).
Local LLMs · Open-source models · AI coding tools · AI agents · AI automation · AI app development · AI productivity & business · AI hardware & edge · Practical AI tools
npm install
cp .env.example .env
# Fill in GEMINI_API_KEY and NOTION_TOKEN (reuse from your other digest projects).Point the setup script at an existing digest page (e.g. your YouTube Digest page). It creates a Daily AI Signal page as a sibling and prints the new page id:
SIBLING_PAGE_ID=<your-existing-page-id> npm run setup:notion
# Copy the printed id into .env as NOTION_PAGE_IDIf your existing page is at the workspace root, just create a page named
Daily AI Signal in Notion, share it with your integration, and paste its id
into NOTION_PAGE_ID.
npm start # full pipeline: collect → summarize → check articles → save → publish
npm run collect # same, but skip Notion (writes local Markdown only)
npm test # run the unit + integration tests (node:test, no deps)
npm run e2e:x # end-to-end smoke test of the X source (fixture → Gemini → Markdown)Watches artificialanalysis.ai/articles — an independent AI benchmarking site — and summarizes anything newly published. The result is its own section at the top of the daily digest.
## 📊 Artificial Analysis — New Articles
> 2 new articles · 2026-07-30 → 2026-07-31
- **[DeepSeek V4 Flash 0731 scores 50 on the Intelligence Index](...)** — 10 points
above the previous DeepSeek V4 Flash, now matching ...
- **Why it matters:** open-weights models close the gap on ...When nothing was published the section says so explicitly rather than disappearing, so a silent section can be told apart from a broken monitor.
How "new" is decided. The site has no RSS feed, so the monitor reads the article
index for slugs and each article's OpenGraph tags for title, summary, and
article:published_time. Since CI runners keep no state between runs, "new" is a date
window: lookbackDays: 1 (the default) means published yesterday or today, which
reports each article exactly once for a once-a-day schedule. Widen it with
ARTICLES_LOOKBACK_DAYS if a scheduled run is ever missed.
Tunables live in config/articles.json. Disable with --skip-articles,
DIGEST_SKIP_ARTICLES=true, or "enabled": false.
Publish dates on the site have day granularity (no clock time), so the window is measured in days rather than hours. A failed monitor never breaks the digest — the step is wrapped and the rest of the pipeline continues.
Reddit credentials are optional. Without
REDDIT_CLIENT_ID/REDDIT_CLIENT_SECRETthe collector uses anonymous RSS, which Reddit throttles to roughly one request per 30s per IP. The pipeline handles this by pacing requests and waiting the throttle out, so coverage is still complete — it just costs wall time (~5 min for the daily collect). With OAuth (free "script" app at reddit.com/prefs/apps) the same runs take seconds, post scores become available for ranking, and comment sentiment turns on. Subreddits that could not be reached are listed in the report.
Anonymous RSS reports no score or comment count. Those posts are not ranked as if
they had zero engagement — that would push every Reddit post below every Hacker News
post before content is considered. Instead they inherit the median engagement of the
posts that do report metrics, so they compete on content signal. See popularityScore
in src/filter.js.
X is disabled by default to keep the pipeline free. Reddit + Hacker News + the Gemini free API tier cost nothing to run. X requires a paid data source (see Cost & budget below), so enable it only when you're ready.
X is a plugin-style collector. Enable it in config/sources.json
(x.enabled = true) and choose a mode:
mode: "grok"— use your Grok/xAI API key (XAI_API_KEY) with the built-inx_searchtool. This is the easiest way to collect X posts and does not require a Twitter developer account. Note: a Grok key is NOT a Twitter/X bearer token, and the xAI API is paid (needs credits).mode: "api"— official X recent-search API. Needs a Twitter developerX_BEARER_TOKEN(the free Twitter tier has no search).mode: "playwright"— an external scraper command (X_PLAYWRIGHT_COMMAND) that prints a JSON array of posts to stdout.mode: "fixture"— load posts from a local JSON file (X_FIXTURE_PATH, defaulttest/fixtures/x-sample.json). No credentials needed — ideal for offline testing, CI, and demos.
The hoursBack, minScore, and lang settings in the x config are applied
uniformly across all modes.
This project is designed to run for free:
- Reddit — free (public RSS, or free OAuth app credentials).
- Hacker News — free (public Firebase API).
- Gemini summarization — free API tier from Google AI Studio
(
GEMINI_API_KEY). Note: a Gemini Advanced consumer subscription is not the same thing — you need an API key, which has its own free tier. - Notion publishing — free.
X is the only paid source. There is no usable free tier for X search:
xAI's x_search (grok mode) needs account credits, and Twitter's free API has
no search. Consumer chat subscriptions (ChatGPT Plus, Claude Pro, Gemini
Advanced) do not provide API access, so they can't be used here. If you want
X, the cheapest path is funding a small amount of xAI credits (check the
data-sharing free-credits program at https://console.x.ai → Billing), then set
x.enabled = true.
If you want X without the full Twitter API price, these are the realistic routes,
roughly cheapest/most-reliable first. All of them plug into the existing
playwright mode (any command that prints a JSON array of posts to stdout) with
no code changes — see the normalized fields in test/fixtures/x-sample.json.
- Third-party scraper APIs (best value) — services that scrape X and return
JSON, usually with a small free tier and no Twitter dev account:
Apify Twitter/X actors,
twitterapi.io,socialdata.tools,scrapecreators.com, or "Twitter" endpoints on RapidAPI. Wrap the call in a tiny script, setX_PLAYWRIGHT_COMMANDto run it, and setx.mode = "playwright". - xAI/Grok
x_search— already implemented asmode: "grok"; needs credits but is the least custom code. snscrape(free, flaky) — the open-sourcesnscrapeCLI pulls tweets with no key. Great for a manual laptop run; unreliable in an automated GitHub Action because X rate-limits/blocks CI IPs.- RSS bridges (free, unstable) — RSSHub or a self-hosted Nitter instance can expose X accounts as RSS; most public Nitter instances are now dead, so this needs self-hosting.
Rule of thumb: for a reliable daily automated digest, a paid-but-cheap scraper API or Grok credits is what holds up; free scrapers are fine for manual runs but tend to break in CI.
This repo includes a scheduled workflow at
.github/workflows/daily-ai-signal.yml.
- Schedule: daily at
03:30 America/New_York(DST-safe via07:30+08:30UTC triggers) - Also supports manual run via Actions → Daily AI Signal → Run workflow
GEMINI_API_KEYNOTION_TOKENNOTION_PAGE_ID
REDDIT_CLIENT_IDREDDIT_CLIENT_SECRETX_BEARER_TOKEN(only for Xapimode)XAI_API_KEY(only for Xgrokmode — your Grok/xAI key)
DIGEST_TIMEZONE(default:America/New_York)DIGEST_LANGUAGE(default:en)DIGEST_SKIP_NOTION(default:false)GEMINI_MODELGEMINI_MODEL_FALLBACKSGEMINI_REQUEST_TIMEOUT_MSGEMINI_DELAY_MSNOTION_ROOT_TITLENOTION_VERSIONREDDIT_USER_AGENTREDDIT_DELAY_MS(default:30000without OAuth,600with)REDDIT_RSS_RETRIES(default:3)REDDIT_RSS_MAX_429_SUBS(default:0= never give up early)X_API_BASEX_PLAYWRIGHT_COMMANDX_PLAYWRIGHT_TIMEOUT_MSXAI_API_BASEXAI_MODELXAI_REQUEST_TIMEOUT_MSARTICLES_LOOKBACK_DAYS(default:1)DIGEST_SKIP_ARTICLES(default:false)
If Reddit OAuth secrets are not provided, the workflow still runs — it just paces the RSS requests, which makes the collect step take a few minutes instead of seconds.
X is scaffolded in plugin form. To collect X posts, set
config/sources.json -> x.enabled = true, then choose a mode:
mode: "grok"+XAI_API_KEY(Grok/xAI key — recommended, no Twitter account)mode: "api"+X_BEARER_TOKEN(Twitter developer bearer token)mode: "playwright"+X_PLAYWRIGHT_COMMANDmode: "fixture"+X_FIXTURE_PATH(offline/CI, no credentials)
config/sources.json— subreddits, sort (top/hot/new), time window, per-sub limit, min score.config/sources.json— source settings for Reddit, Hacker News, and future X collection.config/topics.json— topics, their matching keywords, and max posts per topic.config/articles.json— Artificial Analysis monitor: base URL, lookback window, and how many article pages to check per run.
All secrets and tunables live in .env (see .env.example).
config/
sources.json # source configuration (Reddit today)
topics.json # topics + keywords for filtering/grouping
articles.json # Artificial Analysis monitor config
src/
collectors/
reddit.js # Reddit collector (implements the collect() contract)
hackernews.js # Hacker News collector (official Firebase API)
x.js # X.com collector (api | playwright plugin scaffold)
monitors/
artificial-analysis.js # watches artificialanalysis.ai/articles for new posts
filter.js # relevance filter, ranking, topic grouping
summarize.js # Gemini summarization (raw HTTP + model fallback)
render-markdown.js # builds and saves the Markdown digest
notion.js # Notion REST helpers (markdown → blocks, publish)
index.js # pipeline orchestrator
scripts/
setup-notion.js # one-time: create the Daily AI Signal parent page
output/ # generated digests: <date>.md
- Create
src/collectors/<name>.jsthat exportsasync collect(config)returning the normalized post shape documented inreddit.js. - Add a
<name>section toconfig/sources.json. - Register it in the
COLLECTORSmap insrc/index.js.
No other code needs to change — filtering, summarizing, rendering, and publishing all operate on the normalized post shape.
- Reddit collection (MVP)
- Gemini summarization + Notion publishing
- Artificial Analysis article monitor
- LLM-based relevance scoring (replace keyword matching)
- More monitored publications (blogs, changelogs, release notes)
- X.com / GitHub trending / RSS / newsletter sources
- Scheduled daily run (GitHub Actions / cron)
- Email / webhook / Slack delivery