@zosmaai/pi-llm-wiki 0.12.0 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/README.md +16 -8
  3. package/dist/extensions/llm-wiki/lib/bootstrap.js +2 -0
  4. package/dist/extensions/llm-wiki/lib/indexing.js +24 -1
  5. package/dist/extensions/llm-wiki/lib/ingest-worker.js +3 -1
  6. package/dist/extensions/llm-wiki/lib/knowledge-document.js +11 -2
  7. package/dist/extensions/llm-wiki/lib/knowledge-links.js +41 -6
  8. package/dist/extensions/llm-wiki/lib/model-command.js +45 -8
  9. package/dist/extensions/llm-wiki/lib/qmd-indexing.js +1024 -0
  10. package/dist/extensions/llm-wiki/lib/qmd-mirror.js +418 -0
  11. package/dist/extensions/llm-wiki/lib/qmd-store.js +112 -0
  12. package/dist/extensions/llm-wiki/lib/recall.js +77 -3
  13. package/dist/extensions/llm-wiki/lib/runtime.js +25 -1
  14. package/dist/extensions/llm-wiki/lib/subagent.js +47 -7
  15. package/dist/extensions/llm-wiki/lib/tools.js +165 -5
  16. package/dist/extensions/llm-wiki/lib/utils.js +16 -2
  17. package/dist/extensions/llm-wiki/lib/wiki-service.js +104 -5
  18. package/dist/mcp/index.js +66 -2
  19. package/dist/mcp/operations.js +26 -2
  20. package/docs/api.md +43 -1
  21. package/docs/architecture.md +28 -0
  22. package/docs/commands.md +1 -0
  23. package/docs/qmd-compatibility.md +47 -0
  24. package/docs/retrieval-benchmark.md +47 -0
  25. package/docs/superpowers/benchmarks/phase-1-current-baseline.json +53 -0
  26. package/docs/superpowers/plans/2026-08-09-qmd-retrieval-phase-2-remediation.md +549 -0
  27. package/docs/superpowers/plans/2026-08-09-qmd-retrieval-phase-2-validated-indexing.md +1493 -0
  28. package/docs/superpowers/plans/2026-08-11-qmd-retrieval-phase-3-retrieval-modes-and-recall-cutover.md +678 -0
  29. package/docs/superpowers/plans/2026-09-05-wikilink-alias-pipe-table-only.md +257 -0
  30. package/extensions/llm-wiki/index.ts +14 -1
  31. package/extensions/llm-wiki/lib/bootstrap.ts +2 -0
  32. package/extensions/llm-wiki/lib/indexing.ts +24 -1
  33. package/extensions/llm-wiki/lib/ingest-worker.ts +10 -2
  34. package/extensions/llm-wiki/lib/knowledge-document.ts +20 -3
  35. package/extensions/llm-wiki/lib/knowledge-links.ts +39 -7
  36. package/extensions/llm-wiki/lib/model-command.ts +57 -12
  37. package/extensions/llm-wiki/lib/qmd-indexing.ts +1304 -0
  38. package/extensions/llm-wiki/lib/qmd-mirror.ts +496 -0
  39. package/extensions/llm-wiki/lib/qmd-store.ts +222 -0
  40. package/extensions/llm-wiki/lib/recall.ts +77 -3
  41. package/extensions/llm-wiki/lib/runtime.ts +57 -5
  42. package/extensions/llm-wiki/lib/subagent.ts +73 -10
  43. package/extensions/llm-wiki/lib/tools.ts +188 -4
  44. package/extensions/llm-wiki/lib/utils.ts +21 -2
  45. package/extensions/llm-wiki/lib/wiki-service.ts +160 -4
  46. package/mcp/index.ts +78 -1
  47. package/mcp/operations.ts +41 -2
  48. package/package.json +9 -6
  49. package/skills/llm-wiki/SKILL.md +7 -1
@@ -9,13 +9,14 @@ import { join } from "node:path";
9
9
  import { bootstrapVault } from "../extensions/llm-wiki/lib/bootstrap.js";
10
10
  import { applyWikilinkGate, buildWikilinkIndex, } from "../extensions/llm-wiki/lib/knowledge-links.js";
11
11
  import { rebuildMetadata } from "../extensions/llm-wiki/lib/metadata.js";
12
+ import { reindexQmdVault } from "../extensions/llm-wiki/lib/qmd-indexing.js";
12
13
  import { searchWikiLayered } from "../extensions/llm-wiki/lib/recall.js";
