stellavault 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (294) hide show
  1. package/.env.example +12 -0
  2. package/CLAUDE.md +39 -0
  3. package/CONTRIBUTING.md +65 -0
  4. package/LICENSE +21 -0
  5. package/README.md +182 -0
  6. package/memory/MEMORY.md +25 -0
  7. package/package.json +33 -0
  8. package/packages/cli/bin/ekh.js +2 -0
  9. package/packages/cli/bin/stellavault.js +2 -0
  10. package/packages/cli/dist/commands/brief-cmd.d.ts +2 -0
  11. package/packages/cli/dist/commands/brief-cmd.d.ts.map +1 -0
  12. package/packages/cli/dist/commands/brief-cmd.js +82 -0
  13. package/packages/cli/dist/commands/brief-cmd.js.map +1 -0
  14. package/packages/cli/dist/commands/capture-cmd.d.ts +7 -0
  15. package/packages/cli/dist/commands/capture-cmd.d.ts.map +1 -0
  16. package/packages/cli/dist/commands/capture-cmd.js +31 -0
  17. package/packages/cli/dist/commands/capture-cmd.js.map +1 -0
  18. package/packages/cli/dist/commands/card-cmd.d.ts +4 -0
  19. package/packages/cli/dist/commands/card-cmd.d.ts.map +1 -0
  20. package/packages/cli/dist/commands/card-cmd.js +26 -0
  21. package/packages/cli/dist/commands/card-cmd.js.map +1 -0
  22. package/packages/cli/dist/commands/clip-cmd.d.ts +4 -0
  23. package/packages/cli/dist/commands/clip-cmd.d.ts.map +1 -0
  24. package/packages/cli/dist/commands/clip-cmd.js +151 -0
  25. package/packages/cli/dist/commands/clip-cmd.js.map +1 -0
  26. package/packages/cli/dist/commands/cloud-cmd.d.ts +4 -0
  27. package/packages/cli/dist/commands/cloud-cmd.d.ts.map +1 -0
  28. package/packages/cli/dist/commands/cloud-cmd.js +64 -0
  29. package/packages/cli/dist/commands/cloud-cmd.js.map +1 -0
  30. package/packages/cli/dist/commands/contradictions-cmd.d.ts +2 -0
  31. package/packages/cli/dist/commands/contradictions-cmd.d.ts.map +1 -0
  32. package/packages/cli/dist/commands/contradictions-cmd.js +34 -0
  33. package/packages/cli/dist/commands/contradictions-cmd.js.map +1 -0
  34. package/packages/cli/dist/commands/decay-cmd.d.ts +2 -0
  35. package/packages/cli/dist/commands/decay-cmd.d.ts.map +1 -0
  36. package/packages/cli/dist/commands/decay-cmd.js +48 -0
  37. package/packages/cli/dist/commands/decay-cmd.js.map +1 -0
  38. package/packages/cli/dist/commands/digest-cmd.d.ts +4 -0
  39. package/packages/cli/dist/commands/digest-cmd.d.ts.map +1 -0
  40. package/packages/cli/dist/commands/digest-cmd.js +79 -0
  41. package/packages/cli/dist/commands/digest-cmd.js.map +1 -0
  42. package/packages/cli/dist/commands/duplicates-cmd.d.ts +4 -0
  43. package/packages/cli/dist/commands/duplicates-cmd.d.ts.map +1 -0
  44. package/packages/cli/dist/commands/duplicates-cmd.js +30 -0
  45. package/packages/cli/dist/commands/duplicates-cmd.js.map +1 -0
  46. package/packages/cli/dist/commands/federate-cmd.d.ts +5 -0
  47. package/packages/cli/dist/commands/federate-cmd.d.ts.map +1 -0
  48. package/packages/cli/dist/commands/federate-cmd.js +217 -0
  49. package/packages/cli/dist/commands/federate-cmd.js.map +1 -0
  50. package/packages/cli/dist/commands/gaps-cmd.d.ts +2 -0
  51. package/packages/cli/dist/commands/gaps-cmd.d.ts.map +1 -0
  52. package/packages/cli/dist/commands/gaps-cmd.js +33 -0
  53. package/packages/cli/dist/commands/gaps-cmd.js.map +1 -0
  54. package/packages/cli/dist/commands/graph-cmd.d.ts +2 -0
  55. package/packages/cli/dist/commands/graph-cmd.d.ts.map +1 -0
  56. package/packages/cli/dist/commands/graph-cmd.js +77 -0
  57. package/packages/cli/dist/commands/graph-cmd.js.map +1 -0
  58. package/packages/cli/dist/commands/index-cmd.d.ts +2 -0
  59. package/packages/cli/dist/commands/index-cmd.d.ts.map +1 -0
  60. package/packages/cli/dist/commands/index-cmd.js +57 -0
  61. package/packages/cli/dist/commands/index-cmd.js.map +1 -0
  62. package/packages/cli/dist/commands/init-cmd.d.ts +2 -0
  63. package/packages/cli/dist/commands/init-cmd.d.ts.map +1 -0
  64. package/packages/cli/dist/commands/init-cmd.js +123 -0
  65. package/packages/cli/dist/commands/init-cmd.js.map +1 -0
  66. package/packages/cli/dist/commands/learn-cmd.d.ts +2 -0
  67. package/packages/cli/dist/commands/learn-cmd.d.ts.map +1 -0
  68. package/packages/cli/dist/commands/learn-cmd.js +48 -0
  69. package/packages/cli/dist/commands/learn-cmd.js.map +1 -0
  70. package/packages/cli/dist/commands/pack-cmd.d.ts +15 -0
  71. package/packages/cli/dist/commands/pack-cmd.d.ts.map +1 -0
  72. package/packages/cli/dist/commands/pack-cmd.js +93 -0
  73. package/packages/cli/dist/commands/pack-cmd.js.map +1 -0
  74. package/packages/cli/dist/commands/review-cmd.d.ts +4 -0
  75. package/packages/cli/dist/commands/review-cmd.d.ts.map +1 -0
  76. package/packages/cli/dist/commands/review-cmd.js +107 -0
  77. package/packages/cli/dist/commands/review-cmd.js.map +1 -0
  78. package/packages/cli/dist/commands/search-cmd.d.ts +4 -0
  79. package/packages/cli/dist/commands/search-cmd.d.ts.map +1 -0
  80. package/packages/cli/dist/commands/search-cmd.js +38 -0
  81. package/packages/cli/dist/commands/search-cmd.js.map +1 -0
  82. package/packages/cli/dist/commands/serve-cmd.d.ts +2 -0
  83. package/packages/cli/dist/commands/serve-cmd.d.ts.map +1 -0
  84. package/packages/cli/dist/commands/serve-cmd.js +14 -0
  85. package/packages/cli/dist/commands/serve-cmd.js.map +1 -0
  86. package/packages/cli/dist/commands/status-cmd.d.ts +2 -0
  87. package/packages/cli/dist/commands/status-cmd.d.ts.map +1 -0
  88. package/packages/cli/dist/commands/status-cmd.js +33 -0
  89. package/packages/cli/dist/commands/status-cmd.js.map +1 -0
  90. package/packages/cli/dist/commands/sync-cmd.d.ts +5 -0
  91. package/packages/cli/dist/commands/sync-cmd.d.ts.map +1 -0
  92. package/packages/cli/dist/commands/sync-cmd.js +62 -0
  93. package/packages/cli/dist/commands/sync-cmd.js.map +1 -0
  94. package/packages/cli/dist/commands/vault-cmd.d.ts +10 -0
  95. package/packages/cli/dist/commands/vault-cmd.d.ts.map +1 -0
  96. package/packages/cli/dist/commands/vault-cmd.js +54 -0
  97. package/packages/cli/dist/commands/vault-cmd.js.map +1 -0
  98. package/packages/cli/dist/index.d.ts +2 -0
  99. package/packages/cli/dist/index.d.ts.map +1 -0
  100. package/packages/cli/dist/index.js +156 -0
  101. package/packages/cli/dist/index.js.map +1 -0
  102. package/packages/cli/package.json +24 -0
  103. package/packages/cli/src/commands/brief-cmd.ts +87 -0
  104. package/packages/cli/src/commands/capture-cmd.ts +34 -0
  105. package/packages/cli/src/commands/card-cmd.ts +29 -0
  106. package/packages/cli/src/commands/clip-cmd.ts +172 -0
  107. package/packages/cli/src/commands/cloud-cmd.ts +75 -0
  108. package/packages/cli/src/commands/contradictions-cmd.ts +41 -0
  109. package/packages/cli/src/commands/decay-cmd.ts +57 -0
  110. package/packages/cli/src/commands/digest-cmd.ts +89 -0
  111. package/packages/cli/src/commands/duplicates-cmd.ts +38 -0
  112. package/packages/cli/src/commands/federate-cmd.ts +236 -0
  113. package/packages/cli/src/commands/gaps-cmd.ts +40 -0
  114. package/packages/cli/src/commands/graph-cmd.ts +88 -0
  115. package/packages/cli/src/commands/index-cmd.ts +65 -0
  116. package/packages/cli/src/commands/init-cmd.ts +145 -0
  117. package/packages/cli/src/commands/learn-cmd.ts +56 -0
  118. package/packages/cli/src/commands/pack-cmd.ts +121 -0
  119. package/packages/cli/src/commands/review-cmd.ts +125 -0
  120. package/packages/cli/src/commands/search-cmd.ts +45 -0
  121. package/packages/cli/src/commands/serve-cmd.ts +17 -0
  122. package/packages/cli/src/commands/status-cmd.ts +37 -0
  123. package/packages/cli/src/commands/sync-cmd.ts +68 -0
  124. package/packages/cli/src/commands/vault-cmd.ts +64 -0
  125. package/packages/cli/src/index.ts +187 -0
  126. package/packages/core/package.json +40 -0
  127. package/packages/core/src/api/dashboard.ts +138 -0
  128. package/packages/core/src/api/graph-data.ts +286 -0
  129. package/packages/core/src/api/pwa.ts +82 -0
  130. package/packages/core/src/api/server.ts +660 -0
  131. package/packages/core/src/capture/voice.ts +168 -0
  132. package/packages/core/src/cloud/index.ts +2 -0
  133. package/packages/core/src/cloud/sync.ts +167 -0
  134. package/packages/core/src/config.ts +82 -0
  135. package/packages/core/src/federation/credits.ts +80 -0
  136. package/packages/core/src/federation/hyperswarm.d.ts +19 -0
  137. package/packages/core/src/federation/identity.ts +90 -0
  138. package/packages/core/src/federation/index.ts +8 -0
  139. package/packages/core/src/federation/node.ts +235 -0
  140. package/packages/core/src/federation/privacy.ts +52 -0
  141. package/packages/core/src/federation/reputation.ts +202 -0
  142. package/packages/core/src/federation/search.ts +129 -0
  143. package/packages/core/src/federation/sharing.ts +165 -0
  144. package/packages/core/src/federation/trust.ts +76 -0
  145. package/packages/core/src/federation/types.ts +25 -0
  146. package/packages/core/src/i18n/index.ts +85 -0
  147. package/packages/core/src/index.ts +133 -0
  148. package/packages/core/src/indexer/chunker.ts +180 -0
  149. package/packages/core/src/indexer/embedder.ts +9 -0
  150. package/packages/core/src/indexer/index.ts +113 -0
  151. package/packages/core/src/indexer/local-embedder.ts +35 -0
  152. package/packages/core/src/indexer/scanner.ts +142 -0
  153. package/packages/core/src/indexer/watcher.ts +62 -0
  154. package/packages/core/src/intelligence/contradiction-detector.ts +134 -0
  155. package/packages/core/src/intelligence/decay-engine.ts +229 -0
  156. package/packages/core/src/intelligence/duplicate-detector.ts +71 -0
  157. package/packages/core/src/intelligence/fsrs.ts +79 -0
  158. package/packages/core/src/intelligence/gap-detector.ts +109 -0
  159. package/packages/core/src/intelligence/learning-path.ts +86 -0
  160. package/packages/core/src/intelligence/notifications.ts +106 -0
  161. package/packages/core/src/intelligence/predictive-gaps.ts +94 -0
  162. package/packages/core/src/intelligence/semantic-versioning.ts +97 -0
  163. package/packages/core/src/intelligence/types.ts +28 -0
  164. package/packages/core/src/mcp/custom-tools.ts +97 -0
  165. package/packages/core/src/mcp/index.ts +1 -0
  166. package/packages/core/src/mcp/server.ts +142 -0
  167. package/packages/core/src/mcp/tools/agentic-graph.ts +96 -0
  168. package/packages/core/src/mcp/tools/brief.ts +49 -0
  169. package/packages/core/src/mcp/tools/decay.ts +40 -0
  170. package/packages/core/src/mcp/tools/decision-journal.ts +95 -0
  171. package/packages/core/src/mcp/tools/export.ts +72 -0
  172. package/packages/core/src/mcp/tools/federated-search.ts +43 -0
  173. package/packages/core/src/mcp/tools/generate-claude-md.ts +130 -0
  174. package/packages/core/src/mcp/tools/get-document.ts +26 -0
  175. package/packages/core/src/mcp/tools/get-related.ts +41 -0
  176. package/packages/core/src/mcp/tools/learning-path.ts +52 -0
  177. package/packages/core/src/mcp/tools/list-topics.ts +20 -0
  178. package/packages/core/src/mcp/tools/search.ts +35 -0
  179. package/packages/core/src/mcp/tools/snapshot.ts +98 -0
  180. package/packages/core/src/multi-vault/index.ts +118 -0
  181. package/packages/core/src/pack/creator.ts +127 -0
  182. package/packages/core/src/pack/exporter.ts +21 -0
  183. package/packages/core/src/pack/importer.ts +82 -0
  184. package/packages/core/src/pack/index.ts +5 -0
  185. package/packages/core/src/pack/marketplace.ts +103 -0
  186. package/packages/core/src/pack/pii-masker.ts +38 -0
  187. package/packages/core/src/pack/types.ts +39 -0
  188. package/packages/core/src/plugins/index.ts +100 -0
  189. package/packages/core/src/plugins/webhooks.ts +110 -0
  190. package/packages/core/src/search/bm25.ts +16 -0
  191. package/packages/core/src/search/index.ts +83 -0
  192. package/packages/core/src/search/rrf.ts +31 -0
  193. package/packages/core/src/search/semantic.ts +15 -0
  194. package/packages/core/src/store/index.ts +2 -0
  195. package/packages/core/src/store/sqlite-vec.ts +290 -0
  196. package/packages/core/src/store/types.ts +22 -0
  197. package/packages/core/src/team/index.ts +126 -0
  198. package/packages/core/src/types/chunk.ts +25 -0
  199. package/packages/core/src/types/document.ts +24 -0
  200. package/packages/core/src/types/graph.ts +44 -0
  201. package/packages/core/src/types/index.ts +15 -0
  202. package/packages/core/src/types/search.ts +38 -0
  203. package/packages/core/src/utils/retry.ts +85 -0
  204. package/packages/core/tests/api-card.test.ts +60 -0
  205. package/packages/core/tests/api-routes.test.ts +98 -0
  206. package/packages/core/tests/bm25.test.ts +87 -0
  207. package/packages/core/tests/chunker.test.ts +48 -0
  208. package/packages/core/tests/cluster.test.ts +75 -0
  209. package/packages/core/tests/constellation.test.ts +77 -0
  210. package/packages/core/tests/export-utils.test.ts +97 -0
  211. package/packages/core/tests/fsrs.test.ts +96 -0
  212. package/packages/core/tests/gesture-detector.test.ts +45 -0
  213. package/packages/core/tests/graph-data.test.ts +87 -0
  214. package/packages/core/tests/layout.test.ts +83 -0
  215. package/packages/core/tests/mcp.test.ts +148 -0
  216. package/packages/core/tests/pack.test.ts +127 -0
  217. package/packages/core/tests/pii-masker.test.ts +42 -0
  218. package/packages/core/tests/profile-card.test.ts +62 -0
  219. package/packages/core/tests/rrf.test.ts +29 -0
  220. package/packages/core/tests/search-integration.test.ts +139 -0
  221. package/packages/core/tests/store.test.ts +80 -0
  222. package/packages/graph/click-result.png +0 -0
  223. package/packages/graph/index.html +17 -0
  224. package/packages/graph/package.json +32 -0
  225. package/packages/graph/src/App.tsx +7 -0
  226. package/packages/graph/src/api/client.ts +39 -0
  227. package/packages/graph/src/components/ClusterFilter.tsx +73 -0
  228. package/packages/graph/src/components/ConstellationView.tsx +232 -0
  229. package/packages/graph/src/components/ExportPanel.tsx +177 -0
  230. package/packages/graph/src/components/Graph3D.tsx +230 -0
  231. package/packages/graph/src/components/GraphEdges.tsx +100 -0
  232. package/packages/graph/src/components/GraphNodes.tsx +386 -0
  233. package/packages/graph/src/components/HealthDashboard.tsx +173 -0
  234. package/packages/graph/src/components/Layout.tsx +214 -0
  235. package/packages/graph/src/components/MotionOverlay.tsx +81 -0
  236. package/packages/graph/src/components/MotionToggle.tsx +33 -0
  237. package/packages/graph/src/components/MultiverseView.tsx +286 -0
  238. package/packages/graph/src/components/NodeDetail.tsx +232 -0
  239. package/packages/graph/src/components/PulseParticle.tsx +232 -0
  240. package/packages/graph/src/components/SearchBar.tsx +107 -0
  241. package/packages/graph/src/components/StarField.tsx +197 -0
  242. package/packages/graph/src/components/StatusBar.tsx +53 -0
  243. package/packages/graph/src/components/Timeline.tsx +148 -0
  244. package/packages/graph/src/components/ToolsPanel.tsx +512 -0
  245. package/packages/graph/src/components/Tooltip.tsx +100 -0
  246. package/packages/graph/src/components/TypeFilter.tsx +131 -0
  247. package/packages/graph/src/embed/EmbedGraph.tsx +144 -0
  248. package/packages/graph/src/hooks/useConstellationLOD.ts +76 -0
  249. package/packages/graph/src/hooks/useDecay.ts +37 -0
  250. package/packages/graph/src/hooks/useExport.ts +165 -0
  251. package/packages/graph/src/hooks/useGraph.ts +69 -0
  252. package/packages/graph/src/hooks/useKeyboardNav.ts +122 -0
  253. package/packages/graph/src/hooks/useLayout.ts +45 -0
  254. package/packages/graph/src/hooks/useMotion.ts +120 -0
  255. package/packages/graph/src/hooks/usePulse.ts +58 -0
  256. package/packages/graph/src/hooks/useSearch.ts +71 -0
  257. package/packages/graph/src/lib/constellation.ts +107 -0
  258. package/packages/graph/src/lib/export-utils.ts +48 -0
  259. package/packages/graph/src/lib/gesture-detector.ts +123 -0
  260. package/packages/graph/src/lib/layout.worker.ts +153 -0
  261. package/packages/graph/src/lib/motion-controller.ts +83 -0
  262. package/packages/graph/src/lib/profile-card.ts +122 -0
  263. package/packages/graph/src/main.tsx +4 -0
  264. package/packages/graph/src/stores/graph-store.ts +155 -0
  265. package/packages/graph/success.png +0 -0
  266. package/packages/graph/test-click.mjs +49 -0
  267. package/packages/graph/test-explore.mjs +102 -0
  268. package/packages/graph/test-final.mjs +61 -0
  269. package/packages/graph/test-graph.mjs +139 -0
  270. package/packages/graph/test-hover.mjs +48 -0
  271. package/packages/graph/test-pulse.mjs +68 -0
  272. package/packages/graph/test-screenshot.mjs +56 -0
  273. package/packages/graph/test-v2.mjs +97 -0
  274. package/packages/graph/vite.config.ts +15 -0
  275. package/packages/sync/.env.example +11 -0
  276. package/packages/sync/.sync-state.json +317 -0
  277. package/packages/sync/.upload-state.json +1009 -0
  278. package/packages/sync/create-stella-network-notion.mjs +151 -0
  279. package/packages/sync/create-stellavault-project-notion.mjs +322 -0
  280. package/packages/sync/logs/sync-2026-03-28.log +6 -0
  281. package/packages/sync/logs/sync-2026-03-29.log +12 -0
  282. package/packages/sync/logs/sync-2026-03-30.log +6 -0
  283. package/packages/sync/logs/sync-2026-03-31.log +6 -0
  284. package/packages/sync/logs/sync-2026-04-01.log +6 -0
  285. package/packages/sync/logs/sync-2026-04-02.log +6 -0
  286. package/packages/sync/package-lock.json +373 -0
  287. package/packages/sync/package.json +16 -0
  288. package/packages/sync/run-sync.bat +18 -0
  289. package/packages/sync/run-sync.mjs +46 -0
  290. package/packages/sync/setup-scheduler.mjs +119 -0
  291. package/packages/sync/structured-sync.mjs +187 -0
  292. package/packages/sync/sync-to-obsidian.mjs +264 -0
  293. package/packages/sync/upload-pdca-to-notion.mjs +495 -0
  294. package/tsconfig.base.json +18 -0
