@zosmaai/pi-llm-wiki 0.11.7 → 0.11.8

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 (48) hide show
  1. package/README.md +7 -0
  2. package/dist/extensions/llm-wiki/lib/dashboard-command.js +7 -2
  3. package/dist/extensions/llm-wiki/lib/dashboard.js +1 -1
  4. package/dist/extensions/llm-wiki/lib/host.js +1 -1
  5. package/dist/extensions/llm-wiki/lib/ingest-worker.js +1 -1
  6. package/dist/extensions/llm-wiki/lib/knowledge-links.js +1 -1
  7. package/dist/extensions/llm-wiki/lib/legacy-repair.js +1 -1
  8. package/dist/extensions/llm-wiki/lib/metadata.js +1 -1
  9. package/dist/extensions/llm-wiki/lib/model-command.js +77 -4
  10. package/dist/extensions/llm-wiki/lib/recall.js +2 -2
  11. package/dist/extensions/llm-wiki/lib/runtime.js +1 -1
  12. package/dist/extensions/llm-wiki/lib/settings-command.js +6 -1
  13. package/dist/extensions/llm-wiki/lib/source-extractors.js +21 -5
  14. package/dist/extensions/llm-wiki/lib/tools.js +1 -1
  15. package/dist/extensions/llm-wiki/lib/trajectory.js +1 -1
  16. package/dist/extensions/llm-wiki/lib/utils.js +1 -1
  17. package/dist/extensions/llm-wiki/lib/vault-format.js +1 -1
  18. package/dist/extensions/llm-wiki/lib/visible-status.js +1 -1
  19. package/dist/mcp/operations.js +1 -1
  20. package/docs/superpowers/plans/2026-08-29-custom-wiki-page-types.md +378 -0
  21. package/docs/superpowers/plans/2026-08-29-keyless-provider-ingest-fix.md +256 -0
  22. package/extensions/llm-wiki/lib/bootstrap.ts +2 -2
  23. package/extensions/llm-wiki/lib/dashboard-command.ts +9 -3
  24. package/extensions/llm-wiki/lib/dashboard.ts +1 -1
  25. package/extensions/llm-wiki/lib/embeddings.ts +1 -1
  26. package/extensions/llm-wiki/lib/guardrails.ts +2 -2
  27. package/extensions/llm-wiki/lib/host.ts +1 -1
  28. package/extensions/llm-wiki/lib/ingest-worker.ts +6 -6
  29. package/extensions/llm-wiki/lib/knowledge-links.ts +1 -1
  30. package/extensions/llm-wiki/lib/legacy-repair.ts +4 -4
  31. package/extensions/llm-wiki/lib/metadata.ts +3 -3
  32. package/extensions/llm-wiki/lib/model-command.ts +116 -6
  33. package/extensions/llm-wiki/lib/observation.ts +2 -2
  34. package/extensions/llm-wiki/lib/recall.ts +4 -4
  35. package/extensions/llm-wiki/lib/retro.ts +1 -1
  36. package/extensions/llm-wiki/lib/runtime.ts +1 -1
  37. package/extensions/llm-wiki/lib/settings-command.ts +9 -2
  38. package/extensions/llm-wiki/lib/source-extractors.ts +20 -12
  39. package/extensions/llm-wiki/lib/source-packet.ts +2 -3
  40. package/extensions/llm-wiki/lib/task-config.ts +1 -1
  41. package/extensions/llm-wiki/lib/tools.ts +3 -3
  42. package/extensions/llm-wiki/lib/trajectory.ts +2 -2
  43. package/extensions/llm-wiki/lib/utils.ts +1 -1
  44. package/extensions/llm-wiki/lib/vault-format.ts +2 -2
  45. package/extensions/llm-wiki/lib/visible-status.ts +1 -1
  46. package/extensions/llm-wiki/lib/wiki-service.ts +1 -1
  47. package/mcp/operations.ts +3 -3
  48. package/package.json +7 -7
@@ -1,10 +1,10 @@
1
- import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
2
1
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
3
3
  import { scheduleReindex } from "./indexing.js";
4
4
  import { rebuildMetadataLight } from "./metadata.js";
