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,115 +0,0 @@
1
- // Contradiction Detector (F-A12)
2
- // Finds potentially contradicting statements across notes
3
- // Uses embedding similarity + negation pattern detection
4
- // Negation/opposition patterns
5
- const NEGATION_PAIRS = [
6
- ['should', 'should not'], ['must', 'must not'], ['always', 'never'],
7
- ['best', 'worst'], ['good', 'bad'], ['correct', 'incorrect'],
8
- ['true', 'false'], ['increase', 'decrease'], ['enable', 'disable'],
9
- ['recommended', 'not recommended'], ['use', 'avoid'],
10
- ['prefer', 'avoid'], ['do', "don't"], ['is', "isn't"],
11
- ['can', "can't"], ['will', "won't"], ['important', 'unimportant'],
12
- ['필요', '불필요'], ['해야', '하면 안'], ['좋', '나쁜'], ['맞', '틀'],
13
- ];
14
- function extractKeyStatements(content) {
15
- return content
16
- .split(/[.\n!?]/)
17
- .map(s => s.trim())
18
- .filter(s => s.length > 20 && s.length < 200)
19
- .filter(s => {
20
- const lower = s.toLowerCase();
21
- return /should|must|always|never|best|important|recommend|prefer|avoid|필요|해야|좋|나쁜/.test(lower);
22
- })
23
- .slice(0, 10); // max 10 statements per document
24
- }
25
- function detectNegationConflict(stmtA, stmtB) {
26
- const a = stmtA.toLowerCase();
27
- const b = stmtB.toLowerCase();
28
- // Check negation pairs
29
- for (const [pos, neg] of NEGATION_PAIRS) {
30
- if ((a.includes(pos) && b.includes(neg)) || (a.includes(neg) && b.includes(pos))) {
31
- // Check if they're talking about the same subject (share words)
32
- const wordsA = new Set(a.split(/\s+/).filter(w => w.length > 3));
33
- const wordsB = new Set(b.split(/\s+/).filter(w => w.length > 3));
34
- const overlap = [...wordsA].filter(w => wordsB.has(w)).length;
35
- const minSize = Math.min(wordsA.size, wordsB.size) || 1;
36
- const subjectOverlap = overlap / minSize;
37
- if (subjectOverlap > 0.2) {
38
- return { isConflict: true, confidence: Math.min(0.5 + subjectOverlap * 0.5, 0.95), type: 'negation' };
39
- }
40
- }
41
- }
42
- // Check numeric value conflicts (e.g., "timeout should be 30s" vs "timeout should be 5s")
43
- const numsA = a.match(/\d+/g);
44
- const numsB = b.match(/\d+/g);
45
- if (numsA && numsB) {
46
- const wordsA = new Set(a.replace(/\d+/g, '').split(/\s+/).filter(w => w.length > 3));
47
- const wordsB = new Set(b.replace(/\d+/g, '').split(/\s+/).filter(w => w.length > 3));
48
- const overlap = [...wordsA].filter(w => wordsB.has(w)).length;
49
- if (overlap >= 2 && numsA[0] !== numsB[0]) {
50
- return { isConflict: true, confidence: 0.6, type: 'value_conflict' };
51
- }
52
- }
53
- return { isConflict: false, confidence: 0, type: 'semantic' };
54
- }
55
- export async function detectContradictions(store, limit = 20) {
56
- const docs = await store.getAllDocuments();
57
- const embeddings = await store.getDocumentEmbeddings();
58
- if (docs.length < 2)
59
- return [];
60
- // Build document vectors + key statements
61
- const docData = new Map();
62
- for (const doc of docs) {
63
- const vec = embeddings.get(doc.id);
64
- if (!vec)
65
- continue;
66
- const statements = extractKeyStatements(doc.content);
67
- if (statements.length === 0)
68
- continue;
69
- docData.set(doc.id, { vec: Array.from(vec), title: doc.title, filePath: doc.filePath, statements });
70
- }
71
- const ids = [...docData.keys()];
72
- const results = [];
73
- // Compare documents with moderate similarity (same topic, possibly conflicting)
74
- for (let i = 0; i < ids.length && results.length < limit * 3; i++) {
75
- for (let j = i + 1; j < ids.length && results.length < limit * 3; j++) {
76
- const a = docData.get(ids[i]);
77
- const b = docData.get(ids[j]);
78
- const sim = cosineSim(a.vec, b.vec);
79
- // Sweet spot: similar enough to be same topic, not identical
80
- if (sim < 0.3 || sim > 0.9)
81
- continue;
82
- // Compare statements
83
- for (const stmtA of a.statements) {
84
- for (const stmtB of b.statements) {
85
- const { isConflict, confidence, type } = detectNegationConflict(stmtA, stmtB);
86
- if (isConflict && confidence >= 0.5) {
87
- results.push({
88
- docA: { id: ids[i], title: a.title, filePath: a.filePath, statement: stmtA },
89
- docB: { id: ids[j], title: b.title, filePath: b.filePath, statement: stmtB },
90
- similarity: Math.round(sim * 1000) / 1000,
91
- confidence: Math.round(confidence * 100) / 100,
92
- type,
93
- });
94
- }
95
- }
96
- }
97
- }
98
- }
99
- return results
100
- .sort((a, b) => b.confidence - a.confidence)
101
- .slice(0, limit);
102
- }
103
- function cosineSim(a, b) {
104
- if (a.length !== b.length)
105
- return 0;
106
- let dot = 0, na = 0, nb = 0;
107
- for (let i = 0; i < a.length; i++) {
108
- dot += a[i] * b[i];
109
- na += a[i] * a[i];
110
- nb += b[i] * b[i];
111
- }
112
- const d = Math.sqrt(na) * Math.sqrt(nb);
113
- return d === 0 ? 0 : dot / d;
114
- }
115
- //# sourceMappingURL=contradiction-detector.js.map
@@ -1,27 +0,0 @@
1
- import type { Database } from 'better-sqlite3';
2
- import type { DecayState, AccessEvent, DecayReport } from './types.js';
3
- export declare class DecayEngine {
4
- private db;
5
- constructor(db: Database);
6
- private ensureTables;
7
- /**
8
- * Record an access event and update decay state.
9
- */
10
- recordAccess(event: AccessEvent): Promise<void>;
11
- /**
12
- * Batch compute retrievability for all documents.
13
- */
14
- computeAll(): Promise<DecayReport>;
15
- private computeClusterHealth;
16
- /**
17
- * Get documents below decay threshold.
18
- */
19
- getDecaying(threshold?: number, limit?: number): Promise<Array<DecayState & {
20
- title: string;
21
- }>>;
22
- /**
23
- * Initialize decay state for documents that don't have one yet.
24
- */
25
- initializeNewDocuments(): Promise<number>;
26
- }
27
- //# sourceMappingURL=decay-engine.d.ts.map
@@ -1,190 +0,0 @@
1
- // Design Ref: §3.2 — 감쇠 엔진 (DB 연동)
2
- // Plan SC: SC-01, SC-02, SC-04
3
- import { computeRetrievability, updateStability, estimateInitialStability, elapsedDays, FSRS_PARAMS, } from './fsrs.js';
4
- export class DecayEngine {
5
- db;
6
- constructor(db) {
7
- this.db = db;
8
- this.ensureTables();
9
- }
10
- ensureTables() {
11
- this.db.exec(`
12
- CREATE TABLE IF NOT EXISTS access_log (
13
- id INTEGER PRIMARY KEY AUTOINCREMENT,
14
- document_id TEXT NOT NULL,
15
- access_type TEXT NOT NULL,
16
- accessed_at TEXT NOT NULL
17
- );
18
- CREATE INDEX IF NOT EXISTS idx_access_log_doc ON access_log(document_id);
19
- CREATE INDEX IF NOT EXISTS idx_access_log_time ON access_log(accessed_at);
20
-
21
- CREATE TABLE IF NOT EXISTS decay_state (
22
- document_id TEXT PRIMARY KEY,
23
- stability REAL NOT NULL DEFAULT 7.0,
24
- difficulty REAL NOT NULL DEFAULT 5.0,
25
- last_access TEXT NOT NULL,
26
- retrievability REAL NOT NULL DEFAULT 1.0,
27
- updated_at TEXT NOT NULL
28
- );
29
- `);
30
- }
31
- /**
32
- * Record an access event and update decay state.
33
- */
34
- async recordAccess(event) {
35
- const now = event.timestamp || new Date().toISOString();
36
- // Log the event
37
- this.db.prepare('INSERT INTO access_log (document_id, access_type, accessed_at) VALUES (?, ?, ?)').run(event.documentId, event.type, now);
38
- // Get or create decay state
39
- const existing = this.db.prepare('SELECT * FROM decay_state WHERE document_id = ?').get(event.documentId);
40
- if (existing) {
41
- const elapsed = elapsedDays(existing.last_access, now);
42
- const currentR = computeRetrievability(existing.stability, elapsed);
43
- const newS = updateStability(existing.stability, existing.difficulty, currentR);
44
- this.db.prepare(`
45
- UPDATE decay_state SET stability = ?, last_access = ?, retrievability = 1.0, updated_at = ?
46
- WHERE document_id = ?
47
- `).run(newS, now, now, event.documentId);
48
- }
49
- else {
50
- // New document — estimate initial stability
51
- const doc = this.db.prepare('SELECT content FROM documents WHERE id = ?').get(event.documentId);
52
- const contentLen = doc?.content?.length ?? 500;
53
- const connCount = this.db.prepare('SELECT COUNT(*) as c FROM chunks WHERE document_id = ?').get(event.documentId)?.c ?? 1;
54
- const initS = estimateInitialStability(contentLen, connCount);
55
- this.db.prepare(`
56
- INSERT INTO decay_state (document_id, stability, difficulty, last_access, retrievability, updated_at)
57
- VALUES (?, ?, ?, ?, 1.0, ?)
58
- `).run(event.documentId, initS, FSRS_PARAMS.difficulty, now, now);
59
- }
60
- }
61
- /**
62
- * Batch compute retrievability for all documents.
63
- */
64
- async computeAll() {
65
- const now = new Date().toISOString();
66
- // Initialize documents that don't have decay state yet
67
- await this.initializeNewDocuments();
68
- // Compute R for all
69
- const states = this.db.prepare('SELECT * FROM decay_state').all();
70
- const docs = this.db.prepare('SELECT id, title FROM documents').all();
71
- const titleMap = new Map(docs.map((d) => [d.id, d.title]));
72
- const updated = [];
73
- const updateStmt = this.db.prepare('UPDATE decay_state SET retrievability = ?, updated_at = ? WHERE document_id = ?');
74
- const tx = this.db.transaction(() => {
75
- for (const s of states) {
76
- const elapsed = elapsedDays(s.last_access, now);
77
- const r = computeRetrievability(s.stability, elapsed);
78
- updateStmt.run(r, now, s.document_id);
79
- updated.push({
80
- documentId: s.document_id,
81
- stability: s.stability,
82
- difficulty: s.difficulty,
83
- lastAccess: s.last_access,
84
- retrievability: r,
85
- });
86
- }
87
- });
88
- tx();
89
- // Build report
90
- const decaying = updated.filter(s => s.retrievability < 0.5);
91
- const critical = updated.filter(s => s.retrievability < 0.3);
92
- const avgR = updated.length > 0
93
- ? updated.reduce((sum, s) => sum + s.retrievability, 0) / updated.length
94
- : 1.0;
95
- // Top decaying (sorted by R ascending)
96
- const topDecaying = [...decaying]
97
- .sort((a, b) => a.retrievability - b.retrievability)
98
- .slice(0, 20)
99
- .map(s => ({
100
- ...s,
101
- title: titleMap.get(s.documentId) ?? s.documentId,
102
- daysSinceAccess: Math.round(elapsedDays(s.lastAccess, now)),
103
- }));
104
- // Cluster health
105
- const clusterData = this.db.prepare(`
106
- SELECT ds.document_id, ds.retrievability
107
- FROM decay_state ds
108
- JOIN documents d ON d.id = ds.document_id
109
- `).all();
110
- // Get cluster info from graph data cache (simplified: group by first folder)
111
- const clusterHealth = this.computeClusterHealth(clusterData);
112
- return {
113
- totalDocuments: updated.length,
114
- decayingCount: decaying.length,
115
- criticalCount: critical.length,
116
- averageR: Math.round(avgR * 100) / 100,
117
- topDecaying,
118
- clusterHealth,
119
- };
120
- }
121
- computeClusterHealth(data) {
122
- // Group by document's top-level folder as proxy for cluster
123
- const groups = new Map();
124
- for (const row of data) {
125
- const doc = this.db.prepare('SELECT file_path FROM documents WHERE id = ?').get(row.document_id);
126
- const folder = doc?.file_path?.split('/')[0] ?? 'unknown';
127
- if (!groups.has(folder))
128
- groups.set(folder, []);
129
- groups.get(folder).push(row.retrievability);
130
- }
131
- return [...groups.entries()]
132
- .map(([label, rs]) => ({
133
- label,
134
- avgR: Math.round((rs.reduce((a, b) => a + b, 0) / rs.length) * 100) / 100,
135
- count: rs.length,
136
- }))
137
- .sort((a, b) => a.avgR - b.avgR);
138
- }
139
- /**
140
- * Get documents below decay threshold.
141
- */
142
- async getDecaying(threshold = 0.5, limit = 20) {
143
- await this.computeAll(); // refresh R values
144
- const rows = this.db.prepare(`
145
- SELECT ds.*, d.title FROM decay_state ds
146
- JOIN documents d ON d.id = ds.document_id
147
- WHERE ds.retrievability < ?
148
- ORDER BY ds.retrievability ASC
149
- LIMIT ?
150
- `).all(threshold, limit);
151
- return rows.map(r => ({
152
- documentId: r.document_id,
153
- stability: r.stability,
154
- difficulty: r.difficulty,
155
- lastAccess: r.last_access,
156
- retrievability: r.retrievability,
157
- title: r.title,
158
- }));
159
- }
160
- /**
161
- * Initialize decay state for documents that don't have one yet.
162
- */
163
- async initializeNewDocuments() {
164
- const missing = this.db.prepare(`
165
- SELECT d.id, d.content, d.last_modified
166
- FROM documents d
167
- LEFT JOIN decay_state ds ON d.id = ds.document_id
168
- WHERE ds.document_id IS NULL
169
- `).all();
170
- if (missing.length === 0)
171
- return 0;
172
- const insert = this.db.prepare(`
173
- INSERT OR IGNORE INTO decay_state (document_id, stability, difficulty, last_access, retrievability, updated_at)
174
- VALUES (?, ?, ?, ?, ?, ?)
175
- `);
176
- const tx = this.db.transaction(() => {
177
- for (const doc of missing) {
178
- const contentLen = doc.content?.length ?? 500;
179
- const initS = estimateInitialStability(contentLen, 1);
180
- const lastAccess = doc.last_modified || new Date().toISOString();
181
- const elapsed = elapsedDays(lastAccess);
182
- const r = computeRetrievability(initS, elapsed);
183
- insert.run(doc.id, initS, FSRS_PARAMS.difficulty, lastAccess, r, new Date().toISOString());
184
- }
185
- });
186
- tx();
187
- return missing.length;
188
- }
189
- }
190
- //# sourceMappingURL=decay-engine.js.map
@@ -1,25 +0,0 @@
1
- import { type FolderNames } from '../config.js';
2
- export interface BlueprintSection {
3
- title: string;
4
- tags?: string[];
5
- description?: string;
6
- }
7
- export interface DraftOptions {
8
- topic?: string;
9
- format?: 'blog' | 'report' | 'outline' | 'instagram' | 'thread' | 'script';
10
- maxSections?: number;
11
- blueprint?: BlueprintSection[];
12
- }
13
- export interface DraftResult {
14
- title: string;
15
- filePath: string;
16
- wordCount: number;
17
- sourceCount: number;
18
- concepts: string[];
19
- }
20
- /**
21
- * vault의 wiki 데이터를 기반으로 초안(draft)을 생성.
22
- * Express 단계: 지식이 vault에서 나가는 출구.
23
- */
24
- export declare function generateDraft(vaultPath: string, options?: DraftOptions, folders?: FolderNames): DraftResult;
25
- //# sourceMappingURL=draft-generator.d.ts.map
@@ -1,277 +0,0 @@
1
- // Express 단계: stellavault draft — wiki 기반 초안 생성
2
- // Plan SC: SC3 (Express)
3
- // 카파시의 "자가 컴파일" 결과물을 외부로 표현하는 출구
4
- import { writeFileSync, mkdirSync, existsSync } from 'node:fs';
5
- import { join, resolve, basename, extname } from 'node:path';
6
- import { scanRawDirectory, extractConcepts } from './wiki-compiler.js';
7
- import { DEFAULT_FOLDERS } from '../config.js';
8
- /**
9
- * vault의 wiki 데이터를 기반으로 초안(draft)을 생성.
10
- * Express 단계: 지식이 vault에서 나가는 출구.
11
- */
12
- export function generateDraft(vaultPath, options = {}, folders = DEFAULT_FOLDERS) {
13
- const { topic, format = 'blog', maxSections = 8, blueprint } = options;
14
- // raw + wiki 문서 스캔
15
- const rawDir = resolve(vaultPath, folders.fleeting);
16
- const wikiDir = resolve(vaultPath, folders.wiki);
17
- const litDir = resolve(vaultPath, folders.literature);
18
- const allDocs = [];
19
- for (const dir of [rawDir, wikiDir, litDir]) {
20
- if (existsSync(dir)) {
21
- allDocs.push(...scanRawDirectory(dir));
22
- }
23
- }
24
- if (allDocs.length === 0) {
25
- throw new Error('No documents found in vault. Run `stellavault ingest` first.');
26
- }
27
- // 토픽 필터
28
- const filteredDocs = topic
29
- ? allDocs.filter(d => d.tags.some(t => t.toLowerCase().includes(topic.toLowerCase())) ||
30
- d.title.toLowerCase().includes(topic.toLowerCase()) ||
31
- d.content.toLowerCase().includes(topic.toLowerCase()))
32
- : allDocs;
33
- if (filteredDocs.length === 0) {
34
- throw new Error(`No documents found for topic "${topic}". Try a broader term.`);
35
- }
36
- // 개념 추출
37
- const concepts = extractConcepts(filteredDocs);
38
- const topConcepts = [...concepts.entries()]
39
- .sort((a, b) => b[1].length - a[1].length)
40
- .slice(0, maxSections);
41
- // 초안 생성
42
- const draftTitle = topic
43
- ? `Draft: ${topic}`
44
- : `Knowledge Draft — ${new Date().toISOString().split('T')[0]}`;
45
- let body;
46
- // Blueprint 모드: 사용자 정의 챕터 구성
47
- if (blueprint && blueprint.length > 0) {
48
- const bpLines = [`# ${draftTitle}\n`];
49
- bpLines.push(`> Blueprint draft from ${filteredDocs.length} sources\n`);
50
- for (const section of blueprint) {
51
- bpLines.push(`## ${section.title}\n`);
52
- if (section.description)
53
- bpLines.push(`*${section.description}*\n`);
54
- // 태그 필터로 관련 문서 찾기
55
- const sectionDocs = section.tags
56
- ? filteredDocs.filter(d => d.tags.some(t => section.tags.includes(t)) || d.title.toLowerCase().includes(section.title.toLowerCase()))
57
- : filteredDocs;
58
- const usedDocs = sectionDocs.slice(0, 5);
59
- for (const doc of usedDocs) {
60
- const excerpt = extractExcerpt(doc.content, 200);
61
- if (excerpt)
62
- bpLines.push(`> ${excerpt}\n> — *${doc.title}*\n`);
63
- }
64
- if (usedDocs.length > 0) {
65
- bpLines.push(`*Sources: ${usedDocs.map(d => d.title).join(', ')}*\n`);
66
- }
67
- }
68
- bpLines.push(`---\n*Generated by \`stellavault draft --blueprint\` at ${new Date().toISOString()}*`);
69
- body = bpLines.join('\n');
70
- }
71
- else
72
- switch (format) {
73
- case 'outline':
74
- body = generateOutline(draftTitle, topConcepts, filteredDocs);
75
- break;
76
- case 'report':
77
- body = generateReport(draftTitle, topConcepts, filteredDocs);
78
- break;
79
- case 'instagram':
80
- body = generateInstagram(draftTitle, topConcepts, filteredDocs);
81
- break;
82
- case 'thread':
83
- body = generateThread(draftTitle, topConcepts, filteredDocs);
84
- break;
85
- case 'script':
86
- body = generateScript(draftTitle, topConcepts, filteredDocs);
87
- break;
88
- case 'blog':
89
- default:
90
- body = generateBlog(draftTitle, topConcepts, filteredDocs);
91
- break;
92
- }
93
- const wordCount = body.split(/\s+/).filter(Boolean).length;
94
- // _drafts/ 폴더에 저장
95
- const draftsDir = resolve(vaultPath, '_drafts');
96
- if (!existsSync(draftsDir))
97
- mkdirSync(draftsDir, { recursive: true });
98
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
99
- const slug = (topic ?? 'knowledge').replace(/[^a-zA-Z0-9가-힣\s]/g, '').replace(/\s+/g, '-').toLowerCase().slice(0, 40);
100
- const filename = `${timestamp}-${slug}.md`;
101
- const filePath = join('_drafts', filename);
102
- const fullPath = resolve(vaultPath, filePath);
103
- writeFileSync(fullPath, body, 'utf-8');
104
- return {
105
- title: draftTitle,
106
- filePath,
107
- wordCount,
108
- sourceCount: filteredDocs.length,
109
- concepts: topConcepts.map(([c]) => c),
110
- };
111
- }
112
- // ─── 포맷별 생성기 ───
113
- function generateBlog(title, concepts, docs) {
114
- const lines = [];
115
- lines.push(`# ${title}`, '');
116
- lines.push(`> Auto-generated from ${docs.length} knowledge notes`, '');
117
- // 도입부: 전체 주제 요약
118
- lines.push('## Introduction', '');
119
- const topicWords = concepts.slice(0, 3).map(([c]) => c).join(', ');
120
- lines.push(`This post explores ${topicWords} based on ${docs.length} curated knowledge notes.`, '');
121
- // 섹션별: 개념 + 관련 문서 발췌
122
- for (const [concept, docPaths] of concepts) {
123
- lines.push(`## ${capitalize(concept)}`, '');
124
- const relatedDocs = docs.filter(d => docPaths.includes(d.filePath) ||
125
- d.tags.includes(concept) ||
126
- d.title.toLowerCase().includes(concept.toLowerCase())).slice(0, 3);
127
- for (const doc of relatedDocs) {
128
- const excerpt = extractExcerpt(doc.content, 150);
129
- if (excerpt) {
130
- lines.push(`> ${excerpt}`, `> — *${doc.title}*`, '');
131
- }
132
- }
133
- lines.push('<!-- TODO: Add your analysis and insights here -->', '');
134
- }
135
- // 참고 자료 (APA-style citations)
136
- lines.push('## References', '');
137
- const uniqueDocs = [...new Map(docs.map(d => [d.filePath, d])).values()].slice(0, 20);
138
- const now = new Date();
139
- for (let i = 0; i < uniqueDocs.length; i++) {
140
- const doc = uniqueDocs[i];
141
- const dateMatch = doc.content.match(/(?:created|date):\s*(\d{4}-\d{2}-\d{2})/);
142
- const year = dateMatch ? dateMatch[1].split('-')[0] : now.getFullYear().toString();
143
- const sourceMatch = doc.content.match(/source:\s*(.+)/);
144
- const source = sourceMatch ? sourceMatch[1].trim() : '';
145
- // APA-style: Author/Title (Year). Source.
146
- if (source.startsWith('http')) {
147
- lines.push(`[${i + 1}] ${doc.title} (${year}). Retrieved from ${source}`);
148
- }
149
- else {
150
- lines.push(`[${i + 1}] ${doc.title} (${year}). [[${basename(doc.filePath, extname(doc.filePath))}|Link]]`);
151
- }
152
- }
153
- lines.push('');
154
- lines.push(`---`, `*Generated by \`stellavault draft\` at ${new Date().toISOString()}*`);
155
- return lines.join('\n');
156
- }
157
- function generateReport(title, concepts, docs) {
158
- const lines = [];
159
- lines.push(`# ${title}`, '');
160
- lines.push(`**Date:** ${new Date().toISOString().split('T')[0]}`);
161
- lines.push(`**Sources:** ${docs.length} documents`);
162
- lines.push(`**Key Topics:** ${concepts.map(([c]) => c).join(', ')}`, '');
163
- lines.push('## Executive Summary', '');
164
- lines.push('<!-- TODO: Write 2-3 sentence summary -->', '');
165
- for (const [concept, docPaths] of concepts) {
166
- lines.push(`## ${capitalize(concept)}`, '');
167
- lines.push(`**Related documents:** ${docPaths.length}`, '');
168
- const relatedDocs = docs.filter(d => docPaths.includes(d.filePath)).slice(0, 3);
169
- for (const doc of relatedDocs) {
170
- const excerpt = extractExcerpt(doc.content, 200);
171
- if (excerpt)
172
- lines.push(`- ${excerpt} *(${doc.title})*`);
173
- }
174
- lines.push('');
175
- lines.push('**Analysis:** <!-- TODO -->', '');
176
- }
177
- lines.push('## Conclusion', '', '<!-- TODO -->', '');
178
- lines.push(`---`, `*Generated by \`stellavault draft\` at ${new Date().toISOString()}*`);
179
- return lines.join('\n');
180
- }
181
- function generateOutline(title, concepts, docs) {
182
- const lines = [];
183
- lines.push(`# ${title} — Outline`, '');
184
- lines.push(`Sources: ${docs.length} documents`, '');
185
- for (let i = 0; i < concepts.length; i++) {
186
- const [concept, docPaths] = concepts[i];
187
- lines.push(`${i + 1}. **${capitalize(concept)}** (${docPaths.length} sources)`);
188
- const relatedDocs = docs.filter(d => docPaths.includes(d.filePath)).slice(0, 3);
189
- for (const doc of relatedDocs) {
190
- lines.push(` - ${doc.title}`);
191
- }
192
- }
193
- lines.push('', `---`, `*Generated by \`stellavault draft\` at ${new Date().toISOString()}*`);
194
- return lines.join('\n');
195
- }
196
- // ─── SNS 포맷 ───
197
- function generateInstagram(title, concepts, docs) {
198
- const lines = [];
199
- const topicWords = concepts.slice(0, 3).map(([c]) => c);
200
- lines.push(`${title}`, '');
201
- lines.push('---', '');
202
- // 슬라이드 1: Hook
203
- lines.push('**Slide 1 (Hook)**', '');
204
- lines.push(`Did you know about ${topicWords.join(', ')}?`, '');
205
- // 슬라이드 2-4: Key Points
206
- for (let i = 0; i < Math.min(3, concepts.length); i++) {
207
- const [concept] = concepts[i];
208
- const doc = docs.find(d => d.tags.includes(concept) || d.title.toLowerCase().includes(concept.toLowerCase()));
209
- const excerpt = doc ? extractExcerpt(doc.content, 80) : '';
210
- lines.push(`**Slide ${i + 2} (${capitalize(concept)})**`, '');
211
- lines.push(excerpt || `Key insight about ${concept}`, '');
212
- }
213
- // CTA
214
- lines.push('**Last Slide (CTA)**', '');
215
- lines.push('Save this for later! Follow for more knowledge drops.', '');
216
- // Caption
217
- lines.push('---', '', '**Caption:**', '');
218
- lines.push(`${topicWords.map(w => `#${w}`).join(' ')} #knowledge #secondbrain #stellavault`, '');
219
- lines.push(`---`, `*Generated by \`stellavault draft --format instagram\`*`);
220
- return lines.join('\n');
221
- }
222
- function generateThread(title, concepts, docs) {
223
- const lines = [];
224
- // Tweet 1: Hook
225
- lines.push(`**1/** ${title}`, '');
226
- lines.push(`Here's what I learned from ${docs.length} sources:`, '');
227
- // Tweet 2-N: One per concept
228
- for (let i = 0; i < Math.min(6, concepts.length); i++) {
229
- const [concept, docPaths] = concepts[i];
230
- const doc = docs.find(d => docPaths.includes(d.filePath));
231
- const excerpt = doc ? extractExcerpt(doc.content, 200) : '';
232
- lines.push(`**${i + 2}/** ${capitalize(concept)}`, '');
233
- lines.push(excerpt || `Key point about ${concept}`, '');
234
- }
235
- // Final tweet
236
- lines.push(`**${Math.min(6, concepts.length) + 2}/** That's a wrap!`, '');
237
- lines.push('If this was useful, repost the first tweet.', '');
238
- lines.push(`---`, `*Generated by \`stellavault draft --format thread\`*`);
239
- return lines.join('\n');
240
- }
241
- function generateScript(title, concepts, docs) {
242
- const lines = [];
243
- lines.push(`# ${title} — Video Script`, '');
244
- lines.push(`**Duration:** ~${Math.max(3, concepts.length * 2)} minutes`, '');
245
- // Intro
246
- lines.push('## Intro (30s)', '');
247
- lines.push(`"Hey everyone, today we're diving into ${concepts.slice(0, 2).map(([c]) => c).join(' and ')}."`, '');
248
- // Sections
249
- for (let i = 0; i < Math.min(5, concepts.length); i++) {
250
- const [concept, docPaths] = concepts[i];
251
- const relatedDocs = docs.filter(d => docPaths.includes(d.filePath)).slice(0, 2);
252
- lines.push(`## Section ${i + 1}: ${capitalize(concept)} (~${Math.max(1, Math.floor(relatedDocs.length))}min)`, '');
253
- for (const doc of relatedDocs) {
254
- const excerpt = extractExcerpt(doc.content, 150);
255
- if (excerpt)
256
- lines.push(`[Visual: ${doc.title}]`, excerpt, '');
257
- }
258
- }
259
- // Outro
260
- lines.push('## Outro (15s)', '');
261
- lines.push('"If you found this helpful, like and subscribe!"', '');
262
- lines.push(`---`, `*Generated by \`stellavault draft --format script\`*`);
263
- return lines.join('\n');
264
- }
265
- // ─── 유틸 ───
266
- function extractExcerpt(content, maxLen) {
267
- // frontmatter 제거
268
- const body = content.replace(/^---[\s\S]*?---\n?/, '').replace(/^#+\s+.+\n/m, '').trim();
269
- // 첫 의미 있는 문단
270
- const paragraphs = body.split(/\n\n+/).filter(p => p.length > 20 && !p.startsWith('> ') && !p.startsWith('- '));
271
- const first = paragraphs[0] ?? '';
272
- return first.length > maxLen ? first.slice(0, maxLen) + '...' : first;
273
- }
274
- function capitalize(s) {
275
- return s.charAt(0).toUpperCase() + s.slice(1);
276
- }
277
- //# sourceMappingURL=draft-generator.js.map
@@ -1,20 +0,0 @@
1
- import type { VectorStore } from '../store/types.js';
2
- export interface DuplicatePair {
3
- docA: {
4
- id: string;
5
- title: string;
6
- filePath: string;
7
- };
8
- docB: {
9
- id: string;
10
- title: string;
11
- filePath: string;
12
- };
13
- similarity: number;
14
- }
15
- /**
16
- * 벡터 유사도 기반 중복 노트 탐지.
17
- * 문서별 평균 임베딩을 비교하여 threshold 이상인 쌍을 반환.
18
- */
19
- export declare function detectDuplicates(store: VectorStore, threshold?: number, limit?: number): Promise<DuplicatePair[]>;
20
- //# sourceMappingURL=duplicate-detector.d.ts.map