A CLI tool that generates a curated personal newsletter from RSS/Atom feeds. Uses deterministic Python for data processing and the Claude Agent SDK for relevance scoring, summarization, and editorial selection.
- Python 3.11+
- uv for dependency management
- Claude Code CLI installed and authenticated (for local development), or an
ANTHROPIC_API_KEY
git clone <this-repo>
cd rss-newsletter
uv syncThe application supports multiple authentication modes:
- Claude Code login (recommended for local dev): If you have
claudeCLI authenticated, no additional setup is needed. - API key: Set
ANTHROPIC_API_KEYin.env.localor your shell environment.
# Optional: create .env.local for API key auth
cp .env.example .env.local
# Edit .env.local and set ANTHROPIC_API_KEY=sk-ant-...Define RSS/Atom feeds to monitor:
feeds:
- name: Hacker News
url: https://news.ycombinator.com/rss
enabled: true
category_hints: [technology, startups]Define your interests and preferences:
name: "AI & Developer Tools Weekly"
language: en
interests:
- AI agents
- Python
- developer tools
avoid_terms:
- cryptocurrency
max_items: 8
style: "concise and practical"# Generate newsletter (default: last 1 day, max 8 items)
# Progress log is printed to stdout during the run
uv run personal-newsletter generate
# Custom options
uv run personal-newsletter generate --days 3 --max-items 5
# With history tracking (avoids repeating articles across runs)
uv run personal-newsletter generate --state-dir state
# Write progress log to a file instead of (in addition to) stdout
# The file includes timestamps, token usage, and full agent response details
uv run personal-newsletter generate --log output/run.log
# Custom config paths
uv run personal-newsletter generate \
--profile config/profile.yml \
--feeds config/feeds.yml \
--output-dir output| Option | Default | Description |
|---|---|---|
--profile |
config/profile.yml |
Path to profile YAML |
--feeds |
config/feeds.yml |
Path to feeds YAML |
--days |
1 |
Look back N days for articles |
--max-items |
8 |
Maximum newsletter items |
--output-dir |
output |
Output directory for generated files |
--state-dir |
(none) | State directory for history tracking |
--log |
(none) | Write timestamped progress log to FILE (stdout always receives INFO-level progress) |
Each run produces three files in output/:
| File | Description |
|---|---|
newsletter_YYYY-MM-DD.md |
Readable Markdown newsletter |
newsletter_YYYY-MM-DD.json |
Structured JSON artifact with all intermediate data |
run_YYYY-MM-DD.log |
Run report with statistics |
The pipeline runs in this order:
- Load config — Parse feeds.yml and profile.yml
- Fetch RSS — Concurrent HTTP fetching with httpx + feedparser
- Deduplicate — URL canonicalization + title normalization
- Score (Claude agent) — Relevance scoring against user profile
- Summarize (Claude agent) — Concise summaries with "why it matters"
- Edit (Claude agent) — Final selection, ranking, and newsletter assembly
- Render — Markdown + JSON output generation
Deterministic steps (1-3) use plain Python. Agent steps (4-6) use the Claude Agent SDK with structured output validation.
For deeper technical details, see specs/architecture.md and specs/runtime-agent-config.md.
# Run tests
uv run pytest
# Lint
uv run ruff check .
# Format check
uv run ruff format --check .
# Type check
uv run mypy src- Verify Claude Code authentication: run
claude --versionin your terminal. - Or set
ANTHROPIC_API_KEYin.env.local.
- The app handles broken feeds gracefully — it skips them and includes warnings.
- Check
output/run_YYYY-MM-DD.logfor details on which feeds failed.
- The SDK retries up to 2 times on validation failure.
- If persistent, check that the model supports structured outputs (claude-sonnet-4-6 recommended).
- Override the model with
NEWSLETTER_MODELenvironment variable.
- Increase
--daysto look further back. - Check that feeds in
config/feeds.ymlare enabled and URLs are reachable.
MIT