5
5
  import type { Runtime } from "./runtime.js";
6
- import { isPathWithin, isProtectedPath, resolveVaultPaths } from "./utils.js";
7
6
  import type { VaultPaths } from "./utils.js";
7
+ import { isPathWithin, isProtectedPath, resolveVaultPaths } from "./utils.js";
8
8
  import { inspectVaultFormat, isGeneratedOkfPath } from "./vault-format.js";
9
9
 
10
10
  /**
@@ -129,7 +129,7 @@ export function resolveProjectSettingsPath(cwd: string, host: HostKind = detectH
129
129
  *
130
130
  * Always writes to `settings.json` inside the agent dir — both hosts read it.
131
131
  */
132
- export function resolveGlobalSettingsPath(host: HostKind = detectHost()): string {
132
+ export function resolveGlobalSettingsPath(_host: HostKind = detectHost()): string {
133
133
  let agentDir = "";
134
134
  try {
135
135
  agentDir = getAgentDir();
@@ -1,28 +1,28 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import type { AgentTool } from "@mariozechner/pi-agent-core";
4
4
  import type { Api, Model } from "@mariozechner/pi-ai";
5
- import { Type } from "typebox";
6
5
  import type { Static } from "typebox";
6
+ import { Type } from "typebox";
7
7
  import {
8
+ createKnowledgeDocument,
8
9
  type KnowledgeDiagnostic,
9
10
  type KnowledgeDocument,
10
- createKnowledgeDocument,
11
11
  patchKnowledgeDocument,
12
12
  readKnowledgeDocumentFile,
13
13
  serializeKnowledgeDocument,
14
14
  writeKnowledgeDocumentFile,
15
15
  } from "./knowledge-document.js";
16
16
  import {
17
- type WikilinkValidationMode,
18
17
  auditWikilinks,
19
18
  buildWikilinkIndex,
19
+ type WikilinkValidationMode,
20
20
  } from "./knowledge-links.js";
21
21
  import { appendEvent, rebuildMetadataLight } from "./metadata.js";
22
22
  import { runSubAgent } from "./subagent.js";
23
23
  import { resolveWikilinkValidation } from "./task-config.js";
24
- import { type VaultPaths, fmtDate, readJson, slugify } from "./utils.js";
25
- import { VaultWriteError, assertWritableVault } from "./vault-format.js";
24
+ import { fmtDate, readJson, slugify, type VaultPaths } from "./utils.js";
25
+ import { assertWritableVault, VaultWriteError } from "./vault-format.js";
26
26
 
27
27
  /**
28
28
  * Background ingest synthesis (issue #65, part of epic #63).
@@ -225,7 +225,7 @@ function resolveMarkdownTarget(
225
225
  // Convert decoded backslashes to /
226
226
  const normalized = decoded.map((s) => s.replace(/\\/g, "/")).join("/");
227
227
 
228
- const sourceDir = sourceId.replace(/[^\/]*$/, ""); // directory of source
228
+ const sourceDir = sourceId.replace(/[^/]*$/, ""); // directory of source
229
229
  const parts = normalized.split("/");
230
230
  const isRootRelative = normalized.startsWith("/");
231
231
 
@@ -1,8 +1,8 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import {
3
- constants,
4
3
  chmodSync,
5
4
  closeSync,
5
+ constants,
6
6
  existsSync,
7
7
  fstatSync,
8
8
  fsyncSync,
@@ -10,8 +10,8 @@ import {
10
10
  lstatSync,
11
11
  mkdirSync,
12
12
  openSync,
13
- readFileSync,
14
13
  readdirSync,
14
+ readFileSync,
15
15
  renameSync,
16
16
  unlinkSync,
17
17
  utimesSync,
@@ -19,14 +19,14 @@ import {
19
19
  } from "node:fs";
20
20
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
21
21
  import {
22
+ createKnowledgeDocument,
22
23
  type DiagnosticCode,
23
24
  type KnowledgeDiagnostic,
24
- createKnowledgeDocument,
25
25
  parseKnowledgeDocument,
26
26
  parseMarkdownFrontmatter,
27
27
  serializeKnowledgeDocument,
28
28
  } from "./knowledge-document.js";
29
- import { type VaultPaths, readJson } from "./utils.js";
29
+ import { readJson, type VaultPaths } from "./utils.js";
30
30
  import { discoverKnowledgeDocuments, inspectVaultFormat } from "./vault-format.js";
31
31
 
32
32
  const RECOVERABLE = new Set<DiagnosticCode>([
@@ -3,8 +3,8 @@ import {
3
3
  existsSync,
4
4
  lstatSync,
5
5
  mkdirSync,
6
- readFileSync,
7
6
  readdirSync,
7
+ readFileSync,
8
8
  renameSync,
9
9
  rmSync,
10
10
  writeFileSync,
@@ -12,11 +12,11 @@ import {
12
12
  import { dirname, join } from "node:path";
13
13
  import type { KnowledgeDiagnostic, KnowledgeDocument } from "./knowledge-document.js";
14
14
  import {
15
- type WikilinkIndex,
16
15
  buildResolvedBacklinks,
17
16
  buildWikilinkIndex,
17
+ type WikilinkIndex,
18
18
  } from "./knowledge-links.js";
19
- import { type VaultPaths, isPathWithin, readJson } from "./utils.js";
19
+ import { isPathWithin, readJson, type VaultPaths } from "./utils.js";
20
20
  import {
21
21
  assertWritableVault,
22
22
  compareCodePoint,
@@ -1,6 +1,15 @@
1
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
1
+ import type { ExtensionAPI, Theme } from "@mariozechner/pi-coding-agent";
2
+ import {
3
+ Container,
4
+ matchesKey,
5
+ type SelectItem,
6
+ SelectList,
7
+ type SelectListTheme,
8
+ Spacer,
9
+ Text,
10
+ } from "@mariozechner/pi-tui";
2
11
  import type { Runtime } from "./runtime.js";
3
- import { type TaskConfig, parseModelRef, persistTaskModel } from "./task-config.js";
12
+ import { parseModelRef, persistTaskModel, type TaskConfig } from "./task-config.js";
4
13
 
5
14
  /**
6
15
  * Model selection surface for the wiki background lane (issue #69, epic #63).
@@ -25,6 +34,12 @@ interface ModelLike {
25
34
  /** Words that clear the override and revert to the session model. */
26
35
  const CLEAR_WORDS = new Set(["session", "default", "reset", "clear", "none", "unset"]);
27
36
 
37
+ /** Sentinel value for the "use session model" picker row — not a valid provider/id. */
38
+ const SESSION_VALUE = "__session__";
39
+
40
+ /** Same window size as pi's built-in `/model` picker. */
41
+ const MAX_VISIBLE = 10;
42
+
28
43
  /** The status-bar key for the active-model label (so we can update it in place). */
29
44
  export const MODEL_STATUS_KEY = "llm-wiki-model";
30
45
 
@@ -43,6 +58,85 @@ function modelRef(m: ModelLike): string {
43
58
  return `${m.provider}/${m.id}`;
44
59
  }
45
60
 
61
+ function buildSelectTheme(theme: Theme): SelectListTheme {
62
+ return {
63
+ selectedPrefix: (text) => theme.fg("accent", text),
64
+ selectedText: (text) => theme.fg("accent", text),
65
+ description: (text) => theme.fg("dim", text),
66
+ scrollInfo: (text) => theme.fg("dim", text),
67
+ noMatch: (text) => theme.fg("muted", text),
68
+ };
69
+ }
70
+
71
+ function buildPickerItems(
72
+ pool: ModelLike[],
73
+ currentRef: string | undefined,
74
+ ): { items: SelectItem[]; selectedIndex: number } {
75
+ const items: SelectItem[] = [
76
+ {
77
+ value: SESSION_VALUE,
78
+ label: "↩ Use session model (clear override)",
79
+ description: currentRef ? undefined : "current",
80
+ },
81
+ ];
82
+ let selectedIndex = 0;
83
+ for (const m of pool) {
84
+ const ref = modelRef(m);
85
+ const isCurrent = currentRef === ref;
86
+ items.push({
87
+ value: ref,
88
+ label: ref,
89
+ description: isCurrent ? "current" : undefined,
90
+ });
91
+ if (isCurrent) selectedIndex = items.length - 1;
92
+ }
93
+ return { items, selectedIndex };
94
+ }
95
+
96
+ /** Editor-dock picker; `ui.custom` without overlay replaces the input slot like `/model`. */
97
+ class ModelPickerScreen extends Container {
98
+ private list: SelectList;
99
+ private doneFn: (result?: string) => void;
100
+ private closed = false;
101
+
102
+ constructor(
103
+ title: string,
104
+ items: SelectItem[],
105
+ selectedIndex: number,
106
+ theme: Theme,
107
+ done: (result?: string) => void,
108
+ ) {
109
+ super();
110
+ this.doneFn = done;
111
+ this.addChild(new Text(title, 0, 0));
112
+ this.addChild(new Spacer(1));
113
+ this.list = new SelectList(
114
+ items,
115
+ Math.min(MAX_VISIBLE, Math.max(items.length, 1)),
116
+ buildSelectTheme(theme),
117
+ );
118
+ this.list.setSelectedIndex(selectedIndex);
119
+ this.list.onSelect = (item) => this.finish(item.value);
120
+ this.list.onCancel = () => this.finish(undefined);
121
+ this.addChild(this.list);
122
+ }
123
+
124
+ handleInput(data: string): void {
125
+ // matchesKey covers kitty CSI-u Esc; SelectList cancel is the fallback.
126
+ if (matchesKey(data, "escape")) {
127
+ this.finish(undefined);
128
+ return;
129
+ }
130
+ this.list.handleInput(data);
131
+ }
132
+
133
+ private finish(result?: string): void {
134
+ if (this.closed) return;
135
+ this.closed = true;
136
+ this.doneFn(result);
137
+ }
138
+ }
139
+
46
140
  /**
47
141
  * Register the `/wiki-model` slash command. Lets the user view the active
48
142
  * background task model and choose another (or revert to the session model).
@@ -108,15 +202,31 @@ export function registerWikiModelCommand(pi: ExtensionAPI, runtime: Runtime): vo
108
202
  return;
109
203
  }
110
204
 
205
+ if (typeof ctx.ui.custom !== "function") {
206
+ ctx.ui.notify(
207
+ `LLM Wiki: this host does not support the model picker. Pass provider/id (e.g. anthropic/claude-haiku) or "session". Active: ${current}.`,
208
+ "warning",
209
+ );
210
+ return;
211
+ }
212
+
111
213
  const available = (ctx.modelRegistry.getAvailable() as ModelLike[]) ?? [];
112
214
  const pool = available.length > 0 ? available : (ctx.modelRegistry.getAll() as ModelLike[]);
113
- const sessionOption = "↩ Use session model (clear override)";
114
- const options = [sessionOption, ...pool.map(modelRef)];
215
+ const currentRef = runtime.config.taskModel ? modelRef(runtime.config.taskModel) : undefined;
216
+ const { items, selectedIndex } = buildPickerItems(pool, currentRef);
115
217
 
116
- const picked = await ctx.ui.select(`Wiki background model (current: ${current})`, options);
218
+ const picked = await ctx.ui.custom<string | undefined>((_tui, theme, _keybindings, done) => {
219
+ return new ModelPickerScreen(
220
+ `Wiki background model (current: ${current})`,
221
+ items,
222
+ selectedIndex,
223
+ theme,
224
+ done,
225
+ );
226
+ });
117
227
  if (picked === undefined) return; // cancelled
118
228
 
119
- if (picked === sessionOption) {
229
+ if (picked === SESSION_VALUE) {
120
230
  apply(undefined);
121
231
  return;
122
232
  }
@@ -1,11 +1,11 @@
1
1
  import { join } from "node:path";
2
- import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
2
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
3
3
  import { Type } from "typebox";
4
4
  import { scheduleReindex } from "./indexing.js";
5
5
  import { createKnowledgeDocument, writeKnowledgeDocumentFile } from "./knowledge-document.js";
6
6
  import { appendEvent, rebuildMetadataLight } from "./metadata.js";
7
7
  import type { Runtime } from "./runtime.js";
8
- import { type VaultPaths, fmtDate, resolveVaultPaths } from "./utils.js";
8
+ import { fmtDate, resolveVaultPaths, type VaultPaths } from "./utils.js";
9
9
  import { assertWritableVault, inspectWritableVault } from "./vault-format.js";
10
10
 
11
11
  // ─── Types ─────────────────────────────────────────────
@@ -3,9 +3,9 @@ import { join } from "node:path";
3
3
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
4
4
  import { Type } from "typebox";
5
5
  import {
6
+ cosineSimilarity,
6
7
  type Embedder,
7
8
  type EmbeddingStore,
8
- cosineSimilarity,
9
9
  normalizeVector,
10
10
  readEmbeddingStore,
11
11
  resolveEmbedder,
@@ -15,11 +15,11 @@ import type { Registry } from "./metadata.js";
15
15
  import type { Runtime } from "./runtime.js";
16
16
  import type { TaskConfig } from "./task-config.js";
17
17
  import {
18
- type VaultPaths,
19
18
  getPersonalWikiPaths,
20
19
  isPersonalVault,
21
20
  readJson,
22
21
  resolveVaultPaths,
22
+ type VaultPaths,
23
23
  } from "./utils.js";
24
24
  import { inspectVaultFormat } from "./vault-format.js";
25
25
 
@@ -111,7 +111,7 @@ function normalizeText(value: unknown): string {
111
111
  return flattenSearchValue(value)
112
112
  .toLowerCase()
113
113
  .normalize("NFKC")
114
- .replace(/[\-_./\\]+/g, " ")
114
+ .replace(/[-_./\\]+/g, " ")
115
115
  .replace(/[\p{P}\p{S}]+/gu, " ")
116
116
  .replace(/\s+/g, " ")
117
117
  .trim();
@@ -333,7 +333,7 @@ function chunkPreview(heading: string, content: string): string {
333
333
  function extractExpansionTerms(
334
334
  scored: Scored[],
335
335
  originalQuery: string,
336
- paths: VaultPaths,
336
+ _paths: VaultPaths,
337
337
  maxTerms = 6,
338
338
  ): string[] {
339
339
  const topResults = scored.slice(0, Math.min(3, scored.length));
@@ -7,7 +7,7 @@ import { applyWikilinkGate, buildWikilinkIndex } from "./knowledge-links.js";
7
7
  import { appendEvent, rebuildMetadataLight } from "./metadata.js";
8
8
  import type { Runtime } from "./runtime.js";
9
9
  import { loadTaskConfig, resolveWikilinkValidation } from "./task-config.js";
10
- import { type VaultPaths, fmtDate, readJson, resolveVaultPaths } from "./utils.js";
10
+ import { fmtDate, readJson, resolveVaultPaths, type VaultPaths } from "./utils.js";
11
11
  import { assertWritableVault, inspectWritableVault } from "./vault-format.js";
12
12
 
13
13
  // ─── Public API ────────────────────────────────────────
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
- import { TASK_DEFAULTS, type TaskConfig, loadTaskConfig, noticesEnabled } from "./task-config.js";
2
+ import { loadTaskConfig, noticesEnabled, TASK_DEFAULTS, type TaskConfig } from "./task-config.js";
3
3
 
4
4
  /**
5
5
  * Background-task runtime for the LLM Wiki (issue #64, part of #63).
@@ -11,11 +11,11 @@ import {
11
11
  } from "@mariozechner/pi-tui";
12
12
  import type { Runtime } from "./runtime.js";
13
13
  import {
14
- type SettingScope,
15
14
  loadTaskConfig,
16
15
  loadTaskConfigSources,
17
16
  parseModelRef,
18
17
  persistSetting,
18
+ type SettingScope,
19
19
  trajectoriesEnabled,
20
20
  } from "./task-config.js";
21
21
 
@@ -49,6 +49,7 @@ interface Ui {
49
49
  keybindings: unknown,
50
50
  done: (result?: unknown) => void,
51
51
  ) => unknown,
52
+ options?: { overlay?: boolean; overlayOptions?: Record<string, unknown> },
52
53
  ): Promise<unknown>;
53
54
  }
54
55
 
@@ -363,6 +364,12 @@ function scopeTitle(scope: SettingScope): string {
363
364
  return `\u{1F9E0} LLM Wiki Settings \u2014 ${scope === "global" ? "Global" : "Project"} (Esc to close)`;
364
365
  }
365
366
 
367
+ // ponytail: overlay options matching pi-blackhole's config modal style
368
+ const OVERLAY_OPTS = {
369
+ overlay: true,
370
+ overlayOptions: { anchor: "center", width: "92%", maxHeight: "95%" },
371
+ };
372
+
366
373
  async function showSettingsTui(ui: Ui, cwd: string, scope: SettingScope): Promise<void> {
367
374
  if (typeof ui.custom !== "function") {
368
375
  ui.notify(
@@ -445,7 +452,7 @@ async function showSettingsTui(ui: Ui, cwd: string, scope: SettingScope): Promis
445
452
  );
446
453
  screenRef = new SettingsScreen(scopeTitle(writeScope), list);
447
454
  return screenRef;
448
- });
455
+ }, OVERLAY_OPTS);
449
456
  }
450
457
 
451
458
  /**
@@ -1,5 +1,4 @@
1
1
  import { open } from "node:fs/promises";
2
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
3
2
  import { NodeHtmlMarkdown } from "node-html-markdown";
4
3
  import { type ExecApi, exec } from "./utils.js";
5
4
 
@@ -270,8 +269,8 @@ async function extractWithMarkItDown(
270
269
  if (!(await hasMarkItDown(pi, signal))) return "";
271
270
  const mdResult = await exec(
272
271
  pi,
273
- "sh",
274
- ["-c", `uvx --from 'markitdown[docx,pdf]' markitdown "${source}" 2>/dev/null || echo ""`],
272
+ "uvx",
273
+ ["--from", "markitdown[docx,pdf]", "markitdown", source],
275
274
  { signal, timeout: markitdownTimeoutMs() },
276
275
  );
277
276
  return mdResult.stdout.trim() ? mdResult.stdout : "";
@@ -281,13 +280,21 @@ async function extractWithMarkItDown(
281
280
  }
282
281
 
283
282
  async function hasMarkItDown(pi: ExecApi, signal?: AbortSignal): Promise<boolean> {
284
- const markitdown = await exec(
285
- pi,
286
- "sh",
287
- ["-c", `which uvx >/dev/null 2>&1 && echo "yes" || echo "no"`],
288
- { signal },
289
- );
290
- return markitdown.stdout.trim() === "yes";
283
+ // Probe without shell to avoid CWE-78; try which/where then uvx --version
284
+ try {
285
+ await exec(pi, "which", ["uvx"], { signal });
286
+ return true;
287
+ } catch {}
288
+ try {
289
+ await exec(pi, "where", ["uvx"], { signal });
290
+ return true;
291
+ } catch {}
292
+ try {
293
+ await exec(pi, "uvx", ["--version"], { signal });
294
+ return true;
295
+ } catch {
296
+ return false;
297
+ }
291
298
  }
292
299
 
293
300
  async function fetchTextUrl(pi: ExecApi, url: string, signal?: AbortSignal): Promise<string> {
@@ -350,7 +357,8 @@ function decodeHtmlEntities(text: string): string {
350
357
  };
351
358
  const lower = entity.toLowerCase();
352
359
  if (map[lower]) return map[lower];
353
- if (lower.startsWith("&#")) return String.fromCodePoint(Number.parseInt(entity.slice(2, -1)));
360
+ if (lower.startsWith("&#"))
361
+ return String.fromCodePoint(Number.parseInt(entity.slice(2, -1), 10));
354
362
  return entity;
355
363
  });
356
364
  }
@@ -369,7 +377,7 @@ function xmlToMarkdown(xml: string): string {
369
377
  let prev = "";
370
378
  while (prev !== text) {
371
379
  prev = text;
372
- text = text.replace(/<[a-zA-Z\/!?][^>]*>/g, "");
380
+ text = text.replace(/<[a-zA-Z/!?][^>]*>/g, "");
373
381
  }
374
382
  text = text.replace(/</g, "");
375
383
 
@@ -1,23 +1,22 @@
1
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
2
  import { copyFile } from "node:fs/promises";
3
3
  import { extname, join } from "node:path";
4
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
5
4
  import { createKnowledgeDocument, serializeKnowledgeDocument } from "./knowledge-document.js";
6
5
  import { appendEvent } from "./metadata.js";
7
6
  import {
8
- type ExtractedContent,
9
7
  binaryExtractionFailureMessage,
10
8
  detectBinaryMagicBytes,
9
+ type ExtractedContent,
11
10
  extractUrlContent,
12
11
  fileExtractorFor,
13
12
  } from "./source-extractors.js";
14
13
  import {
15
14
  type ExecApi,
16
- type VaultPaths,
17
15
  exec,
18
16
  fmtDate,
19
17
  nextSourceId,
20
18
  readText,
19
+ type VaultPaths,
21
20
  writeJson,
22
21
  } from "./utils.js";
23
22
  import { assertWritableVault } from "./vault-format.js";
@@ -2,8 +2,8 @@ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname } from "node:path";
3
3
  import { parse as parseYaml } from "yaml";
4
4
  import {
5
- type HostKind,
6
5
  detectHost,
6
+ type HostKind,
7
7
  listGlobalSettingsFiles,
8
8
  listProjectSettingsFiles,
9
9
  resolveGlobalSettingsPath,
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join, relative } from "node:path";
3
3
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
4
4
  import { Type } from "typebox";
@@ -17,18 +17,18 @@ import {
17
17
  buildWikilinkIndex,
18
18
  } from "./knowledge-links.js";
19
19
  import { repairLegacyKnowledgeDocuments } from "./legacy-repair.js";
20
- import { type Registry, appendEvent, rebuildMetadata, rebuildMetadataLight } from "./metadata.js";
20
+ import { appendEvent, type Registry, rebuildMetadata, rebuildMetadataLight } from "./metadata.js";
21
21
  import type { Runtime } from "./runtime.js";
22
22
  import { captureFile, captureText, captureUrl } from "./source-packet.js";
23
23
  import { loadTaskConfig, parseModelRef, resolveWikilinkValidation } from "./task-config.js";
24
24
  import {
25
- type VaultPaths,
26
25
  detectVaultFormat,
27
26
  fmtDate,
28
27
  getVaultPaths,
29
28
  readJson,
30
29
  resolveVaultPaths,
31
30
  slugify,
31
+ type VaultPaths,
32
32
  writeJson,
33
33
  } from "./utils.js";
34
34
  import {
@@ -1,15 +1,15 @@
1
- import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
4
4
  import { Type } from "typebox";
5
5
  import { appendEvent, rebuildMetadataLight } from "./metadata.js";
6
6
  import { searchWikiLayered } from "./recall.js";
7
7
  import {
8
- type VaultPaths,
9
8
  fmtDate,
10
9
  nextTrajectoryId,
11
10
  readJson,
12
11
  resolveVaultPaths,
12
+ type VaultPaths,
13
13
  writeJson,
14
14
  } from "./utils.js";
15
15
  import { assertWritableVault, inspectWritableVault } from "./vault-format.js";
@@ -2,8 +2,8 @@ import {
2
2
  existsSync,
3
3
  lstatSync,
4
4
  mkdirSync,
5
- readFileSync,
6
5
  readdirSync,
6
+ readFileSync,
7
7
  readlinkSync,
8
8
  realpathSync,
9
9
  renameSync,
@@ -1,4 +1,4 @@
1
- import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
1
+ import { existsSync, lstatSync, readdirSync, readFileSync } from "node:fs";
2
2
  import { isAbsolute, join, relative, sep } from "node:path";
3
3
  import {
4
4
  type KnowledgeDiagnostic,
@@ -6,7 +6,7 @@ import {
6
6
  parseKnowledgeDocument,
7
7
  parseMarkdownFrontmatter,
8
8
  } from "./knowledge-document.js";
9
- import { type VaultPaths, relativePhysicalPath } from "./utils.js";
9
+ import { relativePhysicalPath, type VaultPaths } from "./utils.js";
10
10
 
11
11
  export type KnowledgeFormat = "legacy" | "okf-0.2";
12
12
 
@@ -1,4 +1,4 @@
1
- import { MODEL_STATUS_KEY, formatActiveModelLabel } from "./model-command.js";
1
+ import { formatActiveModelLabel, MODEL_STATUS_KEY } from "./model-command.js";
2
2
  import type { Runtime } from "./runtime.js";
3
3
  import { noticesEnabled } from "./task-config.js";
4
4
 
@@ -2,8 +2,8 @@ import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import type { KnowledgeDiagnostic } from "./knowledge-document.js";
4
4
  import type { Registry } from "./metadata.js";
5
- import { readJson } from "./utils.js";
6
5
  import type { VaultPaths } from "./utils.js";
6
+ import { readJson } from "./utils.js";
7
7
  import type { KnowledgeFormat } from "./vault-format.js";
8
8
  import {
9
9
  compareCodePoint,
package/mcp/operations.ts CHANGED
@@ -10,20 +10,20 @@ import { join } from "node:path";
10
10
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
11
11
  import { bootstrapVault } from "../extensions/llm-wiki/lib/bootstrap.js";
12
12
  import {
13
- type WikilinkValidationMode,
14
13
  applyWikilinkGate,
15
14
  buildWikilinkIndex,
15
+ type WikilinkValidationMode,
16
16
  } from "../extensions/llm-wiki/lib/knowledge-links.js";
17
17
  import { type ProjectionResult, rebuildMetadata } from "../extensions/llm-wiki/lib/metadata.js";
18
18
  import { type RecallResult, searchWikiLayered } from "../extensions/llm-wiki/lib/recall.js";
19
19
  import { saveInsight } from "../extensions/llm-wiki/lib/retro.js";
20
20
  import { captureFile, captureText, captureUrl } from "../extensions/llm-wiki/lib/source-packet.js";
21
21
  import { resolveWikilinkValidation } from "../extensions/llm-wiki/lib/task-config.js";
22
- import { type VaultPaths, readJson } from "../extensions/llm-wiki/lib/utils.js";
22
+ import { readJson, type VaultPaths } from "../extensions/llm-wiki/lib/utils.js";
23
23
  import {
24
- VaultWriteError,
25
24
  inspectVaultFormat,
26
25
  inspectWritableVault,
26
+ VaultWriteError,
27
27
  } from "../extensions/llm-wiki/lib/vault-format.js";
28
28
  import { getWikiStatus, searchRegistry } from "../extensions/llm-wiki/lib/wiki-service.js";
29
29
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zosmaai/pi-llm-wiki",
3
- "version": "0.11.7",
3
+ "version": "0.11.8",
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",
@@ -82,7 +82,7 @@
82
82
  },
83
83
  "dependencies": {
84
84
  "@cfworker/json-schema": "^4.1.1",
85
- "@mariozechner/pi-tui": "0.70.6",
85
+ "@mariozechner/pi-tui": "0.73.1",
86
86
  "@modelcontextprotocol/server": "^2.0.0",
87
87
  "mdast-util-from-markdown": "^2.0.3",
88
88
  "node-html-markdown": "^2.0.0",
@@ -92,16 +92,16 @@
92
92
  },
93
93
  "devDependencies": {
94
94
  "@astrojs/starlight": "^0.41.10",
95
- "@biomejs/biome": "^1.9.4",
96
- "@mariozechner/pi-agent-core": "^0.70.2",
97
- "@mariozechner/pi-ai": "^0.70.2",
95
+ "@biomejs/biome": "^2.5.11",
96
+ "@mariozechner/pi-agent-core": "^0.73.1",
97
+ "@mariozechner/pi-ai": "^0.73.1",
98
98
  "@mariozechner/pi-coding-agent": "^0.70.2",
99
99
  "@mermaid-js/mermaid-cli": "^11.12.0",
100
100
  "@types/mdast": "^4.0.4",
101
- "@types/node": "^22",
101
+ "@types/node": "^26",
102
102
  "@vitest/coverage-v8": "^3.2.4",
103
103
  "astro": "^7.2.9",
104
- "typescript": "^5.7.0",
104
+ "typescript": "^7.0.2",
105
105
  "vitest": "^3.0.0"
106
106
  },
107
107
  "scripts": {