stellavault 0.7.4 → 0.8.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.
@@ -36,10 +36,38 @@ function mergeConfig(defaults, overrides) {
36
36
  folders: { ...defaults.folders, ...overrides.folders },
37
37
  embedding: { ...defaults.embedding, ...overrides.embedding },
38
38
  chunking: { ...defaults.chunking, ...overrides.chunking },
39
- search: { ...defaults.search, ...overrides.search },
39
+ search: {
40
+ ...defaults.search,
41
+ ...overrides.search,
42
+ // B3 §4 — deep-merge weights so a partial override keeps the other defaults.
43
+ weights: { ...defaults.search.weights, ...overrides.search?.weights }
44
+ },
40
45
  mcp: { ...defaults.mcp, ...overrides.mcp }
41
46
  };
42
47
  }
48
+ function resolveSearchWeights(config, env = process.env) {
49
+ const base = {
50
+ semantic: config.search.weights?.semantic ?? 1,
51
+ bm25: config.search.weights?.bm25 ?? 1,
52
+ entity: config.search.weights?.entity ?? 1.5,
53
+ recency: config.search.recencyWeight ?? 0.2
54
+ };
55
+ const parse = (raw, min, max) => {
56
+ const s = String(raw ?? "").trim();
57
+ if (s === "")
58
+ return void 0;
59
+ const n = Number(s);
60
+ if (!Number.isFinite(n) || n < min)
61
+ return void 0;
62
+ return Math.min(n, max);
63
+ };
64
+ return {
65
+ semantic: parse(env.STELLAVAULT_W_SEMANTIC, 0, Infinity) ?? base.semantic,
66
+ bm25: parse(env.STELLAVAULT_W_BM25, 0, Infinity) ?? base.bm25,
67
+ entity: parse(env.STELLAVAULT_W_ENTITY, 0, Infinity) ?? base.entity,
68
+ recency: parse(env.STELLAVAULT_RECENCY_WEIGHT, 0, 1) ?? base.recency
69
+ };
70
+ }
43
71
  var DEFAULT_FOLDERS, DEFAULT_CONFIG;
44
72
  var init_config = __esm({
45
73
  "packages/core/dist/config.js"() {
@@ -65,7 +93,11 @@ var init_config = __esm({
65
93
  },
66
94
  search: {
67
95
  defaultLimit: 10,
68
- rrfK: 60
96
+ rrfK: 60,
97
+ weights: { semantic: 1, bm25: 1, entity: 1.5 },
98
+ // B2.1: entity leads (per-doc cap prevents flooding)
99
+ recencyWeight: 0.2
100
+ // B3 §1.3 (±10% bound)
69
101
  },
70
102
  mcp: {
71
103
  mode: "stdio",
@@ -367,6 +399,165 @@ var init_chunker = __esm({
367
399
  }
368
400
  });
369
401
 
402
+ // packages/core/dist/indexer/entity-extractor.js
403
+ function normalize(s) {
404
+ return s.replace(/[^\p{L}\p{N}\s]/gu, " ").replace(/\s+/g, " ").trim().toLowerCase();
405
+ }
406
+ function isMeaningful(n) {
407
+ if (n.length < 2)
408
+ return false;
409
+ if (STOPWORDS.has(n))
410
+ return false;
411
+ return true;
412
+ }
413
+ function extractWikilinks(text) {
414
+ const out = [];
415
+ const re = /\[\[([^\]]+)\]\]/g;
416
+ let m;
417
+ while ((m = re.exec(text)) !== null) {
418
+ let target = m[1];
419
+ const pipe = target.indexOf("|");
420
+ if (pipe >= 0)
421
+ target = target.slice(0, pipe);
422
+ const hash = target.indexOf("#");
423
+ if (hash >= 0)
424
+ target = target.slice(0, hash);
425
+ target = target.trim();
426
+ if (target)
427
+ out.push(target);
428
+ }
429
+ return out;
430
+ }
431
+ function extractInlineTags(text) {
432
+ const out = [];
433
+ const re = /(?:^|\s)#([A-Za-z][\w/-]+)/g;
434
+ let m;
435
+ while ((m = re.exec(text)) !== null)
436
+ out.push(m[1].replace(/[/_-]/g, " "));
437
+ return out;
438
+ }
439
+ function extractNounPhrases(text) {
440
+ const out = [];
441
+ const cleaned = text.replace(/`[^`]*`/g, " ").replace(/https?:\/\/\S+/g, " ");
442
+ const tc = /\b([A-Z][a-z0-9]+(?:\s+[A-Z][a-z0-9]+){1,4})\b/g;
443
+ let m;
444
+ while ((m = tc.exec(cleaned)) !== null)
445
+ out.push(m[1]);
446
+ const ac = /\b([A-Z]{2,6})\b/g;
447
+ while ((m = ac.exec(cleaned)) !== null)
448
+ out.push(m[1]);
449
+ return out;
450
+ }
451
+ function extractEntities(input) {
452
+ const set = /* @__PURE__ */ new Set();
453
+ const add = (s) => {
454
+ const n = normalize(s);
455
+ if (isMeaningful(n))
456
+ set.add(n);
457
+ };
458
+ for (const w of extractWikilinks(input.content))
459
+ add(w);
460
+ for (const t2 of input.tags ?? [])
461
+ add(t2.replace(/^#/, "").replace(/[/_-]/g, " "));
462
+ for (const t2 of extractInlineTags(input.content))
463
+ add(t2);
464
+ if (input.heading)
465
+ add(input.heading);
466
+ if (input.title)
467
+ add(input.title);
468
+ for (const p of extractNounPhrases(input.content))
469
+ add(p);
470
+ if (input.heading)
471
+ for (const p of extractNounPhrases(input.heading))
472
+ add(p);
473
+ return [...set].slice(0, MAX_ENTITIES_PER_CHUNK);
474
+ }
475
+ function extractQueryTerms(query) {
476
+ const set = /* @__PURE__ */ new Set();
477
+ for (const w of extractWikilinks(query)) {
478
+ const n = normalize(w);
479
+ if (n)
480
+ set.add(n);
481
+ }
482
+ for (const a of query.match(/\b[A-Z]{2,6}\b/g) ?? [])
483
+ set.add(a.toLowerCase());
484
+ const words = query.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/).filter(Boolean);
485
+ for (let n = 1; n <= 4; n++) {
486
+ for (let i = 0; i + n <= words.length; i++) {
487
+ const gram = words.slice(i, i + n);
488
+ if (gram.every((w) => STOPWORDS.has(w)))
489
+ continue;
490
+ if (n > 1 && (STOPWORDS.has(gram[0]) || STOPWORDS.has(gram[gram.length - 1])))
491
+ continue;
492
+ const phrase = gram.join(" ");
493
+ if (phrase.length >= 2)
494
+ set.add(phrase);
495
+ }
496
+ }
497
+ return [...set].slice(0, MAX_QUERY_TERMS);
498
+ }
499
+ var MAX_ENTITIES_PER_CHUNK, MAX_QUERY_TERMS, STOPWORDS;
500
+ var init_entity_extractor = __esm({
501
+ "packages/core/dist/indexer/entity-extractor.js"() {
502
+ "use strict";
503
+ MAX_ENTITIES_PER_CHUNK = 30;
504
+ MAX_QUERY_TERMS = 64;
505
+ STOPWORDS = /* @__PURE__ */ new Set([
506
+ "the",
507
+ "and",
508
+ "for",
509
+ "with",
510
+ "this",
511
+ "that",
512
+ "from",
513
+ "into",
514
+ "your",
515
+ "you",
516
+ "are",
517
+ "was",
518
+ "not",
519
+ "but",
520
+ "all",
521
+ "can",
522
+ "has",
523
+ "have",
524
+ "will",
525
+ "what",
526
+ "when",
527
+ "which",
528
+ "their",
529
+ "them",
530
+ "these",
531
+ "those",
532
+ "then",
533
+ "than",
534
+ "about",
535
+ "over",
536
+ "more",
537
+ "most",
538
+ "some",
539
+ "such",
540
+ "also",
541
+ "how",
542
+ "why",
543
+ "does",
544
+ "did",
545
+ "who",
546
+ "where",
547
+ "a",
548
+ "an",
549
+ "of",
550
+ "to",
551
+ "in",
552
+ "on",
553
+ "is",
554
+ "it",
555
+ "or",
556
+ "as"
557
+ ]);
558
+ }
559
+ });
560
+
370
561
  // packages/core/dist/utils/retry.js
371
562
  async function withRetry(fn, options = {}) {
372
563
  const { maxRetries, baseDelayMs, maxDelayMs } = { ...DEFAULT_OPTIONS2, ...options };
@@ -513,13 +704,19 @@ function createWatcher(options) {
513
704
  let watcher = null;
514
705
  let debounceTimer = null;
515
706
  let reindexing = false;
707
+ let pendingReindex = false;
516
708
  async function triggerReindex() {
517
- if (reindexing)
709
+ if (reindexing) {
710
+ pendingReindex = true;
518
711
  return;
712
+ }
519
713
  reindexing = true;
520
714
  try {
521
- const result = await indexVault(vaultPath, { store, embedder, chunkOptions });
522
- onReindex?.({ indexed: result.indexed, skipped: result.skipped });
715
+ do {
716
+ pendingReindex = false;
717
+ const result = await indexVault(vaultPath, { store, embedder, chunkOptions });
718
+ onReindex?.({ indexed: result.indexed, skipped: result.skipped });
719
+ } while (pendingReindex);
523
720
  } finally {
524
721
  reindexing = false;
525
722
  }
@@ -595,7 +792,13 @@ async function indexVault(vaultPath, options) {
595
792
  const embeddings = await withRetry(() => embedder.embedBatch(texts), { maxRetries: 2, baseDelayMs: 1e3 });
596
793
  const chunksWithEmbeddings = chunks.map((c, j) => ({
597
794
  ...c,
598
- embedding: embeddings[j]
795
+ embedding: embeddings[j],
796
+ entities: extractEntities({
797
+ content: c.content,
798
+ heading: c.heading,
799
+ title: doc.title,
800
+ tags: doc.tags
801
+ })
599
802
  }));
600
803
  await store.upsertDocument(doc);
601
804
  await store.upsertChunks(chunksWithEmbeddings);
@@ -631,6 +834,7 @@ var init_indexer = __esm({
631
834
  "use strict";
632
835
  init_scanner();
633
836
  init_chunker();
837
+ init_entity_extractor();
634
838
  init_retry();
635
839
  init_local_embedder();
636
840
  init_scanner();
@@ -2411,25 +2615,25 @@ async function extractTimedTranscriptFromHtml(html) {
2411
2615
  return parseTranscriptXml(xml);
2412
2616
  }
2413
2617
  async function extractTimedTranscriptViaTool(videoId) {
2414
- const { execSync: execSync2 } = await import("node:child_process");
2415
- const { readdirSync: readdirSync9, readFileSync: readFileSync18, unlinkSync } = await import("node:fs");
2416
- const { join: join30 } = await import("node:path");
2618
+ const { execSync: execSync3 } = await import("node:child_process");
2619
+ const { readdirSync: readdirSync9, readFileSync: readFileSync19, unlinkSync } = await import("node:fs");
2620
+ const { join: join32 } = await import("node:path");
2417
2621
  const tmpDir = (await import("node:os")).tmpdir();
2418
- const tmpBase = join30(tmpDir, `sv-sub-${videoId}`);
2622
+ const tmpBase = join32(tmpDir, `sv-sub-${videoId}`);
2419
2623
  const url = `https://www.youtube.com/watch?v=${videoId}`;
2420
2624
  for (const lang of ["ko", "en"]) {
2421
2625
  try {
2422
- execSync2(`python -m yt_dlp --write-auto-sub --sub-lang ${lang} --skip-download --sub-format srv1 -o "${tmpBase}" "${url}"`, { timeout: 3e4, stdio: "pipe" });
2626
+ execSync3(`python -m yt_dlp --write-auto-sub --sub-lang ${lang} --skip-download --sub-format srv1 -o "${tmpBase}" "${url}"`, { timeout: 3e4, stdio: "pipe" });
2423
2627
  } catch {
2424
2628
  continue;
2425
2629
  }
2426
2630
  const files = readdirSync9(tmpDir).filter((f) => f.startsWith(`sv-sub-${videoId}`) && f.endsWith(".srv1"));
2427
2631
  if (files.length === 0)
2428
2632
  continue;
2429
- const xml = readFileSync18(join30(tmpDir, files[0]), "utf-8");
2633
+ const xml = readFileSync19(join32(tmpDir, files[0]), "utf-8");
2430
2634
  for (const f of files) {
2431
2635
  try {
2432
- unlinkSync(join30(tmpDir, f));
2636
+ unlinkSync(join32(tmpDir, f));
2433
2637
  } catch {
2434
2638
  }
2435
2639
  }
@@ -3402,6 +3606,195 @@ var init_file_extractors = __esm({
3402
3606
  }
3403
3607
  });
3404
3608
 
3609
+ // packages/cli/dist/mcp-clients.js
3610
+ import { execSync as execSync2 } from "node:child_process";
3611
+ import { existsSync as existsSync15, mkdirSync as mkdirSync16, readFileSync as readFileSync15, writeFileSync as writeFileSync16 } from "node:fs";
3612
+ import { dirname as dirname4, join as join21 } from "node:path";
3613
+ import { homedir as homedir13 } from "node:os";
3614
+ function appData() {
3615
+ return process.env.APPDATA ?? join21(homedir13(), "AppData", "Roaming");
3616
+ }
3617
+ function xdgConfig() {
3618
+ return process.env.XDG_CONFIG_HOME ?? join21(homedir13(), ".config");
3619
+ }
3620
+ function resolveServeCommand(override) {
3621
+ if (override?.command) {
3622
+ const args = override.args ? override.args.split(/\s+/).filter(Boolean) : ["serve"];
3623
+ return { command: override.command, args };
3624
+ }
3625
+ if (isWin)
3626
+ return { command: "cmd", args: ["/c", "stellavault", "serve"] };
3627
+ return { command: "stellavault", args: ["serve"] };
3628
+ }
3629
+ function claudeDesktopPath() {
3630
+ if (isWin)
3631
+ return join21(appData(), "Claude", "claude_desktop_config.json");
3632
+ if (isMac)
3633
+ return join21(homedir13(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
3634
+ return join21(xdgConfig(), "Claude", "claude_desktop_config.json");
3635
+ }
3636
+ function vscodePath() {
3637
+ if (isWin)
3638
+ return join21(appData(), "Code", "User", "mcp.json");
3639
+ if (isMac)
3640
+ return join21(homedir13(), "Library", "Application Support", "Code", "User", "mcp.json");
3641
+ return join21(xdgConfig(), "Code", "User", "mcp.json");
3642
+ }
3643
+ function isDetected(client) {
3644
+ return existsSync15(client.detectDir);
3645
+ }
3646
+ function writeClientConfig(client, serve) {
3647
+ const path = client.configPath;
3648
+ try {
3649
+ let json = {};
3650
+ if (existsSync15(path)) {
3651
+ const raw = readFileSync15(path, "utf-8").trim();
3652
+ if (raw)
3653
+ json = JSON.parse(raw);
3654
+ }
3655
+ const key = client.serversKey;
3656
+ if (typeof json[key] !== "object" || json[key] === null)
3657
+ json[key] = {};
3658
+ const already = Boolean(json[key].stellavault);
3659
+ json[key].stellavault = client.needsType ? { type: "stdio", command: serve.command, args: serve.args } : { command: serve.command, args: serve.args };
3660
+ mkdirSync16(dirname4(path), { recursive: true });
3661
+ writeFileSync16(path, JSON.stringify(json, null, 2) + "\n", "utf-8");
3662
+ return { client: client.label, status: already ? "updated" : "written", path };
3663
+ } catch (err) {
3664
+ return { client: client.label, status: "error", path, detail: err instanceof Error ? err.message : String(err) };
3665
+ }
3666
+ }
3667
+ function setupClaudeCode(serve) {
3668
+ const manual = `claude mcp add -s user stellavault -- ${serve.command} ${serve.args.join(" ")}`;
3669
+ try {
3670
+ execSync2("claude --version", { stdio: "ignore" });
3671
+ } catch {
3672
+ return { client: "Claude Code", status: "skipped", detail: `claude CLI not found \u2014 run manually:
3673
+ ${manual}` };
3674
+ }
3675
+ try {
3676
+ try {
3677
+ execSync2("claude mcp remove stellavault", { stdio: "ignore" });
3678
+ } catch {
3679
+ }
3680
+ execSync2(manual, { stdio: "ignore" });
3681
+ return { client: "Claude Code", status: "written", detail: "via claude mcp add (user scope)" };
3682
+ } catch (err) {
3683
+ return { client: "Claude Code", status: "error", detail: `${err instanceof Error ? err.message : String(err)} \u2014 try: ${manual}` };
3684
+ }
3685
+ }
3686
+ var isWin, isMac, FILE_CLIENTS, ALL_CLIENT_IDS;
3687
+ var init_mcp_clients = __esm({
3688
+ "packages/cli/dist/mcp-clients.js"() {
3689
+ "use strict";
3690
+ isWin = process.platform === "win32";
3691
+ isMac = process.platform === "darwin";
3692
+ FILE_CLIENTS = [
3693
+ {
3694
+ id: "claude-desktop",
3695
+ label: "Claude Desktop",
3696
+ configPath: claudeDesktopPath(),
3697
+ detectDir: dirname4(claudeDesktopPath()),
3698
+ serversKey: "mcpServers",
3699
+ needsType: false
3700
+ },
3701
+ {
3702
+ id: "cursor",
3703
+ label: "Cursor",
3704
+ configPath: join21(homedir13(), ".cursor", "mcp.json"),
3705
+ detectDir: join21(homedir13(), ".cursor"),
3706
+ serversKey: "mcpServers",
3707
+ needsType: false
3708
+ },
3709
+ {
3710
+ id: "windsurf",
3711
+ label: "Windsurf",
3712
+ configPath: join21(homedir13(), ".codeium", "windsurf", "mcp_config.json"),
3713
+ detectDir: join21(homedir13(), ".codeium"),
3714
+ serversKey: "mcpServers",
3715
+ needsType: false
3716
+ },
3717
+ {
3718
+ id: "vscode",
3719
+ label: "VS Code",
3720
+ configPath: vscodePath(),
3721
+ detectDir: dirname4(dirname4(vscodePath())),
3722
+ // .../Code
3723
+ serversKey: "servers",
3724
+ needsType: true
3725
+ }
3726
+ ];
3727
+ ALL_CLIENT_IDS = ["claude-code", ...FILE_CLIENTS.map((c) => c.id)];
3728
+ }
3729
+ });
3730
+
3731
+ // packages/cli/dist/commands/setup-cmd.js
3732
+ var setup_cmd_exports = {};
3733
+ __export(setup_cmd_exports, {
3734
+ setupCommand: () => setupCommand
3735
+ });
3736
+ import chalk4 from "chalk";
3737
+ import { existsSync as existsSync16 } from "node:fs";
3738
+ import { join as join22 } from "node:path";
3739
+ import { homedir as homedir14 } from "node:os";
3740
+ async function setupCommand(options) {
3741
+ console.log("");
3742
+ console.log(chalk4.bold(" \u2726 Stellavault \u2014 Connect to your AI clients"));
3743
+ console.log(chalk4.dim(" Registering Stellavault as an MCP server.\n"));
3744
+ if (!existsSync16(join22(homedir14(), ".stellavault.json"))) {
3745
+ console.log(chalk4.yellow(" \u26A0 No config found. Run ") + chalk4.cyan("stellavault init") + chalk4.yellow(" first to index your vault.\n"));
3746
+ }
3747
+ const serve = resolveServeCommand({ command: options.command, args: options.args });
3748
+ console.log(chalk4.dim(` Server command: ${serve.command} ${serve.args.join(" ")}
3749
+ `));
3750
+ const requested = options.client && options.client.length > 0 ? options.client.map((c) => c.toLowerCase()) : null;
3751
+ if (requested) {
3752
+ const unknown = requested.filter((id) => !ALL_CLIENT_IDS.includes(id));
3753
+ if (unknown.length > 0) {
3754
+ console.log(chalk4.red(` Unknown client(s): ${unknown.join(", ")}`));
3755
+ console.log(chalk4.dim(` Valid ids: ${ALL_CLIENT_IDS.join(", ")}
3756
+ `));
3757
+ return;
3758
+ }
3759
+ }
3760
+ const results = [];
3761
+ if (!requested || requested.includes("claude-code")) {
3762
+ results.push(setupClaudeCode(serve));
3763
+ }
3764
+ for (const client of FILE_CLIENTS) {
3765
+ if (requested && !requested.includes(client.id))
3766
+ continue;
3767
+ if (!requested && !options.all && !isDetected(client)) {
3768
+ results.push({ client: client.label, status: "skipped", detail: "not detected (use --all to force)" });
3769
+ continue;
3770
+ }
3771
+ results.push(writeClientConfig(client, serve));
3772
+ }
3773
+ console.log(chalk4.bold(" Results:"));
3774
+ for (const r of results) {
3775
+ const icon = r.status === "written" ? chalk4.green("\u2713 added ") : r.status === "updated" ? chalk4.green("\u2713 updated") : r.status === "skipped" ? chalk4.dim("\u2022 skipped") : chalk4.red("\u2717 error ");
3776
+ console.log(` ${icon} ${chalk4.white(r.client)}`);
3777
+ if (r.path)
3778
+ console.log(` ${chalk4.dim(r.path)}`);
3779
+ if (r.detail)
3780
+ console.log(` ${chalk4.dim(r.detail)}`);
3781
+ }
3782
+ const ok = results.filter((r) => r.status === "written" || r.status === "updated").length;
3783
+ console.log("");
3784
+ if (ok > 0) {
3785
+ console.log(chalk4.green(` \u2726 Connected ${ok} client${ok > 1 ? "s" : ""}.`) + chalk4.dim(" Restart the app(s) to load Stellavault."));
3786
+ } else {
3787
+ console.log(chalk4.yellow(" No clients configured.") + chalk4.dim(" Use --all to write configs even when a client is not detected."));
3788
+ }
3789
+ console.log("");
3790
+ }
3791
+ var init_setup_cmd = __esm({
3792
+ "packages/cli/dist/commands/setup-cmd.js"() {
3793
+ "use strict";
3794
+ init_mcp_clients();
3795
+ }
3796
+ });
3797
+
3405
3798
  // packages/cli/dist/index.js
3406
3799
  import { Command } from "commander";
3407
3800
 
@@ -3459,11 +3852,19 @@ function createSqliteVecStore(dbPath, dimensions = 384) {
3459
3852
  INSERT INTO chunk_embeddings (chunk_id, embedding)
3460
3853
  VALUES (?, ?)
3461
3854
  `);
3855
+ const insertEntity = db.prepare(`
3856
+ INSERT INTO chunk_entities (chunk_id, entity)
3857
+ VALUES (?, ?)
3858
+ `);
3462
3859
  for (const chunk of chunks) {
3463
3860
  insertChunk.run(chunk.id, chunk.documentId, chunk.content, chunk.heading, chunk.startLine, chunk.endLine, chunk.tokenCount);
3464
3861
  if (chunk.embedding) {
3465
3862
  insertEmbedding.run(chunk.id, float32Buffer(chunk.embedding));
3466
3863
  }
3864
+ if (chunk.entities) {
3865
+ for (const entity of chunk.entities)
3866
+ insertEntity.run(chunk.id, entity);
3867
+ }
3467
3868
  }
3468
3869
  });
3469
3870
  tx();
@@ -3504,6 +3905,41 @@ function createSqliteVecStore(dbPath, dimensions = 384) {
3504
3905
  // FTS5 rank is negative (lower = better)
3505
3906
  }));
3506
3907
  },
3908
+ async searchEntities(entities, limit) {
3909
+ if (!entities || entities.length === 0)
3910
+ return [];
3911
+ const exactPH = entities.map(() => "?").join(",");
3912
+ const fuzzy = entities.filter((t2) => t2.length >= 4 && (/\s/.test(t2) || /[^\x00-\x7f]/.test(t2) || t2.length >= 6)).slice(0, 16);
3913
+ let matched;
3914
+ let matchedParams;
3915
+ if (fuzzy.length === 0) {
3916
+ matched = `SELECT chunk_id, CAST(COUNT(*) AS REAL) AS score FROM chunk_entities WHERE entity IN (${exactPH}) GROUP BY chunk_id`;
3917
+ matchedParams = [...entities];
3918
+ } else {
3919
+ const esc = (t2) => t2.replace(/[\\%_]/g, "\\$&");
3920
+ const likeClause = fuzzy.map(() => `entity LIKE ? ESCAPE '\\'`).join(" OR ");
3921
+ matched = `
3922
+ SELECT chunk_id, SUM(w) AS score FROM (
3923
+ SELECT chunk_id, 1.0 AS w FROM chunk_entities WHERE entity IN (${exactPH})
3924
+ UNION ALL
3925
+ SELECT chunk_id, 0.4 AS w FROM chunk_entities
3926
+ WHERE (${likeClause}) AND entity NOT IN (${exactPH})
3927
+ ) GROUP BY chunk_id`;
3928
+ matchedParams = [...entities, ...fuzzy.map((t2) => `%${esc(t2)}%`), ...entities];
3929
+ }
3930
+ const rows = db.prepare(`
3931
+ SELECT chunk_id, score FROM (
3932
+ SELECT m.chunk_id AS chunk_id, m.score AS score,
3933
+ ROW_NUMBER() OVER (PARTITION BY c.document_id ORDER BY m.score DESC, m.chunk_id) AS rn
3934
+ FROM (${matched}) m
3935
+ JOIN chunks c ON c.id = m.chunk_id
3936
+ )
3937
+ WHERE rn <= 2
3938
+ ORDER BY score DESC, chunk_id
3939
+ LIMIT ?
3940
+ `).all(...matchedParams, limit);
3941
+ return rows.map((r) => ({ chunkId: r.chunk_id, score: r.score }));
3942
+ },
3507
3943
  async getDocument(documentId) {
3508
3944
  const row = db.prepare("SELECT * FROM documents WHERE id = ?").get(documentId);
3509
3945
  if (!row)
@@ -3643,6 +4079,13 @@ function createTables(db, dimensions = 384) {
3643
4079
 
3644
4080
  CREATE INDEX IF NOT EXISTS idx_chunks_document_id ON chunks(document_id);
3645
4081
  CREATE INDEX IF NOT EXISTS idx_documents_content_hash ON documents(content_hash);
4082
+
4083
+ CREATE TABLE IF NOT EXISTS chunk_entities (
4084
+ chunk_id TEXT NOT NULL REFERENCES chunks(id) ON DELETE CASCADE,
4085
+ entity TEXT NOT NULL
4086
+ );
4087
+ CREATE INDEX IF NOT EXISTS idx_chunk_entities_entity ON chunk_entities(entity);
4088
+ CREATE INDEX IF NOT EXISTS idx_chunk_entities_chunk ON chunk_entities(chunk_id);
3646
4089
  `);
3647
4090
  db.exec(`
3648
4091
  CREATE TRIGGER IF NOT EXISTS chunks_ai AFTER INSERT ON chunks BEGIN
@@ -3698,32 +4141,127 @@ async function searchSemantic(store, embedder, query, limit) {
3698
4141
  return store.searchSemantic(embedding, limit);
3699
4142
  }
3700
4143
 
4144
+ // packages/core/dist/search/entity.js
4145
+ init_entity_extractor();
4146
+ async function searchEntities(store, query, limit) {
4147
+ if (typeof store.searchEntities !== "function")
4148
+ return [];
4149
+ const terms = extractQueryTerms(query);
4150
+ if (terms.length === 0)
4151
+ return [];
4152
+ return store.searchEntities(terms, limit);
4153
+ }
4154
+
3701
4155
  // packages/core/dist/search/rrf.js
3702
- function rrfFusion(listA, listB, k = 60, limit = 10) {
4156
+ function rrfFusionN(lists, k = 60, limit = 10, opts = {}) {
4157
+ const { weights, recencyScores, recencyWeight = 0 } = opts;
3703
4158
  const scores = /* @__PURE__ */ new Map();
3704
- for (let i = 0; i < listA.length; i++) {
3705
- const id = listA[i].chunkId;
3706
- scores.set(id, (scores.get(id) ?? 0) + 1 / (k + i + 1));
4159
+ for (let li = 0; li < lists.length; li++) {
4160
+ const w = weights?.[li] ?? 1;
4161
+ const list = lists[li];
4162
+ for (let i = 0; i < list.length; i++) {
4163
+ const id = list[i].chunkId;
4164
+ scores.set(id, (scores.get(id) ?? 0) + w * (1 / (k + i + 1)));
4165
+ }
3707
4166
  }
3708
- for (let i = 0; i < listB.length; i++) {
3709
- const id = listB[i].chunkId;
3710
- scores.set(id, (scores.get(id) ?? 0) + 1 / (k + i + 1));
4167
+ if (recencyWeight > 0 && recencyScores) {
4168
+ for (const [id, s] of scores) {
4169
+ const r = recencyScores.get(id) ?? 0.5;
4170
+ scores.set(id, s * (1 + recencyWeight * (r - 0.5)));
4171
+ }
3711
4172
  }
3712
4173
  return [...scores.entries()].sort((a, b) => b[1] - a[1]).slice(0, limit).map(([chunkId, score]) => ({ chunkId, score }));
3713
4174
  }
3714
4175
 
4176
+ // packages/core/dist/search/adaptive.js
4177
+ function createAdaptiveSearch(deps) {
4178
+ const { baseSearch } = deps;
4179
+ const searchHistory = [];
4180
+ const recentTags = [];
4181
+ return {
4182
+ async search(options) {
4183
+ const { context, ...baseOptions } = options;
4184
+ const results = await baseSearch.search(baseOptions);
4185
+ if (!context && searchHistory.length === 0)
4186
+ return results;
4187
+ const ctx = {
4188
+ recentSearches: context?.recentSearches ?? searchHistory.slice(-5),
4189
+ recentDocTags: context?.recentDocTags ?? recentTags.slice(-10),
4190
+ currentFilePath: context?.currentFilePath
4191
+ };
4192
+ const reranked = results.map((r) => {
4193
+ let boost = 0;
4194
+ const docTags = ctx.recentDocTags ?? [];
4195
+ if (docTags.length > 0 && r.document.tags.length > 0) {
4196
+ const docTagSet = new Set(r.document.tags);
4197
+ const overlap = docTags.filter((t2) => docTagSet.has(t2)).length;
4198
+ boost += Math.min(overlap / Math.max(docTags.length, 1), 1) * 0.3;
4199
+ }
4200
+ if (ctx.currentFilePath && r.document.filePath) {
4201
+ const ctxParts = ctx.currentFilePath.split("/");
4202
+ const docParts = r.document.filePath.split("/");
4203
+ let common = 0;
4204
+ for (let i = 0; i < Math.min(ctxParts.length, docParts.length); i++) {
4205
+ if (ctxParts[i] === docParts[i])
4206
+ common++;
4207
+ else
4208
+ break;
4209
+ }
4210
+ if (common > 0) {
4211
+ boost += Math.min(common / Math.max(ctxParts.length - 1, 1), 1) * 0.2;
4212
+ }
4213
+ }
4214
+ return { ...r, score: r.score * (1 + boost) };
4215
+ });
4216
+ reranked.sort((a, b) => b.score - a.score);
4217
+ searchHistory.push(options.query);
4218
+ if (searchHistory.length > 20)
4219
+ searchHistory.shift();
4220
+ for (const r of reranked.slice(0, 3)) {
4221
+ for (const t2 of r.document.tags) {
4222
+ recentTags.push(t2);
4223
+ }
4224
+ }
4225
+ while (recentTags.length > 30)
4226
+ recentTags.shift();
4227
+ return reranked;
4228
+ }
4229
+ };
4230
+ }
4231
+
3715
4232
  // packages/core/dist/search/index.js
4233
+ var DEFAULT_SIGNAL_WEIGHTS = {
4234
+ semantic: 1,
4235
+ bm25: 1,
4236
+ entity: 1.5,
4237
+ // B2.1: leading curated-graph signal. Per-doc cap in searchEntities
4238
+ // prevents one large note flooding top-k. Tune via STELLAVAULT_W_ENTITY
4239
+ // (e.g. 2.0 for aggressive project-name surfacing, 0.5 for conservative).
4240
+ recency: 0.2
4241
+ // ±10% bound on relevance
4242
+ };
3716
4243
  function createSearchEngine(deps) {
3717
- const { store, embedder, rrfK = 60 } = deps;
4244
+ const { store, embedder, rrfK = 60, getDecayEngine } = deps;
4245
+ const baseWeights = { ...DEFAULT_SIGNAL_WEIGHTS, ...deps.weights };
3718
4246
  const FETCH_LIMIT = 30;
3719
4247
  return {
3720
4248
  async search(options) {
3721
- const { query, limit = 10, threshold = 0, tags } = options;
3722
- const [bm25Results, semanticResults] = await Promise.all([
4249
+ const { query, limit = 10, threshold = 0, tags, signalWeights } = options;
4250
+ const w = { ...baseWeights, ...signalWeights };
4251
+ const [bm25Results, semanticResults, entityResults] = await Promise.all([
3723
4252
  searchBm25(store, query, FETCH_LIMIT),
3724
- searchSemantic(store, embedder, query, FETCH_LIMIT)
4253
+ searchSemantic(store, embedder, query, FETCH_LIMIT),
4254
+ searchEntities(store, query, FETCH_LIMIT)
3725
4255
  ]);
3726
- const fused = rrfFusion(semanticResults, bm25Results, rrfK, limit * 2);
4256
+ const lists = [semanticResults, bm25Results, entityResults];
4257
+ const weights = [w.semantic, w.bm25, w.entity];
4258
+ const decay = getDecayEngine?.();
4259
+ const recencyScores = decay ? await buildRecencyMap(store, decay, lists) : void 0;
4260
+ const fused = rrfFusionN(lists, rrfK, limit * 2, {
4261
+ weights,
4262
+ recencyScores,
4263
+ recencyWeight: recencyScores ? w.recency : 0
4264
+ });
3727
4265
  const results = [];
3728
4266
  for (const scored of fused) {
3729
4267
  if (scored.score < threshold)
@@ -3752,6 +4290,33 @@ function createSearchEngine(deps) {
3752
4290
  }
3753
4291
  };
3754
4292
  }
4293
+ async function buildRecencyMap(store, decay, lists) {
4294
+ const chunkIds = /* @__PURE__ */ new Set();
4295
+ for (const list of lists)
4296
+ for (const c of list)
4297
+ chunkIds.add(c.chunkId);
4298
+ if (chunkIds.size === 0)
4299
+ return /* @__PURE__ */ new Map();
4300
+ const chunkDoc = [];
4301
+ const docIds = /* @__PURE__ */ new Set();
4302
+ for (const chunkId of chunkIds) {
4303
+ const chunk = await store.getChunk(chunkId);
4304
+ if (!chunk)
4305
+ continue;
4306
+ chunkDoc.push({ chunkId, documentId: chunk.documentId });
4307
+ docIds.add(chunk.documentId);
4308
+ }
4309
+ if (docIds.size === 0)
4310
+ return /* @__PURE__ */ new Map();
4311
+ const rByDoc = await decay.getRetrievabilityForDocs([...docIds]);
4312
+ const map = /* @__PURE__ */ new Map();
4313
+ for (const { chunkId, documentId } of chunkDoc) {
4314
+ const r = rByDoc.get(documentId);
4315
+ if (r !== void 0)
4316
+ map.set(chunkId, r);
4317
+ }
4318
+ return map;
4319
+ }
3755
4320
  function extractHighlights(content, query) {
3756
4321
  const words = query.toLowerCase().split(/\s+/).filter((w) => w.length > 1);
3757
4322
  const lines = content.split("\n");
@@ -3794,6 +4359,7 @@ async function handleSearch(searchEngine, args) {
3794
4359
  tags: args.tags
3795
4360
  });
3796
4361
  return results.map((r) => ({
4362
+ documentId: r.document.id,
3797
4363
  title: r.document.title,
3798
4364
  filePath: r.document.filePath,
3799
4365
  heading: r.chunk.heading,
@@ -4334,6 +4900,12 @@ var DecayEngine = class {
4334
4900
  retrievability REAL NOT NULL DEFAULT 1.0,
4335
4901
  updated_at TEXT NOT NULL
4336
4902
  );
4903
+ -- 2026-05-15: getDecaying() \uC758 'WHERE retrievability < ? ORDER BY
4904
+ -- retrievability ASC' \uAC00 full table scan \uC73C\uB85C 1215+ docs vault \uC5D0\uC11C
4905
+ -- \uBB34\uAC70\uC6C0. index \uCD94\uAC00\uB85C sort+filter \uB458 \uB2E4 \uAC00\uC18D.
4906
+ CREATE INDEX IF NOT EXISTS idx_decay_state_retrievability ON decay_state(retrievability);
4907
+ -- recompute \uC2DC stale row \uBE60\uB974\uAC8C \uCC3E\uAE30.
4908
+ CREATE INDEX IF NOT EXISTS idx_decay_state_updated_at ON decay_state(updated_at);
4337
4909
  `);
4338
4910
  }
4339
4911
  /**
@@ -4447,6 +5019,25 @@ var DecayEngine = class {
4447
5019
  title: r.title
4448
5020
  }));
4449
5021
  }
5022
+ /**
5023
+ * Design Ref: §B3.3.3 — read-only live retrievability for a set of documents.
5024
+ * Reuses persisted stability + last_access and recomputes R fresh, ignoring the
5025
+ * stale decay_state.retrievability snapshot column. No writes → no contention
5026
+ * with recordAccess. Single parametrized IN(...) query (bounded by ≤90 fused
5027
+ * candidates); documents without a decay_state row are simply absent from the map.
5028
+ */
5029
+ async getRetrievabilityForDocs(documentIds) {
5030
+ const out = /* @__PURE__ */ new Map();
5031
+ if (documentIds.length === 0)
5032
+ return out;
5033
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5034
+ const placeholders = documentIds.map(() => "?").join(",");
5035
+ const rows = this.db.prepare(`SELECT document_id, stability, last_access FROM decay_state WHERE document_id IN (${placeholders})`).all(...documentIds);
5036
+ for (const r of rows) {
5037
+ out.set(r.document_id, computeRetrievability(r.stability, elapsedDays(r.last_access, now)));
5038
+ }
5039
+ return out;
5040
+ }
4450
5041
  /**
4451
5042
  * Initialize decay state for documents that don't have one yet.
4452
5043
  */
@@ -4570,12 +5161,99 @@ function createLearningPathTool(store) {
4570
5161
  };
4571
5162
  }
4572
5163
 
4573
- // packages/core/dist/mcp/tools/detect-gaps.js
5164
+ // packages/core/dist/intelligence/gap-cache.js
4574
5165
  init_gap_detector();
4575
- function createDetectGapsTool(store) {
5166
+ var CACHE_VERSION = 1;
5167
+ var DEFAULT_MAX_AGE_MS = 6 * 60 * 60 * 1e3;
5168
+ var inflightByDb = /* @__PURE__ */ new WeakMap();
5169
+ var generationByDb = /* @__PURE__ */ new WeakMap();
5170
+ function bumpGeneration(db) {
5171
+ const cur = generationByDb.get(db) ?? 0;
5172
+ const next = cur + 1;
5173
+ generationByDb.set(db, next);
5174
+ return next;
5175
+ }
5176
+ function getGeneration(db) {
5177
+ return generationByDb.get(db) ?? 0;
5178
+ }
5179
+ function ensureGapCacheTable(db) {
5180
+ db.exec(`
5181
+ CREATE TABLE IF NOT EXISTS gap_cache (
5182
+ id INTEGER PRIMARY KEY,
5183
+ payload TEXT NOT NULL,
5184
+ version INTEGER NOT NULL DEFAULT 1,
5185
+ computed_at TEXT NOT NULL
5186
+ );
5187
+ `);
5188
+ }
5189
+ function readCachedGapReport(db, maxAgeMs = DEFAULT_MAX_AGE_MS) {
5190
+ try {
5191
+ ensureGapCacheTable(db);
5192
+ const row = db.prepare("SELECT * FROM gap_cache WHERE id = 1").get();
5193
+ if (!row)
5194
+ return null;
5195
+ if (row.version !== CACHE_VERSION)
5196
+ return null;
5197
+ const computedAt = new Date(row.computed_at).getTime();
5198
+ if (Number.isNaN(computedAt))
5199
+ return null;
5200
+ if (Date.now() - computedAt > maxAgeMs)
5201
+ return null;
5202
+ return JSON.parse(row.payload);
5203
+ } catch {
5204
+ return null;
5205
+ }
5206
+ }
5207
+ async function computeAndCacheGaps(store, db) {
5208
+ const currentGeneration = getGeneration(db);
5209
+ const existing = inflightByDb.get(db);
5210
+ if (existing && existing.generation === currentGeneration) {
5211
+ return existing.promise;
5212
+ }
5213
+ const startGeneration = currentGeneration;
5214
+ const promise = (async () => {
5215
+ try {
5216
+ ensureGapCacheTable(db);
5217
+ const report = await detectKnowledgeGaps(store);
5218
+ if (getGeneration(db) === startGeneration) {
5219
+ const payload = JSON.stringify(report);
5220
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5221
+ db.prepare(`INSERT INTO gap_cache (id, payload, version, computed_at) VALUES (1, ?, ?, ?)
5222
+ ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, version = excluded.version, computed_at = excluded.computed_at`).run(payload, CACHE_VERSION, now);
5223
+ }
5224
+ return report;
5225
+ } finally {
5226
+ const cur = inflightByDb.get(db);
5227
+ if (cur?.generation === startGeneration)
5228
+ inflightByDb.delete(db);
5229
+ }
5230
+ })();
5231
+ inflightByDb.set(db, { generation: startGeneration, promise });
5232
+ return promise;
5233
+ }
5234
+ function invalidateGapCache(db) {
5235
+ try {
5236
+ ensureGapCacheTable(db);
5237
+ db.prepare("DELETE FROM gap_cache WHERE id = 1").run();
5238
+ bumpGeneration(db);
5239
+ } catch {
5240
+ }
5241
+ }
5242
+ async function getGapReport(store, db, opts = {}) {
5243
+ if (!opts.forceRefresh) {
5244
+ const cached = readCachedGapReport(db, opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS);
5245
+ if (cached)
5246
+ return { report: cached, fromCache: true };
5247
+ }
5248
+ const fresh = await computeAndCacheGaps(store, db);
5249
+ return { report: fresh, fromCache: false };
5250
+ }
5251
+
5252
+ // packages/core/dist/mcp/tools/detect-gaps.js
5253
+ function createDetectGapsTool(store, getDb) {
4576
5254
  return {
4577
5255
  name: "detect-gaps",
4578
- description: "Detect knowledge gaps between topic clusters. Returns gap severity, isolated nodes, and suggested topics to bridge gaps.",
5256
+ description: "Detect knowledge gaps between topic clusters. Returns gap severity, isolated nodes, and suggested topics to bridge gaps. Result cached 6h (set forceRefresh=true to recompute).",
4579
5257
  inputSchema: {
4580
5258
  type: "object",
4581
5259
  properties: {
@@ -4583,12 +5261,17 @@ function createDetectGapsTool(store) {
4583
5261
  type: "string",
4584
5262
  description: "Minimum gap severity to include: high, medium, or low",
4585
5263
  enum: ["high", "medium", "low"]
5264
+ },
5265
+ forceRefresh: {
5266
+ type: "boolean",
5267
+ description: "Bypass cache and recompute (expensive: 30s+ on large vaults). Default false."
4586
5268
  }
4587
5269
  }
4588
5270
  },
4589
5271
  handler: async (args) => {
4590
5272
  const minSeverity = args.minSeverity ?? "medium";
4591
- const report = await detectKnowledgeGaps(store);
5273
+ const db = getDb();
5274
+ const { report, fromCache } = await getGapReport(store, db, { forceRefresh: args.forceRefresh });
4592
5275
  const sevOrder = { high: 0, medium: 1, low: 2 };
4593
5276
  const threshold = sevOrder[minSeverity] ?? 1;
4594
5277
  const filtered = report.gaps.filter((g) => sevOrder[g.severity] <= threshold);
@@ -4606,7 +5289,8 @@ function createDetectGapsTool(store) {
4606
5289
  suggestedTopic: g.suggestedTopic
4607
5290
  })),
4608
5291
  isolatedNodes: report.isolatedNodes.slice(0, 10),
4609
- suggestion: filtered.length > 0 ? `${filtered[0].suggestedTopic} \uC8FC\uC81C\uB85C \uB178\uD2B8\uB97C \uC791\uC131\uD558\uBA74 \uC9C0\uC2DD \uAC2D\uC744 \uC904\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4.` : "\uD604\uC7AC \uC2EC\uAC01\uD55C \uC9C0\uC2DD \uAC2D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."
5292
+ suggestion: filtered.length > 0 ? `${filtered[0].suggestedTopic} \uC8FC\uC81C\uB85C \uB178\uD2B8\uB97C \uC791\uC131\uD558\uBA74 \uC9C0\uC2DD \uAC2D\uC744 \uC904\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4.` : "\uD604\uC7AC \uC2EC\uAC01\uD55C \uC9C0\uC2DD \uAC2D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
5293
+ cacheStatus: fromCache ? "cached" : "fresh"
4610
5294
  }, null, 2)
4611
5295
  }]
4612
5296
  };
@@ -5036,11 +5720,11 @@ The note will appear in the graph after next index.`
5036
5720
  if (!source) {
5037
5721
  return { content: [{ type: "text", text: `Source note "${args.sourceTitle}" not found.` }] };
5038
5722
  }
5039
- const { readFileSync: readFileSync18 } = await import("node:fs");
5723
+ const { readFileSync: readFileSync19 } = await import("node:fs");
5040
5724
  const fullPath = join7(vaultPath, source.filePath);
5041
5725
  let existing = "";
5042
5726
  try {
5043
- existing = readFileSync18(fullPath, "utf-8");
5727
+ existing = readFileSync19(fullPath, "utf-8");
5044
5728
  } catch {
5045
5729
  }
5046
5730
  const linkSection = `
@@ -5065,14 +5749,15 @@ The note will appear in the graph after next index.`
5065
5749
  function createMcpServer(options) {
5066
5750
  const { store, searchEngine, embedder, vaultPath = "", decayEngine } = options;
5067
5751
  const ready = options.ready ?? Promise.resolve();
5752
+ const adaptiveSearch = createAdaptiveSearch({ baseSearch: searchEngine });
5068
5753
  const learningPathTool = createLearningPathTool(store);
5069
- const detectGapsTool = createDetectGapsTool(store);
5754
+ const detectGapsTool = createDetectGapsTool(store, () => store.getDb());
5070
5755
  const getEvolutionTool = createGetEvolutionTool(store);
5071
5756
  const linkCodeTool = createLinkCodeTool(searchEngine);
5072
5757
  const askTool = createAskTool(searchEngine, vaultPath);
5073
5758
  const generateDraftTool = createGenerateDraftTool(searchEngine, vaultPath);
5074
5759
  const agenticTools = embedder ? createAgenticGraphTools(store, embedder, vaultPath) : [];
5075
- const server = new Server({ name: "stellavault", version: "0.7.3" }, { capabilities: { tools: {} } });
5760
+ const server = new Server({ name: "stellavault", version: "0.8.2" }, { capabilities: { tools: {} } });
5076
5761
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
5077
5762
  tools: [
5078
5763
  searchToolDef,
@@ -5102,10 +5787,10 @@ function createMcpServer(options) {
5102
5787
  let result;
5103
5788
  switch (name) {
5104
5789
  case "search":
5105
- result = await handleSearch(searchEngine, args);
5106
- if (decayEngine && result && typeof result === "object" && "results" in result) {
5790
+ result = await handleSearch(adaptiveSearch, args);
5791
+ if (decayEngine && Array.isArray(result)) {
5107
5792
  const now = (/* @__PURE__ */ new Date()).toISOString();
5108
- for (const r of result.results ?? []) {
5793
+ for (const r of result) {
5109
5794
  if (r.documentId)
5110
5795
  decayEngine.recordAccess({ documentId: r.documentId, type: "mcp_query", timestamp: now }).catch(() => {
5111
5796
  });
@@ -5576,17 +6261,17 @@ function createKnowledgeRouter(opts) {
5576
6261
  res.status(404).json({ error: "Document not found" });
5577
6262
  return;
5578
6263
  }
5579
- const { readFileSync: readFileSync18, writeFileSync: writeFileSync22, unlinkSync } = await import("node:fs");
5580
- const { join: join30, resolve: resolve22 } = await import("node:path");
6264
+ const { readFileSync: readFileSync19, writeFileSync: writeFileSync23, unlinkSync } = await import("node:fs");
6265
+ const { join: join32, resolve: resolve22 } = await import("node:path");
5581
6266
  const [keeper, removed] = docA.content.length >= docB.content.length ? [docA, docB] : [docB, docA];
5582
- const keeperPath = resolve22(join30(vaultPath, keeper.filePath));
5583
- const removedPath = resolve22(join30(vaultPath, removed.filePath));
6267
+ const keeperPath = resolve22(join32(vaultPath, keeper.filePath));
6268
+ const removedPath = resolve22(join32(vaultPath, removed.filePath));
5584
6269
  const vaultRoot = resolve22(vaultPath);
5585
6270
  if (!keeperPath.startsWith(vaultRoot) || !removedPath.startsWith(vaultRoot)) {
5586
6271
  res.status(400).json({ error: "Invalid file path" });
5587
6272
  return;
5588
6273
  }
5589
- const keeperContent = readFileSync18(keeperPath, "utf-8");
6274
+ const keeperContent = readFileSync19(keeperPath, "utf-8");
5590
6275
  const appendix = `
5591
6276
 
5592
6277
  ---
@@ -5594,7 +6279,7 @@ function createKnowledgeRouter(opts) {
5594
6279
  > Merged from: ${removed.title} (${removed.filePath})
5595
6280
 
5596
6281
  ${removed.content}`;
5597
- writeFileSync22(keeperPath, keeperContent + appendix, "utf-8");
6282
+ writeFileSync23(keeperPath, keeperContent + appendix, "utf-8");
5598
6283
  try {
5599
6284
  unlinkSync(removedPath);
5600
6285
  } catch {
@@ -5617,8 +6302,8 @@ ${removed.content}`;
5617
6302
  res.status(400).json({ error: "clusterA, clusterB required" });
5618
6303
  return;
5619
6304
  }
5620
- const { writeFileSync: writeFileSync22, mkdirSync: mkdirSync22 } = await import("node:fs");
5621
- const { join: join30, resolve: resolve22 } = await import("node:path");
6305
+ const { writeFileSync: writeFileSync23, mkdirSync: mkdirSync23 } = await import("node:fs");
6306
+ const { join: join32, resolve: resolve22 } = await import("node:path");
5622
6307
  const nameA = clusterA.replace(/\s*\(\d+\)$/, "");
5623
6308
  const nameB = clusterB.replace(/\s*\(\d+\)$/, "");
5624
6309
  const resultsA = await searchEngine.search({ query: nameA, limit: 3 });
@@ -5658,14 +6343,14 @@ ${removed.content}`;
5658
6343
  ""
5659
6344
  ].join("\n");
5660
6345
  const safeTitle = title.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, " ");
5661
- const dir = resolve22(join30(vaultPath, "01_Knowledge"));
6346
+ const dir = resolve22(join32(vaultPath, "01_Knowledge"));
5662
6347
  if (!dir.startsWith(resolve22(vaultPath))) {
5663
6348
  res.status(400).json({ error: "Invalid path" });
5664
6349
  return;
5665
6350
  }
5666
- mkdirSync22(dir, { recursive: true });
5667
- const filePath = join30(dir, `${safeTitle}.md`);
5668
- writeFileSync22(filePath, content, "utf-8");
6351
+ mkdirSync23(dir, { recursive: true });
6352
+ const filePath = join32(dir, `${safeTitle}.md`);
6353
+ writeFileSync23(filePath, content, "utf-8");
5669
6354
  res.json({ success: true, title: safeTitle, path: filePath });
5670
6355
  } catch (err) {
5671
6356
  console.error(err);
@@ -5815,12 +6500,12 @@ function createIngestRouter(opts) {
5815
6500
  return;
5816
6501
  }
5817
6502
  try {
5818
- const { writeFileSync: writeFileSync22, unlinkSync } = await import("node:fs");
5819
- const { join: join30 } = await import("node:path");
6503
+ const { writeFileSync: writeFileSync23, unlinkSync } = await import("node:fs");
6504
+ const { join: join32 } = await import("node:path");
5820
6505
  const { tmpdir } = await import("node:os");
5821
6506
  const safeName = (file.originalname ?? "upload").replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 100);
5822
- const tmpPath = join30(tmpdir(), `sv-upload-${Date.now()}-${safeName}`);
5823
- writeFileSync22(tmpPath, file.buffer);
6507
+ const tmpPath = join32(tmpdir(), `sv-upload-${Date.now()}-${safeName}`);
6508
+ writeFileSync23(tmpPath, file.buffer);
5824
6509
  const { extractFileContent: extractFileContent2 } = await Promise.resolve().then(() => (init_file_extractors(), file_extractors_exports));
5825
6510
  const ext = file.originalname.split(".").pop()?.toLowerCase() ?? "";
5826
6511
  const binaryExts = /* @__PURE__ */ new Set(["pdf", "docx", "pptx", "xlsx", "xls"]);
@@ -5926,11 +6611,11 @@ ${desc}
5926
6611
  if (content.length > 1e4)
5927
6612
  content = content.slice(0, 1e4) + "\n\n...(truncated)";
5928
6613
  }
5929
- const { writeFileSync: writeFileSync22, mkdirSync: mkdirSync22 } = await import("node:fs");
5930
- const { join: join30 } = await import("node:path");
6614
+ const { writeFileSync: writeFileSync23, mkdirSync: mkdirSync23 } = await import("node:fs");
6615
+ const { join: join32 } = await import("node:path");
5931
6616
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5932
- const clipDir = join30(vaultPath || ".", "06_Research", "clips");
5933
- mkdirSync22(clipDir, { recursive: true });
6617
+ const clipDir = join32(vaultPath || ".", "06_Research", "clips");
6618
+ mkdirSync23(clipDir, { recursive: true });
5934
6619
  const fileName = `${date} ${safeTitle}.md`;
5935
6620
  const md = `---
5936
6621
  title: "${safeTitle}"
@@ -5944,8 +6629,8 @@ tags: [clip${isYT ? ", youtube" : ""}]
5944
6629
  > Source: ${url}
5945
6630
 
5946
6631
  ${content}`;
5947
- writeFileSync22(join30(clipDir, fileName), md, "utf-8");
5948
- res.json({ success: true, fileName, path: join30(clipDir, fileName) });
6632
+ writeFileSync23(join32(clipDir, fileName), md, "utf-8");
6633
+ res.json({ success: true, fileName, path: join32(clipDir, fileName) });
5949
6634
  } catch (err) {
5950
6635
  console.error(err);
5951
6636
  res.status(500).json({ error: "Clip failed" });
@@ -6433,14 +7118,14 @@ function createApiServer(options) {
6433
7118
  res.status(404).json({ error: "Document not found" });
6434
7119
  return;
6435
7120
  }
6436
- const { resolve: resolve22, join: join30 } = await import("node:path");
6437
- const { writeFileSync: writeFileSync22, readFileSync: readFileSync18 } = await import("node:fs");
7121
+ const { resolve: resolve22, join: join32 } = await import("node:path");
7122
+ const { writeFileSync: writeFileSync23, readFileSync: readFileSync19 } = await import("node:fs");
6438
7123
  const fullPath = resolve22(vaultPath, doc.filePath);
6439
7124
  if (!fullPath.startsWith(resolve22(vaultPath))) {
6440
7125
  res.status(403).json({ error: "Access denied" });
6441
7126
  return;
6442
7127
  }
6443
- const existing = readFileSync18(fullPath, "utf-8");
7128
+ const existing = readFileSync19(fullPath, "utf-8");
6444
7129
  let updated = existing;
6445
7130
  if (title && title !== doc.title) {
6446
7131
  updated = updated.replace(/^title:\s*.+$/m, `title: "${title.replace(/"/g, "''")}"`);
@@ -6461,7 +7146,7 @@ function createApiServer(options) {
6461
7146
  updated = content;
6462
7147
  }
6463
7148
  }
