@zosmaai/pi-llm-wiki 0.11.4 → 0.11.6

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 (62) hide show
  1. package/README.de.md +8 -0
  2. package/README.es.md +8 -0
  3. package/README.fr.md +8 -0
  4. package/README.hi.md +8 -0
  5. package/README.ja.md +8 -0
  6. package/README.ko.md +8 -0
  7. package/README.md +8 -0
  8. package/README.pt.md +8 -0
  9. package/README.ru.md +8 -0
  10. package/README.zh.md +8 -0
  11. package/assets/wiki-dashboard.png +0 -0
  12. package/commands/wiki-ingest.md +1 -0
  13. package/commands/wiki-req.md +1 -0
  14. package/commands/wiki-retro.md +1 -0
  15. package/dist/extensions/llm-wiki/lib/dashboard-command.js +86 -0
  16. package/dist/extensions/llm-wiki/lib/dashboard.js +175 -0
  17. package/dist/extensions/llm-wiki/lib/guardrails.js +30 -1
  18. package/dist/extensions/llm-wiki/lib/host.js +21 -1
  19. package/dist/extensions/llm-wiki/lib/ingest-worker.js +44 -20
  20. package/dist/extensions/llm-wiki/lib/knowledge-document.js +20 -2
  21. package/dist/extensions/llm-wiki/lib/knowledge-links.js +133 -27
  22. package/dist/extensions/llm-wiki/lib/metadata.js +6 -6
  23. package/dist/extensions/llm-wiki/lib/observation.js +22 -3
  24. package/dist/extensions/llm-wiki/lib/retro.js +38 -4
  25. package/dist/extensions/llm-wiki/lib/runtime.js +2 -2
  26. package/dist/extensions/llm-wiki/lib/settings-command.js +377 -0
  27. package/dist/extensions/llm-wiki/lib/task-config.js +100 -1
  28. package/dist/extensions/llm-wiki/lib/tools.js +47 -8
  29. package/dist/mcp/index.js +2 -1
  30. package/dist/mcp/operations.js +21 -2
  31. package/docs/api.md +24 -1
  32. package/docs/commands.md +6 -1
  33. package/docs/configuration.md +11 -0
  34. package/docs/obsidian.md +6 -6
  35. package/docs/superpowers/plans/2026-08-09-qmd-retrieval-phase-1-quality-baseline-and-compatibility.md +1520 -0
  36. package/docs/superpowers/plans/2026-08-27-wikilink-resolver-normalization.md +735 -0
  37. package/docs/superpowers/plans/2026-08-29-wikilink-gate-ensure-page-retro.md +642 -0
  38. package/docs/superpowers/plans/2026-08-29-wikilink-write-validation.md +695 -0
  39. package/docs/superpowers/roadmaps/2026-08-09-qmd-retrieval-roadmap.md +448 -0
  40. package/docs/superpowers/specs/2026-08-08-qmd-retrieval-design.md +806 -0
  41. package/extensions/llm-wiki/index.ts +4 -0
  42. package/extensions/llm-wiki/lib/dashboard-command.ts +106 -0
  43. package/extensions/llm-wiki/lib/dashboard.ts +210 -0
  44. package/extensions/llm-wiki/lib/guardrails.ts +26 -1
  45. package/extensions/llm-wiki/lib/host.ts +21 -1
  46. package/extensions/llm-wiki/lib/ingest-worker.ts +64 -27
  47. package/extensions/llm-wiki/lib/knowledge-document.ts +21 -2
  48. package/extensions/llm-wiki/lib/knowledge-links.ts +208 -35
  49. package/extensions/llm-wiki/lib/metadata.ts +10 -6
  50. package/extensions/llm-wiki/lib/observation.ts +23 -3
  51. package/extensions/llm-wiki/lib/retro.ts +48 -4
  52. package/extensions/llm-wiki/lib/runtime.ts +2 -2
  53. package/extensions/llm-wiki/lib/settings-command.ts +483 -0
  54. package/extensions/llm-wiki/lib/task-config.ts +138 -0
  55. package/extensions/llm-wiki/lib/tools.ts +62 -8
  56. package/mcp/index.ts +12 -1
  57. package/mcp/operations.ts +32 -2
  58. package/package.json +4 -4
  59. package/prompts/wiki-ingest.md +1 -0
  60. package/prompts/wiki-req.md +1 -0
  61. package/prompts/wiki-retro.md +1 -0
  62. package/skills/llm-wiki/SKILL.md +11 -1
