Skip to content

Changelog

Newest first.

Dashboard cards now have an ✏️ edit button to set a link’s title and summary by hand — for the occasional site (e.g. Canva) that blocks server-side enrichment behind a bot challenge, so it can’t be auto-summarized. The edit resyncs full-text search and re-embeds the link so /find and /ask stay accurate.

LinkBrain is an enriched, searchable repository of links — not a read-it-later triage queue — so the reading-status workflow is gone: the unread/reading/read/archived field, its /read /reading /archive /unread commands, /inbox, the Reading/Read tap-buttons on the save reply, the dashboard status dropdown + filter, and the status column (migration 0007). Favorites, collections, and the AI category stay — those describe a link, they don’t track your progress through it.

The Remind feature is gone — the date picker on the dashboard card, the /remind and /due commands, and the remind_at column (dropped via migration 0006). The reminder push (Cron) was always out of scope, so a stored date with no nudge wasn’t earning its place. Favorites, collections, and the AI category remain.

Smarter categories, auto-collections & a rebuilt web UI

Section titled “Smarter categories, auto-collections & a rebuilt web UI”
  • Smarter auto-categorization — the AI category is now reuse-or-coin: the model reuses a category already in your library when one fits and only coins a new short lowercase one when nothing does, so the taxonomy grows richer without fragmenting.
  • /recategorize re-derives every link’s category from its stored text (no re-fetching) to backfill the smarter taxonomy across the whole library.
  • Auto-collections on save — a saved link is auto-filed into the nearest collection by semantic similarity (the same vectors/threshold as /organize), but only when it isn’t already in one — a collection you set by hand is never overridden.
  • Web UI rebuilt — the private /dashboard and the public /list are now a React + shadcn/ui SPA (Vite + Tailwind v4) in a new dashboard-app/ project, built to /app/ and served by the same Worker. The Worker exposes a JSON API it calls (/dashboard/api/links, /dashboard/api/meta, /dashboard/api/mutate, /dashboard/api/action, all behind Cloudflare Access; public /list/api/links). The dashboard now filters by category/collection + text, edits category and collection inline, refreshes a link, deletes behind a confirm, and runs Recategorize / Organize / Find duplicates from buttons; List groups shared links by collection with category badges. Access gating is unchanged.

Saved-link replies now carry tap buttons (Star / Share) handled via Telegram callback_query updates — organize a link without typing its id.

  • /organize clusters links by embedding similarity, names each group with the model (e.g. password-vaults, dev-interviews), and assigns the name to uncollected members.
  • /dupes lists near-duplicate / overlapping links for review.

Enrichment now reads the link’s full content — the article body (via HTMLRewriter) and the GitHub README — not just metadata. The model also produces a multi-sentence details synopsis, and summaries/tags/key-points are grounded in real content.

Ask a question and get a short answer synthesized from your most relevant saved links, with citations ([#5]).

A Workers AI embedding model (@cf/baai/bge-base-en-v1.5) + a Vectorize index make /find hybrid: semantic matches (by meaning) ranked first, keyword FTS filling in. A query-instruction prefix and a score threshold keep results precise.

A private web dashboard at links.<domain>/dashboard behind Cloudflare Access — browse and manage every link by clicking (star, share, collection, and delete), with a second Worker-side JWT check as defense in depth.

  • /export md|csv|json sends your whole library to the chat as a file.
  • /share <id> publishes a link to List, a public page at /list.

Three tiers of new columns: auto-captured (domain, image, site_name, author, published_at, GitHub stars/language/license), AI-extracted (category, key_points), and user-set state (favorite, collection) with commands to set them and filter lists.

Replaced the original local Python bot with a single Cloudflare Worker.

  • Telegram webhook entry (no more long-polling); a Worker that wakes per request and validates a secret header.
  • Cloudflare D1 (SQLite + FTS5) for storage, via checked-in migrations. Existing links seeded across from the old local database.
  • Workers AI (@cf/google/gemma-4-26b-a4b-it, JSON mode) for summaries and tags, replacing local Ollama — with graceful degradation when the model is unavailable.
  • HTMLRewriter for OpenGraph/HTML scraping, replacing BeautifulSoup.
  • Single-user lock via ALLOWED_CHAT_ID in addition to the webhook secret.
  • Fixes: multi-word tags normalized to single tokens; reliable insert detection via INSERT OR IGNORE … RETURNING id; Workers AI output read from the chat-completions shape.