A personal LLM-powered knowledge base. Raw documents in, linked markdown wiki out. Ask questions with cited answers.
You drop documents (PDFs, HTML, markdown, text) into raw/. The tool compiles each one into a summarized, categorized markdown article with backlinks. Then you ask questions against the corpus — the assistant retrieves the right pages, answers with inline citations, and every claim is grounded in your source material.
Non-goals: not a chat app, not a note-taking app, not a cloud service. This is a local CLI + web UI for long-form research workflows.
Requires Python 3.10+ and an OpenAI API key.
git clone <repo>
cd wikicompile
python3 -m venv .venv
.venv/bin/pip install -e .
export OPENAI_API_KEY=sk-...Optional model override (default: gpt-5.4-nano):
export WIKICOMPILE_MODEL=gpt-4o-mini# Build and run with docker compose
echo "OPENAI_API_KEY=sk-..." > .env
docker compose up -d
# Or run directly
docker build -t wikicompile .
docker run -p 8765:8765 -v ./data:/data -e OPENAI_API_KEY=sk-... wikicompileThe container auto-initializes the project on first run. Drop files into ./data/raw/ and use the web UI at http://localhost:8765.
You can also run CLI commands inside the container:
docker compose exec wikicompile wikicompile compile -p /data
docker compose exec wikicompile wikicompile ask "your question" -p /data# 1. Create a project
wikicompile init -p ~/my-research
# 2. Add sources to raw/
cp ~/papers/*.pdf ~/my-research/raw/
wikicompile clip https://en.wikipedia.org/wiki/BM25 -p ~/my-research
# 3. Compile into a wiki
wikicompile compile -p ~/my-research
# 4. Ask questions
wikicompile ask "What is BM25 and who invented it?" -p ~/my-research
# 5. Multi-turn conversation
wikicompile chat research-thread -p ~/my-research
# 6. Launch the web UI
wikicompile serve-web -p ~/my-research --port 8765
# → http://127.0.0.1:8765my-research/
├── raw/ # Source files you add
│ ├── paper.pdf
│ └── clipped/
│ └── wikipedia-bm25/
│ ├── page.html
│ └── images/
├── wiki/ # LLM-generated markdown
│ ├── index.md
│ ├── pages/
│ │ ├── bm25-ranking.md
│ │ └── dense-retrieval.md
│ └── categories/
│ └── information-retrieval.md
└── .wikicompile/
├── state.json # source tracking, aliases, stubs
├── fts.db # SQLite FTS5 search index
└── sessions/ # chat session history
You rarely touch wiki/ manually — the LLM owns it. Point Obsidian at wiki/ as a vault for reading.
.md, .txt, .markdown, .rst, .org, .html, .htm, .pdf
Each source is SHA-256 hashed; only changed files are re-compiled. The LLM extracts a title, 1-4 categories, an abstract, a full markdown summary, related-term wikilinks, and canonical aliases for the concept.
Aliases let wikilinks route to canonical pages. When an article writes [[BM25]] and the canonical page is pages/okapi-bm25, the alias table maps bm25 → pages/okapi-bm25. Aliases are emitted at compile time by the LLM and can be backfilled retroactively.
When many articles reference a concept that lacks its own page, wikicompile stubs generates a short stub article for it. Stubs appear alongside real articles but with a > **Stub:** banner.
Chat sessions persist to .wikicompile/sessions/<name>.json. They preserve turn history, which pages have been loaded, and an auto-generated summary of older turns (to handle long conversations without blowing context windows).
Run wikicompile --help to list all commands.
| command | what it does |
|---|---|
init |
create raw/, wiki/, state file |
clip <url> |
fetch a URL into raw/clipped/<slug>/ (HTML + images) |
compile |
ingest new/changed files from raw/ → wiki/ |
rebuild-index |
regenerate wiki/index.md and per-category pages from state |
reindex |
rebuild the FTS5 search index from scratch |
Flags: --force (recompile unchanged), --limit N, --no-prune (keep pages from removed raw files).
| command | what it does |
|---|---|
search <query> |
BM25-ranked full-text search over pages |
ask "<question>" |
single-turn Q&A with citations |
chat <session> [<q>] |
multi-turn Q&A (REPL if no question, streaming output) |
sessions |
list chat sessions |
| command | what it does |
|---|---|
status |
article/category/alias counts |
lint |
find dangling wikilinks, orphan pages, candidate new articles |
stubs |
auto-generate stub pages for frequent dangling targets |
link-repair |
LLM-map dangling targets to existing canonical pages |
backfill-aliases |
emit aliases for every real page (handles cross-page conflicts) |
alias set <target> <page_key> |
manually add an alias |
alias unset <target> |
remove an alias |
alias list |
show all aliases |
| command | what it does |
|---|---|
serve |
launch the MCP stdio server (for Claude Code / other LLM clients) |
serve-web [--port N] |
launch the FastAPI HTTP server + web UI |
wikicompile serve-web -p ~/my-research --port 8765
# → http://127.0.0.1:8765A three-pane interface:
- Left: library sidebar — corpus stats, full-text search, sessions, categories, admin buttons
- Center: streaming chat with inline scholarly citations that open pages in the right pane
- Right: page reader with markdown rendering and clickable wikilinks
Slash commands in the chat composer (type / to see the palette):
| command | args |
|---|---|
/search |
<query> — FTS search inline |
/clip |
<url> — clip a URL and auto-compile |
/compile |
— run compile |
/stubs |
— generate stub pages |
/lint |
— dangling links report |
/link-repair |
— map dangling to canonical pages |
/backfill-aliases |
— retroactive alias emission |
/alias |
<target> <page_key> — manual alias |
Admin actions in the sidebar open modals with results tables and progress indicators.
Exposes the wiki as tools to Claude Code, Claude Desktop, or any MCP client.
Register with Claude Code:
claude mcp add wikicompile -- \
/path/to/.venv/bin/wikicompile serve -p ~/my-researchTools exposed:
search_wiki(query, limit)— BM25 searchget_page(page_key)— read a page's markdownlist_pages()— enumerate articles + stubswiki_info()— corpus stats
After registration, you can say "search the wiki for contrastive retrieval" or "read the Self-RAG page" in any Claude Code conversation.
When serve-web is running, these endpoints are available:
Read: GET /api/info, /api/pages, /api/pages/{key}, /api/search?q=..., /api/categories, /api/aliases
Sessions: GET /api/sessions, /api/sessions/{name}, DELETE /api/sessions/{name}
Chat (SSE streaming): POST /api/chat body {session, question} → chunk/done/error events
Ingest & admin: POST /api/clip, /api/admin/compile, /api/admin/stubs, /api/admin/lint, /api/admin/link-repair, /api/admin/backfill-aliases, /api/admin/alias/set, DELETE /api/admin/alias/{target}
All JSON. CORS open for local development.
raw/ ──[compile]──▶ wiki/pages/*.md ──┬──▶ FTS5 index (BM25 search)
├──▶ state.aliases (wikilink routing)
└──▶ state.stubs (auto-generated pages)
question ──[select via FTS + LLM]──▶ 3-6 relevant pages
│
▼
[synthesize with citations] ──▶ answer
Key modules (in wikicompile/):
| module | purpose |
|---|---|
compiler.py |
incremental compile loop with hash-based skip |
extract.py |
md/txt/html/pdf text extraction |
clipper.py |
URL fetcher (stdlib only) |
llm.py |
OpenAI chat completions wrapper (JSON mode) |
search.py |
SQLite FTS5 index |
qa.py |
2-phase Q&A (select → synthesize) |
chat.py |
multi-turn sessions + streaming + summarization |
stubs.py |
stub generation for dangling targets |
aliases.py |
alias inference + backfill with conflict resolution |
lint.py |
dangling links, orphans, new-article candidates |
state.py |
persistent project state (sources, categories, stubs, aliases) |
api.py |
FastAPI HTTP server |
mcp_server.py |
MCP stdio server |
cli.py |
Typer CLI |
wikicompile init -p ~/topic-x
for url in url1 url2 url3; do wikicompile clip "$url" -p ~/topic-x; done
# drop PDFs into ~/topic-x/raw/
wikicompile compile -p ~/topic-x
wikicompile backfill-aliases -p ~/topic-x
wikicompile ask "What are the key ideas in this field?" -p ~/topic-xwikicompile chat deep-dive -p ~/topic-x
>>> What does method X do?
>>> How does it compare to Y?
>>> What are the benchmark numbers for both on dataset Z?Context carries across turns. After ~12 turns older ones get auto-summarized.
wikicompile lint -p ~/topic-x # see dangling links + candidates
wikicompile link-repair -p ~/topic-x # map short names to canonical pages
wikicompile backfill-aliases -p ~/topic-x # emit aliases for all pages
wikicompile stubs --min-refs 3 -p ~/topic-x # generate stubs for high-freq conceptswikicompile serve -p ~/topic-x # run MCP server separately, or
claude mcp add topicX -- wikicompile serve -p ~/topic-x # register persistent serverThen ask Claude Code things like "search my topic-x wiki for dense retrieval" — it calls the MCP tools.
This is a local single-user tool. The security model assumes:
- The HTTP server binds to
127.0.0.1by default — only processes on your machine can reach it. - Admin endpoints (
/api/admin/*) have no authentication — anything running locally can trigger expensive operations (LLM calls cost money). - CORS is locked to
localhost/127.0.0.1origins only, so other browser tabs cannot invoke the API against you.
Hardening in place:
- Path traversal protection on
/api/pages/{key}andget_page()(MCP) — resolved paths must stay insidewiki/. - SSRF protection on
/api/clip— onlyhttp(s)URLs, rejects private/loopback/link-local/reserved IPs (blocks cloud metadata endpoints, RFC 1918 ranges,localhost, etc.). - XSS protection on chat markdown rendering — DOMPurify sanitizes LLM output before injection (defends against prompt-injection via malicious raw/ source documents).
If you want to expose the server on your LAN (--host 0.0.0.0): don't, without adding a reverse proxy with authentication in front. The admin endpoints will let anyone drain your OpenAI credits, overwrite your wiki, or read your compiled pages.
Before publishing a wiki: check .wikicompile/sessions/ — chat history includes the questions you asked and the answers, which may be sensitive. The tool's own .gitignore doesn't exclude project directories since they live outside the package, so if you commit a my-research/ folder alongside, scrub sessions first.
- Cost: each compile call is one LLM request per source (
$0.01-0.05 per document at current prices). Backfill aliases runs once per page ($0.005 each). Q&A is 2 calls per question. - Model choice:
WIKICOMPILE_MODELenv var overrides. Stronger models give better summaries + answers; cheaper models work fine for stubs and alias inference. - Privacy: everything runs locally except OpenAI API calls (for compile/Q&A). No telemetry, no cloud storage.
- Obsidian integration: point Obsidian at
wiki/as a vault. Wikilinks and backlinks work automatically.