Skip to content

wislonl/newsline

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

33 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ“ฐ newsline

A personal news radar that tracks storylines, learns your taste, and runs locally on your Mac.

License Platform Language Language Claude GPT MiniMax

Newsline screenshot

Why

Most news aggregators give you a flat list that resets every day. "GPT-5 released" Monday and "GPT-5 benchmarks land" Wednesday show up as two unrelated rows; the story isn't carried.

newsline builds on the same shape as Horizon (fetch from many sources, score with AI, deliver a briefing) but takes three swings Horizon doesn't:

  1. Storyline tracking โ€” items cluster into stories that grow over time, not daily disposable rows.
  2. Personalization that learns โ€” open / dwell / ๐Ÿ‘๐Ÿ‘Ž / dismiss signals get recorded for a future ranker.
  3. Local-first, Mac-native โ€” Python daemon writes a SQLite file; a SwiftUI app reads it. No server, no account, no cloud round-trip.

Features

  • ๐Ÿ“ก Multi-source ingest โ€” Hacker News, RSS / Atom (8 feeds out of the box), Reddit (subreddits via public JSON). GitHub releases & Twitter scaffolded.
  • ๐Ÿค– AI scoring โ€” Each item rated 0-10 on a calibrated importance rubric. Pluggable provider: Anthropic, OpenAI, MiniMax (ๅ›ฝๅ†… & ๅ›ฝ้™… endpoints), any OpenAI-compatible URL.
  • ๐Ÿงญ Storyline clustering โ€” Entity extraction + entity-overlap inverted index + LLM verify. Cross-source items about the same event merge into one story (the "TanStack supply-chain attack" cluster from HN + r/programming was the first real-data validation).
  • ๐Ÿ“ Evolving summaries โ€” When new events attach to a story, the summary is rewritten to fold in the new information. Single-event stories use their original title and AI summary.
  • ๐Ÿ’ฌ Chat over a story โ€” Streaming chat sidecar (aiohttp on 127.0.0.1) lets you ask follow-up questions grounded in the events. Multi-turn โ€” the model carries the prior conversation. Reasoning-model <think> blocks suppressed.
  • ๐ŸŽฏ Personal signals โ€” Opens, dwell time (โ‰ฅ1s), thumbs, dismiss are all written to user_signals. Building the data set now so a ranker has something to train on later.
  • ๐ŸŒ Full Chinese support โ€” One config flag (ai.language: "zh") flips every user-facing LLM output to ็ฎ€ไฝ“ไธญๆ–‡. App UI and date formatters follow your macOS language. Titles are translated while preserving repo paths, version strings, and code identifiers.
  • ๐Ÿ–ฅ Native Mac reader โ€” SwiftUI NavigationSplitView with time-grouped storyline sidebar, score-filter slider, full-text search, tag-click filtering, source icons, read state, dismiss with undo, keyboard nav (j/k/space/Enter/โŒ˜F), and a chat panel that streams tokens.
  • ๐Ÿ” Manual or scheduled โ€” Tap the Fetch button in the app, or install the com.newsline.daily LaunchAgent for an automated daily pull.
  • ๐Ÿ“„ Markdown digest โ€” news digest --days 7 --top 10 renders a shareable rollup of the recent high-score storylines.

How it works

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Mac App (SwiftUI)                                           โ”‚
โ”‚  โ€ข read sidebar / detail   โ€ข record signals   โ€ข chat UI       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                     โ”‚ SQLite (WAL) + localhost:8137 HTTP
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Python daemon                                               โ”‚
โ”‚  fetch  โ†’  AI score  โ†’  storyline match  โ†’  summary rewrite  โ”‚
โ”‚                                            โ†“                  โ”‚
โ”‚                                       chat sidecar (aiohttp) โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                     โ–ฒ
                     โ”‚ HTTP
        Anthropic / OpenAI / MiniMax / OpenAI-compatible

The Python pipeline owns the database. The Mac app reads it directly (RW connection, SELECT-only by convention โ€” WAL mode lets the two coexist). A separate newsline serve HTTP sidecar handles chat streaming so the UI can render tokens as they arrive instead of waiting on a subprocess to spawn.

Quick start

git clone https://github.com/wislonl/newsline
cd newsline
uv sync                                  # or: pip install -e .
cp .env.example .env                     # add your LLM API key
cp config.example.json config.json       # pick provider, sources, language

# Single command โ€” builds the .app on first run, then opens it
news

# Other useful subcommands
news run            # fetch + score + match + rewrite (manual refresh)
news stories        # CLI: list active storylines
news story <id>     # CLI: show one story's timeline
news chat <id> 'โ€ฆ'  # CLI: ground a question in one story
news digest --days 7 --top 10
news retranslate    # re-translate after switching ai.language
news where          # print local data dir
news help

App keyboard shortcuts: j / k next/prev, space advance, Enter open original, โŒ˜F search, โŒ˜R fetch, โŒ˜Z undo dismiss.

Configuration sketch

