stellavault 0.4.4 → 0.5.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 (265) hide show
  1. package/README.md +52 -16
  2. package/SECURITY.md +50 -0
  3. package/dist/stellavault.js +8968 -0
  4. package/package.json +70 -33
  5. package/.env.example +0 -12
  6. package/CHANGELOG.md +0 -106
  7. package/CLAUDE.md +0 -42
  8. package/CONTRIBUTING.md +0 -65
  9. package/index.html +0 -509
  10. package/packages/cli/bin/ekh.js +0 -2
  11. package/packages/cli/bin/stellavault.js +0 -2
  12. package/packages/cli/dist/commands/adr-cmd.d.ts +0 -7
  13. package/packages/cli/dist/commands/adr-cmd.js +0 -45
  14. package/packages/cli/dist/commands/ask-cmd.d.ts +0 -5
  15. package/packages/cli/dist/commands/ask-cmd.js +0 -36
  16. package/packages/cli/dist/commands/autopilot-cmd.d.ts +0 -4
  17. package/packages/cli/dist/commands/autopilot-cmd.js +0 -76
  18. package/packages/cli/dist/commands/brief-cmd.d.ts +0 -2
  19. package/packages/cli/dist/commands/brief-cmd.js +0 -82
  20. package/packages/cli/dist/commands/capture-cmd.d.ts +0 -7
  21. package/packages/cli/dist/commands/capture-cmd.js +0 -31
  22. package/packages/cli/dist/commands/card-cmd.d.ts +0 -4
  23. package/packages/cli/dist/commands/card-cmd.js +0 -26
  24. package/packages/cli/dist/commands/clip-cmd.d.ts +0 -4
  25. package/packages/cli/dist/commands/clip-cmd.js +0 -151
  26. package/packages/cli/dist/commands/cloud-cmd.d.ts +0 -4
  27. package/packages/cli/dist/commands/cloud-cmd.js +0 -64
  28. package/packages/cli/dist/commands/compile-cmd.d.ts +0 -6
  29. package/packages/cli/dist/commands/compile-cmd.js +0 -30
  30. package/packages/cli/dist/commands/contradictions-cmd.d.ts +0 -2
  31. package/packages/cli/dist/commands/contradictions-cmd.js +0 -34
  32. package/packages/cli/dist/commands/decay-cmd.d.ts +0 -2
  33. package/packages/cli/dist/commands/decay-cmd.js +0 -48
  34. package/packages/cli/dist/commands/digest-cmd.d.ts +0 -5
  35. package/packages/cli/dist/commands/digest-cmd.js +0 -136
  36. package/packages/cli/dist/commands/draft-cmd.d.ts +0 -6
  37. package/packages/cli/dist/commands/draft-cmd.js +0 -110
  38. package/packages/cli/dist/commands/duplicates-cmd.d.ts +0 -4
  39. package/packages/cli/dist/commands/duplicates-cmd.js +0 -30
  40. package/packages/cli/dist/commands/federate-cmd.d.ts +0 -5
  41. package/packages/cli/dist/commands/federate-cmd.js +0 -247
  42. package/packages/cli/dist/commands/fleeting-cmd.d.ts +0 -4
  43. package/packages/cli/dist/commands/fleeting-cmd.js +0 -45
  44. package/packages/cli/dist/commands/flush-cmd.d.ts +0 -2
  45. package/packages/cli/dist/commands/flush-cmd.js +0 -58
  46. package/packages/cli/dist/commands/gaps-cmd.d.ts +0 -2
  47. package/packages/cli/dist/commands/gaps-cmd.js +0 -33
  48. package/packages/cli/dist/commands/graph-cmd.d.ts +0 -2
  49. package/packages/cli/dist/commands/graph-cmd.js +0 -89
  50. package/packages/cli/dist/commands/index-cmd.d.ts +0 -2
  51. package/packages/cli/dist/commands/index-cmd.js +0 -57
  52. package/packages/cli/dist/commands/ingest-cmd.d.ts +0 -9
  53. package/packages/cli/dist/commands/ingest-cmd.js +0 -195
  54. package/packages/cli/dist/commands/init-cmd.d.ts +0 -2
  55. package/packages/cli/dist/commands/init-cmd.js +0 -161
  56. package/packages/cli/dist/commands/learn-cmd.d.ts +0 -2
  57. package/packages/cli/dist/commands/learn-cmd.js +0 -48
  58. package/packages/cli/dist/commands/lint-cmd.d.ts +0 -2
  59. package/packages/cli/dist/commands/lint-cmd.js +0 -61
  60. package/packages/cli/dist/commands/pack-cmd.d.ts +0 -15
  61. package/packages/cli/dist/commands/pack-cmd.js +0 -93
  62. package/packages/cli/dist/commands/review-cmd.d.ts +0 -4
  63. package/packages/cli/dist/commands/review-cmd.js +0 -107
  64. package/packages/cli/dist/commands/search-cmd.d.ts +0 -4
  65. package/packages/cli/dist/commands/search-cmd.js +0 -38
  66. package/packages/cli/dist/commands/serve-cmd.d.ts +0 -2
  67. package/packages/cli/dist/commands/serve-cmd.js +0 -14
  68. package/packages/cli/dist/commands/session-cmd.d.ts +0 -7
  69. package/packages/cli/dist/commands/session-cmd.js +0 -95
  70. package/packages/cli/dist/commands/status-cmd.d.ts +0 -2
  71. package/packages/cli/dist/commands/status-cmd.js +0 -33
  72. package/packages/cli/dist/commands/sync-cmd.d.ts +0 -5
  73. package/packages/cli/dist/commands/sync-cmd.js +0 -62
  74. package/packages/cli/dist/commands/vault-cmd.d.ts +0 -10
  75. package/packages/cli/dist/commands/vault-cmd.js +0 -54
  76. package/packages/cli/dist/index.d.ts +0 -2
  77. package/packages/cli/dist/index.js +0 -233
  78. package/packages/cli/package.json +0 -24
  79. package/packages/core/dist/api/dashboard.d.ts +0 -3
  80. package/packages/core/dist/api/dashboard.js +0 -135
  81. package/packages/core/dist/api/graph-data.d.ts +0 -11
  82. package/packages/core/dist/api/graph-data.js +0 -255
  83. package/packages/core/dist/api/pwa.d.ts +0 -3
  84. package/packages/core/dist/api/pwa.js +0 -77
  85. package/packages/core/dist/api/server.d.ts +0 -16
  86. package/packages/core/dist/api/server.js +0 -1122
  87. package/packages/core/dist/capture/voice.d.ts +0 -24
  88. package/packages/core/dist/capture/voice.js +0 -135
  89. package/packages/core/dist/cloud/index.d.ts +0 -3
  90. package/packages/core/dist/cloud/index.js +0 -2
  91. package/packages/core/dist/cloud/sync.d.ts +0 -29
  92. package/packages/core/dist/cloud/sync.js +0 -137
  93. package/packages/core/dist/config.d.ts +0 -35
  94. package/packages/core/dist/config.js +0 -63
  95. package/packages/core/dist/federation/credits.d.ts +0 -26
  96. package/packages/core/dist/federation/credits.js +0 -56
  97. package/packages/core/dist/federation/identity.d.ts +0 -14
  98. package/packages/core/dist/federation/identity.js +0 -74
  99. package/packages/core/dist/federation/index.d.ts +0 -7
  100. package/packages/core/dist/federation/index.js +0 -5
  101. package/packages/core/dist/federation/node.d.ts +0 -31
  102. package/packages/core/dist/federation/node.js +0 -216
  103. package/packages/core/dist/federation/privacy.d.ts +0 -8
  104. package/packages/core/dist/federation/privacy.js +0 -40
  105. package/packages/core/dist/federation/reputation.d.ts +0 -37
  106. package/packages/core/dist/federation/reputation.js +0 -139
  107. package/packages/core/dist/federation/search.d.ts +0 -19
  108. package/packages/core/dist/federation/search.js +0 -101
  109. package/packages/core/dist/federation/sharing.d.ts +0 -72
  110. package/packages/core/dist/federation/sharing.js +0 -246
  111. package/packages/core/dist/federation/trust.d.ts +0 -15
  112. package/packages/core/dist/federation/trust.js +0 -60
  113. package/packages/core/dist/federation/types.d.ts +0 -40
  114. package/packages/core/dist/federation/types.js +0 -3
  115. package/packages/core/dist/i18n/index.d.ts +0 -6
  116. package/packages/core/dist/i18n/index.js +0 -81
  117. package/packages/core/dist/i18n/note-strings.d.ts +0 -5
  118. package/packages/core/dist/i18n/note-strings.js +0 -94
  119. package/packages/core/dist/index.d.ts +0 -123
  120. package/packages/core/dist/index.js +0 -74
  121. package/packages/core/dist/indexer/chunker.d.ts +0 -14
  122. package/packages/core/dist/indexer/chunker.js +0 -148
  123. package/packages/core/dist/indexer/embedder.d.ts +0 -8
  124. package/packages/core/dist/indexer/embedder.js +0 -3
  125. package/packages/core/dist/indexer/index.d.ts +0 -28
  126. package/packages/core/dist/indexer/index.js +0 -74
  127. package/packages/core/dist/indexer/local-embedder.d.ts +0 -3
  128. package/packages/core/dist/indexer/local-embedder.js +0 -29
  129. package/packages/core/dist/indexer/scanner.d.ts +0 -11
  130. package/packages/core/dist/indexer/scanner.js +0 -148
  131. package/packages/core/dist/indexer/watcher.d.ts +0 -19
  132. package/packages/core/dist/indexer/watcher.js +0 -49
  133. package/packages/core/dist/intelligence/ask-engine.d.ts +0 -24
  134. package/packages/core/dist/intelligence/ask-engine.js +0 -142
  135. package/packages/core/dist/intelligence/auto-linker.d.ts +0 -19
  136. package/packages/core/dist/intelligence/auto-linker.js +0 -126
  137. package/packages/core/dist/intelligence/code-linker.d.ts +0 -20
  138. package/packages/core/dist/intelligence/code-linker.js +0 -88
  139. package/packages/core/dist/intelligence/contradiction-detector.d.ts +0 -20
  140. package/packages/core/dist/intelligence/contradiction-detector.js +0 -115
  141. package/packages/core/dist/intelligence/decay-engine.d.ts +0 -27
  142. package/packages/core/dist/intelligence/decay-engine.js +0 -190
  143. package/packages/core/dist/intelligence/draft-generator.d.ts +0 -25
  144. package/packages/core/dist/intelligence/draft-generator.js +0 -277
  145. package/packages/core/dist/intelligence/duplicate-detector.d.ts +0 -20
  146. package/packages/core/dist/intelligence/duplicate-detector.js +0 -55
  147. package/packages/core/dist/intelligence/file-extractors.d.ts +0 -13
  148. package/packages/core/dist/intelligence/file-extractors.js +0 -155
  149. package/packages/core/dist/intelligence/fsrs.d.ts +0 -43
  150. package/packages/core/dist/intelligence/fsrs.js +0 -70
  151. package/packages/core/dist/intelligence/gap-detector.d.ts +0 -25
  152. package/packages/core/dist/intelligence/gap-detector.js +0 -78
  153. package/packages/core/dist/intelligence/ingest-pipeline.d.ts +0 -32
  154. package/packages/core/dist/intelligence/ingest-pipeline.js +0 -245
  155. package/packages/core/dist/intelligence/knowledge-lint.d.ts +0 -27
  156. package/packages/core/dist/intelligence/knowledge-lint.js +0 -132
  157. package/packages/core/dist/intelligence/learning-path.d.ts +0 -31
  158. package/packages/core/dist/intelligence/learning-path.js +0 -53
  159. package/packages/core/dist/intelligence/notifications.d.ts +0 -31
  160. package/packages/core/dist/intelligence/notifications.js +0 -65
  161. package/packages/core/dist/intelligence/predictive-gaps.d.ts +0 -14
  162. package/packages/core/dist/intelligence/predictive-gaps.js +0 -74
  163. package/packages/core/dist/intelligence/semantic-versioning.d.ts +0 -37
  164. package/packages/core/dist/intelligence/semantic-versioning.js +0 -68
  165. package/packages/core/dist/intelligence/types.d.ts +0 -28
  166. package/packages/core/dist/intelligence/types.js +0 -3
  167. package/packages/core/dist/intelligence/wiki-compiler.d.ts +0 -30
  168. package/packages/core/dist/intelligence/wiki-compiler.js +0 -222
  169. package/packages/core/dist/intelligence/youtube-extractor.d.ts +0 -29
  170. package/packages/core/dist/intelligence/youtube-extractor.js +0 -321
  171. package/packages/core/dist/intelligence/zettelkasten.d.ts +0 -59
  172. package/packages/core/dist/intelligence/zettelkasten.js +0 -234
  173. package/packages/core/dist/mcp/custom-tools.d.ts +0 -29
  174. package/packages/core/dist/mcp/custom-tools.js +0 -70
  175. package/packages/core/dist/mcp/index.d.ts +0 -2
  176. package/packages/core/dist/mcp/index.js +0 -2
  177. package/packages/core/dist/mcp/server.d.ts +0 -51
  178. package/packages/core/dist/mcp/server.js +0 -174
  179. package/packages/core/dist/mcp/tools/agentic-graph.d.ts +0 -93
  180. package/packages/core/dist/mcp/tools/agentic-graph.js +0 -116
  181. package/packages/core/dist/mcp/tools/ask.d.ts +0 -29
  182. package/packages/core/dist/mcp/tools/ask.js +0 -43
  183. package/packages/core/dist/mcp/tools/brief.d.ts +0 -31
  184. package/packages/core/dist/mcp/tools/brief.js +0 -39
  185. package/packages/core/dist/mcp/tools/decay.d.ts +0 -33
  186. package/packages/core/dist/mcp/tools/decay.js +0 -32
  187. package/packages/core/dist/mcp/tools/decision-journal.d.ts +0 -78
  188. package/packages/core/dist/mcp/tools/decision-journal.js +0 -79
  189. package/packages/core/dist/mcp/tools/detect-gaps.d.ts +0 -24
  190. package/packages/core/dist/mcp/tools/detect-gaps.js +0 -47
  191. package/packages/core/dist/mcp/tools/export.d.ts +0 -29
  192. package/packages/core/dist/mcp/tools/export.js +0 -60
  193. package/packages/core/dist/mcp/tools/federated-search.d.ts +0 -29
  194. package/packages/core/dist/mcp/tools/federated-search.js +0 -36
  195. package/packages/core/dist/mcp/tools/generate-claude-md.d.ts +0 -35
  196. package/packages/core/dist/mcp/tools/generate-claude-md.js +0 -107
  197. package/packages/core/dist/mcp/tools/generate-draft.d.ts +0 -34
  198. package/packages/core/dist/mcp/tools/generate-draft.js +0 -120
  199. package/packages/core/dist/mcp/tools/get-document.d.ts +0 -35
  200. package/packages/core/dist/mcp/tools/get-document.js +0 -25
  201. package/packages/core/dist/mcp/tools/get-evolution.d.ts +0 -28
  202. package/packages/core/dist/mcp/tools/get-evolution.js +0 -70
  203. package/packages/core/dist/mcp/tools/get-related.d.ts +0 -32
  204. package/packages/core/dist/mcp/tools/get-related.js +0 -33
  205. package/packages/core/dist/mcp/tools/learning-path.d.ts +0 -23
  206. package/packages/core/dist/mcp/tools/learning-path.js +0 -45
  207. package/packages/core/dist/mcp/tools/link-code.d.ts +0 -34
  208. package/packages/core/dist/mcp/tools/link-code.js +0 -44
  209. package/packages/core/dist/mcp/tools/list-topics.d.ts +0 -15
  210. package/packages/core/dist/mcp/tools/list-topics.js +0 -18
  211. package/packages/core/dist/mcp/tools/search.d.ts +0 -39
  212. package/packages/core/dist/mcp/tools/search.js +0 -29
  213. package/packages/core/dist/mcp/tools/snapshot.d.ts +0 -47
  214. package/packages/core/dist/mcp/tools/snapshot.js +0 -84
  215. package/packages/core/dist/multi-vault/index.d.ts +0 -26
  216. package/packages/core/dist/multi-vault/index.js +0 -80
  217. package/packages/core/dist/pack/creator.d.ts +0 -21
  218. package/packages/core/dist/pack/creator.js +0 -105
  219. package/packages/core/dist/pack/exporter.d.ts +0 -4
  220. package/packages/core/dist/pack/exporter.js +0 -18
  221. package/packages/core/dist/pack/importer.d.ts +0 -10
  222. package/packages/core/dist/pack/importer.js +0 -55
  223. package/packages/core/dist/pack/index.d.ts +0 -6
  224. package/packages/core/dist/pack/index.js +0 -5
  225. package/packages/core/dist/pack/marketplace.d.ts +0 -14
  226. package/packages/core/dist/pack/marketplace.js +0 -90
  227. package/packages/core/dist/pack/pii-masker.d.ts +0 -7
  228. package/packages/core/dist/pack/pii-masker.js +0 -29
  229. package/packages/core/dist/pack/types.d.ts +0 -36
  230. package/packages/core/dist/pack/types.js +0 -3
  231. package/packages/core/dist/plugins/index.d.ts +0 -35
  232. package/packages/core/dist/plugins/index.js +0 -57
  233. package/packages/core/dist/plugins/webhooks.d.ts +0 -30
  234. package/packages/core/dist/plugins/webhooks.js +0 -79
  235. package/packages/core/dist/search/adaptive.d.ts +0 -16
  236. package/packages/core/dist/search/adaptive.js +0 -67
  237. package/packages/core/dist/search/bm25.d.ts +0 -4
  238. package/packages/core/dist/search/bm25.js +0 -10
  239. package/packages/core/dist/search/index.d.ts +0 -15
  240. package/packages/core/dist/search/index.js +0 -64
  241. package/packages/core/dist/search/rrf.d.ts +0 -7
  242. package/packages/core/dist/search/rrf.js +0 -21
  243. package/packages/core/dist/search/semantic.d.ts +0 -5
  244. package/packages/core/dist/search/semantic.js +0 -6
  245. package/packages/core/dist/store/index.d.ts +0 -3
  246. package/packages/core/dist/store/index.js +0 -2
  247. package/packages/core/dist/store/sqlite-vec.d.ts +0 -6
  248. package/packages/core/dist/store/sqlite-vec.js +0 -251
  249. package/packages/core/dist/store/types.d.ts +0 -20
  250. package/packages/core/dist/store/types.js +0 -3
  251. package/packages/core/dist/team/index.d.ts +0 -25
  252. package/packages/core/dist/team/index.js +0 -97
  253. package/packages/core/dist/types/chunk.d.ts +0 -23
  254. package/packages/core/dist/types/chunk.js +0 -3
  255. package/packages/core/dist/types/document.d.ts +0 -23
  256. package/packages/core/dist/types/document.js +0 -3
  257. package/packages/core/dist/types/graph.d.ts +0 -39
  258. package/packages/core/dist/types/graph.js +0 -3
  259. package/packages/core/dist/types/index.d.ts +0 -5
  260. package/packages/core/dist/types/index.js +0 -2
  261. package/packages/core/dist/types/search.d.ts +0 -39
  262. package/packages/core/dist/types/search.js +0 -3
  263. package/packages/core/dist/utils/retry.d.ts +0 -25
  264. package/packages/core/dist/utils/retry.js +0 -59
  265. package/packages/core/package.json +0 -67
@@ -1,148 +0,0 @@
1
- // Design Ref: §6 — Indexer (scanner: glob + frontmatter 파싱)
2
- import { readdirSync, readFileSync, statSync } from 'node:fs';
3
- import { join, relative, extname } from 'node:path';
4
- import { createHash } from 'node:crypto';
5
- import matter from 'gray-matter';
6
- /**
7
- * vault 디렉토리에서 모든 .md 파일을 스캔하여 Document 목록 반환
8
- */
9
- export function scanVault(vaultPath) {
10
- const documents = [];
11
- let skippedFiles = 0;
12
- const mdFiles = findMdFiles(vaultPath);
13
- for (const filePath of mdFiles) {
14
- try {
15
- const doc = parseDocument(vaultPath, filePath);
16
- documents.push(doc);
17
- }
18
- catch {
19
- skippedFiles++;
20
- }
21
- }
22
- return { documents, scannedFiles: mdFiles.length, skippedFiles };
23
- }
24
- function findMdFiles(dir, files = []) {
25
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
26
- const fullPath = join(dir, entry.name);
27
- if (entry.isDirectory()) {
28
- if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'zh-CN')
29
- continue;
30
- findMdFiles(fullPath, files);
31
- }
32
- else if (extname(entry.name) === '.md') {
33
- files.push(fullPath);
34
- }
35
- }
36
- return files;
37
- }
38
- function parseDocument(vaultPath, filePath) {
39
- const raw = readFileSync(filePath, 'utf-8');
40
- const stat = statSync(filePath);
41
- const { data: frontmatter, content } = matter(raw);
42
- const relativePath = relative(vaultPath, filePath).replace(/\\/g, '/');
43
- const id = createHash('sha256').update(relativePath).digest('hex').slice(0, 16);
44
- const contentHash = createHash('sha256').update(raw).digest('hex').slice(0, 16);
45
- const title = frontmatter.title
46
- ?? extractFirstHeading(content)
47
- ?? relativePath.replace(/\.md$/, '');
48
- const tags = extractTags(frontmatter, content);
49
- // Obsidian frontmatter 호환: aliases를 tags에 추가
50
- if (frontmatter.aliases) {
51
- const aliases = Array.isArray(frontmatter.aliases) ? frontmatter.aliases : [frontmatter.aliases];
52
- for (const alias of aliases) {
53
- if (typeof alias === 'string' && alias.length > 1)
54
- tags.push(alias);
55
- }
56
- }
57
- // source/type 자동 추출 (원본 파일 수정 없이 DB에만 저장)
58
- const source = inferSource(frontmatter, relativePath);
59
- const type = inferType(frontmatter, relativePath);
60
- // Obsidian frontmatter 호환: date/created 우선 사용
61
- const fmDate = frontmatter.date ?? frontmatter.created ?? frontmatter.created_at;
62
- const lastModified = fmDate ? new Date(fmDate).toISOString() : stat.mtime.toISOString();
63
- return {
64
- id,
65
- filePath: relativePath,
66
- title,
67
- content,
68
- frontmatter,
69
- tags,
70
- lastModified,
71
- contentHash,
72
- source,
73
- type,
74
- };
75
- }
76
- function inferSource(frontmatter, filePath) {
77
- // frontmatter에 명시된 경우
78
- if (frontmatter.source && typeof frontmatter.source === 'string') {
79
- if (frontmatter.source.startsWith('http'))
80
- return 'clip';
81
- return frontmatter.source;
82
- }
83
- // 경로 기반 추론
84
- if (filePath.includes('clips/') || filePath.includes('clip/'))
85
- return 'clip';
86
- if (filePath.includes('PDCA') || filePath.includes('pdca'))
87
- return 'local';
88
- if (frontmatter['x-i18n'])
89
- return 'notion'; // Notion 번역 문서
90
- if (frontmatter.clipped)
91
- return 'clip';
92
- return 'local';
93
- }
94
- function inferType(frontmatter, filePath) {
95
- // frontmatter에 명시된 경우
96
- if (frontmatter.type && typeof frontmatter.type === 'string')
97
- return frontmatter.type;
98
- // tags 기반
99
- const tags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [];
100
- if (tags.includes('bridge') || tags.includes('auto-generated'))
101
- return 'bridge';
102
- if (tags.includes('clip') || tags.includes('youtube'))
103
- return 'clip';
104
- if (tags.includes('decision'))
105
- return 'decision';
106
- // 경로 기반
107
- if (filePath.includes('clips/'))
108
- return 'clip';
109
- if (filePath.includes('Decisions/') || filePath.includes('decisions/'))
110
- return 'decision';
111
- if (filePath.includes('Sessions/') || filePath.includes('sessions/'))
112
- return 'session';
113
- if (filePath.includes('Research/'))
114
- return 'research';
115
- if (filePath.includes('Lessons/'))
116
- return 'lesson';
117
- if (filePath.includes('Templates/'))
118
- return 'template';
119
- return 'note';
120
- }
121
- function extractFirstHeading(content) {
122
- const match = content.match(/^#\s+(.+)$/m);
123
- return match ? match[1].trim() : null;
124
- }
125
- function extractTags(frontmatter, content) {
126
- const tags = new Set();
127
- // frontmatter tags
128
- const fmTags = frontmatter.tags;
129
- if (Array.isArray(fmTags)) {
130
- fmTags.forEach(t => tags.add(String(t)));
131
- }
132
- else if (typeof fmTags === 'string') {
133
- fmTags.split(',').map(t => t.trim()).filter(Boolean).forEach(t => tags.add(t));
134
- }
135
- // inline #tags (CSS 컬러코드, 순수 숫자, heading # 제외)
136
- const inlineTags = content.match(/(?:^|\s)#([a-zA-Z가-힣][a-zA-Z가-힣\w-]*)/g);
137
- if (inlineTags) {
138
- for (const raw of inlineTags) {
139
- const tag = raw.trim().slice(1);
140
- // CSS hex 컬러 (#fff, #6c5ce7 등) 제외
141
- if (/^[0-9a-fA-F]{3,8}$/.test(tag))
142
- continue;
143
- tags.add(tag);
144
- }
145
- }
146
- return [...tags];
147
- }
148
- //# sourceMappingURL=scanner.js.map
@@ -1,19 +0,0 @@
1
- import type { Embedder } from './embedder.js';
2
- import type { VectorStore } from '../store/types.js';
3
- import type { ChunkOptions } from './chunker.js';
4
- export interface WatcherOptions {
5
- vaultPath: string;
6
- store: VectorStore;
7
- embedder: Embedder;
8
- chunkOptions?: Partial<ChunkOptions>;
9
- debounceMs?: number;
10
- onReindex?: (result: {
11
- indexed: number;
12
- skipped: number;
13
- }) => void;
14
- }
15
- export declare function createWatcher(options: WatcherOptions): {
16
- start(): void;
17
- stop(): void;
18
- };
19
- //# sourceMappingURL=watcher.d.ts.map
@@ -1,49 +0,0 @@
1
- // Design Ref: §6.3 — 파일 감시 + 증분 인덱싱 (debounce 5s)
2
- import { watch } from 'chokidar';
3
- import { extname } from 'node:path';
4
- import { indexVault } from './index.js';
5
- export function createWatcher(options) {
6
- const { vaultPath, store, embedder, chunkOptions, debounceMs = 5000, onReindex } = options;
7
- let watcher = null;
8
- let debounceTimer = null;
9
- let reindexing = false;
10
- async function triggerReindex() {
11
- if (reindexing)
12
- return;
13
- reindexing = true;
14
- try {
15
- const result = await indexVault(vaultPath, { store, embedder, chunkOptions });
16
- onReindex?.({ indexed: result.indexed, skipped: result.skipped });
17
- }
18
- finally {
19
- reindexing = false;
20
- }
21
- }
22
- function scheduleReindex() {
23
- if (debounceTimer)
24
- clearTimeout(debounceTimer);
25
- debounceTimer = setTimeout(() => triggerReindex(), debounceMs);
26
- }
27
- return {
28
- start() {
29
- watcher = watch(vaultPath, {
30
- ignored: /(^|[\/\\])\.|node_modules/,
31
- persistent: true,
32
- ignoreInitial: true,
33
- });
34
- watcher.on('all', (event, path) => {
35
- if (extname(path) !== '.md')
36
- return;
37
- if (['add', 'change', 'unlink'].includes(event)) {
38
- scheduleReindex();
39
- }
40
- });
41
- },
42
- stop() {
43
- if (debounceTimer)
44
- clearTimeout(debounceTimer);
45
- watcher?.close();
46
- },
47
- };
48
- }
49
- //# sourceMappingURL=watcher.js.map
@@ -1,24 +0,0 @@
1
- import type { SearchEngine } from '../search/index.js';
2
- export interface AskResult {
3
- question: string;
4
- answer: string;
5
- sources: Array<{
6
- title: string;
7
- filePath: string;
8
- score: number;
9
- snippet: string;
10
- }>;
11
- savedTo: string | null;
12
- }
13
- /**
14
- * 질문에 대해 vault를 검색하고 구조화된 답변을 생성.
15
- * LLM 없이 검색 결과를 구조화하는 버전 (LLM 연동은 MCP ask tool에서 처리).
16
- */
17
- export declare function askVault(searchEngine: SearchEngine, question: string, options?: {
18
- limit?: number;
19
- save?: boolean;
20
- vaultPath?: string;
21
- outputDir?: string;
22
- mode?: 'default' | 'quotes';
23
- }): Promise<AskResult>;
24
- //# sourceMappingURL=ask-engine.d.ts.map
@@ -1,142 +0,0 @@
1
- // Feature: stellavault ask — Q&A + auto-filing
2
- // vault 대상 자연어 질문 → 검색 → 답변 구조화 → vault에 .md 저장
3
- import { writeFileSync, mkdirSync, existsSync } from 'node:fs';
4
- import { join, resolve } from 'node:path';
5
- /**
6
- * 질문에 대해 vault를 검색하고 구조화된 답변을 생성.
7
- * LLM 없이 검색 결과를 구조화하는 버전 (LLM 연동은 MCP ask tool에서 처리).
8
- */
9
- export async function askVault(searchEngine, question, options = {}) {
10
- const { limit = 10, save = false, vaultPath, outputDir = '_stellavault/answers', mode = 'default' } = options;
11
- // 1. 검색
12
- const results = await searchEngine.search({ query: question, limit });
13
- // 2. 소스 정리
14
- const sources = results.map((r) => ({
15
- title: r.document.title,
16
- filePath: r.document.filePath,
17
- score: Math.round(r.score * 1000) / 1000,
18
- snippet: r.chunk?.content?.substring(0, 200) ?? '',
19
- }));
20
- // 3. 답변 구성 (검색 결과 기반 구조화)
21
- const answer = mode === 'quotes'
22
- ? composeQuotes(question, results)
23
- : composeAnswer(question, results);
24
- // 4. vault에 저장 (선택)
25
- let savedTo = null;
26
- if (save && vaultPath) {
27
- savedTo = saveAnswerToVault(question, answer, sources, vaultPath, outputDir);
28
- }
29
- return { question, answer, sources, savedTo };
30
- }
31
- /**
32
- * 검색 결과를 구조화된 답변으로 구성.
33
- * LLM 없이도 유용한 요약을 만듦.
34
- */
35
- function composeAnswer(question, results) {
36
- if (results.length === 0) {
37
- return `No results found for "${question}". Try different keywords or create a note on this topic.`;
38
- }
39
- const lines = [];
40
- lines.push(`## ${question}\n`);
41
- // Related documents
42
- lines.push(`### Related Documents (${results.length})\n`);
43
- for (const r of results.slice(0, 5)) {
44
- const score = Math.round(r.score * 100);
45
- lines.push(`- **${r.document.title}** (${score}% 관련)`);
46
- if (r.chunk?.content) {
47
- const snippet = r.chunk.content.substring(0, 150).replace(/\n/g, ' ').trim();
48
- lines.push(` > ${snippet}...`);
49
- }
50
- if (r.document.tags.length > 0) {
51
- lines.push(` Tags: ${r.document.tags.map(t => `#${t}`).join(' ')}`);
52
- }
53
- lines.push('');
54
- }
55
- // Related tags
56
- const allTags = new Set();
57
- for (const r of results) {
58
- r.document.tags.forEach((t) => allTags.add(t));
59
- }
60
- if (allTags.size > 0) {
61
- lines.push(`### Related Tags`);
62
- lines.push([...allTags].map(t => `#${t}`).join(' '));
63
- lines.push('');
64
- }
65
- // Explore further
66
- lines.push(`### Explore Further`);
67
- lines.push(`- Dig deeper: \`stellavault ask "${question} advanced"\``);
68
- lines.push(`- Find knowledge gaps: \`stellavault gaps\``);
69
- return lines.join('\n');
70
- }
71
- /**
72
- * Quotes 모드: 원문 인용을 카드 형태로 나열.
73
- */
74
- function composeQuotes(question, results) {
75
- if (results.length === 0) {
76
- return `No quotes found for "${question}".`;
77
- }
78
- const lines = [];
79
- lines.push(`## Quotes: "${question}"\n`);
80
- lines.push(`*${results.length} sources found. Each quote is a direct excerpt.*\n`);
81
- for (let i = 0; i < Math.min(8, results.length); i++) {
82
- const r = results[i];
83
- const quote = r.chunk?.content
84
- ?.replace(/^---[\s\S]*?---\n?/, '')
85
- ?.replace(/^#+\s+.+\n/m, '')
86
- ?.trim()
87
- ?.substring(0, 300) ?? '';
88
- if (!quote)
89
- continue;
90
- const score = Math.round(r.score * 100);
91
- lines.push(`---`);
92
- lines.push(`**[${i + 1}] ${r.document.title}** (${score}% match)`);
93
- lines.push(`> ${quote.replace(/\n/g, '\n> ')}`);
94
- if (r.document.tags.length > 0) {
95
- lines.push(`Tags: ${r.document.tags.slice(0, 5).map(t => `#${t}`).join(' ')}`);
96
- }
97
- lines.push('');
98
- }
99
- lines.push('---');
100
- lines.push(`*Use \`stellavault ask "${question}" --save\` to save these quotes as a note.*`);
101
- return lines.join('\n');
102
- }
103
- /**
104
- * 답변을 vault에 .md 파일로 저장.
105
- * "_stellavault/answers/YYYY-MM-DD-question.md" 형식.
106
- */
107
- function saveAnswerToVault(question, answer, sources, vaultPath, outputDir) {
108
- const dir = resolve(vaultPath, outputDir);
109
- if (!existsSync(dir)) {
110
- mkdirSync(dir, { recursive: true });
111
- }
112
- const date = new Date().toISOString().split('T')[0];
113
- const slug = question
114
- .toLowerCase()
115
- .replace(/[^a-z0-9가-힣\s]/g, '')
116
- .replace(/\s+/g, '-')
117
- .substring(0, 60);
118
- const filename = `${date}-${slug}.md`;
119
- const filePath = join(outputDir, filename);
120
- const fullPath = resolve(vaultPath, filePath);
121
- // vault 경로 내인지 확인 (path traversal 방지)
122
- if (!fullPath.startsWith(resolve(vaultPath))) {
123
- throw new Error('Invalid output path');
124
- }
125
- const content = [
126
- '---',
127
- `title: "${question}"`,
128
- `date: ${new Date().toISOString()}`,
129
- 'type: answer',
130
- 'source: stellavault-ask',
131
- `tags: [${sources.slice(0, 5).flatMap(s => s.title.split(/\s+/).slice(0, 2)).map(t => `"${t}"`).join(', ')}]`,
132
- '---',
133
- '',
134
- answer,
135
- '',
136
- '---',
137
- '*Generated by `stellavault ask`. Sources from your vault.*',
138
- ].join('\n');
139
- writeFileSync(fullPath, content, 'utf-8');
140
- return filePath;
141
- }
142
- //# sourceMappingURL=ask-engine.js.map
@@ -1,19 +0,0 @@
1
- import { type FolderNames } from '../config.js';
2
- /**
3
- * vault의 모든 .md 노트 제목을 수집.
4
- * frontmatter title 또는 첫 heading 또는 파일명.
5
- */
6
- export declare function collectVaultTitles(vaultPath: string, folders?: FolderNames): string[];
7
- /**
8
- * 노트 본문에서 기존 노트 제목/핵심 구문과 매칭하여 [[wikilink]]를 삽입.
9
- * - 이미 [[...]]로 감싸진 부분은 건드리지 않음
10
- * - frontmatter 영역은 건드리지 않음
11
- * - 자기 자신의 제목은 건드리지 않음
12
- * - heading(#) 줄은 건드리지 않음
13
- */
14
- export declare function insertWikilinks(body: string, vaultTitles: string[], selfTitle?: string): string;
15
- /**
16
- * vault 스캔 + wikilink 삽입을 한 번에 수행하는 편의 함수.
17
- */
18
- export declare function autoLink(body: string, vaultPath: string, selfTitle?: string, folders?: FolderNames): string;
19
- //# sourceMappingURL=auto-linker.d.ts.map
@@ -1,126 +0,0 @@
1
- // Auto-Linker: 노트 본문에서 기존 노트 제목과 매칭하여 [[wikilink]] 자동 삽입
2
- // 제텔카스텐 핵심: 노트 간 연결이 지식 네트워크를 만든다
3
- import { readdirSync, readFileSync, existsSync } from 'node:fs';
4
- import { join, resolve, extname } from 'node:path';
5
- import { DEFAULT_FOLDERS } from '../config.js';
6
- /**
7
- * vault의 모든 .md 노트 제목을 수집.
8
- * frontmatter title 또는 첫 heading 또는 파일명.
9
- */
10
- export function collectVaultTitles(vaultPath, folders = DEFAULT_FOLDERS) {
11
- const titles = new Set();
12
- const dirs = [folders.fleeting, folders.literature, folders.permanent, folders.wiki, '_drafts'];
13
- for (const dir of dirs) {
14
- const fullDir = resolve(vaultPath, dir);
15
- if (!existsSync(fullDir))
16
- continue;
17
- try {
18
- const files = readdirSync(fullDir).filter(f => extname(f) === '.md');
19
- for (const file of files) {
20
- try {
21
- const content = readFileSync(join(fullDir, file), 'utf-8');
22
- // frontmatter title
23
- const titleMatch = content.match(/^title:\s*"?([^"\n]+)"?\s*$/m);
24
- if (titleMatch && titleMatch[1].length > 2) {
25
- titles.add(titleMatch[1].trim());
26
- continue;
27
- }
28
- // first heading
29
- const headingMatch = content.match(/^#\s+(.+)$/m);
30
- if (headingMatch && headingMatch[1].length > 2) {
31
- titles.add(headingMatch[1].trim());
32
- continue;
33
- }
34
- // filename without extension + timestamp prefix
35
- const name = file.replace(/^\d{4}-\d{2}-\d{2}T[\d-]+-/, '').replace(/\.md$/, '').replace(/-/g, ' ');
36
- if (name.length > 2)
37
- titles.add(name);
38
- }
39
- catch { /* skip unreadable files */ }
40
- }
41
- }
42
- catch { /* dir not readable */ }
43
- }
44
- return [...titles].filter(t => t.length > 3); // ignore very short titles
45
- }
46
- /**
47
- * 제목에서 링크 가능한 핵심 구문(phrases)을 추출.
48
- * 예: "Steve Jobs' 2005 Stanford Commencement Address"
49
- * → ["Steve Jobs", "Stanford Commencement Address", "Stanford"]
50
- */
51
- function extractLinkablePhrases(title) {
52
- const phrases = [];
53
- // 전체 제목 (짧으면)
54
- if (title.length <= 40)
55
- phrases.push(title);
56
- // 고유명사/핵심 구문 추출: 대문자로 시작하는 연속 단어
57
- const properNouns = title.match(/[A-Z\uAC00-\uD7A3][a-z\uAC00-\uD7A3]+(?:\s+[A-Z\uAC00-\uD7A3][a-z\uAC00-\uD7A3]+)*/g);
58
- if (properNouns) {
59
- for (const pn of properNouns) {
60
- if (pn.length > 4)
61
- phrases.push(pn);
62
- }
63
- }
64
- // 한국어: 긴 제목에서 의미 있는 부분 (4글자 이상 한글 연속)
65
- const koreanPhrases = title.match(/[\uAC00-\uD7A3]{4,}/g);
66
- if (koreanPhrases)
67
- phrases.push(...koreanPhrases);
68
- return [...new Set(phrases)].filter(p => p.length > 3);
69
- }
70
- /**
71
- * 노트 본문에서 기존 노트 제목/핵심 구문과 매칭하여 [[wikilink]]를 삽입.
72
- * - 이미 [[...]]로 감싸진 부분은 건드리지 않음
73
- * - frontmatter 영역은 건드리지 않음
74
- * - 자기 자신의 제목은 건드리지 않음
75
- * - heading(#) 줄은 건드리지 않음
76
- */
77
- export function insertWikilinks(body, vaultTitles, selfTitle) {
78
- if (vaultTitles.length === 0)
79
- return body;
80
- // frontmatter 분리
81
- const fmMatch = body.match(/^(---[\s\S]*?---\n?)/);
82
- const frontmatter = fmMatch ? fmMatch[1] : '';
83
- let content = fmMatch ? body.slice(frontmatter.length) : body;
84
- // 제목 → 링크 가능 구문 매핑
85
- const phraseToTitle = new Map();
86
- for (const title of vaultTitles) {
87
- if (title === selfTitle)
88
- continue;
89
- for (const phrase of extractLinkablePhrases(title)) {
90
- if (!phraseToTitle.has(phrase)) {
91
- phraseToTitle.set(phrase, title);
92
- }
93
- }
94
- }
95
- // 긴 구문부터 매칭 (짧은 구문이 긴 구문의 부분 매칭되는 것 방지)
96
- const sortedPhrases = [...phraseToTitle.keys()].sort((a, b) => b.length - a.length);
97
- const linkedPhrases = new Set(); // 이미 링크된 구문 (중복 방지)
98
- const contentLower = content.toLowerCase();
99
- for (const phrase of sortedPhrases) {
100
- const targetTitle = phraseToTitle.get(phrase);
101
- if (linkedPhrases.has(targetTitle))
102
- continue;
103
- // 사전 필터: 본문에 포함되지 않으면 regex 생성 스킵 (성능 최적화)
104
- if (!contentLower.includes(phrase.toLowerCase()))
105
- continue;
106
- const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
107
- const regex = new RegExp(`(?<!\\[\\[)(?<!#\\s)\\b(${escaped})\\b(?!\\]\\])(?![^\\[]*\\]\\])`, 'gi');
108
- let replaced = false;
109
- content = content.replace(regex, (match) => {
110
- if (replaced)
111
- return match;
112
- replaced = true;
113
- linkedPhrases.add(targetTitle);
114
- return `[[${targetTitle}|${match}]]`;
115
- });
116
- }
117
- return frontmatter + content;
118
- }
119
- /**
120
- * vault 스캔 + wikilink 삽입을 한 번에 수행하는 편의 함수.
121
- */
122
- export function autoLink(body, vaultPath, selfTitle, folders = DEFAULT_FOLDERS) {
123
- const titles = collectVaultTitles(vaultPath, folders);
124
- return insertWikilinks(body, titles, selfTitle);
125
- }
126
- //# sourceMappingURL=auto-linker.js.map
@@ -1,20 +0,0 @@
1
- import type { SearchEngine } from '../search/index.js';
2
- export interface CodeLink {
3
- filePath: string;
4
- keywords: string[];
5
- relatedNotes: Array<{
6
- documentId: string;
7
- title: string;
8
- score: number;
9
- matchedKeywords: string[];
10
- }>;
11
- }
12
- /**
13
- * Extract meaningful keywords from a code file path and optional content.
14
- */
15
- export declare function extractCodeKeywords(filePath: string, content?: string): string[];
16
- /**
17
- * Link a code file to related knowledge notes via keyword search.
18
- */
19
- export declare function linkCodeToKnowledge(searchEngine: SearchEngine, filePath: string, content?: string, limit?: number): Promise<CodeLink>;
20
- //# sourceMappingURL=code-linker.d.ts.map
@@ -1,88 +0,0 @@
1
- // Design Ref: F15 — 코드-지식 링커
2
- // 코드 파일/함수에서 키워드 추출 → 관련 노트 자동 매칭
3
- // Plan SC: SC-03 코드 파일에서 관련 노트 3개+ 반환
4
- /**
5
- * Extract meaningful keywords from a code file path and optional content.
6
- */
7
- export function extractCodeKeywords(filePath, content) {
8
- const keywords = new Set();
9
- // From file path: directory names + file name parts
10
- const skipParts = new Set(['src', 'lib', 'dist', 'node_modules', '.', '']);
11
- const parts = filePath
12
- .replace(/\\/g, '/')
13
- .split('/')
14
- .filter((p) => !skipParts.has(p));
15
- for (const part of parts) {
16
- // Split camelCase/PascalCase/kebab-case/snake_case
17
- const words = part
18
- .replace(/\.[^.]+$/, '') // remove extension
19
- .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase split
20
- .replace(/[-_]/g, ' ')
21
- .split(/\s+/)
22
- .filter((w) => w.length > 2)
23
- .map((w) => w.toLowerCase());
24
- words.forEach((w) => keywords.add(w));
25
- }
26
- // From content: extract identifiers and comments
27
- if (content) {
28
- // Extract import statements
29
- const imports = content.match(/import\s+.*?from\s+['"]([^'"]+)['"]/g) ?? [];
30
- for (const imp of imports) {
31
- const match = imp.match(/from\s+['"]([^'"]+)['"]/);
32
- if (match) {
33
- const modName = match[1].split('/').pop()?.replace(/\.[^.]+$/, '') ?? '';
34
- if (modName.length > 2)
35
- keywords.add(modName.toLowerCase());
36
- }
37
- }
38
- // Extract TODO/FIXME comments
39
- const comments = content.match(/\/\/\s*(TODO|FIXME|NOTE):\s*(.+)/gi) ?? [];
40
- for (const c of comments) {
41
- const words = c.replace(/\/\/\s*(TODO|FIXME|NOTE):\s*/i, '').split(/\s+/);
42
- words.filter((w) => w.length > 3).forEach((w) => keywords.add(w.toLowerCase()));
43
- }
44
- // Extract function/class names
45
- const funcNames = content.match(/(?:function|class|const|let|var)\s+([A-Za-z]\w{3,})/g) ?? [];
46
- for (const fn of funcNames) {
47
- const name = fn.split(/\s+/)[1];
48
- if (name) {
49
- // Split camelCase
50
- const words = name.replace(/([a-z])([A-Z])/g, '$1 $2').split(/\s+/);
51
- words.filter((w) => w.length > 2).forEach((w) => keywords.add(w.toLowerCase()));
52
- }
53
- }
54
- }
55
- // Remove common noise words
56
- const noise = new Set(['the', 'and', 'for', 'with', 'from', 'this', 'that', 'not', 'but', 'are', 'was', 'has', 'have', 'new', 'get', 'set', 'use', 'type', 'void', 'null', 'true', 'false', 'return', 'async', 'await', 'export', 'default', 'import', 'const', 'string', 'number', 'boolean', 'interface', 'function', 'index', 'main', 'app', 'test', 'spec', 'utils', 'helpers']);
57
- return [...keywords].filter((k) => !noise.has(k)).slice(0, 20);
58
- }
59
- /**
60
- * Link a code file to related knowledge notes via keyword search.
61
- */
62
- export async function linkCodeToKnowledge(searchEngine, filePath, content, limit = 5) {
63
- const keywords = extractCodeKeywords(filePath, content);
64
- if (keywords.length === 0) {
65
- return { filePath, keywords: [], relatedNotes: [] };
66
- }
67
- // Search using top keywords as query
68
- const query = keywords.slice(0, 8).join(' ');
69
- const results = await searchEngine.search({
70
- query,
71
- limit: limit * 2,
72
- threshold: 0.1,
73
- });
74
- // Score which keywords matched in each result
75
- const relatedNotes = results.slice(0, limit).map((r) => {
76
- const titleLower = r.document.title.toLowerCase();
77
- const contentLower = r.chunk.content.toLowerCase();
78
- const matchedKeywords = keywords.filter((k) => titleLower.includes(k) || contentLower.includes(k) || r.document.tags.some((t) => t.toLowerCase().includes(k)));
79
- return {
80
- documentId: r.document.id,
81
- title: r.document.title,
82
- score: r.score,
83
- matchedKeywords,
84
- };
85
- });
86
- return { filePath, keywords, relatedNotes };
87
- }
88
- //# sourceMappingURL=code-linker.js.map
@@ -1,20 +0,0 @@
1
- import type { VectorStore } from '../store/types.js';
2
- export interface ContradictionPair {
3
- docA: {
4
- id: string;
5
- title: string;
6
- filePath: string;
7
- statement: string;
8
- };
9
- docB: {
10
- id: string;
11
- title: string;
12
- filePath: string;
13
- statement: string;
14
- };
15
- similarity: number;
16
- confidence: number;
17
- type: 'negation' | 'value_conflict' | 'temporal' | 'semantic';
18
- }
19
- export declare function detectContradictions(store: VectorStore, limit?: number): Promise<ContradictionPair[]>;
20
- //# sourceMappingURL=contradiction-detector.d.ts.map