Command Line

~9 min read

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.

Common pattern

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:

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

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.

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.

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

Scripting tips

See also REST API for using the same operations over plain HTTP.