← Docs hub

Configuration Reference

Complete reference for all CLI subcommands, flags, environment variables, and configuration options.

CLI subcommands

llmwiki init

Scaffold the raw/, wiki/, site/ directory structure and seed initial wiki files.

python3 -m llmwiki init

No options. Creates directories and seeds wiki/index.md, wiki/log.md, wiki/overview.md if they don't already exist.

llmwiki sync

Convert agent session transcripts (.jsonl) into markdown under raw/sessions/.

python3 -m llmwiki sync [options]
Flag Type Default Description
--adapter name... all available Only run the named adapter(s)
--since YYYY-MM-DD none One-run lookback: only sessions on or after this date. Overrides durable filters.since / adapters.*.since for every source in the run (#192)
--project substring none Only sync projects whose slug contains this
--include-current flag off Don't skip live sessions (< 60 min old)
--force flag off Ignore state file, reconvert everything
--force-resync flag off Override the newer-schema / corrupt-state guard (#29) and reconvert from scratch. Implies --force; may duplicate an already-populated raw/
--dry-run flag off Preview what would be written
--download-images flag off Download remote images in .md files to raw/assets/
--fail-on-errors flag off Exit 1 if any file fails to convert; by default per-file errors are quarantined and the run exits 0

llmwiki build

Compile the static HTML site from raw/ and wiki/. Also writes AI-consumable exports into the output directory: llms.txt, llms-full.txt, graph.jsonld, sitemap.xml, rss.xml, robots.txt, and ai-readme.md. There is no separate export subcommand.

python3 -m llmwiki build [options]
Flag Type Default Description
--out path ./site Output directory
--synthesize flag off Call claude CLI to generate an Overview synthesis
--claude path /usr/local/bin/claude Path to the claude binary
--local-root path this machine's home Value shown in place of a session's stored home directory (#109)

llmwiki adapters

List every registered adapter and whether its session store is present.

python3 -m llmwiki adapters

No options.

llmwiki graph

Build the knowledge graph from wiki/ wikilinks.

python3 -m llmwiki graph [options]
Flag Type Default Description
--format json\|html\|both both Output format

llmwiki lint

Run every registered quality rule against the wiki and print a report. Rules the wiki switched off in its llmwiki.json never run, and every report names them.

python3 -m llmwiki lint [options]
Flag Type Default Description
--wiki-dir path <content root>/wiki Wiki directory to lint. Narrower than --vault, and wins over it; the vault settings file is then read from this directory's parent
--rules name,name all registered rules Comma-separated rule names to run. An unrecognised name stops the run with exit 2 and lists the valid names
--min-refs N 3 How many distinct wiki/sources/ pages must name a [[wikilink]] target before an unresolved link to it is reported as broken (see below). Must be at least 1 — a value below that is rejected rather than silently treated as 1
--json flag off Print the machine-readable report — summary, issues, total_pages, disabled_rules, ran — instead of the text one
--fail-on-errors flag off Exit 1 when any error-severity finding was reported
--fail-on-warnings flag off Exit 1 when any warning-severity finding was reported. Stricter than --fail-on-errors; pass both to gate on either. A rule the wiki switched off cannot stop the gate — it never ran
--vault path vault.default_path from config.json Lint the wiki under this vault root, and read that vault's llmwiki.json

Why --min-refs changes how many cross-reference findings you see. synth writes a [[wikilink]] for every topic it names, and the candidate harvest materializes a page only for a target named by enough distinct source pages to look significant. Everything below that bar is left unmaterialized on purpose, so link_integrity honours the same threshold rather than reporting the product's own design decisions as defects. The gate is three-way:

Distinct wiki/sources/ pages naming a target with no page of its own Reported? Why
none yes, at every threshold Nothing was ever going to materialize it, so no decision was taken — it is simply a dangling reference
fewer than --min-refs no The harvest deliberately declined to give this target a page
--min-refs or more yes A genuine gap: named often enough to deserve a page, and it does not have one

So lowering the threshold widens the middle band into the reported band: --min-refs 1 reports every unresolved link, which on a mature wiki can be hundreds of findings that were all deliberate declines. Nothing is hidden permanently — the lower threshold always brings them back. The stock value lives in one place, llmwiki.vault_settings.DEFAULT_MIN_REFS, which the harvest reads too, so the step that declines to create a page and the check that reports the missing page cannot drift apart. llmwiki all --min-refs N sets it for the synth and lint stages of a pipeline run.

llmwiki all (v1.2)

Run the full pipeline: sync → synth → build → graph → lint. Every stage runs by default; each has an opt-out flag. AI-consumable exports are written by build, not a separate step.

python3 -m llmwiki all [options]
Flag Type Default Description
--out path ./site Output directory
--no-sync flag off Skip the sync step (do not convert new agent sessions first)
--no-synth flag off Skip the synth step, so the run makes no LLM calls
--synth-force flag off Pass --force to synth (re-synthesize every session)
--search-mode auto/tree/flat auto Forwarded to build
--graph-engine builtin/graphify graphify Forwarded to graph
--skip-graph flag off Skip the graph step
--skip-lint flag off Skip the lint step
--lint-fail never/errors/warnings never Which lint findings end the run with exit 2
--strict flag off Spelling for --lint-fail warnings (CI gate); the stricter of the two wins
--fail-fast flag off Stop at first non-zero step
--with-sync, --with-synth flag off Deprecated and inert — the stages they name run by default. Accepted so an already-installed scheduled command keeps parsing; each prints a one-line notice

llmwiki version

Print the current version.

python3 -m llmwiki version

Config file (config.json)

Copy the example and edit:

cp examples/sessions_config.json config.json

config.json is gitignored. The converter auto-loads it if present at the repo root.

Full schema

{
  "filters": {
    "live_session_minutes": 60,
    "include_projects": [],
    "exclude_projects": [],
    "drop_record_types": ["queue-operation", "file-history-snapshot", "progress"],
    "exclude_headless": true,
    "exclude_temp_cwd": false
  },

  "redaction": {
    "real_username": "",
    "replacement_username": "USER",
    "extra_patterns": [
      "(?i)(api[_-]?key|secret|token|bearer|password)[\"'\\s:=]+[\\w\\-\\.]{8,}",
      "sk-[A-Za-z0-9]{20,}",
      "[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+"
    ]
  },

  "truncation": {
    "tool_result_chars": 500,
    "bash_stdout_lines": 5,
    "write_content_preview_lines": 5,
    "user_prompt_chars": 4000,
    "assistant_text_chars": 8000
  },

  "drop_thinking_blocks": true,

  "adapters": {
    "obsidian": {
      "vault_paths": ["~/Documents/Obsidian Vault"],
      "exclude_folders": [".obsidian", "Templates"],
      "min_content_chars": 50
    },
    "codex_cli": {
      "roots": ["~/.codex/sessions", "~/.codex/projects"]
    },
    "gemini_cli": {
      "roots": ["~/.gemini"]
    },
    "openclaw": {
      "roots": ["~/.openclaw/agents", "<vault>/.openclaw-sessions-inbox"]
    }
  }
}

Sync lookback

Optional absolute date gate so bare llmwiki sync does not ingest years of history (#192). Unset shared and per-adapter keys mean unlimited — same as today’s default.

Key Values Meaning
filters.since absent, "", or YYYY-MM-DD Shared earliest session day; absent/empty = no shared floor
adapters.<name>.since absent, YYYY-MM-DD, or "all" Per-source override; absent = inherit shared; "all" = no date gate for that source only

Precedence (per adapter, highest first): CLI --since → adapter YYYY-MM-DD → adapter "all"filters.since → unlimited. "all" is valid only on the per-adapter key. An invalid date string exits 2 (same as a bad --since).

How sync applies it

configure-sources quizshared start date first (Enter = today−30, or keep a stored date; or type YYYY-MM-DD). Then each source: facts block (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 → start date (Enter = use shared, or YYYY-MM-DD). Non-interactive / skipped interviews invent no dates. See CLI configure-sources.

Section reference

Section Key Type Default Description
filters live_session_minutes int 60 Skip sessions younger than this (prevents reading mid-write)
filters include_projects list [] If non-empty, only sync matching project slugs
filters exclude_projects list [] Skip projects containing these substrings
filters drop_record_types list [3 types] JSONL record types to discard
filters since string unset (unlimited) Shared sync lookback as absolute YYYY-MM-DD. Absent or empty = no shared date gate. Overridden per run by CLI --since. See Sync lookback
filters exclude_headless bool true Skip automated / headless launches across coding-agent adapters (Claude SDK markers; Cursor Agent CLI subagentInfo / approvalMode=auto-review; OpenClaw never skipped; others false until markers exist). Prevents the synthesis feedback loop. Applies at both ingest and synthesis. See multi-agent-setup.md
filters exclude_temp_cwd bool false Opt-in: skip sessions whose cwd is a throwaway temp dir (/tmp, /var/folders, …). Off by default — a git worktree under /tmp is often real work
redaction real_username string $USER Your OS username (auto-detected if empty)
redaction replacement_username string USER Replacement in path redaction
redaction extra_patterns list [3 regexes] Additional Python regex patterns to redact
truncation tool_result_chars int 500 Max chars per tool result
truncation bash_stdout_lines int 5 Max lines from bash output
truncation write_content_preview_lines int 5 Max lines from Write tool preview
truncation user_prompt_chars int 4000 Max chars per user prompt
truncation assistant_text_chars int 8000 Max chars of assistant text
root drop_thinking_blocks bool true Drop <thinking> blocks from output
adapters per-adapter object varies Override adapter-specific settings (roots, enabled, optional since, plus adapter-specific fields)
adapters.<name> since string unset (inherit) Per-source lookback: YYYY-MM-DD override, or "all" for no date gate on that source. Omit the key to inherit filters.since. See Sync lookback
schedule build enum "on-sync" When /wiki-build runs. on-sync / daily / weekly / manual / never.
schedule lint enum "manual" When /wiki-lint runs. Same enum.
synthesis backend enum "dummy" Which synthesizer: "dummy" / "ollama" / "claude" (synchronous claude -p) / "cursor_cli" (Cursor Agent CLI agent -p, #230). Unknown values warn and fall back to "dummy". The old "agent" / agent-delegate backend was removed in v1.4.0. One-run override: llmwiki synth --backend <name> (does not write config). Distinct from the cursor_cli / cursor_ide ingest adapters. See configuration.md § Synthesis backend.
synthesis overview_model string "haiku" Model for the landing-page overview call in build --synthesize when the active backend is Claude. Prose-from-JSON, so the small model is the default. Overview follows the active synthesis backend (#230): cursor_cli / ollama use that engine; dummy or unavailable skips the LLM. See reference/synthesis-cost.md
synthesis concurrency int 2 How many source pages synth synthesizes at once (range 1–16; 1 is strictly sequential). Bounds concurrent backend calls — for CLI backends that is concurrent subprocesses, which is why the ceiling exists. Unusable or out-of-range values warn and fall back to the default (out-of-range clamps to 16); a missing key is silent. llmwiki synth --concurrency N overrides it for one run
synthesis.claude model (alias claude_model) string "sonnet" Claude model id / alias. Nested synthesis.claude.model wins over flat synthesis.claude_model.
synthesis.claude path (alias claude_path) string "" Path to the claude binary (overrides $PATH). Nested wins over flat.
synthesis.claude timeout (alias claude_timeout) int (s) 180 Per-page timeout for Claude. Nested wins over flat. Separate from synthesis.ollama.timeout — before v1.4.1 both backends shared one timeout key, so the Ollama default silently capped Claude pages at 60s.
synthesis.claude lean (alias claude_lean) bool true Strip agent scaffolding (tool schemas, MCP, skills, CLAUDE.md, agent system prompt) from each claude call — ~9x cheaper per page, measured. Nested wins over flat. Only an explicit false opts out. See reference/synthesis-cost.md.
synthesis.claude effort (alias claude_effort) enum unset --effort for Claude (low/medium/high/xhigh/max). Nested wins over flat. Extended thinking is billed as output at ~5x input; on Haiku it was 5,753 output tokens/page at the default vs 1,609 at low. Set low on small models.
synthesis.cursor_cli model string "composer-2.5" Cursor Agent CLI --model id (#230). Cheapest Composer id Agent CLI lists; pricing aliases in model_pricing.csv include composer and Grok effort/fast variants from Cursor models & pricing.
synthesis.cursor_cli timeout int (s) 180 Per-page timeout for the cursor_cli backend. No user-facing binary-path key — resolves agent then cursor-agent from $PATH.
synthesis.ollama model (legacy flat synthesis.model) string "llama3.1:8b" Ollama model name (pull via ollama pull). Nested synthesis.ollama is canonical; legacy flat synthesis.model / timeout / … still work but share a namespace with the other backends.
synthesis.ollama base_url string "http://127.0.0.1:11434" Ollama HTTP endpoint
synthesis.ollama timeout (legacy flat synthesis.timeout) int (s) 60 Per-request timeout
synthesis.ollama max_retries int 3 Exponential-backoff retry count on 5xx / timeout
meeting enabled bool false Opt-in; non-AI adapter
meeting source_dirs list ["~/Meetings"] Directories to scan
meeting extensions list [".vtt", ".srt"] File extensions to consider
jira enabled bool false Opt-in; non-AI adapter
jira server string Jira Cloud/Server URL
jira email string Account email
jira api_token string "" Prefer api_token_env + .env
jira jql string sensible default Query for tickets to sync
jira max_results int 50 Pagination cap
chatgpt enabled bool false Opt-in; requires explicit conversations_json
chatgpt conversations_json string Path to export file
web_clipper enabled bool false Obsidian Web Clipper intake path
web_clipper watch_dir string "raw/web" Directory to watch
web_clipper extensions list [".md"] File extensions to pick up
web_clipper auto_queue bool true Auto-enqueue into unified llmwiki-state.json queue
site github_repo string "" Optional owner/name for CHANGELOG / edit-on-GitHub / source-code links in compiled docs. Empty = detect from git remote get-url origin, else Pratiyush/llm-wiki

Vault file (llmwiki.json)

<vault-root>/llmwiki.json holds the settings that belong to the wiki itself rather than to this install. It sits at the vault root beside wiki/, raw/ and the vault's llmwiki-state.json, and it is meant to be committed: copying, sharing, or publishing the vault carries it along. The example wiki shipped with the project has one — demo/llmwiki.json.

Two similarly-named JSON files, so keep them apart:

config.json llmwiki.json
Lives at the repo / install root the vault root, beside wiki/
Describes how this install behaves — adapters, redaction, truncation, synthesis backend what is true of this wiki — today, the quality checks that cannot apply to it
Committed? no, it is gitignored yes, that is the point of it
Travels with a copied or published vault? no yes
Read by sync, synth, build, … lint, the all pipeline's lint stage, and the MCP wiki_lint tool
When missing falls back to examples/sessions_config.json the wiki declares nothing, and lint behaves exactly as it did before this file existed

lint.disabled_rules

Names the quality checks that do not apply to this wiki. A disabled rule is never constructed, never runs, and contributes no findings — and is named as skipped in every report, whether or not anything was found, so a short report can never be mistaken for a clean one. It is on/off only: a wiki cannot re-grade a finding's severity, and cannot switch a rule off for part of itself.

Two shapes are accepted. A bare list, when you do not want to record a reason:

{
  "lint": {
    "disabled_rules": ["content_freshness"]
  }
}

Or an object mapping each rule to a written reason — preferred, because the reason is what you or a reviewer will read six months from now:

{
  "lint": {
    "disabled_rules": {
      "content_freshness": "This wiki is a committed snapshot, so this check measures elapsed calendar time rather than a defect in the pages."
    }
  }
}

Rule names are the ones the report prints as ## <rule> headings; the full list is in reference/cli.md, and any run that rejects a name prints the valid ones.

Keep a reason to a sentence or two. It is printed verbatim, on one line of every report the wiki produces, so a paragraph-length reason wraps badly in a CI log.

Worked example

Write the file at the vault root and run the check:

cat > /path/to/vault/llmwiki.json <<'JSON'
{
  "lint": {
    "disabled_rules": {
      "content_freshness": "This wiki is a committed snapshot, so this check measures elapsed calendar time rather than a defect in the pages."
    }
  }
}
JSON

python3 -m llmwiki lint --vault /path/to/vault

The report names what it skipped, above the findings:

  scanned 3 pages
  2 issues: 1 errors, 1 warnings, 0 info
  skipped 1 of 17 rules (disabled in llmwiki.json):
    - content_freshness — This wiki is a committed snapshot, so this check measures elapsed calendar time rather than a defect in the pages.

## index_sync (1)
  [error] index.md: page 'sources/demo-source.md' not listed in index.md

## link_integrity (1)
  [warning] entities/Widget.md: broken wikilink [[Gadget]]

lint --json carries the same declaration under disabled_rules, which is always present — empty when the wiki declares nothing — so a consumer can read it without probing for the key:

{
  "summary": { "error": 1, "warning": 1 },
  "issues": [
    { "rule": "index_sync", "severity": "error", "page": "index.md", "message": "page 'sources/demo-source.md' not listed in index.md" },
    { "rule": "link_integrity", "severity": "warning", "page": "entities/Widget.md", "message": "broken wikilink [[Gadget]]" }
  ],
  "total_pages": 3,
  "disabled_rules": {
    "content_freshness": "This wiki is a committed snapshot, so this check measures elapsed calendar time rather than a defect in the pages."
  },
  "ran": ["frontmatter_completeness", "index_sync", "link_integrity", "orphan_detection"]
}

ran names the checks that actually produced the report, in registry order. disabled_rules covers only the narrowing the wiki declared; --rules (and the MCP tool's rules argument) narrows a run without declaring anything, so without ran a short report is indistinguishable from a full one.

A declaration that cannot be honoured is an error, never a silent skip

Situation What happens
A name that is not a registered rule (a typo, or a rule since retired) The run stops with exit 2, naming the file, the unrecognised entry, and every valid rule name
llmwiki.json that is not valid JSON, or whose top level is not a JSON object exit 2, naming the file and the parse error
A lint.disabled_rules that is neither a list of names nor an object mapping names to reasons exit 2
Every registered rule disabled The report states that nothing was checked, instead of printing a clean summary
Every rule a narrowed run selected disabled The same, counted against what the run would have used — --rules content_freshness on a wiki that disables it skipped 1 rule of 1, not 1 of 17

None of these fall back to "this wiki declares no opt-outs". A declaration nobody can read might be switching every check off, so reporting the wiki as clean would be a guess dressed up as a result — and a typo must never leave a check switched on that you believed you had switched off.

Switching a check off hides real findings

This is not a noise filter. A disabled rule does not run, so anything it would have found is simply absent from the report — and the report will look shorter and healthier for it. Reserve the declaration for checks that cannot apply to a wiki, not for checks that are merely inconvenient.

content_freshness on a committed snapshot is the legitimate case, and the one the shipped example uses. That check asks whether a page has gone untouched for three months; on a frozen, published copy the answer is decided by the calendar rather than by anything wrong with the pages, so the check would redden on a date rather than on a defect. Where staleness is real, refreshing the content is the honest cure — switching the check off only removes the reminder that the content has aged.

Environment variables

Variable Description Default
LLMWIKI_CONFIG Override the config file path ./config.json, then examples/sessions_config.json
COPILOT_HOME Override the Copilot CLI base directory ~/.copilot

Vault content root is vault.default_path in config.json. The removed LLMWIKI_ROOT env var is no longer read.

.llmwikiignore

Gitignore-style file at the repo root. One pattern per line. Sessions matching any pattern are skipped during sync.

# Skip a whole project
confidential-client/*

# Skip anything before a date
*2025-11-*

# Skip a specific session
ai-newsletter/2026-04-04-*secret*

# Comments start with #
# Blank lines are ignored

Per-adapter configuration

Each adapter can be configured in the adapters section of config.json. The key must match the adapter's registry name.

Adapter Config key Default enablement Configurable fields
Claude Code claude_code Auto when store present roots, enabled, since
Codex CLI codex_cli Auto when store present roots, enabled, since
Copilot Chat copilot_chat Auto when store present roots, enabled, since
Copilot CLI copilot_cli Auto when store present roots, enabled, since
Cursor IDE cursor_ide Bare sync when enabled / store present roots, enabled, since, global_db
Cursor Agent CLI cursor_cli Auto when store present roots, enabled, since
Gemini CLI gemini_cli Auto when store present roots, enabled, since
OpenCode opencode Auto when store present roots, enabled, since
OpenClaw openclaw Auto when store present roots, enabled, since
ChatGPT chatgpt Opt-in (enabled: true) enabled, export_dirs, min_messages, since
Obsidian obsidian Opt-in (enabled: true, notes intake) vault_paths, exclude_folders, min_content_chars, since
Jira jira Opt-in (enabled: true) server, email, api_token / api_token_env, jql, max_results, since
Meeting transcripts meeting Opt-in (enabled: true) source_dirs, extensions, since

Every adapter accepts optional since (YYYY-MM-DD or "all") — see Sync lookback. Bare llmwiki sync runs every ingest-ready coding-agent adapter whose store is present and not explicitly disabled. Notes/export intake needs enabled: true (#326). Run llmwiki configure-sources to probe paths, set lookbacks, and write settings. Support map: multi-agent-setup.md.

Example:

{
  "filters": {
    "since": "2026-07-31"
  },
  "adapters": {
    "copilot_chat": {
      "roots": ["/custom/path/to/vscode/workspaceStorage"]
    },
    "openclaw": {
      "since": "all"
    }
  }
}

Keyboard shortcuts

⌘K / Ctrl+KOpen command palette
/Focus search
g hGo to home
g pGo to projects
g sGo to sessions
j / kNext / prev row (tables)
?Show this help
EscClose dialogs

Structured queries

Mix key:value filters with free text in the palette:

type:sessionOnly session pages
project:llm-wikiFilter by project name (substring)
model:claudeFilter by model name (substring)
date:>2026-03-01Sessions after a date
date:<2026-04-01Sessions before a date
tags:rustPages mentioning a tag/topic
sort:dateSort results by date (newest first)

Example: type:session project:llm-wiki date:>2026-04 sort:date