Distillate is now an autonomous research platform. Describe a hypothesis, and an AI agent runs experiments for you — training models, tuning hyperparameters, and reporting results. A desktop GUI lets you watch the frontier improve in real time, steer the agent, and compare runs across projects.
- Autonomous experiments: describe what you want to test, an agent runs it. Time-budgeted sessions with live metric tracking, automatic frontier detection (
best/completedruns), and structured insight extraction - Desktop IDE: Electron app with four tabs — Control Panel (frontier chart + live session status), Session (terminal), Results (run grid + insights), Prompt (PROMPT.md editor). Context-aware chat with Nicolas throughout
- Cloud & email (optional): sign up with
--emailto get experiment reports with embedded charts, daily paper suggestions, and weekly digests. Papers and experiments sync to the cloud for backup - One-click onboarding: install to live experiment in 60 seconds — new users see CTAs to launch a demo or connect their paper library
- Paper-experiment integration: link papers to experiments, discover relevant papers, credit papers via
inspired_byon runs — what you read informs what you try - Desktop IDE: four-tab Electron app (Control Panel, Session, Results, Prompt) with context-aware chat, stop generation, and full highlight display
- No API key needed: runs entirely through your Claude Code subscription (Max or Pro)
- Autonomous experiments: describe a hypothesis, Nicolas spawns an agent to test it. Time-budgeted sessions, live metric tracking, and automatic insight extraction
- Alchemy skill system: 9 slash commands — The Laboratory (
/survey,/conjure,/steer,/assay,/distill), The Library (/brew,/forage,/tincture), and/transmuteto bridge papers and experiments - Onboarding flow: sidebar CTA, welcome screen CTA, chat suggestion — all lead to a 3-step scaffold/GitHub/launch flow that gets you to a running experiment in under a minute
- Library setup wizard: "Connect your library" CTA in papers sidebar → Zotero credentials → reading surface choice (reMarkable / iPad / any device) → sync
- Paper-experiment cross-references: linked papers in experiment detail, linked experiments in paper detail, both clickable for navigation
discover_relevant_paperstool: keyword-matches your paper library against experiment goals, returns candidates with relevance reasonsinspired_byon runs:conclude_runaccepts a paper reference, auto-links it to the project- Stop generation: red stop button interrupts Claude via SDK, Nicolas says "Stopped. What should I do instead?"
- Context-aware suggestions: chat pills above the input update based on context — experiment (steer, analyze, compare) or paper (summarize, experiment ideas, similar papers)
- Link handling: external links open in system browser, arXiv/DOI links show popup with "Open in browser" or "Add to library queue"
- Windows support:
install.ps1script,npm run install:win, homepage auto-detects OS via user agent purge_hook_runstool: clean up accumulated noise from manual script executions- 49 MCP tools: paper library, experiments, bridge, and cleanup tools via the Distillate MCP server
- Monorepo: desktop app, MCP server, and Agent SDK in a single repository
- GitHub integration: public repos by default (
distillate-xp-prefix), GitHub flare in control panel
- Control Panel: metric chart with log scale toggle, export to PNG, session timer, goal chips
- Session tab: embedded xterm.js terminal attached to the running Claude Code agent
- Results tab: runs grid with research insights (key breakthrough, lessons learned, dead ends)
- Prompt tab: view and edit PROMPT.md with markdown rendering and syntax highlighting
- Three experiment states: running (green triangle), ready (purple circle), paused (gray square)
- Chat UX: purple presence dot on Nicolas messages, thinking spinner after tool completion, tool subtitles showing what's happening (search terms, project names, etc.)
- Full highlights: paper detail view shows complete highlights (no truncation)
- Keyboard shortcuts: Cmd+R refresh, Cmd+1-4 switch tabs, Cmd+E toggle sidebar, Cmd+K toggle chat
- Onboarding: first-use welcome with two clear paths — conjure an experiment or connect Zotero via
/init - Contextual suggestions: hints adapt to what you have (papers, experiments, or both)
- Rotating tips: one random skill tip per session
--report: reading insights dashboard — lifetime stats, weekly velocity, topic breakdown--export-state/--import-state: backup and restore tracked papers and reading history
- Scanner spurious runs: hooks fired on all Bash commands, creating ghost runs. Now gated by
DISTILLATE_SESSIONenv var — only fires inside Distillate-managed tmux sessions compare_projectsnull values: skipped non-"keep" runs and used wrong direction for loss metrics. Now includes all non-discarded runs with direction-aware best (min for loss, max for accuracy)- Unhashable list crash on delete: run names in set comprehensions crashed with list-valued hyperparameters. Defensive
str()wrapping - Highlights truncated at 3000 chars: desktop paper detail used the MCP tool's truncation limit. Now reads full highlights directly
- Links opened inside Electron app: no navigation interception. Added
will-navigate+setWindowOpenHandlerguards - Control panel disappeared on select:
isReadyvariable removed during refactor but still referenced, crashing the render - Tab hijack on polling: 15-second session poll re-rendered the detail pane, snapping back to Control Panel. Polling now only updates sidebar
- Sync 501 errors in DevTools:
triggerCloudSync()called.json()on non-ok responses - Dock icon too large in dev mode: now uses
.icnswith proper resolutions
- Cloud state sync: papers and experiments sync to the Distillate cloud API via granular endpoints with incremental pull (
?since=watermark) and additive merge - Run-level merge: cross-device sync merges individual runs — decisions advance monotonically (best > completed > crash), metrics and metadata fill gaps
- Email notifications: experiment reports on session completion (with embedded chart), daily paper suggestions, and weekly digest — each toggled independently
- Auto sync triggers: background sync after first app load, automatic push after experiment sessions end
- Supabase URL auto-derived: no manual configuration needed — the Supabase project ref is extracted from the anon key JWT
- Auto-detected decisions:
conclude_run()automatically marks runs asbest(frontier-improving) orcompleted— agents no longer decide keep/discard - State migration v1→v2: existing experiments re-evaluated chronologically to compute best/completed from the key metric frontier
- Backward compatible: old
keep/discardin runs.jsonl parsed ascompleted;bestdetermined from state migration - Commit format:
[best]prefix on frontier-improving runs — e.g.[best] baseline CNN: f1=0.42
- Chart Y-axis: scales to best runs only (discarded outliers no longer squish the frontier flat)
- Log-scale ticks: clean rounded values when data spans less than one decade
- Sidebar stability: experiments sorted by
added_atinstead oflast_scanned_at(no more reordering on reload) - Faster sidebar: removed auto-rescan from experiment list — state.json is authoritative, rescans only on session end and manual reload
- Session status: "Agent working — analyzing runs" instead of misleading "awaiting instructions"
- Enrichment prompts: cleaner output — one-sentence breakthroughs, no ALL CAPS, no Greek letters
--connectors: check status of all integrations (Zotero, email, Obsidian, reMarkable) at a glance--email: interactive email management with per-type toggles--sync-state: manual cloud sync trigger- Security: CORS tightened to localhost only, auth token verification on sensitive endpoints
- No breaking changes — existing paper libraries carry over unchanged
distillatenow requires Claude Code — install from docs.anthropic.com/en/docs/claude-code- Desktop app is optional — the CLI works exactly the same as before
- Read on any device: no longer requires a reMarkable tablet — read and highlight in the Zotero app on iPad, desktop, Android, or any device. Pick your reading surface during
--initsetup. reMarkable remains fully supported.
- Agent: dim magenta for verbose tool output, response truncation fix
- Email digest: trending section with top 3 papers, mobile-friendly layout
- Init wizard: WebDAV configuration step, reading surface choice
- Landing page: reMarkable 67E6 now optional, "any device" messaging
- Windows:
--scheduleshows Task Scheduler instructions instead of crashing - Lazy rmscene imports: Zotero-only users don't need rmscene/rmapi installed
- WebDAV fallback: catches all HTTP errors, visible retry output, manual upload detection
- Newsletter signup — the init wizard (
--init) now offers an optional email signup at the end
- HuggingFace summary fallback: papers get a real one-liner even without a Claude API key, using HF's AI-generated summaries
- Email trending: default limit tightened to 3 papers
- WebDAV PDF downloads broken since 0.4.4:
get_pdf_attachment()only matchedimported_fileandimported_urllink modes, missing WebDAV'slinked_urlattachments. Papers got stuck as "Awaiting PDF" instead of downloading.
Interactive agent mode — distillate becomes a research assistant.
- Nicolas — AI research assistant:
distillatenow launches an interactive REPL ("Nicolas") powered by Claude. Search papers, compare findings, get reading suggestions — all in natural language. - Add papers from the REPL: give an arXiv ID and it's added to Zotero, enriched with metadata, and synced on next run.
- HuggingFace Daily Papers: trending research with GitHub repo links, AI summaries, and community votes.
- Cross-paper synthesis: ask Nicolas to compare or synthesize across multiple papers in your library.
- Refresh metadata: agent can fix metadata gaps on existing papers via Semantic Scholar.
- Conversation memory: sessions persist locally for cross-session context.
distillatenow opens the agent REPL — usedistillate --syncfor the previous sync-only behavior- Optional extras removed —
[ai],[email], and[all]install extras are gone.pip install distillate(oruv tool install distillate) now includes everything
Paper index numbers, terminal colors, and reliability fixes.
- Paper index numbers: every paper gets a stable
[index]shown in--status,--digest,--suggest,--list, and emails — use it to target papers in commands - Paper lookup by index, citekey, or title:
--reprocess 3,--remove kindel,--refresh-metadata "DynaSpec"all work - Single-paper refresh:
--refresh-metadatanow accepts an optional query to refresh just one paper - PDF subfolder: annotated PDFs now saved to
Saved/pdf/(configurable viaPDF_SUBFOLDER), keeping notes and PDFs separate — auto-migrates existing files - S2 author backfill: papers with unknown authors are enriched from Semantic Scholar, with automatic citekey regeneration
- Terminal colors: bold bright-white titles on dark terminals, dim gray metadata lines — TTY-aware with dark/light background detection
- Awaiting PDF retry: re-checks Zotero children when the stored attachment returns 404 — fixes papers where the user adds a PDF after initial import
- Missing years in citekeys: extracts year from DOI patterns (e.g.
chemrxiv-2026-xxx) when Zotero has no date; uses S2yearfield as fallback whenpublicationDateis empty - Title cleaning: strips
: JournalNamesuffixes from Zotero web clipper titles (e.g. "Title: Neuron" → "Title") - Citekey for S2 authors: handles "First Last" name format (not just "Last, First") when generating citekeys from Semantic Scholar data
- Author preservation:
--refresh-metadatano longer overwrites S2-filled authors with empty Zotero creators - Refresh reporting: citekey changes for non-processed papers (queue, awaiting) are now reported instead of showing "up to date"
- Email stats missing pages/words: the main sync loop now auto-pushes state to the Gist after processing papers, so GH Actions emails have fresh reading stats (page counts, highlight word counts)
- Suggestion compute-once:
--suggest-emailnow calls Claude at most once per day — subsequent runs reuse cached suggestions (from local state or Gist) and just re-send the email. Saves API cost on retries and manual re-runs
- Traceback logging on crash: unhandled exceptions now log the full traceback to the log file / CI output, instead of only showing the one-line message
- CI uv cache warning: disabled uv cache in the email workflow to silence spurious "no lockfile" warnings
Handwriting OCR, personalized summaries, and Zotero collection filtering.
- Handwriting OCR via Claude Vision: handwritten margin notes are transcribed using Claude Haiku — renders ink onto the PDF page for context, then sends to the Vision API for accurate OCR
- Personalized AI summaries: reader's handwritten margin notes are fed to the summarizer, so AI-generated insights prioritize what you found interesting (not just what the paper says)
- Zotero collection filtering: scope Distillate to a specific Zotero collection (e.g. "To Read") via
ZOTERO_COLLECTION_KEY— only papers in that collection get picked up - Collection picker in init wizard:
--initnow lists your Zotero collections and lets you pick one to scope to
- Landing page restructure: separated core features ("Built in") from optional extensions ("Plug in what you need")
- Apple Vision / Pillow dependencies: handwriting OCR now uses Claude Vision instead — removed
pyobjc-framework-VisionandPillowoptional dependency group
Handwritten notes on PDFs, Windows compatibility, and Zotero WebDAV support.
- Ink layer on PDFs: handwritten strokes from reMarkable are now rendered onto the annotated PDF, preserving your margin notes and annotations alongside highlights
- reMarkable Paper Pro support: auto-detects Paper Pro's 227 DPI coordinate system and maps ink/highlights accurately (classic RM1/RM2 also supported)
- Typed notes from reMarkable: keyboard-typed text on the reMarkable is extracted and included in Obsidian notes under "Notes from reMarkable"
- Zotero WebDAV fallback: users storing attachments via WebDAV can now download PDFs automatically — configure
ZOTERO_WEBDAV_URL,ZOTERO_WEBDAV_USERNAME,ZOTERO_WEBDAV_PASSWORD - Auto-promote from suggestions: papers suggested via
--suggestthat are later added to Zotero are auto-promoted out of the suggestion pool
- PDF highlights invisible in Obsidian: fixed SYNC_HIGHLIGHTS overwriting annotated PDFs with originals, making highlights disappear
- Windows rmapi path leak:
rmapi puton Windows could use the full temp path as the document name — now detected and renamed automatically - Windows UTF-8 encoding: all file I/O now specifies
encoding="utf-8"andnewline="\n"to prevent corruption on Windows (thanks @davidlukacik!) - Tag pills in emails: abbreviated long category names (e.g. "Computer Science" → "CS"), smaller pill size
- Email footer overflow: condensed stats labels for mobile readability
- Suggestion emails include already-read papers:
_sync_tags()now marks papers as processed when Zotero tag isread - Year frontmatter garbled: dates like "8 September 2024" were parsed as "8 Se" — now extracts 4-digit year correctly from any format
--dry-run: removed vestigial flag that added maintenance burden without clear user value
UX fixes, CI hardening, and documentation improvements.
- Unknown CLI flags detected:
distillate --foonow prints an error instead of silently triggering a full sync - TTY output for
--reprocess,--dry-run,--backfill-s2: these commands now print to the terminal instead of silently logging to file - Tag pills in suggestion emails: topic tag pills are now actually rendered (were silently skipped)
- Queue health on empty queue: no longer shows "oldest: 0 days" when no papers are waiting
--send-digestfeedback: prints "digest not sent" when no papers processed recently instead of returning silently- Digest email mobile links: paper titles link to web URLs (works on any device) with Obsidian as a secondary link
- CI matrix: added Python 3.11 and 3.13 (matching pyproject.toml classifiers)
- Publish safety gate: PyPI publish workflow now runs tests before building
.env.example: addedSYNC_HIGHLIGHTSand uncommentedOBSIDIAN_VAULT_NAME- README config table: added
OBSIDIAN_VAULT_NAMEsetting pyproject.toml: added Changelog URL to project URLs
Smart metadata enrichment from Semantic Scholar.
- Smart metadata from Semantic Scholar: auto-completes missing publication dates, venues, and citation counts from Semantic Scholar when Zotero data is incomplete
- Citekey regeneration: when Semantic Scholar fills a missing date, the citekey is regenerated (e.g.
liu_embeddings→liu_embeddings_2024) and all files are renamed automatically --refresh-metadatawith S2 enrichment: re-queries Semantic Scholar for papers missing dates or S2 data, in addition to re-fetching from Zotero
- Landing page copy refresh: new ASCII flow ending, section subtitles ("Save. Read. Highlight. Distill."), redistributed copy
Citekey rename, metadata refresh, and naming consistency fixes.
- Citekey rename on metadata sync: when a paper's citekey changes in Zotero (e.g. after adding a publication date), Distillate renames note + PDF files, updates reading log wikilinks, and PATCHes Zotero linked attachments — no orphaned files or broken links
--refresh-metadata: new command to re-extract metadata from Zotero for all tracked papers, with verbose progress output — useful for one-time migrations and fixing stale citekeys- Title sync: title changes in Zotero propagate to note frontmatter and reading log entries
- Zotero PATCH helpers:
update_obsidian_link()andupdate_linked_attachment_path()update existing Zotero attachments in place (safer than delete + recreate)
- Citekey date parsing:
_generate_citekey()now extracts 4-digit years from any date format (10/2024,12 February 2026,2024-10-15) — previously only worked withYYYY-*format - Inbox PDFs use citekey:
save_inbox_pdf()anddelete_inbox_pdf()now use citekey-based filenames, consistent with Saved folder naming - Frontmatter title updates:
update_note_frontmatter()now syncs thetitle,citekey, andpdffields - Obsidian Bases template v4: fixed sort syntax and added
file.ext == "md"filter to exclude PDFs from Base view
Bug fixes, safety improvements, and dead code cleanup.
distillate --version: was stuck at "0.1.7" — now reads version from package metadatadate_readfrontmatter: papers processed in the main sync pipeline now use the actual processing date instead of defaulting to todaydelete_attachment("")crash: papers fetched from arXiv (no Zotero PDF) no longer trigger an invalid API call whenKEEP_ZOTERO_PDF=false- Highlight OCR crash:
_recover_pdf_textno longer crashes when a highlight normalizes to an empty string (e.g. pure hyphens) - Double-sleep on Zotero 429: when Zotero sends a
Retry-Afterheader, we no longer also apply the exponential backoff delay - Citekey change detection: Better BibTeX citekey changes are now picked up by metadata sync
- Annotation create-before-delete: Zotero highlight annotations are now created before deleting old ones, preventing data loss if the POST fails mid-batch
- Missing end marker protection: if
<!-- distillate:end -->is accidentally deleted from a note, Distillate warns and appends instead of silently deleting everything after the start marker
- Removed dead
_themes()code: removed_themes(),generate_monthly_themes(),create_themes_note(), andsend_themes_email()— unreachable since v0.1.7 - Public API functions:
zotero_client.build_note_html()andremarkable_client.sanitize_filename()are now public (were private but called cross-module) - Removed duplicate migration calls:
ensure_dataview_note(),ensure_stats_note(),ensure_bases_note()no longer run inside the per-paper loop (already run at sync start) - Module-level marker constants:
MARKER_STARTandMARKER_ENDpromoted to module-level constants inobsidian.py
- Awaiting PDF retry: papers stuck in
awaiting_pdfstatus are now correctly retried when a PDF attachment appears in Zotero
Zotero Round-Trip: highlights flow back from reMarkable to Zotero, citekey-based naming, and Obsidian plugin compatibility.
- Zotero highlight back-sync: highlights made on reMarkable are written back to Zotero as searchable annotations — visible in Zotero's built-in PDF reader and compatible with the Zotero iOS/Android apps
- Citekey-based file naming: notes and annotated PDFs use Better BibTeX citekeys (e.g.
einstein_relativity_1905.md) for compatibility with the Obsidian Zotero Integration plugin ecosystem - Note merge for plugin coexistence: when a note already exists (e.g. from the Zotero Integration plugin), Distillate appends its sections between
<!-- distillate:start/end -->markers instead of overwriting - Obsidian Bases support: generates a
Papers.basefile for native table views in Obsidian 1.9+ (alongside existing Dataview template) --backfill-highlights [N]: back-propagate highlights to Zotero for already-processed papers (processes last N, default: all)--listcommand: list all tracked papers grouped by status
- Tag sanitization: Zotero tags like
Computer Science - Artificial Intelligencebecome nested Obsidian tags (computer-science/artificial-intelligence) instead of appearing crossed out - Citekey fallback: when Better BibTeX isn't installed, generates citekeys automatically from
surname_word_year - Frontmatter additions: notes now include
citekey,year, andaliasesfields for richer Obsidian integration - Saved/ folder rename: processed papers output folder changed from
Read/toSaved/for clarity SYNC_HIGHLIGHTSconfig toggle: control highlight back-propagation (default: on)- Duplicate prevention: Zotero annotations tagged
distillateare cleaned up before re-sync to prevent duplicates - Reading log citekey links: reading log uses
[[citekey|title]]wikilinks for stable references
Friction reduction, power-user documentation, and tech debt cleanup.
--statusshows Read/ folder: papers waiting in Distillate/Read/ on your reMarkable are now listed, so you can confirm a paper is ready for processing- Power users guide: new standalone page at distillate.dev/power-users.html documenting GitHub Actions automation, engagement scores, reprocessing, custom AI models, storage management, debug mode, and state sync
- Better no-highlights guidance: when no highlights are found, shows a numbered checklist (text recognition, highlighter tool,
--reprocess) instead of a one-line warning - Awaiting PDF explanation:
--statusand--listnow explain why papers are stuck and what to do about it - Note overwrite on re-sync:
create_paper_notenow overwrites existing notes instead of silently skipping — no more stale notes after re-sync - First-run
--statusonboarding: shows "No papers tracked yet. Rundistillate --initto get started." when state and config are empty - Unified suggestion title-matching: extracted shared
match_suggestion_to_title()helper, replacing three duplicate implementations acrossdigest.pyandmain.py extract_insightsuses Haiku: key learnings extraction now uses the fast model (Haiku) instead of Sonnet — equivalent quality at lower cost- GH Actions workflow fixes: added
DISTILLATE_CONFIG_DIRandOBSIDIAN_VAULT_NAMEto workflow environment
--themesentry point disabled: monthly research themes synthesis is removed from--help, CLI routing, and GH Actions workflow. The underlying code is preserved for future use when users have enough papers to make it useful.
First-impression hardening: make the first 5 minutes bulletproof.
.envfile permissions: config directory created with 0700,.envfile set to 0600 after every write — API keys no longer world-readable- PDF delete guard: Zotero PDF is no longer deleted when local save fails — prevents data loss when
KEEP_ZOTERO_PDF=false
--listcommand: list all tracked papers grouped by status (on_remarkable, processing, awaiting_pdf, processed)--remove "Title"command: remove a paper from tracking with substring match and confirmation prompt--statusqueue contents: shows individual paper titles with age in days (up to 10)
- Clean terminal output: TTY-aware logging — sync shows progress milestones (
Checking Zotero...,Uploading: "Title",Extracting highlights... 14 found,Done: 2 sent, 1 synced) instead of raw log lines; full logs go to~/.config/distillate/distillate.log - Claude data disclosure: init wizard Step 5 now mentions that highlights and abstracts are sent to the Claude API
- Text recognition prerequisite: init wizard Step 2 and README mention enabling text recognition on reMarkable
- Intermediate state save: Step 2 saves
processingstatus after Zotero tag change, resumes on restart — prevents papers stuck in limbo after crashes - "My Notes" section: Obsidian/markdown notes now include a
## My Notessection at the end - DOI link in notes: papers with a DOI get an "Open paper" link at the top of the note
_sync_statetimeout: Gist sync now times out after 30 seconds instead of hanging indefinitely- PDF download logging: failed arXiv/biorxiv downloads now log a warning instead of silently failing
- Expanded
--help: commands grouped by category (core, management, advanced) with descriptions - Local-first positioning: landing page and README now emphasize that notes stay on your machine
- Troubleshooting guide: README section covering common issues (rmapi not found, empty highlights, API errors)
- Resend custom domain: init wizard mentions free tier includes 1 custom domain
- Config table: README now documents
DIGEST_FROM,KEEP_ZOTERO_PDF,LOG_LEVEL,STATE_GIST_ID
--suggestpolish: structured terminal output matching--digeststyle — per-paper blocks with title, reason, days in queue, and citation count- First-run guidance: helpful message on first sync explaining watermark and pointing to
--import - Missing API key UX:
--suggestwithout Anthropic key now shows a clear message instead of silently failing
- Suggest failure no longer demotes: Claude API errors no longer remove previously promoted papers from your reMarkable
--scheduleworks for pip installs: plist generation is now inline Python instead of shelling out to a bundled script- OG URL: fixed
og:urlmeta tag to point to distillate.dev instead of GitHub Pages
- Empty highlights warning: prints "Is text recognition enabled on your reMarkable?" when no highlights are found
- Import progress: shows per-paper progress (
[3/47] Uploading: Paper Title...) and separates papers awaiting PDF in final count --statusconfig clarity: missing optional features labeled as "Optional" instead of appearing as issues--statusempty queue hint: suggests--importwhen queue is empty- Init Step 5 skip hint: makes it clear optional features can be skipped and configured later via
--init - Init Step 3 fix: removed stale wiki-links claim, fixed Obsidian vault path navigation instructions
- Init Step 5 DIGEST_FROM: mentions custom sender domain option when Resend is configured
- Register output formatting: consistent indentation with the rest of the wizard
- Top-level error handler: unhandled exceptions show a clean message with a link to report issues
- README model IDs: synced config table with actual default model identifiers
--importcommand: import existing papers from your Zotero library (interactive selection or--import all)--schedulecommand: set up, check, or remove automatic syncing (launchd on macOS, cron instructions on Linux)- Init seed: setup wizard now offers to import existing papers at the end
_upload_paper()helper: extracted reusable per-paper upload logic from sync loop- Command order: commands now follow workflow lifecycle across
--help, landing page, and README - ASCII flow: concrete outputs ("Notes + highlights + annotated PDF") instead of vague ending
- Title propagation: changing a paper title in Zotero now updates
--statusand promoted papers list stat_document()false negatives: papers stuck in promoted list because empty rmapi stat output was treated as failure- Corrupt state recovery: corrupted
state.jsonis backed up and reset instead of crashing - rmapi timeout handling: network timeouts now show a clean error instead of an unhandled exception
--statuspromoted list: show last 3 promoted papers, one per line (was all on one unreadable line)- ASCII flow refresh:
$ distillate # turn papers into notes!shell-comment style
- Cross-page highlight merging: highlights that span page breaks are now joined into a single passage
- Citation data surfacing: Semantic Scholar citation counts shown in digest emails, suggestion prompts, and
--statusoutput - Richer
--status: config warnings, awaiting PDF titles, pending promotions - Smart
--initre-run: detects existing config and offers shortcut to optional features; shows existing values as defaults
- Zotero API retry logic: exponential backoff on 5xx, 429, and connection errors
- Friendly error messages: connection failures and auth errors show human-readable messages instead of stack traces
- Config validation: warnings for missing directories, malformed API keys
- Duplicate detection: skip papers already tracked by DOI or title
- Item type filtering: skip non-paper items (books, webpages, patents, etc.)
- Palatino serif typography, new tagline, FileHeart favicon
- Integration wordmarks with brand colors, Semantic Scholar card
- Open Graph and Twitter Card meta tags, colophon
--statuscommand: show queue health, reading stats, and config summary--promotecleanup: demote old picks before promoting new ones- rmapi auth detection: detect expired tokens and prompt re-registration
- Suggest-then-promote flow: GitHub Actions picks papers, local
--syncpromotes them on reMarkable - Engagement scores: quantify reading engagement from highlight density, coverage, and volume
- Email redesign: lead with content, stats as footer, unified styling
Initial public release.
- Zotero to reMarkable sync: automatically upload new papers from Zotero to reMarkable
- Highlight extraction: parse highlighted text from reMarkable
.rmfiles via rmscene - Annotated PDFs: overlay highlights on the original PDF using PyMuPDF text search
- Markdown notes: generate structured notes with metadata, highlights grouped by page, and optional AI summary
- Reading log: auto-updated log of all read papers, sorted by date
- AI summaries: one-liner, paragraph summary, and key learnings via Claude (optional, requires Anthropic API key)
- Paper suggestions: AI-powered daily reading suggestions from your queue
- Email digest: weekly reading digest and monthly research themes via Resend (optional)
- Engagement scores: quantify reading engagement from highlight density, coverage, and volume
- Semantic Scholar enrichment: citation counts and metadata via S2 API
- Obsidian integration: wiki-links, Dataview templates, reading stats
- Plain folder mode: alternative to Obsidian — just markdown notes and PDFs
- Setup wizard: interactive
distillate initfor first-time setup - Scheduling: launchd (macOS) and cron (Linux) support for automatic syncing