@jafreck/lore 0.2.0 → 0.2.2

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 (180) hide show
  1. package/README.md +399 -199
  2. package/dist/cli.js +139 -18
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.d.ts +7 -6
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +3 -3
  7. package/dist/index.js.map +1 -1
  8. package/dist/indexer/db.d.ts +12 -11
  9. package/dist/indexer/db.d.ts.map +1 -1
  10. package/dist/indexer/db.js +118 -18
  11. package/dist/indexer/db.js.map +1 -1
  12. package/dist/indexer/docs.d.ts +42 -0
  13. package/dist/indexer/docs.d.ts.map +1 -0
  14. package/dist/indexer/docs.js +214 -0
  15. package/dist/indexer/docs.js.map +1 -0
  16. package/dist/indexer/embedder.d.ts +7 -0
  17. package/dist/indexer/embedder.d.ts.map +1 -1
  18. package/dist/indexer/embedder.js +10 -0
  19. package/dist/indexer/embedder.js.map +1 -1
  20. package/dist/indexer/ensure-python-deps.d.ts +1 -1
  21. package/dist/indexer/ensure-python-deps.js +1 -1
  22. package/dist/indexer/extractors/types.d.ts +22 -0
  23. package/dist/indexer/extractors/types.d.ts.map +1 -1
  24. package/dist/indexer/extractors/types.js +12 -0
  25. package/dist/indexer/extractors/types.js.map +1 -1
  26. package/dist/indexer/extractors/typescript.d.ts +1 -1
  27. package/dist/indexer/extractors/typescript.d.ts.map +1 -1
  28. package/dist/indexer/extractors/typescript.js +38 -8
  29. package/dist/indexer/extractors/typescript.js.map +1 -1
  30. package/dist/indexer/git-history.d.ts.map +1 -1
  31. package/dist/indexer/git-history.js +4 -4
  32. package/dist/indexer/git-history.js.map +1 -1
  33. package/dist/indexer/git-hooks.d.ts +1 -0
  34. package/dist/indexer/git-hooks.d.ts.map +1 -1
  35. package/dist/indexer/git-hooks.js +3 -2
  36. package/dist/indexer/git-hooks.js.map +1 -1
  37. package/dist/indexer/index.d.ts +35 -9
  38. package/dist/indexer/index.d.ts.map +1 -1
  39. package/dist/indexer/index.js +470 -30
  40. package/dist/indexer/index.js.map +1 -1
  41. package/dist/indexer/lsp/client.d.ts +61 -0
  42. package/dist/indexer/lsp/client.d.ts.map +1 -0
  43. package/dist/indexer/lsp/client.js +217 -0
  44. package/dist/indexer/lsp/client.js.map +1 -0
  45. package/dist/indexer/lsp/config.d.ts +16 -0
  46. package/dist/indexer/lsp/config.d.ts.map +1 -0
  47. package/dist/indexer/lsp/config.js +78 -0
  48. package/dist/indexer/lsp/config.js.map +1 -0
  49. package/dist/indexer/lsp/enrichment.d.ts +55 -0
  50. package/dist/indexer/lsp/enrichment.d.ts.map +1 -0
  51. package/dist/indexer/lsp/enrichment.js +211 -0
  52. package/dist/indexer/lsp/enrichment.js.map +1 -0
  53. package/dist/indexer/lsp/registry.d.ts +19 -0
  54. package/dist/indexer/lsp/registry.d.ts.map +1 -0
  55. package/dist/indexer/lsp/registry.js +118 -0
  56. package/dist/indexer/lsp/registry.js.map +1 -0
  57. package/dist/indexer/parser.d.ts +7 -0
  58. package/dist/indexer/parser.d.ts.map +1 -1
  59. package/dist/indexer/parser.js +3 -1
  60. package/dist/indexer/parser.js.map +1 -1
  61. package/dist/indexer/poller.d.ts +8 -1
  62. package/dist/indexer/poller.d.ts.map +1 -1
  63. package/dist/indexer/poller.js +7 -1
  64. package/dist/indexer/poller.js.map +1 -1
  65. package/dist/indexer/walker.d.ts +22 -0
  66. package/dist/indexer/walker.d.ts.map +1 -1
  67. package/dist/indexer/walker.js +15 -0
  68. package/dist/indexer/walker.js.map +1 -1
  69. package/dist/indexer/watcher.d.ts +8 -1
  70. package/dist/indexer/watcher.d.ts.map +1 -1
  71. package/dist/indexer/watcher.js +7 -1
  72. package/dist/indexer/watcher.js.map +1 -1
  73. package/dist/{kb-server → lore-server}/db.d.ts +158 -4
  74. package/dist/lore-server/db.d.ts.map +1 -0
  75. package/dist/{kb-server → lore-server}/db.js +457 -26
  76. package/dist/lore-server/db.js.map +1 -0
  77. package/dist/lore-server/server.d.ts +43 -0
  78. package/dist/lore-server/server.d.ts.map +1 -0
  79. package/dist/lore-server/server.js +385 -0
  80. package/dist/lore-server/server.js.map +1 -0
  81. package/dist/{kb-server → lore-server}/tools/annotations.d.ts +2 -2
  82. package/dist/lore-server/tools/annotations.d.ts.map +1 -0
  83. package/dist/{kb-server → lore-server}/tools/annotations.js +2 -2
  84. package/dist/lore-server/tools/annotations.js.map +1 -0
  85. package/dist/{kb-server → lore-server}/tools/architecture.d.ts +9 -2
  86. package/dist/lore-server/tools/architecture.d.ts.map +1 -0
  87. package/dist/{kb-server → lore-server}/tools/architecture.js +37 -2
  88. package/dist/lore-server/tools/architecture.js.map +1 -0
  89. package/dist/lore-server/tools/blame.d.ts +159 -0
  90. package/dist/lore-server/tools/blame.d.ts.map +1 -0
  91. package/dist/lore-server/tools/blame.js +595 -0
  92. package/dist/lore-server/tools/blame.js.map +1 -0
  93. package/dist/{kb-server → lore-server}/tools/coverage.d.ts +2 -2
  94. package/dist/lore-server/tools/coverage.d.ts.map +1 -0
  95. package/dist/{kb-server → lore-server}/tools/coverage.js +2 -2
  96. package/dist/lore-server/tools/coverage.js.map +1 -0
  97. package/dist/lore-server/tools/docs.d.ts +86 -0
  98. package/dist/lore-server/tools/docs.d.ts.map +1 -0
  99. package/dist/lore-server/tools/docs.js +243 -0
  100. package/dist/lore-server/tools/docs.js.map +1 -0
  101. package/dist/{kb-server → lore-server}/tools/graph.d.ts +40 -3
  102. package/dist/lore-server/tools/graph.d.ts.map +1 -0
  103. package/dist/{kb-server → lore-server}/tools/graph.js +122 -11
  104. package/dist/lore-server/tools/graph.js.map +1 -0
  105. package/dist/{kb-server → lore-server}/tools/history.d.ts +13 -6
  106. package/dist/lore-server/tools/history.d.ts.map +1 -0
  107. package/dist/{kb-server → lore-server}/tools/history.js +43 -10
  108. package/dist/lore-server/tools/history.js.map +1 -0
  109. package/dist/lore-server/tools/lookup.d.ts +75 -0
  110. package/dist/lore-server/tools/lookup.d.ts.map +1 -0
  111. package/dist/lore-server/tools/lookup.js +148 -0
  112. package/dist/lore-server/tools/lookup.js.map +1 -0
  113. package/dist/{kb-server → lore-server}/tools/metrics.d.ts +3 -3
  114. package/dist/lore-server/tools/metrics.d.ts.map +1 -0
  115. package/dist/{kb-server → lore-server}/tools/metrics.js +3 -3
  116. package/dist/lore-server/tools/metrics.js.map +1 -0
  117. package/dist/{kb-server → lore-server}/tools/notes.d.ts +16 -16
  118. package/dist/lore-server/tools/notes.d.ts.map +1 -0
  119. package/dist/{kb-server → lore-server}/tools/notes.js +55 -20
  120. package/dist/lore-server/tools/notes.js.map +1 -0
  121. package/dist/{kb-server → lore-server}/tools/routes.d.ts +2 -2
  122. package/dist/lore-server/tools/routes.d.ts.map +1 -0
  123. package/dist/{kb-server → lore-server}/tools/routes.js +2 -2
  124. package/dist/lore-server/tools/routes.js.map +1 -0
  125. package/dist/{kb-server → lore-server}/tools/search.d.ts +57 -13
  126. package/dist/lore-server/tools/search.d.ts.map +1 -0
  127. package/dist/{kb-server → lore-server}/tools/search.js +123 -23
  128. package/dist/lore-server/tools/search.js.map +1 -0
  129. package/dist/{kb-server → lore-server}/tools/snippet.d.ts +20 -4
  130. package/dist/lore-server/tools/snippet.d.ts.map +1 -0
  131. package/dist/lore-server/tools/snippet.js +116 -0
  132. package/dist/lore-server/tools/snippet.js.map +1 -0
  133. package/dist/{kb-server → lore-server}/tools/test-map.d.ts +2 -2
  134. package/dist/lore-server/tools/test-map.d.ts.map +1 -0
  135. package/dist/{kb-server → lore-server}/tools/test-map.js +2 -2
  136. package/dist/lore-server/tools/test-map.js.map +1 -0
  137. package/dist/{kb-server → lore-server}/tools/writeback.d.ts +2 -2
  138. package/dist/lore-server/tools/writeback.d.ts.map +1 -0
  139. package/dist/{kb-server → lore-server}/tools/writeback.js +3 -3
  140. package/dist/lore-server/tools/writeback.js.map +1 -0
  141. package/package.json +14 -14
  142. package/dist/kb-server/db.d.ts.map +0 -1
  143. package/dist/kb-server/db.js.map +0 -1
  144. package/dist/kb-server/server.d.ts +0 -42
  145. package/dist/kb-server/server.d.ts.map +0 -1
  146. package/dist/kb-server/server.js +0 -241
  147. package/dist/kb-server/server.js.map +0 -1
  148. package/dist/kb-server/tools/annotations.d.ts.map +0 -1
  149. package/dist/kb-server/tools/annotations.js.map +0 -1
  150. package/dist/kb-server/tools/architecture.d.ts.map +0 -1
  151. package/dist/kb-server/tools/architecture.js.map +0 -1
  152. package/dist/kb-server/tools/blame.d.ts +0 -67
  153. package/dist/kb-server/tools/blame.d.ts.map +0 -1
  154. package/dist/kb-server/tools/blame.js +0 -162
  155. package/dist/kb-server/tools/blame.js.map +0 -1
  156. package/dist/kb-server/tools/coverage.d.ts.map +0 -1
  157. package/dist/kb-server/tools/coverage.js.map +0 -1
  158. package/dist/kb-server/tools/graph.d.ts.map +0 -1
  159. package/dist/kb-server/tools/graph.js.map +0 -1
  160. package/dist/kb-server/tools/history.d.ts.map +0 -1
  161. package/dist/kb-server/tools/history.js.map +0 -1
  162. package/dist/kb-server/tools/lookup.d.ts +0 -36
  163. package/dist/kb-server/tools/lookup.d.ts.map +0 -1
  164. package/dist/kb-server/tools/lookup.js +0 -45
  165. package/dist/kb-server/tools/lookup.js.map +0 -1
  166. package/dist/kb-server/tools/metrics.d.ts.map +0 -1
  167. package/dist/kb-server/tools/metrics.js.map +0 -1
  168. package/dist/kb-server/tools/notes.d.ts.map +0 -1
  169. package/dist/kb-server/tools/notes.js.map +0 -1
  170. package/dist/kb-server/tools/routes.d.ts.map +0 -1
  171. package/dist/kb-server/tools/routes.js.map +0 -1
  172. package/dist/kb-server/tools/search.d.ts.map +0 -1
  173. package/dist/kb-server/tools/search.js.map +0 -1
  174. package/dist/kb-server/tools/snippet.d.ts.map +0 -1
  175. package/dist/kb-server/tools/snippet.js +0 -49
  176. package/dist/kb-server/tools/snippet.js.map +0 -1
  177. package/dist/kb-server/tools/test-map.d.ts.map +0 -1
  178. package/dist/kb-server/tools/test-map.js.map +0 -1
  179. package/dist/kb-server/tools/writeback.d.ts.map +0 -1
  180. package/dist/kb-server/tools/writeback.js.map +0 -1