13
14
  import { saveInsight } from "../extensions/llm-wiki/lib/retro.js";
14
15
  import { captureFile, captureText, captureUrl } from "../extensions/llm-wiki/lib/source-packet.js";
15
16
  import { resolveWikilinkValidation } from "../extensions/llm-wiki/lib/task-config.js";
16
17
  import { readJson } from "../extensions/llm-wiki/lib/utils.js";
17
18
  import { inspectVaultFormat, inspectWritableVault, VaultWriteError, } from "../extensions/llm-wiki/lib/vault-format.js";
18
- import { getWikiStatus, searchRegistry } from "../extensions/llm-wiki/lib/wiki-service.js";
19
+ import { getWikiStatus, reindexWiki, searchRegistry, } from "../extensions/llm-wiki/lib/wiki-service.js";
19
20
  function projectionOutcome(projection) {
20
21
  return projection.ok
21
22
  ? { ok: true }
@@ -24,6 +25,22 @@ function projectionOutcome(projection) {
24
25
  diagnostics: projection.diagnostics.map(({ code, message }) => ({ code, message })),
25
26
  };
26
27
  }
28
+ /**
29
+ * Enqueue a post-projection lexical QMD pass. Model-free and repairable; never
30
+ * fails the authoritative write. Goes through the per-vault in-process queue.
31
+ */
32
+ async function scheduleLexicalQmd(paths) {
33
+ try {
34
+ await reindexQmdVault(paths, {
35
+ scope: "changed",
36
+ components: ["lexical"],
37
+ force: false,
38
+ });
39
+ }
40
+ catch {
41
+ // Generated QMD state is repairable; an authoritative write must not fail.
42
+ }
43
+ }
27
44
  /**
28
45
  * Shared bootstrap operation: create (or update) the vault at `paths`.
29
46
  *
@@ -76,9 +93,13 @@ export async function searchOperation(paths, query, type) {
76
93
  diagnostics: result.diagnostics.map((d) => ({ code: d.code, message: d.message })),
77
94
  };
78
95
  }
96
+ /** Shared reindex operation: delegates to the shared reindexWiki operation. */
97
+ export async function reindexOperation(paths, input) {
98
+ return reindexWiki(paths, input);
99
+ }
79
100
  /** Shared status operation: delegates directly to wiki-service. */
80
101
  export async function statusOperation(paths) {
81
- const status = getWikiStatus(paths);
102
+ const status = await getWikiStatus(paths);
82
103
  return {
83
104
  knowledgeFormat: status.knowledgeFormat,
84
105
  totalPages: status.totalPages,
@@ -88,6 +109,7 @@ export async function statusOperation(paths) {
88
109
  message: d.message,
89
110
  })),
90
111
  lastUpdated: status.lastUpdated,
112
+ qmd: status.qmd,
91
113
  };
92
114
  }
93
115
  /** Shared retro operation: validates vault then delegates to saveInsight. */
@@ -119,6 +141,7 @@ export async function retroOperation(paths, slug, title, body, category, wikilin
119
141
  const projection = projectionOutcome(rebuildMetadata(paths));
120
142
  if (!projection.ok)
121
143
  return projection;
144
+ await scheduleLexicalQmd(paths);
122
145
  return { ok: true, slug: result.slug, sourcePagePath: result.sourcePagePath };
123
146
  }
