wiki-knowledge

Personal knowledge management powered by LLM agents. Turns raw documents into a structured, searchable, git-backed markdown wiki vault — then answers questions over it with typed-edge graph traversal and cited synthesis.

Follows the Karpathy LLM-wiki pattern.

What’s inside

Install

Any host (Agent Skills package)

npx skills add dhague/wiki-knowledge --all

Installs all eight skills into the host’s own skill directory — .agents/skills/ for OpenCode and DeepSeek Harness (which reads <projectRoot>/.agents/skills natively), .claude/skills/ for Claude Code, and the equivalent for the other hosts the skills CLI supports. Add -g to install for your user rather than the current project.

The package is host-neutral, so it carries no model tiers and no hooks: every host runs the procedures on its own session model. For the model-pinned install, use the Claude Code plugin below.

Claude Code

  1. Add the marketplace entry and install the plugin:
    /plugin marketplace add dhague/wiki-knowledge
    /plugin install wiki-knowledge
    
  2. Create a vault — either:
    • Local: /wiki-init . inside a project to keep the vault alongside your codebase.
    • Remote: /wiki-init /some/remote/path then set WIKI_ROOT to query it from anywhere. Useful when a wiki spans multiple projects or lives on a shared drive.

The plugin is the fuller install: the same eight skills, plus the three model-pinned subagents and the session hooks.

Joule Work Desktop

Download the per-skill ZIP files from the latest GitHub Release — one per skill — and install each via Joule Desktop’s “Install from file” option (Extensions > Add Skill > Upload).

Standalone CLI

The script layer is a TypeScript bundle shipped inside each skill’s scripts/ directory, run by node (or bun where that is the only runtime available). In a checkout, wiki-plugin/bin/enchiridion is a thin shim that execs node against the plugin’s own copy of the bundle.

Design principles

Cost-optimised by design. Ingestion and retrieval run as subagents with model selection tuned to task. Sonnet handles the expensive judgment work (semantic chunking, edge typing); Haiku handles high-volume retrieval at a fraction of the cost. Each query only explores the frontier it needs — no expensive vector re-ranking, no full-graph traversal.

Predictability through scripts, not prompts. Everything that can be deterministic is. Page placement, frontmatter parsing, link rewriting, search indexing, and commit construction run as subcommands of a single CLI (the enchiridion bundle, run on Node or Bun) — no model in the loop. The agents call it for side effects and read its output; they never generate file paths, YAML, or git operations from a prompt.

No new infrastructure. SQLite FTS5 search runs in-process with zero extra dependencies. No additional runtime to install — the script layer runs on the Node or Bun the host already has. No vector database, no MCP server, no background daemons. The vault is just a git repo of markdown files — portable, diffable, and backup-friendly.

Trust and provenance. Every derived page traces back to its raw source through a chain of evidence. Bitemporal metadata (when the knowledge is from vs. when it was written) and explicit volatility annotations make staleness visible, not hidden.

Agent-native, not API-native. Ingestion and retrieval are skills that the host’s agent executes by reading instructions and running scripts. This means the full context window, tool use, and reasoning of frontier models are available — not limited by a fixed RAG pipeline or a hardcoded prompt template.

Commands

Command Purpose
/wiki-init [path] Scaffold a new vault (folders, git repo, index)
/wiki-ingest <path> Ingest one file, a folder, or sweep raw/
/wiki-watch Long-running auto-ingest watcher for raw/
/wiki-ask <question> Grounded, cited answer from the vault
/wiki-lint [vault-path] Vault health check — prioritised findings against the conventions contract, plus mechanical auto-fixes
/wiki-export ["Title"] Render the vault as a static HTML site — multi-page, or one self-contained file
/save-conversation Capture and ingest the current session

Vault lint