@@ -9,14 +9,18 @@
9
9
  */
10
10
  import * as fs from 'node:fs';
11
11
  import * as crypto from 'node:crypto';
12
+ import * as path from 'node:path';
12
13
  import { execFileSync } from 'node:child_process';
13
- import { openDb, setKbMeta, getKbMeta, createVec0Tables, KB_META_INDEX_CHECKPOINT, KB_META_LAST_HEAD_SHA, KB_META_COVERAGE_LAST_SOURCE_PATH, KB_META_COVERAGE_LAST_SOURCE_MTIME, } from './db.js';
14
+ import { openDb, setLoreMeta, getLoreMeta, createVec0Tables, LORE_META_INDEX_CHECKPOINT, LORE_META_LAST_HEAD_SHA, LORE_META_COVERAGE_LAST_SOURCE_PATH, LORE_META_COVERAGE_LAST_SOURCE_MTIME, } from './db.js';
14
15
  import { walkFiles } from './walker.js';
15
16
  import { detectLanguageForPath } from './walker.js';
17
+ import { walkDocumentationFiles } from './walker.js';
18
+ import { inferSeededDocNoteKey, buildDocNoteScope } from './docs.js';
16
19
  import { ingestGitHistory } from './git-history.js';
17
20
  import { ParserPool } from './parser.js';
