Distillate — Power Users Guide
Description: Advanced configuration, automation, and troubleshooting for Distillate.

← 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

- `PROMPT.md` — describes the research question, target metric, and constraints
- `CLAUDE.md` — the autonomous agent protocol (run tracking, commit discipline, insight saving)
- `.distillate/runs.jsonl` — structured log of every run with metrics, hyperparameters, and reasoning
- `.distillate/llm_enrichment.json` — research insights (key breakthrough, lessons learned, dead ends)
- `RESULTS.md` — human-readable summary updated after each run

### 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:

- Highlight density (30%) — highlights per page, saturates at 1 highlight per page
- Page coverage (40%) — fraction of pages that have at least one highlight
- Highlight volume (30%) — absolute number of highlights, saturates at 20

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:

- After enabling text recognition on reMarkable — highlights made before text recognition was enabled won't have extractable text. Enable it, then reprocess.
- After upgrading distillate — newer versions may have improved highlight extraction or note formatting.
- To regenerate AI summaries — if you changed the model (see Custom AI Models below) or want fresh summaries.

    # 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`:

- `CLAUDE_SMART_MODEL` (default: `claude-sonnet-4-5-20250929`) — used for paper summaries and one-liner descriptions
- `CLAUDE_FAST_MODEL` (default: `claude-haiku-4-5-20251001`) — used for paper suggestions and key learnings

    # 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.

- `true` (default) — PDF remains in your Zotero library
- `false` — PDF is deleted from Zotero cloud after it's confirmed saved locally and uploaded to 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:

- Interactive terminal (TTY) — debug output goes directly to the console
- Scheduled / non-TTY — logs go to `~/.config/distillate/distillate.log`

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

- Publication date — filled from S2 when Zotero has no date (fixes incomplete citekeys like `liu_embeddings` → `liu_embeddings_2024`)
- Venue — journal or conference name, filled when Zotero's publication field is empty
- Citation counts — total and influential citations, always updated from S2

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:

- Note and PDF files in `Distillate/Saved/` and `Distillate/Inbox/`
- Wikilinks in the Reading Log
- Linked attachments in Zotero (Obsidian link and linked PDF)
- Frontmatter fields (`citekey`, `pdf`)

## 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:

- Existing content is preserved
- Distillate appends its sections (highlights, AI summary, key learnings) between `<!-- distillate:start -->` and `<!-- distillate:end -->` markers
- On re-sync, only the content between markers is replaced

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.