6464
- writeFileSync22(fullPath, updated, "utf-8");
7149
+ writeFileSync23(fullPath, updated, "utf-8");
6465
7150
  await store.upsertDocument({
6466
7151
  ...doc,
6467
7152
  title: title ?? doc.title,
@@ -6484,13 +7169,13 @@ function createApiServer(options) {
6484
7169
  return;
6485
7170
  }
6486
7171
  const { resolve: resolve22 } = await import("node:path");
6487
- const { unlinkSync, existsSync: existsSync25 } = await import("node:fs");
7172
+ const { unlinkSync, existsSync: existsSync27 } = await import("node:fs");
6488
7173
  const fullPath = resolve22(vaultPath, doc.filePath);
6489
7174
  if (!fullPath.startsWith(resolve22(vaultPath))) {
6490
7175
  res.status(403).json({ error: "Access denied" });
6491
7176
  return;
6492
7177
  }
6493
- if (existsSync25(fullPath)) {
7178
+ if (existsSync27(fullPath)) {
6494
7179
  unlinkSync(fullPath);
6495
7180
  }
6496
7181
  await store.deleteByDocumentId(id);
@@ -6616,12 +7301,12 @@ function createApiServer(options) {
6616
7301
  const { resolve: resolve22 } = await import("node:path");
6617
7302
  const syncScript = resolve22(process.cwd(), "packages/sync/sync-to-obsidian.mjs");
6618
7303
  const syncDir = resolve22(process.cwd(), "packages/sync");
6619
- const { existsSync: existsSync25 } = await import("node:fs");
6620
- if (!existsSync25(syncScript)) {
7304
+ const { existsSync: existsSync27 } = await import("node:fs");
7305
+ if (!existsSync27(syncScript)) {
6621
7306
  res.json({ success: false, error: "sync script not found" });
6622
7307
  return;
6623
7308
  }
6624
- if (!existsSync25(resolve22(syncDir, ".env"))) {
7309
+ if (!existsSync27(resolve22(syncDir, ".env"))) {
6625
7310
  res.json({ success: false, error: ".env not found" });
6626
7311
  return;
6627
7312
  }
@@ -7016,8 +7701,8 @@ async function transcribeAudio(audioPath, options = {}) {
7016
7701
  try {
7017
7702
  execFileSync("whisper", args, { stdio: "pipe", timeout: 3e5 });
7018
7703
  const outputName = basename4(audioPath).replace(/\.[^.]+$/, ".txt");
7019
- const { readFileSync: readFileSync18 } = await import("node:fs");
7020
- return readFileSync18(join14("/tmp/sv-whisper", outputName), "utf-8").trim();
7704
+ const { readFileSync: readFileSync19 } = await import("node:fs");
7705
+ return readFileSync19(join14("/tmp/sv-whisper", outputName), "utf-8").trim();
7021
7706
  } catch (err) {
7022
7707
  throw new Error(`Whisper failed: ${err instanceof Error ? err.message : err}`);
7023
7708
  }
@@ -7253,11 +7938,33 @@ var CREDITS_FILE = join19(homedir11(), ".stellavault", "federation", "credits.js
7253
7938
  init_retry();
7254
7939
  init_math();
7255
7940
  init_indexer();
7941
+ init_config();
7256
7942
  function createKnowledgeHub(config, options = {}) {
7257
7943
  const embedder = createLocalEmbedder(config.embedding.localModel);
7258
7944
  const dims = embedder.dimensions;
7259
7945
  const store = createSqliteVecStore(config.dbPath, dims);
7260
- const searchEngine = createSearchEngine({ store, embedder, rrfK: config.search.rrfK });
7946
+ let _decay = null;
7947
+ const getDecayEngine = () => {
7948
+ if (_decay)
7949
+ return _decay;
7950
+ try {
7951
+ const db = store.getDb();
7952
+ if (!db)
7953
+ return void 0;
7954
+ _decay = new DecayEngine(db);
7955
+ return _decay;
7956
+ } catch {
7957
+ return void 0;
7958
+ }
7959
+ };
7960
+ const sw = resolveSearchWeights(config);
7961
+ const searchEngine = createSearchEngine({
7962
+ store,
7963
+ embedder,
7964
+ rrfK: config.search.rrfK,
7965
+ weights: { semantic: sw.semantic, bm25: sw.bm25, entity: sw.entity, recency: sw.recency },
7966
+ getDecayEngine
7967
+ });
7261
7968
  const mcpServer = createMcpServer({ store, searchEngine, vaultPath: config.vaultPath, ready: options.ready });
7262
7969
  return { store, embedder, searchEngine, mcpServer, config };
7263
7970
  }
@@ -7269,6 +7976,14 @@ function getVaultDbPath(vaultPath) {
7269
7976
  mkdirSync15(dir, { recursive: true });
7270
7977
  return join20(dir, `${hash}.db`);
7271
7978
  }
7979
+ function resolveDbPath(vault2, configDbPath) {
7980
+ const envDbPath = process.env.STELLAVAULT_DB_PATH?.trim();
7981
+ if (envDbPath)
7982
+ return envDbPath;
7983
+ if (configDbPath)
7984
+ return configDbPath;
7985
+ return getVaultDbPath(vault2);
7986
+ }
7272
7987
  async function indexCommand(vaultPath, opts = {}) {
7273
7988
  if (opts.profileMemory)
7274
7989
  process.env.STELLAVAULT_PROFILE_MEMORY = "1";
@@ -7278,7 +7993,7 @@ async function indexCommand(vaultPath, opts = {}) {
7278
7993
  console.error(chalk.red("Error: vault path required. Use stellavault index <path> or set vaultPath in .stellavault.json"));
7279
7994
  process.exit(1);
7280
7995
  }
7281
- const dbPath = vaultPath ? getVaultDbPath(vault2) : config.dbPath;
7996
+ const dbPath = resolveDbPath(vault2, config.dbPath);
7282
7997
  const existingVaults = listVaults();
7283
7998
  const vaultName = vault2.split(/[/\\]/).filter(Boolean).pop() ?? "vault";
7284
7999
  if (!existingVaults.some((v) => v.path === vault2)) {
@@ -7380,7 +8095,13 @@ async function searchCommand(query, options, cmd) {
7380
8095
  await store.initialize();
7381
8096
  const embedder = createLocalEmbedder(config.embedding.localModel);
7382
8097
  await embedder.initialize();
7383
- const engine = createSearchEngine({ store, embedder, rrfK: config.search.rrfK });
8098
+ const sw = resolveSearchWeights(config);
8099
+ const engine = createSearchEngine({
8100
+ store,
8101
+ embedder,
8102
+ rrfK: config.search.rrfK,
8103
+ weights: { semantic: sw.semantic, bm25: sw.bm25, entity: sw.entity, recency: sw.recency }
8104
+ });
7384
8105
  const results = await engine.search({ query, limit });
7385
8106
  await store.close();
7386
8107
  if (jsonMode) {
@@ -7422,6 +8143,8 @@ async function serveCommand() {
7422
8143
  console.error(chalk3.green("\u{1F680} MCP Server running (stdio mode) \u2014 index loading in background"));
7423
8144
  console.error(chalk3.dim("\u{1F4A1} Claude Code: claude mcp add stellavault -- stellavault serve"));
7424
8145
  const serverPromise = hub.mcpServer.startStdio();
8146
+ const watcherEnabled = (process.env.STELLAVAULT_WATCH ?? "1").trim() !== "0";
8147
+ let watcherHandle = null;
7425
8148
  (async () => {
7426
8149
  try {
7427
8150
  const t0 = Date.now();
@@ -7431,16 +8154,60 @@ async function serveCommand() {
7431
8154
  const elapsed = Date.now() - t0;
7432
8155
  console.error(`\u{1F4DA} ${stats.documentCount} documents | ${stats.chunkCount} chunks (ready in ${elapsed}ms)`);
7433
8156
  resolveReady();
8157
+ if (watcherEnabled && config.vaultPath) {
8158
+ try {
8159
+ watcherHandle = createWatcher({
8160
+ vaultPath: config.vaultPath,
8161
+ store: hub.store,
8162
+ embedder: hub.embedder,
8163
+ chunkOptions: config.chunking,
8164
+ debounceMs: Number(process.env.STELLAVAULT_WATCH_DEBOUNCE_MS ?? 5e3),
8165
+ onReindex: (r) => {
8166
+ console.error(`\u{1F440} watcher reindex: ${r.indexed} indexed, ${r.skipped} unchanged`);
8167
+ try {
8168
+ invalidateGapCache(hub.store.getDb());
8169
+ } catch {
8170
+ }
8171
+ }
8172
+ });
8173
+ watcherHandle.start();
8174
+ console.error(chalk3.green(`\u{1F440} Watcher started (debounce ${process.env.STELLAVAULT_WATCH_DEBOUNCE_MS ?? 5e3}ms) \u2014 vault changes auto-reindex`));
8175
+ } catch (err) {
8176
+ console.error(chalk3.yellow("\u26A0\uFE0F Watcher init failed: " + err.message));
8177
+ }
8178
+ } else if (!watcherEnabled) {
8179
+ console.error(chalk3.dim("\u{1F440} Watcher disabled (STELLAVAULT_WATCH=0)"));
8180
+ } else {
8181
+ console.error(chalk3.dim("\u{1F440} Watcher skipped (no config.vaultPath set)"));
8182
+ }
7434
8183
  } catch (err) {
7435
8184
  console.error(chalk3.red("\u274C Index load failed: " + err.message));
7436
8185
  resolveReady();
7437
8186
  }
7438
8187
  })();
8188
+ const cleanup = () => {
8189
+ try {
8190
+ watcherHandle?.stop();
8191
+ } catch {
8192
+ }
8193
+ };
8194
+ process.once("SIGINT", () => {
8195
+ cleanup();
8196
+ process.exit(130);
8197
+ });
8198
+ process.once("SIGTERM", () => {
8199
+ cleanup();
8200
+ process.exit(143);
8201
+ });
7439
8202
  await serverPromise;
8203
+ cleanup();
7440
8204
  }
7441
8205
 
8206
+ // packages/cli/dist/index.js
8207
+ init_setup_cmd();
8208
+
7442
8209
  // packages/cli/dist/commands/status-cmd.js
7443
- import chalk4 from "chalk";
8210
+ import chalk5 from "chalk";
7444
8211
  async function statusCommand(_opts, cmd) {
7445
8212
  const globalOpts = cmd?.parent?.opts?.() ?? {};
7446
8213
  const jsonMode = globalOpts.json;
@@ -7472,7 +8239,7 @@ async function statusCommand(_opts, cmd) {
7472
8239
  return;
7473
8240
  }
7474
8241
  console.log("");
7475
- console.log(chalk4.bold("\u{1F4CA} Stellavault Status"));
8242
+ console.log(chalk5.bold("\u{1F4CA} Stellavault Status"));
7476
8243
  console.log("\u2500".repeat(40));
7477
8244
  console.log(` \u{1F4C4} Documents: ${stats.documentCount}${totalFiles != null ? ` / ${totalFiles} files (${Math.round(stats.documentCount / totalFiles * 100)}%)` : ""}`);
7478
8245
  if (skippedFiles != null && skippedFiles > 0) {
@@ -7484,7 +8251,7 @@ async function statusCommand(_opts, cmd) {
7484
8251
  console.log(` \u{1F4C1} Vault: ${config.vaultPath || "(not set)"}`);
7485
8252
  if (topics.length > 0) {
7486
8253
  console.log("");
7487
- console.log(chalk4.bold("\u{1F3F7}\uFE0F Top topics:"));
8254
+ console.log(chalk5.bold("\u{1F3F7}\uFE0F Top topics:"));
7488
8255
  topics.slice(0, 10).forEach((t2) => {
7489
8256
  console.log(` #${t2.topic} (${t2.count})`);
7490
8257
  });
@@ -7493,22 +8260,22 @@ async function statusCommand(_opts, cmd) {
7493
8260
  }
7494
8261
 
7495
8262
  // packages/cli/dist/commands/graph-cmd.js
7496
- import chalk5 from "chalk";
8263
+ import chalk6 from "chalk";
7497
8264
  import { spawn } from "node:child_process";
7498
- import { resolve as resolve11, dirname as dirname4 } from "node:path";
7499
- import { existsSync as existsSync15 } from "node:fs";
8265
+ import { resolve as resolve11, dirname as dirname5 } from "node:path";
8266
+ import { existsSync as existsSync17 } from "node:fs";
7500
8267
  import { fileURLToPath } from "node:url";
7501
8268
  function locateBundledGraphUi() {
7502
8269
  try {
7503
- const here = dirname4(fileURLToPath(import.meta.url));
8270
+ const here = dirname5(fileURLToPath(import.meta.url));
7504
8271
  const bundled = resolve11(here, "graph-ui");
7505
- if (existsSync15(resolve11(bundled, "index.html")))
8272
+ if (existsSync17(resolve11(bundled, "index.html")))
7506
8273
  return bundled;
7507
8274
  const monorepoGraphDist = resolve11(here, "..", "..", "..", "graph", "dist");
7508
- if (existsSync15(resolve11(monorepoGraphDist, "index.html")))
8275
+ if (existsSync17(resolve11(monorepoGraphDist, "index.html")))
7509
8276
  return monorepoGraphDist;
7510
8277
  const singleFileBundle = resolve11(here, "..", "dist", "graph-ui");
7511
- if (existsSync15(resolve11(singleFileBundle, "index.html")))
8278
+ if (existsSync17(resolve11(singleFileBundle, "index.html")))
7512
8279
  return singleFileBundle;
7513
8280
  } catch {
7514
8281
  }
@@ -7517,12 +8284,12 @@ function locateBundledGraphUi() {
7517
8284
  async function graphCommand() {
7518
8285
  const config = loadConfig();
7519
8286
  const hub = createKnowledgeHub(config);
7520
- console.error(chalk5.dim("\u23F3 Initializing..."));
8287
+ console.error(chalk6.dim("\u23F3 Initializing..."));
7521
8288
  await hub.store.initialize();
7522
8289
  await hub.embedder.initialize();
7523
8290
  const stats = await hub.store.getStats();
7524
8291
  if (stats.documentCount === 0) {
7525
- console.error(chalk5.yellow("\u26A0 No documents indexed. Run `stellavault index <vault-path>` first."));
8292
+ console.error(chalk6.yellow("\u26A0 No documents indexed. Run `stellavault index <vault-path>` first."));
7526
8293
  process.exit(1);
7527
8294
  }
7528
8295
  const port = config.mcp.port || 3333;
@@ -7540,28 +8307,28 @@ async function graphCommand() {
7540
8307
  await api.start();
7541
8308
  } catch (err) {
7542
8309
  if (err && typeof err === "object" && "code" in err && err.code === "EADDRINUSE") {
7543
- console.error(chalk5.red(`Port ${port} is already in use.`));
7544
- console.error(chalk5.dim(`Stop the other process or use a different port:`));
7545
- console.error(chalk5.dim(` Edit .stellavault.json: { "mcp": { "port": ${port + 1} } }`));
8310
+ console.error(chalk6.red(`Port ${port} is already in use.`));
8311
+ console.error(chalk6.dim(`Stop the other process or use a different port:`));
8312
+ console.error(chalk6.dim(` Edit .stellavault.json: { "mcp": { "port": ${port + 1} } }`));
7546
8313
  process.exit(1);
7547
8314
  }
7548
8315
  throw err;
7549
8316
  }
7550
- console.error(chalk5.green("\u{1F9E0} Stellavault \u2014 Neural Knowledge Graph"));
8317
+ console.error(chalk6.green("\u{1F9E0} Stellavault \u2014 Neural Knowledge Graph"));
7551
8318
  console.error(` \u{1F4DA} ${stats.documentCount} documents | ${stats.chunkCount} chunks`);
7552
8319
  console.error(` \u{1F310} API: http://127.0.0.1:${port}`);
7553
8320
  if (graphUiPath) {
7554
8321
  const url = `http://127.0.0.1:${port}/`;
7555
- console.error(chalk5.green(` \u{1F52E} Graph: ${url}`));
7556
- console.error(chalk5.dim(` Press Ctrl+C to stop`));
8322
+ console.error(chalk6.green(` \u{1F52E} Graph: ${url}`));
8323
+ console.error(chalk6.dim(` Press Ctrl+C to stop`));
7557
8324
  openBrowser(url);
7558
8325
  process.on("SIGINT", () => process.exit(0));
7559
8326
  return;
7560
8327
  }
7561
8328
  const devGraphDir = resolve11(process.cwd(), "packages/graph");
7562
- const hasDevGraph = existsSync15(resolve11(devGraphDir, "package.json"));
8329
+ const hasDevGraph = existsSync17(resolve11(devGraphDir, "package.json"));
7563
8330
  if (hasDevGraph) {
7564
- console.error(chalk5.dim(" \u{1F680} Starting Vite dev server..."));
8331
+ console.error(chalk6.dim(" \u{1F680} Starting Vite dev server..."));
7565
8332
  const vite = spawn(process.platform === "win32" ? "npx.cmd" : "npx", ["vite", "--host"], {
7566
8333
  cwd: devGraphDir,
7567
8334
  stdio: ["ignore", "pipe", "pipe"]
@@ -7571,21 +8338,21 @@ async function graphCommand() {
7571
8338
  if (line.includes("Local:")) {
7572
8339
  const match = line.match(/http:\/\/localhost:\d+/);
7573
8340
  const url = match?.[0] ?? "http://localhost:5173";
7574
- console.error(chalk5.green(` \u{1F52E} Graph: ${url}`));
8341
+ console.error(chalk6.green(` \u{1F52E} Graph: ${url}`));
7575
8342
  openBrowser(url);
7576
8343
  }
7577
8344
  });
7578
8345
  vite.on("close", () => {
7579
- console.error(chalk5.dim(" Vite server stopped"));
8346
+ console.error(chalk6.dim(" Vite server stopped"));
7580
8347
  });
7581
8348
  process.on("SIGINT", () => {
7582
8349
  vite.kill();
7583
8350
  process.exit(0);
7584
8351
  });
7585
8352
  } else {
7586
- console.error(chalk5.yellow(" \u26A0 Bundled graph UI missing. Reinstall stellavault: npm i -g stellavault@latest"));
8353
+ console.error(chalk6.yellow(" \u26A0 Bundled graph UI missing. Reinstall stellavault: npm i -g stellavault@latest"));
7587
8354
  }
7588
- console.error(chalk5.dim(" Press Ctrl+C to stop"));
8355
+ console.error(chalk6.dim(" Press Ctrl+C to stop"));
7589
8356
  }
7590
8357
  async function openBrowser(url) {
7591
8358
  try {
@@ -7596,41 +8363,41 @@ async function openBrowser(url) {
7596
8363
  }
7597
8364
 
7598
8365
  // packages/cli/dist/commands/card-cmd.js
7599
- import chalk6 from "chalk";
7600
- import { writeFileSync as writeFileSync16 } from "node:fs";
8366
+ import chalk7 from "chalk";
8367
+ import { writeFileSync as writeFileSync17 } from "node:fs";
7601
8368
  import { resolve as resolve12 } from "node:path";
7602
8369
  async function cardCommand(options) {
7603
8370
  const output = options.output ?? "knowledge-card.svg";
7604
8371
  const outPath = resolve12(process.cwd(), output);
7605
- console.error(chalk6.dim("\u23F3 Generating profile card..."));
8372
+ console.error(chalk7.dim("\u23F3 Generating profile card..."));
7606
8373
  try {
7607
8374
  const res = await fetch("http://127.0.0.1:3333/api/profile-card");
7608
8375
  if (!res.ok)
7609
8376
  throw new Error(`API error: ${res.status}. Is 'stellavault graph' running?`);
7610
8377
  const svg = await res.text();
7611
- writeFileSync16(outPath, svg, "utf-8");
7612
- console.error(chalk6.green(`\u2705 Profile card saved: ${outPath}`));
7613
- console.error(chalk6.dim(" Embed in GitHub README:"));
7614
- console.error(chalk6.dim(` ![Knowledge Card](${output})`));
8378
+ writeFileSync17(outPath, svg, "utf-8");
8379
+ console.error(chalk7.green(`\u2705 Profile card saved: ${outPath}`));
8380
+ console.error(chalk7.dim(" Embed in GitHub README:"));
8381
+ console.error(chalk7.dim(` ![Knowledge Card](${output})`));
7615
8382
  } catch (err) {
7616
- console.error(chalk6.red(`\u274C Failed: ${err}`));
7617
- console.error(chalk6.dim(" Make sure API server is running: stellavault graph"));
8383
+ console.error(chalk7.red(`\u274C Failed: ${err}`));
8384
+ console.error(chalk7.dim(" Make sure API server is running: stellavault graph"));
7618
8385
  process.exit(1);
7619
8386
  }
7620
8387
  }
7621
8388
 
7622
8389
  // packages/cli/dist/commands/pack-cmd.js
7623
- import chalk7 from "chalk";
7624
- import { resolve as resolve13, join as join21 } from "node:path";
7625
- import { readdirSync as readdirSync6, existsSync as existsSync16, readFileSync as readFileSync15, mkdirSync as mkdirSync16 } from "node:fs";
7626
- import { homedir as homedir13 } from "node:os";
7627
- var PACKS_DIR = join21(homedir13(), ".stellavault", "packs");
8390
+ import chalk8 from "chalk";
8391
+ import { resolve as resolve13, join as join23 } from "node:path";
8392
+ import { readdirSync as readdirSync6, existsSync as existsSync18, readFileSync as readFileSync16, mkdirSync as mkdirSync17 } from "node:fs";
8393
+ import { homedir as homedir15 } from "node:os";
8394
+ var PACKS_DIR = join23(homedir15(), ".stellavault", "packs");
7628
8395
  async function packCreateCommand(name, options) {
7629
8396
  const config = loadConfig();
7630
8397
  const hub = createKnowledgeHub(config);
7631
8398
  await hub.store.initialize();
7632
8399
  await hub.embedder.initialize();
7633
- console.error(chalk7.dim("\u23F3 Creating pack..."));
8400
+ console.error(chalk8.dim("\u23F3 Creating pack..."));
7634
8401
  const { pack: pack2, piiReport } = await createPack(hub.store, hub.searchEngine, hub.embedder, {
7635
8402
  name,
7636
8403
  fromSearch: options.fromSearch,
@@ -7640,89 +8407,89 @@ async function packCreateCommand(name, options) {
7640
8407
  description: options.description,
7641
8408
  limit: options.limit ? parseInt(options.limit) : 100
7642
8409
  });
7643
- mkdirSync16(PACKS_DIR, { recursive: true });
7644
- const outPath = join21(PACKS_DIR, `${name}.sv-pack`);
8410
+ mkdirSync17(PACKS_DIR, { recursive: true });
8411
+ const outPath = join23(PACKS_DIR, `${name}.sv-pack`);
7645
8412
  exportPack(pack2, outPath);
7646
- console.error(chalk7.green(`\u2705 Pack created: ${name}`));
8413
+ console.error(chalk8.green(`\u2705 Pack created: ${name}`));
7647
8414
  console.error(` \u{1F4E6} ${pack2.chunks.length} chunks`);
7648
8415
  console.error(` \u{1F4BE} ${outPath}`);
7649
8416
  if (piiReport.redactedCount > 0) {
7650
- console.error(chalk7.yellow(` \u{1F512} PII masked: ${piiReport.redactedCount} items (${piiReport.types.join(", ")})`));
8417
+ console.error(chalk8.yellow(` \u{1F512} PII masked: ${piiReport.redactedCount} items (${piiReport.types.join(", ")})`));
7651
8418
  }
7652
8419
  await hub.store.close();
7653
8420
  }
7654
8421
  async function packExportCommand(name, options) {
7655
- const srcPath = join21(PACKS_DIR, `${name}.sv-pack`);
7656
- if (!existsSync16(srcPath)) {
7657
- console.error(chalk7.red(`\u274C Pack not found: ${name}`));
8422
+ const srcPath = join23(PACKS_DIR, `${name}.sv-pack`);
8423
+ if (!existsSync18(srcPath)) {
8424
+ console.error(chalk8.red(`\u274C Pack not found: ${name}`));
7658
8425
  process.exit(1);
7659
8426
  }
7660
8427
  const outPath = resolve13(process.cwd(), options.output ?? `${name}.sv-pack`);
7661
- const content = readFileSync15(srcPath, "utf-8");
7662
- const { writeFileSync: writeFileSync22 } = await import("node:fs");
7663
- writeFileSync22(outPath, content);
7664
- console.error(chalk7.green(`\u2705 Exported: ${outPath}`));
8428
+ const content = readFileSync16(srcPath, "utf-8");
8429
+ const { writeFileSync: writeFileSync23 } = await import("node:fs");
8430
+ writeFileSync23(outPath, content);
8431
+ console.error(chalk8.green(`\u2705 Exported: ${outPath}`));
7665
8432
  }
7666
8433
  async function packImportCommand(filePath) {
7667
8434
  const absPath = resolve13(process.cwd(), filePath);
7668
- if (!existsSync16(absPath)) {
7669
- console.error(chalk7.red(`\u274C File not found: ${absPath}`));
8435
+ if (!existsSync18(absPath)) {
8436
+ console.error(chalk8.red(`\u274C File not found: ${absPath}`));
7670
8437
  process.exit(1);
7671
8438
  }
7672
8439
  const config = loadConfig();
7673
8440
  const hub = createKnowledgeHub(config);
7674
8441
  await hub.store.initialize();
7675
8442
  await hub.embedder.initialize();
7676
- console.error(chalk7.dim("\u23F3 Importing pack..."));
8443
+ console.error(chalk8.dim("\u23F3 Importing pack..."));
7677
8444
  const result = await importPack(hub.store, hub.embedder, absPath);
7678
- console.error(chalk7.green(`\u2705 Imported: ${result.imported} chunks`));
8445
+ console.error(chalk8.green(`\u2705 Imported: ${result.imported} chunks`));
7679
8446
  if (result.skipped > 0)
7680
- console.error(chalk7.yellow(` \u23ED\uFE0F Skipped: ${result.skipped}`));
8447
+ console.error(chalk8.yellow(` \u23ED\uFE0F Skipped: ${result.skipped}`));
7681
8448
  if (result.modelMismatch) {
7682
- console.error(chalk7.yellow(` \u26A0\uFE0F Model mismatch \u2014 ${result.reEmbedded} chunks re-embedded`));
8449
+ console.error(chalk8.yellow(` \u26A0\uFE0F Model mismatch \u2014 ${result.reEmbedded} chunks re-embedded`));
7683
8450
  }
7684
8451
  await hub.store.close();
7685
8452
  }
7686
8453
  async function packListCommand() {
7687
- mkdirSync16(PACKS_DIR, { recursive: true });
8454
+ mkdirSync17(PACKS_DIR, { recursive: true });
7688
8455
  const files = readdirSync6(PACKS_DIR).filter((f) => f.endsWith(".sv-pack"));
7689
8456
  if (files.length === 0) {
7690
- console.error(chalk7.dim("No packs found. Create one: stellavault pack create <name> --from-search <query>"));
8457
+ console.error(chalk8.dim("No packs found. Create one: stellavault pack create <name> --from-search <query>"));
7691
8458
  return;
7692
8459
  }
7693
- console.error(chalk7.green(`\u{1F4E6} ${files.length} packs in ${PACKS_DIR}
8460
+ console.error(chalk8.green(`\u{1F4E6} ${files.length} packs in ${PACKS_DIR}
7694
8461
  `));
7695
8462
  for (const file of files) {
7696
8463
  try {
7697
- const pack2 = JSON.parse(readFileSync15(join21(PACKS_DIR, file), "utf-8"));
7698
- console.error(` ${chalk7.bold(pack2.name)} v${pack2.version} \u2014 ${pack2.chunks.length} chunks (${pack2.license})`);
8464
+ const pack2 = JSON.parse(readFileSync16(join23(PACKS_DIR, file), "utf-8"));
8465
+ console.error(` ${chalk8.bold(pack2.name)} v${pack2.version} \u2014 ${pack2.chunks.length} chunks (${pack2.license})`);
7699
8466
  } catch {
7700
8467
  console.error(` ${file} (invalid)`);
7701
8468
  }
7702
8469
  }
7703
8470
  }
7704
8471
  async function packInfoCommand(name) {
7705
- const filePath = join21(PACKS_DIR, `${name}.sv-pack`);
7706
- if (!existsSync16(filePath)) {
7707
- console.error(chalk7.red(`\u274C Pack not found: ${name}`));
8472
+ const filePath = join23(PACKS_DIR, `${name}.sv-pack`);
8473
+ if (!existsSync18(filePath)) {
8474
+ console.error(chalk8.red(`\u274C Pack not found: ${name}`));
7708
8475
  process.exit(1);
7709
8476
  }
7710
- const pack2 = JSON.parse(readFileSync15(filePath, "utf-8"));
8477
+ const pack2 = JSON.parse(readFileSync16(filePath, "utf-8"));
7711
8478
  console.error(packToSummary(pack2));
7712
8479
  }
7713
8480
 
7714
8481
  // packages/cli/dist/commands/decay-cmd.js
7715
- import chalk8 from "chalk";
8482
+ import chalk9 from "chalk";
7716
8483
  async function decayCommand(_opts, cmd) {
7717
8484
  const globalOpts = cmd?.parent?.opts?.() ?? {};
7718
8485
  const jsonMode = globalOpts.json;
7719
8486
  const config = loadConfig();
7720
8487
  const hub = createKnowledgeHub(config);
7721
- console.error(chalk8.dim("\u23F3 Initializing..."));
8488
+ console.error(chalk9.dim("\u23F3 Initializing..."));
7722
8489
  await hub.store.initialize();
7723
8490
  const db = hub.store.getDb();
7724
8491
  if (!db) {
7725
- console.error(chalk8.red("\u274C Cannot access database"));
8492
+ console.error(chalk9.red("\u274C Cannot access database"));
7726
8493
  process.exit(1);
7727
8494
  }
7728
8495
  const decayEngine = new DecayEngine(db);
@@ -7732,65 +8499,65 @@ async function decayCommand(_opts, cmd) {
7732
8499
  await hub.store.close();
7733
8500
  return;
7734
8501
  }
7735
- console.log(chalk8.green("\n\u{1F9E0} Knowledge Decay Report"));
7736
- console.log(chalk8.dim("\u2500".repeat(50)));
8502
+ console.log(chalk9.green("\n\u{1F9E0} Knowledge Decay Report"));
8503
+ console.log(chalk9.dim("\u2500".repeat(50)));
7737
8504
  console.log(` \u{1F4C4} Total documents: ${report.totalDocuments}`);
7738
- console.log(` \u26A0\uFE0F Decaying (R<0.5): ${chalk8.yellow(String(report.decayingCount))}`);
7739
- console.log(` \u{1F534} Critical (R<0.3): ${chalk8.red(String(report.criticalCount))}`);
8505
+ console.log(` \u26A0\uFE0F Decaying (R<0.5): ${chalk9.yellow(String(report.decayingCount))}`);
8506
+ console.log(` \u{1F534} Critical (R<0.3): ${chalk9.red(String(report.criticalCount))}`);
7740
8507
  console.log(` \u{1F4CA} Average R: ${report.averageR}`);
7741
- console.log(chalk8.dim("\u2500".repeat(50)));
8508
+ console.log(chalk9.dim("\u2500".repeat(50)));
7742
8509
  if (report.topDecaying.length > 0) {
7743
- console.log(chalk8.yellow("\n\u{1F4CB} Top Decaying Notes (need review):"));
8510
+ console.log(chalk9.yellow("\n\u{1F4CB} Top Decaying Notes (need review):"));
7744
8511
  for (const d of report.topDecaying.slice(0, 20)) {
7745
8512
  const rBar = "\u2588".repeat(Math.round(d.retrievability * 10)) + "\u2591".repeat(10 - Math.round(d.retrievability * 10));
7746
- const color = d.retrievability < 0.3 ? chalk8.red : chalk8.yellow;
8513
+ const color = d.retrievability < 0.3 ? chalk9.red : chalk9.yellow;
7747
8514
  console.log(` ${color(rBar)} R=${d.retrievability.toFixed(2)} | ${d.daysSinceAccess}d ago | ${d.title}`);
7748
8515
  }
7749
8516
  }
7750
8517
  if (report.clusterHealth.length > 0) {
7751
- console.log(chalk8.dim("\n\u{1F4CA} Cluster Health:"));
8518
+ console.log(chalk9.dim("\n\u{1F4CA} Cluster Health:"));
7752
8519
  for (const c of report.clusterHealth.slice(0, 10)) {
7753
- const color = c.avgR < 0.3 ? chalk8.red : c.avgR < 0.5 ? chalk8.yellow : chalk8.green;
8520
+ const color = c.avgR < 0.3 ? chalk9.red : c.avgR < 0.5 ? chalk9.yellow : chalk9.green;
7754
8521
  console.log(` ${color(`R=${c.avgR.toFixed(2)}`)} | ${c.count} docs | ${c.label}`);
7755
8522
  }
7756
8523
  }
7757
- console.log(chalk8.dim("\n\u{1F4A1} Tip: stellavault search <topic> to refresh decaying knowledge"));
8524
+ console.log(chalk9.dim("\n\u{1F4A1} Tip: stellavault search <topic> to refresh decaying knowledge"));
7758
8525
  }
7759
8526
 
7760
8527
  // packages/cli/dist/commands/sync-cmd.js
7761
- import chalk9 from "chalk";
8528
+ import chalk10 from "chalk";
7762
8529
  import { spawn as spawn2 } from "node:child_process";
7763
8530
  import { resolve as resolve14 } from "node:path";
7764
- import { existsSync as existsSync17 } from "node:fs";
8531
+ import { existsSync as existsSync19 } from "node:fs";
7765
8532
  async function syncCommand(options) {
7766
8533
  const syncDir = resolve14(process.cwd(), "packages/sync");
7767
8534
  const syncScript = resolve14(syncDir, "sync-to-obsidian.mjs");
7768
- if (!existsSync17(syncScript)) {
7769
- console.error(chalk9.red("\u274C packages/sync/sync-to-obsidian.mjs not found"));
7770
- console.error(chalk9.dim(" Run from project root: cd notion-obsidian-sync"));
8535
+ if (!existsSync19(syncScript)) {
8536
+ console.error(chalk10.red("\u274C packages/sync/sync-to-obsidian.mjs not found"));
8537
+ console.error(chalk10.dim(" Run from project root: cd notion-obsidian-sync"));
7771
8538
  process.exit(1);
7772
8539
  }
7773
8540
  const envFile = resolve14(syncDir, ".env");
7774
- if (!existsSync17(envFile)) {
7775
- console.error(chalk9.red("\u274C packages/sync/.env not found"));
7776
- console.error(chalk9.dim(" Copy .env.example \u2192 .env and set NOTION_TOKEN"));
8541
+ if (!existsSync19(envFile)) {
8542
+ console.error(chalk10.red("\u274C packages/sync/.env not found"));
8543
+ console.error(chalk10.dim(" Copy .env.example \u2192 .env and set NOTION_TOKEN"));
7777
8544
  process.exit(1);
7778
8545
  }
7779
8546
  if (options.upload) {
7780
8547
  const uploadScript = resolve14(syncDir, "upload-pdca-to-notion.mjs");
7781
- if (!existsSync17(uploadScript)) {
7782
- console.error(chalk9.red("\u274C upload-pdca-to-notion.mjs not found"));
8548
+ if (!existsSync19(uploadScript)) {
8549
+ console.error(chalk10.red("\u274C upload-pdca-to-notion.mjs not found"));
7783
8550
  process.exit(1);
7784
8551
  }
7785
- console.error(chalk9.dim("\u{1F4E4} Uploading PDCA documents to Notion..."));
8552
+ console.error(chalk10.dim("\u{1F4E4} Uploading PDCA documents to Notion..."));
7786
8553
  await runScript(uploadScript, syncDir);
7787
8554
  } else {
7788
- console.error(chalk9.dim("\u{1F504} Syncing Notion \u2192 Obsidian..."));
8555
+ console.error(chalk10.dim("\u{1F504} Syncing Notion \u2192 Obsidian..."));
7789
8556
  await runScript(syncScript, syncDir);
7790
8557
  if (options.watch) {
7791
- console.error(chalk9.green("\u{1F440} Watch mode \u2014 syncing every 5 minutes"));
8558
+ console.error(chalk10.green("\u{1F440} Watch mode \u2014 syncing every 5 minutes"));
7792
8559
  setInterval(async () => {
7793
- console.error(chalk9.dim(`\u{1F504} [${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Re-syncing...`));
8560
+ console.error(chalk10.dim(`\u{1F504} [${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Re-syncing...`));
7794
8561
  await runScript(syncScript, syncDir);
7795
8562
  }, 5 * 60 * 1e3);
7796
8563
  process.stdin.resume();
@@ -7815,7 +8582,7 @@ function runScript(scriptPath, cwd) {
7815
8582
  }
7816
8583
 
7817
8584
  // packages/cli/dist/commands/review-cmd.js
7818
- import chalk10 from "chalk";
8585
+ import chalk11 from "chalk";
7819
8586
  import { createInterface } from "node:readline";
7820
8587
  function globToRegex(glob) {
7821
8588
  const esc = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".+").replace(/\*/g, "[^/]*").replace(/\?/g, ".");
@@ -7839,11 +8606,11 @@ async function reviewCommand(options) {
7839
8606
  const minAgeDays = parseInt(options.minAge ?? "0", 10);
7840
8607
  const excludeRe = options.exclude ? globToRegex(options.exclude) : null;
7841
8608
  if (!options.json)
7842
- console.error(chalk10.dim("\u23F3 Initializing..."));
8609
+ console.error(chalk11.dim("\u23F3 Initializing..."));
7843
8610
  await hub.store.initialize();
7844
8611
  const db = hub.store.getDb();
7845
8612
  if (!db) {
7846
- console.error(chalk10.red("\u274C Cannot access database"));
8613
+ console.error(chalk11.red("\u274C Cannot access database"));
7847
8614
  process.exit(1);
7848
8615
  }
7849
8616
  const decayEngine = new DecayEngine(db);
@@ -7883,12 +8650,12 @@ async function reviewCommand(options) {
7883
8650
  return;
7884
8651
  }
7885
8652
  if (decaying.length === 0) {
7886
- console.log(chalk10.green("\n\u2728 All knowledge is healthy! No notes to review."));
8653
+ console.log(chalk11.green("\n\u2728 All knowledge is healthy! No notes to review."));
7887
8654
  return;
7888
8655
  }
7889
- console.log(chalk10.green(`
8656
+ console.log(chalk11.green(`
7890
8657
  \u{1F9E0} Today's review (${decaying.length})`));
7891
- console.log(chalk10.dim("\u2500".repeat(50)));
8658
+ console.log(chalk11.dim("\u2500".repeat(50)));
7892
8659
  const rl = createInterface({ input: process.stdin, output: process.stdout });
7893
8660
  const ask2 = (q) => new Promise((r) => rl.question(q, r));
7894
8661
  let reviewed = 0;
@@ -7896,13 +8663,13 @@ async function reviewCommand(options) {
7896
8663
  const d = decaying[i];
7897
8664
  const elapsed = Math.round((Date.now() - new Date(d.lastAccess).getTime()) / 864e5);
7898
8665
  const rBar = "\u2588".repeat(Math.round(d.retrievability * 10)) + "\u2591".repeat(10 - Math.round(d.retrievability * 10));
7899
- const color = d.retrievability < 0.3 ? chalk10.red : chalk10.yellow;
8666
+ const color = d.retrievability < 0.3 ? chalk11.red : chalk11.yellow;
7900
8667
  console.log(`
7901
- ${chalk10.bold(`[${i + 1}/${decaying.length}]`)} ${chalk10.cyan(d.title)}`);
8668
+ ${chalk11.bold(`[${i + 1}/${decaying.length}]`)} ${chalk11.cyan(d.title)}`);
7902
8669
  console.log(` ${color(rBar)} R=${d.retrievability.toFixed(2)} | ${elapsed} days ago`);
7903
- const answer = await ask2(chalk10.dim(" \u2192 [y]open [n]skip [s]snooze [q]quit: "));
8670
+ const answer = await ask2(chalk11.dim(" \u2192 [y]open [n]skip [s]snooze [q]quit: "));
7904
8671
  if (answer.toLowerCase() === "q") {
7905
- console.log(chalk10.dim("\nReview stopped."));
8672
+ console.log(chalk11.dim("\nReview stopped."));
7906
8673
  break;
7907
8674
  }
7908
8675
  if (answer.toLowerCase() === "s") {
@@ -7912,7 +8679,7 @@ ${chalk10.bold(`[${i + 1}/${decaying.length}]`)} ${chalk10.cyan(d.title)}`);
7912
8679
  timestamp: new Date(Date.now() - 23 * 36e5).toISOString()
7913
8680
  // 23시간 전으로 기록
7914
8681
  });
7915
- console.log(chalk10.dim(" \u23F0 Reminder set for tomorrow"));
8682
+ console.log(chalk11.dim(" \u23F0 Reminder set for tomorrow"));
7916
8683
  continue;
7917
8684
  }
7918
8685
  if (answer.toLowerCase() === "y") {
@@ -7935,14 +8702,14 @@ ${chalk10.bold(`[${i + 1}/${decaying.length}]`)} ${chalk10.cyan(d.title)}`);
7935
8702
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
7936
8703
  });
7937
8704
  reviewed++;
7938
- console.log(chalk10.green(" \u2705 Opened + memory strength updated"));
8705
+ console.log(chalk11.green(" \u2705 Opened + memory strength updated"));
7939
8706
  } else {
7940
- console.log(chalk10.dim(" \u23ED\uFE0F Skipped"));
8707
+ console.log(chalk11.dim(" \u23ED\uFE0F Skipped"));
7941
8708
  }
7942
8709
  }
7943
8710
  rl.close();
7944
- console.log(chalk10.dim("\n\u2500".repeat(50)));
7945
- console.log(chalk10.green(`Review complete! ${reviewed}/${decaying.length} reviewed`));
8711
+ console.log(chalk11.dim("\n\u2500".repeat(50)));
8712
+ console.log(chalk11.green(`Review complete! ${reviewed}/${decaying.length} reviewed`));
7946
8713
  try {
7947
8714
  const days = db.prepare(`
7948
8715
  SELECT DISTINCT date(accessed_at) as d FROM access_log
@@ -7959,94 +8726,94 @@ ${chalk10.bold(`[${i + 1}/${decaying.length}]`)} ${chalk10.cyan(d.title)}`);
7959
8726
  break;
7960
8727
  }
7961
8728
  if (streak > 1) {
7962
- console.log(chalk10.yellow(`\u{1F525} ${streak}-day review streak!`));
8729
+ console.log(chalk11.yellow(`\u{1F525} ${streak}-day review streak!`));
7963
8730
  }
7964
8731
  } catch {
7965
8732
  }
7966
8733
  }
7967
8734
 
7968
8735
  // packages/cli/dist/commands/duplicates-cmd.js
7969
- import chalk11 from "chalk";
8736
+ import chalk12 from "chalk";
7970
8737
  async function duplicatesCommand(options) {
7971
8738
  const config = loadConfig();
7972
8739
  const hub = createKnowledgeHub(config);
7973
8740
  const threshold = parseFloat(options.threshold ?? "0.88");
7974
- console.error(chalk11.dim("\u23F3 Scanning for duplicates..."));
8741
+ console.error(chalk12.dim("\u23F3 Scanning for duplicates..."));
7975
8742
  await hub.store.initialize();
7976
8743
  await hub.embedder.initialize();
7977
8744
  const pairs = await detectDuplicates(hub.store, threshold, 20);
7978
8745
  if (pairs.length === 0) {
7979
- console.log(chalk11.green("\n\u2728 No duplicate notes found!"));
8746
+ console.log(chalk12.green("\n\u2728 No duplicate notes found!"));
7980
8747
  return;
7981
8748
  }
7982
- console.log(chalk11.yellow(`
8749
+ console.log(chalk12.yellow(`
7983
8750
  \u{1F50D} Found ${pairs.length} similar note pairs (threshold: ${threshold})`));
7984
- console.log(chalk11.dim("\u2500".repeat(60)));
8751
+ console.log(chalk12.dim("\u2500".repeat(60)));
7985
8752
  for (let i = 0; i < pairs.length; i++) {
7986
8753
  const p = pairs[i];
7987
8754
  const pct = Math.round(p.similarity * 100);
7988
- const color = pct >= 95 ? chalk11.red : chalk11.yellow;
8755
+ const color = pct >= 95 ? chalk12.red : chalk12.yellow;
7989
8756
  console.log(`
7990
- ${chalk11.bold(`[${i + 1}]`)} ${color(`${pct}% similar`)}`);
7991
- console.log(` A: ${chalk11.cyan(p.docA.title)}`);
7992
- console.log(` ${chalk11.dim(p.docA.filePath)}`);
7993
- console.log(` B: ${chalk11.cyan(p.docB.title)}`);
7994
- console.log(` ${chalk11.dim(p.docB.filePath)}`);
8757
+ ${chalk12.bold(`[${i + 1}]`)} ${color(`${pct}% similar`)}`);
8758
+ console.log(` A: ${chalk12.cyan(p.docA.title)}`);
8759
+ console.log(` ${chalk12.dim(p.docA.filePath)}`);
8760
+ console.log(` B: ${chalk12.cyan(p.docB.title)}`);
8761
+ console.log(` ${chalk12.dim(p.docB.filePath)}`);
7995
8762
  }
7996
- console.log(chalk11.dim("\n\u{1F4A1} Merge or delete duplicates in Obsidian"));
8763
+ console.log(chalk12.dim("\n\u{1F4A1} Merge or delete duplicates in Obsidian"));
7997
8764
  }
7998
8765
 
7999
8766
  // packages/cli/dist/commands/gaps-cmd.js
8000
- import chalk12 from "chalk";
8767
+ import chalk13 from "chalk";
8001
8768
  async function gapsCommand() {
8002
8769
  const config = loadConfig();
8003
8770
  const hub = createKnowledgeHub(config);
8004
- console.error(chalk12.dim("\u23F3 Analyzing knowledge gaps..."));
8771
+ console.error(chalk13.dim("\u23F3 Analyzing knowledge gaps..."));
8005
8772
  await hub.store.initialize();
8006
8773
  await hub.embedder.initialize();
8007
8774
  const report = await detectKnowledgeGaps(hub.store);
8008
- console.log(chalk12.green("\n\u{1F573}\uFE0F Knowledge Gap Report"));
8009
- console.log(chalk12.dim("\u2500".repeat(50)));
8775
+ console.log(chalk13.green("\n\u{1F573}\uFE0F Knowledge Gap Report"));
8776
+ console.log(chalk13.dim("\u2500".repeat(50)));
8010
8777
  console.log(` Clusters: ${report.totalClusters}`);
8011
- console.log(` Gaps: ${chalk12.yellow(String(report.totalGaps))} (High+Medium)`);
8778
+ console.log(` Gaps: ${chalk13.yellow(String(report.totalGaps))} (High+Medium)`);
8012
8779
  console.log(` Isolated nodes: ${report.isolatedNodes.length}`);
8013
- console.log(chalk12.dim("\u2500".repeat(50)));
8780
+ console.log(chalk13.dim("\u2500".repeat(50)));
8014
8781
  if (report.gaps.length > 0) {
8015
- console.log(chalk12.yellow("\n\u{1F4CA} Inter-cluster gaps:"));
8782
+ console.log(chalk13.yellow("\n\u{1F4CA} Inter-cluster gaps:"));
8016
8783
  for (const gap of report.gaps) {
8017
8784
  const icon = gap.severity === "high" ? "\u{1F534}" : gap.severity === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
8018
8785
  console.log(` ${icon} ${gap.clusterA} \u2194 ${gap.clusterB}`);
8019
- console.log(` Bridges: ${gap.bridgeCount} | Suggestion: ${chalk12.cyan(gap.suggestedTopic)}`);
8786
+ console.log(` Bridges: ${gap.bridgeCount} | Suggestion: ${chalk13.cyan(gap.suggestedTopic)}`);
8020
8787
  }
8021
8788
  }
8022
8789
  if (report.isolatedNodes.length > 0) {
8023
- console.log(chalk12.dim("\n\u{1F3DD}\uFE0F Isolated notes (\u22641 connections):"));
8790
+ console.log(chalk13.dim("\n\u{1F3DD}\uFE0F Isolated notes (\u22641 connections):"));
8024
8791
  for (const n of report.isolatedNodes.slice(0, 10)) {
8025
8792
  console.log(` \u2022 ${n.title} (${n.connections} connections)`);
8026
8793
  }
8027
8794
  }
8028
- console.log(chalk12.dim("\n\u{1F4A1} Filling knowledge gaps strengthens your graph"));
8795
+ console.log(chalk13.dim("\n\u{1F4A1} Filling knowledge gaps strengthens your graph"));
8029
8796
  }
8030
8797
 
8031
8798
  // packages/cli/dist/commands/clip-cmd.js
8032
- import chalk13 from "chalk";
8033
- import { writeFileSync as writeFileSync17, mkdirSync as mkdirSync17 } from "node:fs";
8034
- import { join as join22 } from "node:path";
8799
+ import chalk14 from "chalk";
8800
+ import { writeFileSync as writeFileSync18, mkdirSync as mkdirSync18 } from "node:fs";
8801
+ import { join as join24 } from "node:path";
8035
8802
  async function clipCommand(url, options) {
8036
8803
  if (!url) {
8037
- console.error(chalk13.red("\u274C Please provide a URL: stellavault clip <url>"));
8804
+ console.error(chalk14.red("\u274C Please provide a URL: stellavault clip <url>"));
8038
8805
  process.exit(1);
8039
8806
  }
8040
8807
  const config = loadConfig();
8041
8808
  const vaultPath = config.vaultPath;
8042
8809
  if (!vaultPath) {
8043
- console.error(chalk13.red("\u274C vaultPath not configured"));
8810
+ console.error(chalk14.red("\u274C vaultPath not configured"));
8044
8811
  process.exit(1);
8045
8812
  }
8046
8813
  const folder = options.folder ?? "06_Research/clips";
8047
- const targetDir = join22(vaultPath, folder);
8048
- mkdirSync17(targetDir, { recursive: true });
8049
- console.error(chalk13.dim(`\u{1F4CE} Clipping: ${url}`));
8814
+ const targetDir = join24(vaultPath, folder);
8815
+ mkdirSync18(targetDir, { recursive: true });
8816
+ console.error(chalk14.dim(`\u{1F4CE} Clipping: ${url}`));
8050
8817
  try {
8051
8818
  const isYouTube = /youtube\.com\/watch|youtu\.be\//.test(url);
8052
8819
  let title;
@@ -8063,7 +8830,7 @@ async function clipCommand(url, options) {
8063
8830
  const safeTitle = title.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, " ").trim().slice(0, 80);
8064
8831
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
8065
8832
  const fileName = `${date} ${safeTitle}.md`;
8066
- const filePath = join22(targetDir, fileName);
8833
+ const filePath = join24(targetDir, fileName);
8067
8834
  const md = [
8068
8835
  "---",
8069
8836
  `title: "${safeTitle}"`,
@@ -8079,12 +8846,12 @@ async function clipCommand(url, options) {
8079
8846
  "",
8080
8847
  content
8081
8848
  ].join("\n");
8082
- writeFileSync17(filePath, md, "utf-8");
8083
- console.log(chalk13.green(`\u2705 Saved: ${fileName}`));
8084
- console.log(chalk13.dim(` \u2192 ${filePath}`));
8085
- console.log(chalk13.dim(" \u{1F4A1} Run stellavault index to make it searchable"));
8849
+ writeFileSync18(filePath, md, "utf-8");
8850
+ console.log(chalk14.green(`\u2705 Saved: ${fileName}`));
8851
+ console.log(chalk14.dim(` \u2192 ${filePath}`));
8852
+ console.log(chalk14.dim(" \u{1F4A1} Run stellavault index to make it searchable"));
8086
8853
  } catch (err) {
8087
- console.error(chalk13.red(`\u274C Clip failed: ${err.message}`));
8854
+ console.error(chalk14.red(`\u274C Clip failed: ${err.message}`));
8088
8855
  process.exit(1);
8089
8856
  }
8090
8857
  }
@@ -8127,7 +8894,7 @@ async function clipYouTube(url) {
8127
8894
  }
8128
8895
 
8129
8896
  // packages/cli/dist/commands/brief-cmd.js
8130
- import chalk14 from "chalk";
8897
+ import chalk15 from "chalk";
8131
8898
  async function briefCommand() {
8132
8899
  const config = loadConfig();
8133
8900
  const hub = createKnowledgeHub(config);
@@ -8135,31 +8902,31 @@ async function briefCommand() {
8135
8902
  await hub.embedder.initialize();
8136
8903
  const db = hub.store.getDb();
8137
8904
  if (!db) {
8138
- console.error(chalk14.red("\u274C Cannot access database"));
8905
+ console.error(chalk15.red("\u274C Cannot access database"));
8139
8906
  process.exit(1);
8140
8907
  }
8141
8908
  const decayEngine = new DecayEngine(db);
8142
8909
  const stats = await hub.store.getStats();
8143
- console.log(chalk14.green("\n\u2600\uFE0F Good morning! Today's knowledge briefing"));
8144
- console.log(chalk14.dim("\u2500".repeat(50)));
8910
+ console.log(chalk15.green("\n\u2600\uFE0F Good morning! Today's knowledge briefing"));
8911
+ console.log(chalk15.dim("\u2500".repeat(50)));
8145
8912
  console.log(`
8146
- \u{1F4DA} ${chalk14.bold(String(stats.documentCount))} notes | ${stats.chunkCount} chunks`);
8913
+ \u{1F4DA} ${chalk15.bold(String(stats.documentCount))} notes | ${stats.chunkCount} chunks`);
8147
8914
  const report = await decayEngine.computeAll();
8148
- const avgRColor = report.averageR >= 0.7 ? chalk14.green : report.averageR >= 0.5 ? chalk14.yellow : chalk14.red;
8149
- console.log(`\u{1F9E0} Overall health: ${avgRColor("R=" + report.averageR)} | Decaying ${chalk14.yellow(String(report.decayingCount))} | Critical ${chalk14.red(String(report.criticalCount))}`);
8915
+ const avgRColor = report.averageR >= 0.7 ? chalk15.green : report.averageR >= 0.5 ? chalk15.yellow : chalk15.red;
8916
+ console.log(`\u{1F9E0} Overall health: ${avgRColor("R=" + report.averageR)} | Decaying ${chalk15.yellow(String(report.decayingCount))} | Critical ${chalk15.red(String(report.criticalCount))}`);
8150
8917
  if (report.topDecaying.length > 0) {
8151
- console.log(chalk14.yellow("\n\u{1F4CB} Review recommendations:"));
8918
+ console.log(chalk15.yellow("\n\u{1F4CB} Review recommendations:"));
8152
8919
  for (const d of report.topDecaying.slice(0, 5)) {
8153
8920
  const bar = "\u2588".repeat(Math.round(d.retrievability * 10)) + "\u2591".repeat(10 - Math.round(d.retrievability * 10));
8154
- console.log(` ${chalk14.dim(bar)} R=${d.retrievability.toFixed(2)} ${d.title}`);
8921
+ console.log(` ${chalk15.dim(bar)} R=${d.retrievability.toFixed(2)} ${d.title}`);
8155
8922
  }
8156
- console.log(chalk14.dim(" \u2192 Run stellavault review to start"));
8923
+ console.log(chalk15.dim(" \u2192 Run stellavault review to start"));
8157
8924
  }
8158
8925
  try {
8159
8926
  const gapReport = await detectKnowledgeGaps(hub.store);
8160
8927
  const highGaps = gapReport.gaps.filter((g) => g.severity === "high");
8161
8928
  if (highGaps.length > 0) {
8162
- console.log(chalk14.yellow(`
8929
+ console.log(chalk15.yellow(`
8163
8930
  \u{1F573}\uFE0F ${highGaps.length} knowledge gaps:`));
8164
8931
  for (const g of highGaps.slice(0, 3)) {
8165
8932
  console.log(` \u{1F534} ${g.clusterA.replace(/\s*\(\d+\)$/, "")} \u2194 ${g.clusterB.replace(/\s*\(\d+\)$/, "")}`);
@@ -8181,7 +8948,7 @@ async function briefCommand() {
8181
8948
  break;
8182
8949
  }
8183
8950
  if (streak > 0)
8184
- console.log(chalk14.yellow(`
8951
+ console.log(chalk15.yellow(`
8185
8952
  \u{1F525} ${streak}-day review streak!`));
8186
8953
  } catch {
8187
8954
  }
@@ -8192,7 +8959,7 @@ async function briefCommand() {
8192
8959
  GROUP BY document_id ORDER BY cnt DESC LIMIT 3
8193
8960
  `).all();
8194
8961
  if (recent.length > 0) {
8195
- console.log(chalk14.dim("\n\u{1F4CA} Most viewed notes this week:"));
8962
+ console.log(chalk15.dim("\n\u{1F4CA} Most viewed notes this week:"));
8196
8963
  for (const r of recent) {
8197
8964
  const doc = db.prepare("SELECT title FROM documents WHERE id = ?").get(r.document_id);
8198
8965
  console.log(` ${r.cnt} views \u2014 ${doc?.title ?? r.document_id}`);
@@ -8200,12 +8967,12 @@ async function briefCommand() {
8200
8967
  }
8201
8968
  } catch {
8202
8969
  }
8203
- console.log("\n" + chalk14.dim("\u2500".repeat(50)));
8204
- console.log(chalk14.dim("\u{1F4A1} stellavault review | stellavault gaps | stellavault graph"));
8970
+ console.log("\n" + chalk15.dim("\u2500".repeat(50)));
8971
+ console.log(chalk15.dim("\u{1F4A1} stellavault review | stellavault gaps | stellavault graph"));
8205
8972
  }
8206
8973
 
8207
8974
  // packages/cli/dist/commands/digest-cmd.js
8208
- import chalk15 from "chalk";
8975
+ import chalk16 from "chalk";
8209
8976
  async function digestCommand(options) {
8210
8977
  const config = loadConfig();
8211
8978
  const hub = createKnowledgeHub(config);
@@ -8213,12 +8980,12 @@ async function digestCommand(options) {
8213
8980
  await hub.store.initialize();
8214
8981
  const db = hub.store.getDb();
8215
8982
  if (!db) {
8216
- console.error(chalk15.red("\u274C Cannot access database"));
8983
+ console.error(chalk16.red("\u274C Cannot access database"));
8217
8984
  process.exit(1);
8218
8985
  }
8219
- console.log(chalk15.green(`
8986
+ console.log(chalk16.green(`
8220
8987
  \u{1F4CA} Knowledge activity report (last ${days} days)`));
8221
- console.log(chalk15.dim("\u2500".repeat(50)));
8988
+ console.log(chalk16.dim("\u2500".repeat(50)));
8222
8989
  const accessStats = db.prepare(`
8223
8990
  SELECT access_type, COUNT(*) as cnt
8224
8991
  FROM access_log WHERE accessed_at > datetime('now', '-${days} days')
@@ -8226,7 +8993,7 @@ async function digestCommand(options) {
8226
8993
  `).all();
8227
8994
  const totalAccess = accessStats.reduce((s, r) => s + r.cnt, 0);
8228
8995
  console.log(`
8229
- \u{1F50D} Total access: ${chalk15.bold(String(totalAccess))} times`);
8996
+ \u{1F50D} Total access: ${chalk16.bold(String(totalAccess))} times`);
8230
8997
  for (const r of accessStats) {
8231
8998
  const icon = r.access_type === "view" ? "\u{1F441}\uFE0F" : r.access_type === "search" ? "\u{1F50D}" : "\u{1F916}";
8232
8999
  console.log(` ${icon} ${r.access_type}: ${r.cnt} times`);
@@ -8240,11 +9007,11 @@ async function digestCommand(options) {
8240
9007
  ORDER BY cnt DESC LIMIT 10
8241
9008
  `).all();
8242
9009
  if (topDocs.length > 0) {
8243
- console.log(chalk15.dim(`
9010
+ console.log(chalk16.dim(`
8244
9011
  \u{1F4C4} Most accessed notes:`));
8245
9012
  for (const d of topDocs) {
8246
9013
  const bar = "\u2588".repeat(Math.min(d.cnt, 20));
8247
- console.log(` ${chalk15.cyan(bar)} ${d.cnt} views ${d.title}`);
9014
+ console.log(` ${chalk16.cyan(bar)} ${d.cnt} views ${d.title}`);
8248
9015
  }
8249
9016
  }
8250
9017
  const dailyActivity = db.prepare(`
@@ -8253,12 +9020,12 @@ async function digestCommand(options) {
8253
9020
  GROUP BY day ORDER BY day
8254
9021
  `).all();
8255
9022
  if (dailyActivity.length > 0) {
8256
- console.log(chalk15.dim("\n\u{1F4C5} Daily activity:"));
9023
+ console.log(chalk16.dim("\n\u{1F4C5} Daily activity:"));
8257
9024
  const maxCnt = Math.max(...dailyActivity.map((d) => d.cnt));
8258
9025
  for (const d of dailyActivity) {
8259
9026
  const barLen = Math.round(d.cnt / maxCnt * 20);
8260
9027
  const bar = "\u2588".repeat(barLen) + "\u2591".repeat(20 - barLen);
8261
- console.log(` ${d.day.slice(5)} ${chalk15.green(bar)} ${d.cnt}`);
9028
+ console.log(` ${d.day.slice(5)} ${chalk16.green(bar)} ${d.cnt}`);
8262
9029
  }
8263
9030
  }
8264
9031
  const typeStats = db.prepare(`
@@ -8269,7 +9036,7 @@ async function digestCommand(options) {
8269
9036
  GROUP BY d.type ORDER BY cnt DESC
8270
9037
  `).all();
8271
9038
  if (typeStats.length > 0) {
8272
- console.log(chalk15.dim("\n\u{1F4CA} Note types accessed:"));
9039
+ console.log(chalk16.dim("\n\u{1F4CA} Note types accessed:"));
8273
9040
  for (const t2 of typeStats) {
8274
9041
  console.log(` ${t2.type}: ${t2.cnt}`);
8275
9042
  }
@@ -8278,16 +9045,16 @@ async function digestCommand(options) {
8278
9045
  const report = await decayEngine.computeAll();
8279
9046
  console.log(`
8280
9047
  \u{1F9E0} Health: R=${report.averageR} | Decaying ${report.decayingCount} | Critical ${report.criticalCount}`);
8281
- console.log(chalk15.dim("\n\u2550".repeat(50)));
9048
+ console.log(chalk16.dim("\n\u2550".repeat(50)));
8282
9049
  if (options.visual) {
8283
- const { writeFileSync: writeFileSync22, mkdirSync: mkdirSync22, existsSync: existsSync25 } = await import("node:fs");
8284
- const { join: join30, resolve: resolve22 } = await import("node:path");
9050
+ const { writeFileSync: writeFileSync23, mkdirSync: mkdirSync23, existsSync: existsSync27 } = await import("node:fs");
9051
+ const { join: join32, resolve: resolve22 } = await import("node:path");
8285
9052
  const outputDir = resolve22(config.vaultPath, "_stellavault/digests");
8286
- if (!existsSync25(outputDir))
8287
- mkdirSync22(outputDir, { recursive: true });
9053
+ if (!existsSync27(outputDir))
9054
+ mkdirSync23(outputDir, { recursive: true });
8288
9055
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
8289
9056
  const filename = `digest-${date}.md`;
8290
- const outputPath = join30(outputDir, filename);
9057
+ const outputPath = join32(outputDir, filename);
8291
9058
  const pieData = (typeStats.length > 0 ? typeStats : [{ type: "note", cnt: 1 }]).map((t2) => ` "${t2.type}" : ${t2.cnt}`).join("\n");
8292
9059
  const timelineData = dailyActivity.map((d) => ` ${d.day.slice(5)} : ${d.cnt}`).join("\n");
8293
9060
  const md = [
@@ -8325,22 +9092,22 @@ async function digestCommand(options) {
8325
9092
  "---",
8326
9093
  `*Generated by \`stellavault digest --visual\` on ${(/* @__PURE__ */ new Date()).toISOString()}*`
8327
9094
  ].filter(Boolean).join("\n");
8328
- writeFileSync22(outputPath, md, "utf-8");
8329
- console.log(chalk15.green(`
9095
+ writeFileSync23(outputPath, md, "utf-8");
9096
+ console.log(chalk16.green(`
8330
9097
  Visual digest saved: ${filename}`));
8331
- console.log(chalk15.dim(`Open in Obsidian to see Mermaid charts.`));
9098
+ console.log(chalk16.dim(`Open in Obsidian to see Mermaid charts.`));
8332
9099
  }
8333
9100
  }
8334
9101
 
8335
9102
  // packages/cli/dist/commands/init-cmd.js
8336
9103
  import { createInterface as createInterface2 } from "node:readline";
8337
- import { existsSync as existsSync18, mkdirSync as mkdirSync18, writeFileSync as writeFileSync18 } from "node:fs";
8338
- import { join as join23 } from "node:path";
8339
- import { homedir as homedir14 } from "node:os";
9104
+ import { existsSync as existsSync20, mkdirSync as mkdirSync19, writeFileSync as writeFileSync19 } from "node:fs";
9105
+ import { join as join25 } from "node:path";
9106
+ import { homedir as homedir16 } from "node:os";
8340
9107
  import ora2 from "ora";
8341
- import chalk16 from "chalk";
9108
+ import chalk17 from "chalk";
8342
9109
  function ask(rl, question, defaultVal) {
8343
- const suffix = defaultVal ? ` ${chalk16.dim(`(${defaultVal})`)}` : "";
9110
+ const suffix = defaultVal ? ` ${chalk17.dim(`(${defaultVal})`)}` : "";
8344
9111
  return new Promise((resolve22) => {
8345
9112
  rl.question(`${question}${suffix}: `, (answer) => {
8346
9113
  resolve22(answer.trim() || defaultVal || "");
@@ -8349,31 +9116,31 @@ function ask(rl, question, defaultVal) {
8349
9116
  }
8350
9117
  async function initCommand() {
8351
9118
  console.log("");
8352
- console.log(chalk16.bold(" \u2726 Stellavault Setup Wizard"));
8353
- console.log(chalk16.dim(" Notes die in folders. Let's bring yours to life.\n"));
9119
+ console.log(chalk17.bold(" \u2726 Stellavault Setup Wizard"));
9120
+ console.log(chalk17.dim(" Notes die in folders. Let's bring yours to life.\n"));
8354
9121
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
8355
9122
  try {
8356
- console.log(chalk16.cyan(" Step 1/3") + " \u2014 Where is your Obsidian vault?");
8357
- console.log(chalk16.dim(" This is the folder containing your .md files.\n"));
9123
+ console.log(chalk17.cyan(" Step 1/3") + " \u2014 Where is your Obsidian vault?");
9124
+ console.log(chalk17.dim(" This is the folder containing your .md files.\n"));
8358
9125
  let vaultPath = "";
8359
9126
  while (!vaultPath) {
8360
9127
  const input = await ask(rl, " Vault path");
8361
9128
  if (!input) {
8362
- console.log(chalk16.yellow(" Please enter your vault path."));
9129
+ console.log(chalk17.yellow(" Please enter your vault path."));
8363
9130
  continue;
8364
9131
  }
8365
- const resolved = input.replace(/^~/, homedir14());
8366
- if (!existsSync18(resolved)) {
8367
- console.log(chalk16.yellow(` Path not found: ${resolved}`));
9132
+ const resolved = input.replace(/^~/, homedir16());
9133
+ if (!existsSync20(resolved)) {
9134
+ console.log(chalk17.yellow(` Path not found: ${resolved}`));
8368
9135
  continue;
8369
9136
  }
8370
9137
  vaultPath = resolved;
8371
9138
  }
8372
- console.log(chalk16.green(` \u2713 Vault: ${vaultPath}
9139
+ console.log(chalk17.green(` \u2713 Vault: ${vaultPath}
8373
9140
  `));
8374
- const configDir = join23(homedir14(), ".stellavault");
8375
- mkdirSync18(configDir, { recursive: true });
8376
- const dbPath = join23(configDir, "index.db");
9141
+ const configDir = join25(homedir16(), ".stellavault");
9142
+ mkdirSync19(configDir, { recursive: true });
9143
+ const dbPath = join25(configDir, "index.db");
8377
9144
  const configData = {
8378
9145
  vaultPath,
8379
9146
  dbPath,
@@ -8382,11 +9149,11 @@ async function initCommand() {
8382
9149
  search: { defaultLimit: 10, rrfK: 60 },
8383
9150
  mcp: { mode: "stdio", port: 3333 }
8384
9151
  };
8385
- writeFileSync18(join23(homedir14(), ".stellavault.json"), JSON.stringify(configData, null, 2), "utf-8");
8386
- console.log(chalk16.dim(` Config saved: ~/.stellavault.json`));
9152
+ writeFileSync19(join25(homedir16(), ".stellavault.json"), JSON.stringify(configData, null, 2), "utf-8");
9153
+ console.log(chalk17.dim(` Config saved: ~/.stellavault.json`));
8387
9154
  console.log("");
8388
- console.log(chalk16.cyan(" Step 2/3") + " \u2014 Indexing your vault");
8389
- console.log(chalk16.dim(" Vectorizing notes with local AI (no data leaves your machine).\n"));
9155
+ console.log(chalk17.cyan(" Step 2/3") + " \u2014 Indexing your vault");
9156
+ console.log(chalk17.dim(" Vectorizing notes with local AI (no data leaves your machine).\n"));
8390
9157
  const spinner = ora2({ text: " Loading embedding model (first run downloads ~30MB, please wait)...", indent: 2 }).start();
8391
9158
  const store = createSqliteVecStore(dbPath);
8392
9159
  await store.initialize();
@@ -8403,11 +9170,11 @@ async function initCommand() {
8403
9170
  spinner.text = ` [${bar}] ${pct}% (${current}/${total}) ${doc.title.slice(0, 30)}`;
8404
9171
  }
8405
9172
  });
8406
- spinner.succeed(chalk16.green(` Indexed ${result.indexed} docs, ${result.totalChunks} chunks (${(result.elapsedMs / 1e3).toFixed(1)}s)`));
9173
+ spinner.succeed(chalk17.green(` Indexed ${result.indexed} docs, ${result.totalChunks} chunks (${(result.elapsedMs / 1e3).toFixed(1)}s)`));
8407
9174
  if (result.indexed === 0) {
8408
- console.log(chalk16.yellow("\n Your vault is empty \u2014 creating 3 starter notes so you can explore.\n"));
8409
- const rawDir = join23(vaultPath, "raw");
8410
- mkdirSync18(rawDir, { recursive: true });
9175
+ console.log(chalk17.yellow("\n Your vault is empty \u2014 creating 3 starter notes so you can explore.\n"));
9176
+ const rawDir = join25(vaultPath, "raw");
9177
+ mkdirSync19(rawDir, { recursive: true });
8411
9178
  const samples = [
8412
9179
  {
8413
9180
  file: "welcome-to-stellavault.md",
@@ -8480,7 +9247,7 @@ Andrej Karpathy's approach: every session auto-compiles into structured knowledg
8480
9247
  }
8481
9248
  ];
8482
9249
  for (const s of samples) {
8483
- writeFileSync18(join23(rawDir, s.file), s.content, "utf-8");
9250
+ writeFileSync19(join25(rawDir, s.file), s.content, "utf-8");
8484
9251
  }
8485
9252
  const reSpinner = ora2({ text: " Indexing sample notes...", indent: 2 }).start();
8486
9253
  const reResult = await indexVault(vaultPath, {
@@ -8488,83 +9255,88 @@ Andrej Karpathy's approach: every session auto-compiles into structured knowledg
8488
9255
  embedder,
8489
9256
  chunkOptions: { maxTokens: 300, overlap: 50, minTokens: 50 }
8490
9257
  });
8491
- reSpinner.succeed(chalk16.green(` Indexed ${reResult.indexed} sample notes, ${reResult.totalChunks} chunks`));
9258
+ reSpinner.succeed(chalk17.green(` Indexed ${reResult.indexed} sample notes, ${reResult.totalChunks} chunks`));
8492
9259
  }
8493
9260
  console.log("");
8494
- console.log(chalk16.cyan(" Step 3/3") + " \u2014 Try your first search");
8495
- console.log(chalk16.dim(" Type a topic you know about. Stellavault finds connections.\n"));
9261
+ console.log(chalk17.cyan(" Step 3/3") + " \u2014 Try your first search");
9262
+ console.log(chalk17.dim(" Type a topic you know about. Stellavault finds connections.\n"));
8496
9263
  const searchEngine = createSearchEngine({ store, embedder, rrfK: 60 });
8497
9264
  let searchDone = false;
8498
9265
  while (!searchDone) {
8499
9266
  const query = await ask(rl, " Search");
8500
9267
  if (!query) {
8501
- console.log(chalk16.dim(" Type something, or press Ctrl+C to skip."));
9268
+ console.log(chalk17.dim(" Type something, or press Ctrl+C to skip."));
8502
9269
  continue;
8503
9270
  }
8504
9271
  const searchSpinner = ora2({ text: " Searching...", indent: 2 }).start();
8505
9272
  const results = await searchEngine.search({ query, limit: 5 });
8506
9273
  searchSpinner.stop();
8507
9274
  if (results.length === 0) {
8508
- console.log(chalk16.yellow(" No results. Try a different topic."));
9275
+ console.log(chalk17.yellow(" No results. Try a different topic."));
8509
9276
  continue;
8510
9277
  }
8511
9278
  console.log("");
8512
9279
  for (const r of results) {
8513
9280
  const score = Math.round(r.score * 100);
8514
- const bar = score >= 70 ? chalk16.green("\u25CF") : score >= 40 ? chalk16.yellow("\u25CF") : chalk16.dim("\u25CF");
8515
- console.log(` ${bar} ${chalk16.bold(r.document.title)} ${chalk16.dim(`(${score}%)`)}`);
9281
+ const bar = score >= 70 ? chalk17.green("\u25CF") : score >= 40 ? chalk17.yellow("\u25CF") : chalk17.dim("\u25CF");
9282
+ console.log(` ${bar} ${chalk17.bold(r.document.title)} ${chalk17.dim(`(${score}%)`)}`);
8516
9283
  if (r.highlights[0]) {
8517
- console.log(` ${chalk16.dim(r.highlights[0].slice(0, 80))}...`);
9284
+ console.log(` ${chalk17.dim(r.highlights[0].slice(0, 80))}...`);
8518
9285
  }
8519
9286
  }
8520
9287
  console.log("");
8521
9288
  searchDone = true;
8522
9289
  }
8523
9290
  await store.close();
8524
- console.log(chalk16.bold.green(" \u2726 Setup complete!\n"));
9291
+ console.log(chalk17.bold.green(" \u2726 Setup complete!\n"));
8525
9292
  console.log(" What's next:");
8526
- console.log(` ${chalk16.cyan("stellavault graph")} Launch 3D knowledge graph`);
8527
- console.log(` ${chalk16.cyan("stellavault decay")} See what knowledge is fading`);
8528
- console.log(` ${chalk16.cyan("stellavault brief")} Get your daily knowledge briefing`);
8529
- console.log(` ${chalk16.cyan("stellavault serve")} Connect AI agents via MCP`);
9293
+ console.log(` ${chalk17.cyan("stellavault graph")} Launch 3D knowledge graph`);
9294
+ console.log(` ${chalk17.cyan("stellavault decay")} See what knowledge is fading`);
9295
+ console.log(` ${chalk17.cyan("stellavault brief")} Get your daily knowledge briefing`);
9296
+ console.log(` ${chalk17.cyan("stellavault serve")} Connect AI agents via MCP`);
8530
9297
  console.log("");
8531
- console.log(chalk16.cyan(" \u2500\u2500\u2500 Build a habit \u2500\u2500\u2500\n"));
9298
+ const doConnect = await ask(rl, " Connect Stellavault to your AI clients now? [Y/n]", "Y");
9299
+ if (doConnect.toLowerCase() !== "n") {
9300
+ const { setupCommand: setupCommand2 } = await Promise.resolve().then(() => (init_setup_cmd(), setup_cmd_exports));
9301
+ await setupCommand2({});
9302
+ }
9303
+ console.log(chalk17.cyan(" \u2500\u2500\u2500 Build a habit \u2500\u2500\u2500\n"));
8532
9304
  const setupCron = await ask(rl, " Auto-run daily briefing? (adds cron job) [Y/n]", "Y");
8533
9305
  if (setupCron.toLowerCase() !== "n") {
8534
9306
  const cronLine = `0 9 * * * cd "${vaultPath}" && stellavault brief >> ~/.stellavault/daily.log 2>&1`;
8535
9307
  const platform = process.platform;
8536
9308
  if (platform === "win32") {
8537
- console.log(chalk16.dim("\n Windows: Add this to Task Scheduler:"));
8538
- console.log(chalk16.dim(` Action: stellavault brief`));
8539
- console.log(chalk16.dim(` Trigger: Daily at 9:00 AM`));
8540
- console.log(chalk16.dim(` Start in: ${vaultPath}
9309
+ console.log(chalk17.dim("\n Windows: Add this to Task Scheduler:"));
9310
+ console.log(chalk17.dim(` Action: stellavault brief`));
9311
+ console.log(chalk17.dim(` Trigger: Daily at 9:00 AM`));
9312
+ console.log(chalk17.dim(` Start in: ${vaultPath}
8541
9313
  `));
8542
9314
  } else {
8543
- console.log(chalk16.dim("\n Add this to your crontab (crontab -e):"));
8544
- console.log(chalk16.dim(` ${cronLine}
9315
+ console.log(chalk17.dim("\n Add this to your crontab (crontab -e):"));
9316
+ console.log(chalk17.dim(` ${cronLine}
8545
9317
  `));
8546
9318
  const autoAdd = await ask(rl, " Add to crontab now? [Y/n]", "Y");
8547
9319
  if (autoAdd.toLowerCase() !== "n") {
8548
9320
  try {
8549
- const { execSync: execSync2 } = await import("node:child_process");
8550
- const existing = execSync2("crontab -l 2>/dev/null || true", { encoding: "utf-8" });
9321
+ const { execSync: execSync3 } = await import("node:child_process");
9322
+ const existing = execSync3("crontab -l 2>/dev/null || true", { encoding: "utf-8" });
8551
9323
  if (!existing.includes("stellavault brief")) {
8552
- execSync2(`(crontab -l 2>/dev/null; echo "${cronLine}") | crontab -`, { encoding: "utf-8" });
8553
- console.log(chalk16.green(" \u2713 Daily briefing scheduled at 9:00 AM\n"));
9324
+ execSync3(`(crontab -l 2>/dev/null; echo "${cronLine}") | crontab -`, { encoding: "utf-8" });
9325
+ console.log(chalk17.green(" \u2713 Daily briefing scheduled at 9:00 AM\n"));
8554
9326
  } else {
8555
- console.log(chalk16.dim(" Already scheduled.\n"));
9327
+ console.log(chalk17.dim(" Already scheduled.\n"));
8556
9328
  }
8557
9329
  } catch {
8558
- console.log(chalk16.yellow(" Could not auto-add. Please add manually.\n"));
9330
+ console.log(chalk17.yellow(" Could not auto-add. Please add manually.\n"));
8559
9331
  }
8560
9332
  }
8561
9333
  }
8562
9334
  }
8563
- console.log(chalk16.dim(" Tomorrow morning, run:"));
8564
- console.log(` ${chalk16.cyan("stellavault brief")} See what changed overnight`);
8565
- console.log(` ${chalk16.cyan("stellavault decay")} Review fading knowledge`);
9335
+ console.log(chalk17.dim(" Tomorrow morning, run:"));
9336
+ console.log(` ${chalk17.cyan("stellavault brief")} See what changed overnight`);
9337
+ console.log(` ${chalk17.cyan("stellavault decay")} Review fading knowledge`);
8566
9338
  console.log("");
8567
- console.log(chalk16.dim(" Your knowledge is now alive. \u2726"));
9339
+ console.log(chalk17.dim(" Your knowledge is now alive. \u2726"));
8568
9340
  console.log("");
8569
9341
  } finally {
8570
9342
  rl.close();
@@ -8572,7 +9344,7 @@ Andrej Karpathy's approach: every session auto-compiles into structured knowledg
8572
9344
  }
8573
9345
 
8574
9346
  // packages/cli/dist/commands/learn-cmd.js
8575
- import chalk17 from "chalk";
9347
+ import chalk18 from "chalk";
8576
9348
  async function learnCommand(_opts, cmd) {
8577
9349
  const globalOpts = cmd?.parent?.opts?.() ?? {};
8578
9350
  const jsonMode = globalOpts.json;
@@ -8582,7 +9354,7 @@ async function learnCommand(_opts, cmd) {
8582
9354
  await hub.embedder.initialize();
8583
9355
  const db = hub.store.getDb();
8584
9356
  if (!db) {
8585
- console.error(chalk17.red("Cannot access database"));
9357
+ console.error(chalk18.red("Cannot access database"));
8586
9358
  process.exit(1);
8587
9359
  }
8588
9360
  const decayEngine = new DecayEngine(db);
@@ -8600,32 +9372,32 @@ async function learnCommand(_opts, cmd) {
8600
9372
  return;
8601
9373
  }
8602
9374
  console.log("");
8603
- console.log(chalk17.bold(" \u{1F3AF} Your Learning Path"));
8604
- console.log(chalk17.dim(` ${path.summary.reviewCount} to review \xB7 ${path.summary.bridgeCount} gaps to bridge \xB7 ~${path.summary.estimatedMinutes}min`));
9375
+ console.log(chalk18.bold(" \u{1F3AF} Your Learning Path"));
9376
+ console.log(chalk18.dim(` ${path.summary.reviewCount} to review \xB7 ${path.summary.bridgeCount} gaps to bridge \xB7 ~${path.summary.estimatedMinutes}min`));
8605
9377
  console.log("");
8606
9378
  for (const item of path.items) {
8607
9379
  const icon = item.category === "review" ? "\u{1F4D6}" : item.category === "bridge" ? "\u{1F309}" : "\u{1F52D}";
8608
- const prioColor = item.priority === "critical" ? chalk17.red : item.priority === "important" ? chalk17.yellow : chalk17.dim;
9380
+ const prioColor = item.priority === "critical" ? chalk18.red : item.priority === "important" ? chalk18.yellow : chalk18.dim;
8609
9381
  const prioLabel = prioColor(item.priority.toUpperCase());
8610
- console.log(` ${icon} ${prioLabel} ${chalk17.bold(item.title)} ${chalk17.dim(`(${item.score}pt)`)}`);
8611
- console.log(` ${chalk17.dim(item.reason)}`);
9382
+ console.log(` ${icon} ${prioLabel} ${chalk18.bold(item.title)} ${chalk18.dim(`(${item.score}pt)`)}`);
9383
+ console.log(` ${chalk18.dim(item.reason)}`);
8612
9384
  }
8613
9385
  if (path.items.length === 0) {
8614
- console.log(chalk17.green(" All clear! Your knowledge is in great shape."));
9386
+ console.log(chalk18.green(" All clear! Your knowledge is in great shape."));
8615
9387
  }
8616
9388
  console.log("");
8617
- console.log(chalk17.dim(" \u{1F4A1} stellavault review \u2014 start reviewing decaying notes"));
9389
+ console.log(chalk18.dim(" \u{1F4A1} stellavault review \u2014 start reviewing decaying notes"));
8618
9390
  console.log("");
8619
9391
  }
8620
9392
 
8621
9393
  // packages/cli/dist/commands/contradictions-cmd.js
8622
- import chalk18 from "chalk";
9394
+ import chalk19 from "chalk";
8623
9395
  async function contradictionsCommand(_opts, cmd) {
8624
9396
  const globalOpts = cmd?.parent?.opts?.() ?? {};
8625
9397
  const jsonMode = globalOpts.json;
8626
9398
  const config = loadConfig();
8627
9399
  const hub = createKnowledgeHub(config);
8628
- console.error(chalk18.dim("Scanning for contradictions..."));
9400
+ console.error(chalk19.dim("Scanning for contradictions..."));
8629
9401
  await hub.store.initialize();
8630
9402
  await hub.embedder.initialize();
8631
9403
  const pairs = await detectContradictions(hub.store, 20);
@@ -8635,42 +9407,42 @@ async function contradictionsCommand(_opts, cmd) {
8635
9407
  return;
8636
9408
  }
8637
9409
  console.log("");
8638
- console.log(chalk18.bold(` \u26A1 ${pairs.length} potential contradictions found`));
9410
+ console.log(chalk19.bold(` \u26A1 ${pairs.length} potential contradictions found`));
8639
9411
  console.log("");
8640
9412
  for (const p of pairs) {
8641
- const confColor = p.confidence >= 0.8 ? chalk18.red : p.confidence >= 0.6 ? chalk18.yellow : chalk18.dim;
8642
- console.log(` ${confColor(`${Math.round(p.confidence * 100)}%`)} ${chalk18.dim(`[${p.type}]`)} ${chalk18.bold(p.docA.title)} vs ${chalk18.bold(p.docB.title)}`);
8643
- console.log(` A: ${chalk18.dim(p.docA.statement.slice(0, 80))}`);
8644
- console.log(` B: ${chalk18.dim(p.docB.statement.slice(0, 80))}`);
9413
+ const confColor = p.confidence >= 0.8 ? chalk19.red : p.confidence >= 0.6 ? chalk19.yellow : chalk19.dim;
9414
+ console.log(` ${confColor(`${Math.round(p.confidence * 100)}%`)} ${chalk19.dim(`[${p.type}]`)} ${chalk19.bold(p.docA.title)} vs ${chalk19.bold(p.docB.title)}`);
9415
+ console.log(` A: ${chalk19.dim(p.docA.statement.slice(0, 80))}`);
9416
+ console.log(` B: ${chalk19.dim(p.docB.statement.slice(0, 80))}`);
8645
9417
  console.log("");
8646
9418
  }
8647
9419
  if (pairs.length === 0) {
8648
- console.log(chalk18.green(" No contradictions detected. Your knowledge is consistent!"));
9420
+ console.log(chalk19.green(" No contradictions detected. Your knowledge is consistent!"));
8649
9421
  console.log("");
8650
9422
  }
8651
9423
  }
8652
9424
 
8653
9425
  // packages/cli/dist/commands/federate-cmd.js
8654
9426
  import { createInterface as createInterface3 } from "node:readline";
8655
- import chalk19 from "chalk";
9427
+ import chalk20 from "chalk";
8656
9428
  async function federateJoinCommand(options) {
8657
9429
  if (!isFederationExperimentalEnabled()) {
8658
9430
  console.log("");
8659
- console.log(chalk19.red(" \u2726 Federation is experimental and disabled by default."));
9431
+ console.log(chalk20.red(" \u2726 Federation is experimental and disabled by default."));
8660
9432
  console.log("");
8661
- console.log(chalk19.dim(" Enable it by setting an environment variable:"));
8662
- console.log(chalk19.dim(' PowerShell: $env:STELLAVAULT_FEDERATION_EXPERIMENTAL = "1"'));
8663
- console.log(chalk19.dim(" bash/zsh: export STELLAVAULT_FEDERATION_EXPERIMENTAL=1"));
9433
+ console.log(chalk20.dim(" Enable it by setting an environment variable:"));
9434
+ console.log(chalk20.dim(' PowerShell: $env:STELLAVAULT_FEDERATION_EXPERIMENTAL = "1"'));
9435
+ console.log(chalk20.dim(" bash/zsh: export STELLAVAULT_FEDERATION_EXPERIMENTAL=1"));
8664
9436
  console.log("");
8665
- console.log(chalk19.dim(" Then re-run `stellavault federate join`."));
9437
+ console.log(chalk20.dim(" Then re-run `stellavault federate join`."));
8666
9438
  console.log("");
8667
9439
  process.exit(2);
8668
9440
  }
8669
9441
  const config = loadConfig();
8670
9442
  const identity = getOrCreateIdentity(options.name);
8671
9443
  console.log("");
8672
- console.log(chalk19.bold(" \u2726 Stellavault Federation") + chalk19.yellow(" (experimental)"));
8673
- console.log(chalk19.dim(` Node: ${identity.displayName} (${identity.peerId})`));
9444
+ console.log(chalk20.bold(" \u2726 Stellavault Federation") + chalk20.yellow(" (experimental)"));
9445
+ console.log(chalk20.dim(` Node: ${identity.displayName} (${identity.peerId})`));
8674
9446
  console.log("");
8675
9447
  const store = createSqliteVecStore(config.dbPath);
8676
9448
  await store.initialize();
@@ -8684,28 +9456,28 @@ async function federateJoinCommand(options) {
8684
9456
  search.startResponder();
8685
9457
  const sharingCfg = loadSharingConfig();
8686
9458
  if (sharingCfg.myNodeLevel === 0) {
8687
- console.log(chalk19.yellow(" \u26A0 Receive-only mode (my node level = 0)."));
8688
- console.log(chalk19.dim(" Run `set-level 1` or higher in the federation prompt to share."));
9459
+ console.log(chalk20.yellow(" \u26A0 Receive-only mode (my node level = 0)."));
9460
+ console.log(chalk20.dim(" Run `set-level 1` or higher in the federation prompt to share."));
8689
9461
  } else {
8690
- console.log(chalk19.dim(` Sharing level: ${sharingCfg.myNodeLevel} (set-level <0-4> to change)`));
9462
+ console.log(chalk20.dim(` Sharing level: ${sharingCfg.myNodeLevel} (set-level <0-4> to change)`));
8691
9463
  }
8692
9464
  node.on("joined", (info) => {
8693
- console.log(chalk19.green(` \u2726 Joined federation network`));
8694
- console.log(chalk19.dim(` Topic: ${info.topic}`));
8695
- console.log(chalk19.dim(` Waiting for peers...
9465
+ console.log(chalk20.green(` \u2726 Joined federation network`));
9466
+ console.log(chalk20.dim(` Topic: ${info.topic}`));
9467
+ console.log(chalk20.dim(` Waiting for peers...
8696
9468
  `));
8697
9469
  });
8698
9470
  node.on("peer_joined", (peer) => {
8699
- console.log(chalk19.cyan(` \u2192 Peer found: ${peer.displayName} (${peer.documentCount} docs) [${peer.peerId}]`));
9471
+ console.log(chalk20.cyan(` \u2192 Peer found: ${peer.displayName} (${peer.documentCount} docs) [${peer.peerId}]`));
8700
9472
  });
8701
9473
  node.on("peer_left", (info) => {
8702
- console.log(chalk19.yellow(` \u2190 Peer left: ${info.peerId}`));
9474
+ console.log(chalk20.yellow(` \u2190 Peer left: ${info.peerId}`));
8703
9475
  });
8704
9476
  node.on("search_request", () => {
8705
9477
  });
8706
9478
  await node.join();
8707
9479
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
8708
- const prompt = () => rl.question(chalk19.dim("federation> "), handleInput);
9480
+ const prompt = () => rl.question(chalk20.dim("federation> "), handleInput);
8709
9481
  async function handleInput(line) {
8710
9482
  const parts = line.trim().split(/\s+/);
8711
9483
  const cmd = parts[0];
@@ -8713,23 +9485,23 @@ async function federateJoinCommand(options) {
8713
9485
  case "search": {
8714
9486
  const query = parts.slice(1).join(" ");
8715
9487
  if (!query) {
8716
- console.log(chalk19.yellow(" Usage: search <query>"));
9488
+ console.log(chalk20.yellow(" Usage: search <query>"));
8717
9489
  break;
8718
9490
  }
8719
9491
  const start = Date.now();
8720
- console.log(chalk19.dim(` Searching ${node.peerCount} peers...`));
9492
+ console.log(chalk20.dim(` Searching ${node.peerCount} peers...`));
8721
9493
  const results = await search.search(query, { limit: 5, timeout: 5e3 });
8722
9494
  const elapsed = Date.now() - start;
8723
9495
  if (results.length === 0) {
8724
- console.log(chalk19.yellow(` No results from peers. (${elapsed}ms)`));
9496
+ console.log(chalk20.yellow(` No results from peers. (${elapsed}ms)`));
8725
9497
  } else {
8726
9498
  console.log("");
8727
9499
  for (const r of results) {
8728
- const simColor = r.similarity >= 0.7 ? chalk19.green : r.similarity >= 0.4 ? chalk19.yellow : chalk19.dim;
8729
- console.log(` ${simColor(`${Math.round(r.similarity * 100)}%`)} ${chalk19.bold(r.title)} ${chalk19.dim(`[${r.peerName}]`)}`);
8730
- console.log(` ${chalk19.dim(r.snippet)}...`);
9500
+ const simColor = r.similarity >= 0.7 ? chalk20.green : r.similarity >= 0.4 ? chalk20.yellow : chalk20.dim;
9501
+ console.log(` ${simColor(`${Math.round(r.similarity * 100)}%`)} ${chalk20.bold(r.title)} ${chalk20.dim(`[${r.peerName}]`)}`);
9502
+ console.log(` ${chalk20.dim(r.snippet)}...`);
8731
9503
  }
8732
- console.log(chalk19.dim(`
9504
+ console.log(chalk20.dim(`
8733
9505
  ${results.length} results from ${new Set(results.map((r) => r.peerId)).size} peers (${elapsed}ms)`));
8734
9506
  }
8735
9507
  break;
@@ -8737,46 +9509,46 @@ async function federateJoinCommand(options) {
8737
9509
  case "peers": {
8738
9510
  const peers = node.getPeers();
8739
9511
  if (peers.length === 0) {
8740
- console.log(chalk19.yellow(" No peers connected"));
9512
+ console.log(chalk20.yellow(" No peers connected"));
8741
9513
  } else {
8742
9514
  console.log("");
8743
9515
  for (const p of peers) {
8744
- console.log(` ${chalk19.cyan(p.displayName)} ${chalk19.dim(`(${p.documentCount} docs)`)} [${p.peerId}]`);
9516
+ console.log(` ${chalk20.cyan(p.displayName)} ${chalk20.dim(`(${p.documentCount} docs)`)} [${p.peerId}]`);
8745
9517
  if (p.topTopics.length > 0) {
8746
- console.log(` ${chalk19.dim(p.topTopics.map((t2) => `#${t2}`).join(" "))}`);
9518
+ console.log(` ${chalk20.dim(p.topTopics.map((t2) => `#${t2}`).join(" "))}`);
8747
9519
  }
8748
9520
  }
8749
- console.log(chalk19.dim(`
9521
+ console.log(chalk20.dim(`
8750
9522
  ${peers.length} peer(s) connected`));
8751
9523
  }
8752
9524
  break;
8753
9525
  }
8754
9526
  case "status": {
8755
9527
  console.log("");
8756
- console.log(` ${chalk19.bold("Node:")} ${identity.displayName} (${identity.peerId})`);
8757
- console.log(` ${chalk19.bold("Docs:")} ${stats.documentCount}`);
8758
- console.log(` ${chalk19.bold("Peers:")} ${node.peerCount}`);
8759
- console.log(` ${chalk19.bold("Running:")} ${node.isRunning ? chalk19.green("yes") : chalk19.red("no")}`);
9528
+ console.log(` ${chalk20.bold("Node:")} ${identity.displayName} (${identity.peerId})`);
9529
+ console.log(` ${chalk20.bold("Docs:")} ${stats.documentCount}`);
9530
+ console.log(` ${chalk20.bold("Peers:")} ${node.peerCount}`);
9531
+ console.log(` ${chalk20.bold("Running:")} ${node.isRunning ? chalk20.green("yes") : chalk20.red("no")}`);
8760
9532
  break;
8761
9533
  }
8762
9534
  case "connect": {
8763
9535
  const addr = parts[1];
8764
9536
  if (!addr || !addr.includes(":")) {
8765
- console.log(chalk19.yellow(" Usage: connect <host:port>"));
9537
+ console.log(chalk20.yellow(" Usage: connect <host:port>"));
8766
9538
  break;
8767
9539
  }
8768
9540
  const [host, portStr] = addr.split(":");
8769
9541
  try {
8770
- console.log(chalk19.dim(` Connecting to ${addr}...`));
9542
+ console.log(chalk20.dim(` Connecting to ${addr}...`));
8771
9543
  await node.joinDirect(host, parseInt(portStr, 10));
8772
- console.log(chalk19.green(` Connected to ${addr}`));
9544
+ console.log(chalk20.green(` Connected to ${addr}`));
8773
9545
  } catch (err) {
8774
- console.log(chalk19.red(` Failed: ${err instanceof Error ? err.message : err}`));
9546
+ console.log(chalk20.red(` Failed: ${err instanceof Error ? err.message : err}`));
8775
9547
  }
8776
9548
  break;
8777
9549
  }
8778
9550
  case "sharing": {
8779
- console.log("\n" + chalk19.bold(" Sharing Settings"));
9551
+ console.log("\n" + chalk20.bold(" Sharing Settings"));
8780
9552
  console.log(" " + getSharingSummary().split("\n").join("\n "));
8781
9553
  console.log("");
8782
9554
  break;
@@ -8785,38 +9557,38 @@ async function federateJoinCommand(options) {
8785
9557
  const tag = parts[1];
8786
9558
  const lvl = parseInt(parts[2], 10);
8787
9559
  if (!tag || isNaN(lvl) || lvl < 0 || lvl > 4) {
8788
- console.log(chalk19.yellow(" Usage: set-tag <tag> <0-4>"));
9560
+ console.log(chalk20.yellow(" Usage: set-tag <tag> <0-4>"));
8789
9561
  break;
8790
9562
  }
8791
9563
  setTagLevel(tag, lvl);
8792
- console.log(chalk19.green(` #${tag} \u2192 Level ${lvl}`));
9564
+ console.log(chalk20.green(` #${tag} \u2192 Level ${lvl}`));
8793
9565
  break;
8794
9566
  }
8795
9567
  case "set-folder": {
8796
9568
  const folder = parts[1];
8797
9569
  const lvl = parseInt(parts[2], 10);
8798
9570
  if (!folder || isNaN(lvl) || lvl < 0 || lvl > 4) {
8799
- console.log(chalk19.yellow(" Usage: set-folder <folder> <0-4>"));
9571
+ console.log(chalk20.yellow(" Usage: set-folder <folder> <0-4>"));
8800
9572
  break;
8801
9573
  }
8802
9574
  setFolderLevel(folder, lvl);
8803
- console.log(chalk19.green(` ${folder} \u2192 Level ${lvl}`));
9575
+ console.log(chalk20.green(` ${folder} \u2192 Level ${lvl}`));
8804
9576
  break;
8805
9577
  }
8806
9578
  case "set-level": {
8807
9579
  const lvl = parseInt(parts[1], 10);
8808
9580
  if (isNaN(lvl) || lvl < 0 || lvl > 4) {
8809
- console.log(chalk19.yellow(" Usage: set-level <0-4> (your node sharing level)"));
9581
+ console.log(chalk20.yellow(" Usage: set-level <0-4> (your node sharing level)"));
8810
9582
  break;
8811
9583
  }
8812
9584
  setNodeLevel(lvl);
8813
- console.log(chalk19.green(` My node level \u2192 ${lvl}`));
9585
+ console.log(chalk20.green(` My node level \u2192 ${lvl}`));
8814
9586
  break;
8815
9587
  }
8816
9588
  case "requests": {
8817
9589
  const pending = getPendingRequests();
8818
9590
  if (pending.length === 0) {
8819
- console.log(chalk19.dim(" No pending requests"));
9591
+ console.log(chalk20.dim(" No pending requests"));
8820
9592
  break;
8821
9593
  }
8822
9594
  console.log(`
@@ -8829,33 +9601,33 @@ async function federateJoinCommand(options) {
8829
9601
  case "approve": {
8830
9602
  const reqId = parts[1];
8831
9603
  if (!reqId) {
8832
- console.log(chalk19.yellow(" Usage: approve <request-id>"));
9604
+ console.log(chalk20.yellow(" Usage: approve <request-id>"));
8833
9605
  break;
8834
9606
  }
8835
9607
  const match = getPendingRequests().find((r) => r.requestId.startsWith(reqId));
8836
9608
  if (match && approveRequest(match.requestId))
8837
- console.log(chalk19.green(` Approved: ${match.documentTitle}`));
9609
+ console.log(chalk20.green(` Approved: ${match.documentTitle}`));
8838
9610
  else
8839
- console.log(chalk19.red(" Request not found"));
9611
+ console.log(chalk20.red(" Request not found"));
8840
9612
  break;
8841
9613
  }
8842
9614
  case "deny": {
8843
9615
  const reqId = parts[1];
8844
9616
  if (!reqId) {
8845
- console.log(chalk19.yellow(" Usage: deny <request-id>"));
9617
+ console.log(chalk20.yellow(" Usage: deny <request-id>"));
8846
9618
  break;
8847
9619
  }
8848
9620
  const match = getPendingRequests().find((r) => r.requestId.startsWith(reqId));
8849
9621
  if (match && denyRequest(match.requestId))
8850
- console.log(chalk19.green(` Denied: ${match.documentTitle}`));
9622
+ console.log(chalk20.green(` Denied: ${match.documentTitle}`));
8851
9623
  else
8852
- console.log(chalk19.red(" Request not found"));
9624
+ console.log(chalk20.red(" Request not found"));
8853
9625
  break;
8854
9626
  }
8855
9627
  case "leave":
8856
9628
  case "quit":
8857
9629
  case "exit": {
8858
- console.log(chalk19.dim(" Leaving federation..."));
9630
+ console.log(chalk20.dim(" Leaving federation..."));
8859
9631
  await node.leave();
8860
9632
  await store.close();
8861
9633
  rl.close();
@@ -8865,30 +9637,30 @@ async function federateJoinCommand(options) {
8865
9637
  case "help": {
8866
9638
  console.log("");
8867
9639
  console.log(" Commands:");
8868
- console.log(` ${chalk19.cyan("search <query>")} Search across all connected peers`);
8869
- console.log(` ${chalk19.cyan("peers")} List connected peers`);
8870
- console.log(` ${chalk19.cyan("status")} Show node info`);
8871
- console.log(` ${chalk19.cyan("connect <ip:port>")} Connect to peer directly`);
8872
- console.log(` ${chalk19.cyan("sharing")} Show sharing settings`);
8873
- console.log(` ${chalk19.cyan("set-tag <t> <0-4>")} Set tag sharing level`);
8874
- console.log(` ${chalk19.cyan("set-folder <f> <0-4>")} Set folder sharing level`);
8875
- console.log(` ${chalk19.cyan("set-level <0-4>")} Set your node level`);
8876
- console.log(` ${chalk19.cyan("requests")} Show pending full-text requests`);
8877
- console.log(` ${chalk19.cyan("approve <id>")} Approve a request`);
8878
- console.log(` ${chalk19.cyan("deny <id>")} Deny a request`);
8879
- console.log(` ${chalk19.cyan("leave")} Disconnect and exit`);
9640
+ console.log(` ${chalk20.cyan("search <query>")} Search across all connected peers`);
9641
+ console.log(` ${chalk20.cyan("peers")} List connected peers`);
9642
+ console.log(` ${chalk20.cyan("status")} Show node info`);
9643
+ console.log(` ${chalk20.cyan("connect <ip:port>")} Connect to peer directly`);
9644
+ console.log(` ${chalk20.cyan("sharing")} Show sharing settings`);
9645
+ console.log(` ${chalk20.cyan("set-tag <t> <0-4>")} Set tag sharing level`);
9646
+ console.log(` ${chalk20.cyan("set-folder <f> <0-4>")} Set folder sharing level`);
9647
+ console.log(` ${chalk20.cyan("set-level <0-4>")} Set your node level`);
9648
+ console.log(` ${chalk20.cyan("requests")} Show pending full-text requests`);
9649
+ console.log(` ${chalk20.cyan("approve <id>")} Approve a request`);
9650
+ console.log(` ${chalk20.cyan("deny <id>")} Deny a request`);
9651
+ console.log(` ${chalk20.cyan("leave")} Disconnect and exit`);
8880
9652
  break;
8881
9653
  }
8882
9654
  default: {
8883
9655
  if (cmd)
8884
- console.log(chalk19.dim(` Unknown command: ${cmd}. Type 'help' for commands.`));
9656
+ console.log(chalk20.dim(` Unknown command: ${cmd}. Type 'help' for commands.`));
8885
9657
  break;
8886
9658
  }
8887
9659
  }
8888
9660
  prompt();
8889
9661
  }
8890
9662
  process.on("SIGINT", async () => {
8891
- console.log(chalk19.dim("\n Leaving federation..."));
9663
+ console.log(chalk20.dim("\n Leaving federation..."));
8892
9664
  await node.leave();
8893
9665
  await store.close();
8894
9666
  process.exit(0);
@@ -8899,7 +9671,7 @@ async function federateStatusCommand() {
8899
9671
  const identity = getOrCreateIdentity();
8900
9672
  const config = loadConfig();
8901
9673
  console.log("");
8902
- console.log(chalk19.bold(" \u2726 Federation Identity"));
9674
+ console.log(chalk20.bold(" \u2726 Federation Identity"));
8903
9675
  console.log(` PeerID: ${identity.peerId}`);
8904
9676
  console.log(` Name: ${identity.displayName}`);
8905
9677
  console.log(` Since: ${identity.createdAt}`);
@@ -8908,7 +9680,7 @@ async function federateStatusCommand() {
8908
9680
  }
8909
9681
 
8910
9682
  // packages/cli/dist/commands/cloud-cmd.js
8911
- import chalk20 from "chalk";
9683
+ import chalk21 from "chalk";
8912
9684
  function getCloudConfig() {
8913
9685
  const endpoint = process.env.SV_CLOUD_ENDPOINT;
8914
9686
  const bucket = process.env.SV_CLOUD_BUCKET ?? "stellavault";
@@ -8922,22 +9694,22 @@ function getCloudConfig() {
8922
9694
  async function cloudSyncCommand() {
8923
9695
  const cloudConfig = getCloudConfig();
8924
9696
  if (!cloudConfig) {
8925
- console.log(chalk20.red("\n Cloud not configured. Set environment variables:"));
8926
- console.log(chalk20.dim(" SV_CLOUD_ENDPOINT=https://xxx.r2.cloudflarestorage.com"));
8927
- console.log(chalk20.dim(" SV_CLOUD_SECRET_KEY=your_api_token"));
8928
- console.log(chalk20.dim(" SV_CLOUD_ENCRYPTION_KEY=your_passphrase (optional)\n"));
9697
+ console.log(chalk21.red("\n Cloud not configured. Set environment variables:"));
9698
+ console.log(chalk21.dim(" SV_CLOUD_ENDPOINT=https://xxx.r2.cloudflarestorage.com"));
9699
+ console.log(chalk21.dim(" SV_CLOUD_SECRET_KEY=your_api_token"));
9700
+ console.log(chalk21.dim(" SV_CLOUD_ENCRYPTION_KEY=your_passphrase (optional)\n"));
8929
9701
  return;
8930
9702
  }
8931
9703
  const config = loadConfig();
8932
- console.log(chalk20.dim("\n Encrypting and uploading..."));
9704
+ console.log(chalk21.dim("\n Encrypting and uploading..."));
8933
9705
  const result = await syncToCloud(config.dbPath, cloudConfig);
8934
9706
  if (result.success) {
8935
- console.log(chalk20.green("\n \u2705 Cloud sync complete"));
9707
+ console.log(chalk21.green("\n \u2705 Cloud sync complete"));
8936
9708
  console.log(` DB: ${(result.dbSize / 1024).toFixed(0)}KB \u2192 Encrypted: ${(result.encryptedSize / 1024).toFixed(0)}KB`);
8937
- console.log(chalk20.dim(` ${result.timestamp}
9709
+ console.log(chalk21.dim(` ${result.timestamp}
8938
9710
  `));
8939
9711
  } else {
8940
- console.log(chalk20.red(`
9712
+ console.log(chalk21.red(`
8941
9713
  \u274C Sync failed: ${result.error}
8942
9714
  `));
8943
9715
  }
@@ -8945,18 +9717,18 @@ async function cloudSyncCommand() {
8945
9717
  async function cloudRestoreCommand() {
8946
9718
  const cloudConfig = getCloudConfig();
8947
9719
  if (!cloudConfig) {
8948
- console.log(chalk20.red("\n Cloud not configured. See: sv cloud sync --help\n"));
9720
+ console.log(chalk21.red("\n Cloud not configured. See: sv cloud sync --help\n"));
8949
9721
  return;
8950
9722
  }
8951
9723
  const config = loadConfig();
8952
- console.log(chalk20.dim("\n Downloading and decrypting..."));
9724
+ console.log(chalk21.dim("\n Downloading and decrypting..."));
8953
9725
  const result = await restoreFromCloud(config.dbPath, cloudConfig);
8954
9726
  if (result.success) {
8955
- console.log(chalk20.green("\n \u2705 Restore complete"));
9727
+ console.log(chalk21.green("\n \u2705 Restore complete"));
8956
9728
  console.log(` Encrypted: ${(result.encryptedSize / 1024).toFixed(0)}KB \u2192 DB: ${(result.dbSize / 1024).toFixed(0)}KB`);
8957
- console.log(chalk20.dim(" Previous DB backed up as .backup\n"));
9729
+ console.log(chalk21.dim(" Previous DB backed up as .backup\n"));
8958
9730
  } else {
8959
- console.log(chalk20.red(`
9731
+ console.log(chalk21.red(`
8960
9732
  \u274C Restore failed: ${result.error}
8961
9733
  `));
8962
9734
  }
@@ -8964,29 +9736,29 @@ async function cloudRestoreCommand() {
8964
9736
  async function cloudStatusCommand() {
8965
9737
  const state = getSyncState();
8966
9738
  if (!state) {
8967
- console.log(chalk20.yellow("\n No cloud sync history. Run: sv cloud sync\n"));
9739
+ console.log(chalk21.yellow("\n No cloud sync history. Run: sv cloud sync\n"));
8968
9740
  return;
8969
9741
  }
8970
- console.log(chalk20.bold("\n \u2601\uFE0F Cloud Sync Status"));
9742
+ console.log(chalk21.bold("\n \u2601\uFE0F Cloud Sync Status"));
8971
9743
  console.log(` Last sync: ${state.lastSync}`);
8972
9744
  console.log(` DB size: ${(state.dbSize / 1024).toFixed(0)}KB
8973
9745
  `);
8974
9746
  }
8975
9747
 
8976
9748
  // packages/cli/dist/commands/vault-cmd.js
8977
- import chalk21 from "chalk";
9749
+ import chalk22 from "chalk";
8978
9750
  async function vaultAddCommand(id, vaultPath, options) {
8979
9751
  const config = loadConfig();
8980
9752
  const dbPath = vaultPath.replace(/\/$/, "") + "/.stellavault/index.db";
8981
9753
  try {
8982
9754
  const entry = addVault(id, options.name ?? id, vaultPath, dbPath, !!options.shared);
8983
- console.log(chalk21.green(`
9755
+ console.log(chalk22.green(`
8984
9756
  \u2705 Vault "${entry.name}" added (${entry.id})`));
8985
- console.log(chalk21.dim(` Path: ${entry.path}
9757
+ console.log(chalk22.dim(` Path: ${entry.path}
8986
9758
  DB: ${entry.dbPath}
8987
9759
  `));
8988
9760
  } catch (err) {
8989
- console.log(chalk21.red(`
9761
+ console.log(chalk22.red(`
8990
9762
  \u274C ${err instanceof Error ? err.message : err}
8991
9763
  `));
8992
9764
  }
@@ -8994,22 +9766,22 @@ async function vaultAddCommand(id, vaultPath, options) {
8994
9766
  async function vaultListCommand() {
8995
9767
  const vaults = listVaults();
8996
9768
  if (vaults.length === 0) {
8997
- console.log(chalk21.yellow("\n No vaults registered. Use: sv vault add <id> <path>\n"));
9769
+ console.log(chalk22.yellow("\n No vaults registered. Use: sv vault add <id> <path>\n"));
8998
9770
  return;
8999
9771
  }
9000
- console.log(chalk21.bold("\n Registered Vaults"));
9772
+ console.log(chalk22.bold("\n Registered Vaults"));
9001
9773
  for (const v of vaults) {
9002
- console.log(` ${chalk21.cyan(v.id)} ${v.name} ${chalk21.dim(`(${v.path})`)}`);
9774
+ console.log(` ${chalk22.cyan(v.id)} ${v.name} ${chalk22.dim(`(${v.path})`)}`);
9003
9775
  }
9004
9776
  console.log("");
9005
9777
  }
9006
9778
  async function vaultRemoveCommand(id) {
9007
9779
  if (removeVault(id)) {
9008
- console.log(chalk21.green(`
9780
+ console.log(chalk22.green(`
9009
9781
  \u2705 Vault "${id}" removed
9010
9782
  `));
9011
9783
  } else {
9012
- console.log(chalk21.red(`
9784
+ console.log(chalk22.red(`
9013
9785
  \u274C Vault "${id}" not found
9014
9786
  `));
9015
9787
  }
@@ -9018,33 +9790,33 @@ async function vaultSearchAllCommand(query, options) {
9018
9790
  const config = loadConfig();
9019
9791
  const embedder = createLocalEmbedder(config.embedding.localModel);
9020
9792
  await embedder.initialize();
9021
- console.log(chalk21.dim(`
9793
+ console.log(chalk22.dim(`
9022
9794
  Searching all vaults for "${query}"...`));
9023
9795
  const results = await searchAllVaults(query, embedder, (dbPath) => createSqliteVecStore(dbPath), { limit: parseInt(options.limit ?? "10", 10) });
9024
9796
  if (results.length === 0) {
9025
- console.log(chalk21.yellow(" No results across vaults.\n"));
9797
+ console.log(chalk22.yellow(" No results across vaults.\n"));
9026
9798
  return;
9027
9799
  }
9028
9800
  for (const r of results) {
9029
9801
  const pct = Math.round(r.score * 100);
9030
- const color = pct >= 70 ? chalk21.green : pct >= 40 ? chalk21.yellow : chalk21.dim;
9031
- console.log(` ${color(`${pct}%`)} ${chalk21.bold(r.title)} ${chalk21.dim(`[${r.vaultName}]`)}`);
9032
- console.log(` ${chalk21.dim(r.snippet)}...`);
9802
+ const color = pct >= 70 ? chalk22.green : pct >= 40 ? chalk22.yellow : chalk22.dim;
9803
+ console.log(` ${color(`${pct}%`)} ${chalk22.bold(r.title)} ${chalk22.dim(`[${r.vaultName}]`)}`);
9804
+ console.log(` ${chalk22.dim(r.snippet)}...`);
9033
9805
  }
9034
9806
  console.log("");
9035
9807
  }
9036
9808
 
9037
9809
  // packages/cli/dist/commands/capture-cmd.js
9038
- import chalk22 from "chalk";
9810
+ import chalk23 from "chalk";
9039
9811
  async function captureCommand(audioFile, options) {
9040
9812
  if (!isWhisperAvailable()) {
9041
- console.log(chalk22.red("\n Whisper not installed."));
9042
- console.log(chalk22.dim(" Install: pip install openai-whisper"));
9043
- console.log(chalk22.dim(" Or: brew install whisper-cpp\n"));
9813
+ console.log(chalk23.red("\n Whisper not installed."));
9814
+ console.log(chalk23.dim(" Install: pip install openai-whisper"));
9815
+ console.log(chalk23.dim(" Or: brew install whisper-cpp\n"));
9044
9816
  return;
9045
9817
  }
9046
9818
  const config = loadConfig();
9047
- console.log(chalk22.dim(`
9819
+ console.log(chalk23.dim(`
9048
9820
  Transcribing ${audioFile}...`));
9049
9821
  const result = await captureVoice(audioFile, {
9050
9822
  vaultPath: config.vaultPath,
@@ -9054,31 +9826,31 @@ async function captureCommand(audioFile, options) {
9054
9826
  folder: options.folder
9055
9827
  });
9056
9828
  if (result.success) {
9057
- console.log(chalk22.green(`
9829
+ console.log(chalk23.green(`
9058
9830
  \u2705 Captured: "${result.title}"`));
9059
9831
  console.log(` Tags: ${result.tags.join(", ")}`);
9060
9832
  console.log(` File: ${result.filePath}`);
9061
- console.log(chalk22.dim(` Transcript: ${result.transcript.slice(0, 100)}...`));
9062
- console.log(chalk22.dim("\n \u{1F4A1} Run stellavault index to add to the graph\n"));
9833
+ console.log(chalk23.dim(` Transcript: ${result.transcript.slice(0, 100)}...`));
9834
+ console.log(chalk23.dim("\n \u{1F4A1} Run stellavault index to add to the graph\n"));
9063
9835
  } else {
9064
- console.log(chalk22.red(`
9836
+ console.log(chalk23.red(`
9065
9837
  \u274C Capture failed: ${result.error}
9066
9838
  `));
9067
9839
  }
9068
9840
  }
9069
9841
 
9070
9842
  // packages/cli/dist/commands/ask-cmd.js
9071
- import chalk23 from "chalk";
9843
+ import chalk24 from "chalk";
9072
9844
  async function askCommand(question, options) {
9073
9845
  if (!question || question.trim().length < 2) {
9074
- console.error(chalk23.yellow('Usage: stellavault ask "your question here" [--save]'));
9075
- console.error(chalk23.dim("\nSearch Mode: finds relevant notes from your vault."));
9076
- console.error(chalk23.dim("For AI-powered answers, use MCP: claude mcp add stellavault -- stellavault serve"));
9846
+ console.error(chalk24.yellow('Usage: stellavault ask "your question here" [--save]'));
9847
+ console.error(chalk24.dim("\nSearch Mode: finds relevant notes from your vault."));
9848
+ console.error(chalk24.dim("For AI-powered answers, use MCP: claude mcp add stellavault -- stellavault serve"));
9077
9849
  process.exit(1);
9078
9850
  }
9079
9851
  const config = loadConfig();
9080
9852
  const hub = createKnowledgeHub(config);
9081
- console.error(chalk23.dim("Searching your knowledge (local search mode)..."));
9853
+ console.error(chalk24.dim("Searching your knowledge (local search mode)..."));
9082
9854
  await hub.store.initialize();
9083
9855
  await hub.embedder.initialize();
9084
9856
  const result = await askVault(hub.searchEngine, question, {
@@ -9091,39 +9863,39 @@ async function askCommand(question, options) {
9091
9863
  console.log(result.answer);
9092
9864
  if (result.savedTo) {
9093
9865
  console.log("");
9094
- console.log(chalk23.green(`Saved to: ${result.savedTo}`));
9866
+ console.log(chalk24.green(`Saved to: ${result.savedTo}`));
9095
9867
  }
9096
9868
  if (result.sources.length > 0 && !options.save) {
9097
9869
  console.log("");
9098
- console.log(chalk23.dim("Tip: Add --save to file this answer into your vault."));
9099
- console.log(chalk23.dim("For AI-generated answers: use Claude Code with MCP integration."));
9870
+ console.log(chalk24.dim("Tip: Add --save to file this answer into your vault."));
9871
+ console.log(chalk24.dim("For AI-generated answers: use Claude Code with MCP integration."));
9100
9872
  }
9101
9873
  await hub.store.close?.();
9102
9874
  }
9103
9875
 
9104
9876
  // packages/cli/dist/commands/compile-cmd.js
9105
- import chalk24 from "chalk";
9877
+ import chalk25 from "chalk";
9106
9878
  import { resolve as resolve15 } from "node:path";
9107
9879
  async function compileCommand(options) {
9108
9880
  const config = loadConfig();
9109
9881
  const vaultPath = config.vaultPath;
9110
9882
  const rawPath = resolve15(vaultPath, options.raw ?? "raw");
9111
9883
  const wikiPath = resolve15(vaultPath, options.wiki ?? "_wiki");
9112
- console.error(chalk24.dim(`Raw: ${rawPath}`));
9113
- console.error(chalk24.dim(`Wiki: ${wikiPath}`));
9884
+ console.error(chalk25.dim(`Raw: ${rawPath}`));
9885
+ console.error(chalk25.dim(`Wiki: ${wikiPath}`));
9114
9886
  console.error("");
9115
9887
  const result = compileWiki(rawPath, wikiPath, { force: options.force });
9116
9888
  if (result.rawDocCount === 0) {
9117
- console.error(chalk24.yellow(`No documents found in ${rawPath}`));
9118
- console.error(chalk24.dim("Create a raw/ folder in your vault and add .md/.txt files."));
9889
+ console.error(chalk25.yellow(`No documents found in ${rawPath}`));
9890
+ console.error(chalk25.dim("Create a raw/ folder in your vault and add .md/.txt files."));
9119
9891
  return;
9120
9892
  }
9121
- console.log(chalk24.green(`Compiled ${result.rawDocCount} raw docs \u2192 ${result.wikiArticles.length} wiki articles`));
9122
- console.log(chalk24.dim(`Concepts: ${result.concepts.length}`));
9123
- console.log(chalk24.dim(`Index: ${result.indexFile}`));
9893
+ console.log(chalk25.green(`Compiled ${result.rawDocCount} raw docs \u2192 ${result.wikiArticles.length} wiki articles`));
9894
+ console.log(chalk25.dim(`Concepts: ${result.concepts.length}`));
9895
+ console.log(chalk25.dim(`Index: ${result.indexFile}`));
9124
9896
  if (result.concepts.length > 0) {
9125
9897
  console.log("");
9126
- console.log(chalk24.cyan("Top concepts:"));
9898
+ console.log(chalk25.cyan("Top concepts:"));
9127
9899
  for (const c of result.concepts.slice(0, 10)) {
9128
9900
  console.log(` ${c}`);
9129
9901
  }
@@ -9131,13 +9903,13 @@ async function compileCommand(options) {
9131
9903
  }
9132
9904
 
9133
9905
  // packages/cli/dist/commands/draft-cmd.js
9134
- import chalk25 from "chalk";
9906
+ import chalk26 from "chalk";
9135
9907
 
9136
9908
  // packages/core/dist/intelligence/draft-generator.js
9137
9909
  init_wiki_compiler();
9138
9910
  init_config();
9139
- import { writeFileSync as writeFileSync19, mkdirSync as mkdirSync19, existsSync as existsSync19 } from "node:fs";
9140
- import { join as join24, resolve as resolve16, basename as basename5, extname as extname7 } from "node:path";
9911
+ import { writeFileSync as writeFileSync20, mkdirSync as mkdirSync20, existsSync as existsSync21 } from "node:fs";
9912
+ import { join as join26, resolve as resolve16, basename as basename5, extname as extname7 } from "node:path";
9141
9913
  function generateDraft(vaultPath, options = {}, folders = DEFAULT_FOLDERS) {
9142
9914
  const { topic, format = "blog", maxSections = 8, blueprint } = options;
9143
9915
  const rawDir = resolve16(vaultPath, folders.fleeting);
@@ -9145,7 +9917,7 @@ function generateDraft(vaultPath, options = {}, folders = DEFAULT_FOLDERS) {
9145
9917
  const litDir = resolve16(vaultPath, folders.literature);
9146
9918
  const allDocs = [];
9147
9919
  for (const dir of [rawDir, wikiDir, litDir]) {
9148
- if (existsSync19(dir)) {
9920
+ if (existsSync21(dir)) {
9149
9921
  allDocs.push(...scanRawDirectory(dir));
9150
9922
  }
9151
9923
  }
@@ -9213,14 +9985,14 @@ function generateDraft(vaultPath, options = {}, folders = DEFAULT_FOLDERS) {
9213
9985
  }
9214
9986
  const wordCount = body.split(/\s+/).filter(Boolean).length;
9215
9987
  const draftsDir = resolve16(vaultPath, "_drafts");
9216
- if (!existsSync19(draftsDir))
9217
- mkdirSync19(draftsDir, { recursive: true });
9988
+ if (!existsSync21(draftsDir))
9989
+ mkdirSync20(draftsDir, { recursive: true });
9218
9990
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
9219
9991
  const slug = (topic ?? "knowledge").replace(/[^a-zA-Z0-9가-힣\s]/g, "").replace(/\s+/g, "-").toLowerCase().slice(0, 40);
9220
9992
  const filename = `${timestamp}-${slug}.md`;
9221
- const filePath = join24("_drafts", filename);
9993
+ const filePath = join26("_drafts", filename);
9222
9994
  const fullPath = resolve16(vaultPath, filePath);
9223
- writeFileSync19(fullPath, body, "utf-8");
9995
+ writeFileSync20(fullPath, body, "utf-8");
9224
9996
  return {
9225
9997
  title: draftTitle,
9226
9998
  filePath,
@@ -9377,7 +10149,7 @@ function capitalize(s) {
9377
10149
  async function draftCommand(topic, options) {
9378
10150
  const config = loadConfig();
9379
10151
  if (!config.vaultPath) {
9380
- console.error(chalk25.red("No vault configured. Run `stellavault init` first."));
10152
+ console.error(chalk26.red("No vault configured. Run `stellavault init` first."));
9381
10153
  process.exit(1);
9382
10154
  }
9383
10155
  const format = options.format ?? "blog";
@@ -9391,40 +10163,40 @@ async function draftCommand(topic, options) {
9391
10163
  }
9392
10164
  const result = generateDraft(config.vaultPath, { topic, format, blueprint }, config.folders);
9393
10165
  if (options.ai) {
9394
- console.log(chalk25.dim(" AI mode: generating with Claude..."));
10166
+ console.log(chalk26.dim(" AI mode: generating with Claude..."));
9395
10167
  await enhanceWithAI(config.vaultPath, result.filePath, topic ?? "knowledge", format);
9396
10168
  }
9397
- console.log(chalk25.green(`Draft generated: ${result.title}`));
9398
- console.log(chalk25.dim(` Format: ${format}`));
9399
- console.log(chalk25.dim(` Mode: ${options.ai ? "AI-enhanced (Claude)" : "rule-based"}`));
9400
- console.log(chalk25.dim(` Saved: ${result.filePath}`));
9401
- console.log(chalk25.dim(` Words: ${result.wordCount}`));
9402
- console.log(chalk25.dim(` Sources: ${result.sourceCount} documents`));
10169
+ console.log(chalk26.green(`Draft generated: ${result.title}`));
10170
+ console.log(chalk26.dim(` Format: ${format}`));
10171
+ console.log(chalk26.dim(` Mode: ${options.ai ? "AI-enhanced (Claude)" : "rule-based"}`));
10172
+ console.log(chalk26.dim(` Saved: ${result.filePath}`));
10173
+ console.log(chalk26.dim(` Words: ${result.wordCount}`));
10174
+ console.log(chalk26.dim(` Sources: ${result.sourceCount} documents`));
9403
10175
  if (result.concepts.length > 0) {
9404
- console.log(chalk25.dim(` Concepts: ${result.concepts.join(", ")}`));
10176
+ console.log(chalk26.dim(` Concepts: ${result.concepts.join(", ")}`));
9405
10177
  }
9406
10178
  console.log("");
9407
- console.log(chalk25.dim(`Next steps:`));
9408
- console.log(chalk25.dim(` Edit in Obsidian, then promote:`));
9409
- console.log(chalk25.cyan(` stellavault promote ${result.filePath} --to literature`));
10179
+ console.log(chalk26.dim(`Next steps:`));
10180
+ console.log(chalk26.dim(` Edit in Obsidian, then promote:`));
10181
+ console.log(chalk26.cyan(` stellavault promote ${result.filePath} --to literature`));
9410
10182
  if (!options.ai) {
9411
- console.log(chalk25.dim(` Or use --ai for Claude-enhanced draft, or MCP generate-draft in Claude Code.`));
10183
+ console.log(chalk26.dim(` Or use --ai for Claude-enhanced draft, or MCP generate-draft in Claude Code.`));
9412
10184
  }
9413
10185
  } catch (err) {
9414
- console.error(chalk25.red(err instanceof Error ? err.message : "Draft generation failed"));
10186
+ console.error(chalk26.red(err instanceof Error ? err.message : "Draft generation failed"));
9415
10187
  process.exit(1);
9416
10188
  }
9417
10189
  }
9418
10190
  async function enhanceWithAI(vaultPath, draftPath, topic, format) {
9419
- const { readFileSync: readFileSync18, writeFileSync: writeFileSync22 } = await import("node:fs");
10191
+ const { readFileSync: readFileSync19, writeFileSync: writeFileSync23 } = await import("node:fs");
9420
10192
  const { resolve: resolve22 } = await import("node:path");
9421
10193
  const fullPath = resolve22(vaultPath, draftPath);
9422
- const scaffold = readFileSync18(fullPath, "utf-8");
10194
+ const scaffold = readFileSync19(fullPath, "utf-8");
9423
10195
  const excerpts = scaffold.split("\n").filter((l) => l.startsWith("> ")).map((l) => l.slice(2)).join("\n");
9424
10196
  const apiKey = process.env.ANTHROPIC_API_KEY;
9425
10197
  if (!apiKey) {
9426
- console.error(chalk25.yellow(" ANTHROPIC_API_KEY not set. Falling back to rule-based draft."));
9427
- console.error(chalk25.yellow(" Set it with: export ANTHROPIC_API_KEY=sk-ant-..."));
10198
+ console.error(chalk26.yellow(" ANTHROPIC_API_KEY not set. Falling back to rule-based draft."));
10199
+ console.error(chalk26.yellow(" Set it with: export ANTHROPIC_API_KEY=sk-ant-..."));
9428
10200
  return;
9429
10201
  }
9430
10202
  try {
@@ -9461,31 +10233,31 @@ ${aiContent.text}
9461
10233
  ---
9462
10234
  *Generated by \`stellavault draft --ai\` using Claude API at ${(/* @__PURE__ */ new Date()).toISOString()}*
9463
10235
  `;
9464
- writeFileSync22(fullPath, enhanced, "utf-8");
10236
+ writeFileSync23(fullPath, enhanced, "utf-8");
9465
10237
  }
9466
10238
  } catch (err) {
9467
- console.error(chalk25.yellow(` AI enhancement failed: ${err instanceof Error ? err.message : "unknown"}. Keeping rule-based draft.`));
10239
+ console.error(chalk26.yellow(` AI enhancement failed: ${err instanceof Error ? err.message : "unknown"}. Keeping rule-based draft.`));
9468
10240
  }
9469
10241
  }
9470
10242
 
9471
10243
  // packages/cli/dist/commands/session-cmd.js
9472
- import chalk26 from "chalk";
9473
- import { writeFileSync as writeFileSync20, mkdirSync as mkdirSync20, existsSync as existsSync20, appendFileSync } from "node:fs";
9474
- import { resolve as resolve17, join as join25 } from "node:path";
10244
+ import chalk27 from "chalk";
10245
+ import { writeFileSync as writeFileSync21, mkdirSync as mkdirSync21, existsSync as existsSync22, appendFileSync } from "node:fs";
10246
+ import { resolve as resolve17, join as join27 } from "node:path";
9475
10247
  async function sessionSaveCommand(options) {
9476
10248
  const config = loadConfig();
9477
10249
  if (!config.vaultPath) {
9478
- console.error(chalk26.red("No vault configured. Run `stellavault init` first."));
10250
+ console.error(chalk27.red("No vault configured. Run `stellavault init` first."));
9479
10251
  process.exit(1);
9480
10252
  }
9481
10253
  const folders = config.folders;
9482
10254
  const logDir = resolve17(config.vaultPath, folders.fleeting, "_daily-logs");
9483
- if (!existsSync20(logDir))
9484
- mkdirSync20(logDir, { recursive: true });
10255
+ if (!existsSync22(logDir))
10256
+ mkdirSync21(logDir, { recursive: true });
9485
10257
  const now = /* @__PURE__ */ new Date();
9486
10258
  const dateStr = now.toISOString().split("T")[0];
9487
10259
  const timeStr = now.toTimeString().split(" ")[0];
9488
- const logFile = join25(logDir, `daily-log-${dateStr}.md`);
10260
+ const logFile = join27(logDir, `daily-log-${dateStr}.md`);
9489
10261
  let summary = options.summary ?? "";
9490
10262
  if (!summary && !process.stdin.isTTY) {
9491
10263
  const chunks = [];
@@ -9495,7 +10267,7 @@ async function sessionSaveCommand(options) {
9495
10267
  summary = Buffer.concat(chunks).toString("utf-8").trim();
9496
10268
  }
9497
10269
  if (!summary) {
9498
- console.log(chalk26.dim("Enter session summary (Ctrl+D to finish):"));
10270
+ console.log(chalk27.dim("Enter session summary (Ctrl+D to finish):"));
9499
10271
  const chunks = [];
9500
10272
  for await (const chunk of process.stdin) {
9501
10273
  chunks.push(chunk);
@@ -9503,7 +10275,7 @@ async function sessionSaveCommand(options) {
9503
10275
  summary = Buffer.concat(chunks).toString("utf-8").trim();
9504
10276
  }
9505
10277
  if (!summary) {
9506
- console.error(chalk26.yellow("No summary provided. Skipping."));
10278
+ console.error(chalk27.yellow("No summary provided. Skipping."));
9507
10279
  return;
9508
10280
  }
9509
10281
  const entry = [
@@ -9524,7 +10296,7 @@ async function sessionSaveCommand(options) {
9524
10296
  entry.push("### Action Items", options.actions, "");
9525
10297
  }
9526
10298
  entry.push("---", "");
9527
- if (!existsSync20(logFile)) {
10299
+ if (!existsSync22(logFile)) {
9528
10300
  const header = [
9529
10301
  "---",
9530
10302
  `title: "Daily Log \u2014 ${dateStr}"`,
@@ -9536,80 +10308,80 @@ async function sessionSaveCommand(options) {
9536
10308
  `# Daily Log \u2014 ${dateStr}`,
9537
10309
  ""
9538
10310
  ].join("\n");
9539
- writeFileSync20(logFile, header + entry.join("\n"), "utf-8");
10311
+ writeFileSync21(logFile, header + entry.join("\n"), "utf-8");
9540
10312
  } else {
9541
10313
  appendFileSync(logFile, entry.join("\n"), "utf-8");
9542
10314
  }
9543
- console.log(chalk26.green(`Session saved to daily log: ${dateStr}`));
9544
- console.log(chalk26.dim(` File: ${logFile}`));
9545
- console.log(chalk26.dim(` Time: ${timeStr}`));
9546
- console.log(chalk26.dim(` Words: ${summary.split(/\s+/).length}`));
10315
+ console.log(chalk27.green(`Session saved to daily log: ${dateStr}`));
10316
+ console.log(chalk27.dim(` File: ${logFile}`));
10317
+ console.log(chalk27.dim(` Time: ${timeStr}`));
10318
+ console.log(chalk27.dim(` Words: ${summary.split(/\s+/).length}`));
9547
10319
  try {
9548
10320
  const { compileWiki: compileWiki2 } = await Promise.resolve().then(() => (init_wiki_compiler(), wiki_compiler_exports));
9549
10321
  const rawDir = resolve17(config.vaultPath, folders.fleeting);
9550
10322
  const wikiDir = resolve17(config.vaultPath, folders.wiki);
9551
10323
  compileWiki2(rawDir, wikiDir);
9552
- console.log(chalk26.dim(" Wiki: auto-compiled"));
10324
+ console.log(chalk27.dim(" Wiki: auto-compiled"));
9553
10325
  } catch {
9554
10326
  }
9555
10327
  }
9556
10328
 
9557
10329
  // packages/cli/dist/commands/flush-cmd.js
9558
- import chalk27 from "chalk";
9559
- import { readdirSync as readdirSync7, readFileSync as readFileSync16, existsSync as existsSync21 } from "node:fs";
9560
- import { resolve as resolve18, join as join26 } from "node:path";
10330
+ import chalk28 from "chalk";
10331
+ import { readdirSync as readdirSync7, readFileSync as readFileSync17, existsSync as existsSync23 } from "node:fs";
10332
+ import { resolve as resolve18, join as join28 } from "node:path";
9561
10333
  async function flushCommand() {
9562
10334
  const config = loadConfig();
9563
10335
  if (!config.vaultPath) {
9564
- console.error(chalk27.red("No vault configured. Run `stellavault init` first."));
10336
+ console.error(chalk28.red("No vault configured. Run `stellavault init` first."));
9565
10337
  process.exit(1);
9566
10338
  }
9567
10339
  const folders = config.folders;
9568
10340
  const logDir = resolve18(config.vaultPath, folders.fleeting, "_daily-logs");
9569
- if (!existsSync21(logDir)) {
9570
- console.log(chalk27.yellow("No daily logs found. Use `stellavault session-save` or let Claude Code hooks capture sessions."));
10341
+ if (!existsSync23(logDir)) {
10342
+ console.log(chalk28.yellow("No daily logs found. Use `stellavault session-save` or let Claude Code hooks capture sessions."));
9571
10343
  return;
9572
10344
  }
9573
10345
  const logFiles = readdirSync7(logDir).filter((f) => f.startsWith("daily-log-") && f.endsWith(".md"));
9574
10346
  if (logFiles.length === 0) {
9575
- console.log(chalk27.yellow("No daily log files found."));
10347
+ console.log(chalk28.yellow("No daily log files found."));
9576
10348
  return;
9577
10349
  }
9578
- console.log(chalk27.dim(`Found ${logFiles.length} daily logs`));
10350
+ console.log(chalk28.dim(`Found ${logFiles.length} daily logs`));
9579
10351
  let totalSessions = 0;
9580
10352
  const allContent = [];
9581
10353
  for (const file of logFiles) {
9582
- const content = readFileSync16(join26(logDir, file), "utf-8");
10354
+ const content = readFileSync17(join28(logDir, file), "utf-8");
9583
10355
  const sessions = content.split(/^## Session/m).slice(1);
9584
10356
  totalSessions += sessions.length;
9585
10357
  allContent.push(content);
9586
10358
  }
9587
- console.log(chalk27.dim(`Total sessions: ${totalSessions}`));
10359
+ console.log(chalk28.dim(`Total sessions: ${totalSessions}`));
9588
10360
  try {
9589
10361
  const { compileWiki: compileWiki2 } = await Promise.resolve().then(() => (init_wiki_compiler(), wiki_compiler_exports));
9590
10362
  const rawDir = resolve18(config.vaultPath, folders.fleeting);
9591
10363
  const wikiDir = resolve18(config.vaultPath, folders.wiki);
9592
10364
  const result = compileWiki2(rawDir, wikiDir);
9593
- console.log(chalk27.green(`Flush complete!`));
9594
- console.log(chalk27.dim(` Daily logs: ${logFiles.length} files, ${totalSessions} sessions`));
9595
- console.log(chalk27.dim(` Wiki articles: ${result.wikiArticles.length}`));
9596
- console.log(chalk27.dim(` Concepts extracted: ${result.concepts.length}`));
10365
+ console.log(chalk28.green(`Flush complete!`));
10366
+ console.log(chalk28.dim(` Daily logs: ${logFiles.length} files, ${totalSessions} sessions`));
10367
+ console.log(chalk28.dim(` Wiki articles: ${result.wikiArticles.length}`));
10368
+ console.log(chalk28.dim(` Concepts extracted: ${result.concepts.length}`));
9597
10369
  if (result.concepts.length > 0) {
9598
- console.log(chalk27.dim(` Top concepts: ${result.concepts.slice(0, 8).join(", ")}`));
10370
+ console.log(chalk28.dim(` Top concepts: ${result.concepts.slice(0, 8).join(", ")}`));
9599
10371
  }
9600
- console.log(chalk27.dim(` Index: ${result.indexFile}`));
10372
+ console.log(chalk28.dim(` Index: ${result.indexFile}`));
9601
10373
  } catch (err) {
9602
- console.error(chalk27.red(`Flush failed: ${err instanceof Error ? err.message : "unknown"}`));
10374
+ console.error(chalk28.red(`Flush failed: ${err instanceof Error ? err.message : "unknown"}`));
9603
10375
  process.exit(1);
9604
10376
  }
9605
- console.log(chalk27.dim(" Tip: Run `stellavault lint` to check knowledge health"));
10377
+ console.log(chalk28.dim(" Tip: Run `stellavault lint` to check knowledge health"));
9606
10378
  }
9607
10379
 
9608
10380
  // packages/cli/dist/commands/adr-cmd.js
9609
- import chalk28 from "chalk";
10381
+ import chalk29 from "chalk";
9610
10382
  async function adrCommand(title, options) {
9611
10383
  if (!title) {
9612
- console.error(chalk28.yellow('Usage: stellavault adr "Decision Title" --context "..." --options "..." --decision "..." --consequences "..."'));
10384
+ console.error(chalk29.yellow('Usage: stellavault adr "Decision Title" --context "..." --options "..." --decision "..." --consequences "..."'));
9613
10385
  process.exit(1);
9614
10386
  }
9615
10387
  const config = loadConfig();
@@ -9640,25 +10412,25 @@ async function adrCommand(title, options) {
9640
10412
  title: `ADR: ${title}`,
9641
10413
  stage: "literature"
9642
10414
  }, config.folders);
9643
- console.log(chalk28.green(`ADR created: ${title}`));
9644
- console.log(chalk28.dim(` Saved: ${result.savedTo}`));
9645
- console.log(chalk28.dim(` Stage: literature`));
9646
- console.log(chalk28.dim(` Tags: adr, decision`));
10415
+ console.log(chalk29.green(`ADR created: ${title}`));
10416
+ console.log(chalk29.dim(` Saved: ${result.savedTo}`));
10417
+ console.log(chalk29.dim(` Stage: literature`));
10418
+ console.log(chalk29.dim(` Tags: adr, decision`));
9647
10419
  console.log("");
9648
- console.log(chalk28.dim(`Find later: stellavault ask "why did we choose ${title}?"`));
10420
+ console.log(chalk29.dim(`Find later: stellavault ask "why did we choose ${title}?"`));
9649
10421
  }
9650
10422
 
9651
10423
  // packages/cli/dist/commands/lint-cmd.js
9652
- import chalk29 from "chalk";
10424
+ import chalk30 from "chalk";
9653
10425
  async function lintCommand() {
9654
10426
  const config = loadConfig();
9655
10427
  const hub = createKnowledgeHub(config);
9656
- console.error(chalk29.dim("Scanning your knowledge base..."));
10428
+ console.error(chalk30.dim("Scanning your knowledge base..."));
9657
10429
  await hub.store.initialize();
9658
10430
  const result = await lintKnowledge(hub.store);
9659
- const scoreColor = result.score >= 80 ? chalk29.green : result.score >= 50 ? chalk29.yellow : chalk29.red;
10431
+ const scoreColor = result.score >= 80 ? chalk30.green : result.score >= 50 ? chalk30.yellow : chalk30.red;
9660
10432
  console.log("");
9661
- console.log(chalk29.bold("Knowledge Health Report"));
10433
+ console.log(chalk30.bold("Knowledge Health Report"));
9662
10434
  console.log("\u2500".repeat(40));
9663
10435
  console.log(`Score: ${scoreColor(result.score + "/100")}`);
9664
10436
  console.log(`Documents: ${result.stats.totalDocs}`);
@@ -9668,35 +10440,35 @@ async function lintCommand() {
9668
10440
  const warnings = result.issues.filter((i) => i.severity === "warning");
9669
10441
  const info = result.issues.filter((i) => i.severity === "info");
9670
10442
  if (critical.length > 0) {
9671
- console.log(chalk29.red(`Critical: ${critical.length}`));
10443
+ console.log(chalk30.red(`Critical: ${critical.length}`));
9672
10444
  for (const i of critical) {
9673
- console.log(chalk29.red(` \u2717 ${i.message}`));
10445
+ console.log(chalk30.red(` \u2717 ${i.message}`));
9674
10446
  if (i.suggestion)
9675
- console.log(chalk29.dim(` \u2192 ${i.suggestion}`));
10447
+ console.log(chalk30.dim(` \u2192 ${i.suggestion}`));
9676
10448
  }
9677
10449
  console.log("");
9678
10450
  }
9679
10451
  if (warnings.length > 0) {
9680
- console.log(chalk29.yellow(`Warnings: ${warnings.length}`));
10452
+ console.log(chalk30.yellow(`Warnings: ${warnings.length}`));
9681
10453
  for (const i of warnings.slice(0, 10)) {
9682
- console.log(chalk29.yellow(` ! ${i.message}`));
10454
+ console.log(chalk30.yellow(` ! ${i.message}`));
9683
10455
  if (i.suggestion)
9684
- console.log(chalk29.dim(` \u2192 ${i.suggestion}`));
10456
+ console.log(chalk30.dim(` \u2192 ${i.suggestion}`));
9685
10457
  }
9686
10458
  if (warnings.length > 10)
9687
- console.log(chalk29.dim(` ... and ${warnings.length - 10} more`));
10459
+ console.log(chalk30.dim(` ... and ${warnings.length - 10} more`));
9688
10460
  console.log("");
9689
10461
  }
9690
10462
  if (info.length > 0) {
9691
- console.log(chalk29.dim(`Info: ${info.length}`));
10463
+ console.log(chalk30.dim(`Info: ${info.length}`));
9692
10464
  for (const i of info.slice(0, 5)) {
9693
- console.log(chalk29.dim(` \u2139 ${i.message}`));
10465
+ console.log(chalk30.dim(` \u2139 ${i.message}`));
9694
10466
  }
9695
10467
  console.log("");
9696
10468
  }
9697
10469
  }
9698
10470
  if (result.suggestions.length > 0) {
9699
- console.log(chalk29.cyan("Suggestions:"));
10471
+ console.log(chalk30.cyan("Suggestions:"));
9700
10472
  for (const s of result.suggestions) {
9701
10473
  console.log(` \u2192 ${s}`);
9702
10474
  }
@@ -9706,25 +10478,25 @@ async function lintCommand() {
9706
10478
  }
9707
10479
 
9708
10480
  // packages/cli/dist/commands/fleeting-cmd.js
9709
- import chalk30 from "chalk";
9710
- import { writeFileSync as writeFileSync21, mkdirSync as mkdirSync21, existsSync as existsSync22 } from "node:fs";
9711
- import { join as join27, resolve as resolve19 } from "node:path";
10481
+ import chalk31 from "chalk";
10482
+ import { writeFileSync as writeFileSync22, mkdirSync as mkdirSync22, existsSync as existsSync24 } from "node:fs";
10483
+ import { join as join29, resolve as resolve19 } from "node:path";
9712
10484
  async function fleetingCommand(text, options) {
9713
10485
  if (!text || text.trim().length < 2) {
9714
- console.error(chalk30.yellow('Usage: stellavault fleeting "your idea here" [--tags tag1,tag2]'));
10486
+ console.error(chalk31.yellow('Usage: stellavault fleeting "your idea here" [--tags tag1,tag2]'));
9715
10487
  process.exit(1);
9716
10488
  }
9717
10489
  const config = loadConfig();
9718
10490
  const rawDir = resolve19(config.vaultPath, "raw");
9719
- if (!existsSync22(rawDir))
9720
- mkdirSync21(rawDir, { recursive: true });
10491
+ if (!existsSync24(rawDir))
10492
+ mkdirSync22(rawDir, { recursive: true });
9721
10493
  const now = /* @__PURE__ */ new Date();
9722
10494
  const timestamp = now.toISOString().replace(/[:.]/g, "-").slice(0, 19);
9723
10495
  const slug = text.slice(0, 40).replace(/[^a-zA-Z0-9가-힣\s]/g, "").replace(/\s+/g, "-").toLowerCase();
9724
10496
  const filename = `${timestamp}-${slug}.md`;
9725
- const filePath = join27(rawDir, filename);
10497
+ const filePath = join29(rawDir, filename);
9726
10498
  if (!resolve19(filePath).startsWith(resolve19(rawDir))) {
9727
- console.error(chalk30.red("Invalid file path"));
10499
+ console.error(chalk31.red("Invalid file path"));
9728
10500
  process.exit(1);
9729
10501
  }
9730
10502
  const tags = options.tags ? options.tags.split(",").map((t2) => t2.trim()) : [];
@@ -9741,28 +10513,28 @@ async function fleetingCommand(text, options) {
9741
10513
  "---",
9742
10514
  `*Captured via \`stellavault fleeting\` at ${now.toLocaleString("ko-KR")}*`
9743
10515
  ].join("\n");
9744
- writeFileSync21(filePath, content, "utf-8");
9745
- console.log(chalk30.green(`Captured: ${filename}`));
9746
- console.log(chalk30.dim(`Location: raw/${filename}`));
9747
- console.log(chalk30.dim("Run `stellavault compile` to process into wiki."));
10516
+ writeFileSync22(filePath, content, "utf-8");
10517
+ console.log(chalk31.green(`Captured: ${filename}`));
10518
+ console.log(chalk31.dim(`Location: raw/${filename}`));
10519
+ console.log(chalk31.dim("Run `stellavault compile` to process into wiki."));
9748
10520
  }
9749
10521
 
9750
10522
  // packages/cli/dist/commands/ingest-cmd.js
9751
- import chalk31 from "chalk";
9752
- import { readFileSync as readFileSync17, existsSync as existsSync23, readdirSync as readdirSync8, statSync as statSync5 } from "node:fs";
9753
- import { extname as extname8, resolve as resolve20, join as join28 } from "node:path";
10523
+ import chalk32 from "chalk";
10524
+ import { readFileSync as readFileSync18, existsSync as existsSync25, readdirSync as readdirSync8, statSync as statSync5 } from "node:fs";
10525
+ import { extname as extname8, resolve as resolve20, join as join30 } from "node:path";
9754
10526
  async function ingestCommand(input, options) {
9755
10527
  if (!input) {
9756
- console.error(chalk31.yellow("Usage: stellavault ingest <url|file|text|folder/> [--tags t1,t2]"));
10528
+ console.error(chalk32.yellow("Usage: stellavault ingest <url|file|text|folder/> [--tags t1,t2]"));
9757
10529
  process.exit(1);
9758
10530
  }
9759
- if (existsSync23(input) && statSync5(input).isDirectory()) {
9760
- const files = readdirSync8(input).filter((f) => /\.(md|txt|pdf|docx|pptx|xlsx|xls|csv|json|xml|html|htm|yaml|yml|rtf)$/i.test(f)).map((f) => join28(input, f));
10531
+ if (existsSync25(input) && statSync5(input).isDirectory()) {
10532
+ const files = readdirSync8(input).filter((f) => /\.(md|txt|pdf|docx|pptx|xlsx|xls|csv|json|xml|html|htm|yaml|yml|rtf)$/i.test(f)).map((f) => join30(input, f));
9761
10533
  if (files.length === 0) {
9762
- console.error(chalk31.yellow(`No supported files found in ${input}`));
10534
+ console.error(chalk32.yellow(`No supported files found in ${input}`));
9763
10535
  process.exit(1);
9764
10536
  }
9765
- console.log(chalk31.dim(`Batch ingest: ${files.length} files from ${input}
10537
+ console.log(chalk32.dim(`Batch ingest: ${files.length} files from ${input}
9766
10538
  `));
9767
10539
  let success = 0;
9768
10540
  const failed = [];
@@ -9770,7 +10542,7 @@ async function ingestCommand(input, options) {
9770
10542
  const file = files[i];
9771
10543
  const name = file.split(/[/\\]/).pop() ?? file;
9772
10544
  const progress = `[${i + 1}/${files.length}]`;
9773
- process.stderr.write(`\r${chalk31.dim(progress)} ${name}...`);
10545
+ process.stderr.write(`\r${chalk32.dim(progress)} ${name}...`);
9774
10546
  try {
9775
10547
  await ingestSingleFile(file, options);
9776
10548
  success++;
@@ -9779,12 +10551,12 @@ async function ingestCommand(input, options) {
9779
10551
  }
9780
10552
  }
9781
10553
  process.stderr.write("\r" + " ".repeat(80) + "\r");
9782
- console.log(chalk31.green(`Batch complete: ${success}/${files.length} files ingested`));
10554
+ console.log(chalk32.green(`Batch complete: ${success}/${files.length} files ingested`));
9783
10555
  if (failed.length > 0) {
9784
- console.log(chalk31.yellow(`
10556
+ console.log(chalk32.yellow(`
9785
10557
  Failed (${failed.length}):`));
9786
10558
  for (const f of failed)
9787
- console.log(chalk31.yellow(` - ${f}`));
10559
+ console.log(chalk32.yellow(` - ${f}`));
9788
10560
  }
9789
10561
  return;
9790
10562
  }
@@ -9805,7 +10577,7 @@ async function ingestSingleFile(input, options) {
9805
10577
  const url = new URL(input);
9806
10578
  const host = url.hostname;
9807
10579
  if (/^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|0\.|localhost|::1)/i.test(host)) {
9808
- console.error(chalk31.yellow("Private/local URLs are not allowed for security."));
10580
+ console.error(chalk32.yellow("Private/local URLs are not allowed for security."));
9809
10581
  process.exit(1);
9810
10582
  }
9811
10583
  } catch {
@@ -9825,7 +10597,7 @@ async function ingestSingleFile(input, options) {
9825
10597
  source: input
9826
10598
  };
9827
10599
  } catch (err) {
9828
- console.error(chalk31.yellow(`YouTube extraction failed, falling back to basic URL. (${err instanceof Error ? err.message : "error"})`));
10600
+ console.error(chalk32.yellow(`YouTube extraction failed, falling back to basic URL. (${err instanceof Error ? err.message : "error"})`));
9829
10601
  ingestInput = {
9830
10602
  type: "youtube",
9831
10603
  content: input + "\n",
@@ -9843,7 +10615,7 @@ async function ingestSingleFile(input, options) {
9843
10615
  const text = html.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim().slice(0, 5e3);
9844
10616
  content += text;
9845
10617
  } catch (err) {
9846
- console.error(chalk31.yellow(`Web fetch failed: saving URL only. (${err instanceof Error ? err.message : "network error"})`));
10618
+ console.error(chalk32.yellow(`Web fetch failed: saving URL only. (${err instanceof Error ? err.message : "network error"})`));
9847
10619
  }
9848
10620
  ingestInput = {
9849
10621
  type: "url",
@@ -9854,7 +10626,7 @@ async function ingestSingleFile(input, options) {
9854
10626
  source: input
9855
10627
  };
9856
10628
  }
9857
- } else if (existsSync23(input)) {
10629
+ } else if (existsSync25(input)) {
9858
10630
  const ext = extname8(input).toLowerCase();
9859
10631
  const binaryExts = /* @__PURE__ */ new Set([".pdf", ".docx", ".pptx", ".xlsx", ".xls"]);
9860
10632
  const structuredExts = /* @__PURE__ */ new Set([".json", ".csv", ".xml", ".html", ".htm", ".yaml", ".yml", ".rtf"]);
@@ -9862,7 +10634,7 @@ async function ingestSingleFile(input, options) {
9862
10634
  try {
9863
10635
  const { extractFileContent: extractFileContent2 } = await Promise.resolve().then(() => (init_file_extractors(), file_extractors_exports));
9864
10636
  const extracted = await extractFileContent2(resolve20(input));
9865
- console.log(chalk31.dim(` Extracted ${extracted.metadata.wordCount} words from ${ext} file`));
10637
+ console.log(chalk32.dim(` Extracted ${extracted.metadata.wordCount} words from ${ext} file`));
9866
10638
  ingestInput = {
9867
10639
  type: extracted.sourceFormat,
9868
10640
  content: extracted.text,
@@ -9873,10 +10645,10 @@ async function ingestSingleFile(input, options) {
9873
10645
  source: input
9874
10646
  };
9875
10647
  } catch (err) {
9876
- console.error(chalk31.yellow(`Binary file extraction failed, saving as-is. (${err instanceof Error ? err.message : "error"})`));
10648
+ console.error(chalk32.yellow(`Binary file extraction failed, saving as-is. (${err instanceof Error ? err.message : "error"})`));
9877
10649
  ingestInput = {
9878
10650
  type: "file",
9879
- content: readFileSync17(input, "utf-8"),
10651
+ content: readFileSync18(input, "utf-8"),
9880
10652
  tags,
9881
10653
  stage,
9882
10654
  title: options.title,
@@ -9887,7 +10659,7 @@ async function ingestSingleFile(input, options) {
9887
10659
  try {
9888
10660
  const { extractFileContent: extractFileContent2 } = await Promise.resolve().then(() => (init_file_extractors(), file_extractors_exports));
9889
10661
  const extracted = await extractFileContent2(resolve20(input));
9890
- console.log(chalk31.dim(` Extracted ${extracted.metadata.wordCount} words from ${ext} file`));
10662
+ console.log(chalk32.dim(` Extracted ${extracted.metadata.wordCount} words from ${ext} file`));
9891
10663
  ingestInput = {
9892
10664
  type: "file",
9893
10665
  content: extracted.text,
@@ -9897,11 +10669,11 @@ async function ingestSingleFile(input, options) {
9897
10669
  source: input
9898
10670
  };
9899
10671
  } catch (err) {
9900
- const fileContent = readFileSync17(input, "utf-8");
10672
+ const fileContent = readFileSync18(input, "utf-8");
9901
10673
  ingestInput = { type: "file", content: fileContent, tags, stage, title: options.title, source: input };
9902
10674
  }
9903
10675
  } else {
9904
- const fileContent = readFileSync17(input, "utf-8");
10676
+ const fileContent = readFileSync18(input, "utf-8");
9905
10677
  ingestInput = {
9906
10678
  type: "file",
9907
10679
  content: fileContent,
@@ -9921,103 +10693,103 @@ async function ingestSingleFile(input, options) {
9921
10693
  };
9922
10694
  }
9923
10695
  const result = ingest(config.vaultPath, ingestInput);
9924
- console.log(chalk31.green(`Ingested: ${result.title}`));
9925
- console.log(chalk31.dim(` Stage: ${result.stage}`));
9926
- console.log(chalk31.dim(` Saved: ${result.savedTo}`));
9927
- console.log(chalk31.dim(` Words: ${result.wordCount}`));
10696
+ console.log(chalk32.green(`Ingested: ${result.title}`));
10697
+ console.log(chalk32.dim(` Stage: ${result.stage}`));
10698
+ console.log(chalk32.dim(` Saved: ${result.savedTo}`));
10699
+ console.log(chalk32.dim(` Words: ${result.wordCount}`));
9928
10700
  if (result.indexCode)
9929
- console.log(chalk31.dim(` Index: ${result.indexCode}`));
10701
+ console.log(chalk32.dim(` Index: ${result.indexCode}`));
9930
10702
  if (result.tags.length > 0)
9931
- console.log(chalk31.dim(` Tags: ${result.tags.join(", ")}`));
9932
- console.log(chalk31.dim(" Wiki: auto-compiled"));
10703
+ console.log(chalk32.dim(` Tags: ${result.tags.join(", ")}`));
10704
+ console.log(chalk32.dim(" Wiki: auto-compiled"));
9933
10705
  console.log("");
9934
10706
  }
9935
10707
  async function promoteCommand(filePath, options) {
9936
10708
  const config = loadConfig();
9937
10709
  const target = options.to;
9938
10710
  if (!["fleeting", "literature", "permanent"].includes(target)) {
9939
- console.error(chalk31.red("--to must be: fleeting, literature, or permanent"));
10711
+ console.error(chalk32.red("--to must be: fleeting, literature, or permanent"));
9940
10712
  process.exit(1);
9941
10713
  }
9942
10714
  const newPath = promoteNote(config.vaultPath, filePath, target);
9943
- console.log(chalk31.green(`Promoted to ${target}: ${newPath}`));
10715
+ console.log(chalk32.green(`Promoted to ${target}: ${newPath}`));
9944
10716
  }
9945
10717
 
9946
10718
  // packages/cli/dist/commands/autopilot-cmd.js
9947
- import chalk32 from "chalk";
10719
+ import chalk33 from "chalk";
9948
10720
  import { resolve as resolve21 } from "node:path";
9949
10721
  async function autopilotCommand(options) {
9950
10722
  const config = loadConfig();
9951
10723
  const vaultPath = config.vaultPath;
9952
- console.log(chalk32.bold("\n \u2726 Stellavault Autopilot"));
9953
- console.log(chalk32.dim(" Knowledge flywheel: inbox \u2192 compile \u2192 lint \u2192 repeat\n"));
9954
- console.log(chalk32.cyan("Step 1/4: Checking inbox..."));
10724
+ console.log(chalk33.bold("\n \u2726 Stellavault Autopilot"));
10725
+ console.log(chalk33.dim(" Knowledge flywheel: inbox \u2192 compile \u2192 lint \u2192 repeat\n"));
10726
+ console.log(chalk33.cyan("Step 1/4: Checking inbox..."));
9955
10727
  const inbox = getInboxItems(vaultPath);
9956
10728
  if (inbox.length === 0) {
9957
- console.log(chalk32.dim(" No new items in raw/ folder."));
10729
+ console.log(chalk33.dim(" No new items in raw/ folder."));
9958
10730
  } else {
9959
- console.log(chalk32.green(` ${inbox.length} new items found.`));
10731
+ console.log(chalk33.green(` ${inbox.length} new items found.`));
9960
10732
  for (const item of inbox.slice(0, 5)) {
9961
- console.log(chalk32.dim(` - ${item.title} (${item.wordCount} words)`));
10733
+ console.log(chalk33.dim(` - ${item.title} (${item.wordCount} words)`));
9962
10734
  }
9963
10735
  if (inbox.length > 5)
9964
- console.log(chalk32.dim(` ... and ${inbox.length - 5} more`));
10736
+ console.log(chalk33.dim(` ... and ${inbox.length - 5} more`));
9965
10737
  }
9966
- console.log(chalk32.cyan("\nStep 2/4: Compiling wiki..."));
10738
+ console.log(chalk33.cyan("\nStep 2/4: Compiling wiki..."));
9967
10739
  const rawPath = resolve21(vaultPath, "raw");
9968
10740
  const wikiPath = resolve21(vaultPath, "_wiki");
9969
10741
  const compileResult = compileWiki(rawPath, wikiPath);
9970
10742
  if (compileResult.rawDocCount > 0) {
9971
- console.log(chalk32.green(` Compiled ${compileResult.rawDocCount} raw \u2192 ${compileResult.wikiArticles.length} wiki articles`));
9972
- console.log(chalk32.dim(` Concepts: ${compileResult.concepts.length}`));
10743
+ console.log(chalk33.green(` Compiled ${compileResult.rawDocCount} raw \u2192 ${compileResult.wikiArticles.length} wiki articles`));
10744
+ console.log(chalk33.dim(` Concepts: ${compileResult.concepts.length}`));
9973
10745
  for (const item of inbox) {
9974
10746
  try {
9975
10747
  archiveFile(resolve21(vaultPath, "raw", item.filePath));
9976
10748
  } catch {
9977
10749
  }
9978
10750
  }
9979
- console.log(chalk32.dim(` Archived ${inbox.length} processed items.`));
10751
+ console.log(chalk33.dim(` Archived ${inbox.length} processed items.`));
9980
10752
  } else {
9981
- console.log(chalk32.dim(" No raw documents to compile."));
10753
+ console.log(chalk33.dim(" No raw documents to compile."));
9982
10754
  }
9983
- console.log(chalk32.cyan("\nStep 3/4: Running health check..."));
10755
+ console.log(chalk33.cyan("\nStep 3/4: Running health check..."));
9984
10756
  const hub = createKnowledgeHub(config);
9985
10757
  await hub.store.initialize();
9986
10758
  const lintResult = await lintKnowledge(hub.store);
9987
- const scoreColor = lintResult.score >= 80 ? chalk32.green : lintResult.score >= 50 ? chalk32.yellow : chalk32.red;
10759
+ const scoreColor = lintResult.score >= 80 ? chalk33.green : lintResult.score >= 50 ? chalk33.yellow : chalk33.red;
9988
10760
  console.log(` Health: ${scoreColor(lintResult.score + "/100")}`);
9989
10761
  const critical = lintResult.issues.filter((i) => i.severity === "critical").length;
9990
10762
  const warnings = lintResult.issues.filter((i) => i.severity === "warning").length;
9991
10763
  if (critical > 0)
9992
- console.log(chalk32.red(` Critical: ${critical}`));
10764
+ console.log(chalk33.red(` Critical: ${critical}`));
9993
10765
  if (warnings > 0)
9994
- console.log(chalk32.yellow(` Warnings: ${warnings}`));
9995
- console.log(chalk32.cyan("\nStep 4/4: Summary"));
9996
- console.log(chalk32.dim("\u2500".repeat(40)));
10766
+ console.log(chalk33.yellow(` Warnings: ${warnings}`));
10767
+ console.log(chalk33.cyan("\nStep 4/4: Summary"));
10768
+ console.log(chalk33.dim("\u2500".repeat(40)));
9997
10769
  console.log(` Inbox processed: ${inbox.length}`);
9998
10770
  console.log(` Wiki articles: ${compileResult.wikiArticles.length}`);
9999
10771
  console.log(` Health score: ${lintResult.score}/100`);
10000
10772
  console.log(` Issues: ${lintResult.issues.length}`);
10001
10773
  if (lintResult.suggestions.length > 0) {
10002
- console.log(chalk32.cyan("\n Suggestions:"));
10774
+ console.log(chalk33.cyan("\n Suggestions:"));
10003
10775
  for (const s of lintResult.suggestions.slice(0, 3)) {
10004
- console.log(chalk32.dim(` \u2192 ${s}`));
10776
+ console.log(chalk33.dim(` \u2192 ${s}`));
10005
10777
  }
10006
10778
  }
10007
- console.log(chalk32.dim("\n\u2500".repeat(40)));
10008
- console.log(chalk32.dim("\n Next: run `stellavault index` to update search vectors."));
10009
- console.log(chalk32.green(" Autopilot complete.\n"));
10779
+ console.log(chalk33.dim("\n\u2500".repeat(40)));
10780
+ console.log(chalk33.dim("\n Next: run `stellavault index` to update search vectors."));
10781
+ console.log(chalk33.green(" Autopilot complete.\n"));
10010
10782
  await hub.store.close?.();
10011
10783
  }
10012
10784
 
10013
10785
  // packages/cli/dist/commands/doctor-cmd.js
10014
- import chalk33 from "chalk";
10015
- import { existsSync as existsSync24, statSync as statSync6 } from "node:fs";
10016
- import { join as join29 } from "node:path";
10017
- import { homedir as homedir15 } from "node:os";
10786
+ import chalk34 from "chalk";
10787
+ import { existsSync as existsSync26, statSync as statSync6 } from "node:fs";
10788
+ import { join as join31 } from "node:path";
10789
+ import { homedir as homedir17 } from "node:os";
10018
10790
  async function doctorCommand() {
10019
10791
  console.log("");
10020
- console.log(chalk33.bold(" \u{1FA7A} Stellavault Doctor\n"));
10792
+ console.log(chalk34.bold(" \u{1FA7A} Stellavault Doctor\n"));
10021
10793
  const checks = [];
10022
10794
  const nodeVersion2 = parseInt(process.versions.node.split(".")[0], 10);
10023
10795
  checks.push({
@@ -10027,10 +10799,10 @@ async function doctorCommand() {
10027
10799
  fix: nodeVersion2 < 20 ? "Download Node.js 20+: https://nodejs.org" : void 0
10028
10800
  });
10029
10801
  const configPaths = [
10030
- join29(process.cwd(), ".stellavault.json"),
10031
- join29(homedir15(), ".stellavault.json")
10802
+ join31(process.cwd(), ".stellavault.json"),
10803
+ join31(homedir17(), ".stellavault.json")
10032
10804
  ];
10033
- const configPath = configPaths.find((p) => existsSync24(p));
10805
+ const configPath = configPaths.find((p) => existsSync26(p));
10034
10806
  checks.push({
10035
10807
  name: "Config file",
10036
10808
  pass: !!configPath,
@@ -10041,8 +10813,8 @@ async function doctorCommand() {
10041
10813
  let dbPath = "";
10042
10814
  if (configPath) {
10043
10815
  try {
10044
- const { readFileSync: readFileSync18 } = await import("node:fs");
10045
- const config = JSON.parse(readFileSync18(configPath, "utf-8"));
10816
+ const { readFileSync: readFileSync19 } = await import("node:fs");
10817
+ const config = JSON.parse(readFileSync19(configPath, "utf-8"));
10046
10818
  vaultPath = config.vaultPath || "";
10047
10819
  dbPath = config.dbPath || "";
10048
10820
  } catch (err) {
@@ -10055,7 +10827,7 @@ async function doctorCommand() {
10055
10827
  }
10056
10828
  }
10057
10829
  if (vaultPath) {
10058
- const vaultExists = existsSync24(vaultPath);
10830
+ const vaultExists = existsSync26(vaultPath);
10059
10831
  checks.push({
10060
10832
  name: "Vault path",
10061
10833
  pass: vaultExists,
@@ -10084,7 +10856,7 @@ async function doctorCommand() {
10084
10856
  }
10085
10857
  }
10086
10858
  if (dbPath) {
10087
- const dbExists = existsSync24(dbPath);
10859
+ const dbExists = existsSync26(dbPath);
10088
10860
  checks.push({
10089
10861
  name: "Database",
10090
10862
  pass: dbExists,
@@ -10099,20 +10871,20 @@ async function doctorCommand() {
10099
10871
  fix: "Re-run: stellavault init"
10100
10872
  });
10101
10873
  }
10102
- const cacheDir = join29(homedir15(), ".cache", "onnxruntime");
10103
- const hfCache = join29(homedir15(), ".cache", "huggingface");
10104
- const xenovaCache = join29(homedir15(), ".cache", "xenova");
10105
- const modelCached = existsSync24(cacheDir) || existsSync24(hfCache) || existsSync24(xenovaCache) || existsSync24(join29(homedir15(), ".cache", "transformers"));
10874
+ const cacheDir = join31(homedir17(), ".cache", "onnxruntime");
10875
+ const hfCache = join31(homedir17(), ".cache", "huggingface");
10876
+ const xenovaCache = join31(homedir17(), ".cache", "xenova");
10877
+ const modelCached = existsSync26(cacheDir) || existsSync26(hfCache) || existsSync26(xenovaCache) || existsSync26(join31(homedir17(), ".cache", "transformers"));
10106
10878
  checks.push({
10107
10879
  name: "Embedding model cached",
10108
10880
  pass: modelCached,
10109
10881
  detail: modelCached ? "local model files found" : "not downloaded yet",
10110
10882
  fix: !modelCached ? "Will download automatically on first index (~30MB)" : void 0
10111
10883
  });
10112
- const testDir = join29(homedir15(), ".stellavault");
10884
+ const testDir = join31(homedir17(), ".stellavault");
10113
10885
  try {
10114
- const { mkdirSync: mkdirSync22 } = await import("node:fs");
10115
- mkdirSync22(testDir, { recursive: true });
10886
+ const { mkdirSync: mkdirSync23 } = await import("node:fs");
10887
+ mkdirSync23(testDir, { recursive: true });
10116
10888
  checks.push({ name: "Write permission (~/.stellavault)", pass: true, detail: "OK" });
10117
10889
  } catch {
10118
10890
  checks.push({
@@ -10124,20 +10896,20 @@ async function doctorCommand() {
10124
10896
  }
10125
10897
  let passCount = 0;
10126
10898
  for (const c of checks) {
10127
- const icon = c.pass ? chalk33.green("\u2713") : chalk33.red("\u2717");
10128
- console.log(` ${icon} ${c.name} ${chalk33.dim("\u2014")} ${c.pass ? chalk33.dim(c.detail) : chalk33.yellow(c.detail)}`);
10899
+ const icon = c.pass ? chalk34.green("\u2713") : chalk34.red("\u2717");
10900
+ console.log(` ${icon} ${c.name} ${chalk34.dim("\u2014")} ${c.pass ? chalk34.dim(c.detail) : chalk34.yellow(c.detail)}`);
10129
10901
  if (c.fix)
10130
- console.log(` ${chalk33.dim("Fix:")} ${c.fix}`);
10902
+ console.log(` ${chalk34.dim("Fix:")} ${c.fix}`);
10131
10903
  if (c.pass)
10132
10904
  passCount++;
10133
10905
  }
10134
10906
  console.log("");
10135
10907
  if (passCount === checks.length) {
10136
- console.log(chalk33.green.bold(` All ${checks.length} checks passed. You're good to go! \u2726
10908
+ console.log(chalk34.green.bold(` All ${checks.length} checks passed. You're good to go! \u2726
10137
10909
  `));
10138
10910
  } else {
10139
10911
  const failCount = checks.length - passCount;
10140
- console.log(chalk33.yellow(` ${failCount} issue${failCount > 1 ? "s" : ""} found. Fix them and re-run: stellavault doctor
10912
+ console.log(chalk34.yellow(` ${failCount} issue${failCount > 1 ? "s" : ""} found. Fix them and re-run: stellavault doctor
10141
10913
  `));
10142
10914
  }
10143
10915
  process.exit(passCount === checks.length ? 0 : 1);
@@ -10162,7 +10934,7 @@ if (nodeVersion < 20) {
10162
10934
  process.exit(1);
10163
10935
  }
10164
10936
  var program = new Command();
10165
- var SV_VERSION = true ? "0.7.4" : "0.0.0-dev";
10937
+ var SV_VERSION = true ? "0.8.2" : "0.0.0-dev";
10166
10938
  program.name("stellavault").description("Stellavault \u2014 Self-compiling knowledge base for your Obsidian vault").version(SV_VERSION).option("--json", "Output in JSON format (for scripting)").option("--quiet", "Suppress non-essential output");
10167
10939
  program.command("init").description("Interactive setup wizard \u2014 get started in 3 minutes").action(initCommand);
10168
10940
  program.command("doctor").description("Diagnose setup issues (config, vault, DB, model, Node version)").action(doctorCommand);
@@ -10174,6 +10946,7 @@ program.command("ingest <input>").description("Ingest any input (URL, file, text
10174
10946
  program.command("clip <url>").description("Clip a web page or YouTube video into your vault").option("-f, --folder <path>", "Vault subfolder for clips", "06_Research/clips").action(clipCommand);
10175
10947
  program.command("graph").description("Launch the 3D knowledge graph in your browser").action(graphCommand);
10176
10948
  program.command("serve").alias("mcp").description("Start MCP server (for Claude Code / Claude Desktop). Alias: mcp").action(serveCommand);
10949
+ program.command("setup").description("Connect Stellavault to your AI clients (Claude Code/Desktop, Cursor, Windsurf, VS Code) in one command").option("-c, --client <id>", "Target a specific client (repeatable): claude-code, claude-desktop, cursor, windsurf, vscode", (val, prev) => [...prev, val], []).option("--all", "Write configs to all supported clients, even if not detected").option("--command <cmd>", "Override the server command (advanced/dev)").option("--args <args>", "Override server args (space-separated; default: serve)").action((opts) => setupCommand(opts));
10177
10950
  program.command("decay").description("Memory decay report \u2014 find notes you are forgetting").action(decayCommand);
10178
10951
  program.command("brief").description("Daily knowledge briefing (decay + gaps + activity)").action(briefCommand);
10179
10952
  program.command("digest").description("Weekly knowledge activity report").option("-d, --days <n>", "Period in days", "7").option("-v, --visual", "Save as .md with Mermaid charts for Obsidian").action(digestCommand);