/wiki-lint runs 20 checks in two dimensions — structural (kind-folder conformance, frontmatter link format, split links, duplicate frontmatter, orphans, concept fragmentation, Consolidation-exclusion registry integrity) and retrievability (missing volatility/source_date, unresolved supersession, data gaps, summary quality, under-typed edges, over-typed and stale edges) — and reports what it finds ordered HIGH, MEDIUM, LOW.

Thirteen of the checks are mechanical, run through enchiridion check <name> --json; the other seven need page judgment. Every finding carries a fix level: auto-fix (applied without asking — link format, unambiguous raw_source and cross-reference repairs, folded frontmatter links, redundant frontmatter blocks), report-only (the fix needs author judgment), or confirm-first (a proposed change you approve or skip — a page move, an edge retype, an orphan delete, or a concept consolidation).

A concept consolidation (concept-fragmentation check) is proposed one cluster at a time and never batched, because it deletes committed pages. Each cluster is first assessed: enchiridion assess <refs...> reads every member in full from one committed snapshot, and the assessment returns one of three dispositions — consolidate (one concept: recommend a survivor), relate (distinct but related: recommend typed edges), or conflict (the claims disagree: recommend the supersession flow) — with a short rationale. Assessment writes nothing; on yes to a consolidate it hands off to the wiki-ingest procedure, which reads the same members, authors the merged survivor, and pins the plan to the assessed snapshot so a cluster that changed before the write is refused rather than merged stale. On no it offers to remember the decision as a Consolidation exclusion.

Consolidation exclusions

A remembered decline lives in CONSOLIDATION_EXCLUSIONS.yaml in the pages’ kind-folder — committed with the vault, and safe to inspect or edit by hand:

exclusions:
  - members:
      - page_ref: wiki/concepts/authentication.md
        blob_oid: 9574fbc08f                  # the blob object ID at HEAD
        fingerprint: sha256:0123456789abcdef  # the semantic fingerprint
      - page_ref: wiki/concepts/authorization.md
        blob_oid: 53a7d7365a
        fingerprint: sha256:fedcba9876543210
    reason: "Distinct concepts: authentication establishes identity; authorization grants access."

The object IDs shown are illustrative; the plugin writes their full Git value and the full semantic digest. A matching blob_oid is the fast path. If the blob changed, the plugin compares the semantic fingerprint, which covers the title, summary, typed relationships, supersedes and body while ignoring tags, source date, volatility, YAML formatting and field order. An irrelevant edit therefore preserves the decision and refreshes the cached object ID, while a content change removes that member from the effective exclusion — unchanged members remain excluded while at least two remain, and only a matching member set suppresses a proposal.

On yes to the offer it runs

enchiridion exclusion add wiki/concepts/a.md wiki/concepts/b.md --reason "Distinct concepts: …"

which records the members’ committed revisions and commits the registry on its own; deleting a record by hand lets its cluster be proposed again. Cached values need no hand maintenance — enchiridion fix consolidation-exclusions refreshes a blob ID whose page changed only in ways outside the fingerprint and collapses identical records, while --prune drops members that no longer match HEAD and deletes a record left with fewer than two. A malformed registry never suppresses a candidate and is reported as a HIGH finding. Any contradicts: or supersedes: edge between two candidate pages excludes them from a proposal automatically, with no registry record.

Implicit concepts

A term that recurs across three or more pages without a page of its own is proposed as a candidate for extraction. Recurrence is judged from page bodies, never from titles, summaries, tags or search snippets: enchiridion read-pages reads them from one committed snapshot in bounded batches (12 pages or 48 KiB at a time), and every body a lint run reads is remembered for that run, so a page another body-reading check already read is not read twice. A run’s sweep reads at most 60 new bodies by default. A proposal names the term and at least three supporting pages with the revision each was read at, so anyone can re-run the read and check it; when a large vault leaves pages unread, the report says so rather than implying full coverage.

Invocation follows the usual vault-root resolution: $WIKI_ROOT if set, else a path argument, else the current directory.

Export flags