@@ -11,12 +11,16 @@ import {
11
11
  serializeKnowledgeDocument,
12
12
  writeKnowledgeDocumentFile,
13
13
  } from "./knowledge-document.js";
14
- import { buildResolvedBacklinks } from "./knowledge-links.js";
14
+ import {
15
+ applyWikilinkGate,
16
+ buildResolvedBacklinks,
17
+ buildWikilinkIndex,
18
+ } from "./knowledge-links.js";
15
19
  import { repairLegacyKnowledgeDocuments } from "./legacy-repair.js";
16
20
  import { type Registry, appendEvent, rebuildMetadata, rebuildMetadataLight } from "./metadata.js";
17
21
  import type { Runtime } from "./runtime.js";
18
22
  import { captureFile, captureText, captureUrl } from "./source-packet.js";
19
- import { parseModelRef } from "./task-config.js";
23
+ import { loadTaskConfig, parseModelRef, resolveWikilinkValidation } from "./task-config.js";
20
24
  import {
21
25
  type VaultPaths,
22
26
  detectVaultFormat,
@@ -415,6 +419,7 @@ export function registerWikiIngest(pi: ExtensionAPI, runtime?: Runtime): void {
415
419
  manifest: s.manifest,
416
420
  extracted: s.extracted,
417
421
  synthesisLanguage: runtime.config.synthesisLanguage,
422
+ wikilinkValidation: runtime.config.wikilinkValidation,
418
423
  });
419
424
  if (committed) {
420
425
  // Background semantic embeddings (#66): embed the pages this
@@ -428,8 +433,10 @@ export function registerWikiIngest(pi: ExtensionAPI, runtime?: Runtime): void {
428
433
  ];
429
434
  launchEmbedPages(runtime, launchCtx, paths, pageIds, `embed:ingest:${s.id}`);
430
435
  }
436
+ const wl = committed?.wikilinkDiagnostics?.length ?? 0;
437
+ const wlNote = wl > 0 ? `, ${wl} wikilink issue${wl === 1 ? "" : "s"}` : "";
431
438
  const summary = committed
432
- ? `LLM Wiki: ingested ${s.id} → ${committed.entitiesCreated.length} entit${committed.entitiesCreated.length === 1 ? "y" : "ies"}, ${committed.conceptsCreated.length} concept${committed.conceptsCreated.length === 1 ? "" : "s"}`
439
+ ? `LLM Wiki: ingested ${s.id} → ${committed.entitiesCreated.length} entit${committed.entitiesCreated.length === 1 ? "y" : "ies"}, ${committed.conceptsCreated.length} concept${committed.conceptsCreated.length === 1 ? "" : "s"}${wlNote}`
433
440
  : `LLM Wiki: ${s.id} produced no synthesis`;
434
441
  if (ctx.hasUI) {
435
442
  ctx.ui.notify(summary, committed ? "info" : "warning");
@@ -565,7 +572,42 @@ export function registerWikiEnsurePage(pi: ExtensionAPI, runtime?: Runtime): voi
565
572
  }
566
573
 
567
574
  const today = fmtDate();