@@ -0,0 +1,145 @@
1
+ // stellavault init β€” μΈν„°λž™ν‹°λΈŒ μ˜¨λ³΄λ”© μœ„μ €λ“œ (F-A01)
2
+
3
+ import { createInterface } from 'node:readline';
4
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { homedir } from 'node:os';
7
+ import ora from 'ora';
8
+ import chalk from 'chalk';
9
+ import { loadConfig, createSqliteVecStore, createLocalEmbedder, indexVault, createSearchEngine } from '@stellavault/core';
10
+
11
+ function ask(rl: ReturnType<typeof createInterface>, question: string, defaultVal?: string): Promise<string> {
12
+ const suffix = defaultVal ? ` ${chalk.dim(`(${defaultVal})`)}` : '';
13
+ return new Promise((resolve) => {
14
+ rl.question(`${question}${suffix}: `, (answer) => {
15
+ resolve(answer.trim() || defaultVal || '');
16
+ });
17
+ });
18
+ }
19
+
20
+ export async function initCommand() {
21
+ console.log('');
22
+ console.log(chalk.bold(' ✦ Stellavault Setup Wizard'));
23
+ console.log(chalk.dim(' Notes die in folders. Let\'s bring yours to life.\n'));
24
+
25
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
26
+
27
+ try {
28
+ // Step 1: Vault Path
29
+ console.log(chalk.cyan(' Step 1/3') + ' β€” Where is your Obsidian vault?');
30
+ console.log(chalk.dim(' This is the folder containing your .md files.\n'));
31
+
32
+ let vaultPath = '';
33
+ while (!vaultPath) {
34
+ const input = await ask(rl, ' Vault path');
35
+ if (!input) {
36
+ console.log(chalk.yellow(' Please enter your vault path.'));
37
+ continue;
38
+ }
39
+ const resolved = input.replace(/^~/, homedir());
40
+ if (!existsSync(resolved)) {
41
+ console.log(chalk.yellow(` Path not found: ${resolved}`));
42
+ continue;
43
+ }
44
+ vaultPath = resolved;
45
+ }
46
+
47
+ console.log(chalk.green(` βœ“ Vault: ${vaultPath}\n`));
48
+
49
+ // Save config
50
+ const configDir = join(homedir(), '.stellavault');
51
+ mkdirSync(configDir, { recursive: true });
52
+ const dbPath = join(configDir, 'index.db');
53
+
54
+ const configData = {
55
+ vaultPath,
56
+ dbPath,
57
+ embedding: { model: 'local', localModel: 'all-MiniLM-L6-v2' },
58
+ chunking: { maxTokens: 300, overlap: 50, minTokens: 50 },
59
+ search: { defaultLimit: 10, rrfK: 60 },
60
+ mcp: { mode: 'stdio', port: 3333 },
61
+ };
62
+ writeFileSync(join(homedir(), '.stellavault.json'), JSON.stringify(configData, null, 2), 'utf-8');
63
+ console.log(chalk.dim(` Config saved: ~/.stellavault.json`));
64
+
65
+ // Step 2: Indexing
66
+ console.log('');
67
+ console.log(chalk.cyan(' Step 2/3') + ' β€” Indexing your vault');
68
+ console.log(chalk.dim(' Vectorizing notes with local AI (no data leaves your machine).\n'));
69
+
70
+ const spinner = ora({ text: ' Loading embedding model...', indent: 2 }).start();
71
+
72
+ const store = createSqliteVecStore(dbPath);
73
+ await store.initialize();
74
+
75
+ const embedder = createLocalEmbedder('all-MiniLM-L6-v2');
76
+ await embedder.initialize();
77
+ spinner.text = ' Scanning vault...';
78
+
79
+ const result = await indexVault(vaultPath, {
80
+ store,
81
+ embedder,
82
+ chunkOptions: { maxTokens: 300, overlap: 50, minTokens: 50 },
83
+ onProgress(current, total, doc) {
84
+ const pct = Math.round((current / total) * 100);
85
+ const bar = 'β–ˆ'.repeat(Math.floor(pct / 4)) + 'β–‘'.repeat(25 - Math.floor(pct / 4));
86
+ spinner.text = ` [${bar}] ${pct}% (${current}/${total}) ${doc.title.slice(0, 30)}`;
87
+ },
88
+ });
89
+
90
+ spinner.succeed(chalk.green(` Indexed ${result.indexed} docs, ${result.totalChunks} chunks (${(result.elapsedMs / 1000).toFixed(1)}s)`));
91
+
92
+ // Step 3: First Search
93
+ console.log('');
94
+ console.log(chalk.cyan(' Step 3/3') + ' β€” Try your first search');
95
+ console.log(chalk.dim(' Type a topic you know about. Stellavault finds connections.\n'));
96
+
97
+ const searchEngine = createSearchEngine({ store, embedder, rrfK: 60 });
98
+
99
+ let searchDone = false;
100
+ while (!searchDone) {
101
+ const query = await ask(rl, ' Search');
102
+ if (!query) {
103
+ console.log(chalk.dim(' Type something, or press Ctrl+C to skip.'));
104
+ continue;
105
+ }
106
+
107
+ const searchSpinner = ora({ text: ' Searching...', indent: 2 }).start();
108
+ const results = await searchEngine.search({ query, limit: 5 });
109
+ searchSpinner.stop();
110
+
111
+ if (results.length === 0) {
112
+ console.log(chalk.yellow(' No results. Try a different topic.'));
113
+ continue;
114
+ }
115
+
116
+ console.log('');
117
+ for (const r of results) {
118
+ const score = Math.round(r.score * 100);
119
+ const bar = score >= 70 ? chalk.green('●') : score >= 40 ? chalk.yellow('●') : chalk.dim('●');
120
+ console.log(` ${bar} ${chalk.bold(r.document.title)} ${chalk.dim(`(${score}%)`)}`);
121
+ if (r.highlights[0]) {
122
+ console.log(` ${chalk.dim(r.highlights[0].slice(0, 80))}...`);
123
+ }
124
+ }
125
+ console.log('');
126
+ searchDone = true;
127
+ }
128
+
129
+ await store.close();
130
+
131
+ // Done!
132
+ console.log(chalk.bold.green(' ✦ Setup complete!\n'));
133
+ console.log(' What\'s next:');
134
+ console.log(` ${chalk.cyan('stellavault graph')} Launch 3D knowledge graph`);
135
+ console.log(` ${chalk.cyan('stellavault decay')} See what knowledge is fading`);
136
+ console.log(` ${chalk.cyan('stellavault brief')} Get your daily knowledge briefing`);
137
+ console.log(` ${chalk.cyan('stellavault serve')} Connect AI agents via MCP`);
138
+ console.log('');
139
+ console.log(chalk.dim(' Your knowledge is now alive. ✦'));
140
+ console.log('');
141
+
142
+ } finally {
143
+ rl.close();
144
+ }
145
+ }
@@ -0,0 +1,56 @@
1
+ // stellavault learn β€” AI learning path recommendations (F-A11)
2
+
3
+ import chalk from 'chalk';
4
+ import { loadConfig, createKnowledgeHub, DecayEngine, detectKnowledgeGaps, generateLearningPath } from '@stellavault/core';
5
+
6
+ export async function learnCommand(_opts: any, cmd: any) {
7
+ const globalOpts = cmd?.parent?.opts?.() ?? {};
8
+ const jsonMode = globalOpts.json;
9
+ const config = loadConfig();
10
+ const hub = createKnowledgeHub(config);
11
+
12
+ await hub.store.initialize();
13
+ await hub.embedder.initialize();
14
+
15
+ const db = hub.store.getDb() as any;
16
+ if (!db) { console.error(chalk.red('Cannot access database')); process.exit(1); }
17
+
18
+ const decayEngine = new DecayEngine(db);
19
+ const decayReport = await decayEngine.computeAll();
20
+
21
+ let gaps: any[] = [];
22
+ try {
23
+ const gapReport = await detectKnowledgeGaps(hub.store);
24
+ gaps = gapReport.gaps ?? [];
25
+ } catch { /* gaps may fail without enough data */ }
26
+
27
+ const path = generateLearningPath({ decayReport, gaps }, 15);
28
+ await hub.store.close();
29
+
30
+ if (jsonMode) {
31
+ console.log(JSON.stringify(path, null, 2));
32
+ return;
33
+ }
34
+
35
+ console.log('');
36
+ console.log(chalk.bold(' 🎯 Your Learning Path'));
37
+ console.log(chalk.dim(` ${path.summary.reviewCount} to review Β· ${path.summary.bridgeCount} gaps to bridge Β· ~${path.summary.estimatedMinutes}min`));
38
+ console.log('');
39
+
40
+ for (const item of path.items) {
41
+ const icon = item.category === 'review' ? 'πŸ“–' : item.category === 'bridge' ? 'πŸŒ‰' : 'πŸ”­';
42
+ const prioColor = item.priority === 'critical' ? chalk.red : item.priority === 'important' ? chalk.yellow : chalk.dim;
43
+ const prioLabel = prioColor(item.priority.toUpperCase());
44
+
45
+ console.log(` ${icon} ${prioLabel} ${chalk.bold(item.title)} ${chalk.dim(`(${item.score}pt)`)}`);
46
+ console.log(` ${chalk.dim(item.reason)}`);
47
+ }
48
+
49
+ if (path.items.length === 0) {
50
+ console.log(chalk.green(' All clear! Your knowledge is in great shape.'));
51
+ }
52
+
53
+ console.log('');
54
+ console.log(chalk.dim(' πŸ’‘ stellavault review β€” start reviewing decaying notes'));
55
+ console.log('');
56
+ }
@@ -0,0 +1,121 @@
1
+ // stellavault pack β€” Knowledge Pack 관리
2
+
3
+ import chalk from 'chalk';
4
+ import { resolve, join } from 'node:path';
5
+ import { readdirSync, existsSync, readFileSync, mkdirSync } from 'node:fs';
6
+ import { homedir } from 'node:os';
7
+ import {
8
+ loadConfig, createKnowledgeHub,
9
+ createPack, exportPack, importPack, packToSummary,
10
+ } from '@stellavault/core';
11
+
12
+ const PACKS_DIR = join(homedir(), '.stellavault', 'packs');
13
+
14
+ export async function packCreateCommand(name: string, options: {
15
+ fromSearch?: string;
16
+ fromCluster?: string;
17
+ author?: string;
18
+ license?: string;
19
+ description?: string;
20
+ limit?: string;
21
+ }) {
22
+ const config = loadConfig();
23
+ const hub = createKnowledgeHub(config);
24
+ await hub.store.initialize();
25
+ await hub.embedder.initialize();
26
+
27
+ console.error(chalk.dim('⏳ Creating pack...'));
28
+
29
+ const { pack, piiReport } = await createPack(hub.store, hub.searchEngine, hub.embedder, {
30
+ name,
31
+ fromSearch: options.fromSearch,
32
+ fromCluster: options.fromCluster ? parseInt(options.fromCluster) : undefined,
33
+ author: options.author ?? 'anonymous',
34
+ license: options.license ?? 'CC-BY-4.0',
35
+ description: options.description,
36
+ limit: options.limit ? parseInt(options.limit) : 100,
37
+ });
38
+
39
+ mkdirSync(PACKS_DIR, { recursive: true });
40
+ const outPath = join(PACKS_DIR, `${name}.sv-pack`);
41
+ exportPack(pack, outPath);
42
+
43
+ console.error(chalk.green(`βœ… Pack created: ${name}`));
44
+ console.error(` πŸ“¦ ${pack.chunks.length} chunks`);
45
+ console.error(` πŸ’Ύ ${outPath}`);
46
+ if (piiReport.redactedCount > 0) {
47
+ console.error(chalk.yellow(` πŸ”’ PII masked: ${piiReport.redactedCount} items (${piiReport.types.join(', ')})`));
48
+ }
49
+
50
+ await hub.store.close();
51
+ }
52
+
53
+ export async function packExportCommand(name: string, options: { output?: string }) {
54
+ const srcPath = join(PACKS_DIR, `${name}.sv-pack`);
55
+ if (!existsSync(srcPath)) {
56
+ console.error(chalk.red(`❌ Pack not found: ${name}`));
57
+ process.exit(1);
58
+ }
59
+
60
+ const outPath = resolve(process.cwd(), options.output ?? `${name}.sv-pack`);
61
+ const content = readFileSync(srcPath, 'utf-8');
62
+ const { writeFileSync } = await import('node:fs');
63
+ writeFileSync(outPath, content);
64
+
65
+ console.error(chalk.green(`βœ… Exported: ${outPath}`));
66
+ }
67
+
68
+ export async function packImportCommand(filePath: string) {
69
+ const absPath = resolve(process.cwd(), filePath);
70
+ if (!existsSync(absPath)) {
71
+ console.error(chalk.red(`❌ File not found: ${absPath}`));
72
+ process.exit(1);
73
+ }
74
+
75
+ const config = loadConfig();
76
+ const hub = createKnowledgeHub(config);
77
+ await hub.store.initialize();
78
+ await hub.embedder.initialize();
79
+
80
+ console.error(chalk.dim('⏳ Importing pack...'));
81
+ const result = await importPack(hub.store, hub.embedder, absPath);
82
+
83
+ console.error(chalk.green(`βœ… Imported: ${result.imported} chunks`));
84
+ if (result.skipped > 0) console.error(chalk.yellow(` ⏭️ Skipped: ${result.skipped}`));
85
+ if (result.modelMismatch) {
86
+ console.error(chalk.yellow(` ⚠️ Model mismatch β€” ${result.reEmbedded} chunks re-embedded`));
87
+ }
88
+
89
+ await hub.store.close();
90
+ }
91
+
92
+ export async function packListCommand() {
93
+ mkdirSync(PACKS_DIR, { recursive: true });
94
+ const files = readdirSync(PACKS_DIR).filter(f => f.endsWith('.sv-pack'));
95
+
96
+ if (files.length === 0) {
97
+ console.error(chalk.dim('No packs found. Create one: stellavault pack create <name> --from-search <query>'));
98
+ return;
99
+ }
100
+
101
+ console.error(chalk.green(`πŸ“¦ ${files.length} packs in ${PACKS_DIR}\n`));
102
+ for (const file of files) {
103
+ try {
104
+ const pack = JSON.parse(readFileSync(join(PACKS_DIR, file), 'utf-8'));
105
+ console.error(` ${chalk.bold(pack.name)} v${pack.version} β€” ${pack.chunks.length} chunks (${pack.license})`);
106
+ } catch {
107
+ console.error(` ${file} (invalid)`);
108
+ }
109
+ }
110
+ }
111
+
112
+ export async function packInfoCommand(name: string) {
113
+ const filePath = join(PACKS_DIR, `${name}.sv-pack`);
114
+ if (!existsSync(filePath)) {
115
+ console.error(chalk.red(`❌ Pack not found: ${name}`));
116
+ process.exit(1);
117
+ }
118
+
119
+ const pack = JSON.parse(readFileSync(filePath, 'utf-8'));
120
+ console.error(packToSummary(pack));
121
+ }
@@ -0,0 +1,125 @@
1
+ // Design Ref: stellavault review β€” 일일 지식 리뷰 (FSRS 기반)
2
+ // Plan SC: "μžŠμ–΄κ°€λŠ” 지식 λ¦¬λ§ˆμΈλ“œ β†’ μ‹€μ œ μ•‘μ…˜"
3
+
4
+ import chalk from 'chalk';
5
+ import { createInterface } from 'node:readline';
6
+ import { loadConfig, createKnowledgeHub, DecayEngine } from '@stellavault/core';
7
+
8
+ export async function reviewCommand(options: { count?: string }) {
9
+ const config = loadConfig();
10
+ const hub = createKnowledgeHub(config);
11
+ const count = parseInt(options.count ?? '5', 10);
12
+
13
+ console.error(chalk.dim('⏳ Initializing...'));
14
+ await hub.store.initialize();
15
+
16
+ const db = hub.store.getDb() as any;
17
+ if (!db) {
18
+ console.error(chalk.red('❌ Cannot access database'));
19
+ process.exit(1);
20
+ }
21
+
22
+ const decayEngine = new DecayEngine(db);
23
+ const decaying = await decayEngine.getDecaying(0.6, count);
24
+
25
+ if (decaying.length === 0) {
26
+ console.log(chalk.green('\n✨ λͺ¨λ“  지식이 κ±΄κ°•ν•©λ‹ˆλ‹€! 리뷰할 λ…ΈνŠΈκ°€ μ—†μŠ΅λ‹ˆλ‹€.'));
27
+ return;
28
+ }
29
+
30
+ console.log(chalk.green(`\n🧠 였늘의 리뷰 (${decaying.length}개)`));
31
+ console.log(chalk.dim('─'.repeat(50)));
32
+
33
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
34
+ const ask = (q: string): Promise<string> => new Promise(r => rl.question(q, r));
35
+
36
+ let reviewed = 0;
37
+
38
+ for (let i = 0; i < decaying.length; i++) {
39
+ const d = decaying[i];
40
+ const elapsed = Math.round(
41
+ (Date.now() - new Date(d.lastAccess).getTime()) / 86400000
42
+ );
43
+ const rBar = 'β–ˆ'.repeat(Math.round(d.retrievability * 10))
44
+ + 'β–‘'.repeat(10 - Math.round(d.retrievability * 10));
45
+ const color = d.retrievability < 0.3 ? chalk.red : chalk.yellow;
46
+
47
+ console.log(`\n${chalk.bold(`[${i + 1}/${decaying.length}]`)} ${chalk.cyan(d.title)}`);
48
+ console.log(` ${color(rBar)} R=${d.retrievability.toFixed(2)} | ${elapsed}일 μ „`);
49
+
50
+ const answer = await ask(chalk.dim(' β†’ [y]μ—΄κΈ° [n]μŠ€ν‚΅ [s]내일 λ‹€μ‹œ [q]μ’…λ£Œ: '));
51
+
52
+ if (answer.toLowerCase() === 'q') {
53
+ console.log(chalk.dim('\n리뷰 쀑단.'));
54
+ break;
55
+ }
56
+
57
+ if (answer.toLowerCase() === 's') {
58
+ // Snooze: stabilityλ₯Ό 1일만 살짝 μ˜¬λ €μ„œ 내일 λ‹€μ‹œ λ‚˜μ˜€κ²Œ
59
+ await decayEngine.recordAccess({
60
+ documentId: d.documentId,
61
+ type: 'view',
62
+ timestamp: new Date(Date.now() - 23 * 3600000).toISOString(), // 23μ‹œκ°„ μ „μœΌλ‘œ 기둝
63
+ });
64
+ console.log(chalk.dim(' ⏰ 내일 λ‹€μ‹œ λ¦¬λ§ˆμΈλ“œ'));
65
+ continue;
66
+ }
67
+
68
+ if (answer.toLowerCase() === 'y') {
69
+ // Obsidianμ—μ„œ μ—΄κΈ°
70
+ const relFile = (d as any).filePath
71
+ ? (d as any).filePath.replace(/\\/g, '/').replace(/\.md$/, '')
72
+ : d.title;
73
+
74
+ let vault = 'Evan';
75
+ try {
76
+ const doc = db.prepare('SELECT file_path FROM documents WHERE id = ?').get(d.documentId) as any;
77
+ if (doc?.file_path) {
78
+ const fp = doc.file_path.replace(/\\/g, '/').replace(/\.md$/, '');
79
+ // vault 이름 = config.vaultPath의 λ§ˆμ§€λ§‰ 디렉토리λͺ…
80
+ vault = config.vaultPath?.replace(/\\/g, '/').replace(/\/$/, '').split('/').pop() ?? 'Evan';
81
+
82
+ const uri = `obsidian://open?vault=${encodeURIComponent(vault)}&file=${encodeURIComponent(fp)}`;
83
+ const open = await import('open');
84
+ await open.default(uri);
85
+ }
86
+ } catch { /* fallback: no open */ }
87
+
88
+ // μ ‘κ·Ό 기둝 β†’ stability μ—…λ°μ΄νŠΈ
89
+ await decayEngine.recordAccess({
90
+ documentId: d.documentId,
91
+ type: 'view',
92
+ timestamp: new Date().toISOString(),
93
+ });
94
+ reviewed++;
95
+ console.log(chalk.green(' βœ… μ—΄κΈ° + κΈ°μ–΅ 강도 μ—…λ°μ΄νŠΈ'));
96
+ } else {
97
+ console.log(chalk.dim(' ⏭️ μŠ€ν‚΅'));
98
+ }
99
+ }
100
+
101
+ rl.close();
102
+
103
+ console.log(chalk.dim('\n─'.repeat(50)));
104
+ console.log(chalk.green(`리뷰 μ™„λ£Œ! ${reviewed}/${decaying.length}개 μ—΄λžŒ`));
105
+
106
+ // streak 계산 (access_logμ—μ„œ 연속 일수)
107
+ try {
108
+ const days = db.prepare(`
109
+ SELECT DISTINCT date(accessed_at) as d FROM access_log
110
+ WHERE access_type = 'view'
111
+ ORDER BY d DESC LIMIT 30
112
+ `).all() as any[];
113
+
114
+ let streak = 0;
115
+ const today = new Date().toISOString().slice(0, 10);
116
+ for (let i = 0; i < days.length; i++) {
117
+ const expected = new Date(Date.now() - i * 86400000).toISOString().slice(0, 10);
118
+ if (days[i]?.d === expected) streak++;
119
+ else break;
120
+ }
121
+ if (streak > 1) {
122
+ console.log(chalk.yellow(`πŸ”₯ ${streak}일 연속 리뷰!`));
123
+ }
124
+ } catch { /* streak μ‹€νŒ¨ν•΄λ„ λ¬΄μ‹œ */ }
125
+ }
@@ -0,0 +1,45 @@
1
+ import chalk from 'chalk';
2
+ import { loadConfig, createSqliteVecStore, createLocalEmbedder, createSearchEngine } from '@stellavault/core';
3
+
4
+ export async function searchCommand(query: string, options: { limit?: string }, cmd: any) {
5
+ const globalOpts = cmd?.parent?.opts?.() ?? {};
6
+ const jsonMode = globalOpts.json;
7
+ const config = loadConfig();
8
+ const limit = parseInt(options.limit ?? '5', 10);
9
+
10
+ const store = createSqliteVecStore(config.dbPath);
11
+ await store.initialize();
12
+
13
+ const embedder = createLocalEmbedder(config.embedding.localModel);
14
+ await embedder.initialize();
15
+
16
+ const engine = createSearchEngine({ store, embedder, rrfK: config.search.rrfK });
17
+ const results = await engine.search({ query, limit });
18
+
19
+ await store.close();
20
+
21
+ if (jsonMode) {
22
+ console.log(JSON.stringify({
23
+ query, count: results.length,
24
+ results: results.map(r => ({
25
+ title: r.document.title, path: r.document.filePath,
26
+ score: r.score, heading: r.chunk.heading,
27
+ snippet: r.chunk.content.slice(0, 200),
28
+ })),
29
+ }, null, 2));
30
+ return;
31
+ }
32
+
33
+ if (results.length === 0) {
34
+ console.log(chalk.yellow('검색 κ²°κ³Όκ°€ μ—†μŠ΅λ‹ˆλ‹€.'));
35
+ return;
36
+ }
37
+
38
+ console.log('');
39
+ for (let i = 0; i < results.length; i++) {
40
+ const r = results[i];
41
+ console.log(` ${chalk.bold(`${i + 1}.`)} ${chalk.cyan(`[${r.score.toFixed(3)}]`)} ${chalk.white(r.document.filePath)} ${chalk.dim(`Β§${r.chunk.heading}`)}`);
42
+ console.log(` ${chalk.dim(r.chunk.content.slice(0, 120).replace(/\n/g, ' '))}...`);
43
+ }
44
+ console.log('');
45
+ }
@@ -0,0 +1,17 @@
1
+ import chalk from 'chalk';
2
+ import { loadConfig, createKnowledgeHub } from '@stellavault/core';
3
+
4
+ export async function serveCommand() {
5
+ const config = loadConfig();
6
+ const hub = createKnowledgeHub(config);
7
+
8
+ await hub.store.initialize();
9
+ await hub.embedder.initialize();
10
+
11
+ const stats = await hub.store.getStats();
12
+ console.error(chalk.green('πŸš€ MCP Server running (stdio mode)'));
13
+ console.error(`πŸ“š ${stats.documentCount} documents | ${stats.chunkCount} chunks`);
14
+ console.error(chalk.dim('πŸ’‘ Claude Code: claude mcp add stellavault -- stellavault serve'));
15
+
16
+ await hub.mcpServer.startStdio();
17
+ }
@@ -0,0 +1,37 @@
1
+ import chalk from 'chalk';
2
+ import { loadConfig, createSqliteVecStore } from '@stellavault/core';
3
+
4
+ export async function statusCommand(_opts: any, cmd: any) {
5
+ const globalOpts = cmd?.parent?.opts?.() ?? {};
6
+ const jsonMode = globalOpts.json;
7
+ const config = loadConfig();
8
+
9
+ const store = createSqliteVecStore(config.dbPath);
10
+ await store.initialize();
11
+ const stats = await store.getStats();
12
+ const topics = await store.getTopics();
13
+ await store.close();
14
+
15
+ if (jsonMode) {
16
+ console.log(JSON.stringify({ ...stats, vaultPath: config.vaultPath, dbPath: config.dbPath, topics: topics.slice(0, 20) }, null, 2));
17
+ return;
18
+ }
19
+
20
+ console.log('');
21
+ console.log(chalk.bold('πŸ“Š Stellavault Status'));
22
+ console.log('─'.repeat(40));
23
+ console.log(` πŸ“„ Documents: ${stats.documentCount}`);
24
+ console.log(` 🧩 Chunks: ${stats.chunkCount}`);
25
+ console.log(` πŸ• Last indexed: ${stats.lastIndexed ?? 'never'}`);
26
+ console.log(` πŸ’Ύ DB: ${config.dbPath}`);
27
+ console.log(` πŸ“ Vault: ${config.vaultPath || '(not set)'}`);
28
+
29
+ if (topics.length > 0) {
30
+ console.log('');
31
+ console.log(chalk.bold('🏷️ Top topics:'));
32
+ topics.slice(0, 10).forEach((t: any) => {
33
+ console.log(` #${t.topic} (${t.count})`);
34
+ });
35
+ }
36
+ console.log('');
37
+ }
@@ -0,0 +1,68 @@
1
+ // Design Ref: stellavault sync β€” Notion↔Obsidian 동기화 CLI 톡합
2
+
3
+ import chalk from 'chalk';
4
+ import { spawn } from 'node:child_process';
5
+ import { resolve } from 'node:path';
6
+ import { existsSync } from 'node:fs';
7
+
8
+ export async function syncCommand(options: { upload?: boolean; watch?: boolean }) {
9
+ const syncDir = resolve(process.cwd(), 'packages/sync');
10
+ const syncScript = resolve(syncDir, 'sync-to-obsidian.mjs');
11
+
12
+ if (!existsSync(syncScript)) {
13
+ console.error(chalk.red('❌ packages/sync/sync-to-obsidian.mjs not found'));
14
+ console.error(chalk.dim(' Run from project root: cd notion-obsidian-sync'));
15
+ process.exit(1);
16
+ }
17
+
18
+ // .env 확인
19
+ const envFile = resolve(syncDir, '.env');
20
+ if (!existsSync(envFile)) {
21
+ console.error(chalk.red('❌ packages/sync/.env not found'));
22
+ console.error(chalk.dim(' Copy .env.example β†’ .env and set NOTION_TOKEN'));
23
+ process.exit(1);
24
+ }
25
+
26
+ if (options.upload) {
27
+ // PDCA λ¬Έμ„œ β†’ Notion μ—…λ‘œλ“œ
28
+ const uploadScript = resolve(syncDir, 'upload-pdca-to-notion.mjs');
29
+ if (!existsSync(uploadScript)) {
30
+ console.error(chalk.red('❌ upload-pdca-to-notion.mjs not found'));
31
+ process.exit(1);
32
+ }
33
+ console.error(chalk.dim('πŸ“€ Uploading PDCA documents to Notion...'));
34
+ await runScript(uploadScript, syncDir);
35
+ } else {
36
+ // Notion β†’ Obsidian 동기화
37
+ console.error(chalk.dim('πŸ”„ Syncing Notion β†’ Obsidian...'));
38
+ await runScript(syncScript, syncDir);
39
+
40
+ if (options.watch) {
41
+ console.error(chalk.green('πŸ‘€ Watch mode β€” syncing every 5 minutes'));
42
+ setInterval(async () => {
43
+ console.error(chalk.dim(`πŸ”„ [${new Date().toLocaleTimeString()}] Re-syncing...`));
44
+ await runScript(syncScript, syncDir);
45
+ }, 5 * 60 * 1000);
46
+
47
+ // Keep alive
48
+ process.stdin.resume();
49
+ }
50
+ }
51
+ }
52
+
53
+ function runScript(scriptPath: string, cwd: string): Promise<void> {
54
+ return new Promise((resolve, reject) => {
55
+ const child = spawn('node', [scriptPath], {
56
+ cwd,
57
+ stdio: 'inherit',
58
+ shell: true,
59
+ });
60
+
61
+ child.on('close', (code) => {
62
+ if (code === 0) resolve();
63
+ else reject(new Error(`Script exited with code ${code}`));
64
+ });
65
+
66
+ child.on('error', reject);
67
+ });
68
+ }
@@ -0,0 +1,64 @@
1
+ // sv vault add/list/remove/search-all β€” Cross-Vault (P3)
2
+
3
+ import chalk from 'chalk';
4
+ import { addVault, removeVault, listVaults, searchAllVaults, loadConfig, createSqliteVecStore, createLocalEmbedder } from '@stellavault/core';
5
+
6
+ export async function vaultAddCommand(id: string, vaultPath: string, options: { name?: string; shared?: boolean }) {
7
+ const config = loadConfig();
8
+ const dbPath = vaultPath.replace(/\/$/, '') + '/.stellavault/index.db';
9
+ try {
10
+ const entry = addVault(id, options.name ?? id, vaultPath, dbPath, !!options.shared);
11
+ console.log(chalk.green(`\n βœ… Vault "${entry.name}" added (${entry.id})`));
12
+ console.log(chalk.dim(` Path: ${entry.path}\n DB: ${entry.dbPath}\n`));
13
+ } catch (err) {
14
+ console.log(chalk.red(`\n ❌ ${err instanceof Error ? err.message : err}\n`));
15
+ }
16
+ }
17
+
18
+ export async function vaultListCommand() {
19
+ const vaults = listVaults();
20
+ if (vaults.length === 0) {
21
+ console.log(chalk.yellow('\n No vaults registered. Use: sv vault add <id> <path>\n'));
22
+ return;
23
+ }
24
+ console.log(chalk.bold('\n Registered Vaults'));
25
+ for (const v of vaults) {
26
+ console.log(` ${chalk.cyan(v.id)} ${v.name} ${chalk.dim(`(${v.path})`)}`);
27
+ }
28
+ console.log('');
29
+ }
30
+
31
+ export async function vaultRemoveCommand(id: string) {
32
+ if (removeVault(id)) {
33
+ console.log(chalk.green(`\n βœ… Vault "${id}" removed\n`));
34
+ } else {
35
+ console.log(chalk.red(`\n ❌ Vault "${id}" not found\n`));
36
+ }
37
+ }
38
+
39
+ export async function vaultSearchAllCommand(query: string, options: { limit?: string }) {
40
+ const config = loadConfig();
41
+ const embedder = createLocalEmbedder(config.embedding.localModel);
42
+ await embedder.initialize();
43
+
44
+ console.log(chalk.dim(`\n Searching all vaults for "${query}"...`));
45
+
46
+ const results = await searchAllVaults(
47
+ query, embedder,
48
+ (dbPath) => createSqliteVecStore(dbPath),
49
+ { limit: parseInt(options.limit ?? '10', 10) },
50
+ );
51
+
52
+ if (results.length === 0) {
53
+ console.log(chalk.yellow(' No results across vaults.\n'));
54
+ return;
55
+ }
56
+
57
+ for (const r of results) {
58
+ const pct = Math.round(r.score * 100);
59
+ const color = pct >= 70 ? chalk.green : pct >= 40 ? chalk.yellow : chalk.dim;
60
+ console.log(` ${color(`${pct}%`)} ${chalk.bold(r.title)} ${chalk.dim(`[${r.vaultName}]`)}`);
61
+ console.log(` ${chalk.dim(r.snippet)}...`);
62
+ }
63
+ console.log('');
64
+ }