← Back to distillate.dev

Power Users Guide

CLI reference, skills, experiments, and advanced configuration.

Nicolas — The CLI Agent

Run distillate with no arguments to enter the Nicolas REPL — an interactive research agent powered by Claude Code. Nicolas can search your paper library, launch experiments, and synthesize insights across your work.

$ distillate
─── ⚗️  Nicolas ──────────────────────────────
Your research alchemist.
🧪 4 experiments · 12 runs · 1 running
📚 42 papers read · 7 in queue

Single-turn mode: pass a query as an argument.

$ distillate "what papers discuss attention mechanisms?"

Skills

Type a skill name in the REPL to invoke it. Skills are organized into two areas:

Laboratory (experiments)

/conjure Launch a new autonomous experiment /steer Guide a running agent — adjust goals or direction /assay Deep analysis of experiment results /distill Extract insights from session histories /survey Scan all experiments for new runs and breakthroughs /transmute Turn paper insights into experiment ideas

Library (papers)

/brew Sync papers from Zotero, extract highlights, generate summaries /forage Browse trending research, get reading suggestions /tincture Deep extraction from a single paper

Experiments

Distillate experiments are git repos where autonomous Claude Code agents run ML training loops. Each commit is one run. Results are tracked in .distillate/runs.jsonl.

Launching an experiment

# In the Nicolas REPL:
> /conjure tiny-matmul --duration 30m

# Or via the CLI:
$ distillate "conjure an experiment to classify sigma factors"

This creates a git repo, writes PROMPT.md (what to optimize), installs CLAUDE.md (the agent protocol), and spawns a Claude Code session in tmux.

Experiment structure

MCP tools

Each experiment repo has a .mcp.json that connects the agent to Distillate's tool server. The agent uses these tools to track runs and save insights:

start_run Announce a new run with description and hypothesis conclude_run Record results with duration tracking save_enrichment Save structured research insights scan_project Sync run state with the desktop app annotate_run Add notes or hypotheses to a specific run

Engagement Scores

Every processed paper gets an engagement score from 0 to 100, measuring how deeply you interacted with it. The score appears in your notes (YAML frontmatter), --status output, --digest, and email digests.

Three components, weighted:

Each component is normalized to 0.0–1.0, then the weighted sum is scaled to 0–100:

score = round((density * 0.3 + coverage * 0.4 + volume * 0.3) * 100)

A paper you skimmed lightly might score 15–25. A paper you highlighted thoroughly across most pages will score 70+. The score is a quick signal for which papers you actually engaged with versus those you just scanned.

Reprocessing

Use --reprocess to re-extract highlights and regenerate notes for a paper that's already been processed. Common reasons:

# Substring match on the paper title
$ distillate --reprocess "Attention Is All"

What it does: re-downloads the bundle from Saved/ on your reMarkable, re-extracts highlights, re-renders the annotated PDF, regenerates the AI summary, and updates both the markdown note and the reading log.

Custom AI Models

Distillate uses two model tiers, configurable via environment variables in your .env:

# In your .env file:
CLAUDE_SMART_MODEL=claude-opus-4-6
CLAUDE_FAST_MODEL=claude-sonnet-4-5-20250929

Without ANTHROPIC_API_KEY set, sync pipeline AI features (summaries, suggestions) use abstracts as fallback. The Nicolas agent uses Claude Code instead — no API key needed.

Storage Management

The KEEP_ZOTERO_PDF setting controls whether the original PDF stays in Zotero cloud after being uploaded to your reMarkable.

# In your .env file:
KEEP_ZOTERO_PDF=false

This is useful if you're on Zotero's free tier (300 MB storage). The safety order is: the PDF is first saved to your local Distillate/Inbox/ folder, then uploaded to reMarkable. Only after both succeed is the Zotero cloud copy removed. Your local copy is always preserved.

Debug Mode

Set LOG_LEVEL=DEBUG for verbose output:

# One-off
$ LOG_LEVEL=DEBUG distillate

# Persistent (add to .env)
LOG_LEVEL=DEBUG

Behavior depends on how distillate is running:

Debug output includes: rmapi commands and responses, API call details, file read/write operations, and state changes. Useful for diagnosing issues like "why didn't my paper sync?" or "why are highlights missing?"

State Sync and Backup