568
- const body = params.content ?? buildPageBody(type, params.title);
575
+ let body = params.content ?? buildPageBody(type, params.title);
576
+
577
+ // Pre-write wikilink gate (#172): validate/normalize caller-supplied content.
578
+ const mode = resolveWikilinkValidation(loadTaskConfig(ctx.cwd));
579
+ let wikilinkIssues: string[] = [];
580
+ if (mode !== "off") {
581
+ const registry = readJson<{ pages: Record<string, unknown> }>(
582
+ join(paths.meta, "registry.json"),
583
+ { pages: {} },
584
+ );
585
+ const gate = applyWikilinkGate(
586
+ body,
587
+ buildWikilinkIndex(Object.keys(registry.pages)),
588
+ `${folder}/${slug}`,
589
+ mode,
590
+ );
591
+ wikilinkIssues = gate.diagnostics.map((d) => d.message);
592
+ if (!gate.ok) {
593
+ return {
594
+ content: [
595
+ {
596
+ type: "text",
597
+ text: `Rejected write — unresolved/ambiguous wikilinks:\n${wikilinkIssues
598
+ .map((m) => `- ${m}`)
599
+ .join("\n")}`,
600
+ },
601
+ ],
602
+ details: { error: "link_validation", issues: wikilinkIssues } as Record<
603
+ string,
604
+ unknown
605
+ >,
606
+ isError: true,
607
+ };
608
+ }
609
+ if (mode === "normalize") body = gate.body;
610
+ }
569
611
  const doc = createKnowledgeDocument(
570
612
  `${folder}/${slug}.md`,
571
613
  {
@@ -595,9 +637,16 @@ export function registerWikiEnsurePage(pi: ExtensionAPI, runtime?: Runtime): voi
595
637
  rebuildMetadataLight(paths);
596
638
  }
597
639
 
640
+ const gateNote = wikilinkIssues.length
641
+ ? `\n\n⚠️ ${wikilinkIssues.length} wikilink issue(s):\n${wikilinkIssues.map((m) => `- ${m}`).join("\n")}`
642
+ : "";
598
643
  return {
599
- content: [{ type: "text", text: `✅ Created ${type} page: \`${pagePath}\`` }],
600
- details: { path: pagePath, created: true } as Record<string, unknown>,
644
+ content: [{ type: "text", text: `✅ Created ${type} page: \`${pagePath}\`${gateNote}` }],
645
+ details: {
646
+ path: pagePath,
647
+ created: true,
648
+ wikilinkIssues,
649
+ } as Record<string, unknown>,
601
650
  };
602
651
  },
603
652
  });
