wiki-knowledge

Architecture

Point-in-time snapshot of the wiki-knowledge plugin’s script layer, agents, and skills, as of plugin version 0.8.2. This is not maintained on every change — treat it as a map from roughly now, not a live contract. If it disagrees with the code, the code wins.

The script layer is a single TypeScript implementation at enchiridion-ts/ (enchiridion, one subcommand per capability), bundled by esbuild and invoked through wiki-plugin/bin/enchiridion — a POSIX-sh entrypoint that execs node against the bundle (ADR-0017). The names in these diagrams are TypeScript modules under enchiridion-ts/src/.

Diagrams:

  1. Module dependency graph — how enchiridion-ts/src/* depends on each other, clustered by responsibility.
  2. Skill → agent → cluster flow — how each of the plugin’s five slash-command entrypoints reaches the code in diagram 1.
  3. Type diagrams by cluster — one diagram per cluster from (1), sketching its types and module-level functions.

Two seams worth keeping straight, since the diagrams show them:

How this stays current. The redraw keeps the diagrams hand-drawn rather than generated, and the opening warning stays honest about that. Mechanical generation was weighed and set aside: a tool could emit the raw import graph, but the value of this file is the responsibility clustering and the reasoning about the seams — the parts tooling cannot produce. The type diagrams are kept for the same reason: they are the cheap, nameable API contract of each module, and a reader who finds a stale method name is told to trust the code. The cost is the warning above: the next structural change to the module set should touch this file again.

Module dependency graph

Modules are grouped into responsibility clusters. An arrow between clusters means at least one module in the source cluster imports at least one module in the target cluster; individual module-level imports are collapsed for readability (see each cluster’s file list for exact contents). The dashed arrows from the composite root are cli.ts importing every cluster — it is the composition root, one commander file per subcommand, and the wiring of every subcommand passes through it.

Module dependency graph

Cluster contents:

Skill → agent → cluster flow

Each of the plugin’s five skills, traced through its agent (if any) to the cluster(s) it drives. /wiki-watch and /save-conversation are dispatchers: both hand off into the /wiki-ingest flow rather than duplicating it. The hooks row shows the automatic path — hooks.json wires both events to bin/enchiridion hook <event>, and the state they write is what Session capture and Stats read.

Skill → agent → cluster flow

Notes:

Type diagrams by cluster

One diagram per cluster from the module dependency graph. The modules’ types are shown as class boxes with their methods, and module-level functions as <<package>> boxes; interfaces are <<interface>>. Cross-cluster references are dashed and named after the target cluster.

Core library

Core library type diagram

Vault ops

Vault ops type diagram

No SearchIndex relationship here on purpose — the first two seams note above: searchindex does not go through vault, so the old facade arrow is reversed (it imports vaultgit only), and enchiridion search opens the index itself.

Search type diagram

Search correctness lives in Index.sync, which every Search and a bare --reindex run before matching: it compares meta.git_head (the watermark) against Git.CommittedPages(watermark)’s reported Head, and does nothing when they’re equal — one commit lookup, no filesystem work. When they differ, it applies the returned delta (or, on an unreachable watermark or a first build, a full rebuild from HEAD’s tree — ADR-0015) — so the FTS5 table can never go stale because a caller forgot an inline update, and a page that was never committed is never seen at all. There is no ForRoot per-root cache and no Vault facade (the two seams above): the CLI command opens the one Index via searchindex.Open, and passes it down as a discover.Searcher (ADR-0010).

Ingestion pipeline

Ingestion pipeline type diagram

The pipeline is Resolve → Validate → Execute → commit; validation reads only resolved facts and execution writes only resolved pages, so the checked plan and the written plan cannot diverge. The chain-of-evidence check is run twice — pre-flight by validation (a courtesy) and again by commit.Commit as the hard gate — so a hand-built manifest can’t route around it. discover is the one place this cluster reaches into Search: Check classifies overlap candidates against the index via a Searcher, which is how cli’s single open Index reaches it without a vault root. Two types here share a name with another in the same diagram, so the ingestscan ones carry a prefix — IngestCandidate is ingestscan.Candidate (vs discover.Candidate above) and ScanGit is ingestscan.Git (vs commit.Git); the stereotypes name the real package either way.

Session capture

Session capture type diagram

enchiridion save-session reads the transcript path the SessionStart hook recorded (under .claude/wiki-knowledge/sessions/), renders the JSONL transcript to markdown, and writes raw/conversations/<YYYY-MM-DD-hhmm>-<slug>-<short-id>.md, printing the vault-relative path. Both the hook and the session-id environment the capture depends on are Claude Code surfaces. The code still carries an OpenCode adapter — $OPENCODE_SESSION_ID plus opencode export, tie-broken on tracker state — but nothing installs the session-tracker plugin that injected that variable since ADR-0026 retired the OpenCode wiring, so no current install path reaches it.

Watch

Watch type diagram

watch.ts itself is pure — it holds no watcher and touches no filesystem it isn’t handed. The cli.ts watch subcommand is where the composition happens: a chokidar observer feeds Debouncer.RecordEvent, a ticker drains Debouncer.SettledFiles(), and each settled file is queued only if ingestscan.Scan marks it eligible. watch and ingestscan don’t import each other; that edge runs through the composite root.

Stats

Stats type diagram

enchiridion tool-call-stats reads the JSON-lines log the PostToolUse hook appends to per session, and prints the per-tool histogram with the prompt-count proxy — tool-call count, not exact turn count, is the recoverable metric (#99). enchiridion ingest also prints the same summary after the commit SHA, best-effort and silent when no log exists.