@zosmaai/pi-llm-wiki 0.11.7 → 0.12.0

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 (55) 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/guardrails.js +1 -1
  5. package/dist/extensions/llm-wiki/lib/host.js +2 -2
  6. package/dist/extensions/llm-wiki/lib/ingest-worker.js +1 -1
  7. package/dist/extensions/llm-wiki/lib/knowledge-links.js +1 -1
  8. package/dist/extensions/llm-wiki/lib/legacy-repair.js +1 -1
  9. package/dist/extensions/llm-wiki/lib/metadata.js +1 -1
  10. package/dist/extensions/llm-wiki/lib/model-command.js +77 -4
  11. package/dist/extensions/llm-wiki/lib/recall.js +2 -2
  12. package/dist/extensions/llm-wiki/lib/runtime.js +1 -1
  13. package/dist/extensions/llm-wiki/lib/settings-command.js +7 -2
  14. package/dist/extensions/llm-wiki/lib/source-extractors.js +21 -5
  15. package/dist/extensions/llm-wiki/lib/source-packet.js +10 -2
  16. package/dist/extensions/llm-wiki/lib/subagent.js +1 -1
  17. package/dist/extensions/llm-wiki/lib/tools.js +4 -1
  18. package/dist/extensions/llm-wiki/lib/trajectory.js +1 -1
  19. package/dist/extensions/llm-wiki/lib/utils.js +1 -1
  20. package/dist/extensions/llm-wiki/lib/vault-format.js +1 -1
  21. package/dist/extensions/llm-wiki/lib/visible-status.js +1 -1
  22. package/dist/mcp/operations.js +1 -1
  23. package/docs/superpowers/plans/2026-08-29-custom-wiki-page-types.md +378 -0
  24. package/docs/superpowers/plans/2026-08-29-keyless-provider-ingest-fix.md +256 -0
  25. package/docs/superpowers/plans/2026-09-02-wiki-lint-fresh-vault-and-windows-capture-fixes.md +284 -0
  26. package/extensions/llm-wiki/index.ts +1 -1
  27. package/extensions/llm-wiki/lib/bootstrap.ts +2 -2
  28. package/extensions/llm-wiki/lib/dashboard-command.ts +10 -4
  29. package/extensions/llm-wiki/lib/dashboard.ts +1 -1
  30. package/extensions/llm-wiki/lib/embeddings.ts +1 -1
  31. package/extensions/llm-wiki/lib/guardrails.ts +3 -3
  32. package/extensions/llm-wiki/lib/host.ts +4 -4
  33. package/extensions/llm-wiki/lib/ingest-worker.ts +8 -8
  34. package/extensions/llm-wiki/lib/knowledge-links.ts +1 -1
  35. package/extensions/llm-wiki/lib/legacy-repair.ts +4 -4
  36. package/extensions/llm-wiki/lib/metadata.ts +3 -3
  37. package/extensions/llm-wiki/lib/model-command.ts +116 -6
  38. package/extensions/llm-wiki/lib/observation.ts +2 -2
  39. package/extensions/llm-wiki/lib/recall.ts +5 -5
  40. package/extensions/llm-wiki/lib/retro.ts +2 -2
  41. package/extensions/llm-wiki/lib/runtime.ts +2 -2
  42. package/extensions/llm-wiki/lib/settings-command.ts +11 -4
  43. package/extensions/llm-wiki/lib/source-extractors.ts +20 -12
  44. package/extensions/llm-wiki/lib/source-packet.ts +13 -5
  45. package/extensions/llm-wiki/lib/subagent.ts +2 -2
  46. package/extensions/llm-wiki/lib/task-config.ts +1 -1
  47. package/extensions/llm-wiki/lib/tools.ts +7 -4
  48. package/extensions/llm-wiki/lib/trajectories-command.ts +1 -1
  49. package/extensions/llm-wiki/lib/trajectory.ts +3 -3
  50. package/extensions/llm-wiki/lib/utils.ts +2 -2
  51. package/extensions/llm-wiki/lib/vault-format.ts +2 -2
  52. package/extensions/llm-wiki/lib/visible-status.ts +1 -1
  53. package/extensions/llm-wiki/lib/wiki-service.ts +1 -1
  54. package/mcp/operations.ts +4 -4
  55. package/package.json +9 -9