124
147
  catch (error) {
@@ -171,6 +194,7 @@ export async function captureSourceOperation(paths, input, execApi) {
171
194
  const projection = projectionOutcome(rebuildMetadata(paths));
172
195
  if (!projection.ok)
173
196
  return projection;
197
+ await scheduleLexicalQmd(paths);
174
198
  return { ok: true, sourceId };
175
199
  }
176
200
  catch (error) {
package/docs/api.md CHANGED
@@ -154,7 +154,7 @@ scoring, no PRF, no vault layering. Use for lookups when you already know what y
154
154
 
155
155
  | Name | Type | Required | Description |
156
156
  |------|------|----------|-------------|
157
- | `query` | `string` | ✅ | Search term matched against page IDs, titles, and types |
157
+ | `query` | `string` | ✅ | Search term matched against page IDs, titles, types, states, statuses, categories, domains, tags, aliases, and recall triggers |
158
158
  | `type` | `string` | — | Filter results to a specific page type (e.g. `"concept"`, `"entity"`) |
159
159
 
160
160
  **Returns**
@@ -302,6 +302,48 @@ details: { pageCount: number }
302
302
 
303
303
  ---
304
304
 
305
+ ## wiki_reindex
306
+
307
+ Rebuild or repair the generated QMD search index under `meta/qmd`. Validated, parser-checked mirrors of
308
+ `.llm-wiki/wiki/**` are written to `meta/qmd/documents/{canonical,evidence}/**`, and a manifest maps them
309
+ back to stable `(vault_id, page_id)` identities. The live store lives at `meta/qmd/current/index.sqlite`
310
+ and is replaced atomically via a recoverable copy-on-write swap. **Active recall still uses the legacy
311
+ heuristic until Phase 3**; QMD indexing is independently observable and repairable but no recall path
312
+ depends on it yet.
313
+
314
+ **Parameters**
315
+
316
+ ```
317
+ wiki_reindex(
318
+ scope: "changed" | "all" = "changed",
319
+ components: ("lexical" | "vectors")[] = ["lexical", "vectors"],
320
+ force: boolean = false,
321
+ vault: "active" | "personal" | "project" | "all" = "active"
322
+ )
323
+ ```
324
+
325
+ - `scope`: `changed` hashes and skips unchanged mirrors; `all` rewrites every accepted file (unchanged pages are still counted as `unchanged`).
326
+ - `components`: `vectors` first refreshes documents; `lexical`-only never loads a model. Default: `components: ["lexical", "vectors"]`.
327
+ - `force`: applies only to the selected components (a forced lexical rebuild starts from an empty staging store).
328
+ - `vault`: which vaults to reindex. `all` reports each vault independently.
329
+
330
+ Selecting `vectors` may download approximately 2 GB of models on first use. Cancellation and failures retain
331
+ the last usable `current` store; stale/error/recovering state is repaired by re-running this tool. The swap
332
+ journal is write-ahead intent — each phase is published before the destructive rename it covers, and recovery
333
+ also checks filesystem state, so interrupted promotions are restored or rolled back rather than guessed. Do not copy,
334
+ partially restore, or edit individual SQLite/WAL/SHM files inside `current` — restore the whole directory or rebuild.
335
+
336
+ **Returns**
337
+
338
+ ```
339
+ details: {
340
+ scope, components, vault,
341
+ results: Array<{ root, label, result: QmdReindexResult }>
342
+ }
343
+ ```
344
+
345
+ ---
346
+
305
347
  ## wiki_log_event
306
348
 
307
349
  Append a structured event to the authoritative, append-only `meta/events.jsonl` stream and regenerate available log projections. Every event is timestamped automatically. The event stream must be preserved in full-vault backups; generated Markdown logs cannot reconstruct it.
@@ -75,10 +75,38 @@ WIKI_ROOT/
75
75
  | `.llm-wiki/wiki/**` | Model + user | Editable knowledge pages |
76
76
  | `.llm-wiki/meta/events.jsonl` | Extension tools | Authoritative, append-only; preserve in full-vault backups |
77
77
  | `.llm-wiki/meta/**` except `events.jsonl` | Extension | Generated projections |
78
+ | `.llm-wiki/meta/qmd/**` | Extension | Generated QMD search index (mirrors + SQLite); local, rebuildable with `wiki_reindex` |
78
79
  | `.llm-wiki/` | Human + explicit request | Operating rules |
79
80
 
80
81
  `events.jsonl` records selected extension operations, not every filesystem edit. `meta/log.md` and OKF-mode `wiki/log.md` are one-way projections; neither can recover the event stream.
81
82
 
83
+ ## Generated QMD Search Index (phase 2)
84
+
85
+ `.llm-wiki/meta/qmd/**` is extension-owned, generated, local, and **generated and rebuildable** via `wiki_reindex`. It is a validated, independently repairable search index that no recall path depends on yet (active recall stays the legacy heuristic until Phase 3).
86
+
87
+ ```
88
+ meta/qmd/
89
+ manifest.json # maps generated paths -> (vault_id, page_id)
90
+ documents/
91
+ canonical/**/*.md # parser-valid concept/entity/analysis/synthesis/requirement/skill/case
92
+ evidence/**/*.md # parser-valid source/unknown types
93
+ current/index.sqlite # live QMD store (copied, never edited in place)
94
+ index.lock/ # cross-process lock (owner.json)
95
+ swap.json # journal for crash-safe promotion
96
+ ```
97
+
98
+ - QMD never scans `.llm-wiki/wiki/**` directly; it reads only the validated mirrors QMD owns.
99
+ - `manifest.json` maps validated mirrors back to stable `(vault_id, page_id)` identities.
100
+ - Canonical and evidence collections never overlap.
101
+ - Ordinary write-triggered updates are **lexical and model-free**; `vectors` may download ~2 GB on first use.
102
+ - The live store is replaced atomically via a recoverable copy-on-write swap; failures retain the last usable `current`.
103
+ - `swap.json` is a **write-ahead journal**: each phase is published before the destructive rename it covers, and recovery re-checks the filesystem, so every crash window restores a usable `current` or an explicit missing/error state.
104
+ - Stale `staging-<uuid>` directories left by failed or cancelled pre-journal work are extension-owned and swept while the per-vault lock is held; recovery never touches arbitrary names under `meta/qmd`.
105
+ - Malformed generated artifacts (`swap.json`, `manifest.json`, `index-state.json`) report `error`, never `missing` or `ready`.
106
+ - Generated status exposes `repairComponents` (valid tool component values) so lint can suggest an exact `wiki_reindex` command. `vectors` refreshes documents before embedding, so `components=["vectors"]` repairs stale vectors and their document index together.
107
+ - Do not copy, partially restore, or edit individual SQLite/WAL/SHM files inside `current` — restore the whole directory or rebuild.
108
+ - Full-vault backups include generated searchable text; OKF-only exports do not.
109
+
82
110
  ## Source Packet Format
83
111
 
84
112
  Each captured source becomes a packet:
package/docs/commands.md CHANGED
@@ -41,6 +41,7 @@ off by default** (issue #80) — registered only when `llm-wiki.trajectories` is
41
41
  | `wiki_status` | Instant stats |
42
42
  | `wiki_observe` | Record a timestamped observation from the current session |
43
43
  | `wiki_rebuild_meta` | Force metadata rebuild |
44
+ | `wiki_reindex` | Rebuild/repair the QMD search index at `meta/qmd` (lexical model-free; vectors may download ~2 GB) |
44
45
  | `wiki_reindex_embeddings` | Refresh semantic embeddings (no-op when no embedding provider) |
45
46
  | `wiki_log_event` | Record custom event |
46
47
  | `wiki_watch` | Schedule auto-updates |
@@ -0,0 +1,47 @@
1
+ # QMD Compatibility
2
+
3
+ pi-llm-wiki's next major pins `@tobilu/qmd` **2.5.3**, the latest version published to npm when Phase 1 was planned.
4
+
5
+ ## Runtime
6
+
7
+ - Node.js: `>=22.0.0`
8
+ - TypeScript development peer: `^5.9.3`
9
+ - Package manager: pnpm 9
10
+
11
+ Users requiring Node.js 18 must remain on the previous pi-llm-wiki major.
12
+
13
+ ## Native compatibility
14
+
15
+ Clean-install CI covers:
16
+
17
+ - Linux x64
18
+ - macOS arm64
19
+ - Windows x64
20
+
21
+ QMD brings `better-sqlite3`, `sqlite-vec`, and `node-llama-cpp`. Failure to install required native packages is an installation failure, not a runtime lexical fallback.
22
+
23
+ ## Model-free contract
24
+
25
+ `createStore`, `update`, `searchLex`, `getStatus`, and `close` must work without downloading or loading an embedding, expansion, or reranking model. Ordinary CI tests this path with `QMD_FORCE_CPU=1`.
26
+
27
+ ## Model-backed contract
28
+
29
+ The scheduled/manual model smoke exercises:
30
+
31
+ - `embed`
32
+ - `searchVector`
33
+ - typed hybrid `search` with reranking disabled
34
+ - `expandQuery`
35
+ - expanded/reranked `search`
36
+
37
+ QMD stores default models under `~/.cache/qmd/models`. First use downloads roughly 2 GB across embedding, reranking, and expansion models. CI caches that directory. `QMD_FORCE_CPU=1` avoids GPU probing in compatibility jobs.
38
+
39
+ ## Upgrade rule
40
+
41
+ Do not widen the QMD version range. A QMD upgrade requires:
42
+
43
+ 1. exact-version lock update
44
+ 2. SDK contract and clean-install CI passing
45
+ 3. model smoke passing
46
+ 4. retrieval benchmark comparison before production use
47
+ 5. updated model and native-support documentation
@@ -0,0 +1,47 @@
1
+ # Retrieval Benchmark
2
+
3
+ The Phase 1 benchmark records current heuristic recall quality before QMD powers production retrieval.
4
+
5
+ ## Corpus
6
+
7
+ `test/fixtures/retrieval-benchmark/fixture.ts` contains 22 sanitized Markdown pages and 60 graded queries:
8
+
9
+ - 45 train queries
10
+ - 15 immutable held-out queries
11
+ - exact lookup, aliases, paraphrase, vague recollection, concepts, graph scope, evidence, time, conflicts, conclusions, synthesis, and unrelated negatives
12
+ - English, Chinese, and mixed-language examples
13
+
14
+ The fixture must not contain raw home paths, email addresses, credentials, customer identifiers, or copied private notes. Sanitize representative phrasing before committing it.
15
+
16
+ The CJK query `什么是卡片盒笔记法` (zettelkasten-concept #4) is an intentional guaranteed miss for the current heuristic engine: the page body is English-only, so lexical score is zero by construction. Do not reword the page to make it pass; it exists to prove that only CJK-aware lexical handling (QMD's normalized FTS) can recover it.
17
+
18
+ ## Judgments
19
+
20
+ Grades are:
21
+
22
+ - `3`: directly answers the query
23
+ - `2`: useful supporting evidence or secondary answer
24
+ - `1`: relevant context
25
+
26
+ Roles are `canonical` or `evidence`. Contradiction queries list every claim that must appear together.
27
+
28
+ ## Metrics
29
+
30
+ The report records candidate Recall@20, MRR, nDCG@5, nDCG@10, canonical@3, evidence Recall@20, contradiction coverage, and automatic-recall false-positive rate. Scores are computed from ranked page IDs, never raw engine scores.
31
+
32
+ ## Commands
33
+
34
+ Verify the committed baseline:
35
+
36
+ ```bash
37
+ pnpm benchmark:retrieval
38
+ ```
39
+
40
+ Regenerate after an intentional fixture or baseline-engine change:
41
+
42
+ ```bash
43
+ pnpm benchmark:retrieval:update
44
+ pnpm benchmark:retrieval
45
+ ```
46
+
47
+ Never hand-edit `docs/superpowers/benchmarks/phase-1-current-baseline.json`. Every update must explain why the benchmark or baseline engine changed. Later phases may tune against `train`, but must not inspect or alter held-out judgments while tuning.
@@ -0,0 +1,53 @@
1
+ {
2
+ "schema": 1,
3
+ "fixtureVersion": 1,
4
+ "engine": "current-heuristic",
5
+ "productionRecallChanged": false,
6
+ "packageContract": {
7
+ "node": ">=22.0.0",
8
+ "qmd": "2.5.3"
9
+ },
10
+ "queryCounts": {
11
+ "all": 60,
12
+ "train": 45,
13
+ "heldout": 15
14
+ },
15
+ "metrics": {
16
+ "all": {
17
+ "queryCount": 60,
18
+ "judgedQueryCount": 55,
19
+ "candidateRecall20": 0.872727,
20
+ "mrr": 0.785455,
21
+ "ndcg5": 0.748766,
22
+ "ndcg10": 0.759226,
23
+ "canonicalAt3": 0.836364,
24
+ "evidenceRecall20": 0.8,
25
+ "contradictionCoverage": 0.6,
26
+ "autoFalsePositiveRate": 0.2
27
+ },
28
+ "train": {
29
+ "queryCount": 45,
30
+ "judgedQueryCount": 40,
31
+ "candidateRecall20": 0.925,
32
+ "mrr": 0.827917,
33
+ "ndcg5": 0.775036,
34
+ "ndcg10": 0.787218,
35
+ "canonicalAt3": 0.875,
36
+ "evidenceRecall20": 0.84,
37
+ "contradictionCoverage": 0,
38
+ "autoFalsePositiveRate": 0.2
39
+ },
40
+ "heldout": {
41
+ "queryCount": 15,
42
+ "judgedQueryCount": 15,
43
+ "candidateRecall20": 0.733333,
44
+ "mrr": 0.672222,
45
+ "ndcg5": 0.678714,
46
+ "ndcg10": 0.684582,
47
+ "canonicalAt3": 0.733333,
48
+ "evidenceRecall20": 0.6,
49
+ "contradictionCoverage": 0.6,
50
+ "autoFalsePositiveRate": 0
51
+ }
52
+ }
53
+ }