{
  "ai": {
    "provider": "minimax",
    "model": "MiniMax-M2.7-highspeed",
    "base_url": "https://api.minimaxi.com/v1",
    "api_key_env": "MINIMAX_API_KEY",
    "language": "zh"
  },
  "filtering": {
    "time_window_hours": 168,
    "ai_score_threshold": 4.0,
    "dormant_after_days": 30,
    "source_bias": { "hackernews": 0.0, "reddit": 0.0 }
  },
  "sources": {
    "rss": [
      { "name": "Simon Willison", "url": "https://simonwillison.net/atom/everything/" },
      { "name": "Anthropic News", "url": "https://www.anthropic.com/news/rss.xml" }
    ],
    "hackernews": { "enabled": true, "top_n": 100, "min_points": 50 },
    "reddit":     { "enabled": true, "subreddits": [
      { "subreddit": "MachineLearning", "sort": "hot", "fetch_limit": 25, "min_score": 50 }
    ]}
  }
}

See config.example.json for the full schema.

Switching models / providers

Three built-in providers: anthropic, openai, minimax.

Just change the model name (same provider):

{ "ai": { "provider": "anthropic", "model": "claude-sonnet-4-5-20251022",
          "api_key_env": "ANTHROPIC_API_KEY" } }

Use any OpenAI-compatible API without writing code โ€” set provider: "openai" and a base_url. DeepSeek, Doubao, Together, Groq, Moonshot, OpenRouter, your own proxy, etc. all work:

// DeepSeek
{ "ai": { "provider": "openai", "model": "deepseek-chat",
          "base_url": "https://api.deepseek.com/v1",
          "api_key_env": "DEEPSEEK_API_KEY" } }

// Groq
{ "ai": { "provider": "openai", "model": "llama-3.3-70b-versatile",
          "base_url": "https://api.groq.com/openai/v1",
          "api_key_env": "GROQ_API_KEY" } }

// Doubao (Volcengine Ark)
{ "ai": { "provider": "openai", "model": "doubao-pro-32k",
          "base_url": "https://ark.cn-beijing.volces.com/api/v3",
          "api_key_env": "DOUBAO_API_KEY" } }

MiniMax has two non-interchangeable endpoints:

// ๅ›ฝๅ†… (platform.minimaxi.com)
{ "ai": { "provider": "minimax", "model": "MiniMax-M2.7-highspeed",
          "base_url": "https://api.minimaxi.com/v1",
          "api_key_env": "MINIMAX_API_KEY" } }

// International (api.minimax.io)
{ "ai": { "provider": "minimax", "model": "MiniMax-M2.7-highspeed",
          "base_url": "https://api.minimax.io/v1",
          "api_key_env": "MINIMAX_API_KEY" } }

A domestic key returns 401 against the international endpoint and vice versa. Confirm which console issued your key before setting base_url.

Adding a genuinely new (non-OpenAI-compatible) provider is a ~20-line class in newsline/ai/client.py implementing complete_json, complete_text, and stream_chat, plus one branch in create_client(). The existing AnthropicClient and OpenAIClient are templates to copy.

After any provider change, drop your key into .env and restart the app (or run news run from a fresh shell so the daemon picks up the new env).

Where your data lives

  • Database: ~/Library/Application Support/newsline/newsline.db (SQLite WAL)
  • LaunchAgent: ~/Library/LaunchAgents/com.newsline.daily.plist (when installed)
  • Daemon log: ~/Library/Logs/newsline.log

Nothing leaves your machine except the outbound calls to your configured LLM provider.

Project structure

newsline/
โ”œโ”€โ”€ newsline/                  # Python daemon
โ”‚   โ”œโ”€โ”€ scrapers/              # base, rss, hackernews, reddit, github, twitter, telegram
โ”‚   โ”œโ”€โ”€ ai/                    # client, scorer, extractor, rewriter, chatter, translator, language
โ”‚   โ”œโ”€โ”€ storyline/             # matcher
โ”‚   โ”œโ”€โ”€ services/              # email, webhook
โ”‚   โ”œโ”€โ”€ db.py ยท models.py ยท config.py ยท pipeline.py ยท cli.py ยท server.py ยท digest.py
โ”œโ”€โ”€ apps/macos/                # SwiftPM macOS app
โ”‚   โ””โ”€โ”€ Sources/Newsline/      # NewslineApp, ContentView, Store, Signals, ChatStore,
โ”‚                              # ChatService, PipelineService, Sidecar, DBWatcher, L10n
โ”œโ”€โ”€ scripts/                   # news, build-app.sh, install-app.sh, install-daemon.sh
โ””โ”€โ”€ tests/                     # pytest smoke tests

Roadmap

Status
M0 Python daemon + SQLite + scrapers + AI scoring โœ…
M1 Storyline engine (entity overlap + LLM verify) โœ…
M1.5 Multi-event summary rewrite + dormant archive โœ…
M2 Mac app shell (NavigationSplitView, 3-pane, watcher) โœ…
M3 Behavior signals collection (open / dwell / ๐Ÿ‘๐Ÿ‘Ž / dismiss) โœ…
M3.5 Personalized ranker on top of base score โณ (need 1โ€“2 weeks of signals)
M4 Streaming chat over a story (multi-turn) โœ…
i18n Full Chinese stack โ€” output, UI, dates, titles โœ…
Polish Read state ยท dismiss undo ยท keyboard nav ยท search ยท tags ยท auto-scroll โœ…
Multi-modal sources (podcasts, YouTube transcripts) โณ
Reddit OAuth (replace UA-spoofing) โณ
Breaking-news push notifications โณ

Acknowledgements

  • Scrapers adapted from Horizon โ€” MIT, see NOTICE.
  • Mac app architecture borrowed from Hermes Agent's SwiftPM-only macOS pattern.

License

Apache-2.0

About

Local-first personal news radar that tracks storylines and learns your taste. macOS native, Chinese & English.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors