Command Line
Command Line
lettuce ships as a CLI for the operations that don’t need a live session: building a
static site, deploying it, scaffolding, linting, and scriptable folder management. For
interactive work see the Agent reference.
cd ~/Notes first, then run subcommands without a vault argument. The vault is a
positional argument that most subcommands default to the current directory.
Subcommands
serve: start the agent server
lettuce serve [vault] [--port 51234]
Starts the local MCP and REST server. Lettuce.app starts the same server automatically
when you open it, so a separate lettuce serve is only needed headless or in CI. It also
runs the schedule poller (every 60s), so any > [!ai] directives you’ve scheduled fire
even with no GUI open — the wiki maintains itself on a long-lived headless server.
publish: build a static site
lettuce publish [vault] [--out _site] [--all] [--base-url https://you.example.com] [--contact you@example.com]
Renders notes with published: true (or all notes with --all) into a self-contained
static site: search, graph, tag pages, sitemap.
deploy: push to GitHub Pages
lettuce deploy [vault] --repo owner/repo [--remote URL] [--branch gh-pages] [--message "Weekly update"] [--token $GITHUB_TOKEN] [--private]
Builds and pushes to the configured branch. Token can come from --token or the
GITHUB_TOKEN env var.
lint: vault hygiene
lettuce lint [vault] [--suggest] [--strict]
Reports broken [[wikilinks]], orphan notes, untagged notes (when a lint: schema is in
index.md), and tag typos. --suggest asks your configured AI provider for tag suggestions
on untagged notes. --strict makes the command exit non-zero when there are warning-level
findings (info-level ones like untagged-note stay advisory), so a CI job can gate on them
(default is advisory — it prints and exits 0).
--semantic runs the LLM health check (contradictions, stale claims, missing concepts, open
questions, sources to ingest). Add --file to act on it: each new finding is spliced into the
right note as a > [!ai] directive (deduped against .lettuce/lint-state.json, so re-runs don’t
flood), turning the findings into a self-draining work queue an agent picks up via pending_directives.
health: a 0–100 wiki-health score
lettuce health [vault] [--json]
A single deterministic dial — link health (few orphans), lint cleanliness (few warnings), and tag
coverage — to track whether the wiki is getting healthier over time. Good as a CI gauge or a
dashboard line; the wiki_health MCP tool returns the same breakdown.
maintain: one-shot autonomous health pass
lettuce maintain [vault]
Equivalent to lint --semantic --file: runs the semantic health check and files each new finding as
a deduped > [!ai] directive. Cron/launchd-friendly — point a daily job at it and the wiki keeps its
own backlog. You can also schedule it inside the vault: a .lettuce/schedules.yml rule with
action: lint-semantic (or its alias action: maintain) runs the same pass on the server’s poll
cadence (no GUI needed).
slides: render a deck
lettuce slides note.md [--out deck.html] [--pdf | --pptx]
Splits at --- thematic breaks (falling back to ## headings) and produces a self-contained,
navigable HTML deck — code highlighting, math, and Mermaid are inlined, so it needs no network.
Export to PDF / PPTX with --pdf / --pptx: these shell out to
marp-cli (npm i -g @marp-team/marp-cli) for the portable
formats — a clear error if it isn’t installed. Add marp: true to the note’s frontmatter so it
paginates. The default (no flag) is the dependency-free HTML deck.
Marp-compatible. A deck written for Marp renders faithfully:
Global directives in frontmatter —
marp: true,theme:,paginate:,header:,footer:,class:,size:(16:9/4:3).Spot directives as HTML comments —
<!-- paginate: true -->persists from that slide on; the_prefix scopes it to the current slide (<!-- _class: lead -->).leadcenters a title slide;invertflips the colors.Speaker notes — any HTML comment that isn’t a directive becomes a presenter note. Press
Nin the deck to toggle the notes panel; notes never render on the slide itself.
canvas: render a .canvas board
lettuce canvas board.canvas [--out board.html]
Renders an Obsidian-style .canvas document (positioned nodes + edges) to a self-contained,
pannable/zoomable HTML page — drag to pan, scroll to zoom, press 0 to fit. Text nodes render as
Markdown (wikilinks, code, math, Mermaid, and chart blocks all work); file/link nodes become safe
links; edges draw as arrows with optional labels. No network or sidecar assets.
wiki init: scaffold a new wiki
lettuce wiki init [dir] [--force] [--agents]
Drops the four LLM-wiki files (CLAUDE.md, index.md, log.md, sources/README.md) into a
directory; --agents also writes a tailored AGENTS.md. See CLI cookbook/07 lettuce wiki.
ingest: pull a source into sources/
lettuce ingest <url | file | dir> [--vault dir]
Converts a source to Markdown and writes it to the raw sources/ layer (with provenance
frontmatter), appending a dated log.md entry if the vault has a ledger. Pandoc-based (requires
pandoc on PATH for non-markdown formats).
URL — fetches the page, runs readability, downloads referenced images locally.
Local file —
lettuce ingest paper.docxconverts a single file. Supported: markdown, text, HTML, PDF (text-layer extraction via PDFKit — a scanned/image-only PDF errors with a clear “OCR it first”),docx,odt,rtf,epub,org,rst, LaTeX.Directory —
lettuce ingest ./inboxingests every supported file in the folder (batch).
Ingest is de-duplicating: re-ingesting the same source (identical body, even on a later day)
reuses the existing note instead of writing a copy, two different sources that slugify to the same
name get a short content-hash suffix rather than silently clobbering each other, and — when an
embeddings provider is configured — a near-duplicate (same article from another URL) is detected
by cosine similarity and reused instead of written. Ingesting a new source also fires any
file:created:sources/** trigger — which splices that trigger’s > [!ai] prompt into the new
note as a directive (e.g. “summarize and link this source”). A connected MCP agent picks the
directive up via pending_directives and does the actual digesting.
search: full-text search
lettuce search <vault> <query…> [--limit N] [--semantic | --hybrid]
FTS5/BM25 search over a vault. Supports "phrases", OR/NOT, and -exclude. See
CLI cookbook/10 lettuce search. --semantic switches to vector search (find by meaning);
--hybrid fuses vector + keyword ranking. Both need an embeddings provider configured (see embed).
embed: build the semantic-search index
lettuce embed [vault]
Embeds new/changed notes through the configured OpenAI-compatible embeddings endpoint and caches the
vectors in .lettuce/vectors.json, so search --semantic (and the semantic_search MCP tool) work.
It also builds a passage index (.lettuce/passages.json) — each note split into section-sized
chunks and embedded individually — which answer uses to retrieve the specific passage that bears
on a question rather than whole notes. A local endpoint like Ollama needs no key; set it with
LETTUCE_EMBED_BASE_URL / LETTUCE_EMBED_MODEL. Re-run after large edits to refresh both indexes.
answer: ask the wiki a question
lettuce answer <vault> <question…>
Retrieves the most relevant material (passage-level when an embeddings provider is configured, else keyword search over whole notes) and synthesizes a cited answer grounded in it — prints the answer and its sources. With embeddings it ranks individual passages and feeds the model just the matching sections (tighter, more on-topic context than whole-note prefixes), grouped back by source page for citation. Requires an LLM provider (Settings → AI providers, or env). The RAG capstone of the query loop.
eval: measure retrieval/answer quality
lettuce eval <vault> [--k 6] [--json]
Scores the vault’s eval/questions.jsonl (one JSON object per line: {question, expected_paths, must_contain}) and prints a scorecard — recall@k, precision@k, MRR, hit-rate, and (in live mode)
answer faithfulness. The instrument for checking whether a change to chunking, ranking, or prompts
actually improves retrieval rather than guessing. --json emits the full report.
By default it runs retrieval-only (lexical) — free and deterministic, so it’s safe in CI. Set
LETTUCE_EVAL_LIVE=1 to score against your configured embedding/LLM providers (live). Every run
writes a timestamped scorecard to eval/runs/<YYYYMMDD-HHMMSS>.json so you can diff quality across
prompt/model changes over time.
related: meaning-based link suggestions
lettuce related <vault> <note-path>
Lists the notes semantically nearest to a note that it isn’t already linked to — use it to thread a
page into the graph. Requires an embeddings provider (see embed).
folder: manage groups from the shell
lettuce folder list # one per line
lettuce folder add <path> # create incl. intermediates
lettuce folder rename <from> <to> # move or rename
lettuce folder delete <path> --yes [--hard] # trash by default; --hard removes
[--vault <path>] # defaults to cwd
Returns non-zero on failure, so shell scripts can detect errors. Soft-deletes route
through .trash/ (reversible from Finder).
serve-site: preview a published site
lettuce serve-site _site [--port 8000]
Plain static file server. Useful for checking publish output before deploy.
directive-worker: auto-answer [!ai] directives in the background
lettuce directive-worker [vault] [--worker ID] [--once] # foreground loop
lettuce directive-worker install [vault] # always-on LaunchAgent
lettuce directive-worker uninstall | status
Long-polls the vault and answers > [!ai] directives with the configured LLM, closing the
in-note mailbox loop without an interactive session. See CLI cookbook/13 lettuce directive-worker.
agents / keys / docs / render: housekeeping
lettuce agents init [--vault path] [--force] # scaffold/refresh the vault's AGENTS.md brief
lettuce keys add|remove|list … # manage scoped agent API keys (same Keychain the app uses)
lettuce docs publish [--out PATH] [--base-url URL] # build the bundled MCP/CLI/REST reference notes into a static site
lettuce render <input.md> [--out OUT.html] [--title T] # one Markdown file → a self-contained styled HTML page
Environment variables
LETTUCE_EMBED_BASE_URL/LETTUCE_EMBED_MODEL— OpenAI-compatible embeddings endpoint (a local one like Ollama needs no key) forembed/semantic_search/answer.LETTUCE_EVAL_LIVE=1— runlettuce evalagainst the live providers (default is free, deterministic, retrieval-only).LETTUCE_RERANK_URL(+ optionalLETTUCE_RERANK_KEY/LETTUCE_RERANK_MODEL) — a hosted cross-encoder/rerankendpoint; when set, theanswerMCP tool’srerankparam uses it instead of the LLM listwise reranker.
Scripting tips
Combine them:
lettuce folder add Projects/2026/Q1 && lettuce publish .is a perfectly fine Makefile target.CI / GitHub Actions: install
lettuce, runlettuce publishthenlettuce deploy. Pin a version per release.The
folderfamily exits non-zero on failure. The older commands currently print to stderr but exit 0; watch for that when wrapping inset -escripts.The vault is a positional argument (
lettuce publish ~/Notes), so pass the path directly when you can’tcdfirst. Onlyingest,agents init, andfoldertake an explicit--vaultflag.
See also REST API for using the same operations over plain HTTP.