package/README.md CHANGED
@@ -605,6 +605,13 @@ Thanks to everyone who has contributed! This list is regenerated automatically b
605
605
  <sub><b>Superdao</b></sub>
606
606
  </a>
607
607
  </td>
608
+ <td align="center">
609
+ <a href="https://github.com/wooksong">
610
+ <img src="https://avatars.githubusercontent.com/u/2772376?v=4" width="64;" alt="wooksong"/>
611
+ <br />
612
+ <sub><b>wooksong</b></sub>
613
+ </a>
614
+ </td>
608
615
  <td align="center">
609
616
  <a href="https://github.com/mystery4f">
610
617
  <img src="https://avatars.githubusercontent.com/u/40482524?v=4" width="64;" alt="mystery4f"/>
@@ -1,4 +1,4 @@
1
- import { Container, Text, matchesKey } from "@mariozechner/pi-tui";
1
+ import { Container, matchesKey, Text } from "@earendil-works/pi-tui";
2
2
  import { collectDashboardStats } from "./dashboard.js";
3
3
  const TYPE_ORDER = [
4
4
  "concept",
@@ -65,6 +65,11 @@ export class DashboardScreen extends Container {
65
65
  }
66
66
  }
67
67
  }
68
+ // ponytail: overlay options matching pi-blackhole's config modal style
69
+ const OVERLAY_OPTS = {
70
+ overlay: true,
71
+ overlayOptions: { anchor: "center", width: "92%", maxHeight: "95%" },
72
+ };
68
73
  /**
69
74
  * Register the /wiki-dashboard command.
70
75
  */
@@ -80,7 +85,7 @@ export function registerWikiDashboardCommand(pi, runtime) {
80
85
  const stats = await collectDashboardStats(ctx.cwd);
81
86
  await ctx.ui.custom((_tui, _theme, _keybindings, close) => {
82
87
  return new DashboardScreen(renderStatsLines(stats), close);
83
- });
88
+ }, OVERLAY_OPTS);
84
89
  },
85
90
  });
86
91
  }
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * No writes, no LLM calls, no new files. Pure reader.
13
13
  */
14
- import { readFile, readdir, stat } from "node:fs/promises";
14
+ import { readdir, readFile, stat } from "node:fs/promises";
15
15
  import { readJson, resolveVaultPaths } from "./utils.js";
16
16
  /** Directories excluded from the page walk (infrastructure, not pages). */
17
17
  const SKIP_DIRS = new Set([
@@ -1,4 +1,4 @@
1
- import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
1
+ import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
2
2
  import { scheduleReindex } from "./indexing.js";
3
3
  import { rebuildMetadataLight } from "./metadata.js";
4
4
  import { isPathWithin, isProtectedPath, resolveVaultPaths } from "./utils.js";
@@ -1,6 +1,6 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { getAgentDir } from "@mariozechner/pi-coding-agent";
3
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
4
4
  /** Project config directory name per host. */
5
5
  const CONFIG_DIR = { pi: ".pi", omp: ".omp" };
6
6
  /** Settings file names inside a config directory, lowest → highest precedence. */
@@ -100,7 +100,7 @@ export function resolveProjectSettingsPath(cwd, host = detectHost()) {
100
100
  *
101
101
  * Always writes to `settings.json` inside the agent dir — both hosts read it.
102
102
  */
103
- export function resolveGlobalSettingsPath(host = detectHost()) {
103
+ export function resolveGlobalSettingsPath(_host = detectHost()) {
104
104
  let agentDir = "";
105
105
  try {
106
106
  agentDir = getAgentDir();
@@ -7,7 +7,7 @@ import { appendEvent, rebuildMetadataLight } from "./metadata.js";
7
7
  import { runSubAgent } from "./subagent.js";
8
8
  import { resolveWikilinkValidation } from "./task-config.js";
9
9
  import { fmtDate, readJson, slugify } from "./utils.js";
10
- import { VaultWriteError, assertWritableVault } from "./vault-format.js";
10
+ import { assertWritableVault, VaultWriteError } from "./vault-format.js";
11
11
  /**
12
12
  * Background ingest synthesis (issue #65, part of epic #63).
13
13
  *
@@ -158,7 +158,7 @@ function resolveMarkdownTarget(target, sourceId) {
158
158
  }
159
159
  // Convert decoded backslashes to /
160
160
  const normalized = decoded.map((s) => s.replace(/\\/g, "/")).join("/");
161
- const sourceDir = sourceId.replace(/[^\/]*$/, ""); // directory of source
161
+ const sourceDir = sourceId.replace(/[^/]*$/, ""); // directory of source
162
162
  const parts = normalized.split("/");
163
163
  const isRootRelative = normalized.startsWith("/");
164
164
  if (isRootRelative) {
@@ -1,5 +1,5 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
- import { constants, chmodSync, closeSync, existsSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, unlinkSync, utimesSync, writeFileSync, } from "node:fs";
2
+ import { chmodSync, closeSync, constants, existsSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readdirSync, readFileSync, renameSync, unlinkSync, utimesSync, writeFileSync, } from "node:fs";
3
3
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
4
4
  import { createKnowledgeDocument, parseKnowledgeDocument, parseMarkdownFrontmatter, serializeKnowledgeDocument, } from "./knowledge-document.js";
5
5
  import { readJson } from "./utils.js";
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, } from "node:fs";
2
+ import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
  import { buildResolvedBacklinks, buildWikilinkIndex, } from "./knowledge-links.js";
5
5
  import { isPathWithin, readJson } from "./utils.js";
@@ -1,6 +1,11 @@
1
+ import { Container, matchesKey, SelectList, Spacer, Text, } from "@earendil-works/pi-tui";
1
2
  import { parseModelRef, persistTaskModel } from "./task-config.js";
2
3
  /** Words that clear the override and revert to the session model. */
3
4
  const CLEAR_WORDS = new Set(["session", "default", "reset", "clear", "none", "unset"]);
5
+ /** Sentinel value for the "use session model" picker row — not a valid provider/id. */
6
+ const SESSION_VALUE = "__session__";
7
+ /** Same window size as pi's built-in `/model` picker. */
8
+ const MAX_VISIBLE = 10;
4
9
  /** The status-bar key for the active-model label (so we can update it in place). */
5
10
  export const MODEL_STATUS_KEY = "llm-wiki-model";
6
11
  /**
@@ -17,6 +22,68 @@ export function formatActiveModelLabel(config, sessionModelId) {
17
22
  function modelRef(m) {
18
23
  return `${m.provider}/${m.id}`;
19
24
  }
25
+ function buildSelectTheme(theme) {
26
+ return {
27
+ selectedPrefix: (text) => theme.fg("accent", text),
28
+ selectedText: (text) => theme.fg("accent", text),
29
+ description: (text) => theme.fg("dim", text),
30
+ scrollInfo: (text) => theme.fg("dim", text),
31
+ noMatch: (text) => theme.fg("muted", text),
32
+ };
33
+ }
34
+ function buildPickerItems(pool, currentRef) {
35
+ const items = [
36
+ {
37
+ value: SESSION_VALUE,
38
+ label: "↩ Use session model (clear override)",
39
+ description: currentRef ? undefined : "current",
40
+ },
41
+ ];
42
+ let selectedIndex = 0;
43
+ for (const m of pool) {
44
+ const ref = modelRef(m);
45
+ const isCurrent = currentRef === ref;
46
+ items.push({
47
+ value: ref,
48
+ label: ref,
49
+ description: isCurrent ? "current" : undefined,
50
+ });
51
+ if (isCurrent)
52
+ selectedIndex = items.length - 1;
53
+ }
54
+ return { items, selectedIndex };
55
+ }
56
+ /** Editor-dock picker; `ui.custom` without overlay replaces the input slot like `/model`. */
57
+ class ModelPickerScreen extends Container {
58
+ list;
59
+ doneFn;
60
+ closed = false;
61
+ constructor(title, items, selectedIndex, theme, done) {
62
+ super();
63
+ this.doneFn = done;
64
+ this.addChild(new Text(title, 0, 0));
65
+ this.addChild(new Spacer(1));
66
+ this.list = new SelectList(items, Math.min(MAX_VISIBLE, Math.max(items.length, 1)), buildSelectTheme(theme));
67
+ this.list.setSelectedIndex(selectedIndex);
68
+ this.list.onSelect = (item) => this.finish(item.value);
69
+ this.list.onCancel = () => this.finish(undefined);
70
+ this.addChild(this.list);
71
+ }
72
+ handleInput(data) {
73
+ // matchesKey covers kitty CSI-u Esc; SelectList cancel is the fallback.
74
+ if (matchesKey(data, "escape")) {
75
+ this.finish(undefined);
76
+ return;
77
+ }
78
+ this.list.handleInput(data);
79
+ }
80
+ finish(result) {
81
+ if (this.closed)
82
+ return;
83
+ this.closed = true;
84
+ this.doneFn(result);
85
+ }
86
+ }
20
87
  /**
21
88
  * Register the `/wiki-model` slash command. Lets the user view the active
22
89
  * background task model and choose another (or revert to the session model).
@@ -66,14 +133,20 @@ export function registerWikiModelCommand(pi, runtime) {
66
133
  ctx.ui.notify(`LLM Wiki: active background model is ${current}. Pass provider/id to change it (no interactive UI here).`, "info");
67
134
  return;
68
135
  }
136
+ if (typeof ctx.ui.custom !== "function") {
137
+ ctx.ui.notify(`LLM Wiki: this host does not support the model picker. Pass provider/id (e.g. anthropic/claude-haiku) or "session". Active: ${current}.`, "warning");
138
+ return;
139
+ }
69
140
  const available = ctx.modelRegistry.getAvailable() ?? [];
70
141
  const pool = available.length > 0 ? available : ctx.modelRegistry.getAll();
71
- const sessionOption = "↩ Use session model (clear override)";
72
- const options = [sessionOption, ...pool.map(modelRef)];
73
- const picked = await ctx.ui.select(`Wiki background model (current: ${current})`, options);
142
+ const currentRef = runtime.config.taskModel ? modelRef(runtime.config.taskModel) : undefined;
143
+ const { items, selectedIndex } = buildPickerItems(pool, currentRef);
144
+ const picked = await ctx.ui.custom((_tui, theme, _keybindings, done) => {
145
+ return new ModelPickerScreen(`Wiki background model (current: ${current})`, items, selectedIndex, theme, done);
146
+ });
74
147
  if (picked === undefined)
75
148
  return; // cancelled
76
- if (picked === sessionOption) {
149
+ if (picked === SESSION_VALUE) {
77
150
  apply(undefined);
78
151
  return;
79
152
  }
@@ -42,7 +42,7 @@ function normalizeText(value) {
42
42
  return flattenSearchValue(value)
43
43
  .toLowerCase()
44
44
  .normalize("NFKC")
45
- .replace(/[\-_./\\]+/g, " ")
45
+ .replace(/[-_./\\]+/g, " ")
46
46
  .replace(/[\p{P}\p{S}]+/gu, " ")
47
47
  .replace(/\s+/g, " ")
48
48
  .trim();
@@ -236,7 +236,7 @@ function chunkPreview(heading, content) {
236
236
  * Pseudo-relevance feedback: terms from top-matching pages that aren't in
237
237
  * the original query become expansion candidates.
238
238
  */
239
- function extractExpansionTerms(scored, originalQuery, paths, maxTerms = 6) {
239
+ function extractExpansionTerms(scored, originalQuery, _paths, maxTerms = 6) {
240
240
  const topResults = scored.slice(0, Math.min(3, scored.length));
241
241
  if (topResults.length === 0)
242
242
  return [];
@@ -1,4 +1,4 @@
1
- import { TASK_DEFAULTS, loadTaskConfig, noticesEnabled } from "./task-config.js";
1
+ import { loadTaskConfig, noticesEnabled, TASK_DEFAULTS } from "./task-config.js";
2
2
  export class Runtime {
3
3
  config = { ...TASK_DEFAULTS };
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  import { homedir } from "node:os";
2
- import { Container, Input, SettingsList, Spacer, Text, } from "@mariozechner/pi-tui";
2
+ import { Container, Input, SettingsList, Spacer, Text, } from "@earendil-works/pi-tui";
3
3
  import { loadTaskConfig, loadTaskConfigSources, parseModelRef, persistSetting, trajectoriesEnabled, } from "./task-config.js";
4
4
  const SETTINGS = [
5
5
  {
@@ -280,6 +280,11 @@ class SettingsScreen extends Container {
280
280
  function scopeTitle(scope) {
281
281
  return `\u{1F9E0} LLM Wiki Settings \u2014 ${scope === "global" ? "Global" : "Project"} (Esc to close)`;
282
282
  }
283
+ // ponytail: overlay options matching pi-blackhole's config modal style
284
+ const OVERLAY_OPTS = {
285
+ overlay: true,
286
+ overlayOptions: { anchor: "center", width: "92%", maxHeight: "95%" },
287
+ };
283
288
  async function showSettingsTui(ui, cwd, scope) {
284
289
  if (typeof ui.custom !== "function") {
285
290
  ui.notify("LLM Wiki: this host does not support the settings screen. Edit settings.json directly.", "warning");
@@ -344,7 +349,7 @@ async function showSettingsTui(ui, cwd, scope) {
344
349
  }, () => close());
345
350
  screenRef = new SettingsScreen(scopeTitle(writeScope), list);
346
351
  return screenRef;
347
- });
352
+ }, OVERLAY_OPTS);
348
353
  }
349
354
  /**
350
355
  * Register the /wiki-settings command.
@@ -190,7 +190,7 @@ async function extractWithMarkItDown(pi, source, signal) {
190
190
  try {
191
191
  if (!(await hasMarkItDown(pi, signal)))
192
192
  return "";
193
- const mdResult = await exec(pi, "sh", ["-c", `uvx --from 'markitdown[docx,pdf]' markitdown "${source}" 2>/dev/null || echo ""`], { signal, timeout: markitdownTimeoutMs() });
193
+ const mdResult = await exec(pi, "uvx", ["--from", "markitdown[docx,pdf]", "markitdown", source], { signal, timeout: markitdownTimeoutMs() });
194
194
  return mdResult.stdout.trim() ? mdResult.stdout : "";
195
195
  }
196
196
  catch {
@@ -198,8 +198,24 @@ async function extractWithMarkItDown(pi, source, signal) {
198
198
  }
199
199
  }
200
200
  async function hasMarkItDown(pi, signal) {
201
- const markitdown = await exec(pi, "sh", ["-c", `which uvx >/dev/null 2>&1 && echo "yes" || echo "no"`], { signal });
202
- return markitdown.stdout.trim() === "yes";
201
+ // Probe without shell to avoid CWE-78; try which/where then uvx --version
202
+ try {
203
+ await exec(pi, "which", ["uvx"], { signal });
204
+ return true;
205
+ }
206
+ catch { }
207
+ try {
208
+ await exec(pi, "where", ["uvx"], { signal });
209
+ return true;
210
+ }
211
+ catch { }
212
+ try {
213
+ await exec(pi, "uvx", ["--version"], { signal });
214
+ return true;
215
+ }
216
+ catch {
217
+ return false;
218
+ }
203
219
  }
204
220
  async function fetchTextUrl(pi, url, signal) {
205
221
  try {
@@ -254,7 +270,7 @@ function decodeHtmlEntities(text) {
254
270
  if (map[lower])
255
271
  return map[lower];
256
272
  if (lower.startsWith("&#"))
257
- return String.fromCodePoint(Number.parseInt(entity.slice(2, -1)));
273
+ return String.fromCodePoint(Number.parseInt(entity.slice(2, -1), 10));
258
274
  return entity;
259
275
  });
260
276
  }
@@ -271,7 +287,7 @@ function xmlToMarkdown(xml) {
271
287
  let prev = "";
272
288
  while (prev !== text) {
273
289
  prev = text;
274
- text = text.replace(/<[a-zA-Z\/!?][^>]*>/g, "");
290
+ text = text.replace(/<[a-zA-Z/!?][^>]*>/g, "");
275
291
  }
276
292
  text = text.replace(/</g, "");
277
293
  text = decodeHtmlEntities(text);
@@ -1,6 +1,6 @@
1
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
2
  import { copyFile } from "node:fs/promises";
3
- import { extname, join } from "node:path";
3
+ import { basename, extname, join } from "node:path";
4
4
  import { createKnowledgeDocument, serializeKnowledgeDocument } from "./knowledge-document.js";
5
5
  import { appendEvent } from "./metadata.js";
6
6
  import { binaryExtractionFailureMessage, detectBinaryMagicBytes, extractUrlContent, fileExtractorFor, } from "./source-extractors.js";
@@ -47,7 +47,7 @@ function urlCaptureSource(pi, url, signal) {
47
47
  };
48
48
  }
49
49
  function fileCaptureSource(pi, filePath, signal) {
50
- const fileName = filePath.split("/").pop() || "unknown";
50
+ const fileName = originalFileNameForPath(filePath);
51
51
  const extractor = fileExtractorFor(filePath);
52
52
  const content = extractor.shouldReadText ? readText(filePath) : "";
53
53
  return {
@@ -168,6 +168,14 @@ function originalFileNameForUrl(url) {
168
168
  }
169
169
  return "source.html";
170
170
  }
171
+ /**
172
+ * Original-artifact name for a local file capture. Uses platform basename so
173
+ * Windows absolute paths (drive colon + backslashes) cannot leak into the
174
+ * packet/original file name (issue #204).
175
+ */
176
+ export function originalFileNameForPath(filePath) {
177
+ return basename(filePath) || "unknown";
178
+ }
171
179
  function contentExtractionFailureMessage(source) {
172
180
  return `_Content could not be extracted from ${source}_\n`;
173
181
  }
@@ -1,4 +1,4 @@
1
- import { agentLoop, } from "@mariozechner/pi-agent-core";
1
+ import { agentLoop, } from "@earendil-works/pi-agent-core";
2
2
  /**
3
3
  * Run a sub-agent loop to completion.
4
4
  *
@@ -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 } from "typebox";
4
4
  import { bootstrapVault } from "./bootstrap.js";
@@ -871,6 +871,9 @@ function runWikiLint(paths, autoFix) {
871
871
  // The gap snapshot is generated discovery metadata consumed by wiki_status:
872
872
  // persist it on every successful lint so status never reports a stale count.
873
873
  // Corrective actions below (report, event, meta rebuild) stay autoFix-only.
874
+ // mkdir mirrors the autoFix report write below: on a fresh checkout the
875
+ // gitignored .discoveries dir does not exist yet (issue #203).
876
+ mkdirSync(paths.discoveries, { recursive: true });
874
877
  writeJson(join(paths.discoveries, "gaps.json"), {
875
878
  gaps,
876
879
  generated: new Date().toISOString(),
@@ -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 } from "node:path";
3
3
  import { Type } from "typebox";
4
4
  import { appendEvent, rebuildMetadataLight } from "./metadata.js";
@@ -1,4 +1,4 @@
1
- import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmdirSync, writeFileSync, } from "node:fs";
1
+ import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, realpathSync, renameSync, rmdirSync, writeFileSync, } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { basename, dirname, isAbsolute, join, relative, sep } from "node:path";
4
4
  /**
@@ -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 { parseKnowledgeDocument, parseMarkdownFrontmatter, } from "./knowledge-document.js";
4
4
  import { relativePhysicalPath } from "./utils.js";
@@ -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 { noticesEnabled } from "./task-config.js";
3
3
  /**
4
4
  * Apply the two post-session-start visible status lines (issue #77,
@@ -14,7 +14,7 @@ import { saveInsight } from "../extensions/llm-wiki/lib/retro.js";
14
14
  import { captureFile, captureText, captureUrl } from "../extensions/llm-wiki/lib/source-packet.js";
15
15
  import { resolveWikilinkValidation } from "../extensions/llm-wiki/lib/task-config.js";
16
16
  import { readJson } from "../extensions/llm-wiki/lib/utils.js";
17
- import { VaultWriteError, inspectVaultFormat, inspectWritableVault, } from "../extensions/llm-wiki/lib/vault-format.js";
17
+ import { inspectVaultFormat, inspectWritableVault, VaultWriteError, } from "../extensions/llm-wiki/lib/vault-format.js";
18
18
  import { getWikiStatus, searchRegistry } from "../extensions/llm-wiki/lib/wiki-service.js";
19
19
  function projectionOutcome(projection) {
20
20
  return projection.ok