Distillate tracks all your papers and experiments in a local state.json file. The --sync-state command syncs it with Supabase cloud — keeping the same reading state across your laptop and desktop, and enabling cloud email features.

# Sync state with the cloud
$ distillate --sync-state

Requires email registration in the desktop app (Control Panel → Updates). State sync uses the same Supabase account.

Corrupt state recovery

If state.json becomes corrupted (malformed JSON), distillate automatically backs it up as state.json.bak and starts with a fresh state. No data is silently lost — the backup file preserves whatever was there.

Manual inspection

state.json is plain JSON. You can read it directly, pipe it through jq, or edit it if you know what you're doing:

# Pretty-print your state
$ cat state.json | jq .

# Count processed papers
$ cat state.json | jq '.documents | length'

Zotero Highlight Back-Propagation

When you process a paper, Distillate writes your reMarkable highlights back to Zotero as native annotation items. These are visible in Zotero's built-in PDF reader on desktop and mobile.

How it works

  1. Highlighted text is extracted from the reMarkable .rm files
  2. Each highlight is located in the original PDF via text search
  3. Bounding-box coordinates are converted to Zotero's format (PDF bottom-left origin)
  4. Annotations are created via the Zotero Web API with itemType: "annotation"

All Distillate-created annotations are tagged distillate. On re-sync, existing Distillate annotations are replaced (your manual annotations are never touched).

Configuration

# In your .env file (default: true)
SYNC_HIGHLIGHTS=true

Set to false to disable highlight back-propagation entirely.

Backfilling existing papers

If you processed papers before v0.2.0, you can back-propagate their highlights retroactively:

# Back-propagate highlights for all processed papers
$ distillate --backfill-highlights

# Or just the last 5
$ distillate --backfill-highlights 5

Papers that already have synced highlights are skipped. To force a re-sync, use --reprocess instead.

Requires the document on reMarkable Backfill downloads the document bundle from your reMarkable's Saved/ folder. If you've deleted the document from reMarkable, backfill will skip it.

Metadata Enrichment

When a paper is added, Distillate queries Semantic Scholar to fill gaps in the Zotero metadata. This happens automatically during sync and can also be triggered manually.

What gets enriched

Zotero is always the source of truth: S2 only fills empty fields, never overwrites existing data.

Refresh all papers

Use --refresh-metadata to re-fetch metadata from both Zotero and Semantic Scholar for all tracked papers:

$ distillate --refresh-metadata

This is useful after editing papers in Zotero (adding dates, fixing titles) or as a one-time migration to enrich older papers with S2 data. Shows progress and only reports papers that changed.

Citekey Naming

Notes and annotated PDFs are named using citekeys — short identifiers like einstein_relativity_1905 instead of full paper titles. This makes filenames predictable and compatible with Obsidian plugins that use citekeys.

Better BibTeX integration

If you use Better BibTeX for Zotero, Distillate reads the citekey from the item's extra field (where Better BibTeX stores it as Citation Key: AuthorYear).

Automatic fallback

Without Better BibTeX, Distillate generates a citekey from the first author's surname, the first meaningful word of the title, and the year. Accented characters are normalized (e.g. Lála → Lala, Müller → Muller):

# "Attention Is All You Need" by Vaswani et al., 2017
→ vaswani_attention_2017

# "Biology needs to become prospective" by Avasthi, 2026
→ avasthi_biology_2026

Automatic rename

When a paper's citekey changes — because you edited the date in Zotero, or Semantic Scholar filled a missing year — Distillate renames everything automatically:

Obsidian Plugin Compatibility

Distillate is designed to complement — not replace — existing Obsidian plugins for academic workflows.

Zotero Integration plugin

The Obsidian Zotero Integration plugin creates notes from Zotero items. If a note with the same citekey already exists when Distillate processes a paper:

This means both tools can write to the same note without conflicts.

PDF++

PDF++ provides enhanced PDF viewing in Obsidian. Distillate's annotated PDFs are stored alongside notes in Distillate/Saved/ with citekey filenames, making them easy to reference from PDF++ links.

Obsidian Bases

Distillate generates a Papers.base file for Obsidian Bases (available in Obsidian 1.9+), providing a native table view of all your papers with columns for title, dates, engagement score, and highlight counts. The existing Dataview template is also still generated for users on older Obsidian versions.

Built with love and coffee by Romain Lacombe.