18
21
  import { ImportResolver } from './resolver.js';
19
22
  import { buildCallGraph } from './call-graph.js';
23
+ import { isPublicDeclarationSurfaceSymbol, } from './extractors/types.js';
20
24
  import { CExtractor } from './extractors/c.js';
21
25
  import { RustExtractor } from './extractors/rust.js';
22
26
  import { PythonExtractor } from './extractors/python.js';
@@ -41,9 +45,10 @@ import { HaskellExtractor } from './extractors/haskell.js';
41
45
  import { JuliaExtractor } from './extractors/julia.js';
42
46
  import { ElmExtractor } from './extractors/elm.js';
43
47
  import { ObjcExtractor } from './extractors/objc.js';
44
- import { DEFAULT_EMBEDDING_MODEL } from './embedder.js';
48
+ import { DEFAULT_EMBEDDING_MODEL, buildStructuralEmbeddingText } from './embedder.js';
45
49
  import { ingestCoverageReport } from './coverage.js';
46
50
  import { refreshTestMappings } from './test-mapper.js';
51
+ import { LspEnrichmentCoordinator } from './lsp/enrichment.js';
47
52
  // ─── Extractor registry ───────────────────────────────────────────────────────
48
53
  const EXTRACTORS = {
49
54
  c: new CExtractor(),
@@ -79,7 +84,7 @@ const EMBED_BATCH_SIZE = 64;
79
84
  *
80
85
  * @example
81
86
  * ```ts
82
- * const builder = new IndexBuilder('/path/to/kb.db', { rootDir: '/path/to/src' });
87
+ * const builder = new IndexBuilder('/path/to/lore.db', { rootDir: '/path/to/src' });
83
88
  * await builder.build();
84
89
  * ```
85
90
  */
@@ -90,7 +95,10 @@ export class IndexBuilder {
90
95
  resolver;
91
96
  embedder;
92
97
  history;
98
+ indexDependencies;
93
99
  embeddingModel;
100
+ docsAutoNotes;
101
+ lspSettings;
94
102
  constructor(dbPath, walkerConfig, embedder, embeddingModelOrOptions) {
95
103
  this.dbPath = dbPath;
96
104
  this.walkerConfig = walkerConfig;
@@ -108,6 +116,9 @@ export class IndexBuilder {
108
116
  this.embedder = null;
109
117
  }
110
118
  this.history = opts.history ?? false;
119
+ this.docsAutoNotes = opts.docsAutoNotes ?? true;
120
+ this.indexDependencies = opts.indexDependencies ?? false;
121
+ this.lspSettings = opts.lsp ?? null;
111
122
  }
112
123
  // ─── Public API ──────────────────────────────────────────────────────────
113
124
  /**
@@ -118,8 +129,17 @@ export class IndexBuilder {
118
129
  async build() {
119
130
  const db = openDb(this.dbPath);
120
131
  const branch = this.resolveBranch();
132
+ const lspCoordinator = this.createLspEnrichmentCoordinator();
121
133
  try {
134
+ this.saveDocsAutoNotesSetting(db);
122
135
  const files = await walkFiles(this.walkerConfig);
136
+ const docs = await walkDocumentationFiles(this.walkerConfig);
137
+ if (lspCoordinator) {
138
+ const languages = new Set(files.map((file) => file.language));
139
+ if (this.indexDependencies)
140
+ languages.add('typescript');
141
+ await lspCoordinator.start(languages);
142
+ }
123
143
  const resumeAt = this.loadBuildCheckpoint(db, branch, files.length);
124
144
  db.transaction(() => {
125
145
  for (let i = resumeAt; i < files.length; i++) {
@@ -129,22 +149,38 @@ export class IndexBuilder {
129
149
  this.processFile(db, file.path, file.language, branch);
130
150
  this.saveBuildCheckpoint(db, branch, i + 1, files.length);
131
151
  }
152
+ const seenDocPaths = new Set();
153
+ for (const doc of docs) {
154
+ seenDocPaths.add(doc.path);
155
+ this.processDocumentationFile(db, doc, branch);
156
+ this.upsertSeededDocumentationNote(db, doc, branch);
157
+ }
158
+ this.removeStaleDocumentation(db, branch, seenDocPaths);
132
159
  })();
133
160
  this.saveBuildCheckpoint(db, branch, files.length, files.length);
134
161
  this.resolveImports(db, branch);
162
+ await this.indexDependencyDeclarations(db, lspCoordinator);
163
+ await this.enrichProjectSymbolsAndCallRefs(db, branch, files, lspCoordinator);
135
164
  refreshTestMappings(db, branch);
136
165
  buildCallGraph(db);
137
166
  this.saveLastKnownHead(db);
138
167
  if (this.embedder) {
139
168
  await this.embedder.init();
140
169
  await this.embedStructural(db);
170
+ await this.embedDocumentation(db);
141
171
  }
142
172
  if (this.history) {
143
173
  const historyOptions = typeof this.history === 'object' ? this.history : undefined;
144
174
  await ingestGitHistory(db, this.walkerConfig.rootDir, historyOptions);
175
+ if (this.embedder) {
176
+ await this.embedCommitMessages(db);
177
+ }
145
178
  }
146
179
  }
147
180
  finally {
181
+ if (lspCoordinator) {
182
+ await lspCoordinator.dispose();
183
+ }
148
184
  db.close();
149
185
  }
150
186
  }
@@ -157,7 +193,25 @@ export class IndexBuilder {
157
193
  async update(changedFiles) {
158
194
  const db = openDb(this.dbPath);
159
195
  const branch = this.resolveBranch();
196
+ const lspCoordinator = this.createLspEnrichmentCoordinator();
197
+ const enrichedFiles = [];
160
198
  try {
199
+ this.saveDocsAutoNotesSetting(db);
200
+ const docs = await walkDocumentationFiles(this.walkerConfig);
201
+ const docsByPath = new Map(docs.map(doc => [doc.path, doc]));
202
+ if (lspCoordinator) {
203
+ const languages = new Set();
204
+ for (const filePath of changedFiles) {
205
+ if (!fs.existsSync(filePath))
206
+ continue;
207
+ const language = detectLanguageForPath(filePath, this.walkerConfig);
208
+ if (language)
209
+ languages.add(language);
210
+ }
211
+ if (this.indexDependencies)
212
+ languages.add('typescript');
213
+ await lspCoordinator.start(languages);
214
+ }
161
215
  db.transaction(() => {
162
216
  for (const filePath of changedFiles) {
163
217
  // If the file no longer exists, remove it from the DB
@@ -169,23 +223,35 @@ export class IndexBuilder {
169
223
  db.prepare('DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_id = ?)').run(row.id);
170
224
  db.prepare('DELETE FROM files WHERE id = ?').run(row.id);
171
225
  }
226
+ this.deleteDocumentationByPath(db, filePath, branch);
172
227
  continue;
173
228
  }
174
229
  const language = detectLanguageForPath(filePath, this.walkerConfig);
175
- if (!language)
176
- continue;
177
- // Null out resolved_id references pointing to this file before deletion
178
- const existingRow = db.prepare('SELECT id FROM files WHERE path = ? AND branch = ?').get(filePath, branch);
179
- if (existingRow) {
180
- db.prepare('UPDATE file_imports SET resolved_id = NULL WHERE resolved_id = ?').run(existingRow.id);
181
- db.prepare('DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_id = ?)').run(existingRow.id);
230
+ if (language) {
231
+ enrichedFiles.push({ path: filePath, language });
232
+ // Null out resolved_id references pointing to this file before deletion
233
+ const existingRow = db.prepare('SELECT id FROM files WHERE path = ? AND branch = ?').get(filePath, branch);
234
+ if (existingRow) {
235
+ db.prepare('UPDATE file_imports SET resolved_id = NULL WHERE resolved_id = ?').run(existingRow.id);
236
+ db.prepare('DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_id = ?)').run(existingRow.id);
237
+ }
238
+ // Delete existing rows for this file (cascade handles symbols/imports)
239
+ db.prepare('DELETE FROM files WHERE path = ? AND branch = ?').run(filePath, branch);
240
+ this.processFile(db, filePath, language, branch);
241
+ }
242
+ const changedDoc = docsByPath.get(filePath);
243
+ if (changedDoc) {
244
+ this.processDocumentationFile(db, changedDoc, branch);
245
+ this.upsertSeededDocumentationNote(db, changedDoc, branch);
246
+ }
247
+ else {
248
+ this.deleteDocumentationByPath(db, filePath, branch);
182
249
  }
183
- // Delete existing rows for this file (cascade handles symbols/imports)
184
- db.prepare('DELETE FROM files WHERE path = ? AND branch = ?').run(filePath, branch);
185
- this.processFile(db, filePath, language, branch);
186
250
  }
187
251
  })();
188
252
  this.resolveImports(db, branch);
253
+ await this.indexDependencyDeclarations(db, lspCoordinator);
254
+ await this.enrichProjectSymbolsAndCallRefs(db, branch, enrichedFiles, lspCoordinator);
189
255
  refreshTestMappings(db, branch);
190
256
  if (this.history) {
191
257
  const historyOptions = typeof this.history === 'object' ? this.history : undefined;
@@ -194,11 +260,18 @@ export class IndexBuilder {
194
260
  if (this.embedder) {
195
261
  await this.embedder.init();
196
262
  await this.embedStructural(db);
263
+ await this.embedDocumentation(db);
264
+ if (this.history) {
265
+ await this.embedCommitMessages(db);
266
+ }
197
267
  }
198
268
  buildCallGraph(db);
199
269
  this.saveLastKnownHead(db);
200
270
  }
201
271
  finally {
272
+ if (lspCoordinator) {
273
+ await lspCoordinator.dispose();
274
+ }
202
275
  db.close();
203
276
  }
204
277
  }
@@ -238,8 +311,8 @@ export class IndexBuilder {
238
311
  commitSha: resolvedCommitSha,
239
312
  sourceMtime,
240
313
  });
241
- setKbMeta(db, KB_META_COVERAGE_LAST_SOURCE_PATH, reportPath);
242
- setKbMeta(db, KB_META_COVERAGE_LAST_SOURCE_MTIME, String(sourceMtime));
314
+ setLoreMeta(db, LORE_META_COVERAGE_LAST_SOURCE_PATH, reportPath);
315
+ setLoreMeta(db, LORE_META_COVERAGE_LAST_SOURCE_MTIME, String(sourceMtime));
243
316
  }
244
317
  finally {
245
318
  db.close();
@@ -264,8 +337,8 @@ export class IndexBuilder {
264
337
  // Upsert the file row
265
338
  let fileId;
266
339
  if (existing) {
267
- db.prepare(`UPDATE files SET language = ?, size_bytes = ?, last_hash = ?, indexed_at = unixepoch()
268
- WHERE id = ?`).run(language, sizeBytes, hash, existing.id);
340
+ db.prepare(`UPDATE files SET language = ?, size_bytes = ?, last_hash = ?, source = ?, indexed_at = unixepoch()
341
+ WHERE id = ?`).run(language, sizeBytes, hash, source, existing.id);
269
342
  fileId = existing.id;
270
343
  // Remove stale symbols / imports / external deps (also clean up FTS5 index)
271
344
  db.prepare(`DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_id = ?)`).run(fileId);
@@ -276,9 +349,9 @@ export class IndexBuilder {
276
349
  }
277
350
  else {
278
351
  const info = db
279
- .prepare(`INSERT INTO files (path, branch, language, size_bytes, last_hash)
280
- VALUES (?, ?, ?, ?, ?)`)
281
- .run(filePath, branch, language, sizeBytes, hash);
352
+ .prepare(`INSERT INTO files (path, branch, language, size_bytes, last_hash, source)
353
+ VALUES (?, ?, ?, ?, ?, ?)`)
354
+ .run(filePath, branch, language, sizeBytes, hash, source);
282
355
  fileId = Number(info.lastInsertRowid);
283
356
  }
284
357
  // Parse the source
@@ -299,7 +372,10 @@ export class IndexBuilder {
299
372
  const info = insertSymbol.run(fileId, sym.name, sym.kind, sym.startLine, sym.endLine, sym.signature ?? null, sym.docComment ?? null);
300
373
  const symId = Number(info.lastInsertRowid);
301
374
  symbolIdMap.set(sym.name, symId);
302
- insertFts.run(symId, sym.name, sym.signature ?? '', sym.kind);
375
+ insertFts.run(symId, sym.name, buildStructuralEmbeddingText({
376
+ name: sym.name,
377
+ signature: sym.signature ?? null,
378
+ }), sym.kind);
303
379
  }
304
380
  const insertRoute = db.prepare(`INSERT INTO api_routes (file_id, method, path, handler_id, handler_name, framework, line, middleware)
305
381
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
@@ -321,6 +397,95 @@ export class IndexBuilder {
321
397
  }
322
398
  }
323
399
  }
400
+ processDocumentationFile(db, doc, branch) {
401
+ const existing = db.prepare('SELECT id, content_hash FROM docs WHERE path = ? AND branch = ?').get(doc.path, branch);
402
+ if (existing?.content_hash === doc.hash) {
403
+ return;
404
+ }
405
+ let docId;
406
+ if (existing) {
407
+ db.prepare(`UPDATE docs
408
+ SET kind = ?, title = ?, content = ?, content_hash = ?, indexed_at = unixepoch()
409
+ WHERE id = ?`).run(doc.kind, doc.title, doc.content, doc.hash, existing.id);
410
+ docId = existing.id;
411
+ }
412
+ else {
413
+ const info = db.prepare(`INSERT INTO docs (path, branch, kind, title, content, content_hash)
414
+ VALUES (?, ?, ?, ?, ?, ?)`).run(doc.path, branch, doc.kind, doc.title, doc.content, doc.hash);
415
+ docId = Number(info.lastInsertRowid);
416
+ }
417
+ const existingSections = db.prepare('SELECT id, section_index FROM doc_sections WHERE doc_id = ?').all(docId);
418
+ const insertSection = db.prepare(`INSERT INTO doc_sections (
419
+ doc_id, section_index, title, depth, heading_path, line_start, line_end, content, content_hash
420
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
421
+ ON CONFLICT(doc_id, section_index) DO UPDATE SET
422
+ title = excluded.title,
423
+ depth = excluded.depth,
424
+ heading_path = excluded.heading_path,
425
+ line_start = excluded.line_start,
426
+ line_end = excluded.line_end,
427
+ content = excluded.content,
428
+ content_hash = excluded.content_hash`);
429
+ const activeSectionIndexes = new Set();
430
+ for (const chunk of doc.chunks) {
431
+ activeSectionIndexes.add(chunk.sectionIndex);
432
+ insertSection.run(docId, chunk.sectionIndex, chunk.title, chunk.depth, JSON.stringify(chunk.headingPath), chunk.lineStart, chunk.lineEnd, chunk.content, chunk.hash);
433
+ }
434
+ const staleSectionIds = existingSections
435
+ .filter(section => !activeSectionIndexes.has(section.section_index))
436
+ .map(section => section.id);
437
+ this.deleteDocSectionEmbeddings(db, staleSectionIds);
438
+ if (staleSectionIds.length > 0) {
439
+ db.prepare(`DELETE FROM doc_sections
440
+ WHERE id IN (${staleSectionIds.map(() => '?').join(', ')})`).run(...staleSectionIds);
441
+ }
442
+ }
443
+ upsertSeededDocumentationNote(db, doc, branch) {
444
+ if (!this.docsAutoNotes)
445
+ return;
446
+ const key = inferSeededDocNoteKey(doc);
447
+ if (!key)
448
+ return;
449
+ const scope = buildDocNoteScope(doc.path, branch);
450
+ const existing = db.prepare('SELECT content, source_hash FROM notes WHERE key = ? AND scope = ?').get(key, scope);
451
+ if (existing?.content === doc.content && existing.source_hash === doc.hash) {
452
+ return;
453
+ }
454
+ db.prepare(`INSERT INTO notes (key, scope, content, model, source_hash, created_at, updated_at)
455
+ VALUES (?, ?, ?, ?, ?, unixepoch(), unixepoch())
456
+ ON CONFLICT(key, scope) DO UPDATE SET
457
+ content = excluded.content,
458
+ model = excluded.model,
459
+ source_hash = excluded.source_hash,
460
+ updated_at = unixepoch()`).run(key, scope, doc.content, 'system:auto-doc-seed', doc.hash);
461
+ }
462
+ removeStaleDocumentation(db, branch, retainedPaths) {
463
+ const docs = db.prepare('SELECT id, path FROM docs WHERE branch = ?').all(branch);
464
+ for (const doc of docs) {
465
+ if (!retainedPaths.has(doc.path)) {
466
+ this.deleteDocumentationById(db, doc.id);
467
+ }
468
+ }
469
+ }
470
+ deleteDocumentationByPath(db, docPath, branch) {
471
+ const row = db.prepare('SELECT id FROM docs WHERE path = ? AND branch = ?').get(docPath, branch);
472
+ if (!row)
473
+ return;
474
+ this.deleteDocumentationById(db, row.id);
475
+ }
476
+ deleteDocumentationById(db, docId) {
477
+ const sectionIds = db.prepare('SELECT id FROM doc_sections WHERE doc_id = ?').all(docId);
478
+ this.deleteDocSectionEmbeddings(db, sectionIds.map(row => row.id));
479
+ db.prepare('DELETE FROM docs WHERE id = ?').run(docId);
480
+ }
481
+ deleteDocSectionEmbeddings(db, sectionIds) {
482
+ if (sectionIds.length === 0)
483
+ return;
484
+ const hasEmbeddingsTable = db.prepare("SELECT 1 AS present FROM sqlite_master WHERE type IN ('table', 'virtual table') AND name = 'doc_section_embeddings'").get();
485
+ if (!hasEmbeddingsTable)
486
+ return;
487
+ db.prepare(`DELETE FROM doc_section_embeddings WHERE rowid IN (${sectionIds.map(() => '?').join(', ')})`).run(...sectionIds);
488
+ }
324
489
  /**
325
490
  * Second pass: resolve raw_import strings to file IDs in the
326
491
  * `file_imports.resolved_id` column. Also populates `external_deps` for
@@ -352,6 +517,146 @@ export class IndexBuilder {
352
517
  }
353
518
  }
354
519
  }
520
+ async indexDependencyDeclarations(db, lspCoordinator) {
521
+ db.prepare('DELETE FROM external_symbols').run();
522
+ if (!this.indexDependencies)
523
+ return;
524
+ const directDependencies = this.loadDirectDependencies();
525
+ if (directDependencies.size === 0)
526
+ return;
527
+ const extractor = EXTRACTORS.typescript;
528
+ if (!extractor)
529
+ return;
530
+ const insertExternalSymbol = db.prepare(`INSERT OR IGNORE INTO external_symbols
531
+ (
532
+ package_name,
533
+ package_version,
534
+ source_ref,
535
+ symbol_name,
536
+ symbol_kind,
537
+ signature,
538
+ doc_comment,
539
+ resolved_type_signature,
540
+ resolved_return_type,
541
+ definition_uri,
542
+ definition_path
543
+ )
544
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
545
+ for (const [packageName, declaredVersion] of directDependencies) {
546
+ const packageDir = path.join(this.walkerConfig.rootDir, 'node_modules', packageName);
547
+ if (!fs.existsSync(packageDir) || !fs.statSync(packageDir).isDirectory())
548
+ continue;
549
+ const packageVersion = this.readInstalledPackageVersion(packageDir) ?? declaredVersion ?? null;
550
+ const declarationFiles = this.collectDeclarationFiles(packageDir);
551
+ for (const declarationFile of declarationFiles) {
552
+ const source = fs.readFileSync(declarationFile, 'utf8');
553
+ const tree = this.pool.parse('typescript', source);
554
+ if (!tree)
555
+ continue;
556
+ const result = extractor.extract(tree, source, declarationFile);
557
+ const declarationSymbols = result.symbols.filter((symbol) => this.shouldIndexDependencySymbol(symbol));
558
+ const enrichmentRows = lspCoordinator
559
+ ? await lspCoordinator.enrich({
560
+ filePath: declarationFile,
561
+ language: 'typescript',
562
+ source,
563
+ targets: declarationSymbols.map((symbol) => ({
564
+ line: symbol.startLine,
565
+ character: symbol.startCharacter ?? 0,
566
+ })),
567
+ })
568
+ : declarationSymbols.map(() => null);
569
+ for (let i = 0; i < declarationSymbols.length; i++) {
570
+ const symbol = declarationSymbols[i];
571
+ if (!symbol)
572
+ continue;
573
+ const metadata = enrichmentRows[i];
574
+ insertExternalSymbol.run(packageName, packageVersion, declarationFile, symbol.name, symbol.kind, symbol.signature, symbol.docComment ?? null, metadata?.resolvedTypeSignature ?? null, metadata?.resolvedReturnType ?? null, metadata?.definitionUri ?? null, metadata?.definitionPath ?? null);
575
+ }
576
+ }
577
+ }
578
+ }
579
+ createLspEnrichmentCoordinator() {
580
+ if (!this.lspSettings?.enabled) {
581
+ return null;
582
+ }
583
+ return new LspEnrichmentCoordinator(this.lspSettings, this.walkerConfig.rootDir);
584
+ }
585
+ async enrichProjectSymbolsAndCallRefs(db, branch, files, lspCoordinator) {
586
+ if (!lspCoordinator || files.length === 0)
587
+ return;
588
+ const selectSymbols = db.prepare(`SELECT s.id, s.name, s.signature, s.start_line
589
+ FROM symbols s
590
+ JOIN files f ON f.id = s.file_id
591
+ WHERE f.path = ? AND f.branch = ?
592
+ ORDER BY s.id`);
593
+ const selectCallRefs = db.prepare(`SELECT sr.id, sr.call_line
594
+ FROM symbol_refs sr
595
+ JOIN symbols s ON s.id = sr.caller_id
596
+ JOIN files f ON f.id = s.file_id
597
+ WHERE f.path = ? AND f.branch = ?
598
+ ORDER BY sr.id`);
599
+ const updateSymbol = db.prepare(`UPDATE symbols
600
+ SET resolved_type_signature = ?, resolved_return_type = ?, definition_uri = ?, definition_path = ?
601
+ WHERE id = ?`);
602
+ const updateSymbolFts = db.prepare('UPDATE symbols_fts SET signature = ? WHERE rowid = ?');
603
+ const updateCallRef = db.prepare(`UPDATE symbol_refs
604
+ SET resolved_type_signature = ?, resolved_return_type = ?, definition_uri = ?, definition_path = ?
605
+ WHERE id = ?`);
606
+ for (const file of files) {
607
+ if (!file || !fs.existsSync(file.path))
608
+ continue;
609
+ let source;
610
+ try {
611
+ source = fs.readFileSync(file.path, 'utf8');
612
+ }
613
+ catch {
614
+ continue;
615
+ }
616
+ const symbols = selectSymbols.all(file.path, branch);
617
+ if (symbols.length > 0) {
618
+ const symbolMetadata = await lspCoordinator.enrich({
619
+ filePath: file.path,
620
+ language: file.language,
621
+ source,
622
+ targets: symbols.map((symbol) => ({ line: symbol.start_line, character: 0 })),
623
+ });
624
+ for (let i = 0; i < symbols.length; i++) {
625
+ const symbol = symbols[i];
626
+ if (!symbol)
627
+ continue;
628
+ const metadata = symbolMetadata[i];
629
+ if (!metadata)
630
+ continue;
631
+ updateSymbol.run(metadata.resolvedTypeSignature, metadata.resolvedReturnType, metadata.definitionUri, metadata.definitionPath, symbol.id);
632
+ updateSymbolFts.run(buildStructuralEmbeddingText({
633
+ name: symbol.name,
634
+ signature: symbol.signature,
635
+ resolvedTypeSignature: metadata.resolvedTypeSignature,
636
+ resolvedReturnType: metadata.resolvedReturnType,
637
+ }), symbol.id);
638
+ }
639
+ }
640
+ const callRefs = selectCallRefs.all(file.path, branch);
641
+ if (callRefs.length > 0) {
642
+ const callRefMetadata = await lspCoordinator.enrich({
643
+ filePath: file.path,
644
+ language: file.language,
645
+ source,
646
+ targets: callRefs.map((callRef) => ({ line: callRef.call_line, character: 0 })),
647
+ });
648
+ for (let i = 0; i < callRefs.length; i++) {
649
+ const callRef = callRefs[i];
650
+ if (!callRef)
651
+ continue;
652
+ const metadata = callRefMetadata[i];
653
+ if (!metadata)
654
+ continue;
655
+ updateCallRef.run(metadata.resolvedTypeSignature, metadata.resolvedReturnType, metadata.definitionUri, metadata.definitionPath, callRef.id);
656
+ }
657
+ }
658
+ }
659
+ }
355
660
  resolveBranch() {
356
661
  if (this.walkerConfig.branch)
357
662
  return this.walkerConfig.branch;
@@ -360,9 +665,12 @@ export class IndexBuilder {
360
665
  saveLastKnownHead(db) {
361
666
  const headSha = this.readGitValue(['rev-parse', 'HEAD']);
362
667
  if (headSha) {
363
- setKbMeta(db, KB_META_LAST_HEAD_SHA, headSha);
668
+ setLoreMeta(db, LORE_META_LAST_HEAD_SHA, headSha);
364
669
  }
365
670
  }
671
+ saveDocsAutoNotesSetting(db) {
672
+ setLoreMeta(db, 'docs_auto_notes', this.docsAutoNotes ? '1' : '0');
673
+ }
366
674
  readGitValue(args) {
367
675
  try {
368
676
  const value = execFileSync('git', ['-C', this.walkerConfig.rootDir, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
@@ -372,8 +680,82 @@ export class IndexBuilder {
372
680
  return undefined;
373
681
  }
374
682
  }
683
+ loadDirectDependencies() {
684
+ const packageJsonPath = path.join(this.walkerConfig.rootDir, 'package.json');
685
+ if (!fs.existsSync(packageJsonPath))
686
+ return new Map();
687
+ const raw = fs.readFileSync(packageJsonPath, 'utf8');
688
+ const pkg = JSON.parse(raw);
689
+ const deps = new Map();
690
+ for (const section of [pkg.dependencies, pkg.devDependencies, pkg.peerDependencies]) {
691
+ if (!section)
692
+ continue;
693
+ for (const [name, version] of Object.entries(section)) {
694
+ if (!deps.has(name))
695
+ deps.set(name, version);
696
+ }
697
+ }
698
+ return deps;
699
+ }
700
+ readInstalledPackageVersion(packageDir) {
701
+ const packageJsonPath = path.join(packageDir, 'package.json');
702
+ if (!fs.existsSync(packageJsonPath))
703
+ return undefined;
704
+ const raw = fs.readFileSync(packageJsonPath, 'utf8');
705
+ const pkg = JSON.parse(raw);
706
+ return pkg.version;
707
+ }
708
+ collectDeclarationFiles(packageDir) {
709
+ const declarations = [];
710
+ const stack = [packageDir];
711
+ while (stack.length > 0) {
712
+ const currentDir = stack.pop();
713
+ if (!currentDir)
714
+ continue;
715
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
716
+ for (const entry of entries) {
717
+ if (entry.name === 'node_modules')
718
+ continue;
719
+ const fullPath = path.join(currentDir, entry.name);
720
+ if (entry.isDirectory()) {
721
+ stack.push(fullPath);
722
+ continue;
723
+ }
724
+ if (entry.isFile() && fullPath.endsWith('.d.ts')) {
725
+ declarations.push(fullPath);
726
+ }
727
+ }
728
+ }
729
+ return declarations;
730
+ }
731
+ shouldIndexDependencySymbol(symbol) {
732
+ if (!isPublicDeclarationSurfaceSymbol(symbol))
733
+ return false;
734
+ if (symbol.declarationSurface)
735
+ return true;
736
+ return !this.hasImplementationBody(symbol);
737
+ }
738
+ hasImplementationBody(symbol) {
739
+ const node = symbol.astNode;
740
+ if (!node)
741
+ return false;
742
+ if (node.type === 'arrow_function' ||
743
+ node.type === 'function_expression' ||
744
+ node.type === 'generator_function') {
745
+ return true;
746
+ }
747
+ if (node.type === 'class_declaration' ||
748
+ node.type === 'interface_declaration' ||
749
+ node.type === 'type_alias_declaration') {
750
+ return false;
751
+ }
752
+ const bodyNode = node.childForFieldName('body');
753
+ if (!bodyNode)
754
+ return false;
755
+ return bodyNode.namedChildCount > 0 || bodyNode.text.trim() !== '';
756
+ }
375
757
  loadBuildCheckpoint(db, branch, totalFiles) {
376
- const raw = getKbMeta(db, KB_META_INDEX_CHECKPOINT);
758
+ const raw = getLoreMeta(db, LORE_META_INDEX_CHECKPOINT);
377
759
  if (!raw)
378
760
  return 0;
379
761
  try {
@@ -395,28 +777,37 @@ export class IndexBuilder {
395
777
  nextFileIndex,
396
778
  updatedAt: Math.floor(Date.now() / 1000),
397
779
  };
398
- setKbMeta(db, KB_META_INDEX_CHECKPOINT, JSON.stringify(checkpoint));
780
+ setLoreMeta(db, LORE_META_INDEX_CHECKPOINT, JSON.stringify(checkpoint));
399
781
  }
400
782
  /**
401
783
  * Embed structural symbol signatures in batches and persist results to
402
784
  * the `symbol_embeddings` vec0 virtual table.
403
785
  *
404
- * Also stores the embedding model name and dims in `kb_meta` and
786
+ * Also stores the embedding model name and dims in `lore_meta` and
405
787
  * creates the vec0 tables if they don't exist yet.
406
788
  */
407
789
  async embedStructural(db) {
408
790
  const embedder = this.embedder;
409
- setKbMeta(db, 'embedding_model', embedder.modelName);
410
- setKbMeta(db, 'embedding_dims', String(embedder.dims));
791
+ setLoreMeta(db, 'embedding_model', embedder.modelName);
792
+ setLoreMeta(db, 'embedding_dims', String(embedder.dims));
411
793
  createVec0Tables(db, embedder.dims);
412
- // Fetch all symbols that have a signature to embed.
794
+ // Fetch all symbols that have structural text to embed.
413
795
  const symbols = db
414
- .prepare('SELECT id, name, signature FROM symbols WHERE signature IS NOT NULL')
796
+ .prepare(`SELECT id, name, signature, resolved_type_signature, resolved_return_type
797
+ FROM symbols
798
+ WHERE signature IS NOT NULL
799
+ OR resolved_type_signature IS NOT NULL
800
+ OR resolved_return_type IS NOT NULL`)
415
801
  .all();
416
802
  const insertEmbed = db.prepare('INSERT OR REPLACE INTO symbol_embeddings(rowid, embedding) VALUES (CAST(? AS INTEGER), json(?))');
417
803
  for (let i = 0; i < symbols.length; i += EMBED_BATCH_SIZE) {
418
804
  const batch = symbols.slice(i, i + EMBED_BATCH_SIZE);
419
- const texts = batch.map(s => s.signature || s.name);
805
+ const texts = batch.map((symbol) => buildStructuralEmbeddingText({
806
+ name: symbol.name,
807
+ signature: symbol.signature,
808
+ resolvedTypeSignature: symbol.resolved_type_signature,
809
+ resolvedReturnType: symbol.resolved_return_type,
810
+ }));
420
811
  const embeddings = await embedder.embed(texts);
421
812
  db.transaction(() => {
422
813
  for (let j = 0; j < batch.length; j++) {
@@ -427,5 +818,54 @@ export class IndexBuilder {
427
818
  })();
428
819
  }
429
820
  }
821
+ async embedDocumentation(db) {
822
+ const embedder = this.embedder;
823
+ db.exec(`
824
+ CREATE VIRTUAL TABLE IF NOT EXISTS doc_section_embeddings USING vec0(
825
+ embedding FLOAT[${embedder.dims}]
826
+ );
827
+ `);
828
+ const sections = db.prepare(`SELECT id, title, content
829
+ FROM doc_sections
830
+ ORDER BY id`).all();
831
+ if (sections.length === 0)
832
+ return;
833
+ const insertEmbed = db.prepare('INSERT OR REPLACE INTO doc_section_embeddings(rowid, embedding) VALUES (CAST(? AS INTEGER), json(?))');
834
+ for (let i = 0; i < sections.length; i += EMBED_BATCH_SIZE) {
835
+ const batch = sections.slice(i, i + EMBED_BATCH_SIZE);
836
+ const texts = batch.map(section => section.content || section.title);
837
+ const embeddings = await embedder.embed(texts);
838
+ db.transaction(() => {
839
+ for (let j = 0; j < batch.length; j++) {
840
+ const section = batch[j];
841
+ if (section) {
842
+ insertEmbed.run(section.id, JSON.stringify(embeddings[j]));
843
+ }
844
+ }
845
+ })();
846
+ }
847
+ }
848
+ async embedCommitMessages(db) {
849
+ const embedder = this.embedder;
850
+ const commits = db.prepare(`SELECT rowid, message
851
+ FROM commits
852
+ WHERE length(trim(message)) > 0
853
+ ORDER BY rowid`).all();
854
+ if (commits.length === 0)
855
+ return;
856
+ const insertEmbed = db.prepare('INSERT OR REPLACE INTO commit_embeddings(rowid, embedding) VALUES (CAST(? AS INTEGER), json(?))');
857
+ for (let i = 0; i < commits.length; i += EMBED_BATCH_SIZE) {
858
+ const batch = commits.slice(i, i + EMBED_BATCH_SIZE);
859
+ const embeddings = await embedder.embed(batch.map((commit) => commit.message));
860
+ db.transaction(() => {
861
+ for (let j = 0; j < batch.length; j++) {
862
+ const commit = batch[j];
863
+ if (commit) {
864
+ insertEmbed.run(commit.rowid, JSON.stringify(embeddings[j]));
865
+ }
866
+ }
867
+ })();
868
+ }
869
+ }
430
870
  }
431
871
  //# sourceMappingURL=index.js.map