A multi-agent knowledge base system that transforms documents into a cross-linked wiki using five distinct editorial roles—reporter, columnist, desk editor, copy editor, and editor-in-chief. It runs entirely local-first without API keys, stores everything as version-controlled markdown, and automatically cascades updates across related pages while tracking conflicting claims.
Self-evolving multi-agent "newsroom" that turns your documents into a cross-linked knowledge wiki — writer ≠ reviewer, local-first, no API keys, a structured alternative to RAG.
README
LLM Wiki Newsroom
A multi-agent AI knowledge base run by a five-role "newsroom" — open-source, local-first, no API keys, no vendor lock-in. Drop articles, documents, and PDFs into the raw/ folder, type a single command, and the newsroom — powered by an agent like Claude Code — reads them, extracts entities, concepts, and relationships, and organizes everything into a fully cross-referenced wiki, a structured and persistent alternative to RAG. Unlike most takes on the idea, the agent that writes a page is never the one that reviews it, and the authoring guidelines evolve themselves over time. Every new document you add also enriches the existing pages. This repo ships with a small example corpus — the debate over what "open source" means for AI — under wiki/, but the framework is domain-agnostic.
Most knowledge tools leave the finding to you. This project makes the AI read and understand your collected documents first, then organizes them into a wiki — with cross-references between pages, automatic flagging of conflicting claims, and per-topic synthesis built in from the start, so later retrieval is fast.
See the output before installing — the example corpus shipped in this repo is published as a browsable GitHub Wiki (no clone needed). It's a rendered static snapshot of the
wiki/folder; the interactive graph below runs locally.

