stellavault 0.7.4 → 0.8.1

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 ?? 0.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: 0.5 },
98
+ // B3 §1.2
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,37 @@ 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
+ if (fuzzy.length === 0) {
3914
+ const rows2 = db.prepare(`
3915
+ SELECT chunk_id, COUNT(*) AS score
3916
+ FROM chunk_entities
3917
+ WHERE entity IN (${exactPH})
3918
+ GROUP BY chunk_id
3919
+ ORDER BY score DESC
3920
+ LIMIT ?
3921
+ `).all(...entities, limit);
3922
+ return rows2.map((r) => ({ chunkId: r.chunk_id, score: r.score }));
3923
+ }
3924
+ const esc = (t2) => t2.replace(/[\\%_]/g, "\\$&");
3925
+ const likeClause = fuzzy.map(() => `entity LIKE ? ESCAPE '\\'`).join(" OR ");
3926
+ const rows = db.prepare(`
3927
+ SELECT chunk_id, SUM(w) AS score FROM (
3928
+ SELECT chunk_id, 1.0 AS w FROM chunk_entities WHERE entity IN (${exactPH})
3929
+ UNION ALL
3930
+ SELECT chunk_id, 0.4 AS w FROM chunk_entities
3931
+ WHERE (${likeClause}) AND entity NOT IN (${exactPH})
3932
+ )
3933
+ GROUP BY chunk_id
3934
+ ORDER BY score DESC
3935
+ LIMIT ?
3936
+ `).all(...entities, ...fuzzy.map((t2) => `%${esc(t2)}%`), ...entities, limit);
3937
+ return rows.map((r) => ({ chunkId: r.chunk_id, score: r.score }));
3938
+ },
3507
3939
  async getDocument(documentId) {
3508
3940
  const row = db.prepare("SELECT * FROM documents WHERE id = ?").get(documentId);
3509
3941
  if (!row)
@@ -3643,6 +4075,13 @@ function createTables(db, dimensions = 384) {
3643
4075
 
3644
4076
  CREATE INDEX IF NOT EXISTS idx_chunks_document_id ON chunks(document_id);
3645
4077
  CREATE INDEX IF NOT EXISTS idx_documents_content_hash ON documents(content_hash);
4078
+
4079
+ CREATE TABLE IF NOT EXISTS chunk_entities (
4080
+ chunk_id TEXT NOT NULL REFERENCES chunks(id) ON DELETE CASCADE,
4081
+ entity TEXT NOT NULL
4082
+ );
4083
+ CREATE INDEX IF NOT EXISTS idx_chunk_entities_entity ON chunk_entities(entity);
4084
+ CREATE INDEX IF NOT EXISTS idx_chunk_entities_chunk ON chunk_entities(chunk_id);
3646
4085
  `);
3647
4086
  db.exec(`
3648
4087
  CREATE TRIGGER IF NOT EXISTS chunks_ai AFTER INSERT ON chunks BEGIN
@@ -3698,32 +4137,125 @@ async function searchSemantic(store, embedder, query, limit) {
3698
4137
  return store.searchSemantic(embedding, limit);
3699
4138
  }
3700
4139
 
4140
+ // packages/core/dist/search/entity.js
4141
+ init_entity_extractor();
4142
+ async function searchEntities(store, query, limit) {
4143
+ if (typeof store.searchEntities !== "function")
4144
+ return [];
4145
+ const terms = extractQueryTerms(query);
4146
+ if (terms.length === 0)
4147
+ return [];
4148
+ return store.searchEntities(terms, limit);
4149
+ }
4150
+
3701
4151
  // packages/core/dist/search/rrf.js
3702
- function rrfFusion(listA, listB, k = 60, limit = 10) {
4152
+ function rrfFusionN(lists, k = 60, limit = 10, opts = {}) {
4153
+ const { weights, recencyScores, recencyWeight = 0 } = opts;
3703
4154
  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));
4155
+ for (let li = 0; li < lists.length; li++) {
4156
+ const w = weights?.[li] ?? 1;
4157
+ const list = lists[li];
4158
+ for (let i = 0; i < list.length; i++) {
4159
+ const id = list[i].chunkId;
4160
+ scores.set(id, (scores.get(id) ?? 0) + w * (1 / (k + i + 1)));
4161
+ }
3707
4162
  }
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));
4163
+ if (recencyWeight > 0 && recencyScores) {
4164
+ for (const [id, s] of scores) {
4165
+ const r = recencyScores.get(id) ?? 0.5;
4166
+ scores.set(id, s * (1 + recencyWeight * (r - 0.5)));
4167
+ }
3711
4168
  }
3712
4169
  return [...scores.entries()].sort((a, b) => b[1] - a[1]).slice(0, limit).map(([chunkId, score]) => ({ chunkId, score }));
3713
4170
  }
3714
4171
 
4172
+ // packages/core/dist/search/adaptive.js
4173
+ function createAdaptiveSearch(deps) {
4174
+ const { baseSearch } = deps;
4175
+ const searchHistory = [];
4176
+ const recentTags = [];
4177
+ return {
4178
+ async search(options) {
4179
+ const { context, ...baseOptions } = options;
4180
+ const results = await baseSearch.search(baseOptions);
4181
+ if (!context && searchHistory.length === 0)
4182
+ return results;
4183
+ const ctx = {
4184
+ recentSearches: context?.recentSearches ?? searchHistory.slice(-5),
4185
+ recentDocTags: context?.recentDocTags ?? recentTags.slice(-10),
4186
+ currentFilePath: context?.currentFilePath
4187
+ };
4188
+ const reranked = results.map((r) => {
4189
+ let boost = 0;
4190
+ const docTags = ctx.recentDocTags ?? [];
4191
+ if (docTags.length > 0 && r.document.tags.length > 0) {
4192
+ const docTagSet = new Set(r.document.tags);
4193
+ const overlap = docTags.filter((t2) => docTagSet.has(t2)).length;
4194
+ boost += Math.min(overlap / Math.max(docTags.length, 1), 1) * 0.3;
4195
+ }
4196
+ if (ctx.currentFilePath && r.document.filePath) {
4197
+ const ctxParts = ctx.currentFilePath.split("/");
4198
+ const docParts = r.document.filePath.split("/");
4199
+ let common = 0;
4200
+ for (let i = 0; i < Math.min(ctxParts.length, docParts.length); i++) {
4201
+ if (ctxParts[i] === docParts[i])
4202
+ common++;
4203
+ else
4204
+ break;
4205
+ }
4206
+ if (common > 0) {
4207
+ boost += Math.min(common / Math.max(ctxParts.length - 1, 1), 1) * 0.2;
4208
+ }
4209
+ }
4210
+ return { ...r, score: r.score * (1 + boost) };
4211
+ });
4212
+ reranked.sort((a, b) => b.score - a.score);
4213
+ searchHistory.push(options.query);
4214
+ if (searchHistory.length > 20)
4215
+ searchHistory.shift();
4216
+ for (const r of reranked.slice(0, 3)) {
4217
+ for (const t2 of r.document.tags) {
4218
+ recentTags.push(t2);
4219
+ }
4220
+ }
4221
+ while (recentTags.length > 30)
4222
+ recentTags.shift();
4223
+ return reranked;
4224
+ }
4225
+ };
4226
+ }
4227
+
3715
4228
  // packages/core/dist/search/index.js