/wiki-export asks whether you want multi-page or a single file, then passes these through to enchiridion export. Multi-page is the default: a directory of linked HTML pages under web/. --single-file writes one self-contained HTML file instead — every page a section, navigated by hash — which is the form to attach to an email: the recipient opens it straight from the attachment, with no unzip, no server and no network.

Flag Purpose
--single-file Write one self-contained HTML file instead of a directory tree
--out <path> Output directory — or, with --single-file, the output file (defaults: web/, or wiki.html at the vault root)
--raw Include raw/ pages in the site
--force Overwrite a non-empty output directory (multi-page only)
--allow-dirty Export with uncommitted changes in the exported subtree
--title <title> Wiki title for this run only
--save-title <title> Save the title as the persistent default (exports nothing)
--start-page <ref> Export this page as the site’s landing page for this run only
--save-start-page <ref> Save the ref as the vault’s persistent start page (a blank ref clears it; exports nothing)
--candidates Emit the ranked entry-point candidates as one JSON line, then exit (writes nothing)
--starters <refs...> The entry-point pages for the front page, optionally as ref=annotation

A single-file export over 5 MB is still written, with a note on stderr suggesting multi-page mode.

The site’s title — the sticky nav bar and the front page heading — resolves --title → the saved title → the vault root directory name. /wiki-export "My Knowledge Base" sets it for one run and then offers to save it; the saved title lives in .wiki-knowledge/config.json at the vault root, gitignored beside the search index (ADR-0023).

--start-page nominates a page as the site’s landing page. That page is exported at the front page’s path instead of its own — so it appears exactly once, and every link to it resolves to the landing page — the generated title/page-count/get-started blocks are dropped, and a list of the kind index pages is added at the foot of the page. The choice resolves the same way as the title: --start-page → the saved startPage → the generated front page. --save-start-page persists it (a blank value clears it). A nominated page the export does not carry is a hard error rather than a quiet fallback to the generated front page (ADR-0022).

Vault structure

raw/           # Inbox — drop documents here for ingestion
wiki/
  concepts/    # Abstract ideas, frameworks, definitions
  entities/    # Concrete people, tools, projects, organizations
  sources/     # Provenance stubs (one per raw artifact)
  synthesis/   # Cross-cutting analysis and summaries

Every page has YAML frontmatter with a typed edge graph (refines, contradicts, example-of, source, related, supersedes) and bitemporal metadata (source_date, volatility).

Development

The script layer is a single TypeScript implementation (ADR-0017), bundled by esbuild and invoked via wiki-plugin/bin/enchiridion — a thin shim that execs node against the bundle. ENCHIRIDION_BIN points that entrypoint at a local build or alternate runtime instead.

cd enchiridion-ts
npm ci
npm run typecheck && npm run lint && npm run format:check
npm run build   # esbuild bundle to dist/cli.cjs + wasm sidecar
npm test

# Run any subcommand against the built bundle
WIKI_ROOT=<path_to_vault> node dist/cli.cjs search "connection pooling" --limit 10
WIKI_ROOT=<path_to_vault> node dist/cli.cjs ingest-scan --json

wiki-plugin/skills/ is the canonical, hand-edited skill tree; repo-root skills/ is a generated copy of it, kept in step by scripts/sync-skills.sh — which scripts/release.sh calls too, alongside refreshing the bundle inside every skill. CI fails a PR if the two trees diverge.

wiki-plugin/tests/ holds the shim’s bats suite (brew install bats-core). The skill tree’s structural checks — frontmatter name matches the directory, descriptions are non-empty, no host-specific spelling in portable prose, and cut-release carries a real metadata.internal: true — live in the TypeScript suite (enchiridion-ts/src/skills.test.ts) and run with npm test.

Architecture

wiki-knowledge Plugin — Runtime Architecture

Key decisions are documented in docs/adr/:

See CONTEXT.md for the domain glossary.

Full architecture documentation is here.