The interactive knowledge graph (graph/graph.html) — every page a node, every wikilink an edge, color-coded by auto-detected cluster, with a live physics layout and filter/search built in. Shown here on a larger private deployment (~2,300 nodes) to convey how it scales; this repo ships a deliberately small 15-node example corpus you can browse the exact same way. (Interface shown in the optional Korean WIKI_LANG=ko mode.)
What makes this different
There are plenty of takes on Karpathy's LLM Wiki idea now. After reading the popular implementations, two things here are genuinely rare:
1. Authoring guidelines that evolve themselves — something I haven't found in the other implementations. When the same review failure keeps recurring, the system drafts a fix to its own writing rules and keeps it only if a blind A/B against a regression set shows it actually helped — an idea borrowed from Self-Harness and Microsoft SkillOpt. So it isn't only the wiki that improves over time, but the rules that build it. (This loop is still experimental — I'm measuring whether it earns its keep rather than claiming it's solved.)
2. A full newsroom, not just "an agent." Plenty of tools wrap one agent around your notes, and a few add a verifier. Here the work is split across five roles — a reporter drafts source pages and entity/concept stubs, a columnist writes the deep cross-source analysis, a desk editor reviews it with fresh eyes, a copy editor runs the deterministic checks, and an editor-in-chief gates publication — and the agent that writes a page is never the one that reviews it. That review is held to an editorial rubric drawn from real craft (journalism, consulting, encyclopedic forms), so a different person or session reproduces the same bar, and a two-sided gate means the deterministic lint and the qualitative review must both pass.
It also leans on Memex-style associative discovery — saved reading trails and "unexpected connection" surfacing — that the other implementations don't carry.
The rest — the knowledge graph, contradiction tracking, cascading updates, plain-markdown/Obsidian output — many LLM-wiki tools have in some form. The self-evolving guidelines, the five-role newsroom with its editorial rubric, and the Memex discovery are the bet.
Highlights
- Persistent, plain-markdown knowledge base — your "second brain" as version-controlled
.mdfiles, not a vendor silo. Doubles as an Obsidian vault for personal knowledge management (PKM). - Cascading updates — ingesting one document refreshes ~10–15 related existing pages automatically.
- Contradiction tracking — conflicting claims between sources are flagged at ingest time, not at query time.
- Interactive knowledge graph — every page a node, every link an edge, auto-clustered and browsable.
- Associative discovery (Memex) — follow connected concepts to surface unexpected relationships.
- Local-first, no API keys — the Python tools (graph, lint, search) run entirely on your machine.
See Key Features below for how each one works.
Installation
git clone https://github.com/alfadur7/llm-wiki-newsroom.git
cd llm-wiki-newsroom
Or click "Use this template" to create your own wiki repo from this scaffold. To start from a clean slate, delete the example pages under
wiki/(keep the folders andgraph/cluster_labels.json), then ingest your own sources with/wiki-ingest.
This project assumes an environment where the AI agent reads and edits files and invokes tools on its own. Support levels by agent:
| Agent | Config file | Support level |
|---|---|---|
| Claude Code (primary) | CLAUDE.md + .claude/commands/ |
All 9 slash commands + advanced features (cascading updates, associative discovery, etc.) |
| Codex | AGENTS.md |
Basic workflow only (drive it with natural language) |
| Gemini CLI | GEMINI.md |
Basic workflow only (drive it with natural language) |
Claude Code-only features include cascading updates that refresh related existing pages whenever a new document is added, a backlink index across all pages, automatic contradiction tracking, and associative discovery that follows connected concepts. AGENTS.md and GEMINI.md carry only the basic workflow from the original SamurAIGPT project. The Python tools used to build the wiki run locally with no external API keys.
Slash Commands (Claude Code)
In Claude Code, typing a /-prefixed command like /wiki-ingest runs the matching workflow. The table below gives each command's role in one line; the Key Features section explains how they work. Argument notation follows <required>, [optional], and a | b | c (pick one); invoking a command with no required argument prints usage and stops.
Core workflow
| Command | Arguments | Description | Example |
|---|---|---|---|
/wiki-ingest |
<file | folder | inbox> |
Absorb one document into the wiki while also refreshing related existing pages. inbox processes the mobile share-sheet queue in a batch |
/wiki-ingest raw/NewsScrap/article.md |
/wiki-query |
<question> |
Find pages related to the question via the graph and answer with supporting evidence | /wiki-query open source AI definition |
/wiki-lint |
[--fix] |
Health-check for broken links, missing pages, contradictions, etc. (--fix auto-repairs) |
/wiki-lint --fix |
/wiki-graph |
— | Compute the connections between pages and generate an interactive graph | /wiki-graph |
News & sharing
| Command | Arguments | Description | Example |
|---|---|---|---|
/wiki-news |
[cluster|keyword] |
Search the web for the latest news related to the wiki's topics and recommend only new articles | /wiki-news open-source-ai-definition |
/wiki-export |
— | Merge the entire wiki into files for upload to a Claude.ai project | /wiki-export |
Associative discovery
"Associative discovery" is inspired by the Memex proposed by Vannevar Bush in 1945 — an exploration style that follows connected concepts to surface unexpected relationships without a fixed search term.
| Command | Arguments | Description | Example |
|---|---|---|---|
/wiki-discover |
<seed | --random | --surprising | --gaps [<slug>]> |
Unexpected connections within 2 hops from a seed (or random seed), or auto-ranking of hubs that bridge clusters | /wiki-discover Meta |
/wiki-trail |
<create|follow|list> [args] |
Build and save a 5–10 step page path that follows a single topic | /wiki-trail create open source AI definition |
/wiki-timeline |
<entity> [year] |
A chronological storyline of a person's or company's events sorted by year | /wiki-timeline Meta 2024 |
You don't have to memorize the slash commands — you can request the same tasks in natural language, and Claude Code figures out the intent and maps it to the right command.
"Ingest this file: raw/NewsScrap/article.md"
"What's the relationship between open weights and the OSI definition?"
"Check the wiki for contradictions and auto-fix them"
"Start from Meta and find unexpected connections"
Architecture
Three-layer structure
This follows the LLM Wiki pattern proposed by Andrej Karpathy. The source documents (Layer 1), the agent-processed wiki (Layer 2), and the agent's operating rules (Layer 3) are kept separate so that humans and AI don't trespass into each other's territory.
The Layer 2 wiki is further formalized into four sub-layers. Knowing which slash command produces or updates each sub-layer makes the workflow intuitive to follow.
| Sub-layer | Role | Producing/updating command | Output location |
|---|---|---|---|
| L2-1 Source reflection | Reflect one source into summary, claims, quotes, and connection sections | /wiki-ingest |
wiki/sources/<slug>.md |
| L2-2 Abstraction | Extract and update the entities, concepts, and timelines mentioned in sources | /wiki-ingest's cascading update · /wiki-timeline |
wiki/entities/, wiki/concepts/, wiki/timelines/ |
| L2-3 Per-domain analysis | Cluster overviews · issue (contradiction) analyses · query answers · discovery trails | Skeletons auto-generated (the full tools/build.py rebuild via /wiki-ingest · /wiki-lint overview --fix; /wiki-lint contradiction --fix for issue analyses) → Claude writes the body against an evaluation rubric. Plus: /wiki-query · /wiki-trail |
wiki/overviews/<cluster>.md, wiki/contradictions/<theme>.md, wiki/syntheses/<slug>.md, wiki/trails/<slug>.md |
| L2-4 Global aggregation | Restate each per-domain overview and per-theme contradiction analysis one paragraph at a time + cross-domain narrative | Claude rewrites periodically | wiki/overview.md, wiki/contradiction.md |
For example, ingesting one article with /wiki-ingest creates L2-1 and L2-2 at the same time. When the next full rebuild recomputes clusters, the relevant per-domain overviews (L2-3) are automatically affected, and Claude rewrites them against the evaluation rubric as needed. The global overview (L2-4) is rewritten periodically once the per-domain overviews stabilize, keeping it current. Each sub-layer is responsible only for its own axis (per-domain overview or issue/contradiction), so editing one page leaves the other axis's files untouched.
raw/ # Layer 1: original sources (immutable)
NewsScrap/ # HTML article clippings (Obsidian Web Clipper-compatible md)
PDF/ # centrally managed PDF documents (drop *.pdf only)
AiChat/ # topic notes excerpted from AI conversations
_inbox.md # mobile share-sheet URL queue (auto-managed)
_archive.md # accumulated inbox processing results (auto-managed)
wiki/ # Layer 2: agent-managed wiki
index.md # full page catalog + drill-down entry point (auto-generated)
overview.md # per-domain overview global aggregation — landscape axis (human + LLM co-managed)
contradiction.md # per-theme contradiction analysis global aggregation — conflict axis (human + LLM co-managed)
_backlinks.json # backlink index (auto-generated)
sources/ # individual source pages + catalog
_source_map.json # ingest deduplication map (auto-generated)
_catalog.md # full source catalog (auto-generated)
_catalog-<slug>.md # per-cluster catalog (auto-generated)
entities/ # companies, institutions, people
concepts/ # technology, strategy, regulation concepts
timelines/ # per-entity chronological timelines
overviews/ # per-domain overviews (landscape per cluster)
contradictions/ # per-theme contradiction analyses (conflict per theme)
_contradictions.json # contradiction source DB — per claim (auto-generated)
_contradictions_themes.json # theme ↔ claim mapping SoT (Claude-generated)
<theme>.md # per-theme deep analysis (human + LLM co-managed)
syntheses/ # saved query answers (Q-A)
trails/ # associative trails (Memex)
wiki-export/ # merged files for Claude.ai Project Knowledge (auto-generated)
graph/ # knowledge graph + clusters
_graph.json # node/edge data (auto-generated)
_clusters.json # Leiden clusters + source assignment (auto-generated)
cluster_labels.json # ★ stable cluster labels — manually edited config file
graph.html # interactive graph visualization (open in browser)
CLAUDE.md # Layer 3: schema (evolved jointly by humans and the LLM)
log.md # chronological operations log (append-only)
lint-report.md # health-check results (auto-generated)
tools/ # Python tools (no API key required)
File naming convention
| Type | Prefix | Examples |
|---|---|---|
| Auto-generated data — scripts overwrite it every run, so do not edit directly | _ |
_backlinks.json, _graph.json, _clusters.json, _catalog*.md, _contradictions.json |
| Human-edited — for manual editing or viewing | none | index.md, overview.md, contradiction.md, graph.html, graph/cluster_labels.json |
Naming rule: an underscore prefix means a script output (don't touch it); no prefix means a file humans edit or read.
The wiki/ root meta files (index, overview, contradiction) start directly with # Title, without frontmatter. Only pages in subdirectories (sources/, entities/, concepts/, overviews/, contradictions/, etc.) require frontmatter.
Placement rules
- repo root: operational outputs (log, lint-report)
wiki/root: wiki-wide meta fileswiki/sources/: source-only meta filesgraph/: graph data and visualizationwiki-export/: merged files for external distributiontools/: Python scripts and script-only config
Language & page schema
The framework's documentation, prose, and wiki page schema tokens are all in English. The tools grep for these exact strings, so they are part of the data format rather than display text: section headers such as ## Summary, ## Connections, ## Overview, and ## Timeline, and inline evidence/relation tags such as [fact], [analysis], and [forecast]. Full internationalization of the page schema — making these tokens configurable per language — is a future direction.
/wiki-ingest <file | folder>
raw/NewsScrap/article.md or raw/PDF/report.pdf (folder argument scans both .md and .pdf)
1) Dedup: check whether it was already ingested, by both URL and file path
2) Read the body: markdown as-is; for PDFs the Read tool interprets the binary directly
3) Write the source page: wiki/sources/<slug>.md with summary, claims, quotes, connection sections
· For PDFs, the original URL is recorded in this page's source_url field (for dedup)
4) Cascading update: append new facts to the entity/concept pages that appear in the source and refresh their reference lists
5) Link enrichment: propose [[wikilink]] candidates for existing page names that appear as plain text in the body
6) Full rebuild: graph/visualization → clusters/catalogs/per-domain overviews → contradiction source DB → index → dependency index,
updated in that order (a single tools/build.py run)
7) Auto-validation: check broken links, filename convention, missing tags
8) Cluster health diagnosis: surface new topic clusters that need a label (graph/cluster_labels.json)
9) Append one line to log.md
/wiki-query <question>
1) Classify the question by type (entity description / relationship between two pages / topic survey / short-fact / wiki overview / contradiction comparison)
2) Narrow candidates by type (the agent checks the "map" before reading full bodies):
tools/query.py graph explain <page> → summarize a page's in/out links, cluster, and connection reasons
tools/query.py graph neighbors <page> → group adjacent pages by topic
tools/query.py graph path <A> <B> → shortest path between two pages and the reason for each hop
tools/query.py qmd hybrid "topic" → body search combining keyword, meaning, and reranking
tools/query.py qmd search "keyword" → BM25 keyword matching (fastest)
tools/query.py qmd vsearch "context sentence" → vector-based semantic similarity
3) Read in full only the ≤10 pages selected above
4) Produce a markdown answer with supporting [[wikilinks]]
5) (Optional) Save the answer to wiki/syntheses/<slug>.md to reuse as material for later queries
/wiki-lint [--fix]
Checks are split into the groups below; invoking with no argument runs them all in sequence
(auxiliary groups like synthesis·trail·timeline also run, and staleness is shown for reference only):
graph — graph topology and reference consistency
· structure : broken wikilinks, orphan hubs, missing entity candidates (and, under WIKI_LANG=ko, Korean entities with English filenames)
· orphans : bidirectional source↔entity reference match (sources frontmatter ↔ source ## Connections)
· clusters : Leiden community health codes [A]–[G] + health-trend log (isolated hubs, unnamed groups, fragile bridges, etc.)
· internal-refs : whether published content links internal build/guide files (.claude/·tools/·CLAUDE.md) — blocks self-references leaking into RAG
· raw-files : source page `source_file:` ↔ raw/ filename integrity (smart-quote folding/stripping; --fix repairs unambiguous matches)
hub — L2-2 hub page (entity·concept·timeline) status
· speakers : people cited ≥3 times across ≥3 distinct sources (speakers/quoted voices)
who lack their own entity page — stub candidates
(under WIKI_LANG=ko; English corpora use cit.A2 + count_mentions.py)
· suggestions : common broken links + frequent body nouns that have no page
(informational, no pass/fail impact)
· schema : required frontmatter fields for entities/·concepts/·timelines/ files
(common title·type·tags·last_updated; entity·concept add sources,
entity adds kind) + type matches the folder name
meta — meta-document conventions (bundle: integrity + drift + language + flat-path guard)
· CLAUDE.md anchor link and file path validity
· craft-skill chain integrity: .claude/layers/_manifest.json ↔ skills criteria.json/checks.py referential closure
· English section-header convention
· python tools/lint.py <flat-subcmd> recurrence prevention
overview — landscape-axis overview files (L2-3 cluster overview + L2-4 overview.md)
· Rubric metrics (W1·W2·W3·X2 / L2-4 adds D1·D2·D3·F1) · sections·frontmatter · Freshness
· narrative drift detection: if another domain's representative page is embedded in the body, advisory notice
(a case where the domain boundary was realigned but the body kept the old flow). Cross-references
declared as paired domains are auto-excluded — see the paired_with field in
graph/cluster_labels.json
· pass a cluster slug or 'aggregate' as the second argument to check only that file
contradiction — conflict-axis per-theme analysis files (wiki/contradictions/<theme>.md)
· frontmatter · four H2 sections · 1:1 theme↔analysis MD mapping
· pass a theme slug as the second argument to check only that file
source — L2-1 source page authoring convention (wiki/sources/<slug>.md)
· diagnose source-schema compliance: claim atomization · quote typing · evidence grading, etc.
· pass a source slug as the second argument to check only that file
Semantic checks (agent): mutually contradicting claims, stale information, empty knowledge gaps
Final result: lint-report.md
--fix behavior by group (only what the group supports actually runs):
| Invocation | What --fix does |
|------|-------------------|
| /wiki-lint --fix (= all) | Run all groups' safe auto-fixes in a batch. EDITOR rewrites and SoT JSON regeneration are **excluded** (require explicit invocation) |
| /wiki-lint graph structure --fix | Auto-add entities mentioned in the body to the source's `## Connections` to reconnect orphan hubs |
| /wiki-lint graph orphans --fix | Auto-backfill source `## Connections` → hub `sources:` frontmatter |
| /wiki-lint graph clusters --fix | Recompute graph data (`_graph.json`·`_clusters.json`) to the current wiki state |
| /wiki-lint hub schema --fix | Auto-fill `type`·`last_updated` in entity·concept·timeline frontmatter |
| /wiki-lint overview --fix | Reconcile cluster ↔ per-domain overview MD (create missing overviews, delete orphan overviews, with a pre-run confirmation prompt) |
| /wiki-lint overview <target> --fix | The above + a Claude EDITOR rewrite instruction block for that domain |
| /wiki-lint contradiction --fix | Reconcile theme ↔ per-theme analysis MD (create missing analyses, delete orphan analyses, with a pre-run confirmation prompt) + insert placeholders for missing H2 |
| /wiki-lint contradiction theme --fix | A Claude instruction block to regenerate the theme ↔ issue mapping SoT (`_contradictions_themes.json`) |
| Other groups/subcommands --fix | Unsupported (warns, then diagnoses only) |
The confirmation prompt can be bypassed with the `--yes` flag — use it in automated environments after pre-review.
New cluster labels are added manually to graph/cluster_labels.json after operator review (no auto-editing).
After --fix completes, tools/build.py runs in full (the empty per-domain overview skeletons get filled at this step).
/wiki-graph
tools/build.py graph (runs the graph stage only)
· Parse every page and compute two kinds of connections (edges)
· explicit edges: [[wikilinks]] in the body
· inferred edges: pairs sharing 3+ common referenced pages
· Generate graph/_graph.json
· graph.html itself is a committed static shell — when opened directly in a browser the physics
simulation runs live to decide node placement. graph.html fetches its data as
JSON directly (_graph.json·_clusters.json·_pages.json); the reading-panel
bundle (_pages.json) is generated in the clusters stage
A full wiki rebuild is run automatically by /wiki-ingest · /wiki-lint --fix.
To run it directly: `python tools/build.py` — a 5-stage sequential pipeline:
graph → _graph.json
clusters → _clusters.json + source catalogs + automatic per-domain overview refresh
+ graph/_pages.json (the reading-panel bundle graph.html fetches)
(Leiden community detection; each document is assigned to the cluster of the hubs
it links to, by weighted vote; labels live in graph/cluster_labels.json)
contradictions → _contradictions.json (source DB collecting contradicting claims from source documents)
index → index.md + backlink index + source map
dependencies → _dependencies.json (dependency index to judge which documents lag behind updates)
Each stage can also run individually: `python tools/build.py <stage>`.
/wiki-news [cluster|keyword]
1) Read the current wiki's topic composition (index.md, cluster meta) to generate search terms
· With a cluster specified: a query combining 3–5 representative hubs of that topic (English by default; Korean only under WIKI_LANG=ko)
· No argument: auto-iterate the top 5 clusters by document count
2) Collect the latest articles via web search → dedup against existing source titles
3) Summarize only new articles into a report and present them as ingest candidates
4) (Optional) Add the URLs of articles to ingest to the inbox queue (raw/_inbox.md)
5) `/wiki-ingest inbox` to fetch + absorb (HTML saved under raw/NewsScrap/, PDF under raw/PDF/)
/wiki-export
Merge only the wiki's synthesis and directory layers under wiki-export/ (for RAG)
· Root meta (overview · contradiction · index) copied 1:1
· Subfolders merged (per-domain overviews · contradiction analyses · timelines · query answers · associative trails)
· Entity/concept bodies are not generated — the graph holds the full text; index.md is a one-line + deep-link directory
· Sources carry only a one-line index instead of full text (all-sources-index.md) — originals live in the graph browser
· README.md — a 2-layer guide + upload budget guide + the deep-link convention to paste into the project instructions box
Push to GitHub to connect as Claude.ai Project Knowledge. Claude.ai loads knowledge in full
(no retrieval), so upload Core (~130K tok) first per the README's budget guide
/wiki-discover <seed | --random | --surprising>
Seed exploration (<seed> or --random)
Choose a starting point:
· With a seed → start from that entity/concept
· --random → randomly pick a "mid-tier hub" with roughly 5–30 backlinks
(neither too famous nor too minor)
Explore:
· Read the seed page + the top 5 documents that reference it
· Find other pages those documents commonly point to
· Present 3–5 "unexpected connections" not directly linked to the seed
Bridge-hub ranking (--surprising)
tools/discover.py surprising
Multiply three criteria to score pages that "bridge topic clusters":
· shortest-path frequency : is the connection hard to make without passing through this page
· neighbor cluster diversity : how many different topics the neighbors span
· over-degree penalty : lower the score for pages that are already too famous
→ auto-present the top N "centers of unexpected connection" with no seed input
/wiki-trail create|follow|list <topic>
create : explore pages related to the topic, build a 5–10 page "reading order" with
per-step commentary, and save it to wiki/trails/<slug>.md
follow : read an existing path in order with per-step commentary
list : list the trails under wiki/trails/
/wiki-timeline <entity> [year]
1) Collect all documents referencing the given person/company and sort by publish date
2) Compose a flow summary in three parts:
· whole trajectory in 1 line (stage1 → stage2 → stage3 ...)
· per-stage summary (year range · key turning points, 2–3 sentences)
· current state in 1–2 sentences
3) Append year-grouped event groups and (optionally) save to wiki/timelines/<entity>.md
How it differs from RAG
If you're weighing this against retrieval rather than against other wiki tools: RAG (Retrieval-Augmented Generation) retrieves source chunks per query and injects them into the answer context. This project instead takes the direction of structuring and accumulating the wiki in advance.
| RAG | LLM Wiki Newsroom | |
|---|---|---|
| Knowledge state | re-extracted per query | organized once and continuously updated |
| Retrieval unit | source chunk | structured wiki page |
| Cross-reference | none | wikilinks + backlink index |
| Contradiction handling | may surface at query time | flagged at ingest time + DB tracked |
| Accumulation effect | none | new sources enrich existing pages |
| Exploration | keyword search | graph traversal + associative trails (Memex) |
Python tools
The scripts that work behind the slash commands. They run locally with no external API keys, and you can call them directly to run just part of the functionality.
| Script | What it does |
|---|---|
build.py |
Rebuild the entire wiki — a 5-stage sequential pipeline: graph/visualization → clusters/catalogs/per-domain overviews → contradiction source DB extraction → index → dependency index. To run a single stage: python tools/build.py <stage> (graph / clusters / contradictions / index / dependencies) |
lint.py |
Group checks: graph (page structure · document↔entity references · cluster health) / hub (quoted speakers · new-page candidates · L2-2 hub frontmatter) / meta (CLAUDE.md internal links · header convention · craft-skill chain integrity) / overview (landscape-axis overview file Rubric · Freshness) / contradiction (conflict-axis per-theme analysis) / source (L2-1 source page schema) / plus auxiliary groups synthesis·trail·timeline·staleness. Also suggests "new page candidates worth creating" at the end of the report. python tools/lint.py graph orphans --fix auto-connects orphan documents; python tools/lint.py overview <target> --fix produces a single per-domain overview skeleton + rewrite instructions |
query.py |
Search/traversal CLI — graph subcommand for graph traversal (path A B·explain N·neighbors N), qmd subcommand for body search (hybrid·search·vsearch). --budget to reduce response size and --json for machine parsing |
discover.py |
Rank pages that bridge clusters (surprising) — combine shortest-path frequency · neighbor cluster diversity · over-degree penalty to auto-surface "centers of unexpected connection" |
Key Features
Persistent wiki
AI conversation sessions evaporate when they end, but this project accumulates the extracted knowledge as structured markdown files. They're plain text files, version-controlled with Git and viewable in ordinary tools like Obsidian or VS Code, so you're not locked into any vendor.
Dual automation
The wiki body is not typed by humans. Two kinds of automation handle it in layers.
- Deterministic automation: Python scripts generate mechanical outputs like links, rankings, and catalogs
- Probabilistic automation: Claude writes narrative prose following authoring guidelines and an evaluation rubric, self-verifying and rewriting against
/wiki-lint's automated metrics
The human operator's role is tuning the guidelines and rubric and making the final acceptance call — focusing on directional oversight rather than sentence-level copyediting. When the guidelines and rubric are fully captured in CLAUDE.md, any other Claude session rewrites to the same quality.
Separation of authoring and review
The probabilistic automation above doesn't have one Claude do everything. The Claude that writes the body and the Claude that re-reads and reviews it are split into different instances, reducing self-bias. Just as a newspaper divides labor among reporters, columnists, and the desk, authoring and verification are placed in different hands.
- Two-sided verification gate — deterministic lint (quantitative metrics like link/citation/structure consistency) and qualitative review with fresh reader's eyes (areas like bias, narrative flow, argument quality) operate as one unit. Passing the quantitative side alone can still be blocked on the qualitative side, and vice versa.
- Verification failure count — if the same criterion fails 1st, 2nd, 3rd in a row, the count is tracked automatically, and a 3rd failure for the same reason is escalated to human-operator review to prevent infinite self-repetition.
- Common 4-stage skeleton — every workflow (ingest, query, health-check, rewrite, etc.) shares an "observe → write → verify → adapt" 4-stage structure, so new tasks are understood by the same pattern.
- Two isomorphic verification ladders — wiki content climbs a Content Verification Ladder (post-edit hook → self-lint → batch lint → qualitative desk review → publish gate), and changes to the writing rules themselves climb a matching Guideline Verification Ladder (deterministic lint → minimal-edit check → blind review → effect-measurement gate). The same "cheapest deterministic check first" principle governs both layers.
- Self-improvement loop — when the same review failure recurs, it doesn't stop at fixing individual cases; it collects those failures and drafts a proposal to fix the authoring guidelines themselves. So not only the wiki body but the rules that build the wiki learn from their own mistakes.
- Proposals aren't applied immediately — a change must show a measured effect: the same task is rewritten side-by-side with and without the change and scored blind, and a pre-fixed regression set confirms nothing else got worse. A substantive change without an accept verdict is held back.
- Guideline edits are reviewed blind — a reviewer who never saw the deliberation reads only the diff and classifies each change as substantive or invariant, so wording churn can't masquerade as improvement.
- Every accepted or rejected proposal lands in a transition ledger, and defects that recur after a fix are ranked first in the next review round — the loop learns from its own failed fixes, not just from new defects.
Cascading updates
Ingesting one new document automatically updates the existing entity/concept pages it mentions. For example, ingesting an article about "Meta releases an open-weights model" extends the fact list in Meta.md and adds an item to the releases section of OpenWeights.md. This implements Karpathy's original idea that "ingesting 1 document changes 10–15 pages" — one of the framework's core characteristics.
Automatic duplicate-document skip
To avoid re-ingesting the same article, it double-indexes by URL and file path (wiki/sources/_source_map.json). Even if a re-scrape via Obsidian Web Clipper changes the filename slightly, a matching URL is detected as a duplicate. Cases where a filename drifts due to Unicode quote differences (' vs ') are normalized and handled too.
Contradiction detection · 3-layer tracking
When a new document conflicts with existing claims, it's auto-recorded at ingest time. Readers drill down three levels using wiki/index.md as the entry point.
wiki/contradiction.md— global aggregation of contradictions. The editor's tension-axis narrative + theme summaries.wiki/contradictions/<theme>.md— per-theme deep analysis (e.g., whether an "open source" model must release its training data). Which themes exist and which raw issues belong to which theme is defined by_contradictions_themes.jsonas the mapping SoT.wiki/contradictions/_contradictions.json— source DB of auto-detected individual issues.
It's designed in macro summary → theme interpretation → raw evidence order, so wherever a reader stops, that layer reads as self-contained. wiki/overview.md covers only per-domain overviews and includes no contradiction references — the common entry point for both axes is wiki/index.md.
Search/traversal queries (tools/query.py)
Having the LLM read every page in full to answer a question is costly, and an AI agent can only handle a limited amount of context at once. This tool is a CLI that helps the agent understand the structure first, or narrow candidates, before reading pages, with two subcommands, graph and qmd.
Graph traversal (python tools/query.py graph ...) — structural queries using link relationships:
graph path Meta OpenSourceInitiative— shows the shortest path connecting two pages and why each link was formed ([[X]] — connection reasonexplanation). You can trace the logical flow from "Meta" to "OpenSourceInitiative" without a single click.graph explain OpenWeights— summarizes which cluster a page belongs to, which pages reference it, and what it points to.graph neighbors Meta— groups directly connected pages by cluster to see "what's around" at a glance.
Body search (python tools/query.py qmd ...) — local search that actually inspects page bodies to find the ones matching a topic (no external transmission):
qmd hybrid "open weights vs open source"— recommendation search combining keyword, meaning, and reranking.qmd search "OpenAI"— BM25 keyword matching (fastest).qmd vsearch "models that are open in name only"— vector-based semantic similarity (finds it by context even when the exact keyword isn't in the body — here, surfacing the open-washing pages).
--budget N limits the number of output lines so the agent pulls exactly as much as it can digest. --json enables program-to-program integration.
PDF ingest (multimodal)
Beyond text articles, PDF documents are absorbed through the same pipeline. Drop a .pdf file into the raw/PDF/ folder, and the next /wiki-ingest run auto-detects it; Claude Code's Read tool opens the PDF directly and interprets its body, tables, and figures. It's a mechanism for handling long documents not published as HTML, like industry reports, regulatory filings, and conference materials.
- Download: Put the PDF URL in the inbox queue (
raw/_inbox.md) and run/wiki-ingest inboxto save it as a binary underraw/PDF/. Just the.pdflands, with no separate meta file. - URL management inside the wiki page: Record the URL in the
source_url:frontmatter ofwiki/sources/<slug>.mdgenerated at ingest, so dedup works even if the same document is re-downloaded. Even if you manually drop just a PDF with no URL, duplicates are filtered by file path.
Health-check · auto-fix
As the wiki grows, problems accumulate: orphan pages (referenced from nowhere), broken wikilinks, missing person pages, naming-convention violations like a Korean company created with an English filename. /wiki-lint checks all of these in one pass and shows a report, and the --fix option immediately repairs the auto-fixable items. When topic clusters form differently than expected, the diagnosis is provided alongside. At the end of the report, "page candidates referenced in multiple places but not yet created" are attached as suggestions, hinting at what to create next.
Hard-to-reverse operations like creating a new page or deleting an existing one first show which files are affected and then ask for confirmation — preventing unintend
Comments (0)
Sign in to join the discussion.
No comments yet
Be the first to share your take.