4229
+ var DEFAULT_SIGNAL_WEIGHTS = {
4230
+ semantic: 1,
4231
+ bm25: 1,
4232
+ entity: 0.5,
4233
+ // conservative: ~20% candidate coverage (arxiv 2508.01405)
4234
+ recency: 0.2
4235
+ // ±10% bound on relevance
4236
+ };
3716
4237
  function createSearchEngine(deps) {
3717
- const { store, embedder, rrfK = 60 } = deps;
4238
+ const { store, embedder, rrfK = 60, getDecayEngine } = deps;
4239
+ const baseWeights = { ...DEFAULT_SIGNAL_WEIGHTS, ...deps.weights };
3718
4240
  const FETCH_LIMIT = 30;
3719
4241
  return {
3720
4242
  async search(options) {
3721
- const { query, limit = 10, threshold = 0, tags } = options;
3722
- const [bm25Results, semanticResults] = await Promise.all([
4243
+ const { query, limit = 10, threshold = 0, tags, signalWeights } = options;
4244
+ const w = { ...baseWeights, ...signalWeights };
4245
+ const [bm25Results, semanticResults, entityResults] = await Promise.all([
3723
4246
  searchBm25(store, query, FETCH_LIMIT),
3724
- searchSemantic(store, embedder, query, FETCH_LIMIT)
4247
+ searchSemantic(store, embedder, query, FETCH_LIMIT),
4248
+ searchEntities(store, query, FETCH_LIMIT)
3725
4249
  ]);
3726
- const fused = rrfFusion(semanticResults, bm25Results, rrfK, limit * 2);
4250
+ const lists = [semanticResults, bm25Results, entityResults];
4251
+ const weights = [w.semantic, w.bm25, w.entity];
4252
+ const decay = getDecayEngine?.();
4253
+ const recencyScores = decay ? await buildRecencyMap(store, decay, lists) : void 0;
4254
+ const fused = rrfFusionN(lists, rrfK, limit * 2, {
4255
+ weights,
4256
+ recencyScores,
4257
+ recencyWeight: recencyScores ? w.recency : 0
4258
+ });
3727
4259
  const results = [];
3728
4260
  for (const scored of fused) {
3729
4261
  if (scored.score < threshold)
@@ -3752,6 +4284,33 @@ function createSearchEngine(deps) {
3752
4284
  }
3753
4285
  };
3754
4286
  }
4287
+ async function buildRecencyMap(store, decay, lists) {
4288
+ const chunkIds = /* @__PURE__ */ new Set();
4289
+ for (const list of lists)
4290
+ for (const c of list)
4291
+ chunkIds.add(c.chunkId);
4292
+ if (chunkIds.size === 0)
4293
+ return /* @__PURE__ */ new Map();
4294
+ const chunkDoc = [];
4295
+ const docIds = /* @__PURE__ */ new Set();
4296
+ for (const chunkId of chunkIds) {
4297
+ const chunk = await store.getChunk(chunkId);
4298
+ if (!chunk)
4299
+ continue;
4300
+ chunkDoc.push({ chunkId, documentId: chunk.documentId });
4301
+ docIds.add(chunk.documentId);
4302
+ }
4303
+ if (docIds.size === 0)
4304
+ return /* @__PURE__ */ new Map();
4305
+ const rByDoc = await decay.getRetrievabilityForDocs([...docIds]);
4306
+ const map = /* @__PURE__ */ new Map();
4307
+ for (const { chunkId, documentId } of chunkDoc) {
4308
+ const r = rByDoc.get(documentId);
4309
+ if (r !== void 0)
4310
+ map.set(chunkId, r);
4311
+ }
4312
+ return map;
4313
+ }
3755
4314
  function extractHighlights(content, query) {
3756
4315
  const words = query.toLowerCase().split(/\s+/).filter((w) => w.length > 1);
3757
4316
  const lines = content.split("\n");
@@ -3794,6 +4353,7 @@ async function handleSearch(searchEngine, args) {
3794
4353
  tags: args.tags
3795
4354
  });
3796
4355
  return results.map((r) => ({
4356
+ documentId: r.document.id,
3797
4357
  title: r.document.title,
3798
4358
  filePath: r.document.filePath,
3799
4359
  heading: r.chunk.heading,
@@ -4334,6 +4894,12 @@ var DecayEngine = class {
4334
4894
  retrievability REAL NOT NULL DEFAULT 1.0,
4335
4895
  updated_at TEXT NOT NULL
4336
4896
  );
4897
+ -- 2026-05-15: getDecaying() \uC758 'WHERE retrievability < ? ORDER BY
4898
+ -- retrievability ASC' \uAC00 full table scan \uC73C\uB85C 1215+ docs vault \uC5D0\uC11C
4899
+ -- \uBB34\uAC70\uC6C0. index \uCD94\uAC00\uB85C sort+filter \uB458 \uB2E4 \uAC00\uC18D.
4900
+ CREATE INDEX IF NOT EXISTS idx_decay_state_retrievability ON decay_state(retrievability);
4901
+ -- recompute \uC2DC stale row \uBE60\uB974\uAC8C \uCC3E\uAE30.
4902
+ CREATE INDEX IF NOT EXISTS idx_decay_state_updated_at ON decay_state(updated_at);
4337
4903
  `);
4338
4904
  }
4339
4905
  /**
@@ -4447,6 +5013,25 @@ var DecayEngine = class {
4447
5013
  title: r.title
4448
5014
  }));
4449
5015
  }
5016
+ /**
5017
+ * Design Ref: §B3.3.3 — read-only live retrievability for a set of documents.
5018
+ * Reuses persisted stability + last_access and recomputes R fresh, ignoring the
5019
+ * stale decay_state.retrievability snapshot column. No writes → no contention
5020
+ * with recordAccess. Single parametrized IN(...) query (bounded by ≤90 fused
5021
+ * candidates); documents without a decay_state row are simply absent from the map.
5022
+ */
5023
+ async getRetrievabilityForDocs(documentIds) {
5024
+ const out = /* @__PURE__ */ new Map();
5025
+ if (documentIds.length === 0)
5026
+ return out;
5027
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5028
+ const placeholders = documentIds.map(() => "?").join(",");
5029
+ const rows = this.db.prepare(`SELECT document_id, stability, last_access FROM decay_state WHERE document_id IN (${placeholders})`).all(...documentIds);
5030
+ for (const r of rows) {
5031
+ out.set(r.document_id, computeRetrievability(r.stability, elapsedDays(r.last_access, now)));
5032
+ }
5033
+ return out;
5034
+ }
4450
5035
  /**
4451
5036
  * Initialize decay state for documents that don't have one yet.
4452
5037
  */
@@ -4570,12 +5155,99 @@ function createLearningPathTool(store) {
4570
5155
  };
4571
5156
  }
4572
5157
 
4573
- // packages/core/dist/mcp/tools/detect-gaps.js
5158
+ // packages/core/dist/intelligence/gap-cache.js
4574
5159
  init_gap_detector();
4575
- function createDetectGapsTool(store) {
5160
+ var CACHE_VERSION = 1;
5161
+ var DEFAULT_MAX_AGE_MS = 6 * 60 * 60 * 1e3;
5162
+ var inflightByDb = /* @__PURE__ */ new WeakMap();
5163
+ var generationByDb = /* @__PURE__ */ new WeakMap();
5164
+ function bumpGeneration(db) {
5165
+ const cur = generationByDb.get(db) ?? 0;
5166
+ const next = cur + 1;
5167
+ generationByDb.set(db, next);
5168
+ return next;
5169
+ }
5170
+ function getGeneration(db) {
5171
+ return generationByDb.get(db) ?? 0;
5172
+ }
5173
+ function ensureGapCacheTable(db) {
5174
+ db.exec(`
5175
+ CREATE TABLE IF NOT EXISTS gap_cache (
5176
+ id INTEGER PRIMARY KEY,
5177
+ payload TEXT NOT NULL,
5178
+ version INTEGER NOT NULL DEFAULT 1,
5179
+ computed_at TEXT NOT NULL
5180
+ );
5181
+ `);
5182
+ }
5183
+ function readCachedGapReport(db, maxAgeMs = DEFAULT_MAX_AGE_MS) {
5184
+ try {
5185
+ ensureGapCacheTable(db);
5186
+ const row = db.prepare("SELECT * FROM gap_cache WHERE id = 1").get();
5187
+ if (!row)
5188
+ return null;
5189
+ if (row.version !== CACHE_VERSION)
5190
+ return null;
5191
+ const computedAt = new Date(row.computed_at).getTime();
5192
+ if (Number.isNaN(computedAt))
5193
+ return null;
5194
+ if (Date.now() - computedAt > maxAgeMs)
5195
+ return null;
5196
+ return JSON.parse(row.payload);
5197
+ } catch {
5198
+ return null;
5199
+ }
5200
+ }
5201
+ async function computeAndCacheGaps(store, db) {
5202
+ const currentGeneration = getGeneration(db);
5203
+ const existing = inflightByDb.get(db);
5204
+ if (existing && existing.generation === currentGeneration) {
5205
+ return existing.promise;
5206
+ }
5207
+ const startGeneration = currentGeneration;
5208
+ const promise = (async () => {
5209
+ try {
5210
+ ensureGapCacheTable(db);
5211
+ const report = await detectKnowledgeGaps(store);
5212
+ if (getGeneration(db) === startGeneration) {
5213
+ const payload = JSON.stringify(report);
5214
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5215
+ db.prepare(`INSERT INTO gap_cache (id, payload, version, computed_at) VALUES (1, ?, ?, ?)
5216
+ ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, version = excluded.version, computed_at = excluded.computed_at`).run(payload, CACHE_VERSION, now);
5217
+ }
5218
+ return report;
5219
+ } finally {
5220
+ const cur = inflightByDb.get(db);
5221
+ if (cur?.generation === startGeneration)
5222
+ inflightByDb.delete(db);
5223
+ }
5224
+ })();
5225
+ inflightByDb.set(db, { generation: startGeneration, promise });
5226
+ return promise;
5227
+ }
5228
+ function invalidateGapCache(db) {
5229
+ try {
5230
+ ensureGapCacheTable(db);
5231
+ db.prepare("DELETE FROM gap_cache WHERE id = 1").run();
5232
+ bumpGeneration(db);
5233
+ } catch {
5234
+ }
5235
+ }
5236
+ async function getGapReport(store, db, opts = {}) {
5237
+ if (!opts.forceRefresh) {
5238
+ const cached = readCachedGapReport(db, opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS);
5239
+ if (cached)
5240
+ return { report: cached, fromCache: true };
5241
+ }
5242
+ const fresh = await computeAndCacheGaps(store, db);
5243
+ return { report: fresh, fromCache: false };
5244
+ }
5245
+
5246
+ // packages/core/dist/mcp/tools/detect-gaps.js
5247
+ function createDetectGapsTool(store, getDb) {
4576
5248
  return {
4577
5249
  name: "detect-gaps",
4578
- description: "Detect knowledge gaps between topic clusters. Returns gap severity, isolated nodes, and suggested topics to bridge gaps.",
5250
+ 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
5251
  inputSchema: {
4580
5252
  type: "object",
4581
5253
  properties: {
@@ -4583,12 +5255,17 @@ function createDetectGapsTool(store) {
4583
5255
  type: "string",
4584
5256
  description: "Minimum gap severity to include: high, medium, or low",
4585
5257
  enum: ["high", "medium", "low"]
5258
+ },
5259
+ forceRefresh: {
5260
+ type: "boolean",
5261
+ description: "Bypass cache and recompute (expensive: 30s+ on large vaults). Default false."
4586
5262
  }
4587
5263
  }
4588
5264
  },
4589
5265
  handler: async (args) => {
4590
5266
  const minSeverity = args.minSeverity ?? "medium";
4591
- const report = await detectKnowledgeGaps(store);
5267
+ const db = getDb();
5268
+ const { report, fromCache } = await getGapReport(store, db, { forceRefresh: args.forceRefresh });
4592
5269
  const sevOrder = { high: 0, medium: 1, low: 2 };
4593
5270
  const threshold = sevOrder[minSeverity] ?? 1;
4594
5271
  const filtered = report.gaps.filter((g) => sevOrder[g.severity] <= threshold);
@@ -4606,7 +5283,8 @@ function createDetectGapsTool(store) {
4606
5283
  suggestedTopic: g.suggestedTopic
4607
5284
  })),
4608
5285
  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."
5286
+ 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.",
5287
+ cacheStatus: fromCache ? "cached" : "fresh"
4610
5288
  }, null, 2)
4611
5289
  }]
4612
5290
  };
@@ -5036,11 +5714,11 @@ The note will appear in the graph after next index.`
5036
5714
  if (!source) {
5037
5715
  return { content: [{ type: "text", text: `Source note "${args.sourceTitle}" not found.` }] };
5038
5716
  }
5039
- const { readFileSync: readFileSync18 } = await import("node:fs");
5717
+ const { readFileSync: readFileSync19 } = await import("node:fs");
5040
5718
  const fullPath = join7(vaultPath, source.filePath);
5041
5719
  let existing = "";
5042
5720
  try {
5043
- existing = readFileSync18(fullPath, "utf-8");
5721
+ existing = readFileSync19(fullPath, "utf-8");
5044
5722
  } catch {
5045
5723
  }
5046
5724
  const linkSection = `
@@ -5065,14 +5743,15 @@ The note will appear in the graph after next index.`
5065
5743
  function createMcpServer(options) {
5066
5744
  const { store, searchEngine, embedder, vaultPath = "", decayEngine } = options;
5067
5745
  const ready = options.ready ?? Promise.resolve();
5746
+ const adaptiveSearch = createAdaptiveSearch({ baseSearch: searchEngine });
5068
5747
  const learningPathTool = createLearningPathTool(store);
5069
- const detectGapsTool = createDetectGapsTool(store);
5748
+ const detectGapsTool = createDetectGapsTool(store, () => store.getDb());
5070
5749
  const getEvolutionTool = createGetEvolutionTool(store);
5071
5750
  const linkCodeTool = createLinkCodeTool(searchEngine);
5072
5751
  const askTool = createAskTool(searchEngine, vaultPath);
5073
5752
  const generateDraftTool = createGenerateDraftTool(searchEngine, vaultPath);
5074
5753
  const agenticTools = embedder ? createAgenticGraphTools(store, embedder, vaultPath) : [];
5075
- const server = new Server({ name: "stellavault", version: "0.7.3" }, { capabilities: { tools: {} } });
5754
+ const server = new Server({ name: "stellavault", version: "0.8.1" }, { capabilities: { tools: {} } });
5076
5755
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
5077
5756
  tools: [
5078
5757
  searchToolDef,
@@ -5102,10 +5781,10 @@ function createMcpServer(options) {
5102
5781
  let result;
5103
5782
  switch (name) {
5104
5783
  case "search":
5105
- result = await handleSearch(searchEngine, args);
5106
- if (decayEngine && result && typeof result === "object" && "results" in result) {
5784
+ result = await handleSearch(adaptiveSearch, args);
5785
+ if (decayEngine && Array.isArray(result)) {
5107
5786
  const now = (/* @__PURE__ */ new Date()).toISOString();
5108
- for (const r of result.results ?? []) {
5787
+ for (const r of result) {
5109
5788
  if (r.documentId)
5110
5789
  decayEngine.recordAccess({ documentId: r.documentId, type: "mcp_query", timestamp: now }).catch(() => {
5111
5790
  });
@@ -5576,17 +6255,17 @@ function createKnowledgeRouter(opts) {
5576
6255
  res.status(404).json({ error: "Document not found" });
5577
6256
  return;
5578
6257
  }
5579
- const { readFileSync: readFileSync18, writeFileSync: writeFileSync22, unlinkSync } = await import("node:fs");
5580
- const { join: join30, resolve: resolve22 } = await import("node:path");
6258
+ const { readFileSync: readFileSync19, writeFileSync: writeFileSync23, unlinkSync } = await import("node:fs");
6259
+ const { join: join32, resolve: resolve22 } = await import("node:path");
5581
6260
  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));
6261
+ const keeperPath = resolve22(join32(vaultPath, keeper.filePath));
6262
+ const removedPath = resolve22(join32(vaultPath, removed.filePath));
5584
6263
  const vaultRoot = resolve22(vaultPath);
5585
6264
  if (!keeperPath.startsWith(vaultRoot) || !removedPath.startsWith(vaultRoot)) {
5586
6265
  res.status(400).json({ error: "Invalid file path" });
5587
6266
  return;
5588
6267
  }
5589
- const keeperContent = readFileSync18(keeperPath, "utf-8");
6268
+ const keeperContent = readFileSync19(keeperPath, "utf-8");
5590
6269
  const appendix = `
5591
6270
 
5592
6271
  ---
@@ -5594,7 +6273,7 @@ function createKnowledgeRouter(opts) {
5594
6273
  > Merged from: ${removed.title} (${removed.filePath})
5595
6274
 
5596
6275
  ${removed.content}`;
5597
- writeFileSync22(keeperPath, keeperContent + appendix, "utf-8");
6276
+ writeFileSync23(keeperPath, keeperContent + appendix, "utf-8");
5598
6277
  try {
5599
6278
  unlinkSync(removedPath);
5600
6279
  } catch {
@@ -5617,8 +6296,8 @@ ${removed.content}`;
5617
6296
  res.status(400).json({ error: "clusterA, clusterB required" });
5618
6297
  return;
5619
6298
  }
5620
- const { writeFileSync: writeFileSync22, mkdirSync: mkdirSync22 } = await import("node:fs");
5621
- const { join: join30, resolve: resolve22 } = await import("node:path");
6299
+ const { writeFileSync: writeFileSync23, mkdirSync: mkdirSync23 } = await import("node:fs");
6300
+ const { join: join32, resolve: resolve22 } = await import("node:path");
5622
6301
  const nameA = clusterA.replace(/\s*\(\d+\)$/, "");
5623
6302
  const nameB = clusterB.replace(/\s*\(\d+\)$/, "");
5624
6303
  const resultsA = await searchEngine.search({ query: nameA, limit: 3 });
@@ -5658,14 +6337,14 @@ ${removed.content}`;
5658
6337
  ""
5659
6338
  ].join("\n");
5660
6339
  const safeTitle = title.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, " ");
5661
- const dir = resolve22(join30(vaultPath, "01_Knowledge"));
6340
+ const dir = resolve22(join32(vaultPath, "01_Knowledge"));
5662
6341
  if (!dir.startsWith(resolve22(vaultPath))) {
5663
6342
  res.status(400).json({ error: "Invalid path" });
5664
6343
  return;
5665
6344
  }
5666
- mkdirSync22(dir, { recursive: true });
5667
- const filePath = join30(dir, `${safeTitle}.md`);
5668
- writeFileSync22(filePath, content, "utf-8");
6345
+ mkdirSync23(dir, { recursive: true });
6346
+ const filePath = join32(dir, `${safeTitle}.md`);
6347
+ writeFileSync23(filePath, content, "utf-8");
5669
6348
  res.json({ success: true, title: safeTitle, path: filePath });
5670
6349
  } catch (err) {
5671
6350
  console.error(err);
@@ -5815,12 +6494,12 @@ function createIngestRouter(opts) {
5815
6494
  return;
5816
6495
  }
5817
6496
  try {
5818
- const { writeFileSync: writeFileSync22, unlinkSync } = await import("node:fs");
5819
- const { join: join30 } = await import("node:path");
6497
+ const { writeFileSync: writeFileSync23, unlinkSync } = await import("node:fs");
6498
+ const { join: join32 } = await import("node:path");
5820
6499
  const { tmpdir } = await import("node:os");
5821
6500
  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);
6501
+ const tmpPath = join32(tmpdir(), `sv-upload-${Date.now()}-${safeName}`);
6502
+ writeFileSync23(tmpPath, file.buffer);
5824
6503
  const { extractFileContent: extractFileContent2 } = await Promise.resolve().then(() => (init_file_extractors(), file_extractors_exports));
5825
6504
  const ext = file.originalname.split(".").pop()?.toLowerCase() ?? "";
5826
6505
  const binaryExts = /* @__PURE__ */ new Set(["pdf", "docx", "pptx", "xlsx", "xls"]);
@@ -5926,11 +6605,11 @@ ${desc}
5926
6605
  if (content.length > 1e4)
5927
6606
  content = content.slice(0, 1e4) + "\n\n...(truncated)";
5928
6607
  }
5929
- const { writeFileSync: writeFileSync22, mkdirSync: mkdirSync22 } = await import("node:fs");
5930
- const { join: join30 } = await import("node:path");
6608
+ const { writeFileSync: writeFileSync23, mkdirSync: mkdirSync23 } = await import("node:fs");
6609
+ const { join: join32 } = await import("node:path");
5931
6610
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5932
- const clipDir = join30(vaultPath || ".", "06_Research", "clips");
5933
- mkdirSync22(clipDir, { recursive: true });
6611
+ const clipDir = join32(vaultPath || ".", "06_Research", "clips");
6612
+ mkdirSync23(clipDir, { recursive: true });
5934
6613
  const fileName = `${date} ${safeTitle}.md`;
5935
6614
  const md = `---
5936
6615
  title: "${safeTitle}"
@@ -5944,8 +6623,8 @@ tags: [clip${isYT ? ", youtube" : ""}]
5944
6623
  > Source: ${url}
5945
6624
 
5946
6625
  ${content}`;
5947
- writeFileSync22(join30(clipDir, fileName), md, "utf-8");
5948
- res.json({ success: true, fileName, path: join30(clipDir, fileName) });
6626
+ writeFileSync23(join32(clipDir, fileName), md, "utf-8");
6627
+ res.json({ success: true, fileName, path: join32(clipDir, fileName) });
5949
6628
  } catch (err) {
5950
6629
  console.error(err);
5951
6630
  res.status(500).json({ error: "Clip failed" });
@@ -6433,14 +7112,14 @@ function createApiServer(options) {
6433
7112
  res.status(404).json({ error: "Document not found" });
6434
7113
  return;
6435
7114
  }
6436
- const { resolve: resolve22, join: join30 } = await import("node:path");
6437
- const { writeFileSync: writeFileSync22, readFileSync: readFileSync18 } = await import("node:fs");
7115
+ const { resolve: resolve22, join: join32 } = await import("node:path");
7116
+ const { writeFileSync: writeFileSync23, readFileSync: readFileSync19 } = await import("node:fs");
6438
7117
  const fullPath = resolve22(vaultPath, doc.filePath);
6439
7118
  if (!fullPath.startsWith(resolve22(vaultPath))) {
6440
7119
  res.status(403).json({ error: "Access denied" });
6441
7120
  return;
6442
7121
  }
6443
- const existing = readFileSync18(fullPath, "utf-8");
7122
+ const existing = readFileSync19(fullPath, "utf-8");
6444
7123
  let updated = existing;
6445
7124
  if (title && title !== doc.title) {
6446
7125
  updated = updated.replace(/^title:\s*.+$/m, `title: "${title.replace(/"/g, "''")}"`);
@@ -6461,7 +7140,7 @@ function createApiServer(options) {
6461
7140
  updated = content;
6462
7141
  }
6463
7142
  }
6464
- writeFileSync22(fullPath, updated, "utf-8");
7143
+ writeFileSync23(fullPath, updated, "utf-8");
6465
7144
  await store.upsertDocument({
6466
7145
  ...doc,
6467
7146
  title: title ?? doc.title,
@@ -6484,13 +7163,13 @@ function createApiServer(options) {
6484
7163
  return;
6485
7164
  }
6486
7165
  const { resolve: resolve22 } = await import("node:path");
6487
- const { unlinkSync, existsSync: existsSync25 } = await import("node:fs");
7166
+ const { unlinkSync, existsSync: existsSync27 } = await import("node:fs");
6488
7167
  const fullPath = resolve22(vaultPath, doc.filePath);
6489
7168
  if (!fullPath.startsWith(resolve22(vaultPath))) {
6490
7169
  res.status(403).json({ error: "Access denied" });
6491
7170
  return;
6492
7171
  }
6493
- if (existsSync25(fullPath)) {
7172
+ if (existsSync27(fullPath)) {
6494
7173
  unlinkSync(fullPath);
6495
7174
  }
6496
7175
  await store.deleteByDocumentId(id);
@@ -6616,12 +7295,12 @@ function createApiServer(options) {
6616
7295
  const { resolve: resolve22 } = await import("node:path");
6617
7296
  const syncScript = resolve22(process.cwd(), "packages/sync/sync-to-obsidian.mjs");
6618
7297
  const syncDir = resolve22(process.cwd(), "packages/sync");
6619
- const { existsSync: existsSync25 } = await import("node:fs");
6620
- if (!existsSync25(syncScript)) {
7298
+ const { existsSync: existsSync27 } = await import("node:fs");
7299
+ if (!existsSync27(syncScript)) {
6621
7300
  res.json({ success: false, error: "sync script not found" });
6622
7301
  return;
6623
7302
  }
6624
- if (!existsSync25(resolve22(syncDir, ".env"))) {
7303
+ if (!existsSync27(resolve22(syncDir, ".env"))) {
6625
7304
  res.json({ success: false, error: ".env not found" });
6626
7305
  return;
6627
7306
  }
@@ -7016,8 +7695,8 @@ async function transcribeAudio(audioPath, options = {}) {
7016
7695
  try {
7017
7696
  execFileSync("whisper", args, { stdio: "pipe", timeout: 3e5 });
7018
7697
  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();
7698
+ const { readFileSync: readFileSync19 } = await import("node:fs");
7699
+ return readFileSync19(join14("/tmp/sv-whisper", outputName), "utf-8").trim();
7021
7700
  } catch (err) {
7022
7701
  throw new Error(`Whisper failed: ${err instanceof Error ? err.message : err}`);
7023
7702
  }
@@ -7253,11 +7932,33 @@ var CREDITS_FILE = join19(homedir11(), ".stellavault", "federation", "credits.js
7253
7932
  init_retry();
7254
7933
  init_math();
7255
7934
  init_indexer();
7935
+ init_config();
7256
7936
  function createKnowledgeHub(config, options = {}) {
7257
7937
  const embedder = createLocalEmbedder(config.embedding.localModel);
7258
7938
  const dims = embedder.dimensions;
7259
7939
  const store = createSqliteVecStore(config.dbPath, dims);
7260
- const searchEngine = createSearchEngine({ store, embedder, rrfK: config.search.rrfK });
7940
+ let _decay = null;
7941
+ const getDecayEngine = () => {
7942
+ if (_decay)
7943
+ return _decay;
7944
+ try {
7945
+ const db = store.getDb();
7946
+ if (!db)
7947
+ return void 0;
7948
+ _decay = new DecayEngine(db);
7949
+ return _decay;
7950
+ } catch {
7951
+ return void 0;
7952
+ }
7953
+ };
7954
+ const sw = resolveSearchWeights(config);
7955
+ const searchEngine = createSearchEngine({
7956
+ store,
7957
+ embedder,
7958
+ rrfK: config.search.rrfK,
7959
+ weights: { semantic: sw.semantic, bm25: sw.bm25, entity: sw.entity, recency: sw.recency },
7960
+ getDecayEngine
7961
+ });
7261
7962
  const mcpServer = createMcpServer({ store, searchEngine, vaultPath: config.vaultPath, ready: options.ready });
7262
7963
  return { store, embedder, searchEngine, mcpServer, config };
7263
7964
  }
@@ -7269,6 +7970,14 @@ function getVaultDbPath(vaultPath) {
7269
7970
  mkdirSync15(dir, { recursive: true });
7270
7971
  return join20(dir, `${hash}.db`);
7271
7972
  }
7973
+ function resolveDbPath(vault2, configDbPath) {
7974
+ const envDbPath = process.env.STELLAVAULT_DB_PATH?.trim();
7975
+ if (envDbPath)
7976
+ return envDbPath;
7977
+ if (configDbPath)
7978
+ return configDbPath;
7979
+ return getVaultDbPath(vault2);
7980
+ }
7272
7981
  async function indexCommand(vaultPath, opts = {}) {
7273
7982
  if (opts.profileMemory)
7274
7983
  process.env.STELLAVAULT_PROFILE_MEMORY = "1";
@@ -7278,7 +7987,7 @@ async function indexCommand(vaultPath, opts = {}) {
7278
7987
  console.error(chalk.red("Error: vault path required. Use stellavault index <path> or set vaultPath in .stellavault.json"));
7279
7988
  process.exit(1);
7280
7989
  }
7281
- const dbPath = vaultPath ? getVaultDbPath(vault2) : config.dbPath;
7990
+ const dbPath = resolveDbPath(vault2, config.dbPath);
7282
7991
  const existingVaults = listVaults();
7283
7992
  const vaultName = vault2.split(/[/\\]/).filter(Boolean).pop() ?? "vault";
7284
7993
  if (!existingVaults.some((v) => v.path === vault2)) {
@@ -7380,7 +8089,13 @@ async function searchCommand(query, options, cmd) {
7380
8089
  await store.initialize();
7381
8090
  const embedder = createLocalEmbedder(config.embedding.localModel);
7382
8091
  await embedder.initialize();
7383
- const engine = createSearchEngine({ store, embedder, rrfK: config.search.rrfK });
8092
+ const sw = resolveSearchWeights(config);
8093
+ const engine = createSearchEngine({
8094
+ store,
8095
+ embedder,
8096
+ rrfK: config.search.rrfK,
8097
+ weights: { semantic: sw.semantic, bm25: sw.bm25, entity: sw.entity, recency: sw.recency }
8098
+ });
7384
8099
  const results = await engine.search({ query, limit });
7385
8100
  await store.close();
7386
8101
  if (jsonMode) {
@@ -7422,6 +8137,8 @@ async function serveCommand() {
7422
8137
  console.error(chalk3.green("\u{1F680} MCP Server running (stdio mode) \u2014 index loading in background"));
7423
8138
  console.error(chalk3.dim("\u{1F4A1} Claude Code: claude mcp add stellavault -- stellavault serve"));
7424
8139
  const serverPromise = hub.mcpServer.startStdio();
8140
+ const watcherEnabled = (process.env.STELLAVAULT_WATCH ?? "1").trim() !== "0";
8141
+ let watcherHandle = null;
7425
8142
  (async () => {
7426
8143
  try {
7427
8144
  const t0 = Date.now();
@@ -7431,16 +8148,60 @@ async function serveCommand() {
7431
8148
  const elapsed = Date.now() - t0;
7432
8149
  console.error(`\u{1F4DA} ${stats.documentCount} documents | ${stats.chunkCount} chunks (ready in ${elapsed}ms)`);
7433
8150
  resolveReady();
8151
+ if (watcherEnabled && config.vaultPath) {
8152
+ try {
8153
+ watcherHandle = createWatcher({
8154
+ vaultPath: config.vaultPath,
8155
+ store: hub.store,
8156
+ embedder: hub.embedder,
8157
+ chunkOptions: config.chunking,
8158
+ debounceMs: Number(process.env.STELLAVAULT_WATCH_DEBOUNCE_MS ?? 5e3),
8159
+ onReindex: (r) => {
8160
+ console.error(`\u{1F440} watcher reindex: ${r.indexed} indexed, ${r.skipped} unchanged`);
8161
+ try {
8162
+ invalidateGapCache(hub.store.getDb());
8163
+ } catch {
8164
+ }
8165
+ }
8166
+ });
8167
+ watcherHandle.start();
8168
+ console.error(chalk3.green(`\u{1F440} Watcher started (debounce ${process.env.STELLAVAULT_WATCH_DEBOUNCE_MS ?? 5e3}ms) \u2014 vault changes auto-reindex`));
8169
+ } catch (err) {
8170
+ console.error(chalk3.yellow("\u26A0\uFE0F Watcher init failed: " + err.message));
8171
+ }
8172
+ } else if (!watcherEnabled) {
8173
+ console.error(chalk3.dim("\u{1F440} Watcher disabled (STELLAVAULT_WATCH=0)"));
8174
+ } else {
8175
+ console.error(chalk3.dim("\u{1F440} Watcher skipped (no config.vaultPath set)"));
8176
+ }
7434
8177
  } catch (err) {
7435
8178
  console.error(chalk3.red("\u274C Index load failed: " + err.message));
7436
8179
  resolveReady();
7437
8180
  }
7438
8181
  })();
8182
+ const cleanup = () => {
8183
+ try {
8184
+ watcherHandle?.stop();
8185
+ } catch {
8186
+ }
8187
+ };
8188
+ process.once("SIGINT", () => {
8189
+ cleanup();
8190
+ process.exit(130);
8191
+ });
8192
+ process.once("SIGTERM", () => {
8193
+ cleanup();
8194
+ process.exit(143);
8195
+ });
7439
8196
  await serverPromise;
8197
+ cleanup();
7440
8198
  }
7441
8199
 
8200
+ // packages/cli/dist/index.js
8201
+ init_setup_cmd();
8202
+
7442
8203
  // packages/cli/dist/commands/status-cmd.js
7443
- import chalk4 from "chalk";
8204
+ import chalk5 from "chalk";
7444
8205
  async function statusCommand(_opts, cmd) {
7445
8206
  const globalOpts = cmd?.parent?.opts?.() ?? {};
7446
8207
  const jsonMode = globalOpts.json;
@@ -7472,7 +8233,7 @@ async function statusCommand(_opts, cmd) {
7472
8233
  return;
7473
8234
  }
7474
8235
  console.log("");
7475
- console.log(chalk4.bold("\u{1F4CA} Stellavault Status"));
8236
+ console.log(chalk5.bold("\u{1F4CA} Stellavault Status"));
7476
8237
  console.log("\u2500".repeat(40));
7477
8238
  console.log(` \u{1F4C4} Documents: ${stats.documentCount}${totalFiles != null ? ` / ${totalFiles} files (${Math.round(stats.documentCount / totalFiles * 100)}%)` : ""}`);
7478
8239
  if (skippedFiles != null && skippedFiles > 0) {
@@ -7484,7 +8245,7 @@ async function statusCommand(_opts, cmd) {
7484
8245
  console.log(` \u{1F4C1} Vault: ${config.vaultPath || "(not set)"}`);
7485
8246
  if (topics.length > 0) {
7486
8247
  console.log("");
7487
- console.log(chalk4.bold("\u{1F3F7}\uFE0F Top topics:"));
8248
+ console.log(chalk5.bold("\u{1F3F7}\uFE0F Top topics:"));
7488
8249
  topics.slice(0, 10).forEach((t2) => {
7489
8250
  console.log(` #${t2.topic} (${t2.count})`);
7490
8251
  });
@@ -7493,22 +8254,22 @@ async function statusCommand(_opts, cmd) {
7493
8254
  }
7494
8255
 
7495
8256
  // packages/cli/dist/commands/graph-cmd.js
7496
- import chalk5 from "chalk";
8257
+ import chalk6 from "chalk";
7497
8258
  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";
8259
+ import { resolve as resolve11, dirname as dirname5 } from "node:path";
8260
+ import { existsSync as existsSync17 } from "node:fs";
7500
8261
  import { fileURLToPath } from "node:url";
7501
8262
  function locateBundledGraphUi() {
7502
8263
  try {
7503
- const here = dirname4(fileURLToPath(import.meta.url));
8264
+ const here = dirname5(fileURLToPath(import.meta.url));
7504
8265
  const bundled = resolve11(here, "graph-ui");
7505
- if (existsSync15(resolve11(bundled, "index.html")))
8266
+ if (existsSync17(resolve11(bundled, "index.html")))
7506
8267
  return bundled;
7507
8268
  const monorepoGraphDist = resolve11(here, "..", "..", "..", "graph", "dist");
7508
- if (existsSync15(resolve11(monorepoGraphDist, "index.html")))
8269
+ if (existsSync17(resolve11(monorepoGraphDist, "index.html")))
7509
8270
  return monorepoGraphDist;
7510
8271
  const singleFileBundle = resolve11(here, "..", "dist", "graph-ui");
7511
- if (existsSync15(resolve11(singleFileBundle, "index.html")))
8272
+ if (existsSync17(resolve11(singleFileBundle, "index.html")))
7512
8273
  return singleFileBundle;
7513
8274
  } catch {
7514
8275
  }
@@ -7517,12 +8278,12 @@ function locateBundledGraphUi() {
7517
8278
  async function graphCommand() {
7518
8279
  const config = loadConfig();
7519
8280
  const hub = createKnowledgeHub(config);
7520
- console.error(chalk5.dim("\u23F3 Initializing..."));
8281
+ console.error(chalk6.dim("\u23F3 Initializing..."));
7521
8282
  await hub.store.initialize();
7522
8283
  await hub.embedder.initialize();
7523
8284
  const stats = await hub.store.getStats();
7524
8285
  if (stats.documentCount === 0) {
7525
- console.error(chalk5.yellow("\u26A0 No documents indexed. Run `stellavault index <vault-path>` first."));
8286
+ console.error(chalk6.yellow("\u26A0 No documents indexed. Run `stellavault index <vault-path>` first."));
7526
8287
  process.exit(1);
7527
8288
  }
7528
8289
  const port = config.mcp.port || 3333;
@@ -7540,28 +8301,28 @@ async function graphCommand() {
7540
8301
  await api.start();
7541
8302
  } catch (err) {
7542
8303
  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} } }`));
8304
+ console.error(chalk6.red(`Port ${port} is already in use.`));
8305
+ console.error(chalk6.dim(`Stop the other process or use a different port:`));
8306
+ console.error(chalk6.dim(` Edit .stellavault.json: { "mcp": { "port": ${port + 1} } }`));
7546
8307
  process.exit(1);
7547
8308
  }
7548
8309
  throw err;
7549
8310
  }
7550
- console.error(chalk5.green("\u{1F9E0} Stellavault \u2014 Neural Knowledge Graph"));
8311
+ console.error(chalk6.green("\u{1F9E0} Stellavault \u2014 Neural Knowledge Graph"));
7551
8312
  console.error(` \u{1F4DA} ${stats.documentCount} documents | ${stats.chunkCount} chunks`);
7552
8313
  console.error(` \u{1F310} API: http://127.0.0.1:${port}`);
7553
8314
  if (graphUiPath) {
7554
8315
  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`));
8316
+ console.error(chalk6.green(` \u{1F52E} Graph: ${url}`));
8317
+ console.error(chalk6.dim(` Press Ctrl+C to stop`));
7557
8318
  openBrowser(url);
7558
8319
  process.on("SIGINT", () => process.exit(0));
7559
8320
  return;
7560
8321
  }
7561
8322
  const devGraphDir = resolve11(process.cwd(), "packages/graph");
7562
- const hasDevGraph = existsSync15(resolve11(devGraphDir, "package.json"));
8323
+ const hasDevGraph = existsSync17(resolve11(devGraphDir, "package.json"));
7563
8324
  if (hasDevGraph) {
7564
- console.error(chalk5.dim(" \u{1F680} Starting Vite dev server..."));
8325
+ console.error(chalk6.dim(" \u{1F680} Starting Vite dev server..."));
7565
8326
  const vite = spawn(process.platform === "win32" ? "npx.cmd" : "npx", ["vite", "--host"], {
7566
8327
  cwd: devGraphDir,
7567
8328
  stdio: ["ignore", "pipe", "pipe"]
@@ -7571,21 +8332,21 @@ async function graphCommand() {
7571
8332
  if (line.includes("Local:")) {
7572
8333
  const match = line.match(/http:\/\/localhost:\d+/);
7573
8334
  const url = match?.[0] ?? "http://localhost:5173";
7574
- console.error(chalk5.green(` \u{1F52E} Graph: ${url}`));
8335
+ console.error(chalk6.green(` \u{1F52E} Graph: ${url}`));
7575
8336
  openBrowser(url);
7576
8337
  }
7577
8338
  });
7578
8339
  vite.on("close", () => {
7579
- console.error(chalk5.dim(" Vite server stopped"));
8340
+ console.error(chalk6.dim(" Vite server stopped"));
7580
8341
  });
7581
8342
  process.on("SIGINT", () => {
7582
8343
  vite.kill();
7583
8344
  process.exit(0);
7584
8345
  });
7585
8346
  } else {
7586
- console.error(chalk5.yellow(" \u26A0 Bundled graph UI missing. Reinstall stellavault: npm i -g stellavault@latest"));
8347
+ console.error(chalk6.yellow(" \u26A0 Bundled graph UI missing. Reinstall stellavault: npm i -g stellavault@latest"));
7587
8348
  }
7588
- console.error(chalk5.dim(" Press Ctrl+C to stop"));
8349
+ console.error(chalk6.dim(" Press Ctrl+C to stop"));
7589
8350
  }
7590
8351
  async function openBrowser(url) {
7591
8352
  try {
@@ -7596,41 +8357,41 @@ async function openBrowser(url) {
7596
8357
  }
7597
8358
 
7598
8359
  // packages/cli/dist/commands/card-cmd.js
7599
- import chalk6 from "chalk";
7600
- import { writeFileSync as writeFileSync16 } from "node:fs";
8360
+ import chalk7 from "chalk";
8361
+ import { writeFileSync as writeFileSync17 } from "node:fs";
7601
8362
  import { resolve as resolve12 } from "node:path";
7602
8363
  async function cardCommand(options) {
7603
8364
  const output = options.output ?? "knowledge-card.svg";
7604
8365
  const outPath = resolve12(process.cwd(), output);
7605
- console.error(chalk6.dim("\u23F3 Generating profile card..."));
8366
+ console.error(chalk7.dim("\u23F3 Generating profile card..."));
7606
8367
  try {
7607
8368
  const res = await fetch("http://127.0.0.1:3333/api/profile-card");
7608
8369
  if (!res.ok)
7609
8370
  throw new Error(`API error: ${res.status}. Is 'stellavault graph' running?`);
7610
8371
  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})`));
8372
+ writeFileSync17(outPath, svg, "utf-8");
8373
+ console.error(chalk7.green(`\u2705 Profile card saved: ${outPath}`));
8374
+ console.error(chalk7.dim(" Embed in GitHub README:"));
8375
+ console.error(chalk7.dim(` ![Knowledge Card](${output})`));
7615
8376
  } catch (err) {
7616
- console.error(chalk6.red(`\u274C Failed: ${err}`));
7617
- console.error(chalk6.dim(" Make sure API server is running: stellavault graph"));
8377
+ console.error(chalk7.red(`\u274C Failed: ${err}`));
8378
+ console.error(chalk7.dim(" Make sure API server is running: stellavault graph"));
7618
8379
  process.exit(1);
7619
8380
  }
7620
8381
  }
7621
8382
 
7622
8383
  // 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");
8384
+ import chalk8 from "chalk";
8385
+ import { resolve as resolve13, join as join23 } from "node:path";
8386
+ import { readdirSync as readdirSync6, existsSync as existsSync18, readFileSync as readFileSync16, mkdirSync as mkdirSync17 } from "node:fs";
8387
+ import { homedir as homedir15 } from "node:os";
8388
+ var PACKS_DIR = join23(homedir15(), ".stellavault", "packs");
7628
8389
  async function packCreateCommand(name, options) {
7629
8390
  const config = loadConfig();
7630
8391
  const hub = createKnowledgeHub(config);
7631
8392
  await hub.store.initialize();
7632
8393
  await hub.embedder.initialize();
7633
- console.error(chalk7.dim("\u23F3 Creating pack..."));
8394
+ console.error(chalk8.dim("\u23F3 Creating pack..."));
7634
8395
  const { pack: pack2, piiReport } = await createPack(hub.store, hub.searchEngine, hub.embedder, {
7635
8396
  name,
7636
8397
  fromSearch: options.fromSearch,
@@ -7640,89 +8401,89 @@ async function packCreateCommand(name, options) {
7640
8401
  description: options.description,
7641
8402
  limit: options.limit ? parseInt(options.limit) : 100
7642
8403
  });
7643
- mkdirSync16(PACKS_DIR, { recursive: true });
7644
- const outPath = join21(PACKS_DIR, `${name}.sv-pack`);
8404
+ mkdirSync17(PACKS_DIR, { recursive: true });
8405
+ const outPath = join23(PACKS_DIR, `${name}.sv-pack`);
7645
8406
  exportPack(pack2, outPath);
7646
- console.error(chalk7.green(`\u2705 Pack created: ${name}`));
8407
+ console.error(chalk8.green(`\u2705 Pack created: ${name}`));
7647
8408
  console.error(` \u{1F4E6} ${pack2.chunks.length} chunks`);
7648
8409
  console.error(` \u{1F4BE} ${outPath}`);
7649
8410
  if (piiReport.redactedCount > 0) {
7650
- console.error(chalk7.yellow(` \u{1F512} PII masked: ${piiReport.redactedCount} items (${piiReport.types.join(", ")})`));
8411
+ console.error(chalk8.yellow(` \u{1F512} PII masked: ${piiReport.redactedCount} items (${piiReport.types.join(", ")})`));
7651
8412
  }
7652
8413
  await hub.store.close();
7653
8414
  }
7654
8415
  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}`));
8416
+ const srcPath = join23(PACKS_DIR, `${name}.sv-pack`);
8417
+ if (!existsSync18(srcPath)) {
8418
+ console.error(chalk8.red(`\u274C Pack not found: ${name}`));
7658
8419
  process.exit(1);
7659
8420
  }
7660
8421
  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}`));
8422
+ const content = readFileSync16(srcPath, "utf-8");
8423
+ const { writeFileSync: writeFileSync23 } = await import("node:fs");
8424
+ writeFileSync23(outPath, content);
8425
+ console.error(chalk8.green(`\u2705 Exported: ${outPath}`));
7665
8426
  }
7666
8427
  async function packImportCommand(filePath) {
7667
8428
  const absPath = resolve13(process.cwd(), filePath);
7668
- if (!existsSync16(absPath)) {
7669
- console.error(chalk7.red(`\u274C File not found: ${absPath}`));
8429
+ if (!existsSync18(absPath)) {
8430
+ console.error(chalk8.red(`\u274C File not found: ${absPath}`));
7670
8431
  process.exit(1);
7671
8432
  }
7672
8433
  const config = loadConfig();
7673
8434
  const hub = createKnowledgeHub(config);
7674
8435
  await hub.store.initialize();
7675
8436
  await hub.embedder.initialize();
7676
- console.error(chalk7.dim("\u23F3 Importing pack..."));
8437
+ console.error(chalk8.dim("\u23F3 Importing pack..."));
7677
8438
  const result = await importPack(hub.store, hub.embedder, absPath);
7678
- console.error(chalk7.green(`\u2705 Imported: ${result.imported} chunks`));
8439
+ console.error(chalk8.green(`\u2705 Imported: ${result.imported} chunks`));
7679
8440
  if (result.skipped > 0)
7680
- console.error(chalk7.yellow(` \u23ED\uFE0F Skipped: ${result.skipped}`));
8441
+ console.error(chalk8.yellow(` \u23ED\uFE0F Skipped: ${result.skipped}`));
7681
8442
  if (result.modelMismatch) {
7682
- console.error(chalk7.yellow(` \u26A0\uFE0F Model mismatch \u2014 ${result.reEmbedded} chunks re-embedded`));
8443
+ console.error(chalk8.yellow(` \u26A0\uFE0F Model mismatch \u2014 ${result.reEmbedded} chunks re-embedded`));
7683
8444
  }
7684
8445
  await hub.store.close();
7685
8446
  }
7686
8447
  async function packListCommand() {
7687
- mkdirSync16(PACKS_DIR, { recursive: true });
8448
+ mkdirSync17(PACKS_DIR, { recursive: true });
7688
8449
  const files = readdirSync6(PACKS_DIR).filter((f) => f.endsWith(".sv-pack"));
7689
8450
  if (files.length === 0) {
7690
- console.error(chalk7.dim("No packs found. Create one: stellavault pack create <name> --from-search <query>"));
8451
+ console.error(chalk8.dim("No packs found. Create one: stellavault pack create <name> --from-search <query>"));
7691
8452
  return;
7692
8453
  }
7693
- console.error(chalk7.green(`\u{1F4E6} ${files.length} packs in ${PACKS_DIR}
8454
+ console.error(chalk8.green(`\u{1F4E6} ${files.length} packs in ${PACKS_DIR}
7694
8455
  `));
7695
8456
  for (const file of files) {
7696
8457
  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})`);
8458
+ const pack2 = JSON.parse(readFileSync16(join23(PACKS_DIR, file), "utf-8"));
8459
+ console.error(` ${chalk8.bold(pack2.name)} v${pack2.version} \u2014 ${pack2.chunks.length} chunks (${pack2.license})`);
7699
8460
  } catch {
7700
8461
  console.error(` ${file} (invalid)`);
7701
8462
  }
7702
8463
  }
7703
8464
  }
7704
8465
  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}`));
8466
+ const filePath = join23(PACKS_DIR, `${name}.sv-pack`);
8467
+ if (!existsSync18(filePath)) {
8468
+ console.error(chalk8.red(`\u274C Pack not found: ${name}`));
7708
8469
  process.exit(1);
7709
8470
  }
7710
- const pack2 = JSON.parse(readFileSync15(filePath, "utf-8"));
8471
+ const pack2 = JSON.parse(readFileSync16(filePath, "utf-8"));
7711
8472
  console.error(packToSummary(pack2));
7712
8473
  }
7713
8474
 
7714
8475
  // packages/cli/dist/commands/decay-cmd.js
7715
- import chalk8 from "chalk";
8476
+ import chalk9 from "chalk";
7716
8477
  async function decayCommand(_opts, cmd) {
7717
8478
  const globalOpts = cmd?.parent?.opts?.() ?? {};
7718
8479
  const jsonMode = globalOpts.json;
7719
8480
  const config = loadConfig();
7720
8481
  const hub = createKnowledgeHub(config);
7721
- console.error(chalk8.dim("\u23F3 Initializing..."));
8482
+ console.error(chalk9.dim("\u23F3 Initializing..."));
7722
8483
  await hub.store.initialize();
7723
8484
  const db = hub.store.getDb();
7724
8485
  if (!db) {
7725
- console.error(chalk8.red("\u274C Cannot access database"));
8486
+ console.error(chalk9.red("\u274C Cannot access database"));
7726
8487
  process.exit(1);
7727
8488
  }
7728
8489
  const decayEngine = new DecayEngine(db);
@@ -7732,65 +8493,65 @@ async function decayCommand(_opts, cmd) {
7732
8493
  await hub.store.close();
7733
8494
  return;
7734
8495
  }
7735
- console.log(chalk8.green("\n\u{1F9E0} Knowledge Decay Report"));
7736
- console.log(chalk8.dim("\u2500".repeat(50)));
8496
+ console.log(chalk9.green("\n\u{1F9E0} Knowledge Decay Report"));
8497
+ console.log(chalk9.dim("\u2500".repeat(50)));
7737
8498
  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))}`);
8499
+ console.log(` \u26A0\uFE0F Decaying (R<0.5): ${chalk9.yellow(String(report.decayingCount))}`);
8500
+ console.log(` \u{1F534} Critical (R<0.3): ${chalk9.red(String(report.criticalCount))}`);
7740
8501
  console.log(` \u{1F4CA} Average R: ${report.averageR}`);
7741
- console.log(chalk8.dim("\u2500".repeat(50)));
8502
+ console.log(chalk9.dim("\u2500".repeat(50)));
7742
8503
  if (report.topDecaying.length > 0) {
7743
- console.log(chalk8.yellow("\n\u{1F4CB} Top Decaying Notes (need review):"));
8504
+ console.log(chalk9.yellow("\n\u{1F4CB} Top Decaying Notes (need review):"));
7744
8505
  for (const d of report.topDecaying.slice(0, 20)) {
7745
8506
  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;
8507
+ const color = d.retrievability < 0.3 ? chalk9.red : chalk9.yellow;
7747
8508
  console.log(` ${color(rBar)} R=${d.retrievability.toFixed(2)} | ${d.daysSinceAccess}d ago | ${d.title}`);
7748
8509
  }
7749
8510
  }
7750
8511
  if (report.clusterHealth.length > 0) {
7751
- console.log(chalk8.dim("\n\u{1F4CA} Cluster Health:"));
8512
+ console.log(chalk9.dim("\n\u{1F4CA} Cluster Health:"));
7752
8513
  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;
8514
+ const color = c.avgR < 0.3 ? chalk9.red : c.avgR < 0.5 ? chalk9.yellow : chalk9.green;
7754
8515
  console.log(` ${color(`R=${c.avgR.toFixed(2)}`)} | ${c.count} docs | ${c.label}`);
7755
8516
  }
7756
8517
  }
7757
- console.log(chalk8.dim("\n\u{1F4A1} Tip: stellavault search <topic> to refresh decaying knowledge"));
8518
+ console.log(chalk9.dim("\n\u{1F4A1} Tip: stellavault search <topic> to refresh decaying knowledge"));
7758
8519
  }
7759
8520
 
7760
8521
  // packages/cli/dist/commands/sync-cmd.js
7761
- import chalk9 from "chalk";
8522
+ import chalk10 from "chalk";
7762
8523
  import { spawn as spawn2 } from "node:child_process";
7763
8524
  import { resolve as resolve14 } from "node:path";
7764
- import { existsSync as existsSync17 } from "node:fs";
8525
+ import { existsSync as existsSync19 } from "node:fs";
7765
8526
  async function syncCommand(options) {
7766
8527
  const syncDir = resolve14(process.cwd(), "packages/sync");
7767
8528
  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"));
8529
+ if (!existsSync19(syncScript)) {
8530
+ console.error(chalk10.red("\u274C packages/sync/sync-to-obsidian.mjs not found"));
8531
+ console.error(chalk10.dim(" Run from project root: cd notion-obsidian-sync"));
7771
8532
  process.exit(1);
7772
8533
  }
7773
8534
  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"));
8535
+ if (!existsSync19(envFile)) {
8536
+ console.error(chalk10.red("\u274C packages/sync/.env not found"));
8537
+ console.error(chalk10.dim(" Copy .env.example \u2192 .env and set NOTION_TOKEN"));
7777
8538
  process.exit(1);
7778
8539
  }
7779
8540
  if (options.upload) {
7780
8541
  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"));
8542
+ if (!existsSync19(uploadScript)) {
8543
+ console.error(chalk10.red("\u274C upload-pdca-to-notion.mjs not found"));
7783
8544
  process.exit(1);
7784
8545
  }
7785
- console.error(chalk9.dim("\u{1F4E4} Uploading PDCA documents to Notion..."));
8546
+ console.error(chalk10.dim("\u{1F4E4} Uploading PDCA documents to Notion..."));
7786
8547
  await runScript(uploadScript, syncDir);
7787
8548
  } else {
7788
- console.error(chalk9.dim("\u{1F504} Syncing Notion \u2192 Obsidian..."));
8549
+ console.error(chalk10.dim("\u{1F504} Syncing Notion \u2192 Obsidian..."));
7789
8550
  await runScript(syncScript, syncDir);
7790
8551
  if (options.watch) {
7791
- console.error(chalk9.green("\u{1F440} Watch mode \u2014 syncing every 5 minutes"));
8552
+ console.error(chalk10.green("\u{1F440} Watch mode \u2014 syncing every 5 minutes"));
7792
8553
  setInterval(async () => {
7793
- console.error(chalk9.dim(`\u{1F504} [${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Re-syncing...`));
8554
+ console.error(chalk10.dim(`\u{1F504} [${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Re-syncing...`));
7794
8555
  await runScript(syncScript, syncDir);
7795
8556
  }, 5 * 60 * 1e3);
7796
8557
  process.stdin.resume();
@@ -7815,7 +8576,7 @@ function runScript(scriptPath, cwd) {
7815
8576
  }
7816
8577
 
7817
8578
  // packages/cli/dist/commands/review-cmd.js
7818
- import chalk10 from "chalk";
8579
+ import chalk11 from "chalk";
7819
8580
  import { createInterface } from "node:readline";
7820
8581
  function globToRegex(glob) {
7821
8582
  const esc = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".+").replace(/\*/g, "[^/]*").replace(/\?/g, ".");
@@ -7839,11 +8600,11 @@ async function reviewCommand(options) {
7839
8600
  const minAgeDays = parseInt(options.minAge ?? "0", 10);
7840
8601
  const excludeRe = options.exclude ? globToRegex(options.exclude) : null;
7841
8602
  if (!options.json)
7842
- console.error(chalk10.dim("\u23F3 Initializing..."));
8603
+ console.error(chalk11.dim("\u23F3 Initializing..."));
7843
8604
  await hub.store.initialize();
7844
8605
  const db = hub.store.getDb();
7845
8606
  if (!db) {
7846
- console.error(chalk10.red("\u274C Cannot access database"));
8607
+ console.error(chalk11.red("\u274C Cannot access database"));
7847
8608
  process.exit(1);
7848
8609
  }
7849
8610
  const decayEngine = new DecayEngine(db);
@@ -7883,12 +8644,12 @@ async function reviewCommand(options) {
7883
8644
  return;
7884
8645
  }
7885
8646
  if (decaying.length === 0) {
7886
- console.log(chalk10.green("\n\u2728 All knowledge is healthy! No notes to review."));
8647
+ console.log(chalk11.green("\n\u2728 All knowledge is healthy! No notes to review."));
7887
8648
  return;
7888
8649
  }
7889
- console.log(chalk10.green(`
8650
+ console.log(chalk11.green(`
7890
8651
  \u{1F9E0} Today's review (${decaying.length})`));
7891
- console.log(chalk10.dim("\u2500".repeat(50)));
8652
+ console.log(chalk11.dim("\u2500".repeat(50)));
7892
8653
  const rl = createInterface({ input: process.stdin, output: process.stdout });
7893
8654
  const ask2 = (q) => new Promise((r) => rl.question(q, r));
7894
8655
  let reviewed = 0;
@@ -7896,13 +8657,13 @@ async function reviewCommand(options) {
7896
8657
  const d = decaying[i];
7897
8658
  const elapsed = Math.round((Date.now() - new Date(d.lastAccess).getTime()) / 864e5);
7898
8659
  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;
8660
+ const color = d.retrievability < 0.3 ? chalk11.red : chalk11.yellow;
7900
8661
  console.log(`
7901
- ${chalk10.bold(`[${i + 1}/${decaying.length}]`)} ${chalk10.cyan(d.title)}`);
8662
+ ${chalk11.bold(`[${i + 1}/${decaying.length}]`)} ${chalk11.cyan(d.title)}`);
7902
8663
  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: "));
8664
+ const answer = await ask2(chalk11.dim(" \u2192 [y]open [n]skip [s]snooze [q]quit: "));
7904
8665
  if (answer.toLowerCase() === "q") {
7905
- console.log(chalk10.dim("\nReview stopped."));
8666
+ console.log(chalk11.dim("\nReview stopped."));
7906
8667
  break;
7907
8668
  }
7908
8669
  if (answer.toLowerCase() === "s") {
@@ -7912,7 +8673,7 @@ ${chalk10.bold(`[${i + 1}/${decaying.length}]`)} ${chalk10.cyan(d.title)}`);
7912
8673
  timestamp: new Date(Date.now() - 23 * 36e5).toISOString()
7913
8674
  // 23시간 전으로 기록
7914
8675
  });
7915
- console.log(chalk10.dim(" \u23F0 Reminder set for tomorrow"));
8676
+ console.log(chalk11.dim(" \u23F0 Reminder set for tomorrow"));
7916
8677
  continue;
7917
8678
  }
7918
8679
  if (answer.toLowerCase() === "y") {
@@ -7935,14 +8696,14 @@ ${chalk10.bold(`[${i + 1}/${decaying.length}]`)} ${chalk10.cyan(d.title)}`);
7935
8696
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
7936
8697
  });
7937
8698
  reviewed++;
7938
- console.log(chalk10.green(" \u2705 Opened + memory strength updated"));
8699
+ console.log(chalk11.green(" \u2705 Opened + memory strength updated"));
7939
8700
  } else {
7940
- console.log(chalk10.dim(" \u23ED\uFE0F Skipped"));
8701
+ console.log(chalk11.dim(" \u23ED\uFE0F Skipped"));
7941
8702
  }
7942
8703
  }
7943
8704
  rl.close();
7944
- console.log(chalk10.dim("\n\u2500".repeat(50)));
7945
- console.log(chalk10.green(`Review complete! ${reviewed}/${decaying.length} reviewed`));
8705
+ console.log(chalk11.dim("\n\u2500".repeat(50)));
8706
+ console.log(chalk11.green(`Review complete! ${reviewed}/${decaying.length} reviewed`));
7946
8707
  try {
7947
8708
  const days = db.prepare(`
7948
8709
  SELECT DISTINCT date(accessed_at) as d FROM access_log
@@ -7959,94 +8720,94 @@ ${chalk10.bold(`[${i + 1}/${decaying.length}]`)} ${chalk10.cyan(d.title)}`);
7959
8720
  break;
7960
8721
  }
7961
8722
  if (streak > 1) {
7962
- console.log(chalk10.yellow(`\u{1F525} ${streak}-day review streak!`));
8723
+ console.log(chalk11.yellow(`\u{1F525} ${streak}-day review streak!`));
7963
8724
  }
7964
8725
  } catch {
7965
8726
  }
7966
8727
  }
7967
8728
 
7968
8729
  // packages/cli/dist/commands/duplicates-cmd.js
7969
- import chalk11 from "chalk";
8730
+ import chalk12 from "chalk";
7970
8731
  async function duplicatesCommand(options) {
7971
8732
  const config = loadConfig();
7972
8733
  const hub = createKnowledgeHub(config);
7973
8734
  const threshold = parseFloat(options.threshold ?? "0.88");
7974
- console.error(chalk11.dim("\u23F3 Scanning for duplicates..."));
8735
+ console.error(chalk12.dim("\u23F3 Scanning for duplicates..."));
7975
8736
  await hub.store.initialize();
7976
8737
  await hub.embedder.initialize();
7977
8738
  const pairs = await detectDuplicates(hub.store, threshold, 20);
7978
8739
  if (pairs.length === 0) {
7979
- console.log(chalk11.green("\n\u2728 No duplicate notes found!"));
8740
+ console.log(chalk12.green("\n\u2728 No duplicate notes found!"));
7980
8741
  return;
7981
8742
  }
7982
- console.log(chalk11.yellow(`
8743
+ console.log(chalk12.yellow(`
7983
8744
  \u{1F50D} Found ${pairs.length} similar note pairs (threshold: ${threshold})`));
7984
- console.log(chalk11.dim("\u2500".repeat(60)));
8745
+ console.log(chalk12.dim("\u2500".repeat(60)));
7985
8746
  for (let i = 0; i < pairs.length; i++) {
7986
8747
  const p = pairs[i];
7987
8748
  const pct = Math.round(p.similarity * 100);
7988
- const color = pct >= 95 ? chalk11.red : chalk11.yellow;
8749
+ const color = pct >= 95 ? chalk12.red : chalk12.yellow;
7989
8750
  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)}`);
8751
+ ${chalk12.bold(`[${i + 1}]`)} ${color(`${pct}% similar`)}`);
8752
+ console.log(` A: ${chalk12.cyan(p.docA.title)}`);
8753
+ console.log(` ${chalk12.dim(p.docA.filePath)}`);
8754
+ console.log(` B: ${chalk12.cyan(p.docB.title)}`);
8755
+ console.log(` ${chalk12.dim(p.docB.filePath)}`);
7995
8756
  }
7996
- console.log(chalk11.dim("\n\u{1F4A1} Merge or delete duplicates in Obsidian"));
8757
+ console.log(chalk12.dim("\n\u{1F4A1} Merge or delete duplicates in Obsidian"));
7997
8758
  }
7998
8759
 
7999
8760
  // packages/cli/dist/commands/gaps-cmd.js
8000
- import chalk12 from "chalk";
8761
+ import chalk13 from "chalk";
8001
8762
  async function gapsCommand() {
8002
8763
  const config = loadConfig();
8003
8764
  const hub = createKnowledgeHub(config);
8004
- console.error(chalk12.dim("\u23F3 Analyzing knowledge gaps..."));
8765
+ console.error(chalk13.dim("\u23F3 Analyzing knowledge gaps..."));
8005
8766
  await hub.store.initialize();
8006
8767
  await hub.embedder.initialize();
8007
8768
  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)));
8769
+ console.log(chalk13.green("\n\u{1F573}\uFE0F Knowledge Gap Report"));
8770
+ console.log(chalk13.dim("\u2500".repeat(50)));
8010
8771
  console.log(` Clusters: ${report.totalClusters}`);
8011
- console.log(` Gaps: ${chalk12.yellow(String(report.totalGaps))} (High+Medium)`);
8772
+ console.log(` Gaps: ${chalk13.yellow(String(report.totalGaps))} (High+Medium)`);
8012
8773
  console.log(` Isolated nodes: ${report.isolatedNodes.length}`);
8013
- console.log(chalk12.dim("\u2500".repeat(50)));
8774
+ console.log(chalk13.dim("\u2500".repeat(50)));
8014
8775
  if (report.gaps.length > 0) {
8015
- console.log(chalk12.yellow("\n\u{1F4CA} Inter-cluster gaps:"));
8776
+ console.log(chalk13.yellow("\n\u{1F4CA} Inter-cluster gaps:"));
8016
8777
  for (const gap of report.gaps) {
8017
8778
  const icon = gap.severity === "high" ? "\u{1F534}" : gap.severity === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
8018
8779
  console.log(` ${icon} ${gap.clusterA} \u2194 ${gap.clusterB}`);
8019
- console.log(` Bridges: ${gap.bridgeCount} | Suggestion: ${chalk12.cyan(gap.suggestedTopic)}`);
8780
+ console.log(` Bridges: ${gap.bridgeCount} | Suggestion: ${chalk13.cyan(gap.suggestedTopic)}`);
8020
8781
  }
8021
8782
  }
8022
8783
  if (report.isolatedNodes.length > 0) {
8023
- console.log(chalk12.dim("\n\u{1F3DD}\uFE0F Isolated notes (\u22641 connections):"));
8784
+ console.log(chalk13.dim("\n\u{1F3DD}\uFE0F Isolated notes (\u22641 connections):"));
8024
8785
  for (const n of report.isolatedNodes.slice(0, 10)) {
8025
8786
  console.log(` \u2022 ${n.title} (${n.connections} connections)`);
8026
8787
  }
8027
8788
  }
8028
- console.log(chalk12.dim("\n\u{1F4A1} Filling knowledge gaps strengthens your graph"));
8789
+ console.log(chalk13.dim("\n\u{1F4A1} Filling knowledge gaps strengthens your graph"));
8029
8790
  }
8030
8791
 
8031
8792
  // 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";
8793
+ import chalk14 from "chalk";
8794
+ import { writeFileSync as writeFileSync18, mkdirSync as mkdirSync18 } from "node:fs";
8795
+ import { join as join24 } from "node:path";
8035
8796
  async function clipCommand(url, options) {
8036
8797
  if (!url) {
8037
- console.error(chalk13.red("\u274C Please provide a URL: stellavault clip <url>"));
8798
+ console.error(chalk14.red("\u274C Please provide a URL: stellavault clip <url>"));
8038
8799
  process.exit(1);
8039
8800
  }
8040
8801
  const config = loadConfig();
8041
8802
  const vaultPath = config.vaultPath;
8042
8803
  if (!vaultPath) {
8043
- console.error(chalk13.red("\u274C vaultPath not configured"));
8804
+ console.error(chalk14.red("\u274C vaultPath not configured"));
8044
8805
  process.exit(1);
8045
8806
  }
8046
8807
  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}`));
8808
+ const targetDir = join24(vaultPath, folder);
8809
+ mkdirSync18(targetDir, { recursive: true });
8810
+ console.error(chalk14.dim(`\u{1F4CE} Clipping: ${url}`));
8050
8811
  try {
8051
8812
  const isYouTube = /youtube\.com\/watch|youtu\.be\//.test(url);
8052
8813
  let title;
@@ -8063,7 +8824,7 @@ async function clipCommand(url, options) {
8063
8824
  const safeTitle = title.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, " ").trim().slice(0, 80);
8064
8825
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
8065
8826
  const fileName = `${date} ${safeTitle}.md`;
8066
- const filePath = join22(targetDir, fileName);
8827
+ const filePath = join24(targetDir, fileName);
8067
8828
  const md = [
8068
8829
  "---",
8069
8830
  `title: "${safeTitle}"`,
@@ -8079,12 +8840,12 @@ async function clipCommand(url, options) {
8079
8840
  "",
8080
8841
  content
8081
8842
  ].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"));
8843
+ writeFileSync18(filePath, md, "utf-8");
8844
+ console.log(chalk14.green(`\u2705 Saved: ${fileName}`));
8845
+ console.log(chalk14.dim(` \u2192 ${filePath}`));
8846
+ console.log(chalk14.dim(" \u{1F4A1} Run stellavault index to make it searchable"));
8086
8847
  } catch (err) {
8087
- console.error(chalk13.red(`\u274C Clip failed: ${err.message}`));
8848
+ console.error(chalk14.red(`\u274C Clip failed: ${err.message}`));
8088
8849
  process.exit(1);
8089
8850
  }
8090
8851
  }
@@ -8127,7 +8888,7 @@ async function clipYouTube(url) {
8127
8888
  }
8128
8889
 
8129
8890
  // packages/cli/dist/commands/brief-cmd.js
8130
- import chalk14 from "chalk";
8891
+ import chalk15 from "chalk";
8131
8892
  async function briefCommand() {
8132
8893
  const config = loadConfig();
8133
8894
  const hub = createKnowledgeHub(config);
@@ -8135,31 +8896,31 @@ async function briefCommand() {
8135
8896
  await hub.embedder.initialize();
8136
8897
  const db = hub.store.getDb();
8137
8898
  if (!db) {
8138
- console.error(chalk14.red("\u274C Cannot access database"));
8899
+ console.error(chalk15.red("\u274C Cannot access database"));
8139
8900
  process.exit(1);
8140
8901
  }
8141
8902
  const decayEngine = new DecayEngine(db);
8142
8903
  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)));
8904
+ console.log(chalk15.green("\n\u2600\uFE0F Good morning! Today's knowledge briefing"));
8905
+ console.log(chalk15.dim("\u2500".repeat(50)));
8145
8906
  console.log(`
8146
- \u{1F4DA} ${chalk14.bold(String(stats.documentCount))} notes | ${stats.chunkCount} chunks`);
8907
+ \u{1F4DA} ${chalk15.bold(String(stats.documentCount))} notes | ${stats.chunkCount} chunks`);
8147
8908
  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))}`);
8909
+ const avgRColor = report.averageR >= 0.7 ? chalk15.green : report.averageR >= 0.5 ? chalk15.yellow : chalk15.red;
8910
+ console.log(`\u{1F9E0} Overall health: ${avgRColor("R=" + report.averageR)} | Decaying ${chalk15.yellow(String(report.decayingCount))} | Critical ${chalk15.red(String(report.criticalCount))}`);
8150
8911
  if (report.topDecaying.length > 0) {
8151
- console.log(chalk14.yellow("\n\u{1F4CB} Review recommendations:"));
8912
+ console.log(chalk15.yellow("\n\u{1F4CB} Review recommendations:"));
8152
8913
  for (const d of report.topDecaying.slice(0, 5)) {
8153
8914
  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}`);
8915
+ console.log(` ${chalk15.dim(bar)} R=${d.retrievability.toFixed(2)} ${d.title}`);
8155
8916
  }
8156
- console.log(chalk14.dim(" \u2192 Run stellavault review to start"));
8917
+ console.log(chalk15.dim(" \u2192 Run stellavault review to start"));
8157
8918
  }
8158
8919
  try {
8159
8920
  const gapReport = await detectKnowledgeGaps(hub.store);
8160
8921
  const highGaps = gapReport.gaps.filter((g) => g.severity === "high");
8161
8922
  if (highGaps.length > 0) {
8162
- console.log(chalk14.yellow(`
8923
+ console.log(chalk15.yellow(`
8163
8924
  \u{1F573}\uFE0F ${highGaps.length} knowledge gaps:`));
8164
8925
  for (const g of highGaps.slice(0, 3)) {
8165
8926
  console.log(` \u{1F534} ${g.clusterA.replace(/\s*\(\d+\)$/, "")} \u2194 ${g.clusterB.replace(/\s*\(\d+\)$/, "")}`);
@@ -8181,7 +8942,7 @@ async function briefCommand() {
8181
8942
  break;
8182
8943
  }
8183
8944
  if (streak > 0)
8184
- console.log(chalk14.yellow(`
8945
+ console.log(chalk15.yellow(`
8185
8946
  \u{1F525} ${streak}-day review streak!`));
8186
8947
  } catch {
8187
8948
  }
@@ -8192,7 +8953,7 @@ async function briefCommand() {
8192
8953
  GROUP BY document_id ORDER BY cnt DESC LIMIT 3
8193
8954
  `).all();
8194
8955
  if (recent.length > 0) {
8195
- console.log(chalk14.dim("\n\u{1F4CA} Most viewed notes this week:"));
8956
+ console.log(chalk15.dim("\n\u{1F4CA} Most viewed notes this week:"));
8196
8957
  for (const r of recent) {
8197
8958
  const doc = db.prepare("SELECT title FROM documents WHERE id = ?").get(r.document_id);
8198
8959
  console.log(` ${r.cnt} views \u2014 ${doc?.title ?? r.document_id}`);
@@ -8200,12 +8961,12 @@ async function briefCommand() {
8200
8961
  }
8201
8962
  } catch {
8202
8963
  }
8203
- console.log("\n" + chalk14.dim("\u2500".repeat(50)));
8204
- console.log(chalk14.dim("\u{1F4A1} stellavault review | stellavault gaps | stellavault graph"));
8964
+ console.log("\n" + chalk15.dim("\u2500".repeat(50)));
8965
+ console.log(chalk15.dim("\u{1F4A1} stellavault review | stellavault gaps | stellavault graph"));
8205
8966
  }
8206
8967
 
8207
8968
  // packages/cli/dist/commands/digest-cmd.js
8208
- import chalk15 from "chalk";
8969
+ import chalk16 from "chalk";
8209
8970
  async function digestCommand(options) {
8210
8971
  const config = loadConfig();
8211
8972
  const hub = createKnowledgeHub(config);
@@ -8213,12 +8974,12 @@ async function digestCommand(options) {
8213
8974
  await hub.store.initialize();
8214
8975
  const db = hub.store.getDb();
8215
8976
  if (!db) {
8216
- console.error(chalk15.red("\u274C Cannot access database"));
8977
+ console.error(chalk16.red("\u274C Cannot access database"));
8217
8978
  process.exit(1);
8218
8979
  }
8219
- console.log(chalk15.green(`
8980
+ console.log(chalk16.green(`
8220
8981
  \u{1F4CA} Knowledge activity report (last ${days} days)`));
8221
- console.log(chalk15.dim("\u2500".repeat(50)));
8982
+ console.log(chalk16.dim("\u2500".repeat(50)));
8222
8983
  const accessStats = db.prepare(`
8223
8984
  SELECT access_type, COUNT(*) as cnt
8224
8985
  FROM access_log WHERE accessed_at > datetime('now', '-${days} days')
@@ -8226,7 +8987,7 @@ async function digestCommand(options) {
8226
8987
  `).all();
8227
8988
  const totalAccess = accessStats.reduce((s, r) => s + r.cnt, 0);
8228
8989
  console.log(`
8229
- \u{1F50D} Total access: ${chalk15.bold(String(totalAccess))} times`);
8990
+ \u{1F50D} Total access: ${chalk16.bold(String(totalAccess))} times`);
8230
8991
  for (const r of accessStats) {
8231
8992
  const icon = r.access_type === "view" ? "\u{1F441}\uFE0F" : r.access_type === "search" ? "\u{1F50D}" : "\u{1F916}";
8232
8993
  console.log(` ${icon} ${r.access_type}: ${r.cnt} times`);
@@ -8240,11 +9001,11 @@ async function digestCommand(options) {
8240
9001
  ORDER BY cnt DESC LIMIT 10
8241
9002
  `).all();
8242
9003
  if (topDocs.length > 0) {
8243
- console.log(chalk15.dim(`
9004
+ console.log(chalk16.dim(`
8244
9005
  \u{1F4C4} Most accessed notes:`));
8245
9006
  for (const d of topDocs) {
8246
9007
  const bar = "\u2588".repeat(Math.min(d.cnt, 20));
8247
- console.log(` ${chalk15.cyan(bar)} ${d.cnt} views ${d.title}`);
9008
+ console.log(` ${chalk16.cyan(bar)} ${d.cnt} views ${d.title}`);
8248
9009
  }
8249
9010
  }
8250
9011
  const dailyActivity = db.prepare(`
@@ -8253,12 +9014,12 @@ async function digestCommand(options) {
8253
9014
  GROUP BY day ORDER BY day
8254
9015
  `).all();
8255
9016
  if (dailyActivity.length > 0) {
8256
- console.log(chalk15.dim("\n\u{1F4C5} Daily activity:"));
9017
+ console.log(chalk16.dim("\n\u{1F4C5} Daily activity:"));
8257
9018
  const maxCnt = Math.max(...dailyActivity.map((d) => d.cnt));
8258
9019
  for (const d of dailyActivity) {
8259
9020
  const barLen = Math.round(d.cnt / maxCnt * 20);
8260
9021
  const bar = "\u2588".repeat(barLen) + "\u2591".repeat(20 - barLen);
8261
- console.log(` ${d.day.slice(5)} ${chalk15.green(bar)} ${d.cnt}`);
9022
+ console.log(` ${d.day.slice(5)} ${chalk16.green(bar)} ${d.cnt}`);
8262
9023
  }
8263
9024
  }
8264
9025
  const typeStats = db.prepare(`
@@ -8269,7 +9030,7 @@ async function digestCommand(options) {
8269
9030
  GROUP BY d.type ORDER BY cnt DESC
8270
9031
  `).all();
8271
9032
  if (typeStats.length > 0) {
8272
- console.log(chalk15.dim("\n\u{1F4CA} Note types accessed:"));
9033
+ console.log(chalk16.dim("\n\u{1F4CA} Note types accessed:"));
8273
9034
  for (const t2 of typeStats) {
8274
9035
  console.log(` ${t2.type}: ${t2.cnt}`);
8275
9036
  }
@@ -8278,16 +9039,16 @@ async function digestCommand(options) {
8278
9039
  const report = await decayEngine.computeAll();
8279
9040
  console.log(`
8280
9041
  \u{1F9E0} Health: R=${report.averageR} | Decaying ${report.decayingCount} | Critical ${report.criticalCount}`);
8281
- console.log(chalk15.dim("\n\u2550".repeat(50)));
9042
+ console.log(chalk16.dim("\n\u2550".repeat(50)));
8282
9043
  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");
9044
+ const { writeFileSync: writeFileSync23, mkdirSync: mkdirSync23, existsSync: existsSync27 } = await import("node:fs");
9045
+ const { join: join32, resolve: resolve22 } = await import("node:path");
8285
9046
  const outputDir = resolve22(config.vaultPath, "_stellavault/digests");
8286
- if (!existsSync25(outputDir))
8287
- mkdirSync22(outputDir, { recursive: true });
9047
+ if (!existsSync27(outputDir))
9048
+ mkdirSync23(outputDir, { recursive: true });
8288
9049
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
8289
9050
  const filename = `digest-${date}.md`;
8290
- const outputPath = join30(outputDir, filename);
9051
+ const outputPath = join32(outputDir, filename);
8291
9052
  const pieData = (typeStats.length > 0 ? typeStats : [{ type: "note", cnt: 1 }]).map((t2) => ` "${t2.type}" : ${t2.cnt}`).join("\n");
8292
9053
  const timelineData = dailyActivity.map((d) => ` ${d.day.slice(5)} : ${d.cnt}`).join("\n");
8293
9054
  const md = [
@@ -8325,22 +9086,22 @@ async function digestCommand(options) {
8325
9086
  "---",
8326
9087
  `*Generated by \`stellavault digest --visual\` on ${(/* @__PURE__ */ new Date()).toISOString()}*`
8327
9088
  ].filter(Boolean).join("\n");
8328
- writeFileSync22(outputPath, md, "utf-8");
8329
- console.log(chalk15.green(`
9089
+ writeFileSync23(outputPath, md, "utf-8");
9090
+ console.log(chalk16.green(`
8330
9091
  Visual digest saved: ${filename}`));
8331
- console.log(chalk15.dim(`Open in Obsidian to see Mermaid charts.`));
9092
+ console.log(chalk16.dim(`Open in Obsidian to see Mermaid charts.`));
8332
9093
  }
8333
9094
  }
8334
9095
 
8335
9096
  // packages/cli/dist/commands/init-cmd.js
8336
9097
  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";
9098
+ import { existsSync as existsSync20, mkdirSync as mkdirSync19, writeFileSync as writeFileSync19 } from "node:fs";
9099
+ import { join as join25 } from "node:path";
9100
+ import { homedir as homedir16 } from "node:os";
8340
9101
  import ora2 from "ora";
8341
- import chalk16 from "chalk";
9102
+ import chalk17 from "chalk";
8342
9103
  function ask(rl, question, defaultVal) {
8343
- const suffix = defaultVal ? ` ${chalk16.dim(`(${defaultVal})`)}` : "";
9104
+ const suffix = defaultVal ? ` ${chalk17.dim(`(${defaultVal})`)}` : "";
8344
9105
  return new Promise((resolve22) => {
8345
9106
  rl.question(`${question}${suffix}: `, (answer) => {
8346
9107
  resolve22(answer.trim() || defaultVal || "");
@@ -8349,31 +9110,31 @@ function ask(rl, question, defaultVal) {
8349
9110
  }
8350
9111
  async function initCommand() {
8351
9112
  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"));
9113
+ console.log(chalk17.bold(" \u2726 Stellavault Setup Wizard"));
9114
+ console.log(chalk17.dim(" Notes die in folders. Let's bring yours to life.\n"));
8354
9115
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
8355
9116
  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"));
9117
+ console.log(chalk17.cyan(" Step 1/3") + " \u2014 Where is your Obsidian vault?");
9118
+ console.log(chalk17.dim(" This is the folder containing your .md files.\n"));
8358
9119
  let vaultPath = "";
8359
9120
  while (!vaultPath) {
8360
9121
  const input = await ask(rl, " Vault path");
8361
9122
  if (!input) {
8362
- console.log(chalk16.yellow(" Please enter your vault path."));
9123
+ console.log(chalk17.yellow(" Please enter your vault path."));
8363
9124
  continue;
8364
9125
  }
8365
- const resolved = input.replace(/^~/, homedir14());
8366
- if (!existsSync18(resolved)) {
8367
- console.log(chalk16.yellow(` Path not found: ${resolved}`));
9126
+ const resolved = input.replace(/^~/, homedir16());
9127
+ if (!existsSync20(resolved)) {
9128
+ console.log(chalk17.yellow(` Path not found: ${resolved}`));
8368
9129
  continue;
8369
9130
  }
8370
9131
  vaultPath = resolved;
8371
9132
  }
8372
- console.log(chalk16.green(` \u2713 Vault: ${vaultPath}
9133
+ console.log(chalk17.green(` \u2713 Vault: ${vaultPath}
8373
9134
  `));
8374
- const configDir = join23(homedir14(), ".stellavault");
8375
- mkdirSync18(configDir, { recursive: true });
8376
- const dbPath = join23(configDir, "index.db");
9135
+ const configDir = join25(homedir16(), ".stellavault");
9136
+ mkdirSync19(configDir, { recursive: true });
9137
+ const dbPath = join25(configDir, "index.db");
8377
9138
  const configData = {
8378
9139
  vaultPath,
8379
9140
  dbPath,
@@ -8382,11 +9143,11 @@ async function initCommand() {
8382
9143
  search: { defaultLimit: 10, rrfK: 60 },
8383
9144
  mcp: { mode: "stdio", port: 3333 }
8384
9145
  };
8385
- writeFileSync18(join23(homedir14(), ".stellavault.json"), JSON.stringify(configData, null, 2), "utf-8");
8386
- console.log(chalk16.dim(` Config saved: ~/.stellavault.json`));
9146
+ writeFileSync19(join25(homedir16(), ".stellavault.json"), JSON.stringify(configData, null, 2), "utf-8");
9147
+ console.log(chalk17.dim(` Config saved: ~/.stellavault.json`));
8387
9148
  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"));
9149
+ console.log(chalk17.cyan(" Step 2/3") + " \u2014 Indexing your vault");
9150
+ console.log(chalk17.dim(" Vectorizing notes with local AI (no data leaves your machine).\n"));
8390
9151
  const spinner = ora2({ text: " Loading embedding model (first run downloads ~30MB, please wait)...", indent: 2 }).start();
8391
9152
  const store = createSqliteVecStore(dbPath);
8392
9153
  await store.initialize();
@@ -8403,11 +9164,11 @@ async function initCommand() {
8403
9164
  spinner.text = ` [${bar}] ${pct}% (${current}/${total}) ${doc.title.slice(0, 30)}`;
8404
9165
  }
8405
9166
  });
8406
- spinner.succeed(chalk16.green(` Indexed ${result.indexed} docs, ${result.totalChunks} chunks (${(result.elapsedMs / 1e3).toFixed(1)}s)`));
9167
+ spinner.succeed(chalk17.green(` Indexed ${result.indexed} docs, ${result.totalChunks} chunks (${(result.elapsedMs / 1e3).toFixed(1)}s)`));
8407
9168
  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 });
9169
+ console.log(chalk17.yellow("\n Your vault is empty \u2014 creating 3 starter notes so you can explore.\n"));
9170
+ const rawDir = join25(vaultPath, "raw");
9171
+ mkdirSync19(rawDir, { recursive: true });
8411
9172
  const samples = [
8412
9173
  {
8413
9174
  file: "welcome-to-stellavault.md",
@@ -8480,7 +9241,7 @@ Andrej Karpathy's approach: every session auto-compiles into structured knowledg
8480
9241
  }
8481
9242
  ];
8482
9243
  for (const s of samples) {
8483
- writeFileSync18(join23(rawDir, s.file), s.content, "utf-8");
9244
+ writeFileSync19(join25(rawDir, s.file), s.content, "utf-8");
8484
9245
  }
8485
9246
  const reSpinner = ora2({ text: " Indexing sample notes...", indent: 2 }).start();
8486
9247
  const reResult = await indexVault(vaultPath, {
@@ -8488,83 +9249,88 @@ Andrej Karpathy's approach: every session auto-compiles into structured knowledg
8488
9249
  embedder,
8489
9250
  chunkOptions: { maxTokens: 300, overlap: 50, minTokens: 50 }
8490
9251
  });
8491
- reSpinner.succeed(chalk16.green(` Indexed ${reResult.indexed} sample notes, ${reResult.totalChunks} chunks`));
9252
+ reSpinner.succeed(chalk17.green(` Indexed ${reResult.indexed} sample notes, ${reResult.totalChunks} chunks`));
8492
9253
  }
8493
9254
  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"));
9255
+ console.log(chalk17.cyan(" Step 3/3") + " \u2014 Try your first search");
9256
+ console.log(chalk17.dim(" Type a topic you know about. Stellavault finds connections.\n"));
8496
9257
  const searchEngine = createSearchEngine({ store, embedder, rrfK: 60 });
8497
9258
  let searchDone = false;
8498
9259
  while (!searchDone) {
8499
9260
  const query = await ask(rl, " Search");
8500
9261
  if (!query) {
8501
- console.log(chalk16.dim(" Type something, or press Ctrl+C to skip."));
9262
+ console.log(chalk17.dim(" Type something, or press Ctrl+C to skip."));
8502
9263
  continue;
8503
9264
  }
8504
9265
  const searchSpinner = ora2({ text: " Searching...", indent: 2 }).start();
8505
9266
  const results = await searchEngine.search({ query, limit: 5 });
8506
9267
  searchSpinner.stop();
8507
9268
  if (results.length === 0) {
8508
- console.log(chalk16.yellow(" No results. Try a different topic."));
9269
+ console.log(chalk17.yellow(" No results. Try a different topic."));
8509
9270
  continue;
8510
9271
  }
8511
9272
  console.log("");
8512
9273
  for (const r of results) {
8513
9274
  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}%)`)}`);
9275
+ const bar = score >= 70 ? chalk17.green("\u25CF") : score >= 40 ? chalk17.yellow("\u25CF") : chalk17.dim("\u25CF");
9276
+ console.log(` ${bar} ${chalk17.bold(r.document.title)} ${chalk17.dim(`(${score}%)`)}`);
8516
9277
  if (r.highlights[0]) {
8517
- console.log(` ${chalk16.dim(r.highlights[0].slice(0, 80))}...`);
9278
+ console.log(` ${chalk17.dim(r.highlights[0].slice(0, 80))}...`);
8518
9279
  }
8519
9280
  }
8520
9281
  console.log("");
8521
9282
  searchDone = true;
8522
9283
  }
8523
9284
  await store.close();
8524
- console.log(chalk16.bold.green(" \u2726 Setup complete!\n"));
9285
+ console.log(chalk17.bold.green(" \u2726 Setup complete!\n"));
8525
9286
  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`);
9287
+ console.log(` ${chalk17.cyan("stellavault graph")} Launch 3D knowledge graph`);
9288
+ console.log(` ${chalk17.cyan("stellavault decay")} See what knowledge is fading`);
9289
+ console.log(` ${chalk17.cyan("stellavault brief")} Get your daily knowledge briefing`);
9290
+ console.log(` ${chalk17.cyan("stellavault serve")} Connect AI agents via MCP`);
8530
9291
  console.log("");
8531
- console.log(chalk16.cyan(" \u2500\u2500\u2500 Build a habit \u2500\u2500\u2500\n"));
9292
+ const doConnect = await ask(rl, " Connect Stellavault to your AI clients now? [Y/n]", "Y");
9293
+ if (doConnect.toLowerCase() !== "n") {
9294
+ const { setupCommand: setupCommand2 } = await Promise.resolve().then(() => (init_setup_cmd(), setup_cmd_exports));
9295
+ await setupCommand2({});
9296
+ }
9297
+ console.log(chalk17.cyan(" \u2500\u2500\u2500 Build a habit \u2500\u2500\u2500\n"));
8532
9298
  const setupCron = await ask(rl, " Auto-run daily briefing? (adds cron job) [Y/n]", "Y");
8533
9299
  if (setupCron.toLowerCase() !== "n") {
8534
9300
  const cronLine = `0 9 * * * cd "${vaultPath}" && stellavault brief >> ~/.stellavault/daily.log 2>&1`;
8535
9301
  const platform = process.platform;
8536
9302
  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}
9303
+ console.log(chalk17.dim("\n Windows: Add this to Task Scheduler:"));
9304
+ console.log(chalk17.dim(` Action: stellavault brief`));
9305
+ console.log(chalk17.dim(` Trigger: Daily at 9:00 AM`));
9306
+ console.log(chalk17.dim(` Start in: ${vaultPath}
8541
9307
  `));
8542
9308
  } else {
8543
- console.log(chalk16.dim("\n Add this to your crontab (crontab -e):"));
8544
- console.log(chalk16.dim(` ${cronLine}
9309
+ console.log(chalk17.dim("\n Add this to your crontab (crontab -e):"));
9310
+ console.log(chalk17.dim(` ${cronLine}
8545
9311
  `));
8546
9312
  const autoAdd = await ask(rl, " Add to crontab now? [Y/n]", "Y");
8547
9313
  if (autoAdd.toLowerCase() !== "n") {
8548
9314
  try {
8549
- const { execSync: execSync2 } = await import("node:child_process");
8550
- const existing = execSync2("crontab -l 2>/dev/null || true", { encoding: "utf-8" });
9315
+ const { execSync: execSync3 } = await import("node:child_process");
9316
+ const existing = execSync3("crontab -l 2>/dev/null || true", { encoding: "utf-8" });
8551
9317
  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"));
9318
+ execSync3(`(crontab -l 2>/dev/null; echo "${cronLine}") | crontab -`, { encoding: "utf-8" });
9319
+ console.log(chalk17.green(" \u2713 Daily briefing scheduled at 9:00 AM\n"));
8554
9320
  } else {
8555
- console.log(chalk16.dim(" Already scheduled.\n"));
9321
+ console.log(chalk17.dim(" Already scheduled.\n"));
8556
9322
  }
8557
9323
  } catch {
8558
- console.log(chalk16.yellow(" Could not auto-add. Please add manually.\n"));
9324
+ console.log(chalk17.yellow(" Could not auto-add. Please add manually.\n"));
8559
9325
  }
8560
9326
  }
8561
9327
  }
8562
9328
  }
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`);
9329
+ console.log(chalk17.dim(" Tomorrow morning, run:"));
9330
+ console.log(` ${chalk17.cyan("stellavault brief")} See what changed overnight`);
9331
+ console.log(` ${chalk17.cyan("stellavault decay")} Review fading knowledge`);
8566
9332
  console.log("");
8567
- console.log(chalk16.dim(" Your knowledge is now alive. \u2726"));
9333
+ console.log(chalk17.dim(" Your knowledge is now alive. \u2726"));
8568
9334
  console.log("");
8569
9335
  } finally {
8570
9336
  rl.close();
@@ -8572,7 +9338,7 @@ Andrej Karpathy's approach: every session auto-compiles into structured knowledg
8572
9338
  }
8573
9339
 
8574
9340
  // packages/cli/dist/commands/learn-cmd.js
8575
- import chalk17 from "chalk";
9341
+ import chalk18 from "chalk";
8576
9342
  async function learnCommand(_opts, cmd) {
8577
9343
  const globalOpts = cmd?.parent?.opts?.() ?? {};
8578
9344
  const jsonMode = globalOpts.json;
@@ -8582,7 +9348,7 @@ async function learnCommand(_opts, cmd) {
8582
9348
  await hub.embedder.initialize();
8583
9349
  const db = hub.store.getDb();
8584
9350
  if (!db) {
8585
- console.error(chalk17.red("Cannot access database"));
9351
+ console.error(chalk18.red("Cannot access database"));
8586
9352
  process.exit(1);
8587
9353
  }
8588
9354
  const decayEngine = new DecayEngine(db);
@@ -8600,32 +9366,32 @@ async function learnCommand(_opts, cmd) {
8600
9366
  return;
8601
9367
  }
8602
9368
  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`));
9369
+ console.log(chalk18.bold(" \u{1F3AF} Your Learning Path"));
9370
+ console.log(chalk18.dim(` ${path.summary.reviewCount} to review \xB7 ${path.summary.bridgeCount} gaps to bridge \xB7 ~${path.summary.estimatedMinutes}min`));
8605
9371
  console.log("");
8606
9372
  for (const item of path.items) {
8607
9373
  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;
9374
+ const prioColor = item.priority === "critical" ? chalk18.red : item.priority === "important" ? chalk18.yellow : chalk18.dim;
8609
9375
  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)}`);
9376
+ console.log(` ${icon} ${prioLabel} ${chalk18.bold(item.title)} ${chalk18.dim(`(${item.score}pt)`)}`);
9377
+ console.log(` ${chalk18.dim(item.reason)}`);
8612
9378
  }
8613
9379
  if (path.items.length === 0) {
8614
- console.log(chalk17.green(" All clear! Your knowledge is in great shape."));
9380
+ console.log(chalk18.green(" All clear! Your knowledge is in great shape."));
8615
9381
  }
8616
9382
  console.log("");
8617
- console.log(chalk17.dim(" \u{1F4A1} stellavault review \u2014 start reviewing decaying notes"));
9383
+ console.log(chalk18.dim(" \u{1F4A1} stellavault review \u2014 start reviewing decaying notes"));
8618
9384
  console.log("");
8619
9385
  }
8620
9386
 
8621
9387
  // packages/cli/dist/commands/contradictions-cmd.js
8622
- import chalk18 from "chalk";
9388
+ import chalk19 from "chalk";
8623
9389
  async function contradictionsCommand(_opts, cmd) {
8624
9390
  const globalOpts = cmd?.parent?.opts?.() ?? {};
8625
9391
  const jsonMode = globalOpts.json;
8626
9392
  const config = loadConfig();
8627
9393
  const hub = createKnowledgeHub(config);
8628
- console.error(chalk18.dim("Scanning for contradictions..."));
9394
+ console.error(chalk19.dim("Scanning for contradictions..."));
8629
9395
  await hub.store.initialize();
8630
9396
  await hub.embedder.initialize();
8631
9397
  const pairs = await detectContradictions(hub.store, 20);
@@ -8635,42 +9401,42 @@ async function contradictionsCommand(_opts, cmd) {
8635
9401
  return;
8636
9402
  }
8637
9403
  console.log("");
8638
- console.log(chalk18.bold(` \u26A1 ${pairs.length} potential contradictions found`));
9404
+ console.log(chalk19.bold(` \u26A1 ${pairs.length} potential contradictions found`));
8639
9405
  console.log("");
8640
9406
  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))}`);
9407
+ const confColor = p.confidence >= 0.8 ? chalk19.red : p.confidence >= 0.6 ? chalk19.yellow : chalk19.dim;
9408
+ console.log(` ${confColor(`${Math.round(p.confidence * 100)}%`)} ${chalk19.dim(`[${p.type}]`)} ${chalk19.bold(p.docA.title)} vs ${chalk19.bold(p.docB.title)}`);
9409
+ console.log(` A: ${chalk19.dim(p.docA.statement.slice(0, 80))}`);
9410
+ console.log(` B: ${chalk19.dim(p.docB.statement.slice(0, 80))}`);
8645
9411
  console.log("");
8646
9412
  }
8647
9413
  if (pairs.length === 0) {
8648
- console.log(chalk18.green(" No contradictions detected. Your knowledge is consistent!"));
9414
+ console.log(chalk19.green(" No contradictions detected. Your knowledge is consistent!"));
8649
9415
  console.log("");
8650
9416
  }
8651
9417
  }
8652
9418
 
8653
9419
  // packages/cli/dist/commands/federate-cmd.js
8654
9420
  import { createInterface as createInterface3 } from "node:readline";
8655
- import chalk19 from "chalk";
9421
+ import chalk20 from "chalk";
8656
9422
  async function federateJoinCommand(options) {
8657
9423
  if (!isFederationExperimentalEnabled()) {
8658
9424
  console.log("");
8659
- console.log(chalk19.red(" \u2726 Federation is experimental and disabled by default."));
9425
+ console.log(chalk20.red(" \u2726 Federation is experimental and disabled by default."));
8660
9426
  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"));
9427
+ console.log(chalk20.dim(" Enable it by setting an environment variable:"));
9428
+ console.log(chalk20.dim(' PowerShell: $env:STELLAVAULT_FEDERATION_EXPERIMENTAL = "1"'));
9429
+ console.log(chalk20.dim(" bash/zsh: export STELLAVAULT_FEDERATION_EXPERIMENTAL=1"));
8664
9430
  console.log("");
8665
- console.log(chalk19.dim(" Then re-run `stellavault federate join`."));
9431
+ console.log(chalk20.dim(" Then re-run `stellavault federate join`."));
8666
9432
  console.log("");
8667
9433
  process.exit(2);
8668
9434
  }
8669
9435
  const config = loadConfig();
8670
9436
  const identity = getOrCreateIdentity(options.name);
8671
9437
  console.log("");
8672
- console.log(chalk19.bold(" \u2726 Stellavault Federation") + chalk19.yellow(" (experimental)"));
8673
- console.log(chalk19.dim(` Node: ${identity.displayName} (${identity.peerId})`));
9438
+ console.log(chalk20.bold(" \u2726 Stellavault Federation") + chalk20.yellow(" (experimental)"));
9439
+ console.log(chalk20.dim(` Node: ${identity.displayName} (${identity.peerId})`));
8674
9440
  console.log("");
8675
9441
  const store = createSqliteVecStore(config.dbPath);
8676
9442
  await store.initialize();
@@ -8684,28 +9450,28 @@ async function federateJoinCommand(options) {
8684
9450
  search.startResponder();
8685
9451
  const sharingCfg = loadSharingConfig();
8686
9452
  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."));
9453
+ console.log(chalk20.yellow(" \u26A0 Receive-only mode (my node level = 0)."));
9454
+ console.log(chalk20.dim(" Run `set-level 1` or higher in the federation prompt to share."));
8689
9455
  } else {
8690
- console.log(chalk19.dim(` Sharing level: ${sharingCfg.myNodeLevel} (set-level <0-4> to change)`));
9456
+ console.log(chalk20.dim(` Sharing level: ${sharingCfg.myNodeLevel} (set-level <0-4> to change)`));
8691
9457
  }
8692
9458
  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...
9459
+ console.log(chalk20.green(` \u2726 Joined federation network`));
9460
+ console.log(chalk20.dim(` Topic: ${info.topic}`));
9461
+ console.log(chalk20.dim(` Waiting for peers...
8696
9462
  `));
8697
9463
  });
8698
9464
  node.on("peer_joined", (peer) => {
8699
- console.log(chalk19.cyan(` \u2192 Peer found: ${peer.displayName} (${peer.documentCount} docs) [${peer.peerId}]`));
9465
+ console.log(chalk20.cyan(` \u2192 Peer found: ${peer.displayName} (${peer.documentCount} docs) [${peer.peerId}]`));
8700
9466
  });
8701
9467
  node.on("peer_left", (info) => {
8702
- console.log(chalk19.yellow(` \u2190 Peer left: ${info.peerId}`));
9468
+ console.log(chalk20.yellow(` \u2190 Peer left: ${info.peerId}`));
8703
9469
  });
8704
9470
  node.on("search_request", () => {
8705
9471
  });
8706
9472
  await node.join();
8707
9473
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
8708
- const prompt = () => rl.question(chalk19.dim("federation> "), handleInput);
9474
+ const prompt = () => rl.question(chalk20.dim("federation> "), handleInput);
8709
9475
  async function handleInput(line) {
8710
9476
  const parts = line.trim().split(/\s+/);
8711
9477
  const cmd = parts[0];
@@ -8713,23 +9479,23 @@ async function federateJoinCommand(options) {
8713
9479
  case "search": {
8714
9480
  const query = parts.slice(1).join(" ");
8715
9481
  if (!query) {
8716
- console.log(chalk19.yellow(" Usage: search <query>"));
9482
+ console.log(chalk20.yellow(" Usage: search <query>"));
8717
9483
  break;
8718
9484
  }
8719
9485
  const start = Date.now();
8720
- console.log(chalk19.dim(` Searching ${node.peerCount} peers...`));
9486
+ console.log(chalk20.dim(` Searching ${node.peerCount} peers...`));
8721
9487
  const results = await search.search(query, { limit: 5, timeout: 5e3 });
8722
9488
  const elapsed = Date.now() - start;
8723
9489
  if (results.length === 0) {
8724
- console.log(chalk19.yellow(` No results from peers. (${elapsed}ms)`));
9490
+ console.log(chalk20.yellow(` No results from peers. (${elapsed}ms)`));
8725
9491
  } else {
8726
9492
  console.log("");
8727
9493
  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)}...`);
9494
+ const simColor = r.similarity >= 0.7 ? chalk20.green : r.similarity >= 0.4 ? chalk20.yellow : chalk20.dim;
9495
+ console.log(` ${simColor(`${Math.round(r.similarity * 100)}%`)} ${chalk20.bold(r.title)} ${chalk20.dim(`[${r.peerName}]`)}`);
9496
+ console.log(` ${chalk20.dim(r.snippet)}...`);
8731
9497
  }
8732
- console.log(chalk19.dim(`
9498
+ console.log(chalk20.dim(`
8733
9499
  ${results.length} results from ${new Set(results.map((r) => r.peerId)).size} peers (${elapsed}ms)`));
8734
9500
  }
8735
9501
  break;
@@ -8737,46 +9503,46 @@ async function federateJoinCommand(options) {
8737
9503
  case "peers": {
8738
9504
  const peers = node.getPeers();
8739
9505
  if (peers.length === 0) {
8740
- console.log(chalk19.yellow(" No peers connected"));
9506
+ console.log(chalk20.yellow(" No peers connected"));
8741
9507
  } else {
8742
9508
  console.log("");
8743
9509
  for (const p of peers) {
8744
- console.log(` ${chalk19.cyan(p.displayName)} ${chalk19.dim(`(${p.documentCount} docs)`)} [${p.peerId}]`);
9510
+ console.log(` ${chalk20.cyan(p.displayName)} ${chalk20.dim(`(${p.documentCount} docs)`)} [${p.peerId}]`);
8745
9511
  if (p.topTopics.length > 0) {
8746
- console.log(` ${chalk19.dim(p.topTopics.map((t2) => `#${t2}`).join(" "))}`);
9512
+ console.log(` ${chalk20.dim(p.topTopics.map((t2) => `#${t2}`).join(" "))}`);
8747
9513
  }
8748
9514
  }
8749
- console.log(chalk19.dim(`
9515
+ console.log(chalk20.dim(`
8750
9516
  ${peers.length} peer(s) connected`));
8751
9517
  }
8752
9518
  break;
8753
9519
  }
8754
9520
  case "status": {
8755
9521
  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")}`);
9522
+ console.log(` ${chalk20.bold("Node:")} ${identity.displayName} (${identity.peerId})`);
9523
+ console.log(` ${chalk20.bold("Docs:")} ${stats.documentCount}`);
9524
+ console.log(` ${chalk20.bold("Peers:")} ${node.peerCount}`);
9525
+ console.log(` ${chalk20.bold("Running:")} ${node.isRunning ? chalk20.green("yes") : chalk20.red("no")}`);
8760
9526
  break;
8761
9527
  }
8762
9528
  case "connect": {
8763
9529
  const addr = parts[1];
8764
9530
  if (!addr || !addr.includes(":")) {
8765
- console.log(chalk19.yellow(" Usage: connect <host:port>"));
9531
+ console.log(chalk20.yellow(" Usage: connect <host:port>"));
8766
9532
  break;
8767
9533
  }
8768
9534
  const [host, portStr] = addr.split(":");
8769
9535
  try {
8770
- console.log(chalk19.dim(` Connecting to ${addr}...`));
9536
+ console.log(chalk20.dim(` Connecting to ${addr}...`));
8771
9537
  await node.joinDirect(host, parseInt(portStr, 10));
8772
- console.log(chalk19.green(` Connected to ${addr}`));
9538
+ console.log(chalk20.green(` Connected to ${addr}`));
8773
9539
  } catch (err) {
8774
- console.log(chalk19.red(` Failed: ${err instanceof Error ? err.message : err}`));
9540
+ console.log(chalk20.red(` Failed: ${err instanceof Error ? err.message : err}`));
8775
9541
  }
8776
9542
  break;
8777
9543
  }
8778
9544
  case "sharing": {
8779
- console.log("\n" + chalk19.bold(" Sharing Settings"));
9545
+ console.log("\n" + chalk20.bold(" Sharing Settings"));
8780
9546
  console.log(" " + getSharingSummary().split("\n").join("\n "));
8781
9547
  console.log("");
8782
9548
  break;
@@ -8785,38 +9551,38 @@ async function federateJoinCommand(options) {
8785
9551
  const tag = parts[1];
8786
9552
  const lvl = parseInt(parts[2], 10);
8787
9553
  if (!tag || isNaN(lvl) || lvl < 0 || lvl > 4) {
8788
- console.log(chalk19.yellow(" Usage: set-tag <tag> <0-4>"));
9554
+ console.log(chalk20.yellow(" Usage: set-tag <tag> <0-4>"));
8789
9555
  break;
8790
9556
  }
8791
9557
  setTagLevel(tag, lvl);
8792
- console.log(chalk19.green(` #${tag} \u2192 Level ${lvl}`));
9558
+ console.log(chalk20.green(` #${tag} \u2192 Level ${lvl}`));
8793
9559
  break;
8794
9560
  }
8795
9561
  case "set-folder": {
8796
9562
  const folder = parts[1];
8797
9563
  const lvl = parseInt(parts[2], 10);
8798
9564
  if (!folder || isNaN(lvl) || lvl < 0 || lvl > 4) {
8799
- console.log(chalk19.yellow(" Usage: set-folder <folder> <0-4>"));
9565
+ console.log(chalk20.yellow(" Usage: set-folder <folder> <0-4>"));
8800
9566
  break;
8801
9567
  }
8802
9568
  setFolderLevel(folder, lvl);
8803
- console.log(chalk19.green(` ${folder} \u2192 Level ${lvl}`));
9569
+ console.log(chalk20.green(` ${folder} \u2192 Level ${lvl}`));
8804
9570
  break;
8805
9571
  }
8806
9572
  case "set-level": {
8807
9573
  const lvl = parseInt(parts[1], 10);
8808
9574
  if (isNaN(lvl) || lvl < 0 || lvl > 4) {
8809
- console.log(chalk19.yellow(" Usage: set-level <0-4> (your node sharing level)"));
9575
+ console.log(chalk20.yellow(" Usage: set-level <0-4> (your node sharing level)"));
8810
9576
  break;
8811
9577
  }
8812
9578
  setNodeLevel(lvl);
8813
- console.log(chalk19.green(` My node level \u2192 ${lvl}`));
9579
+ console.log(chalk20.green(` My node level \u2192 ${lvl}`));
8814
9580
  break;
8815
9581
  }
8816
9582
  case "requests": {
8817
9583
  const pending = getPendingRequests();
8818
9584
  if (pending.length === 0) {
8819
- console.log(chalk19.dim(" No pending requests"));
9585
+ console.log(chalk20.dim(" No pending requests"));
8820
9586
  break;
8821
9587
  }
8822
9588
  console.log(`
@@ -8829,33 +9595,33 @@ async function federateJoinCommand(options) {
8829
9595
  case "approve": {
8830
9596
  const reqId = parts[1];
8831
9597
  if (!reqId) {
8832
- console.log(chalk19.yellow(" Usage: approve <request-id>"));
9598
+ console.log(chalk20.yellow(" Usage: approve <request-id>"));
8833
9599
  break;
8834
9600
  }
8835
9601
  const match = getPendingRequests().find((r) => r.requestId.startsWith(reqId));
8836
9602
  if (match && approveRequest(match.requestId))
8837
- console.log(chalk19.green(` Approved: ${match.documentTitle}`));
9603
+ console.log(chalk20.green(` Approved: ${match.documentTitle}`));
8838
9604
  else
8839
- console.log(chalk19.red(" Request not found"));
9605
+ console.log(chalk20.red(" Request not found"));
8840
9606
  break;
8841
9607
  }
8842
9608
  case "deny": {
8843
9609
  const reqId = parts[1];
8844
9610
  if (!reqId) {
8845
- console.log(chalk19.yellow(" Usage: deny <request-id>"));
9611
+ console.log(chalk20.yellow(" Usage: deny <request-id>"));
8846
9612
  break;
8847
9613
  }
8848
9614
  const match = getPendingRequests().find((r) => r.requestId.startsWith(reqId));
8849
9615
  if (match && denyRequest(match.requestId))
8850
- console.log(chalk19.green(` Denied: ${match.documentTitle}`));
9616
+ console.log(chalk20.green(` Denied: ${match.documentTitle}`));
8851
9617
  else
8852
- console.log(chalk19.red(" Request not found"));
9618
+ console.log(chalk20.red(" Request not found"));
8853
9619
  break;
8854
9620
  }
8855
9621
  case "leave":
8856
9622
  case "quit":
8857
9623
  case "exit": {
8858
- console.log(chalk19.dim(" Leaving federation..."));
9624
+ console.log(chalk20.dim(" Leaving federation..."));
8859
9625
  await node.leave();
8860
9626
  await store.close();
8861
9627
  rl.close();
@@ -8865,30 +9631,30 @@ async function federateJoinCommand(options) {
8865
9631
  case "help": {
8866
9632
  console.log("");
8867
9633
  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`);
9634
+ console.log(` ${chalk20.cyan("search <query>")} Search across all connected peers`);
9635
+ console.log(` ${chalk20.cyan("peers")} List connected peers`);
9636
+ console.log(` ${chalk20.cyan("status")} Show node info`);
9637
+ console.log(` ${chalk20.cyan("connect <ip:port>")} Connect to peer directly`);
9638
+ console.log(` ${chalk20.cyan("sharing")} Show sharing settings`);
9639
+ console.log(` ${chalk20.cyan("set-tag <t> <0-4>")} Set tag sharing level`);
9640
+ console.log(` ${chalk20.cyan("set-folder <f> <0-4>")} Set folder sharing level`);
9641
+ console.log(` ${chalk20.cyan("set-level <0-4>")} Set your node level`);
9642
+ console.log(` ${chalk20.cyan("requests")} Show pending full-text requests`);
9643
+ console.log(` ${chalk20.cyan("approve <id>")} Approve a request`);
9644
+ console.log(` ${chalk20.cyan("deny <id>")} Deny a request`);
9645
+ console.log(` ${chalk20.cyan("leave")} Disconnect and exit`);
8880
9646
  break;
8881
9647
  }
8882
9648
  default: {
8883
9649
  if (cmd)
8884
- console.log(chalk19.dim(` Unknown command: ${cmd}. Type 'help' for commands.`));
9650
+ console.log(chalk20.dim(` Unknown command: ${cmd}. Type 'help' for commands.`));
8885
9651
  break;
8886
9652
  }
8887
9653
  }
8888
9654
  prompt();
8889
9655
  }
8890
9656
  process.on("SIGINT", async () => {
8891
- console.log(chalk19.dim("\n Leaving federation..."));
9657
+ console.log(chalk20.dim("\n Leaving federation..."));
8892
9658
  await node.leave();
8893
9659
  await store.close();
8894
9660
  process.exit(0);
@@ -8899,7 +9665,7 @@ async function federateStatusCommand() {
8899
9665
  const identity = getOrCreateIdentity();
8900
9666
  const config = loadConfig();
8901
9667
  console.log("");
8902
- console.log(chalk19.bold(" \u2726 Federation Identity"));
9668
+ console.log(chalk20.bold(" \u2726 Federation Identity"));
8903
9669
  console.log(` PeerID: ${identity.peerId}`);
8904
9670
  console.log(` Name: ${identity.displayName}`);
8905
9671
  console.log(` Since: ${identity.createdAt}`);
@@ -8908,7 +9674,7 @@ async function federateStatusCommand() {
8908
9674
  }
8909
9675
 
8910
9676
  // packages/cli/dist/commands/cloud-cmd.js
8911
- import chalk20 from "chalk";
9677
+ import chalk21 from "chalk";
8912
9678
  function getCloudConfig() {
8913
9679
  const endpoint = process.env.SV_CLOUD_ENDPOINT;
8914
9680
  const bucket = process.env.SV_CLOUD_BUCKET ?? "stellavault";
@@ -8922,22 +9688,22 @@ function getCloudConfig() {
8922
9688
  async function cloudSyncCommand() {
8923
9689
  const cloudConfig = getCloudConfig();
8924
9690
  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"));
9691
+ console.log(chalk21.red("\n Cloud not configured. Set environment variables:"));
9692
+ console.log(chalk21.dim(" SV_CLOUD_ENDPOINT=https://xxx.r2.cloudflarestorage.com"));
9693
+ console.log(chalk21.dim(" SV_CLOUD_SECRET_KEY=your_api_token"));
9694
+ console.log(chalk21.dim(" SV_CLOUD_ENCRYPTION_KEY=your_passphrase (optional)\n"));
8929
9695
  return;
8930
9696
  }
8931
9697
  const config = loadConfig();
8932
- console.log(chalk20.dim("\n Encrypting and uploading..."));
9698
+ console.log(chalk21.dim("\n Encrypting and uploading..."));
8933
9699
  const result = await syncToCloud(config.dbPath, cloudConfig);
8934
9700
  if (result.success) {
8935
- console.log(chalk20.green("\n \u2705 Cloud sync complete"));
9701
+ console.log(chalk21.green("\n \u2705 Cloud sync complete"));
8936
9702
  console.log(` DB: ${(result.dbSize / 1024).toFixed(0)}KB \u2192 Encrypted: ${(result.encryptedSize / 1024).toFixed(0)}KB`);
8937
- console.log(chalk20.dim(` ${result.timestamp}
9703
+ console.log(chalk21.dim(` ${result.timestamp}
8938
9704
  `));
8939
9705
  } else {
8940
- console.log(chalk20.red(`
9706
+ console.log(chalk21.red(`
8941
9707
  \u274C Sync failed: ${result.error}
8942
9708
  `));
8943
9709
  }
@@ -8945,18 +9711,18 @@ async function cloudSyncCommand() {
8945
9711
  async function cloudRestoreCommand() {
8946
9712
  const cloudConfig = getCloudConfig();
8947
9713
  if (!cloudConfig) {
8948
- console.log(chalk20.red("\n Cloud not configured. See: sv cloud sync --help\n"));
9714
+ console.log(chalk21.red("\n Cloud not configured. See: sv cloud sync --help\n"));
8949
9715
  return;
8950
9716
  }
8951
9717
  const config = loadConfig();
8952
- console.log(chalk20.dim("\n Downloading and decrypting..."));
9718
+ console.log(chalk21.dim("\n Downloading and decrypting..."));
8953
9719
  const result = await restoreFromCloud(config.dbPath, cloudConfig);
8954
9720
  if (result.success) {
8955
- console.log(chalk20.green("\n \u2705 Restore complete"));
9721
+ console.log(chalk21.green("\n \u2705 Restore complete"));
8956
9722
  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"));
9723
+ console.log(chalk21.dim(" Previous DB backed up as .backup\n"));
8958
9724
  } else {
8959
- console.log(chalk20.red(`
9725
+ console.log(chalk21.red(`
8960
9726
  \u274C Restore failed: ${result.error}
8961
9727
  `));
8962
9728
  }
@@ -8964,29 +9730,29 @@ async function cloudRestoreCommand() {
8964
9730
  async function cloudStatusCommand() {
8965
9731
  const state = getSyncState();
8966
9732
  if (!state) {
8967
- console.log(chalk20.yellow("\n No cloud sync history. Run: sv cloud sync\n"));
9733
+ console.log(chalk21.yellow("\n No cloud sync history. Run: sv cloud sync\n"));
8968
9734
  return;
8969
9735
  }
8970
- console.log(chalk20.bold("\n \u2601\uFE0F Cloud Sync Status"));
9736
+ console.log(chalk21.bold("\n \u2601\uFE0F Cloud Sync Status"));
8971
9737
  console.log(` Last sync: ${state.lastSync}`);
8972
9738
  console.log(` DB size: ${(state.dbSize / 1024).toFixed(0)}KB
8973
9739
  `);
8974
9740
  }
8975
9741
 
8976
9742
  // packages/cli/dist/commands/vault-cmd.js
8977
- import chalk21 from "chalk";
9743
+ import chalk22 from "chalk";
8978
9744
  async function vaultAddCommand(id, vaultPath, options) {
8979
9745
  const config = loadConfig();
8980
9746
  const dbPath = vaultPath.replace(/\/$/, "") + "/.stellavault/index.db";
8981
9747
  try {
8982
9748
  const entry = addVault(id, options.name ?? id, vaultPath, dbPath, !!options.shared);
8983
- console.log(chalk21.green(`
9749
+ console.log(chalk22.green(`
8984
9750
  \u2705 Vault "${entry.name}" added (${entry.id})`));
8985
- console.log(chalk21.dim(` Path: ${entry.path}
9751
+ console.log(chalk22.dim(` Path: ${entry.path}
8986
9752
  DB: ${entry.dbPath}
8987
9753
  `));
8988
9754
  } catch (err) {
8989
- console.log(chalk21.red(`
9755
+ console.log(chalk22.red(`
8990
9756
  \u274C ${err instanceof Error ? err.message : err}
8991
9757
  `));
8992
9758
  }
@@ -8994,22 +9760,22 @@ async function vaultAddCommand(id, vaultPath, options) {
8994
9760
  async function vaultListCommand() {
8995
9761
  const vaults = listVaults();
8996
9762
  if (vaults.length === 0) {
8997
- console.log(chalk21.yellow("\n No vaults registered. Use: sv vault add <id> <path>\n"));
9763
+ console.log(chalk22.yellow("\n No vaults registered. Use: sv vault add <id> <path>\n"));
8998
9764
  return;
8999
9765
  }
9000
- console.log(chalk21.bold("\n Registered Vaults"));
9766
+ console.log(chalk22.bold("\n Registered Vaults"));
9001
9767
  for (const v of vaults) {
9002
- console.log(` ${chalk21.cyan(v.id)} ${v.name} ${chalk21.dim(`(${v.path})`)}`);
9768
+ console.log(` ${chalk22.cyan(v.id)} ${v.name} ${chalk22.dim(`(${v.path})`)}`);
9003
9769
  }
9004
9770
  console.log("");
9005
9771
  }
9006
9772
  async function vaultRemoveCommand(id) {
9007
9773
  if (removeVault(id)) {
9008
- console.log(chalk21.green(`
9774
+ console.log(chalk22.green(`
9009
9775
  \u2705 Vault "${id}" removed
9010
9776
  `));
9011
9777
  } else {
9012
- console.log(chalk21.red(`
9778
+ console.log(chalk22.red(`
9013
9779
  \u274C Vault "${id}" not found
9014
9780
  `));
9015
9781
  }
@@ -9018,33 +9784,33 @@ async function vaultSearchAllCommand(query, options) {
9018
9784
  const config = loadConfig();
9019
9785
  const embedder = createLocalEmbedder(config.embedding.localModel);
9020
9786
  await embedder.initialize();
9021
- console.log(chalk21.dim(`
9787
+ console.log(chalk22.dim(`
9022
9788
  Searching all vaults for "${query}"...`));
9023
9789
  const results = await searchAllVaults(query, embedder, (dbPath) => createSqliteVecStore(dbPath), { limit: parseInt(options.limit ?? "10", 10) });
9024
9790
  if (results.length === 0) {
9025
- console.log(chalk21.yellow(" No results across vaults.\n"));
9791
+ console.log(chalk22.yellow(" No results across vaults.\n"));
9026
9792
  return;
9027
9793
  }
9028
9794
  for (const r of results) {
9029
9795
  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)}...`);
9796
+ const color = pct >= 70 ? chalk22.green : pct >= 40 ? chalk22.yellow : chalk22.dim;
9797
+ console.log(` ${color(`${pct}%`)} ${chalk22.bold(r.title)} ${chalk22.dim(`[${r.vaultName}]`)}`);
9798
+ console.log(` ${chalk22.dim(r.snippet)}...`);
9033
9799
  }
9034
9800
  console.log("");
9035
9801
  }
9036
9802
 
9037
9803
  // packages/cli/dist/commands/capture-cmd.js
9038
- import chalk22 from "chalk";
9804
+ import chalk23 from "chalk";
9039
9805
  async function captureCommand(audioFile, options) {
9040
9806
  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"));
9807
+ console.log(chalk23.red("\n Whisper not installed."));
9808
+ console.log(chalk23.dim(" Install: pip install openai-whisper"));
9809
+ console.log(chalk23.dim(" Or: brew install whisper-cpp\n"));
9044
9810
  return;
9045
9811
  }
9046
9812
  const config = loadConfig();
9047
- console.log(chalk22.dim(`
9813
+ console.log(chalk23.dim(`
9048
9814
  Transcribing ${audioFile}...`));
9049
9815
  const result = await captureVoice(audioFile, {
9050
9816
  vaultPath: config.vaultPath,
@@ -9054,31 +9820,31 @@ async function captureCommand(audioFile, options) {
9054
9820
  folder: options.folder
9055
9821
  });
9056
9822
  if (result.success) {
9057
- console.log(chalk22.green(`
9823
+ console.log(chalk23.green(`
9058
9824
  \u2705 Captured: "${result.title}"`));
9059
9825
  console.log(` Tags: ${result.tags.join(", ")}`);
9060
9826
  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"));
9827
+ console.log(chalk23.dim(` Transcript: ${result.transcript.slice(0, 100)}...`));
9828
+ console.log(chalk23.dim("\n \u{1F4A1} Run stellavault index to add to the graph\n"));
9063
9829
  } else {
9064
- console.log(chalk22.red(`
9830
+ console.log(chalk23.red(`
9065
9831
  \u274C Capture failed: ${result.error}
9066
9832
  `));
9067
9833
  }
9068
9834
  }
9069
9835
 
9070
9836
  // packages/cli/dist/commands/ask-cmd.js
9071
- import chalk23 from "chalk";
9837
+ import chalk24 from "chalk";
9072
9838
  async function askCommand(question, options) {
9073
9839
  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"));
9840
+ console.error(chalk24.yellow('Usage: stellavault ask "your question here" [--save]'));
9841
+ console.error(chalk24.dim("\nSearch Mode: finds relevant notes from your vault."));
9842
+ console.error(chalk24.dim("For AI-powered answers, use MCP: claude mcp add stellavault -- stellavault serve"));
9077
9843
  process.exit(1);
9078
9844
  }
9079
9845
  const config = loadConfig();
9080
9846
  const hub = createKnowledgeHub(config);
9081
- console.error(chalk23.dim("Searching your knowledge (local search mode)..."));
9847
+ console.error(chalk24.dim("Searching your knowledge (local search mode)..."));
9082
9848
  await hub.store.initialize();
9083
9849
  await hub.embedder.initialize();
9084
9850
  const result = await askVault(hub.searchEngine, question, {
@@ -9091,39 +9857,39 @@ async function askCommand(question, options) {
9091
9857
  console.log(result.answer);
9092
9858
  if (result.savedTo) {
9093
9859
  console.log("");
9094
- console.log(chalk23.green(`Saved to: ${result.savedTo}`));
9860
+ console.log(chalk24.green(`Saved to: ${result.savedTo}`));
9095
9861
  }
9096
9862
  if (result.sources.length > 0 && !options.save) {
9097
9863
  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."));
9864
+ console.log(chalk24.dim("Tip: Add --save to file this answer into your vault."));
9865
+ console.log(chalk24.dim("For AI-generated answers: use Claude Code with MCP integration."));
9100
9866
  }
9101
9867
  await hub.store.close?.();
9102
9868
  }
9103
9869
 
9104
9870
  // packages/cli/dist/commands/compile-cmd.js
9105
- import chalk24 from "chalk";
9871
+ import chalk25 from "chalk";
9106
9872
  import { resolve as resolve15 } from "node:path";
9107
9873
  async function compileCommand(options) {
9108
9874
  const config = loadConfig();
9109
9875
  const vaultPath = config.vaultPath;
9110
9876
  const rawPath = resolve15(vaultPath, options.raw ?? "raw");
9111
9877
  const wikiPath = resolve15(vaultPath, options.wiki ?? "_wiki");
9112
- console.error(chalk24.dim(`Raw: ${rawPath}`));
9113
- console.error(chalk24.dim(`Wiki: ${wikiPath}`));
9878
+ console.error(chalk25.dim(`Raw: ${rawPath}`));
9879
+ console.error(chalk25.dim(`Wiki: ${wikiPath}`));
9114
9880
  console.error("");
9115
9881
  const result = compileWiki(rawPath, wikiPath, { force: options.force });
9116
9882
  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."));
9883
+ console.error(chalk25.yellow(`No documents found in ${rawPath}`));
9884
+ console.error(chalk25.dim("Create a raw/ folder in your vault and add .md/.txt files."));
9119
9885
  return;
9120
9886
  }
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}`));
9887
+ console.log(chalk25.green(`Compiled ${result.rawDocCount} raw docs \u2192 ${result.wikiArticles.length} wiki articles`));
9888
+ console.log(chalk25.dim(`Concepts: ${result.concepts.length}`));
9889
+ console.log(chalk25.dim(`Index: ${result.indexFile}`));
9124
9890
  if (result.concepts.length > 0) {
9125
9891
  console.log("");
9126
- console.log(chalk24.cyan("Top concepts:"));
9892
+ console.log(chalk25.cyan("Top concepts:"));
9127
9893
  for (const c of result.concepts.slice(0, 10)) {
9128
9894
  console.log(` ${c}`);
9129
9895
  }
@@ -9131,13 +9897,13 @@ async function compileCommand(options) {
9131
9897
  }
9132
9898
 
9133
9899
  // packages/cli/dist/commands/draft-cmd.js
9134
- import chalk25 from "chalk";
9900
+ import chalk26 from "chalk";
9135
9901
 
9136
9902
  // packages/core/dist/intelligence/draft-generator.js
9137
9903
  init_wiki_compiler();
9138
9904
  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";
9905
+ import { writeFileSync as writeFileSync20, mkdirSync as mkdirSync20, existsSync as existsSync21 } from "node:fs";
9906
+ import { join as join26, resolve as resolve16, basename as basename5, extname as extname7 } from "node:path";
9141
9907
  function generateDraft(vaultPath, options = {}, folders = DEFAULT_FOLDERS) {
9142
9908
  const { topic, format = "blog", maxSections = 8, blueprint } = options;
9143
9909
  const rawDir = resolve16(vaultPath, folders.fleeting);
@@ -9145,7 +9911,7 @@ function generateDraft(vaultPath, options = {}, folders = DEFAULT_FOLDERS) {
9145
9911
  const litDir = resolve16(vaultPath, folders.literature);
9146
9912
  const allDocs = [];
9147
9913
  for (const dir of [rawDir, wikiDir, litDir]) {
9148
- if (existsSync19(dir)) {
9914
+ if (existsSync21(dir)) {
9149
9915
  allDocs.push(...scanRawDirectory(dir));
9150
9916
  }
9151
9917
  }
@@ -9213,14 +9979,14 @@ function generateDraft(vaultPath, options = {}, folders = DEFAULT_FOLDERS) {
9213
9979
  }
9214
9980
  const wordCount = body.split(/\s+/).filter(Boolean).length;
9215
9981
  const draftsDir = resolve16(vaultPath, "_drafts");
9216
- if (!existsSync19(draftsDir))
9217
- mkdirSync19(draftsDir, { recursive: true });
9982
+ if (!existsSync21(draftsDir))
9983
+ mkdirSync20(draftsDir, { recursive: true });
9218
9984
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
9219
9985
  const slug = (topic ?? "knowledge").replace(/[^a-zA-Z0-9가-힣\s]/g, "").replace(/\s+/g, "-").toLowerCase().slice(0, 40);
9220
9986
  const filename = `${timestamp}-${slug}.md`;
9221
- const filePath = join24("_drafts", filename);
9987
+ const filePath = join26("_drafts", filename);
9222
9988
  const fullPath = resolve16(vaultPath, filePath);
9223
- writeFileSync19(fullPath, body, "utf-8");
9989
+ writeFileSync20(fullPath, body, "utf-8");
9224
9990
  return {
9225
9991
  title: draftTitle,
9226
9992
  filePath,
@@ -9377,7 +10143,7 @@ function capitalize(s) {
9377
10143
  async function draftCommand(topic, options) {
9378
10144
  const config = loadConfig();
9379
10145
  if (!config.vaultPath) {
9380
- console.error(chalk25.red("No vault configured. Run `stellavault init` first."));
10146
+ console.error(chalk26.red("No vault configured. Run `stellavault init` first."));
9381
10147
  process.exit(1);
9382
10148
  }
9383
10149
  const format = options.format ?? "blog";
@@ -9391,40 +10157,40 @@ async function draftCommand(topic, options) {
9391
10157
  }
9392
10158
  const result = generateDraft(config.vaultPath, { topic, format, blueprint }, config.folders);
9393
10159
  if (options.ai) {
9394
- console.log(chalk25.dim(" AI mode: generating with Claude..."));
10160
+ console.log(chalk26.dim(" AI mode: generating with Claude..."));
9395
10161
  await enhanceWithAI(config.vaultPath, result.filePath, topic ?? "knowledge", format);
9396
10162
  }
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`));
10163
+ console.log(chalk26.green(`Draft generated: ${result.title}`));
10164
+ console.log(chalk26.dim(` Format: ${format}`));
10165
+ console.log(chalk26.dim(` Mode: ${options.ai ? "AI-enhanced (Claude)" : "rule-based"}`));
10166
+ console.log(chalk26.dim(` Saved: ${result.filePath}`));
10167
+ console.log(chalk26.dim(` Words: ${result.wordCount}`));
10168
+ console.log(chalk26.dim(` Sources: ${result.sourceCount} documents`));
9403
10169
  if (result.concepts.length > 0) {
9404
- console.log(chalk25.dim(` Concepts: ${result.concepts.join(", ")}`));
10170
+ console.log(chalk26.dim(` Concepts: ${result.concepts.join(", ")}`));
9405
10171
  }
9406
10172
  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`));
10173
+ console.log(chalk26.dim(`Next steps:`));
10174
+ console.log(chalk26.dim(` Edit in Obsidian, then promote:`));
10175
+ console.log(chalk26.cyan(` stellavault promote ${result.filePath} --to literature`));
9410
10176
  if (!options.ai) {
9411
- console.log(chalk25.dim(` Or use --ai for Claude-enhanced draft, or MCP generate-draft in Claude Code.`));
10177
+ console.log(chalk26.dim(` Or use --ai for Claude-enhanced draft, or MCP generate-draft in Claude Code.`));
9412
10178
  }
9413
10179
  } catch (err) {
9414
- console.error(chalk25.red(err instanceof Error ? err.message : "Draft generation failed"));
10180
+ console.error(chalk26.red(err instanceof Error ? err.message : "Draft generation failed"));
9415
10181
  process.exit(1);
9416
10182
  }
9417
10183
  }
9418
10184
  async function enhanceWithAI(vaultPath, draftPath, topic, format) {
9419
- const { readFileSync: readFileSync18, writeFileSync: writeFileSync22 } = await import("node:fs");
10185
+ const { readFileSync: readFileSync19, writeFileSync: writeFileSync23 } = await import("node:fs");
9420
10186
  const { resolve: resolve22 } = await import("node:path");
9421
10187
  const fullPath = resolve22(vaultPath, draftPath);
9422
- const scaffold = readFileSync18(fullPath, "utf-8");
10188
+ const scaffold = readFileSync19(fullPath, "utf-8");
9423
10189
  const excerpts = scaffold.split("\n").filter((l) => l.startsWith("> ")).map((l) => l.slice(2)).join("\n");
9424
10190
  const apiKey = process.env.ANTHROPIC_API_KEY;
9425
10191
  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-..."));
10192
+ console.error(chalk26.yellow(" ANTHROPIC_API_KEY not set. Falling back to rule-based draft."));
10193
+ console.error(chalk26.yellow(" Set it with: export ANTHROPIC_API_KEY=sk-ant-..."));
9428
10194
  return;
9429
10195
  }
9430
10196
  try {
@@ -9461,31 +10227,31 @@ ${aiContent.text}
9461
10227
  ---
9462
10228
  *Generated by \`stellavault draft --ai\` using Claude API at ${(/* @__PURE__ */ new Date()).toISOString()}*
9463
10229
  `;
9464
- writeFileSync22(fullPath, enhanced, "utf-8");
10230
+ writeFileSync23(fullPath, enhanced, "utf-8");
9465
10231
  }
9466
10232
  } catch (err) {
9467
- console.error(chalk25.yellow(` AI enhancement failed: ${err instanceof Error ? err.message : "unknown"}. Keeping rule-based draft.`));
10233
+ console.error(chalk26.yellow(` AI enhancement failed: ${err instanceof Error ? err.message : "unknown"}. Keeping rule-based draft.`));
9468
10234
  }
9469
10235
  }
9470
10236
 
9471
10237
  // 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";
10238
+ import chalk27 from "chalk";
10239
+ import { writeFileSync as writeFileSync21, mkdirSync as mkdirSync21, existsSync as existsSync22, appendFileSync } from "node:fs";
10240
+ import { resolve as resolve17, join as join27 } from "node:path";
9475
10241
  async function sessionSaveCommand(options) {
9476
10242
  const config = loadConfig();
9477
10243
  if (!config.vaultPath) {
9478
- console.error(chalk26.red("No vault configured. Run `stellavault init` first."));
10244
+ console.error(chalk27.red("No vault configured. Run `stellavault init` first."));
9479
10245
  process.exit(1);
9480
10246
  }
9481
10247
  const folders = config.folders;
9482
10248
  const logDir = resolve17(config.vaultPath, folders.fleeting, "_daily-logs");
9483
- if (!existsSync20(logDir))
9484
- mkdirSync20(logDir, { recursive: true });
10249
+ if (!existsSync22(logDir))
10250
+ mkdirSync21(logDir, { recursive: true });
9485
10251
  const now = /* @__PURE__ */ new Date();
9486
10252
  const dateStr = now.toISOString().split("T")[0];
9487
10253
  const timeStr = now.toTimeString().split(" ")[0];
9488
- const logFile = join25(logDir, `daily-log-${dateStr}.md`);
10254
+ const logFile = join27(logDir, `daily-log-${dateStr}.md`);
9489
10255
  let summary = options.summary ?? "";
9490
10256
  if (!summary && !process.stdin.isTTY) {
9491
10257
  const chunks = [];
@@ -9495,7 +10261,7 @@ async function sessionSaveCommand(options) {
9495
10261
  summary = Buffer.concat(chunks).toString("utf-8").trim();
9496
10262
  }
9497
10263
  if (!summary) {
9498
- console.log(chalk26.dim("Enter session summary (Ctrl+D to finish):"));
10264
+ console.log(chalk27.dim("Enter session summary (Ctrl+D to finish):"));
9499
10265
  const chunks = [];
9500
10266
  for await (const chunk of process.stdin) {
9501
10267
  chunks.push(chunk);
@@ -9503,7 +10269,7 @@ async function sessionSaveCommand(options) {
9503
10269
  summary = Buffer.concat(chunks).toString("utf-8").trim();
9504
10270
  }
9505
10271
  if (!summary) {
9506
- console.error(chalk26.yellow("No summary provided. Skipping."));
10272
+ console.error(chalk27.yellow("No summary provided. Skipping."));
9507
10273
  return;
9508
10274
  }
9509
10275
  const entry = [
@@ -9524,7 +10290,7 @@ async function sessionSaveCommand(options) {
9524
10290
  entry.push("### Action Items", options.actions, "");
9525
10291
  }
9526
10292
  entry.push("---", "");
9527
- if (!existsSync20(logFile)) {
10293
+ if (!existsSync22(logFile)) {
9528
10294
  const header = [
9529
10295
  "---",
9530
10296
  `title: "Daily Log \u2014 ${dateStr}"`,
@@ -9536,80 +10302,80 @@ async function sessionSaveCommand(options) {
9536
10302
  `# Daily Log \u2014 ${dateStr}`,
9537
10303
  ""
9538
10304
  ].join("\n");
9539
- writeFileSync20(logFile, header + entry.join("\n"), "utf-8");
10305
+ writeFileSync21(logFile, header + entry.join("\n"), "utf-8");
9540
10306
  } else {
9541
10307
  appendFileSync(logFile, entry.join("\n"), "utf-8");
9542
10308
  }
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}`));
10309
+ console.log(chalk27.green(`Session saved to daily log: ${dateStr}`));
10310
+ console.log(chalk27.dim(` File: ${logFile}`));
10311
+ console.log(chalk27.dim(` Time: ${timeStr}`));
10312
+ console.log(chalk27.dim(` Words: ${summary.split(/\s+/).length}`));
9547
10313
  try {
9548
10314
  const { compileWiki: compileWiki2 } = await Promise.resolve().then(() => (init_wiki_compiler(), wiki_compiler_exports));
9549
10315
  const rawDir = resolve17(config.vaultPath, folders.fleeting);
9550
10316
  const wikiDir = resolve17(config.vaultPath, folders.wiki);
9551
10317
  compileWiki2(rawDir, wikiDir);
9552
- console.log(chalk26.dim(" Wiki: auto-compiled"));
10318
+ console.log(chalk27.dim(" Wiki: auto-compiled"));
9553
10319
  } catch {
9554
10320
  }
9555
10321
  }
9556
10322
 
9557
10323
  // 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";
10324
+ import chalk28 from "chalk";
10325
+ import { readdirSync as readdirSync7, readFileSync as readFileSync17, existsSync as existsSync23 } from "node:fs";
10326
+ import { resolve as resolve18, join as join28 } from "node:path";
9561
10327
  async function flushCommand() {
9562
10328
  const config = loadConfig();
9563
10329
  if (!config.vaultPath) {
9564
- console.error(chalk27.red("No vault configured. Run `stellavault init` first."));
10330
+ console.error(chalk28.red("No vault configured. Run `stellavault init` first."));
9565
10331
  process.exit(1);
9566
10332
  }
9567
10333
  const folders = config.folders;
9568
10334
  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."));
10335
+ if (!existsSync23(logDir)) {
10336
+ console.log(chalk28.yellow("No daily logs found. Use `stellavault session-save` or let Claude Code hooks capture sessions."));
9571
10337
  return;
9572
10338
  }
9573
10339
  const logFiles = readdirSync7(logDir).filter((f) => f.startsWith("daily-log-") && f.endsWith(".md"));
9574
10340
  if (logFiles.length === 0) {
9575
- console.log(chalk27.yellow("No daily log files found."));
10341
+ console.log(chalk28.yellow("No daily log files found."));
9576
10342
  return;
9577
10343
  }
9578
- console.log(chalk27.dim(`Found ${logFiles.length} daily logs`));
10344
+ console.log(chalk28.dim(`Found ${logFiles.length} daily logs`));
9579
10345
  let totalSessions = 0;
9580
10346
  const allContent = [];
9581
10347
  for (const file of logFiles) {
9582
- const content = readFileSync16(join26(logDir, file), "utf-8");
10348
+ const content = readFileSync17(join28(logDir, file), "utf-8");
9583
10349
  const sessions = content.split(/^## Session/m).slice(1);
9584
10350
  totalSessions += sessions.length;
9585
10351
  allContent.push(content);
9586
10352
  }
9587
- console.log(chalk27.dim(`Total sessions: ${totalSessions}`));
10353
+ console.log(chalk28.dim(`Total sessions: ${totalSessions}`));
9588
10354
  try {
9589
10355
  const { compileWiki: compileWiki2 } = await Promise.resolve().then(() => (init_wiki_compiler(), wiki_compiler_exports));
9590
10356
  const rawDir = resolve18(config.vaultPath, folders.fleeting);
9591
10357
  const wikiDir = resolve18(config.vaultPath, folders.wiki);
9592
10358
  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}`));
10359
+ console.log(chalk28.green(`Flush complete!`));
10360
+ console.log(chalk28.dim(` Daily logs: ${logFiles.length} files, ${totalSessions} sessions`));
10361
+ console.log(chalk28.dim(` Wiki articles: ${result.wikiArticles.length}`));
10362
+ console.log(chalk28.dim(` Concepts extracted: ${result.concepts.length}`));
9597
10363
  if (result.concepts.length > 0) {
9598
- console.log(chalk27.dim(` Top concepts: ${result.concepts.slice(0, 8).join(", ")}`));
10364
+ console.log(chalk28.dim(` Top concepts: ${result.concepts.slice(0, 8).join(", ")}`));
9599
10365
  }
9600
- console.log(chalk27.dim(` Index: ${result.indexFile}`));
10366
+ console.log(chalk28.dim(` Index: ${result.indexFile}`));
9601
10367
  } catch (err) {
9602
- console.error(chalk27.red(`Flush failed: ${err instanceof Error ? err.message : "unknown"}`));
10368
+ console.error(chalk28.red(`Flush failed: ${err instanceof Error ? err.message : "unknown"}`));
9603
10369
  process.exit(1);
9604
10370
  }
9605
- console.log(chalk27.dim(" Tip: Run `stellavault lint` to check knowledge health"));
10371
+ console.log(chalk28.dim(" Tip: Run `stellavault lint` to check knowledge health"));
9606
10372
  }
9607
10373
 
9608
10374
  // packages/cli/dist/commands/adr-cmd.js
9609
- import chalk28 from "chalk";
10375
+ import chalk29 from "chalk";
9610
10376
  async function adrCommand(title, options) {
9611
10377
  if (!title) {
9612
- console.error(chalk28.yellow('Usage: stellavault adr "Decision Title" --context "..." --options "..." --decision "..." --consequences "..."'));
10378
+ console.error(chalk29.yellow('Usage: stellavault adr "Decision Title" --context "..." --options "..." --decision "..." --consequences "..."'));
9613
10379
  process.exit(1);
9614
10380
  }
9615
10381
  const config = loadConfig();
@@ -9640,25 +10406,25 @@ async function adrCommand(title, options) {
9640
10406
  title: `ADR: ${title}`,
9641
10407
  stage: "literature"
9642
10408
  }, 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`));
10409
+ console.log(chalk29.green(`ADR created: ${title}`));
10410
+ console.log(chalk29.dim(` Saved: ${result.savedTo}`));
10411
+ console.log(chalk29.dim(` Stage: literature`));
10412
+ console.log(chalk29.dim(` Tags: adr, decision`));
9647
10413
  console.log("");
9648
- console.log(chalk28.dim(`Find later: stellavault ask "why did we choose ${title}?"`));
10414
+ console.log(chalk29.dim(`Find later: stellavault ask "why did we choose ${title}?"`));
9649
10415
  }
9650
10416
 
9651
10417
  // packages/cli/dist/commands/lint-cmd.js
9652
- import chalk29 from "chalk";
10418
+ import chalk30 from "chalk";
9653
10419
  async function lintCommand() {
9654
10420
  const config = loadConfig();
9655
10421
  const hub = createKnowledgeHub(config);
9656
- console.error(chalk29.dim("Scanning your knowledge base..."));
10422
+ console.error(chalk30.dim("Scanning your knowledge base..."));
9657
10423
  await hub.store.initialize();
9658
10424
  const result = await lintKnowledge(hub.store);
9659
- const scoreColor = result.score >= 80 ? chalk29.green : result.score >= 50 ? chalk29.yellow : chalk29.red;
10425
+ const scoreColor = result.score >= 80 ? chalk30.green : result.score >= 50 ? chalk30.yellow : chalk30.red;
9660
10426
  console.log("");
9661
- console.log(chalk29.bold("Knowledge Health Report"));
10427
+ console.log(chalk30.bold("Knowledge Health Report"));
9662
10428
  console.log("\u2500".repeat(40));
9663
10429
  console.log(`Score: ${scoreColor(result.score + "/100")}`);
9664
10430
  console.log(`Documents: ${result.stats.totalDocs}`);
@@ -9668,35 +10434,35 @@ async function lintCommand() {
9668
10434
  const warnings = result.issues.filter((i) => i.severity === "warning");
9669
10435
  const info = result.issues.filter((i) => i.severity === "info");
9670
10436
  if (critical.length > 0) {
9671
- console.log(chalk29.red(`Critical: ${critical.length}`));
10437
+ console.log(chalk30.red(`Critical: ${critical.length}`));
9672
10438
  for (const i of critical) {
9673
- console.log(chalk29.red(` \u2717 ${i.message}`));
10439
+ console.log(chalk30.red(` \u2717 ${i.message}`));
9674
10440
  if (i.suggestion)
9675
- console.log(chalk29.dim(` \u2192 ${i.suggestion}`));
10441
+ console.log(chalk30.dim(` \u2192 ${i.suggestion}`));
9676
10442
  }
9677
10443
  console.log("");
9678
10444
  }
9679
10445
  if (warnings.length > 0) {
9680
- console.log(chalk29.yellow(`Warnings: ${warnings.length}`));
10446
+ console.log(chalk30.yellow(`Warnings: ${warnings.length}`));
9681
10447
  for (const i of warnings.slice(0, 10)) {
9682
- console.log(chalk29.yellow(` ! ${i.message}`));
10448
+ console.log(chalk30.yellow(` ! ${i.message}`));
9683
10449
  if (i.suggestion)
9684
- console.log(chalk29.dim(` \u2192 ${i.suggestion}`));
10450
+ console.log(chalk30.dim(` \u2192 ${i.suggestion}`));
9685
10451
  }
9686
10452
  if (warnings.length > 10)
9687
- console.log(chalk29.dim(` ... and ${warnings.length - 10} more`));
10453
+ console.log(chalk30.dim(` ... and ${warnings.length - 10} more`));
9688
10454
  console.log("");
9689
10455
  }
9690
10456
  if (info.length > 0) {
9691
- console.log(chalk29.dim(`Info: ${info.length}`));
10457
+ console.log(chalk30.dim(`Info: ${info.length}`));
9692
10458
  for (const i of info.slice(0, 5)) {
9693
- console.log(chalk29.dim(` \u2139 ${i.message}`));
10459
+ console.log(chalk30.dim(` \u2139 ${i.message}`));
9694
10460
  }
9695
10461
  console.log("");
9696
10462
  }
9697
10463
  }
9698
10464
  if (result.suggestions.length > 0) {
9699
- console.log(chalk29.cyan("Suggestions:"));
10465
+ console.log(chalk30.cyan("Suggestions:"));
9700
10466
  for (const s of result.suggestions) {
9701
10467
  console.log(` \u2192 ${s}`);
9702
10468
  }
@@ -9706,25 +10472,25 @@ async function lintCommand() {
9706
10472
  }
9707
10473
 
9708
10474
  // 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";
10475
+ import chalk31 from "chalk";
10476
+ import { writeFileSync as writeFileSync22, mkdirSync as mkdirSync22, existsSync as existsSync24 } from "node:fs";
10477
+ import { join as join29, resolve as resolve19 } from "node:path";
9712
10478
  async function fleetingCommand(text, options) {
9713
10479
  if (!text || text.trim().length < 2) {
9714
- console.error(chalk30.yellow('Usage: stellavault fleeting "your idea here" [--tags tag1,tag2]'));
10480
+ console.error(chalk31.yellow('Usage: stellavault fleeting "your idea here" [--tags tag1,tag2]'));
9715
10481
  process.exit(1);
9716
10482
  }
9717
10483
  const config = loadConfig();
9718
10484
  const rawDir = resolve19(config.vaultPath, "raw");
9719
- if (!existsSync22(rawDir))
9720
- mkdirSync21(rawDir, { recursive: true });
10485
+ if (!existsSync24(rawDir))
10486
+ mkdirSync22(rawDir, { recursive: true });
9721
10487
  const now = /* @__PURE__ */ new Date();
9722
10488
  const timestamp = now.toISOString().replace(/[:.]/g, "-").slice(0, 19);
9723
10489
  const slug = text.slice(0, 40).replace(/[^a-zA-Z0-9가-힣\s]/g, "").replace(/\s+/g, "-").toLowerCase();
9724
10490
  const filename = `${timestamp}-${slug}.md`;
9725
- const filePath = join27(rawDir, filename);
10491
+ const filePath = join29(rawDir, filename);
9726
10492
  if (!resolve19(filePath).startsWith(resolve19(rawDir))) {
9727
- console.error(chalk30.red("Invalid file path"));
10493
+ console.error(chalk31.red("Invalid file path"));
9728
10494
  process.exit(1);
9729
10495
  }
9730
10496
  const tags = options.tags ? options.tags.split(",").map((t2) => t2.trim()) : [];
@@ -9741,28 +10507,28 @@ async function fleetingCommand(text, options) {
9741
10507
  "---",
9742
10508
  `*Captured via \`stellavault fleeting\` at ${now.toLocaleString("ko-KR")}*`
9743
10509
  ].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."));
10510
+ writeFileSync22(filePath, content, "utf-8");
10511
+ console.log(chalk31.green(`Captured: ${filename}`));
10512
+ console.log(chalk31.dim(`Location: raw/${filename}`));
10513
+ console.log(chalk31.dim("Run `stellavault compile` to process into wiki."));
9748
10514
  }
9749
10515
 
9750
10516
  // 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";
10517
+ import chalk32 from "chalk";
10518
+ import { readFileSync as readFileSync18, existsSync as existsSync25, readdirSync as readdirSync8, statSync as statSync5 } from "node:fs";
10519
+ import { extname as extname8, resolve as resolve20, join as join30 } from "node:path";
9754
10520
  async function ingestCommand(input, options) {
9755
10521
  if (!input) {
9756
- console.error(chalk31.yellow("Usage: stellavault ingest <url|file|text|folder/> [--tags t1,t2]"));
10522
+ console.error(chalk32.yellow("Usage: stellavault ingest <url|file|text|folder/> [--tags t1,t2]"));
9757
10523
  process.exit(1);
9758
10524
  }
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));
10525
+ if (existsSync25(input) && statSync5(input).isDirectory()) {
10526
+ 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
10527
  if (files.length === 0) {
9762
- console.error(chalk31.yellow(`No supported files found in ${input}`));
10528
+ console.error(chalk32.yellow(`No supported files found in ${input}`));
9763
10529
  process.exit(1);
9764
10530
  }
9765
- console.log(chalk31.dim(`Batch ingest: ${files.length} files from ${input}
10531
+ console.log(chalk32.dim(`Batch ingest: ${files.length} files from ${input}
9766
10532
  `));
9767
10533
  let success = 0;
9768
10534
  const failed = [];
@@ -9770,7 +10536,7 @@ async function ingestCommand(input, options) {
9770
10536
  const file = files[i];
9771
10537
  const name = file.split(/[/\\]/).pop() ?? file;
9772
10538
  const progress = `[${i + 1}/${files.length}]`;
9773
- process.stderr.write(`\r${chalk31.dim(progress)} ${name}...`);
10539
+ process.stderr.write(`\r${chalk32.dim(progress)} ${name}...`);
9774
10540
  try {
9775
10541
  await ingestSingleFile(file, options);
9776
10542
  success++;
@@ -9779,12 +10545,12 @@ async function ingestCommand(input, options) {
9779
10545
  }
9780
10546
  }
9781
10547
  process.stderr.write("\r" + " ".repeat(80) + "\r");
9782
- console.log(chalk31.green(`Batch complete: ${success}/${files.length} files ingested`));
10548
+ console.log(chalk32.green(`Batch complete: ${success}/${files.length} files ingested`));
9783
10549
  if (failed.length > 0) {
9784
- console.log(chalk31.yellow(`
10550
+ console.log(chalk32.yellow(`
9785
10551
  Failed (${failed.length}):`));
9786
10552
  for (const f of failed)
9787
- console.log(chalk31.yellow(` - ${f}`));
10553
+ console.log(chalk32.yellow(` - ${f}`));
9788
10554
  }
9789
10555
  return;
9790
10556
  }
@@ -9805,7 +10571,7 @@ async function ingestSingleFile(input, options) {
9805
10571
  const url = new URL(input);
9806
10572
  const host = url.hostname;
9807
10573
  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."));
10574
+ console.error(chalk32.yellow("Private/local URLs are not allowed for security."));
9809
10575
  process.exit(1);
9810
10576
  }
9811
10577
  } catch {
@@ -9825,7 +10591,7 @@ async function ingestSingleFile(input, options) {
9825
10591
  source: input
9826
10592
  };
9827
10593
  } catch (err) {
9828
- console.error(chalk31.yellow(`YouTube extraction failed, falling back to basic URL. (${err instanceof Error ? err.message : "error"})`));
10594
+ console.error(chalk32.yellow(`YouTube extraction failed, falling back to basic URL. (${err instanceof Error ? err.message : "error"})`));
9829
10595
  ingestInput = {
9830
10596
  type: "youtube",
9831
10597
  content: input + "\n",
@@ -9843,7 +10609,7 @@ async function ingestSingleFile(input, options) {
9843
10609
  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
10610
  content += text;
9845
10611
  } catch (err) {
9846
- console.error(chalk31.yellow(`Web fetch failed: saving URL only. (${err instanceof Error ? err.message : "network error"})`));
10612
+ console.error(chalk32.yellow(`Web fetch failed: saving URL only. (${err instanceof Error ? err.message : "network error"})`));
9847
10613
  }
9848
10614
  ingestInput = {
9849
10615
  type: "url",
@@ -9854,7 +10620,7 @@ async function ingestSingleFile(input, options) {
9854
10620
  source: input
9855
10621
  };
9856
10622
  }
9857
- } else if (existsSync23(input)) {
10623
+ } else if (existsSync25(input)) {
9858
10624
  const ext = extname8(input).toLowerCase();
9859
10625
  const binaryExts = /* @__PURE__ */ new Set([".pdf", ".docx", ".pptx", ".xlsx", ".xls"]);
9860
10626
  const structuredExts = /* @__PURE__ */ new Set([".json", ".csv", ".xml", ".html", ".htm", ".yaml", ".yml", ".rtf"]);
@@ -9862,7 +10628,7 @@ async function ingestSingleFile(input, options) {
9862
10628
  try {
9863
10629
  const { extractFileContent: extractFileContent2 } = await Promise.resolve().then(() => (init_file_extractors(), file_extractors_exports));
9864
10630
  const extracted = await extractFileContent2(resolve20(input));
9865
- console.log(chalk31.dim(` Extracted ${extracted.metadata.wordCount} words from ${ext} file`));
10631
+ console.log(chalk32.dim(` Extracted ${extracted.metadata.wordCount} words from ${ext} file`));
9866
10632
  ingestInput = {
9867
10633
  type: extracted.sourceFormat,
9868
10634
  content: extracted.text,
@@ -9873,10 +10639,10 @@ async function ingestSingleFile(input, options) {
9873
10639
  source: input
9874
10640
  };
9875
10641
  } catch (err) {
9876
- console.error(chalk31.yellow(`Binary file extraction failed, saving as-is. (${err instanceof Error ? err.message : "error"})`));
10642
+ console.error(chalk32.yellow(`Binary file extraction failed, saving as-is. (${err instanceof Error ? err.message : "error"})`));
9877
10643
  ingestInput = {
9878
10644
  type: "file",
9879
- content: readFileSync17(input, "utf-8"),
10645
+ content: readFileSync18(input, "utf-8"),
9880
10646
  tags,
9881
10647
  stage,
9882
10648
  title: options.title,
@@ -9887,7 +10653,7 @@ async function ingestSingleFile(input, options) {
9887
10653
  try {
9888
10654
  const { extractFileContent: extractFileContent2 } = await Promise.resolve().then(() => (init_file_extractors(), file_extractors_exports));
9889
10655
  const extracted = await extractFileContent2(resolve20(input));
9890
- console.log(chalk31.dim(` Extracted ${extracted.metadata.wordCount} words from ${ext} file`));
10656
+ console.log(chalk32.dim(` Extracted ${extracted.metadata.wordCount} words from ${ext} file`));
9891
10657
  ingestInput = {
9892
10658
  type: "file",
9893
10659
  content: extracted.text,
@@ -9897,11 +10663,11 @@ async function ingestSingleFile(input, options) {
9897
10663
  source: input
9898
10664
  };
9899
10665
  } catch (err) {
9900
- const fileContent = readFileSync17(input, "utf-8");
10666
+ const fileContent = readFileSync18(input, "utf-8");
9901
10667
  ingestInput = { type: "file", content: fileContent, tags, stage, title: options.title, source: input };
9902
10668
  }
9903
10669
  } else {
9904
- const fileContent = readFileSync17(input, "utf-8");
10670
+ const fileContent = readFileSync18(input, "utf-8");
9905
10671
  ingestInput = {
9906
10672
  type: "file",
9907
10673
  content: fileContent,
@@ -9921,103 +10687,103 @@ async function ingestSingleFile(input, options) {
9921
10687
  };
9922
10688
  }
9923
10689
  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}`));
10690
+ console.log(chalk32.green(`Ingested: ${result.title}`));
10691
+ console.log(chalk32.dim(` Stage: ${result.stage}`));
10692
+ console.log(chalk32.dim(` Saved: ${result.savedTo}`));
10693
+ console.log(chalk32.dim(` Words: ${result.wordCount}`));
9928
10694
  if (result.indexCode)
9929
- console.log(chalk31.dim(` Index: ${result.indexCode}`));
10695
+ console.log(chalk32.dim(` Index: ${result.indexCode}`));
9930
10696
  if (result.tags.length > 0)
9931
- console.log(chalk31.dim(` Tags: ${result.tags.join(", ")}`));
9932
- console.log(chalk31.dim(" Wiki: auto-compiled"));
10697
+ console.log(chalk32.dim(` Tags: ${result.tags.join(", ")}`));
10698
+ console.log(chalk32.dim(" Wiki: auto-compiled"));
9933
10699
  console.log("");
9934
10700
  }
9935
10701
  async function promoteCommand(filePath, options) {
9936
10702
  const config = loadConfig();
9937
10703
  const target = options.to;
9938
10704
  if (!["fleeting", "literature", "permanent"].includes(target)) {
9939
- console.error(chalk31.red("--to must be: fleeting, literature, or permanent"));
10705
+ console.error(chalk32.red("--to must be: fleeting, literature, or permanent"));
9940
10706
  process.exit(1);
9941
10707
  }
9942
10708
  const newPath = promoteNote(config.vaultPath, filePath, target);
9943
- console.log(chalk31.green(`Promoted to ${target}: ${newPath}`));
10709
+ console.log(chalk32.green(`Promoted to ${target}: ${newPath}`));
9944
10710
  }
9945
10711
 
9946
10712
  // packages/cli/dist/commands/autopilot-cmd.js
9947
- import chalk32 from "chalk";
10713
+ import chalk33 from "chalk";
9948
10714
  import { resolve as resolve21 } from "node:path";
9949
10715
  async function autopilotCommand(options) {
9950
10716
  const config = loadConfig();
9951
10717
  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..."));
10718
+ console.log(chalk33.bold("\n \u2726 Stellavault Autopilot"));
10719
+ console.log(chalk33.dim(" Knowledge flywheel: inbox \u2192 compile \u2192 lint \u2192 repeat\n"));
10720
+ console.log(chalk33.cyan("Step 1/4: Checking inbox..."));
9955
10721
  const inbox = getInboxItems(vaultPath);
9956
10722
  if (inbox.length === 0) {
9957
- console.log(chalk32.dim(" No new items in raw/ folder."));
10723
+ console.log(chalk33.dim(" No new items in raw/ folder."));
9958
10724
  } else {
9959
- console.log(chalk32.green(` ${inbox.length} new items found.`));
10725
+ console.log(chalk33.green(` ${inbox.length} new items found.`));
9960
10726
  for (const item of inbox.slice(0, 5)) {
9961
- console.log(chalk32.dim(` - ${item.title} (${item.wordCount} words)`));
10727
+ console.log(chalk33.dim(` - ${item.title} (${item.wordCount} words)`));
9962
10728
  }
9963
10729
  if (inbox.length > 5)
9964
- console.log(chalk32.dim(` ... and ${inbox.length - 5} more`));
10730
+ console.log(chalk33.dim(` ... and ${inbox.length - 5} more`));
9965
10731
  }
9966
- console.log(chalk32.cyan("\nStep 2/4: Compiling wiki..."));
10732
+ console.log(chalk33.cyan("\nStep 2/4: Compiling wiki..."));
9967
10733
  const rawPath = resolve21(vaultPath, "raw");
9968
10734
  const wikiPath = resolve21(vaultPath, "_wiki");
9969
10735
  const compileResult = compileWiki(rawPath, wikiPath);
9970
10736
  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}`));
10737
+ console.log(chalk33.green(` Compiled ${compileResult.rawDocCount} raw \u2192 ${compileResult.wikiArticles.length} wiki articles`));
10738
+ console.log(chalk33.dim(` Concepts: ${compileResult.concepts.length}`));
9973
10739
  for (const item of inbox) {
9974
10740
  try {
9975
10741
  archiveFile(resolve21(vaultPath, "raw", item.filePath));
9976
10742
  } catch {
9977
10743
  }
9978
10744
  }
9979
- console.log(chalk32.dim(` Archived ${inbox.length} processed items.`));
10745
+ console.log(chalk33.dim(` Archived ${inbox.length} processed items.`));
9980
10746
  } else {
9981
- console.log(chalk32.dim(" No raw documents to compile."));
10747
+ console.log(chalk33.dim(" No raw documents to compile."));
9982
10748
  }
9983
- console.log(chalk32.cyan("\nStep 3/4: Running health check..."));
10749
+ console.log(chalk33.cyan("\nStep 3/4: Running health check..."));
9984
10750
  const hub = createKnowledgeHub(config);
9985
10751
  await hub.store.initialize();
9986
10752
  const lintResult = await lintKnowledge(hub.store);
9987
- const scoreColor = lintResult.score >= 80 ? chalk32.green : lintResult.score >= 50 ? chalk32.yellow : chalk32.red;
10753
+ const scoreColor = lintResult.score >= 80 ? chalk33.green : lintResult.score >= 50 ? chalk33.yellow : chalk33.red;
9988
10754
  console.log(` Health: ${scoreColor(lintResult.score + "/100")}`);
9989
10755
  const critical = lintResult.issues.filter((i) => i.severity === "critical").length;
9990
10756
  const warnings = lintResult.issues.filter((i) => i.severity === "warning").length;
9991
10757
  if (critical > 0)
9992
- console.log(chalk32.red(` Critical: ${critical}`));
10758
+ console.log(chalk33.red(` Critical: ${critical}`));
9993
10759
  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)));
10760
+ console.log(chalk33.yellow(` Warnings: ${warnings}`));
10761
+ console.log(chalk33.cyan("\nStep 4/4: Summary"));
10762
+ console.log(chalk33.dim("\u2500".repeat(40)));
9997
10763
  console.log(` Inbox processed: ${inbox.length}`);
9998
10764
  console.log(` Wiki articles: ${compileResult.wikiArticles.length}`);
9999
10765
  console.log(` Health score: ${lintResult.score}/100`);
10000
10766
  console.log(` Issues: ${lintResult.issues.length}`);
10001
10767
  if (lintResult.suggestions.length > 0) {
10002
- console.log(chalk32.cyan("\n Suggestions:"));
10768
+ console.log(chalk33.cyan("\n Suggestions:"));
10003
10769
  for (const s of lintResult.suggestions.slice(0, 3)) {
10004
- console.log(chalk32.dim(` \u2192 ${s}`));
10770
+ console.log(chalk33.dim(` \u2192 ${s}`));
10005
10771
  }
10006
10772
  }
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"));
10773
+ console.log(chalk33.dim("\n\u2500".repeat(40)));
10774
+ console.log(chalk33.dim("\n Next: run `stellavault index` to update search vectors."));
10775
+ console.log(chalk33.green(" Autopilot complete.\n"));
10010
10776
  await hub.store.close?.();
10011
10777
  }
10012
10778
 
10013
10779
  // 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";
10780
+ import chalk34 from "chalk";
10781
+ import { existsSync as existsSync26, statSync as statSync6 } from "node:fs";
10782
+ import { join as join31 } from "node:path";
10783
+ import { homedir as homedir17 } from "node:os";
10018
10784
  async function doctorCommand() {
10019
10785
  console.log("");
10020
- console.log(chalk33.bold(" \u{1FA7A} Stellavault Doctor\n"));
10786
+ console.log(chalk34.bold(" \u{1FA7A} Stellavault Doctor\n"));
10021
10787
  const checks = [];
10022
10788
  const nodeVersion2 = parseInt(process.versions.node.split(".")[0], 10);
10023
10789
  checks.push({
@@ -10027,10 +10793,10 @@ async function doctorCommand() {
10027
10793
  fix: nodeVersion2 < 20 ? "Download Node.js 20+: https://nodejs.org" : void 0
10028
10794
  });
10029
10795
  const configPaths = [
10030
- join29(process.cwd(), ".stellavault.json"),
10031
- join29(homedir15(), ".stellavault.json")
10796
+ join31(process.cwd(), ".stellavault.json"),
10797
+ join31(homedir17(), ".stellavault.json")
10032
10798
  ];
10033
- const configPath = configPaths.find((p) => existsSync24(p));
10799
+ const configPath = configPaths.find((p) => existsSync26(p));
10034
10800
  checks.push({
10035
10801
  name: "Config file",
10036
10802
  pass: !!configPath,
@@ -10041,8 +10807,8 @@ async function doctorCommand() {
10041
10807
  let dbPath = "";
10042
10808
  if (configPath) {
10043
10809
  try {
10044
- const { readFileSync: readFileSync18 } = await import("node:fs");
10045
- const config = JSON.parse(readFileSync18(configPath, "utf-8"));
10810
+ const { readFileSync: readFileSync19 } = await import("node:fs");
10811
+ const config = JSON.parse(readFileSync19(configPath, "utf-8"));
10046
10812
  vaultPath = config.vaultPath || "";
10047
10813
  dbPath = config.dbPath || "";
10048
10814
  } catch (err) {
@@ -10055,7 +10821,7 @@ async function doctorCommand() {
10055
10821
  }
10056
10822
  }
10057
10823
  if (vaultPath) {
10058
- const vaultExists = existsSync24(vaultPath);
10824
+ const vaultExists = existsSync26(vaultPath);
10059
10825
  checks.push({
10060
10826
  name: "Vault path",
10061
10827
  pass: vaultExists,
@@ -10084,7 +10850,7 @@ async function doctorCommand() {
10084
10850
  }
10085
10851
  }
10086
10852
  if (dbPath) {
10087
- const dbExists = existsSync24(dbPath);
10853
+ const dbExists = existsSync26(dbPath);
10088
10854
  checks.push({
10089
10855
  name: "Database",
10090
10856
  pass: dbExists,
@@ -10099,20 +10865,20 @@ async function doctorCommand() {
10099
10865
  fix: "Re-run: stellavault init"
10100
10866
  });
10101
10867
  }
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"));
10868
+ const cacheDir = join31(homedir17(), ".cache", "onnxruntime");
10869
+ const hfCache = join31(homedir17(), ".cache", "huggingface");
10870
+ const xenovaCache = join31(homedir17(), ".cache", "xenova");
10871
+ const modelCached = existsSync26(cacheDir) || existsSync26(hfCache) || existsSync26(xenovaCache) || existsSync26(join31(homedir17(), ".cache", "transformers"));
10106
10872
  checks.push({
10107
10873
  name: "Embedding model cached",
10108
10874
  pass: modelCached,
10109
10875
  detail: modelCached ? "local model files found" : "not downloaded yet",
10110
10876
  fix: !modelCached ? "Will download automatically on first index (~30MB)" : void 0
10111
10877
  });
10112
- const testDir = join29(homedir15(), ".stellavault");
10878
+ const testDir = join31(homedir17(), ".stellavault");
10113
10879
  try {
10114
- const { mkdirSync: mkdirSync22 } = await import("node:fs");
10115
- mkdirSync22(testDir, { recursive: true });
10880
+ const { mkdirSync: mkdirSync23 } = await import("node:fs");
10881
+ mkdirSync23(testDir, { recursive: true });
10116
10882
  checks.push({ name: "Write permission (~/.stellavault)", pass: true, detail: "OK" });
10117
10883
  } catch {
10118
10884
  checks.push({
@@ -10124,20 +10890,20 @@ async function doctorCommand() {
10124
10890
  }
10125
10891
  let passCount = 0;
10126
10892
  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)}`);
10893
+ const icon = c.pass ? chalk34.green("\u2713") : chalk34.red("\u2717");
10894
+ console.log(` ${icon} ${c.name} ${chalk34.dim("\u2014")} ${c.pass ? chalk34.dim(c.detail) : chalk34.yellow(c.detail)}`);
10129
10895
  if (c.fix)
10130
- console.log(` ${chalk33.dim("Fix:")} ${c.fix}`);
10896
+ console.log(` ${chalk34.dim("Fix:")} ${c.fix}`);
10131
10897
  if (c.pass)
10132
10898
  passCount++;
10133
10899
  }
10134
10900
  console.log("");
10135
10901
  if (passCount === checks.length) {
10136
- console.log(chalk33.green.bold(` All ${checks.length} checks passed. You're good to go! \u2726
10902
+ console.log(chalk34.green.bold(` All ${checks.length} checks passed. You're good to go! \u2726
10137
10903
  `));
10138
10904
  } else {
10139
10905
  const failCount = checks.length - passCount;
10140
- console.log(chalk33.yellow(` ${failCount} issue${failCount > 1 ? "s" : ""} found. Fix them and re-run: stellavault doctor
10906
+ console.log(chalk34.yellow(` ${failCount} issue${failCount > 1 ? "s" : ""} found. Fix them and re-run: stellavault doctor
10141
10907
  `));
10142
10908
  }
10143
10909
  process.exit(passCount === checks.length ? 0 : 1);
@@ -10162,7 +10928,7 @@ if (nodeVersion < 20) {
10162
10928
  process.exit(1);
10163
10929
  }
10164
10930
  var program = new Command();
10165
- var SV_VERSION = true ? "0.7.4" : "0.0.0-dev";
10931
+ var SV_VERSION = true ? "0.8.1" : "0.0.0-dev";
10166
10932
  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
10933
  program.command("init").description("Interactive setup wizard \u2014 get started in 3 minutes").action(initCommand);
10168
10934
  program.command("doctor").description("Diagnose setup issues (config, vault, DB, model, Node version)").action(doctorCommand);
@@ -10174,6 +10940,7 @@ program.command("ingest <input>").description("Ingest any input (URL, file, text
10174
10940
  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
10941
  program.command("graph").description("Launch the 3D knowledge graph in your browser").action(graphCommand);
10176
10942
  program.command("serve").alias("mcp").description("Start MCP server (for Claude Code / Claude Desktop). Alias: mcp").action(serveCommand);
10943
+ 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
10944
  program.command("decay").description("Memory decay report \u2014 find notes you are forgetting").action(decayCommand);
10178
10945
  program.command("brief").description("Daily knowledge briefing (decay + gaps + activity)").action(briefCommand);
10179
10946
  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);