@@ -871,7 +920,7 @@ function runWikiLint(paths: VaultPaths, autoFix: boolean): string {
871
920
 
872
921
  const discovery = discoverKnowledgeDocuments(paths);
873
922
  const pages = discovery.documents;
874
- const knownIds = new Set(pages.map((page) => page.id));
923
+ const wikilinkIndex = buildWikilinkIndex(pages.map((page) => page.id));
875
924
  const inbound = Object.fromEntries(pages.map((page) => [page.id, 0]));
876
925
  const gapSources = new Map<string, Set<string>>();
877
926
  const findings: string[] = [];
@@ -879,7 +928,7 @@ function runWikiLint(paths: VaultPaths, autoFix: boolean): string {
879
928
  let contradictions = 0;
880
929
 
881
930
  for (const page of pages) {
882
- const resolved = buildResolvedBacklinks(page.id, page.body, knownIds);
931
+ const resolved = buildResolvedBacklinks(page.id, page.body, wikilinkIndex);
883
932
  for (const target of resolved.targets) inbound[target]++;
884
933
  for (const unresolved of resolved.unresolved) {
885
934
  const sources = gapSources.get(unresolved.target) ?? new Set<string>();
@@ -888,6 +937,11 @@ function runWikiLint(paths: VaultPaths, autoFix: boolean): string {
888
937
  missingPages++;
889
938
  findings.push(`Missing page: ${unresolved.target} (in ${page.id})`);
890
939
  }
940
+ for (const d of resolved.diagnostics) {
941
+ if (d.code === "link_ambiguous") {
942
+ findings.push(d.message.replace("Ambiguous wikilink: ", "Ambiguous: "));
943
+ }
944
+ }
891
945
  }
892
946
 
893
947
  let orphans = 0;
package/mcp/index.ts CHANGED
@@ -15,6 +15,10 @@ import { join } from "node:path";
15
15
  import { McpServer } from "@modelcontextprotocol/server";
16
16
  import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
17
17
  import * as z from "zod/v4";
18
+ import {
19
+ loadTaskConfig,
20
+ resolveWikilinkValidation,
21
+ } from "../extensions/llm-wiki/lib/task-config.js";
18
22
  import { getVaultPaths, resolveVaultPaths } from "../extensions/llm-wiki/lib/utils.js";
19
23
  import { createExecApi } from "./exec.js";
20
24
  import {
@@ -265,7 +269,14 @@ server.registerTool(
265
269
  }
266
270
 
267
271
  const paths = getPaths();
268
- const result = await retroOperation(paths, slug, title, body, category);
272
+ const result = await retroOperation(
273
+ paths,
274
+ slug,
275
+ title,
276
+ body,
277
+ category,
278
+ resolveWikilinkValidation(loadTaskConfig(process.cwd())),
279
+ );
269
280
 
270
281
  if (!result.ok) {
271
282
  return {
package/mcp/operations.ts CHANGED
@@ -6,13 +6,20 @@
6
6
  * registry entries, or builds page strings itself.
7
7
  */
8
8
 
9
+ import { join } from "node:path";
9
10
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
10
11
  import { bootstrapVault } from "../extensions/llm-wiki/lib/bootstrap.js";
12
+ import {
13
+ type WikilinkValidationMode,
14
+ applyWikilinkGate,
15
+ buildWikilinkIndex,
16
+ } from "../extensions/llm-wiki/lib/knowledge-links.js";
11
17
  import { type ProjectionResult, rebuildMetadata } from "../extensions/llm-wiki/lib/metadata.js";
12
18
  import { type RecallResult, searchWikiLayered } from "../extensions/llm-wiki/lib/recall.js";
13
19
  import { saveInsight } from "../extensions/llm-wiki/lib/retro.js";
14
20
  import { captureFile, captureText, captureUrl } from "../extensions/llm-wiki/lib/source-packet.js";
15
- import type { VaultPaths } from "../extensions/llm-wiki/lib/utils.js";
21
+ import { resolveWikilinkValidation } from "../extensions/llm-wiki/lib/task-config.js";
22
+ import { type VaultPaths, readJson } from "../extensions/llm-wiki/lib/utils.js";
16
23
  import {
17
24
  VaultWriteError,
18
25
  inspectVaultFormat,
@@ -134,6 +141,7 @@ export async function retroOperation(
134
141
  title: string,
135
142
  body: string,
136
143
  category?: string,
144
+ wikilinkValidation?: WikilinkValidationMode,
137
145
  ): Promise<
138
146
  | { ok: true; slug: string; sourcePagePath: string }
139
147
  | { ok: false; diagnostics: Array<{ code: string; message: string }> }
@@ -145,8 +153,30 @@ export async function retroOperation(
145
153
  diagnostics: vaultCheck.diagnostics.map((d) => ({ code: d.code, message: d.message })),
146
154
  };
147
155
  }
156
+ // Pre-write wikilink gate (#172): validate/normalize caller-supplied body.
157
+ const mode = resolveWikilinkValidation({ wikilinkValidation });
158
+ let gateBody = body;
159
+ if (mode !== "off") {
160
+ const registry = readJson<{ pages: Record<string, unknown> }>(
161
+ join(paths.meta, "registry.json"),
162
+ { pages: {} },
163
+ );
164
+ const gate = applyWikilinkGate(
165
+ body,
166
+ buildWikilinkIndex(Object.keys(registry.pages)),
167
+ `sources/${slug}`,
168
+ mode,
169
+ );
170
+ if (!gate.ok) {
171
+ return {
172
+ ok: false,
173
+ diagnostics: gate.diagnostics.map((d) => ({ code: "link_validation", message: d.message })),
174
+ };
175
+ }
176
+ if (mode === "normalize") gateBody = gate.body;
177
+ }
148
178
  try {
149
- const result = saveInsight(paths, slug, title, body, category, { rebuild: false });
179
+ const result = saveInsight(paths, slug, title, gateBody, category, { rebuild: false });
150
180
  const projection = projectionOutcome(rebuildMetadata(paths));
151
181
  if (!projection.ok) return projection;
152
182
  return { ok: true, slug: result.slug, sourcePagePath: result.sourcePagePath };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zosmaai/pi-llm-wiki",
3
- "version": "0.11.4",
3
+ "version": "0.11.6",
4
4
  "description": "Self-maintaining LLM Wiki for Pi — Karpathy-pattern knowledge base with immutable source capture, automated ingestion, search, linting, and Obsidian-compatible vault. auto-updating personal & company wiki.",
5
5
  "keywords": [
6
6
  "pi",
@@ -75,17 +75,18 @@
75
75
  ]
76
76
  },
77
77
  "peerDependencies": {
78
- "@mariozechner/pi-coding-agent": "*",
79
- "typebox": "*"
78
+ "@mariozechner/pi-coding-agent": "*"
80
79
  },
81
80
  "engines": {
82
81
  "node": ">=18"
83
82
  },
84
83
  "dependencies": {
85
84
  "@cfworker/json-schema": "^4.1.1",
85
+ "@mariozechner/pi-tui": "0.70.6",
86
86
  "@modelcontextprotocol/server": "^2.0.0",
87
87
  "mdast-util-from-markdown": "^2.0.3",
88
88
  "node-html-markdown": "^2.0.0",
89
+ "typebox": "^1.1.34",
89
90
  "yaml": "^2.9.0",
90
91
  "zod": "^4.0"
91
92
  },
@@ -98,7 +99,6 @@
98
99
  "@types/mdast": "^4.0.4",
99
100
  "@types/node": "^22",
100
101
  "@vitest/coverage-v8": "^3.2.4",
101
- "typebox": "^1.1.34",
102
102
  "typescript": "^5.7.0",
103
103
  "vitest": "^3.0.0"
104
104
  },
@@ -33,4 +33,5 @@ $ARGUMENTS
33
33
  **Rules:**
34
34
  - Never modify files in `raw/` — source packets are immutable after capture.
35
35
  - Never fabricate information — always cite sources with `[[sources/SRC-...]]`.
36
+ - Inside Markdown table cells, write aliased wikilinks as `[[target\|alias]]`, never `[[target|alias]]`.
36
37
  - The extension auto-updates metadata — you do NOT need to manually edit `meta/` files.
@@ -53,3 +53,4 @@ Read the LLM Wiki skill at `.pi/skills/llm-wiki/SKILL.md` first to understand th
53
53
  - Use status values: `draft` → `clarified` → `active` → `implemented` → `deferred` → `rejected`
54
54
  - Use priority values: `p0` (blocking), `p1` (critical), `p2` (important), `p3` (nice-to-have)
55
55
  - Do not create requirements in `raw/` — that layer is for external source artifacts only
56
+ - Inside Markdown table cells, write aliased wikilinks as `[[target\|alias]]`, never `[[target|alias]]`.
@@ -32,3 +32,4 @@ Read the LLM Wiki skill at `.pi/skills/llm-wiki/SKILL.md` first to understand th
32
32
  - One atomic insight per `wiki_retro` call. Use multiple calls for multiple insights.
33
33
  - Don't save obvious things. Save non-obvious patterns, tradeoffs, and design decisions.
34
34
  - Always add `[[wikilinks]]` to connect the new insight with existing wiki knowledge.
35
+ - Inside Markdown table cells, write aliased wikilinks as `[[target\|alias]]`, never `[[target|alias]]`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: llm-wiki
3
- description: Build and maintain a persistent, interlinked Obsidian-compatible markdown wiki using Karpathy's LLM Wiki pattern. Extension-backed with auto-generated metadata, guardrails, and 13 custom tools (+3 opt-in agent-trajectory tools).
3
+ description: Build and maintain a persistent, interlinked Obsidian-compatible markdown wiki using Karpathy's LLM Wiki pattern. Extension-backed with auto-generated metadata, guardrails, and 14 custom tools (+3 opt-in agent-trajectory tools).
4
4
  whenToUse: Call wiki_recall at task start to find relevant wiki pages. Call wiki_retro at task end to save new insights. When agent-trajectory working-memory is enabled (opt-in, /wiki-trajectories on), also call wiki_recall_skill at task start to find reusable skills / past cases ("have I done this before?") and wiki_capture_trajectory after non-trivial tasks to record how you solved them. The extension injects a brief status line, but explicit calls with task-specific terms get better results.
5
5
  ---
6
6
 
@@ -178,6 +178,16 @@ The choice is persisted to project settings (`.pi/settings.json` under `llm-wiki
178
178
  wiki_ingest(model="anthropic/claude-haiku")
179
179
  ```
180
180
 
181
+ ### Settings Screen (`/wiki-settings`)
182
+
183
+ **Interactive settings:** run `/wiki-settings` to open a persistent settings screen. It lists every
184
+ `llm-wiki` setting with its current value and where it is set (project overrides global; the
185
+ default is shown when unset). Booleans cycle in place with Enter/Space; numbers, strings, and the
186
+ model edit inline in a prefilled input. Every change persists immediately to the chosen scope
187
+ (project or global). Setting the model to `session` clears `llm-wiki.taskModel` (back to the session model).
188
+
189
+ ### Dashboard Screen (`/wiki-dashboard`) **Read-only vault health:** run `/wiki-dashboard` to open a persistent read-only screen: page counts by type + total size, last page touch + stale (30d+) count, activity by kind last 7 days (observes/retros/syntheses), pending raw-source ingest queue, zero-backlink page count (see `/wiki-lint` full scan), embedding coverage (emb/ files vs. pages). All values computed from existing on-disk state — no writes, no LLM calls. Esc closes.
190
+
181
191
  ### Auto-Bootstrap (One-Time)
182
192
 
183
193
  The extension creates the wiki vault automatically on startup. On the first turn, it injects a directive asking you to infer topic and mode, then call: