@vpxa/kb 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1140 -0
  3. package/bin/kb.mjs +10 -0
  4. package/package.json +67 -0
  5. package/packages/analyzers/dist/blast-radius-analyzer.d.ts +23 -0
  6. package/packages/analyzers/dist/blast-radius-analyzer.js +114 -0
  7. package/packages/analyzers/dist/dependency-analyzer.d.ts +29 -0
  8. package/packages/analyzers/dist/dependency-analyzer.js +425 -0
  9. package/packages/analyzers/dist/diagram-generator.d.ts +13 -0
  10. package/packages/analyzers/dist/diagram-generator.js +86 -0
  11. package/packages/analyzers/dist/entry-point-analyzer.d.ts +19 -0
  12. package/packages/analyzers/dist/entry-point-analyzer.js +239 -0
  13. package/packages/analyzers/dist/index.d.ts +14 -0
  14. package/packages/analyzers/dist/index.js +23 -0
  15. package/packages/analyzers/dist/knowledge-producer.d.ts +32 -0
  16. package/packages/analyzers/dist/knowledge-producer.js +113 -0
  17. package/packages/analyzers/dist/pattern-analyzer.d.ts +12 -0
  18. package/packages/analyzers/dist/pattern-analyzer.js +359 -0
  19. package/packages/analyzers/dist/regex-call-graph.d.ts +17 -0
  20. package/packages/analyzers/dist/regex-call-graph.js +428 -0
  21. package/packages/analyzers/dist/structure-analyzer.d.ts +11 -0
  22. package/packages/analyzers/dist/structure-analyzer.js +258 -0
  23. package/packages/analyzers/dist/symbol-analyzer.d.ts +10 -0
  24. package/packages/analyzers/dist/symbol-analyzer.js +442 -0
  25. package/packages/analyzers/dist/ts-call-graph.d.ts +27 -0
  26. package/packages/analyzers/dist/ts-call-graph.js +160 -0
  27. package/packages/analyzers/dist/types.d.ts +98 -0
  28. package/packages/analyzers/dist/types.js +1 -0
  29. package/packages/chunker/dist/call-graph-extractor.d.ts +22 -0
  30. package/packages/chunker/dist/call-graph-extractor.js +90 -0
  31. package/packages/chunker/dist/chunker-factory.d.ts +7 -0
  32. package/packages/chunker/dist/chunker-factory.js +36 -0
  33. package/packages/chunker/dist/chunker.interface.d.ts +10 -0
  34. package/packages/chunker/dist/chunker.interface.js +1 -0
  35. package/packages/chunker/dist/code-chunker.d.ts +14 -0
  36. package/packages/chunker/dist/code-chunker.js +134 -0
  37. package/packages/chunker/dist/generic-chunker.d.ts +12 -0
  38. package/packages/chunker/dist/generic-chunker.js +72 -0
  39. package/packages/chunker/dist/index.d.ts +8 -0
  40. package/packages/chunker/dist/index.js +21 -0
  41. package/packages/chunker/dist/markdown-chunker.d.ts +14 -0
  42. package/packages/chunker/dist/markdown-chunker.js +122 -0
  43. package/packages/chunker/dist/treesitter-chunker.d.ts +47 -0
  44. package/packages/chunker/dist/treesitter-chunker.js +234 -0
  45. package/packages/cli/dist/commands/analyze.d.ts +3 -0
  46. package/packages/cli/dist/commands/analyze.js +112 -0
  47. package/packages/cli/dist/commands/context-cmds.d.ts +3 -0
  48. package/packages/cli/dist/commands/context-cmds.js +155 -0
  49. package/packages/cli/dist/commands/environment.d.ts +3 -0
  50. package/packages/cli/dist/commands/environment.js +204 -0
  51. package/packages/cli/dist/commands/execution.d.ts +3 -0
  52. package/packages/cli/dist/commands/execution.js +137 -0
  53. package/packages/cli/dist/commands/graph.d.ts +3 -0
  54. package/packages/cli/dist/commands/graph.js +81 -0
  55. package/packages/cli/dist/commands/init.d.ts +8 -0
  56. package/packages/cli/dist/commands/init.js +87 -0
  57. package/packages/cli/dist/commands/knowledge.d.ts +3 -0
  58. package/packages/cli/dist/commands/knowledge.js +139 -0
  59. package/packages/cli/dist/commands/search.d.ts +3 -0
  60. package/packages/cli/dist/commands/search.js +267 -0
  61. package/packages/cli/dist/commands/system.d.ts +3 -0
  62. package/packages/cli/dist/commands/system.js +241 -0
  63. package/packages/cli/dist/commands/workspace.d.ts +3 -0
  64. package/packages/cli/dist/commands/workspace.js +388 -0
  65. package/packages/cli/dist/context.d.ts +5 -0
  66. package/packages/cli/dist/context.js +14 -0
  67. package/packages/cli/dist/helpers.d.ts +52 -0
  68. package/packages/cli/dist/helpers.js +458 -0
  69. package/packages/cli/dist/index.d.ts +8 -0
  70. package/packages/cli/dist/index.js +69 -0
  71. package/packages/cli/dist/kb-init.d.ts +57 -0
  72. package/packages/cli/dist/kb-init.js +82 -0
  73. package/packages/cli/dist/types.d.ts +7 -0
  74. package/packages/cli/dist/types.js +1 -0
  75. package/packages/core/dist/constants.d.ts +49 -0
  76. package/packages/core/dist/constants.js +43 -0
  77. package/packages/core/dist/content-detector.d.ts +9 -0
  78. package/packages/core/dist/content-detector.js +79 -0
  79. package/packages/core/dist/errors.d.ts +18 -0
  80. package/packages/core/dist/errors.js +40 -0
  81. package/packages/core/dist/index.d.ts +6 -0
  82. package/packages/core/dist/index.js +9 -0
  83. package/packages/core/dist/logger.d.ts +9 -0
  84. package/packages/core/dist/logger.js +34 -0
  85. package/packages/core/dist/types.d.ts +108 -0
  86. package/packages/core/dist/types.js +1 -0
  87. package/packages/embeddings/dist/embedder.interface.d.ts +24 -0
  88. package/packages/embeddings/dist/embedder.interface.js +1 -0
  89. package/packages/embeddings/dist/index.d.ts +3 -0
  90. package/packages/embeddings/dist/index.js +5 -0
  91. package/packages/embeddings/dist/onnx-embedder.d.ts +24 -0
  92. package/packages/embeddings/dist/onnx-embedder.js +82 -0
  93. package/packages/indexer/dist/file-hasher.d.ts +11 -0
  94. package/packages/indexer/dist/file-hasher.js +13 -0
  95. package/packages/indexer/dist/filesystem-crawler.d.ts +27 -0
  96. package/packages/indexer/dist/filesystem-crawler.js +125 -0
  97. package/packages/indexer/dist/graph-extractor.d.ts +22 -0
  98. package/packages/indexer/dist/graph-extractor.js +111 -0
  99. package/packages/indexer/dist/incremental-indexer.d.ts +47 -0
  100. package/packages/indexer/dist/incremental-indexer.js +278 -0
  101. package/packages/indexer/dist/index.d.ts +5 -0
  102. package/packages/indexer/dist/index.js +14 -0
  103. package/packages/server/dist/api.d.ts +8 -0
  104. package/packages/server/dist/api.js +9 -0
  105. package/packages/server/dist/config.d.ts +3 -0
  106. package/packages/server/dist/config.js +75 -0
  107. package/packages/server/dist/curated-manager.d.ts +86 -0
  108. package/packages/server/dist/curated-manager.js +357 -0
  109. package/packages/server/dist/index.d.ts +2 -0
  110. package/packages/server/dist/index.js +134 -0
  111. package/packages/server/dist/replay-interceptor.d.ts +11 -0
  112. package/packages/server/dist/replay-interceptor.js +38 -0
  113. package/packages/server/dist/resources/resources.d.ts +4 -0
  114. package/packages/server/dist/resources/resources.js +40 -0
  115. package/packages/server/dist/server.d.ts +21 -0
  116. package/packages/server/dist/server.js +247 -0
  117. package/packages/server/dist/tools/analyze.tools.d.ts +11 -0
  118. package/packages/server/dist/tools/analyze.tools.js +288 -0
  119. package/packages/server/dist/tools/forge.tools.d.ts +12 -0
  120. package/packages/server/dist/tools/forge.tools.js +501 -0
  121. package/packages/server/dist/tools/forget.tool.d.ts +4 -0
  122. package/packages/server/dist/tools/forget.tool.js +43 -0
  123. package/packages/server/dist/tools/graph.tool.d.ts +4 -0
  124. package/packages/server/dist/tools/graph.tool.js +110 -0
  125. package/packages/server/dist/tools/list.tool.d.ts +4 -0
  126. package/packages/server/dist/tools/list.tool.js +56 -0
  127. package/packages/server/dist/tools/lookup.tool.d.ts +4 -0
  128. package/packages/server/dist/tools/lookup.tool.js +53 -0
  129. package/packages/server/dist/tools/onboard.tool.d.ts +5 -0
  130. package/packages/server/dist/tools/onboard.tool.js +112 -0
  131. package/packages/server/dist/tools/produce.tool.d.ts +3 -0
  132. package/packages/server/dist/tools/produce.tool.js +74 -0
  133. package/packages/server/dist/tools/read.tool.d.ts +4 -0
  134. package/packages/server/dist/tools/read.tool.js +49 -0
  135. package/packages/server/dist/tools/reindex.tool.d.ts +7 -0
  136. package/packages/server/dist/tools/reindex.tool.js +70 -0
  137. package/packages/server/dist/tools/remember.tool.d.ts +4 -0
  138. package/packages/server/dist/tools/remember.tool.js +45 -0
  139. package/packages/server/dist/tools/replay.tool.d.ts +3 -0
  140. package/packages/server/dist/tools/replay.tool.js +89 -0
  141. package/packages/server/dist/tools/search.tool.d.ts +5 -0
  142. package/packages/server/dist/tools/search.tool.js +331 -0
  143. package/packages/server/dist/tools/status.tool.d.ts +4 -0
  144. package/packages/server/dist/tools/status.tool.js +68 -0
  145. package/packages/server/dist/tools/toolkit.tools.d.ts +35 -0
  146. package/packages/server/dist/tools/toolkit.tools.js +1674 -0
  147. package/packages/server/dist/tools/update.tool.d.ts +4 -0
  148. package/packages/server/dist/tools/update.tool.js +42 -0
  149. package/packages/server/dist/tools/utility.tools.d.ts +15 -0
  150. package/packages/server/dist/tools/utility.tools.js +461 -0
  151. package/packages/store/dist/graph-store.interface.d.ts +104 -0
  152. package/packages/store/dist/graph-store.interface.js +1 -0
  153. package/packages/store/dist/index.d.ts +6 -0
  154. package/packages/store/dist/index.js +9 -0
  155. package/packages/store/dist/lance-store.d.ts +32 -0
  156. package/packages/store/dist/lance-store.js +258 -0
  157. package/packages/store/dist/sqlite-graph-store.d.ts +43 -0
  158. package/packages/store/dist/sqlite-graph-store.js +374 -0
  159. package/packages/store/dist/store-factory.d.ts +9 -0
  160. package/packages/store/dist/store-factory.js +14 -0
  161. package/packages/store/dist/store.interface.d.ts +48 -0
  162. package/packages/store/dist/store.interface.js +1 -0
  163. package/packages/tools/dist/batch.d.ts +21 -0
  164. package/packages/tools/dist/batch.js +45 -0
  165. package/packages/tools/dist/changelog.d.ts +34 -0
  166. package/packages/tools/dist/changelog.js +112 -0
  167. package/packages/tools/dist/check.d.ts +26 -0
  168. package/packages/tools/dist/check.js +59 -0
  169. package/packages/tools/dist/checkpoint.d.ts +17 -0
  170. package/packages/tools/dist/checkpoint.js +43 -0
  171. package/packages/tools/dist/codemod.d.ts +37 -0
  172. package/packages/tools/dist/codemod.js +69 -0
  173. package/packages/tools/dist/compact.d.ts +41 -0
  174. package/packages/tools/dist/compact.js +60 -0
  175. package/packages/tools/dist/data-transform.d.ts +10 -0
  176. package/packages/tools/dist/data-transform.js +124 -0
  177. package/packages/tools/dist/dead-symbols.d.ts +21 -0
  178. package/packages/tools/dist/dead-symbols.js +71 -0
  179. package/packages/tools/dist/delegate.d.ts +34 -0
  180. package/packages/tools/dist/delegate.js +130 -0
  181. package/packages/tools/dist/diff-parse.d.ts +26 -0
  182. package/packages/tools/dist/diff-parse.js +153 -0
  183. package/packages/tools/dist/digest.d.ts +53 -0
  184. package/packages/tools/dist/digest.js +242 -0
  185. package/packages/tools/dist/encode.d.ts +14 -0
  186. package/packages/tools/dist/encode.js +46 -0
  187. package/packages/tools/dist/env-info.d.ts +28 -0
  188. package/packages/tools/dist/env-info.js +58 -0
  189. package/packages/tools/dist/eval.d.ts +13 -0
  190. package/packages/tools/dist/eval.js +79 -0
  191. package/packages/tools/dist/evidence-map.d.ts +79 -0
  192. package/packages/tools/dist/evidence-map.js +203 -0
  193. package/packages/tools/dist/file-summary.d.ts +32 -0
  194. package/packages/tools/dist/file-summary.js +106 -0
  195. package/packages/tools/dist/file-walk.d.ts +4 -0
  196. package/packages/tools/dist/file-walk.js +75 -0
  197. package/packages/tools/dist/find-examples.d.ts +25 -0
  198. package/packages/tools/dist/find-examples.js +48 -0
  199. package/packages/tools/dist/find.d.ts +47 -0
  200. package/packages/tools/dist/find.js +120 -0
  201. package/packages/tools/dist/forge-classify.d.ts +44 -0
  202. package/packages/tools/dist/forge-classify.js +319 -0
  203. package/packages/tools/dist/forge-ground.d.ts +64 -0
  204. package/packages/tools/dist/forge-ground.js +184 -0
  205. package/packages/tools/dist/git-context.d.ts +22 -0
  206. package/packages/tools/dist/git-context.js +46 -0
  207. package/packages/tools/dist/graph-query.d.ts +89 -0
  208. package/packages/tools/dist/graph-query.js +194 -0
  209. package/packages/tools/dist/health.d.ts +14 -0
  210. package/packages/tools/dist/health.js +118 -0
  211. package/packages/tools/dist/http-request.d.ts +23 -0
  212. package/packages/tools/dist/http-request.js +58 -0
  213. package/packages/tools/dist/index.d.ts +49 -0
  214. package/packages/tools/dist/index.js +273 -0
  215. package/packages/tools/dist/lane.d.ts +39 -0
  216. package/packages/tools/dist/lane.js +227 -0
  217. package/packages/tools/dist/measure.d.ts +38 -0
  218. package/packages/tools/dist/measure.js +119 -0
  219. package/packages/tools/dist/onboard.d.ts +41 -0
  220. package/packages/tools/dist/onboard.js +1139 -0
  221. package/packages/tools/dist/parse-output.d.ts +80 -0
  222. package/packages/tools/dist/parse-output.js +158 -0
  223. package/packages/tools/dist/process-manager.d.ts +18 -0
  224. package/packages/tools/dist/process-manager.js +69 -0
  225. package/packages/tools/dist/queue.d.ts +38 -0
  226. package/packages/tools/dist/queue.js +126 -0
  227. package/packages/tools/dist/regex-test.d.ts +31 -0
  228. package/packages/tools/dist/regex-test.js +39 -0
  229. package/packages/tools/dist/rename.d.ts +29 -0
  230. package/packages/tools/dist/rename.js +70 -0
  231. package/packages/tools/dist/replay.d.ts +56 -0
  232. package/packages/tools/dist/replay.js +108 -0
  233. package/packages/tools/dist/schema-validate.d.ts +23 -0
  234. package/packages/tools/dist/schema-validate.js +141 -0
  235. package/packages/tools/dist/scope-map.d.ts +52 -0
  236. package/packages/tools/dist/scope-map.js +72 -0
  237. package/packages/tools/dist/snippet.d.ts +34 -0
  238. package/packages/tools/dist/snippet.js +80 -0
  239. package/packages/tools/dist/stash.d.ts +12 -0
  240. package/packages/tools/dist/stash.js +60 -0
  241. package/packages/tools/dist/stratum-card.d.ts +31 -0
  242. package/packages/tools/dist/stratum-card.js +239 -0
  243. package/packages/tools/dist/symbol.d.ts +28 -0
  244. package/packages/tools/dist/symbol.js +87 -0
  245. package/packages/tools/dist/test-run.d.ts +23 -0
  246. package/packages/tools/dist/test-run.js +55 -0
  247. package/packages/tools/dist/text-utils.d.ts +16 -0
  248. package/packages/tools/dist/text-utils.js +31 -0
  249. package/packages/tools/dist/time-utils.d.ts +18 -0
  250. package/packages/tools/dist/time-utils.js +135 -0
  251. package/packages/tools/dist/trace.d.ts +24 -0
  252. package/packages/tools/dist/trace.js +114 -0
  253. package/packages/tools/dist/truncation.d.ts +22 -0
  254. package/packages/tools/dist/truncation.js +45 -0
  255. package/packages/tools/dist/watch.d.ts +30 -0
  256. package/packages/tools/dist/watch.js +61 -0
  257. package/packages/tools/dist/web-fetch.d.ts +45 -0
  258. package/packages/tools/dist/web-fetch.js +249 -0
  259. package/packages/tools/dist/web-search.d.ts +23 -0
  260. package/packages/tools/dist/web-search.js +46 -0
  261. package/packages/tools/dist/workset.d.ts +45 -0
  262. package/packages/tools/dist/workset.js +77 -0
  263. package/packages/tui/dist/App.d.ts +8 -0
  264. package/packages/tui/dist/App.js +52659 -0
  265. package/packages/tui/dist/index.d.ts +19 -0
  266. package/packages/tui/dist/index.js +54742 -0
  267. package/packages/tui/dist/panels/CuratedPanel.d.ts +8 -0
  268. package/packages/tui/dist/panels/CuratedPanel.js +34452 -0
  269. package/packages/tui/dist/panels/LogPanel.d.ts +3 -0
  270. package/packages/tui/dist/panels/LogPanel.js +51894 -0
  271. package/packages/tui/dist/panels/SearchPanel.d.ts +10 -0
  272. package/packages/tui/dist/panels/SearchPanel.js +34985 -0
  273. package/packages/tui/dist/panels/StatusPanel.d.ts +8 -0
  274. package/packages/tui/dist/panels/StatusPanel.js +34465 -0
  275. package/skills/knowledge-base/SKILL.md +316 -0
@@ -0,0 +1,258 @@
1
+ import { EMBEDDING_DEFAULTS, SEARCH_DEFAULTS, STORE_DEFAULTS } from "@kb/core";
2
+ import { connect, Index } from "@lancedb/lancedb";
3
+ const SAFE_FILTER_VALUE = /^[\w.\-/ ]+$/;
4
+ function sanitizeFilterValue(value, fieldName) {
5
+ if (!SAFE_FILTER_VALUE.test(value)) {
6
+ throw new Error(`Invalid ${fieldName} filter value: contains disallowed characters`);
7
+ }
8
+ return value.replace(/'/g, "''");
9
+ }
10
+ class LanceStore {
11
+ db = null;
12
+ table = null;
13
+ dbPath;
14
+ tableName;
15
+ constructor(options) {
16
+ this.dbPath = options?.path ?? STORE_DEFAULTS.path;
17
+ this.tableName = options?.tableName ?? STORE_DEFAULTS.tableName;
18
+ }
19
+ async initialize() {
20
+ this.db = await connect(this.dbPath);
21
+ const tableNames = await this.db.tableNames();
22
+ if (tableNames.includes(this.tableName)) {
23
+ this.table = await this.db.openTable(this.tableName);
24
+ await this.createFtsIndex();
25
+ }
26
+ }
27
+ async upsert(records, vectors) {
28
+ if (records.length === 0) return;
29
+ if (records.length !== vectors.length) {
30
+ throw new Error(
31
+ `Record count (${records.length}) does not match vector count (${vectors.length})`
32
+ );
33
+ }
34
+ const lanceRecords = records.map(
35
+ (r, i) => ({
36
+ id: r.id,
37
+ vector: Array.from(vectors[i]),
38
+ content: r.content,
39
+ sourcePath: r.sourcePath,
40
+ contentType: r.contentType,
41
+ headingPath: r.headingPath ?? "",
42
+ chunkIndex: r.chunkIndex,
43
+ totalChunks: r.totalChunks,
44
+ startLine: r.startLine,
45
+ endLine: r.endLine,
46
+ fileHash: r.fileHash,
47
+ indexedAt: r.indexedAt,
48
+ origin: r.origin,
49
+ tags: JSON.stringify(r.tags),
50
+ category: r.category ?? "",
51
+ version: r.version
52
+ })
53
+ );
54
+ if (!this.table) {
55
+ try {
56
+ this.table = await this.db?.createTable(this.tableName, lanceRecords) ?? null;
57
+ } catch (err) {
58
+ if (String(err).includes("already exists") && this.db) {
59
+ this.table = await this.db.openTable(this.tableName);
60
+ await this.table.add(lanceRecords);
61
+ } else {
62
+ throw err;
63
+ }
64
+ }
65
+ } else {
66
+ const sourcePaths = [...new Set(records.map((r) => r.sourcePath))];
67
+ for (const sp of sourcePaths) {
68
+ try {
69
+ await this.table.delete(`sourcePath = '${sanitizeFilterValue(sp, "sourcePath")}'`);
70
+ } catch {
71
+ }
72
+ }
73
+ await this.table.add(lanceRecords);
74
+ }
75
+ }
76
+ async search(queryVector, options) {
77
+ if (!this.table) return [];
78
+ const limit = options?.limit ?? SEARCH_DEFAULTS.maxResults;
79
+ const minScore = options?.minScore ?? SEARCH_DEFAULTS.minScore;
80
+ let query = this.table.search(queryVector).limit(limit * 2);
81
+ const filterStr = this.buildFilterString(options);
82
+ if (filterStr) {
83
+ query = query.where(filterStr);
84
+ }
85
+ const results = await query.toArray();
86
+ return results.map((row) => ({
87
+ record: this.fromLanceRecord(row),
88
+ score: 1 - (row._distance ?? 1)
89
+ // LanceDB returns L2 distance; convert to similarity
90
+ })).filter((r) => r.score >= minScore).slice(0, limit);
91
+ }
92
+ async createFtsIndex() {
93
+ if (!this.table) return;
94
+ try {
95
+ await this.table.createIndex("content", { config: Index.fts() });
96
+ console.error("[KB] FTS index created on content column");
97
+ } catch (err) {
98
+ if (!String(err).includes("already exists")) {
99
+ console.error("[KB] FTS index creation failed (non-fatal):", err);
100
+ }
101
+ }
102
+ }
103
+ async ftsSearch(query, options) {
104
+ if (!this.table) return [];
105
+ const limit = options?.limit ?? SEARCH_DEFAULTS.maxResults;
106
+ try {
107
+ let searchQuery = this.table.search(query).limit(limit * 2);
108
+ const filterStr = this.buildFilterString(options);
109
+ if (filterStr) {
110
+ searchQuery = searchQuery.where(filterStr);
111
+ }
112
+ const results = await searchQuery.toArray();
113
+ return results.map((row) => ({
114
+ record: this.fromLanceRecord(row),
115
+ score: row._score ?? row._relevance_score ?? 0
116
+ }));
117
+ } catch (err) {
118
+ console.error("[KB] FTS search failed (non-fatal):", err);
119
+ return [];
120
+ }
121
+ }
122
+ async getById(id) {
123
+ if (!this.table) return null;
124
+ const results = await this.table.query().where(`id = '${sanitizeFilterValue(id, "id")}'`).limit(1).toArray();
125
+ if (results.length === 0) return null;
126
+ return this.fromLanceRecord(results[0]);
127
+ }
128
+ async deleteBySourcePath(sourcePath) {
129
+ if (!this.table) return 0;
130
+ const existing = await this.getBySourcePath(sourcePath);
131
+ if (existing.length === 0) return 0;
132
+ await this.table.delete(`sourcePath = '${sanitizeFilterValue(sourcePath, "sourcePath")}'`);
133
+ return existing.length;
134
+ }
135
+ async deleteById(id) {
136
+ if (!this.table) return false;
137
+ const existing = await this.getById(id);
138
+ if (!existing) return false;
139
+ await this.table.delete(`id = '${sanitizeFilterValue(id, "id")}'`);
140
+ return true;
141
+ }
142
+ async getBySourcePath(sourcePath) {
143
+ if (!this.table) return [];
144
+ const results = await this.table.query().where(`sourcePath = '${sanitizeFilterValue(sourcePath, "sourcePath")}'`).limit(1e3).toArray();
145
+ return results.map((row) => this.fromLanceRecord(row));
146
+ }
147
+ async getStats() {
148
+ if (!this.table) {
149
+ return {
150
+ totalRecords: 0,
151
+ totalFiles: 0,
152
+ contentTypeBreakdown: {},
153
+ lastIndexedAt: null,
154
+ storeBackend: "lancedb",
155
+ embeddingModel: EMBEDDING_DEFAULTS.model
156
+ };
157
+ }
158
+ const allRows = await this.table.countRows();
159
+ const sample = await this.table.query().select(["sourcePath", "contentType", "indexedAt"]).limit(1e5).toArray();
160
+ const breakdown = {};
161
+ const uniquePaths = /* @__PURE__ */ new Set();
162
+ let lastIndexedAt = null;
163
+ for (const row of sample) {
164
+ const r = row;
165
+ breakdown[r.contentType] = (breakdown[r.contentType] ?? 0) + 1;
166
+ uniquePaths.add(r.sourcePath);
167
+ if (!lastIndexedAt || r.indexedAt > lastIndexedAt) {
168
+ lastIndexedAt = r.indexedAt;
169
+ }
170
+ }
171
+ return {
172
+ totalRecords: allRows,
173
+ totalFiles: uniquePaths.size,
174
+ contentTypeBreakdown: breakdown,
175
+ lastIndexedAt,
176
+ storeBackend: "lancedb",
177
+ embeddingModel: EMBEDDING_DEFAULTS.model
178
+ };
179
+ }
180
+ async listSourcePaths() {
181
+ if (!this.table) return [];
182
+ const results = await this.table.query().select(["sourcePath"]).limit(1e5).toArray();
183
+ return [
184
+ // biome-ignore lint/suspicious/noExplicitAny: LanceDB query returns untyped results
185
+ ...new Set(results.map((r) => r.sourcePath))
186
+ ];
187
+ }
188
+ async dropTable() {
189
+ if (this.db) {
190
+ const tableNames = await this.db.tableNames();
191
+ if (tableNames.includes(this.tableName)) {
192
+ const maxRetries = 3;
193
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
194
+ try {
195
+ await this.db.dropTable(this.tableName);
196
+ break;
197
+ } catch (err) {
198
+ if (attempt === maxRetries) throw err;
199
+ const delay = attempt * 500;
200
+ console.error(`[KB] dropTable attempt ${attempt} failed, retrying in ${delay}ms...`);
201
+ await new Promise((r) => setTimeout(r, delay));
202
+ }
203
+ }
204
+ }
205
+ }
206
+ this.table = null;
207
+ }
208
+ async close() {
209
+ try {
210
+ if (this.db && typeof this.db.close === "function") {
211
+ await this.db.close();
212
+ }
213
+ } catch {
214
+ }
215
+ this.table = null;
216
+ this.db = null;
217
+ }
218
+ buildFilterString(options) {
219
+ const filters = [];
220
+ if (options?.contentType) {
221
+ filters.push(`contentType = '${sanitizeFilterValue(options.contentType, "contentType")}'`);
222
+ }
223
+ if (options?.origin) {
224
+ filters.push(`origin = '${sanitizeFilterValue(options.origin, "origin")}'`);
225
+ }
226
+ if (options?.category) {
227
+ filters.push(`category = '${sanitizeFilterValue(options.category, "category")}'`);
228
+ }
229
+ if (options?.tags && options.tags.length > 0) {
230
+ const tagClauses = options.tags.map((t) => `tags LIKE '%${sanitizeFilterValue(t, "tag")}%'`);
231
+ filters.push(`(${tagClauses.join(" OR ")})`);
232
+ }
233
+ return filters.length > 0 ? filters.join(" AND ") : null;
234
+ }
235
+ fromLanceRecord(row) {
236
+ return {
237
+ id: row.id,
238
+ content: row.content,
239
+ sourcePath: row.sourcePath,
240
+ contentType: row.contentType,
241
+ headingPath: row.headingPath || void 0,
242
+ chunkIndex: row.chunkIndex,
243
+ totalChunks: row.totalChunks,
244
+ startLine: row.startLine,
245
+ endLine: row.endLine,
246
+ fileHash: row.fileHash,
247
+ indexedAt: row.indexedAt,
248
+ origin: row.origin,
249
+ tags: JSON.parse(row.tags || "[]"),
250
+ category: row.category || void 0,
251
+ version: row.version
252
+ };
253
+ }
254
+ }
255
+ export {
256
+ LanceStore
257
+ };
258
+ //# sourceMappingURL=lance-store.js.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * SQLite-backed knowledge graph store.
3
+ *
4
+ * Uses better-sqlite3 for a zero-config, embedded, single-file graph database.
5
+ * Stores nodes and edges with JSON properties, supports multi-hop traversal.
6
+ */
7
+ import type { GraphEdge, GraphNode, GraphStats, GraphTraversalOptions, GraphTraversalResult, IGraphStore } from './graph-store.interface.js';
8
+ export declare class SqliteGraphStore implements IGraphStore {
9
+ private db;
10
+ private readonly dbPath;
11
+ constructor(options?: {
12
+ path?: string;
13
+ });
14
+ initialize(): Promise<void>;
15
+ private ensureDb;
16
+ upsertNode(node: GraphNode): Promise<void>;
17
+ upsertEdge(edge: GraphEdge): Promise<void>;
18
+ upsertNodes(nodes: GraphNode[]): Promise<void>;
19
+ upsertEdges(edges: GraphEdge[]): Promise<void>;
20
+ getNode(id: string): Promise<GraphNode | null>;
21
+ getNeighbors(nodeId: string, options?: GraphTraversalOptions): Promise<GraphTraversalResult>;
22
+ traverse(startId: string, options?: GraphTraversalOptions): Promise<GraphTraversalResult>;
23
+ findNodes(filter: {
24
+ type?: string;
25
+ namePattern?: string;
26
+ sourcePath?: string;
27
+ limit?: number;
28
+ }): Promise<GraphNode[]>;
29
+ findEdges(filter: {
30
+ type?: string;
31
+ fromId?: string;
32
+ toId?: string;
33
+ limit?: number;
34
+ }): Promise<GraphEdge[]>;
35
+ deleteNode(id: string): Promise<void>;
36
+ deleteBySourcePath(sourcePath: string): Promise<number>;
37
+ clear(): Promise<void>;
38
+ getStats(): Promise<GraphStats>;
39
+ close(): Promise<void>;
40
+ private toGraphNode;
41
+ private toGraphEdge;
42
+ }
43
+ //# sourceMappingURL=sqlite-graph-store.d.ts.map
@@ -0,0 +1,374 @@
1
+ import { existsSync, mkdirSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ class SqliteGraphStore {
4
+ db = null;
5
+ dbPath;
6
+ constructor(options) {
7
+ const basePath = options?.path ?? ".kb-data";
8
+ this.dbPath = join(basePath, "graph.db");
9
+ }
10
+ async initialize() {
11
+ const dir = dirname(this.dbPath);
12
+ if (!existsSync(dir)) {
13
+ mkdirSync(dir, { recursive: true });
14
+ }
15
+ const BetterSqlite3 = (await import("better-sqlite3")).default;
16
+ this.db = new BetterSqlite3(this.dbPath);
17
+ this.db.pragma("journal_mode = WAL");
18
+ this.db.pragma("foreign_keys = ON");
19
+ this.db.exec(`
20
+ CREATE TABLE IF NOT EXISTS nodes (
21
+ id TEXT PRIMARY KEY,
22
+ type TEXT NOT NULL,
23
+ name TEXT NOT NULL,
24
+ properties TEXT NOT NULL DEFAULT '{}',
25
+ source_record_id TEXT,
26
+ source_path TEXT,
27
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
28
+ );
29
+
30
+ CREATE TABLE IF NOT EXISTS edges (
31
+ id TEXT PRIMARY KEY,
32
+ from_id TEXT NOT NULL,
33
+ to_id TEXT NOT NULL,
34
+ type TEXT NOT NULL,
35
+ weight REAL DEFAULT 1.0,
36
+ properties TEXT NOT NULL DEFAULT '{}',
37
+ FOREIGN KEY (from_id) REFERENCES nodes(id) ON DELETE CASCADE,
38
+ FOREIGN KEY (to_id) REFERENCES nodes(id) ON DELETE CASCADE
39
+ );
40
+
41
+ CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(type);
42
+ CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);
43
+ CREATE INDEX IF NOT EXISTS idx_nodes_source_path ON nodes(source_path);
44
+ CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(from_id);
45
+ CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(to_id);
46
+ CREATE INDEX IF NOT EXISTS idx_edges_type ON edges(type);
47
+ `);
48
+ }
49
+ ensureDb() {
50
+ if (!this.db) throw new Error("Graph store not initialized \u2014 call initialize() first");
51
+ return this.db;
52
+ }
53
+ async upsertNode(node) {
54
+ const db = this.ensureDb();
55
+ db.prepare(`
56
+ INSERT INTO nodes (id, type, name, properties, source_record_id, source_path, created_at)
57
+ VALUES (?, ?, ?, ?, ?, ?, ?)
58
+ ON CONFLICT(id) DO UPDATE SET
59
+ type = excluded.type,
60
+ name = excluded.name,
61
+ properties = excluded.properties,
62
+ source_record_id = excluded.source_record_id,
63
+ source_path = excluded.source_path
64
+ `).run(
65
+ node.id,
66
+ node.type,
67
+ node.name,
68
+ JSON.stringify(node.properties),
69
+ node.sourceRecordId ?? null,
70
+ node.sourcePath ?? null,
71
+ node.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
72
+ );
73
+ }
74
+ async upsertEdge(edge) {
75
+ const db = this.ensureDb();
76
+ db.prepare(`
77
+ INSERT INTO edges (id, from_id, to_id, type, weight, properties)
78
+ VALUES (?, ?, ?, ?, ?, ?)
79
+ ON CONFLICT(id) DO UPDATE SET
80
+ from_id = excluded.from_id,
81
+ to_id = excluded.to_id,
82
+ type = excluded.type,
83
+ weight = excluded.weight,
84
+ properties = excluded.properties
85
+ `).run(
86
+ edge.id,
87
+ edge.fromId,
88
+ edge.toId,
89
+ edge.type,
90
+ edge.weight ?? 1,
91
+ JSON.stringify(edge.properties ?? {})
92
+ );
93
+ }
94
+ async upsertNodes(nodes) {
95
+ if (nodes.length === 0) return;
96
+ const db = this.ensureDb();
97
+ const stmt = db.prepare(`
98
+ INSERT INTO nodes (id, type, name, properties, source_record_id, source_path, created_at)
99
+ VALUES (?, ?, ?, ?, ?, ?, ?)
100
+ ON CONFLICT(id) DO UPDATE SET
101
+ type = excluded.type,
102
+ name = excluded.name,
103
+ properties = excluded.properties,
104
+ source_record_id = excluded.source_record_id,
105
+ source_path = excluded.source_path
106
+ `);
107
+ const tx = db.transaction(() => {
108
+ for (const node of nodes) {
109
+ stmt.run(
110
+ node.id,
111
+ node.type,
112
+ node.name,
113
+ JSON.stringify(node.properties),
114
+ node.sourceRecordId ?? null,
115
+ node.sourcePath ?? null,
116
+ node.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
117
+ );
118
+ }
119
+ });
120
+ tx();
121
+ }
122
+ async upsertEdges(edges) {
123
+ if (edges.length === 0) return;
124
+ const db = this.ensureDb();
125
+ const stmt = db.prepare(`
126
+ INSERT INTO edges (id, from_id, to_id, type, weight, properties)
127
+ VALUES (?, ?, ?, ?, ?, ?)
128
+ ON CONFLICT(id) DO UPDATE SET
129
+ from_id = excluded.from_id,
130
+ to_id = excluded.to_id,
131
+ type = excluded.type,
132
+ weight = excluded.weight,
133
+ properties = excluded.properties
134
+ `);
135
+ const tx = db.transaction(() => {
136
+ for (const edge of edges) {
137
+ stmt.run(
138
+ edge.id,
139
+ edge.fromId,
140
+ edge.toId,
141
+ edge.type,
142
+ edge.weight ?? 1,
143
+ JSON.stringify(edge.properties ?? {})
144
+ );
145
+ }
146
+ });
147
+ tx();
148
+ }
149
+ async getNode(id) {
150
+ const db = this.ensureDb();
151
+ const row = db.prepare("SELECT * FROM nodes WHERE id = ?").get(id);
152
+ return row ? this.toGraphNode(row) : null;
153
+ }
154
+ async getNeighbors(nodeId, options) {
155
+ const db = this.ensureDb();
156
+ const direction = options?.direction ?? "both";
157
+ const edgeType = options?.edgeType;
158
+ const limit = options?.limit ?? 50;
159
+ const nodes = [];
160
+ const edges = [];
161
+ const seenNodeIds = /* @__PURE__ */ new Set();
162
+ const OUTGOING_SQL = `
163
+ SELECT e.id AS edge_id, e.from_id, e.to_id, e.type AS edge_type, e.weight, e.properties AS edge_props,
164
+ n.id AS node_id, n.type AS node_type, n.name AS node_name, n.properties AS node_props,
165
+ n.source_record_id AS node_src_rec, n.source_path AS node_src_path, n.created_at AS node_created
166
+ FROM edges e JOIN nodes n ON e.to_id = n.id WHERE e.from_id = ?`;
167
+ const INCOMING_SQL = `
168
+ SELECT e.id AS edge_id, e.from_id, e.to_id, e.type AS edge_type, e.weight, e.properties AS edge_props,
169
+ n.id AS node_id, n.type AS node_type, n.name AS node_name, n.properties AS node_props,
170
+ n.source_record_id AS node_src_rec, n.source_path AS node_src_path, n.created_at AS node_created
171
+ FROM edges e JOIN nodes n ON e.from_id = n.id WHERE e.to_id = ?`;
172
+ if (direction === "outgoing" || direction === "both") {
173
+ let sql = OUTGOING_SQL;
174
+ const params = [nodeId];
175
+ if (edgeType) {
176
+ sql += " AND e.type = ?";
177
+ params.push(edgeType);
178
+ }
179
+ sql += " LIMIT ?";
180
+ params.push(limit);
181
+ const rows = db.prepare(sql).all(...params);
182
+ for (const row of rows) {
183
+ edges.push(joinRowToEdge(row));
184
+ if (!seenNodeIds.has(row.node_id)) {
185
+ seenNodeIds.add(row.node_id);
186
+ nodes.push(joinRowToNode(row));
187
+ }
188
+ }
189
+ }
190
+ if (direction === "incoming" || direction === "both") {
191
+ let sql = INCOMING_SQL;
192
+ const params = [nodeId];
193
+ if (edgeType) {
194
+ sql += " AND e.type = ?";
195
+ params.push(edgeType);
196
+ }
197
+ sql += " LIMIT ?";
198
+ params.push(limit);
199
+ const rows = db.prepare(sql).all(...params);
200
+ for (const row of rows) {
201
+ edges.push(joinRowToEdge(row));
202
+ if (!seenNodeIds.has(row.node_id)) {
203
+ seenNodeIds.add(row.node_id);
204
+ nodes.push(joinRowToNode(row));
205
+ }
206
+ }
207
+ }
208
+ return { nodes, edges };
209
+ }
210
+ async traverse(startId, options) {
211
+ const maxDepth = options?.maxDepth ?? 2;
212
+ const direction = options?.direction ?? "both";
213
+ const edgeType = options?.edgeType;
214
+ const limit = options?.limit ?? 50;
215
+ const allNodes = /* @__PURE__ */ new Map();
216
+ const allEdges = /* @__PURE__ */ new Map();
217
+ const visited = /* @__PURE__ */ new Set();
218
+ const queue = [{ nodeId: startId, depth: 0 }];
219
+ while (queue.length > 0 && allNodes.size < limit) {
220
+ const current = queue.shift();
221
+ if (!current || visited.has(current.nodeId)) continue;
222
+ if (current.depth > maxDepth) continue;
223
+ visited.add(current.nodeId);
224
+ const neighbors = await this.getNeighbors(current.nodeId, {
225
+ direction,
226
+ edgeType,
227
+ limit: limit - allNodes.size
228
+ });
229
+ for (const node of neighbors.nodes) {
230
+ if (!allNodes.has(node.id)) {
231
+ allNodes.set(node.id, node);
232
+ if (current.depth + 1 < maxDepth) {
233
+ queue.push({ nodeId: node.id, depth: current.depth + 1 });
234
+ }
235
+ }
236
+ }
237
+ for (const edge of neighbors.edges) {
238
+ allEdges.set(edge.id, edge);
239
+ }
240
+ }
241
+ return { nodes: [...allNodes.values()], edges: [...allEdges.values()] };
242
+ }
243
+ async findNodes(filter) {
244
+ const db = this.ensureDb();
245
+ const conditions = [];
246
+ const params = [];
247
+ if (filter.type) {
248
+ conditions.push("type = ?");
249
+ params.push(filter.type);
250
+ }
251
+ if (filter.namePattern) {
252
+ conditions.push("name LIKE ?");
253
+ params.push(`%${filter.namePattern}%`);
254
+ }
255
+ if (filter.sourcePath) {
256
+ conditions.push("source_path = ?");
257
+ params.push(filter.sourcePath);
258
+ }
259
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
260
+ const maxRows = filter.limit ?? 100;
261
+ const rows = db.prepare(`SELECT * FROM nodes ${where} LIMIT ?`).all(...params, maxRows);
262
+ return rows.map((r) => this.toGraphNode(r));
263
+ }
264
+ async findEdges(filter) {
265
+ const db = this.ensureDb();
266
+ const conditions = [];
267
+ const params = [];
268
+ if (filter.type) {
269
+ conditions.push("type = ?");
270
+ params.push(filter.type);
271
+ }
272
+ if (filter.fromId) {
273
+ conditions.push("from_id = ?");
274
+ params.push(filter.fromId);
275
+ }
276
+ if (filter.toId) {
277
+ conditions.push("to_id = ?");
278
+ params.push(filter.toId);
279
+ }
280
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
281
+ const maxRows = filter.limit ?? 100;
282
+ const rows = db.prepare(`SELECT * FROM edges ${where} LIMIT ?`).all(...params, maxRows);
283
+ return rows.map((r) => this.toGraphEdge(r));
284
+ }
285
+ async deleteNode(id) {
286
+ const db = this.ensureDb();
287
+ db.transaction(() => {
288
+ db.prepare("DELETE FROM edges WHERE from_id = ? OR to_id = ?").run(id, id);
289
+ db.prepare("DELETE FROM nodes WHERE id = ?").run(id);
290
+ })();
291
+ }
292
+ async deleteBySourcePath(sourcePath) {
293
+ const db = this.ensureDb();
294
+ const nodeIds = db.prepare("SELECT id FROM nodes WHERE source_path = ?").all(sourcePath);
295
+ if (nodeIds.length === 0) return 0;
296
+ const tx = db.transaction(() => {
297
+ for (const { id } of nodeIds) {
298
+ db.prepare("DELETE FROM edges WHERE from_id = ? OR to_id = ?").run(id, id);
299
+ }
300
+ db.prepare("DELETE FROM nodes WHERE source_path = ?").run(sourcePath);
301
+ });
302
+ tx();
303
+ return nodeIds.length;
304
+ }
305
+ async clear() {
306
+ const db = this.ensureDb();
307
+ db.exec("DELETE FROM edges; DELETE FROM nodes;");
308
+ }
309
+ async getStats() {
310
+ const db = this.ensureDb();
311
+ const nodeCount = db.prepare("SELECT COUNT(*) as count FROM nodes").get().count;
312
+ const edgeCount = db.prepare("SELECT COUNT(*) as count FROM edges").get().count;
313
+ const nodeTypeRows = db.prepare("SELECT type, COUNT(*) as count FROM nodes GROUP BY type").all();
314
+ const nodeTypes = {};
315
+ for (const row of nodeTypeRows) nodeTypes[row.type] = row.count;
316
+ const edgeTypeRows = db.prepare("SELECT type, COUNT(*) as count FROM edges GROUP BY type").all();
317
+ const edgeTypes = {};
318
+ for (const row of edgeTypeRows) edgeTypes[row.type] = row.count;
319
+ return { nodeCount, edgeCount, nodeTypes, edgeTypes };
320
+ }
321
+ async close() {
322
+ if (this.db) {
323
+ this.db.close();
324
+ this.db = null;
325
+ }
326
+ }
327
+ // ── Private helpers ─────────────────────────────────────────────────
328
+ toGraphNode(row) {
329
+ return {
330
+ id: row.id,
331
+ type: row.type,
332
+ name: row.name,
333
+ properties: JSON.parse(row.properties),
334
+ sourceRecordId: row.source_record_id ?? void 0,
335
+ sourcePath: row.source_path ?? void 0,
336
+ createdAt: row.created_at
337
+ };
338
+ }
339
+ toGraphEdge(row) {
340
+ return {
341
+ id: row.id,
342
+ fromId: row.from_id,
343
+ toId: row.to_id,
344
+ type: row.type,
345
+ weight: row.weight ?? 1,
346
+ properties: JSON.parse(row.properties)
347
+ };
348
+ }
349
+ }
350
+ function joinRowToEdge(row) {
351
+ return {
352
+ id: row.edge_id,
353
+ fromId: row.from_id,
354
+ toId: row.to_id,
355
+ type: row.edge_type,
356
+ weight: row.weight ?? 1,
357
+ properties: JSON.parse(row.edge_props ?? "{}")
358
+ };
359
+ }
360
+ function joinRowToNode(row) {
361
+ return {
362
+ id: row.node_id,
363
+ type: row.node_type,
364
+ name: row.node_name,
365
+ properties: JSON.parse(row.node_props ?? "{}"),
366
+ sourceRecordId: row.node_src_rec ?? void 0,
367
+ sourcePath: row.node_src_path ?? void 0,
368
+ createdAt: row.node_created
369
+ };
370
+ }
371
+ export {
372
+ SqliteGraphStore
373
+ };
374
+ //# sourceMappingURL=sqlite-graph-store.js.map
@@ -0,0 +1,9 @@
1
+ import type { IKnowledgeStore } from './store.interface.js';
2
+ export type StoreBackend = 'lancedb' | 'sqlite-vec';
3
+ export interface StoreConfig {
4
+ backend: StoreBackend;
5
+ path: string;
6
+ options?: Record<string, unknown>;
7
+ }
8
+ export declare function createStore(config: StoreConfig): Promise<IKnowledgeStore>;
9
+ //# sourceMappingURL=store-factory.d.ts.map
@@ -0,0 +1,14 @@
1
+ async function createStore(config) {
2
+ switch (config.backend) {
3
+ case "lancedb": {
4
+ const { LanceStore } = await import("./lance-store.js");
5
+ return new LanceStore({ path: config.path });
6
+ }
7
+ default:
8
+ throw new Error(`Unknown store backend: "${config.backend}". Supported: lancedb`);
9
+ }
10
+ }
11
+ export {
12
+ createStore
13
+ };
14
+ //# sourceMappingURL=store-factory.js.map