hn-bits
v0.8.0
Published
Terminal-first Hacker News client
Readme
hn-bits
A fullscreen terminal client for Hacker News, built with TypeScript + Ink. Optional local AI summaries via Ollama, topic subscriptions with Telegram alerts, no cloud required beyond what you opt into.



Features
- Fullscreen TUI: top/new/best/ask/show/saved/subs tabs, vim-style navigation
- Threaded comments view with fold/collapse
- Search across stories
- 7 built-in themes (
hn,mocha,dracula,tokyo,nord,gruvbox,concord) - Local AI article/thread summaries via Ollama, no cloud calls
- Ask AI: multi-turn Q&A grounded in the current story
- Subscriptions +
hn watch --onceradar with Telegram notifications - Bookmarks:
Bto save a story,hn bookmarksto browse them - SQLite storage (subscriptions, dedup, bookmarks); config file for AI/Telegram settings
Install
npm install -g hn-bitsOr run without installing:
npx hn-bitsFrom source
npm install
npm run build
npm link # exposes the `hn` binaryUsage
hn # fullscreen shell: top/new/best/ask/show/saved/subs tabs
hn search <query...> # search results
hn bookmarks # opens straight into the saved tab
hn subs # opens straight into the subs tab
hn --theme dracula # themes: hn (default), mocha, dracula, tokyo, nord, gruvbox, concordPress ? from any view for the full keybinding help overlay.
Local AI (summaries + Ask AI)
Article/thread summaries (s) and interactive Q&A (a) run against a local Ollama instance, no cloud calls, no API keys. Optional: the app works fully without it, s/a just show a setup hint until configured.
Prerequisites
brew install ollama # or see ollama.com/download
ollama serve # if not already running as a service
ollama pull llama3.2 # or any other chat-capable modelSetup (one-time):
hn config set ollama.host http://localhost:11434
hn config set ollama.model llama3.2Missing fields fall back to the defaults above; invalid JSON degrades to "AI disabled" with a warning rather than crashing the reader.
Subscriptions + notifications
hn sub tracks topic queries; hn watch --once checks each one, dedups against past matches, and sends new ones to Telegram and/or macOS desktop notifications. No daemon: schedule hn watch --once with cron or launchd.
hn sub add apple "Apple" --min-points 50 # topic name, Algolia query, min score
hn sub list
hn sub rm appleTelegram setup (one-time):
- Message
@BotFatheron Telegram, run/newbot, copy the token it gives you. - Message your new bot once (anything, e.g. "hi").
- Fetch
https://api.telegram.org/bot<TOKEN>/getUpdatesand readresult[0].message.chat.id.
hn config set telegram.botToken 123456:ABC-...
hn config set telegram.chatId 987654321
hn config set telegram.enabled trueAll three keys are required, telegram.enabled alone is not enough. With nothing enabled, hn watch --once exits with code 2.
macOS desktop notifications (optional, works alone or alongside Telegram) use the alerter binary (brew install vjeantet/tap/alerter):
hn config set desktopNotifications.enabled true
hn config set desktopNotifications.timeoutSeconds 10 # optional, default 10Clicking a notification opens the story URL (HN discussion for text posts): clicks act only while the notification is on screen (timeoutSeconds); stale entries in Notification Center just dismiss. Desktop delivery is best-effort: if alerter is missing, the watcher warns and skips it (exit 0, nothing marked seen when it's the only channel).
One-time macOS setup: alerter sends as Terminal.app, and macOS silently drops notifications from senders without permission. Check System Settings > Notifications > Terminal is allowed (banner style): there is no prompt and no error when it isn't.
hn watch --once # one pass: query subscriptions, notify new matches, exitThe first hn sub add offers to install a cron job for you (every 30 minutes). Manage it anytime via the CLI, or c on the Subs tab (which also shows a live installed/not-installed status line):
hn schedule status # installed / not installed
hn schedule install # install now (no-op if already installed)
hn schedule remove # uninstallOr set it up by hand:
*/30 * * * * cd /path/to/hn-bits && hn watch --onceConfiguration
hn config reads/writes ~/.config/hn-bits/config.json (override the path with $HN_BITS_CONFIG), no need to hand-edit JSON:
hn config list # every known key + current value
hn config get ollama.model # single value, raw
hn config set ollama.model llama3.2 # validates + writes
hn config unset ollama.model # removes a keySensitive values (e.g. telegram.botToken) print masked in list but raw from get, so scripts can still consume them. File format is plain JSON; hand-editing still works if you prefer it.
Themes
hn, mocha, dracula, tokyo, nord, gruvbox, concord (default: hn, HN-orange). Pick with --theme <name>, the HN_THEME env var, hn config set ui.theme <name>, or press T in the TUI to switch live and persist the choice. Precedence: flag > env > config > default. hn theme shows the active theme and where it came from.
Keybindings
Global
| Key | Action |
|-----|--------|
| q | quit (suppressed while typing in search) |
| ? | help overlay (suppressed while typing in search) |
Story list
| Key | Action |
|-----|--------|
| j/k or ↓/↑ | move selection |
| ←/→ | previous/next feed tab |
| t/n/b | top / new / best directly |
| g g / G | top / bottom |
| enter | open comments |
| o | open story URL in browser |
| r | refresh feed |
| / | search |
| s | AI summary (article, or thread if no article) |
| a | Ask AI (chat grounded in the story) |
| B | bookmark |
Comments
| Key | Action |
|-----|--------|
| j/k or ↓/↑ | move selection |
| space/enter | toggle fold on a comment |
| C/E | collapse / expand all |
| g g / G | top / bottom |
| o | open story URL in browser |
| r | reload |
| s | AI thread summary |
| a | Ask AI (chat grounded in the story) |
| B | bookmark |
| esc/b | back |
Search results
Same as story list, minus the tab/feed keys, plus / to start a new search and S to subscribe (prefills a topic from the query). esc returns to the list if you got here via / in-TUI, or quits if you entered via hn search.
Saved (hn bookmarks, or the Saved tab)
Same as story list, minus search; B removes a bookmark instead of adding one.
Subs (hn subs, or the Subs tab)
| Key | Action |
|-----|--------|
| j/k or ↓/↑ | move selection |
| ←/→ | previous/next tab |
| enter | browse matches for the selected topic |
| a | add subscription |
| e | edit subscription |
| d | delete subscription |
| c | toggle watch cron schedule (install/remove) |
Shows a schedule: installed (every 30 min) / schedule: not installed status line, so you don't need to drop to hn schedule status to check.
Sub matches (opened from Subs via enter)
Same as story list, minus search/tab-switch; r refetches, esc returns to Subs.
Development
npm run dev # run from source (tsx)
npm test # vitest
npm run build # tscTech stack
- TypeScript (strict, ESM) + Ink (React for terminals)
- Commander CLI, native
fetch - HN Firebase + Algolia APIs, no backend
better-sqlite3for subscriptions/dedup/bookmarks- Vitest
Specs
Implementation specs live in specs/; phase-by-phase status is tracked in PROGRESS.md. V1, V1.5, V1.6, V2 (local AI), and V3 (subscriptions/watcher/notifications/bookmarks) are feature-complete.
