CLI reference
Every python3 -m llmwiki <subcommand> — with every flag, realistic examples, and expected output. If a command isn't listed here it isn't shipping. This page is generated against the live argparse tree, so adding a flag without documenting it will fail the guardrail test.
Global flags: -h / --help on every command, --version at the root.
llmwiki --help groups commands into six lifecycle sections (same labels and order as the table below). Canonical loop: ingest (sync / add) → summarise (synth) → review candidates → publish (build). synth does not rebuild the site; run build afterwards when Home / Analytics should refresh.
Top-level
python3 -m llmwiki --version # → llmwiki <version>
python3 -m llmwiki --help # lifecycle map of every subcommand
python3 -m llmwiki # same as --help
| Group | Commands |
|---|---|
| Start here | init · configure-sources · install-agent-kit |
| Daily loop (this order) | sync · add · synth · candidates · build |
| Run the loop for me | all · watch · install-automation |
| Look around | lint · query · trace · graph · adapters · usage · version |
| Take things out | remove |
| Rare — one-time | migrate · queue |
The shorter alias llmwiki works too once the package is installed (pip install llm-wiki-plus or via Homebrew — see deploy/pypi-publishing.md / deploy/homebrew-setup.md).
init — scaffold raw/ / wiki/ / site/
Creates the three data directories + seeds nine navigation files inside wiki/.
python3 -m llmwiki init
Flags: none.
Expected output:
raw/sessions/
wiki/sources/
wiki/entities/
wiki/concepts/
wiki/syntheses/
site/
seeded wiki/dashboard.md
seeded wiki/index.md
...
Idempotent. Safe to re-run — it never overwrites files that exist.
sync — convert .jsonl sessions to markdown
The workhorse. Walks every configured adapter, converts new sessions into raw/sessions/, reconciles wiki/index.md against pages on disk, then (by default) auto-builds and auto-lints.
python3 -m llmwiki sync
python3 -m llmwiki sync --since 2026-04-01 --project llm-wiki
python3 -m llmwiki sync --adapter claude_code codex_cli
python3 -m llmwiki sync --no-auto-build --no-auto-lint
python3 -m llmwiki sync --vault "~/Documents/Obsidian Vault"
python3 -m llmwiki sync --vault ~/my-vault --allow-overwrite
python3 -m llmwiki sync --force
Flags
| Flag | What |
|---|---|
--adapter NAME [NAME ...] |
Limit to / load specific adapters. Default: every ingest-ready coding-agent source with a present store and no enabled: false. Notes intake still needs enabled: true. See multi-agent-setup.md. |
--since YYYY-MM-DD |
Only sessions on/after this date (e.g. --since 2026-04-01). Overrides durable filters.since / adapters.*.since for every source this run. Absent CLI flag: use config lookback, or unlimited if unset. See configuration-reference.md — Sync lookback. |
--project SUBSTRING |
Filter by project-slug substring. |
--include-current |
Include sessions < 60 min old (default skips live ones). |
--force |
Ignore the mtime state file, reconvert everything. |
--auto-build / --no-auto-build |
Rebuild site/ after sync (default: on). |
--auto-lint / --no-auto-lint |
Run lint after sync (default: on). |
--vault PATH |
Vault-overlay mode — write new pages inside the given Obsidian / Logseq vault instead of wiki/. See guides/existing-vault.md. |
--allow-overwrite |
With --vault: allow clobbering existing vault pages (default: refuse, append under ## Connections instead). |
--status |
Show last-sync time + per-adapter counters + quarantine (does not run a sync). |
--recent N |
With --status: also show last N sync/synthesize log entries. |
Note: There is no
sync --dry-run. Usesync --statusfor observability oradd --dry-runfor document-intake previews. State lives inllmwiki-state.json(configured once at CLI entry via--vault/vault.default_path).
Expected output (typical)
==> claude_code: 3 new sessions since last sync
✓ wrote 3 pages under raw/sessions/
✓ ingested into wiki/sources/ (2 new entities, 1 new concept)
✓ auto-build: site/ rebuilt (690 HTML files)
✓ auto-lint: 28 issues: 0 errors, 22 warnings, 6 info
Common recipes
- Nightly cron-style sync of one project only:
llmwiki sync --project my-project --no-auto-lint --since $(date -v-1d +%Y-%m-%d) - Vault-overlay round-trip:
llmwiki sync --vault "~/Documents/Obsidian Vault"
add — add a document to the wiki (#16)
Converts a URL, file, or folder into a raw Markdown document under raw/docs/, then (by default) batch-synthesizes and rebuilds the site once for the whole run. Sources may be freely mixed and repeated.
python3 -m llmwiki add https://example.com/some-article
python3 -m llmwiki add ./notes.pdf ./research-folder/
python3 -m llmwiki add https://example.com/post --title "Custom Title" --tag research
python3 -m llmwiki add ./doc.md --project my-project --note "Imported from Slack"
python3 -m llmwiki add https://example.com/post --dry-run
Flags
| Flag | What |
|---|---|
--title TEXT |
Override title derivation (single source only). |
--project NAME |
Group under raw/docs/<NAME>/ instead of the doc's own slug. |
--tag TAG |
Extra frontmatter tag (repeatable). |
--note TEXT |
Blockquote note prepended to the document body. |
--no-synthesize |
Skip the post-add synthesis pass. |
--no-build |
Skip the post-add site rebuild. |
--render |
Force the headless-browser layer for URLs (needs playwright). |
--no-render |
Never use the headless-browser layer. |
--dry-run |
Convert and report, write nothing, run nothing. |
--force-new |
Always land a new snapshot even when the converted body matches an existing doc (#22). |
--vault PATH |
Write under the given vault's raw/docs/ instead of the repo. |
URL sources go through a layered pipeline (markdown negotiation → extraction → render escalation) before landing as Markdown.
remove — cascade-remove a raw doc and everything derived (#B2)
Selects raw docs under the resolved vault's raw/docs/ by a project name or slug glob, then removes them together with every artifact derived from them — the synth.files state keys and the wiki/sources/ pages (part-pages included) — so a naive delete can never leave orphan pages or dangling state behind. After deletion it prunes backlinks, rebuilds wiki/index.md, and appends a remove entry to wiki/log.md.
python3 -m llmwiki remove old-project --dry-run # preview the full cascade
python3 -m llmwiki remove 'old-project*' --yes # slug glob, no prompt
python3 -m llmwiki remove taxes --vault ~/my-vault --yes
Positional
| Value | What |
|---|---|
SELECTOR |
Project name or slug glob (e.g. old-project*) matched against raw/docs/. |
Flags
| Flag | What |
|---|---|
--dry-run |
Print the full cascade (every raw file, state key, and wiki page) and change nothing. |
--yes |
Skip the confirmation prompt. Required when stdin is not a TTY — cascade deletion is never silent. |
--vault PATH |
Cascade against the given vault instead of the repo's own directories. |
A selector that matches nothing is a clean no-op with a message. Without --dry-run and without --yes, the command prints the cascade and asks for confirmation on a TTY, or refuses (exit 2) when there is none.
build — compile the static HTML site
Turns wiki/ markdown into site/ HTML. Also writes AI-consumable exports (llms.txt, llms-full.txt, sitemap.xml, rss.xml, robots.txt, graph.jsonld, ai-readme.md) into the output directory — there is no separate export subcommand.
python3 -m llmwiki build
python3 -m llmwiki build --out ~/public_html
python3 -m llmwiki build --search-mode tree
python3 -m llmwiki build --synthesize --claude /usr/local/bin/claude
python3 -m llmwiki build --vault ~/my-vault --out ~/site
python3 -m llmwiki build --vault demo --out ./site --local-root /home/user
Flags
| Flag | What |
|---|---|
--out PATH |
Output directory. Default: ./site/. |
--synthesize |
Call the claude CLI for overview synthesis (experimental). |
--claude PATH |
Path to the claude binary. Default: /usr/local/bin/claude. |
--search-mode {auto,tree,flat} |
Search routing mode (#53). auto picks tree vs flat from heading depth; tree / flat force the mode. Default: auto. |
--vault PATH |
Vault-overlay mode — build from an existing Obsidian / Logseq vault. Output still lands at --out. |
--local-root PATH |
Value shown in place of a session's stored home directory (#109). Default: this machine's home directory, so local paths stay usable. Pass a fixed string when publishing so the same vault renders identically anywhere. Substitution applies to the cwd field only. |
--seed-project-stubs |
Create a wiki/projects/<slug>.md stub for any project without one (#414). Off by default — build is read-only on wiki/. |
Expected output (final lines)
wrote search-index.json (7 KB meta) + 30 chunks (904 KB total) · tree mode · 64% deep pages
wrote 7 AI-consumable exports: ai-readme.md, graph.jsonld, llms-full.txt, llms.txt, robots.txt, rss.xml, sitemap.xml
wrote site/graph.html (interactive graph viewer)
wrote site/prototypes/index.html (6 prototype states)
wrote site/docs/ (94 editorial pages: hub + tutorials + style guide)
==> build complete: 703 HTML files, 61 MB
usage — MCP tool-usage telemetry vs synthesis cost (#26)
python3 -m llmwiki usage # human-readable report
python3 -m llmwiki usage --json # machine-readable totals
python3 -m llmwiki usage --compact # roll past months into rollup.json first
Folds the local MCP telemetry logs into totals and prints them next to the synthesis cost persisted in state — so the "is this wiki earning its synthesis spend?" question is answerable at a glance.
The live MCP surface is six tools (wiki_search, wiki_read_page, wiki_health, wiki_sync, wiki_export, wiki_add); see mcp.md for parameters and migration from retired tool names (#196).
The MCP server logs one JSON record per tool call to a per-process file under <vault>/usage/ (mcp-<pid>-<start>.jsonl), merged at read time. Several server processes run at once (one per editor session), so per-process files mean zero write contention and no lock on the hot path; telemetry never touches llmwiki-state.json. Each record carries tool, query, hits (0 = a knowledge gap or noise; null = the tool can't report a count), resp_bytes, duration_ms, caller_project, caller_source, server_pid, server_started. Writes are best-effort — a telemetry failure never breaks a tool call. Opt out with LLMWIKI_MCP_TELEMETRY=0.
Caller attribution. caller_project is resolved per call and caller_source says where it came from:
caller_source |
Meaning |
|---|---|
project-dir-env |
The workspace path a client auto-injects into the server's environment. Claude Code sets CLAUDE_PROJECT_DIR (≥ v2.1.139) into every stdio MCP server — zero config — and spawns one server per session, so it is a stable per-caller signal available at the first call. |
client-root |
The client's own workspace directory, obtained via an MCP roots/list request. Attributed to the first root when a client reports several. |
path |
A path argument carrying the caller's working directory encoded into one segment (…/-home-dev-code-my-app/…), used for clients that offer neither of the above. |
unattributed |
No caller-scoped signal — caller_project is unknown. |
They are tried in that order. All three project sources feed one shared slugs.project_slug_from_abs_path, so a project resolves to the same slug whether it arrived through telemetry or through session ingestion (and thus keys onto its own project page).
Client coverage. Claude Code attributes every call with no setup, via CLAUDE_PROJECT_DIR. Cursor currently provides no zero-config signal — it advertises the roots capability but returns Method not found on the actual roots/list call, and injects no workspace env var — so its calls fall to the path heuristic where a path argument is present, else unknown, until it ships a fix. The server's own os.getcwd() is never used: a client may launch the server anywhere (Claude Code's desktop app uses $HOME), so it is unrelated to the caller's project.
Unattributed calls are counted in the totals but never presented as a project: they print as (unattributed) here and are excluded from the site's "Heaviest project by MCP usage" card. Records written by an earlier version carry no caller_source and are read as unattributed regardless of the project name they hold, because that name is the server process's own working directory rather than the caller's. The same applies to a usage/rollup.json written before this change — the raw records behind it are already deleted, so its labels are retracted rather than recomputed.
Daily series (#52). usage/daily.json stores per-day MCP call totals (mcp_calls, retrievals, writes, session_reads, doc_reads, other_reads, by_tool, attribution counts) so Analytics activity heatmaps survive --compact. Compact folds retiring JSONL files into folded_days before delete; each llmwiki build refreshes the live overlay from non-folded files without double-counting. The CLI report itself is unchanged — the Analytics page is the primary surface. See State persistence.
Scope is MCP calls only — file:// static-site browsing stays untracked.
Flags
| Flag | What |
|---|---|
--json |
Emit the aggregated totals (consumption + cost) as JSON. |
--compact |
Fold whole past months into the kept-forever usage/rollup.json and delete their raw logs before reporting. |
--vault PATH |
Read telemetry from this vault instead of the repo root. |
--state-file PATH |
State file to read the synthesis-cost estimate from. |
configure-sources — enable detected session stores
python3 -m llmwiki configure-sources
Interactive interview: shared start date first (Enter = today−30 or keep stored; or type YYYY-MM-DD). Then each shipped adapter: facts (Sessions · Earliest · In last 30 days, path found or not) → Enable ([Y/n] when a default path exists and ingest is ready, [y/N] otherwise) → path (suggested only if found) → start date (Enter = use shared, or YYYY-MM-DD). Writes filters.since and adapters.<name> to gitignored config.json.
Lookback quiz keys: shared Enter writes today−30 (or keeps stored); typed YYYY-MM-DD sets a custom shared floor. Per-source Enter on start date inherits shared (no since key); typed YYYY-MM-DD writes adapters.<name>.since. Merge-write touches only those since keys plus enable/path. Non-interactive --yes / skipped interview invents no dates. Config "all" on a per-adapter since key (hand-edited) still means no date gate for that source.
| Flag | What |
|---|---|
--yes |
Non-interactive: skip interview (no config writes). |
After pip or Homebrew install (no setup.sh), run this once after llmwiki init. Git clone setup.sh offers the same interview on a TTY before install-automation. Set LLMWIKI_SKIP_CONFIGURE_SOURCES=1 to skip from setup.sh. Durable keys: configuration-reference.md — Sync lookback.
adapters — list every adapter + its status
python3 -m llmwiki adapters
Flags: none.
Expected output:
Registered adapters:
name present enabled description
---------------- -------- -------- ------------------------------
claude_code yes yes Claude Code — reads ~/.claude/projects/
openclaw yes yes OpenClaw — reads configured roots …
cursor_ide yes yes Cursor IDE — Composer sessions (globalStorage state.vscdb)
cursor_cli yes yes Cursor Agent CLI — reads ~/.cursor/chats/
Columns: present (store path on disk), enabled (yes / no — included on the next bare sync). Set via configure-sources; use sync --adapter <name> for a one-off.
graph — build the knowledge graph
python3 -m llmwiki graph # builtin wikilink graph
python3 -m llmwiki graph --engine graphify # AI-powered graph (requires graphifyy)
python3 -m llmwiki graph --format json
python3 -m llmwiki graph --format html
Flags
| Flag | What |
|---|---|
--format {json,html,both} |
Output format(s). Default: both. |
--engine {builtin,graphify} |
Graph engine. builtin = stdlib wikilink graph. graphify = AI-powered with community detection, confidence-scored edges, god nodes. Requires pip install graphifyy. Default: builtin. |
Builtin engine: Emits graph/graph.json (nodes + edges) and/or graph/graph.html (vis-network interactive viewer) plus sibling graph-viewer.js and vis-network.min.js. The interactive trio is also auto-copied into site/ on every build, so the graph works offline from the built static site without a CDN fetch.
Graphify engine: Runs the Graphify pipeline: tree-sitter AST extraction for code, semantic analysis for docs, Leiden community detection, god-node analysis. Outputs to graphify-out/ (graph.json, graph.html, GRAPH_REPORT.md) and copies to graph/ for build compatibility. Install: pip install llm-wiki-plus[graph] or pip install graphifyy.
lint — run 17 wiki-quality rules
python3 -m llmwiki lint
python3 -m llmwiki lint --json
python3 -m llmwiki lint --fail-on-errors --fail-on-warnings
python3 -m llmwiki lint --rules link_integrity,orphan_detection
python3 -m llmwiki lint --wiki-dir ~/another-wiki
Flags
| Flag | What |
|---|---|
--wiki-dir PATH |
Wiki dir. Default: <content root>/wiki. Wins over --vault; the vault settings file is then read from its parent. |
--rules NAMES |
Comma-separated rule names. Default: all applicable. An unrecognised name exits 2 and lists the valid names. |
--min-refs N |
How many distinct wiki/sources/ pages must name a [[wikilink]] target before an unresolved link to it is reported. Default: 3 — the candidate harvest's own threshold, so a target the harvest deliberately declined is not a finding. --min-refs 1 reports every unresolved link, and is the lowest accepted value: 0 and negatives exit 2. |
--json |
JSON output: summary, issues, total_pages, disabled_rules, ran — the last naming the checks that produced the report, so a run narrowed by --rules cannot read as a full one. |
--fail-on-errors |
Exit 1 if any error-severity issues. |
--fail-on-warnings |
Exit 1 if any warning-severity issues. Stricter than --fail-on-errors; pass both to gate on either. |
--vault PATH |
Lint the wiki under this vault root, and read that vault's llmwiki.json. |
A wiki can switch off the rules that cannot apply to it, in a committed <vault>/llmwiki.json — every report then names each skipped rule and its recorded reason. See configuration-reference.md § Vault file for the file's shape and the caution that goes with it.
Rules
17 structural rules (all deterministic — no LLM): frontmatter_completeness, frontmatter_validity, link_integrity, orphan_detection, content_freshness, duplicate_detection, index_sync, contradiction_detection, claim_verification, summary_accuracy, stale_candidates, tags_topics_convention, stale_reference_detection, frontmatter_count_consistency, tools_consistency, stub_source_pages, provenance_integrity.
contradiction_detection, claim_verification, and summary_accuracy used to hide behind --include-llm and advertise an LLM callback that was never wired. As of #72 they always run as structural checks: non-filler ## Contradictions sections, entity/concept claims without sources, and empty summary: frontmatter. Filler bodies like None identified., None detected., and multi-sentence None identified. … elaborations are not findings (unless the section also contains an unnegated affirmative conflict cue such as Contradicts earlier…). Cues that appear only inside negation (does not conflict with prior…, no claims that conflict…) stay filler (#86).
orphan_detection counts inbound [[wikilinks]] and catalog markdown links ([title](path.md) that resolve to a wiki page), so pages listed only from index.md are not orphans. link_integrity resolves targets case- and punctuation-insensitively ([[LLM-Wiki]] → llm-wiki.md) but does not do substring matching. It honours the candidate harvest's significance threshold (#150): a target named by no source page is always reported, a target named fewer than --min-refs times is a deliberate decline and is not, and a target named at least that often with no page of its own is a genuine gap and is.
stub_source_pages (#24) flags pages under wiki/sources/ whose body is machine-generated filler — a pending sentinel (<!-- llmwiki-pending: … -->) or the dummy backend's Auto-synthesized from session body. Those sources still count as unsynthesized backlog; refill them with llmwiki synth on a real backend.
provenance_integrity (#122) emits an error for each broken downward hop on pages that already carry sources: and/or source_file: — missing source-summary pages or missing raw files. Pages without those fields are skipped. The message names the missing hop and points at llmwiki trace, synth, or migrate broken-provenance as appropriate; this rule only reports.
stale_reference_detection (#303 / #87) flags living pages (entities, concepts, …) whose dated claim about a target predates that target's last_updated. Pages under wiki/sources/ and pages with frontmatter type: source are skipped — they are dated session records and cannot be "un-staled" without rewriting history.
Expected output
scanned 31 pages
28 issues: 0 errors, 22 warnings, 6 info
## link_integrity (22)
[warning] entities/GPT5.md: broken wikilink [[MultimodalModels]]
...
candidates — approval workflow
Positional action picks list / promote / flip-promote / merge / discard / apply / rewrite-key-facts.
Successful promote / flip-promote / merge / discard / apply reconcile wiki/index.md (#101): dead candidates/… bullets are dropped, an empty ## Candidates section is removed, and newly trusted pages are listed under Entities/Concepts. /wiki-candidates should call these same actions — do not run idle sync/synth just to refresh the catalog after review. Site UI: open site/candidates.html — it lists everything pending, takes a decision per row, and its Apply button prints the candidates apply --vault … --actions - command plus the JSON batch for the rows you decided (#97). A successful apply then rebuilds site/ so the open candidates page, Home, and Analytics match the wiki; pass --no-rebuild to skip that (for example when applying several batches before one llmwiki build).
promote fills an empty (or heading-only) ## Key Facts from nested fact: bullets on the cited source pages' Connections topics (#147 / #103). That path is offline — Dummy / None backends are fine. Non-empty reviewer Key Facts are left alone. Opt-in rewrite of trusted pages still needs a model: rewrite-key-facts uses the backend named by synthesis.backend (override the prompt per vault at wiki/prompts/key_facts.md).
merge folds a harvest stub into the target by unioning its sources: and Connections links and recording the name under ## Aliases (inbound [[merged-away]] links resolve to the survivor via that section in graph, lint, backlinks, and references); a candidate containing reviewer prose still has that prose appended under ## Candidate merge — <date>. Target may be a trusted page or another pending stub in the same kind.
apply runs a batch of the same intents in one process (the JSON shape site/candidates.html prints). A batch that merges into a peer slug the same batch also promotes, flip-promotes, discards, or merges away is refused before any row runs — the CLI prints the conflicting actions and exits non-zero (#149).
python3 -m llmwiki candidates apply --actions '[{"action":"promote","slug":"Foo","kind":"entities"},{"action":"promote","slug":"Prompt Caching","kind":"concepts"}]'
python3 -m llmwiki candidates apply --actions - <<'EOF'
[{"action":"discard","slug":"Bogus","kind":"entities","reason":"noise"}]
EOF
Already-trusted pages that still carry machine-assembled (regex) Key Facts, or pasted harvest-stub ## Candidate merge blocks from the old merge path, are fixed with rewrite-key-facts:
python3 -m llmwiki candidates list
python3 -m llmwiki candidates list --stale --stale-days 60
python3 -m llmwiki candidates list --json
python3 -m llmwiki candidates promote --slug NewEntity
python3 -m llmwiki candidates promote --slug NewEntity --kind concepts
python3 -m llmwiki candidates flip-promote --slug Misfiled
python3 -m llmwiki candidates merge --slug DuplicateFoo --into Foo
python3 -m llmwiki candidates discard --slug BogusEntity --reason "LLM hallucinated"
python3 -m llmwiki candidates rewrite-key-facts --slug ExistingEntity
python3 -m llmwiki candidates rewrite-key-facts --all
Flags
| Flag | What |
|---|---|
--slug NAME |
Page slug. Required for promote / flip-promote / merge / discard; or with rewrite-key-facts. |
--all |
For rewrite-key-facts: every entity/concept page. |
--into NAME |
For merge: target slug (trusted page or another pending stub in the same kind). |
--reason TEXT |
For discard: why (written to archive's .reason.txt). |
--kind {entities,concepts,sources,syntheses} |
Subtree. Auto-detected if omitted. |
--wiki-dir PATH |
Wiki dir. Default: ./wiki. |
--stale |
With list: only stale candidates. |
--stale-days N |
Staleness threshold. Default: 30. |
--json |
JSON output for list. |
--actions JSON |
For apply: JSON array of {action,slug,kind?,into?,reason?}. Pass - to read the array from stdin. |
--no-rebuild |
For apply: skip rebuilding site/ after a successful batch. Default is to rebuild so candidates.html drops the rows that were just promoted, merged, or discarded. |
See guides/existing-vault.md for the round-trip semantics when a candidate lives inside a vault.
synth — synthesize sources + harvest candidates
Primary command (#90 / #147). Default runs both phases: pending sources → wiki/sources/, then entity/concept candidates → wiki/candidates/.
A real sources pass is two language-model jobs, then bookkeeping: (1) prepare known-names once at the start of the run (canonical name, aliases, kind, short description) from wiki already on disk — Dummy / not is_llm skips this and uses heuristic vocabulary inject; (2) one source-summary ask per queued raw file, with that frozen list in the prompt. Connections bullets name each topic with kind and nested fact: claims. Harvest after sources (and --candidates-only) is a parser over those bullets — no classify LLM call; cost for harvest alone is zero LLM. Ctrl+C drains in-flight pages, then harvests from what was written (unless --sources-only, which prints llmwiki synth --candidates-only) and exits 130 (#145).
python3 -m llmwiki synth --check # probe the backend
python3 -m llmwiki synth --estimate # cost + Candidates (pre-run state)
python3 -m llmwiki synth --force # re-synth everything, then harvest
python3 -m llmwiki synth --sources-only # legacy: sources only
python3 -m llmwiki synth --sessions-only # all pending sessions (skip docs)
python3 -m llmwiki synth --docs-only # all pending docs (skip sessions)
python3 -m llmwiki synth --candidates-only # entity/concept candidates only
python3 -m llmwiki synth --candidates-only --min-refs 5
python3 -m llmwiki synth --path raw/sessions/<file>.md
python3 -m llmwiki synth # real run (sources + candidates)
llmwiki synth is the synthesize entry. Known-names prepare runs at the start of each sources pass.
Before the first page is synthesized, a real run announces the batch: Synthesizing 11 source(s) with ClaudeCLISynthesizer (2 at a time) — the count is the work queue after up-to-date, ineligible, and already-claimed sources are excluded, so it is what the run will actually do. An empty queue says Nothing to synthesize — every source is already up to date. instead. Each result line then carries its position, [3/11] synthesized: <project> → <page>, counting completed sources against that total; pages finish in whatever order the backend returns them, so the positions arrive out of order while the last one is always N/N.
--estimate prints the sources cost estimate with honest input units (#81): Corpus: N eligible sources (S sessions + D docs) and Already synthesized: N of M eligible sources (not page/file counts under wiki/sources/), then a separate Source pages (current state): T on disk (Sess sessions + D docs + X stubs) line for on-disk .md file counts. It also prints a Candidates (pre-run state): block — the harvestable shape of wiki/sources/ as it exists now, with a note that pending sources are not yet reflected. It is not a forecast of what the next run will harvest (#113). After a successful real synth (not estimate), the CLI prints an end-of-run summary: Synthesized:, Duration:, optional Tokens: / Cost: when known. Harvest still prints its Candidates line once; the end summary does not repeat Candidates.
Flags
| Flag | What |
|---|---|
--check |
Probe backend availability + exit (0 if reachable). |
--force |
Ignore state, re-synth every source. |
--estimate |
Print cached-vs-fresh token + dollar estimate for pending sources in eligible-source units (Corpus / Already synthesized), plus Source pages (current state): T on disk (sessions + docs + stubs) and Candidates (pre-run state): (current wiki/sources/ shape — not a forecast of the next harvest) (#50 / #90 / #81 / #113). |
--sources-only |
Synthesize wiki/sources/ only — skip candidate harvest (legacy synthesize behaviour). Mutually exclusive with --candidates-only / --check / --estimate. |
--sessions-only |
Synthesize only raw/sessions/ — skip raw/docs/. Mutually exclusive with --docs-only. Combinable with --path / --force (paths under raw/docs/ then exit 2). Incompatible with --check / --estimate. |
--docs-only |
Synthesize only raw/docs/ — skip raw/sessions/. Mutually exclusive with --sessions-only. Combinable with --path / --force (paths under raw/sessions/ then exit 2). Incompatible with --check / --estimate. |
--path PATH |
Synthesize only this raw session or doc under raw/sessions/ or raw/docs/ (repeatable; relative to the vault root, or absolute under it) (#62). Exit 2 if the path is missing or outside the vault. Still honours filters.include_subagents / exclude_headless (ineligible files are skipped even when named). Incompatible with --check / --estimate. |
--candidates-only |
Harvest entity/concept candidates from already-synthesized wiki/sources/ into wiki/candidates/, then exit (#90 / #147). Reads the source layer only — never raw/ — so it runs no per-source synthesis and no classify LLM call; kind, description, and facts come from Connections topic bullets already on those pages. LLM cost is zero. Unreadable source pages still fail the run and write nothing. Mutually exclusive with --sources-only / --check / --estimate. |
--min-refs N |
Candidate threshold: a [[wikilink]] target becomes a candidate when N or more distinct source pages name it (default: 3). |
--concurrency N |
Synthesize N source pages at once, overriding synthesis.concurrency (default: 2; range 1–16). 1 runs strictly sequentially. Pages are I/O-bound on the backend, so the wall clock shrinks roughly in proportion; raise it only as far as your provider's rate limits and your machine allow. all has no matching flag — its synth stage reads synthesis.concurrency. |
--backend NAME |
One-run overlay of synthesis.backend (dummy | ollama | claude | cursor_cli). Honoured by --check, --estimate, and a real run. Does not write config.json. Unknown names exit 2. |
--vault PATH |
Read/write under the vault root; configures the active llmwiki-state.json. |
Backend is picked from synthesis.backend in config.json / sessions_config.json (dummy by default; ollama for local; claude for synchronous claude -p; cursor_cli for Cursor Agent CLI agent -p, default model composer-2.5). Nested blocks: synthesis.claude, synthesis.cursor_cli, synthesis.ollama (flat claude_* still works). This is the synthesis generator — not the cursor_cli / cursor_ide session-ingest adapters. See configuration.md.
Removed in v1.4.0:
--list-pendingand--complete(agent-delegate pending prompts). Usesynthesis.backend: claude(orcursor_cli) instead.
Auto-tagging (#351)
Every synthesize call now produces topical tags alongside the deterministic baseline. The synthesizer emits a <!-- suggested-tags: prompt-caching, rag, github-actions --> block as the first line of its response; the pipeline parses it, strips it from the body, and merges the tags into frontmatter with:
- Baseline preserved — adapter, project slug, model family stay.
- Maintainer wins — on
--force, whatever you added viallmwiki tag addis kept at the front of the list. - Stop-word filter — the LLM can't re-add boilerplate tags (
session,summary,claude-code, etc.). - Cap 5 — max 5 AI tags per page to prevent drift.
- Near-dup rejection —
prompt-cacheis blocked whenprompt-cachingis already on the page (threshold 0.80 + prefix check).
No extra API round-trip — rides the existing synthesis call, so cost estimates from --estimate are unchanged. If the backend returns no suggested-tags block (dummy backend, malformed output), the page still ships with baseline tags.
Removed: synthesize (use synth; the old name was sources-only by default) and consolidate-topics (known-names prepare is part of synth).
queue — inspect and run unified queue
Manage the unified vault queue in llmwiki-state.json.
python3 -m llmwiki queue
python3 -m llmwiki queue enqueue --task-type add_doc --source https://example.com
python3 -m llmwiki queue run --vault /path/to/vault --limit 20
Positional
| Value | What |
|---|---|
status |
Print queue counts, task-type breakdown, state path, and oldest pending timestamp. |
enqueue |
Add one task (add_doc, session_sync, synthesize, build). |
run |
Execute pending tasks serially (up to --limit). |
Flags
| Flag | What |
|---|---|
--task-type {add_doc,session_sync,synthesize,build} |
Task kind for enqueue. |
--source TEXT |
Source payload for add_doc enqueue. |
--limit N |
Max tasks to process in one run call. Default: 20. |
--vault PATH |
Vault root used for task execution and state lookup. |
--state-file PATH |
Override direct state file path. |
migrate — list or apply a named one-time vault repair
Rare. One-time vault repairs after an upgrade — not part of the daily loop. List available migrations with llmwiki migrate or llmwiki migrate --list. Nothing is applied until you choose a name: llmwiki migrate <name> [flags]. There is no run-everything default. Prefer --dry-run on a named migration to preview writes.
New migrations are registered under migrate in llmwiki/cli.py, not as new top-level commands. Older docs that said migrate-X mean migrate <name> (for example migrate-raw-redaction → migrate raw-redaction).
python3 -m llmwiki migrate
python3 -m llmwiki migrate --list
python3 -m llmwiki migrate state --state-file /path/to/vault/llmwiki-state.json
python3 -m llmwiki migrate raw-redaction --vault /path/to/vault --dry-run
python3 -m llmwiki migrate tools-used --vault /path/to/vault
python3 -m llmwiki migrate page-kinds --vault /path/to/vault --dry-run
python3 -m llmwiki migrate topic-kinds --vault /path/to/vault
python3 -m llmwiki migrate broken-provenance --vault /path/to/vault --dry-run
state — one-time legacy state migration (v1.4.0)
Migrates legacy dotfiles (.llmwiki-state.json, .llmwiki-synth-state.json, .llmwiki-queue.json, .llmwiki-quarantine.json, .llmwiki-pending-prompts/) into the unified llmwiki-state.json.
Implementation lives at scripts/migrate_state_v1_4_0.py; the CLI is a thin wrapper.
python3 -m llmwiki migrate state
python3 -m llmwiki migrate state --state-file /path/to/vault/llmwiki-state.json
python3 scripts/migrate_state_v1_4_0.py --state-file /path/to/vault/llmwiki-state.json
| Flag | What |
|---|---|
--state-file PATH |
Explicit target state file (defaults to configured vault path). |
The command is idempotent and prints cleanup suggestions for migrated legacy files. It also repairs the vault: legacy pending prompts are resolved (not re-queued); dead synth_request queue items are purged; one synthesize queue task is enqueued when synth.pending_total > 0 and none is already pending (drain with llmwiki queue run --vault <path>); removed synthesis backends (agent, agent-delegate, agent_delegate) print a WARNING: to set claude, ollama, or dummy. Report keys: state_file, migrated, orphan_cleanup_suggestions, warnings, pending_prompts_total, pending_prompts_unfilled, synth_request_items_purged, queued_synthesize.
raw-redaction — deterministic username rewrite in raw/
Rewrites already-synced raw/sessions/*.md so home-path and dash-encoded agent-store segments use the USER placeholder (-Users-<you>-… → -Users-USER-…). In-place string rewrite only — does not re-convert from ~/.claude/projects / Cursor stores, does not touch wiki/, and does not enqueue synthesis.
Prefer this over llmwiki sync --force when redaction completeness in existing raw/ matters: agent transcripts are usually retained only ~30 days, so older sessions often have no source left to re-convert; force-sync followed by re-synth also burns LLM tokens for no benefit.
Implementation: scripts/migrate_raw_encoded_username.py. After migrating, rebuild so site/ picks up any display changes: llmwiki build --vault PATH.
python3 -m llmwiki migrate raw-redaction --vault /path/to/vault --dry-run
python3 -m llmwiki migrate raw-redaction --vault /path/to/vault
| Flag | What |
|---|---|
--vault PATH |
Required. Vault root containing raw/sessions/. |
--dry-run |
Report files that would change; write nothing. |
--real-username NAME |
Override redaction.real_username (default: config / $USER). |
--replacement-username NAME |
Override placeholder (default: USER). |
Idempotent: already-redacted files count as unchanged. Private local vaults that never publish raw/ can skip this and only run llmwiki build after upgrading (see UPGRADING.md).
tools-used — expand CallMcpTool frontmatter from origin stores
Rewrites tools_used and tool_counts in already-synced raw/sessions/*.md when the originating agent session file still exists. Re-reads records through the session adapter and applies the same tool_use_recorded_names expansion llmwiki sync uses (CallMcpTool → mcp__{server}__{tool}). In-place frontmatter update only — does not touch wiki/, does not enqueue synthesis, and never invents MCP names when the origin store is gone (TTL / deleted sessions count as skipped_missing_origin and stay unchanged).
Implementation: scripts/migrate_tools_used_mcp.py. After migrating, rebuild so analytics and the site pick up the new tool names: llmwiki build --vault PATH.
python3 -m llmwiki migrate tools-used --vault /path/to/vault --dry-run
python3 -m llmwiki migrate tools-used --vault /path/to/vault
| Flag | What |
|---|---|
--vault PATH |
Required. Vault root containing raw/sessions/. |
--dry-run |
Report files that would change; write nothing. |
--config PATH |
Optional sessions_config.json override (record filters). |
Origin resolution prefers the vault's llmwiki-state.json sync keys (adapter::home-relative-path), then falls back to a glob under the adapter session store by sessionId. Claude Code JSONL is fully supported; Cursor and other non-JSONL stores work when the state key or glob resolves a readable origin path. Missing origins leave CallMcpTool entries intact for wiki_adoption body fallback.
page-kinds — retype pages off the removed question/comparison kinds
llmwiki/schema.py lists five knowledge kinds — source, entity, concept, project, synthesis. A hand-written page declaring type: question or type: comparison is a frontmatter_validity error, and this migration clears it: each such page is retyped to concept and moved into wiki/concepts/ keeping its filename, then wiki/questions/ and wiki/comparisons/ lose their _context.md and are pruned once empty.
Inbound links are left alone on purpose. [[wikilinks]] resolve by filename, never by folder, so a page that keeps its name keeps every inbound link and no referring page needs editing.
Two safety rules: a page whose filename is already taken in wiki/concepts/ is retyped where it stands and reported as a collision rather than overwriting anything, and a removed folder still holding other content is left in place and reported rather than deleted. A vault with no removed-kind page prints nothing to migrate and exits 0 without writing.
Implementation: llmwiki/migrate_page_kinds.py — in the package rather than under scripts/, so it runs from a pip or Homebrew install with no checkout. After migrating, rebuild so site/ picks up the new locations: llmwiki build --vault PATH.
python3 -m llmwiki migrate page-kinds --vault /path/to/vault --dry-run
python3 -m llmwiki migrate page-kinds --vault /path/to/vault
python3 -m llmwiki lint --vault /path/to/vault --rules frontmatter_validity
| Flag | What |
|---|---|
--vault PATH |
Required. Vault root containing wiki/. |
--dry-run |
Report what would change; write nothing. |
Idempotent: a second run finds nothing to migrate. On a run that changed something the command reconciles wiki/index.md and appends ## [YYYY-MM-DD] migrate | page kinds to wiki/log.md.
topic-kinds — stamp entity/concept kinds onto older source Connections
Older source summaries often list [[wikilinks]] under ## Connections without an (entity) or (concept) kind. After the one-pass topic shape, those pages look like they still need a full rewrite. This offline migration stamps known kinds from pages already under wiki/entities/, wiki/concepts/, and the matching wiki/candidates/ folders — no language model, no network call, and raw/ is never written.
Only the Connections section is edited. Nested fact: lines, Key Claims, Key Quotes, and frontmatter stay byte-identical. Names that exist as both an entity and a concept are ambiguous: those bullets are skipped and listed in the report rather than guessed. Already-kinded bullets are left alone.
A successful non-dry-run that stamps at least one page writes .llmwiki-topic-kinds-stamped.json at the vault root (vault-local machine state — not for git) so you can later force-resynthesize exactly those sources if you want fact lines. The same apply (and a re-run over already-clear pages) upserts synth state for every raw session/doc whose wiki target is rewrite-clear — including when many raw files share one synth filename — so plain llmwiki synth / --estimate will not re-bill them. The report always states that zero facts were derived.
Implementation: llmwiki/migrate_topic_kinds.py. Stamping clears the rewrite-needed flag when at least one resolvable kind lands; it does not invent facts. Use llmwiki synth --force --path … on stamped pages if you want fact lines afterwards.
python3 -m llmwiki migrate topic-kinds --vault /path/to/vault --dry-run
python3 -m llmwiki migrate topic-kinds --vault /path/to/vault
| Flag | What |
|---|---|
--vault PATH |
Required. Vault root containing wiki/. |
--dry-run |
Report what would change; write nothing (no stamped JSON either). |
Idempotent: a second run finds nothing to stamp and prints nothing to migrate: no connection lines need topic kinds. Preview with --dry-run before applying.
broken-provenance — remap or clear hops to missing raw sessions
After a Cursor Agent CLI re-sync that used the filesystem stem store as sessionId, force-convert can leave wiki pages pointing at deleted raw/sessions/… paths while newer raw files exist under the same project slug (cursor-<hash>). This offline migration walks wiki pages that carry source_file: / sources: provenance and, when a hop targets a missing raw/sessions/ file:
- Parses the project slug from the missing path (for example
cursor-<hash>). - Finds existing raw files whose names contain that project slug.
- Restricts candidates to the same calendar day (
YYYY-MM-DDprefix). Never remaps across days (that used to point every June stub at a single January session). - Remaps only among same-day interactive raw files: explicit
is_headless: false, or legacy unmarked (nois_headlessfield — same eligibility rule as synth). When several remain, remaps to the uniquely closest HH-MM in that shortlist. - Otherwise clears the broken
source_file(same-day headless-only pools, ambiguous closest-time ties, or no same-day interactive candidate) and drops matchingsources:list aliases. Wiki pages themselves are never deleted. Never remaps to a row that is explicitlyis_headless: true.
Implementation: llmwiki/migrate_broken_provenance.py. Preview with --dry-run. Prefer a Cursor Agent CLI re-sync first so raw filenames carry real chat dates and is_headless is stamped; unmarked legacy same-day files remain remap-eligible until then.
python3 -m llmwiki migrate broken-provenance --vault /path/to/vault --dry-run
python3 -m llmwiki migrate broken-provenance --vault /path/to/vault
| Flag | What |
|---|---|
--vault PATH |
Required. Vault root containing wiki/ and raw/. |
--dry-run |
Report what would change; write nothing. |
The report prints remapped / cleared / unresolved counts. Idempotent once hops are healed or cleared.
install-agent-kit — copy packaged slash commands and skills (#109)
A pip or Homebrew install carries the user-facing /wiki-* slash commands and skills inside the package (llmwiki/agent_kit/). This command copies commands/ and skills/ beneath a directory you name so Claude Code (or any agent that reads that layout) can see them. --dest is required — the command does not guess at agent directory conventions.
Typical destinations: .claude in the project you are working in, or a user-level agent directory. Contributors working in this clone who want /wiki-* locally run llmwiki install-agent-kit --dest .claude.
Re-running after an upgrade refreshes the copies. A destination file whose content already matches the kit is left alone. A destination file that differs is saved as <filename>.bak beside it before the kit version is written, and the backup is reported, so a customisation is never overwritten silently. --dry-run prints the same report and writes nothing.
The install also prunes commands and skills the kit has retired (#214), so an agent directory populated by an older install stops offering them. Pruning is gated on content, never on the name: a file is deleted only while it still hashes to a revision llmwiki is known to have written at that path. Two things supply those digests — a small list of retired paths carried in the package, each mapped to the digests of every revision it ever shipped, and <dest>/.llmwiki-agent-kit.json, a manifest of the llmwiki version and a path → sha256 record of what this command installed, written after a pass. Anything the previous manifest recorded that the current kit no longer ships is pruned when its bytes are unchanged. A file this command never installed is never touched, whatever its name, so your own commands beside the kit's are safe; a retired command you customised is safe for the same reason — an unrecognised digest leaves the file alone and reports it as kept. Manifest entries that are absolute, escape the destination, or sit outside commands//skills/ are ignored, and a manifest that is missing, unreadable, or written in an older shape that carries no digests falls back to the retired list. Because only bytes llmwiki itself wrote are ever removed, a prune makes no backup; only files are removed — never directories. --dry-run reports the prune and deletes nothing.
The manifest is a normal file in --dest. When that is a git-tracked .claude/, commit .llmwiki-agent-kit.json alongside commands/ and skills/: it is what lets a later upgrade recognise its own files and clean them up.
Contributor-only commands (fix-bug, implement-feature, release) and skills (docs-that-work, pytest-best-practices, release, …) stay in this repository's .claude/ tree and are not part of the kit. Cutting a tagged release uses .claude/skills/release/SKILL.md via /release (see docs/maintainers/RELEASE_PROCESS.md).
python3 -m llmwiki install-agent-kit --dest .claude --dry-run
python3 -m llmwiki install-agent-kit --dest .claude
python3 -m llmwiki install-agent-kit --dest /path/to/agent-dir
Flags
| Flag | What |
|---|---|
--dest PATH |
Required. Directory that will receive commands/ and skills/. |
--dry-run |
Report what would be written; write nothing. |
The command prints every path written, every path pruned, every .bak it created for an overwrite, every retired path it kept because the content was not its own, and a count of identical files left untouched. Exit 0 on success, 1 if a file could not be read or written.
version — print the installed version
python3 -m llmwiki version
python3 -m llmwiki --version
Both print llmwiki <version>.
query — search the knowledge graph
python3 -m llmwiki query "what projects is Pratiyush working on"
python3 -m llmwiki query "Flutter mobile" --depth 2 --budget 1000
Flags
| Flag | What |
|---|---|
--depth N |
BFS traversal depth. Default: 3. |
--budget N |
Max output tokens. Default: 2000. |
Requires Graphify (pip install llm-wiki-plus[graph]). Run llmwiki graph first to build the graph.
trace — print downward provenance to raw transcripts (#122)
Walk a wiki page’s encoded chain to its source summaries and raw files. Uses only frontmatter (sources:, source_file:) — no body excerpts. Missing hops are marked; the walk still succeeds.
python3 -m llmwiki trace Demo --vault /path/to/vault
python3 -m llmwiki trace wiki/entities/Demo.md --vault /path/to/vault
Positional
| Arg | What |
|---|---|
PAGE |
Vault-relative wiki path (wiki/entities/Foo.md) or a resolvable page name/stem under wiki/. |
Flags
| Flag | What |
|---|---|
--vault PATH |
Trace under this vault (reads wiki/ + raw/). Without it, uses config.json vault.default_path or the repo demo content. |
Expected output
One line per hop: role, title, location; missing hops append (missing). A page with no provenance prints the page line plus (no further provenance).
page Demo wiki/entities/Demo.md
source Kickoff session wiki/sources/kickoff.md
raw Kickoff transcript raw/sessions/2026-01-01T12-00-demo-kickoff.md
Exit codes
| Code | Meaning |
|---|---|
0 |
Walk completed (including chains with missing hops). |
1 |
Starting page could not be resolved (or locator unsafe / empty). |
2 |
Configured --vault / default vault path is unusable. |
Use trace to inspect broken hops; repair them by hand or with synth / migrate broken-provenance as the lint message suggests. Guided repair under doctor (#110) is roadmap-only.
all — run the full pipeline
The one command to run after agent sessions land. It runs every stage in order — sync → synth → build → graph → lint — so a scheduled job is a bare llmwiki all rather than a trail of flags. AI-consumable exports (llms.txt, sitemap.xml, etc.) are written by build, not a separate step.
Every stage runs by default and every stage has an opt-out flag: --no-sync, --no-synth, --skip-graph, --skip-lint. synth is the only stage that can call an LLM; with the default dummy synthesis backend it makes no provider call at all, and --no-synth turns it off outright.
python3 -m llmwiki all # every stage
python3 -m llmwiki all --no-synth # no LLM calls
python3 -m llmwiki all --no-sync --no-synth # build → graph → lint only
python3 -m llmwiki all --graph-engine builtin # skip optional graphify
python3 -m llmwiki all --skip-graph --lint-fail warnings # fail CI on any lint issue
Flags
| Flag | What |
|---|---|
--out DIR |
Output dir for build. Default: site/. |
--search-mode {auto,tree,flat} |
Forwarded to build. Default: auto. |
--graph-engine {builtin,graphify} |
Forwarded to graph. Default: graphify. |
--no-sync |
Skip the sync step (do not convert new agent sessions first). |
--no-synth |
Skip the synth step, so the run makes no LLM calls. |
--synth-force |
Pass --force to synth (re-synthesize every session). |
--skip-graph |
Skip the graph step entirely (useful when graphify is not installed). |
--skip-lint |
Skip the lint step entirely. |
--lint-fail {never,errors,warnings} |
When lint findings fail the run with exit 2. Default: never. |
--strict |
Spelling for --lint-fail warnings. When both are given, the stricter wins. |
--fail-fast |
Stop at the first non-zero step (useful for demo-vault / unattended runs where nobody will open the static site afterward). Default: continue later stages — e.g. if synth fails after a successful sync, build still runs — and report the worst exit code. |
--with-sync, --with-synth |
Deprecated and inert — the stages they used to enable now run by default. Accepted so an already-installed scheduled command keeps parsing; each prints a one-line notice. |
--vault PATH |
Run every step against this vault instead of the repo. |
Lint failure policy
lint always prints its findings. --lint-fail decides whether those findings end the run:
| Policy | Fails when |
|---|---|
never (default) |
Never — findings are reported and the run still exits 0. |
errors |
Lint reported at least one error-severity issue. |
warnings |
Lint reported at least one error or warning. |
When --lint-fail ends the run with exit 2, the site HTML from the preceding build in this run is kept — lint does not undo or revert site/. Home surfaces the failure via Pipeline state (Last lint + banner); see ui.md.
Conflicting flags
--no-synth wins over --with-synth, and --no-sync wins over --with-sync, in any order on the command line — the deprecated --with-* aliases are inert and cannot re-enable a stage you just switched off. --strict and --lint-fail resolve to whichever of the two is stricter.
Exit codes:
0— every step succeeded.- non-zero — forwarded from the first (or worst) failing step.
2— the lint failure policy was met, or a required directory was missing.
watch — near-real-time maintain when sessions finish
Polls adapter session stores on an interval and runs maintain when a session looks finished. Uses per-adapter turn-complete heuristics (Claude stop_reason, Cursor last role, Codex events). Mid-tool / permission loops stay deferred until the adapter reports safe. Adapters without a finished-signal still trigger after a 2s mtime settle — not a multi-minute quiesce.
Single-flight: only one maintain iteration at a time (sync → synth → build by default). Changes that arrive during a run set a dirty flag and retry after it finishes. Sync may time out (~180s); synth and build have no timeout.
python3 -m llmwiki watch
python3 -m llmwiki watch --adapter claude_code cursor
python3 -m llmwiki watch --interval 10 --settle 3
python3 -m llmwiki watch --dry-run
python3 -m llmwiki watch --no-synthesize --no-build
python3 -m llmwiki watch --vault ~/my-vault
Flags
| Flag | What |
|---|---|
--adapter NAME [NAME ...] |
Limit to / load specific adapters. Default: every ingest-ready coding-agent source with a present store and no enabled: false. Notes intake still needs enabled: true. See multi-agent-setup.md. |
--interval SECONDS |
Poll interval. Default: 5. |
--settle SECONDS |
Mtime settle before ready check for adapters without a finished-signal. Default: 2. |
--dry-run |
Detect finished sessions only; do not run maintain. |
--no-synthesize |
Skip the synthesize step. |
--no-build |
Skip the build step. |
--vault PATH |
Maintain this vault instead of the repo. |
install-automation — set up the daily job
Sets up the job that keeps your wiki current so you do not have to run the steps by hand. Interactive by default: it asks what the daily job should do, when it should run, and shows you the exact command line before writing anything. Pass --yes with the flags below for an unattended install.
What the daily job can do
| Job | What it does | Writes | Cost |
|---|---|---|---|
| Ingest only (default) | Collects new agent sessions into your vault and refreshes the site. | raw/, site/ |
Never contacts an AI provider — free. |
| Maintain | Collects new sessions, summarises each one into a wiki page, gathers candidate topics for review, refreshes the browsable site once per cycle at the build step after summarization, and reports wiki quality findings into the run log. A separate sync-only path (including optional Ingest automation) is a different concern — not “Maintain finished.” | raw/, wiki/sources/, wiki/candidates/, site/ |
Sends session text to your AI provider — this costs money once a real provider is configured. Run llmwiki synth --estimate to see how much before the job first fires. |
Optional extras (maintain only)
Nothing here is on by default; the wizard offers them as one comma-separated question and each has a flag.
| Extra | Flag | Effect |
|---|---|---|
| Build the knowledge graph | --graph builtin / --graph graphify |
The job also builds the graph, with the built-in builder or the richer graphify one (pip install llm-wiki-plus[graph]; the job falls back to the built-in builder until that extra is installed). Writes graph/. |
| Fail the job on quality errors | --lint-fail errors |
The scheduled job reports failure when the quality check finds errors. |
| Fail the job on quality warnings | --lint-fail warnings |
The scheduled job reports failure on any warning or error. Stricter than errors. |
Without a failure policy the quality check still runs and its full report lands in the run log — findings simply never mark the job as failed.
When it runs
The wizard offers presets and translates each into a cron expression; --schedule takes the same expression directly. Whatever the route, the schedule is validated before any unit file is written — an expression llmwiki cannot translate into your OS scheduler's own format is refused with the reason (exit code 2).
| Preset | Cron | Example |
|---|---|---|
| Every day | M H * * * |
"0 8 * * *" — every day at 08:00 (the default) |
| Weekdays only | M H * * 1-5 |
"30 7 * * 1-5" — weekdays at 07:30 |
| Once a week | M H * * D |
"0 18 * * 3" — Wednesdays at 18:00 |
| Custom cron expression | as typed | "0 */6 * * *" — every six hours |
Supported grammar is standard 5-field cron: *, integers, lists (1,15), ranges (1-5), steps (*/15), day names SUN–SAT, month names JAN–DEC. Nicknames (@daily), Vixie/Quartz extensions (L, W, #), a seconds field, and any expression restricting both day-of-month and day-of-week are refused — the last one because cron ORs those two fields and no OS scheduler can express it.
Linux systemd timers use Persistent=true so a missed run catches up once after wake (not every skipped day while the laptop stayed off). By default the installer writes rendered units to ~/.automation/, copies them into your OS scheduler (~/.config/systemd/user on Linux, ~/Library/LaunchAgents on macOS), and enables the job. Pass --no-activate to write unit files only and print manual enable commands. Each run appends to <vault>/.llmwiki/last-automation.log (truncated each run). .llmwiki/automation-status.json under the vault drives the Home Automation panel (settings only — job, schedule, cost/backend, hooks/watch, log path; Maintain notes that the site refreshes once after summarization) and records scheduler activation state. Stage completion times live under Pipeline state, not Automation. The wizard defaults to Maintain on Enter; choose 1 for ingest-only. Re-running replaces the existing job rather than adding a second one.
python3 -m llmwiki install-automation
python3 -m llmwiki install-automation --yes --job maintain
python3 -m llmwiki install-automation --yes --job maintain --graph builtin --lint-fail errors --schedule "0 8 * * 1-5"
python3 -m llmwiki install-automation --yes --job ingest --schedule "30 7 * * *" --units-dir ~/.config/systemd/user
python3 -m llmwiki install-automation --yes --job maintain --synth-backend ollama --watch-enabled
python3 -m llmwiki install-automation --yes --no-activate
python3 -m llmwiki install-automation --vault ~/my-vault
Flags
| Flag | What |
|---|---|
--yes |
Non-interactive: use the flags and defaults below; never installs hooks. |
--job {ingest,maintain} |
What the daily job does. Default: ingest. |
--graph {none,builtin,graphify} |
Build the knowledge graph, and with which builder. Default: none. |
--lint-fail {never,errors,warnings} |
Quality findings at this level report the scheduled job as failed. Default: never. Same spelling as the all flag. |
--schedule "<cron>" |
When the job runs, as a 5-field cron expression. Default: "0 8 * * *". An expression that cannot be translated exits 2 with the reason. |
--synth-backend NAME |
Synthesis backend for automation status (dummy / ollama / claude / cursor_cli). Interactive mode also writes synthesis.backend to config.json, after you confirm the summary. |
--units-dir PATH |
Staging directory for rendered unit files before OS activation. Default: ~/.automation/. Linux/macOS still install into the platform scheduler location unless --no-activate. |
--watch-enabled |
Set watch_enabled in automation status so the site Automation panel shows Watch: on (does not install or start llmwiki watch). |
--force-platform {linux,macos,windows} |
Override platform detection for unit format. |
--activate |
Copy units into the OS scheduler location and enable the job (default). |
--no-activate |
Write unit files only; print copy-paste enable commands. |
--vault PATH |
Vault the job runs against: automation-status.json is written under it, and the scheduled command carries --vault PATH whenever it differs from vault.default_path in config.json, so the job and its status file always mean the same vault. Omitted, the job resolves its vault from config. |
--profile {A,B,C} |
Deprecated — use --job. A maps to ingest, B and C to maintain. Prints a notice; --job wins when both are given. |
--hour N |
Deprecated — use --schedule. Translated to "{minute} {hour} * * *", and ignored with a notice when --schedule is given. |
--minute N |
Deprecated — use --schedule. See --hour. |
Exit codes:
0— the scheduler files were written (and activated unless--no-activate), or you answered n at the final confirmation (automation skipped; vault and config unchanged).1— scheduler activation failed (--activatedefault); status file recordsscheduler_error.2— the schedule is not an expression llmwiki can translate.
Status file fields (under <vault>/.llmwiki/automation-status.json): in addition to job/schedule keys, activation adds scheduler_activated (bool), scheduler_backend (systemd / launchd / schtasks), scheduler_units_dir (install path), scheduler_active (read-back when available), and scheduler_error (string when activation failed).
Exit codes (conventions)
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Operation failed (user-visible error) |
2 |
Usage error (bad flags, missing file, etc.) |
Subcommands document their own non-zero exit conditions where relevant (lint --fail-on-errors).
Related
- Slash commands — the
/wiki-*surface used from Claude Code. - UI reference — every screen + nav surface on the compiled site.
- Configuration · Full configuration reference.