contextpro-mcp 0.0.0-stage → 1.4.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 (466) hide show
  1. package/CHANGELOG.md +416 -0
  2. package/INSTALL.md +128 -0
  3. package/INSTRUCTIONS.md +33 -0
  4. package/LICENSE +21 -0
  5. package/NOTICE +12 -0
  6. package/README.md +164 -2
  7. package/SECURITY.md +27 -0
  8. package/agent-guide.md +323 -0
  9. package/agent-instructions.md +1 -0
  10. package/build/core/agent-setup.d.ts +5 -0
  11. package/build/core/agent-setup.d.ts.map +1 -0
  12. package/build/core/agent-setup.js +94 -0
  13. package/build/core/agent-setup.js.map +1 -0
  14. package/build/core/artifacts.d.ts +64 -0
  15. package/build/core/artifacts.d.ts.map +1 -0
  16. package/build/core/artifacts.js +370 -0
  17. package/build/core/artifacts.js.map +1 -0
  18. package/build/core/bounded-source.d.ts +15 -0
  19. package/build/core/bounded-source.d.ts.map +1 -0
  20. package/build/core/bounded-source.js +78 -0
  21. package/build/core/bounded-source.js.map +1 -0
  22. package/build/core/changed-since.d.ts +20 -0
  23. package/build/core/changed-since.d.ts.map +1 -0
  24. package/build/core/changed-since.js +43 -0
  25. package/build/core/changed-since.js.map +1 -0
  26. package/build/core/chat-model.d.ts +8 -0
  27. package/build/core/chat-model.d.ts.map +1 -0
  28. package/build/core/chat-model.js +135 -0
  29. package/build/core/chat-model.js.map +1 -0
  30. package/build/core/clustering.d.ts +6 -0
  31. package/build/core/clustering.d.ts.map +1 -0
  32. package/build/core/clustering.js +201 -0
  33. package/build/core/clustering.js.map +1 -0
  34. package/build/core/context-metadata.d.ts +50 -0
  35. package/build/core/context-metadata.d.ts.map +1 -0
  36. package/build/core/context-metadata.js +118 -0
  37. package/build/core/context-metadata.js.map +1 -0
  38. package/build/core/data-dir.d.ts +5 -0
  39. package/build/core/data-dir.d.ts.map +1 -0
  40. package/build/core/data-dir.js +53 -0
  41. package/build/core/data-dir.js.map +1 -0
  42. package/build/core/db-native.d.ts +28 -0
  43. package/build/core/db-native.d.ts.map +1 -0
  44. package/build/core/db-native.js +120 -0
  45. package/build/core/db-native.js.map +1 -0
  46. package/build/core/db.d.ts +294 -0
  47. package/build/core/db.d.ts.map +1 -0
  48. package/build/core/db.js +1047 -0
  49. package/build/core/db.js.map +1 -0
  50. package/build/core/doctor.d.ts +13 -0
  51. package/build/core/doctor.d.ts.map +1 -0
  52. package/build/core/doctor.js +55 -0
  53. package/build/core/doctor.js.map +1 -0
  54. package/build/core/efficiency.d.ts +16 -0
  55. package/build/core/efficiency.d.ts.map +1 -0
  56. package/build/core/efficiency.js +170 -0
  57. package/build/core/efficiency.js.map +1 -0
  58. package/build/core/embedding-tracker.d.ts +25 -0
  59. package/build/core/embedding-tracker.d.ts.map +1 -0
  60. package/build/core/embedding-tracker.js +228 -0
  61. package/build/core/embedding-tracker.js.map +1 -0
  62. package/build/core/embeddings.d.ts +90 -0
  63. package/build/core/embeddings.d.ts.map +1 -0
  64. package/build/core/embeddings.js +693 -0
  65. package/build/core/embeddings.js.map +1 -0
  66. package/build/core/evidence-state.d.ts +42 -0
  67. package/build/core/evidence-state.d.ts.map +1 -0
  68. package/build/core/evidence-state.js +153 -0
  69. package/build/core/evidence-state.js.map +1 -0
  70. package/build/core/extractor-stamp.d.ts +10 -0
  71. package/build/core/extractor-stamp.d.ts.map +1 -0
  72. package/build/core/extractor-stamp.js +51 -0
  73. package/build/core/extractor-stamp.js.map +1 -0
  74. package/build/core/feature-brief-cache.d.ts +37 -0
  75. package/build/core/feature-brief-cache.d.ts.map +1 -0
  76. package/build/core/feature-brief-cache.js +100 -0
  77. package/build/core/feature-brief-cache.js.map +1 -0
  78. package/build/core/file-fingerprint.d.ts +15 -0
  79. package/build/core/file-fingerprint.d.ts.map +1 -0
  80. package/build/core/file-fingerprint.js +24 -0
  81. package/build/core/file-fingerprint.js.map +1 -0
  82. package/build/core/graph-analytics.d.ts +46 -0
  83. package/build/core/graph-analytics.d.ts.map +1 -0
  84. package/build/core/graph-analytics.js +371 -0
  85. package/build/core/graph-analytics.js.map +1 -0
  86. package/build/core/hub.d.ts +23 -0
  87. package/build/core/hub.d.ts.map +1 -0
  88. package/build/core/hub.js +123 -0
  89. package/build/core/hub.js.map +1 -0
  90. package/build/core/ignore-dirs.d.ts +7 -0
  91. package/build/core/ignore-dirs.d.ts.map +1 -0
  92. package/build/core/ignore-dirs.js +84 -0
  93. package/build/core/ignore-dirs.js.map +1 -0
  94. package/build/core/index-freshness.d.ts +15 -0
  95. package/build/core/index-freshness.d.ts.map +1 -0
  96. package/build/core/index-freshness.js +96 -0
  97. package/build/core/index-freshness.js.map +1 -0
  98. package/build/core/index-reconcile.d.ts +28 -0
  99. package/build/core/index-reconcile.d.ts.map +1 -0
  100. package/build/core/index-reconcile.js +129 -0
  101. package/build/core/index-reconcile.js.map +1 -0
  102. package/build/core/local-daemon.d.ts +5 -0
  103. package/build/core/local-daemon.d.ts.map +1 -0
  104. package/build/core/local-daemon.js +282 -0
  105. package/build/core/local-daemon.js.map +1 -0
  106. package/build/core/memory-graph.d.ts +68 -0
  107. package/build/core/memory-graph.d.ts.map +1 -0
  108. package/build/core/memory-graph.js +152 -0
  109. package/build/core/memory-graph.js.map +1 -0
  110. package/build/core/native-repair.d.ts +12 -0
  111. package/build/core/native-repair.d.ts.map +1 -0
  112. package/build/core/native-repair.js +167 -0
  113. package/build/core/native-repair.js.map +1 -0
  114. package/build/core/native-runtime.d.ts +12 -0
  115. package/build/core/native-runtime.d.ts.map +1 -0
  116. package/build/core/native-runtime.js +38 -0
  117. package/build/core/native-runtime.js.map +1 -0
  118. package/build/core/pagerank.d.ts +29 -0
  119. package/build/core/pagerank.d.ts.map +1 -0
  120. package/build/core/pagerank.js +90 -0
  121. package/build/core/pagerank.js.map +1 -0
  122. package/build/core/parser-process.d.ts +21 -0
  123. package/build/core/parser-process.d.ts.map +1 -0
  124. package/build/core/parser-process.js +103 -0
  125. package/build/core/parser-process.js.map +1 -0
  126. package/build/core/parser-worker.d.ts +2 -0
  127. package/build/core/parser-worker.d.ts.map +1 -0
  128. package/build/core/parser-worker.js +17 -0
  129. package/build/core/parser-worker.js.map +1 -0
  130. package/build/core/parser.d.ts +66 -0
  131. package/build/core/parser.d.ts.map +1 -0
  132. package/build/core/parser.js +750 -0
  133. package/build/core/parser.js.map +1 -0
  134. package/build/core/path-guard.d.ts +6 -0
  135. package/build/core/path-guard.d.ts.map +1 -0
  136. package/build/core/path-guard.js +55 -0
  137. package/build/core/path-guard.js.map +1 -0
  138. package/build/core/process-lifecycle.d.ts +30 -0
  139. package/build/core/process-lifecycle.d.ts.map +1 -0
  140. package/build/core/process-lifecycle.js +129 -0
  141. package/build/core/process-lifecycle.js.map +1 -0
  142. package/build/core/project-config.d.ts +83 -0
  143. package/build/core/project-config.d.ts.map +1 -0
  144. package/build/core/project-config.js +302 -0
  145. package/build/core/project-config.js.map +1 -0
  146. package/build/core/project-graph.d.ts +69 -0
  147. package/build/core/project-graph.d.ts.map +1 -0
  148. package/build/core/project-graph.js +874 -0
  149. package/build/core/project-graph.js.map +1 -0
  150. package/build/core/provider-config.d.ts +15 -0
  151. package/build/core/provider-config.d.ts.map +1 -0
  152. package/build/core/provider-config.js +67 -0
  153. package/build/core/provider-config.js.map +1 -0
  154. package/build/core/provider-usage.d.ts +21 -0
  155. package/build/core/provider-usage.d.ts.map +1 -0
  156. package/build/core/provider-usage.js +52 -0
  157. package/build/core/provider-usage.js.map +1 -0
  158. package/build/core/python-language-server.d.ts +23 -0
  159. package/build/core/python-language-server.d.ts.map +1 -0
  160. package/build/core/python-language-server.js +217 -0
  161. package/build/core/python-language-server.js.map +1 -0
  162. package/build/core/python-runtime-identity.d.ts +3 -0
  163. package/build/core/python-runtime-identity.d.ts.map +1 -0
  164. package/build/core/python-runtime-identity.js +53 -0
  165. package/build/core/python-runtime-identity.js.map +1 -0
  166. package/build/core/reconcile-scheduler.d.ts +20 -0
  167. package/build/core/reconcile-scheduler.d.ts.map +1 -0
  168. package/build/core/reconcile-scheduler.js +58 -0
  169. package/build/core/reconcile-scheduler.js.map +1 -0
  170. package/build/core/request-context.d.ts +7 -0
  171. package/build/core/request-context.d.ts.map +1 -0
  172. package/build/core/request-context.js +17 -0
  173. package/build/core/request-context.js.map +1 -0
  174. package/build/core/request-root.d.ts +19 -0
  175. package/build/core/request-root.d.ts.map +1 -0
  176. package/build/core/request-root.js +26 -0
  177. package/build/core/request-root.js.map +1 -0
  178. package/build/core/root-guard.d.ts +8 -0
  179. package/build/core/root-guard.d.ts.map +1 -0
  180. package/build/core/root-guard.js +53 -0
  181. package/build/core/root-guard.js.map +1 -0
  182. package/build/core/roots-detect.d.ts +17 -0
  183. package/build/core/roots-detect.d.ts.map +1 -0
  184. package/build/core/roots-detect.js +63 -0
  185. package/build/core/roots-detect.js.map +1 -0
  186. package/build/core/runtime-session.d.ts +5 -0
  187. package/build/core/runtime-session.d.ts.map +1 -0
  188. package/build/core/runtime-session.js +10 -0
  189. package/build/core/runtime-session.js.map +1 -0
  190. package/build/core/scip-index.d.ts +71 -0
  191. package/build/core/scip-index.d.ts.map +1 -0
  192. package/build/core/scip-index.js +343 -0
  193. package/build/core/scip-index.js.map +1 -0
  194. package/build/core/semantic-diff.d.ts +32 -0
  195. package/build/core/semantic-diff.d.ts.map +1 -0
  196. package/build/core/semantic-diff.js +259 -0
  197. package/build/core/semantic-diff.js.map +1 -0
  198. package/build/core/source-context.d.ts +3 -0
  199. package/build/core/source-context.d.ts.map +1 -0
  200. package/build/core/source-context.js +86 -0
  201. package/build/core/source-context.js.map +1 -0
  202. package/build/core/source-mask.d.ts +8 -0
  203. package/build/core/source-mask.d.ts.map +1 -0
  204. package/build/core/source-mask.js +156 -0
  205. package/build/core/source-mask.js.map +1 -0
  206. package/build/core/source-mutation.d.ts +16 -0
  207. package/build/core/source-mutation.d.ts.map +1 -0
  208. package/build/core/source-mutation.js +193 -0
  209. package/build/core/source-mutation.js.map +1 -0
  210. package/build/core/sql-schema.d.ts +59 -0
  211. package/build/core/sql-schema.d.ts.map +1 -0
  212. package/build/core/sql-schema.js +218 -0
  213. package/build/core/sql-schema.js.map +1 -0
  214. package/build/core/startup-health.d.ts +40 -0
  215. package/build/core/startup-health.d.ts.map +1 -0
  216. package/build/core/startup-health.js +132 -0
  217. package/build/core/startup-health.js.map +1 -0
  218. package/build/core/team-ledger.d.ts +60 -0
  219. package/build/core/team-ledger.d.ts.map +1 -0
  220. package/build/core/team-ledger.js +171 -0
  221. package/build/core/team-ledger.js.map +1 -0
  222. package/build/core/test-impact.d.ts +23 -0
  223. package/build/core/test-impact.d.ts.map +1 -0
  224. package/build/core/test-impact.js +131 -0
  225. package/build/core/test-impact.js.map +1 -0
  226. package/build/core/tool-metrics.d.ts +29 -0
  227. package/build/core/tool-metrics.d.ts.map +1 -0
  228. package/build/core/tool-metrics.js +74 -0
  229. package/build/core/tool-metrics.js.map +1 -0
  230. package/build/core/tool-profile.d.ts +12 -0
  231. package/build/core/tool-profile.d.ts.map +1 -0
  232. package/build/core/tool-profile.js +102 -0
  233. package/build/core/tool-profile.js.map +1 -0
  234. package/build/core/tool-trace.d.ts +8 -0
  235. package/build/core/tool-trace.d.ts.map +1 -0
  236. package/build/core/tool-trace.js +70 -0
  237. package/build/core/tool-trace.js.map +1 -0
  238. package/build/core/tree-sitter-runtime.d.ts +20 -0
  239. package/build/core/tree-sitter-runtime.d.ts.map +1 -0
  240. package/build/core/tree-sitter-runtime.js +324 -0
  241. package/build/core/tree-sitter-runtime.js.map +1 -0
  242. package/build/core/tree-sitter.d.ts +6 -0
  243. package/build/core/tree-sitter.d.ts.map +1 -0
  244. package/build/core/tree-sitter.js +24 -0
  245. package/build/core/tree-sitter.js.map +1 -0
  246. package/build/core/typescript-reference-process.d.ts +30 -0
  247. package/build/core/typescript-reference-process.d.ts.map +1 -0
  248. package/build/core/typescript-reference-process.js +124 -0
  249. package/build/core/typescript-reference-process.js.map +1 -0
  250. package/build/core/typescript-reference-worker.d.ts +2 -0
  251. package/build/core/typescript-reference-worker.d.ts.map +1 -0
  252. package/build/core/typescript-reference-worker.js +14 -0
  253. package/build/core/typescript-reference-worker.js.map +1 -0
  254. package/build/core/typescript-references.d.ts +71 -0
  255. package/build/core/typescript-references.d.ts.map +1 -0
  256. package/build/core/typescript-references.js +392 -0
  257. package/build/core/typescript-references.js.map +1 -0
  258. package/build/core/typescript-skeleton.d.ts +5 -0
  259. package/build/core/typescript-skeleton.d.ts.map +1 -0
  260. package/build/core/typescript-skeleton.js +49 -0
  261. package/build/core/typescript-skeleton.js.map +1 -0
  262. package/build/core/walker.d.ts +16 -0
  263. package/build/core/walker.d.ts.map +1 -0
  264. package/build/core/walker.js +152 -0
  265. package/build/core/walker.js.map +1 -0
  266. package/build/core/workspace-router.d.ts +28 -0
  267. package/build/core/workspace-router.d.ts.map +1 -0
  268. package/build/core/workspace-router.js +93 -0
  269. package/build/core/workspace-router.js.map +1 -0
  270. package/build/core/worktree-gc.d.ts +16 -0
  271. package/build/core/worktree-gc.d.ts.map +1 -0
  272. package/build/core/worktree-gc.js +69 -0
  273. package/build/core/worktree-gc.js.map +1 -0
  274. package/build/core/worktree-overlay.d.ts +22 -0
  275. package/build/core/worktree-overlay.d.ts.map +1 -0
  276. package/build/core/worktree-overlay.js +84 -0
  277. package/build/core/worktree-overlay.js.map +1 -0
  278. package/build/core/worktree-root-guard.d.ts +8 -0
  279. package/build/core/worktree-root-guard.d.ts.map +1 -0
  280. package/build/core/worktree-root-guard.js +82 -0
  281. package/build/core/worktree-root-guard.js.map +1 -0
  282. package/build/core/worktree-seed.d.ts +14 -0
  283. package/build/core/worktree-seed.d.ts.map +1 -0
  284. package/build/core/worktree-seed.js +98 -0
  285. package/build/core/worktree-seed.js.map +1 -0
  286. package/build/git/shadow.d.ts +40 -0
  287. package/build/git/shadow.d.ts.map +1 -0
  288. package/build/git/shadow.js +298 -0
  289. package/build/git/shadow.js.map +1 -0
  290. package/build/index.d.ts +3 -0
  291. package/build/index.d.ts.map +1 -0
  292. package/build/index.js +599 -0
  293. package/build/index.js.map +1 -0
  294. package/build/tools/agentic-context.d.ts +86 -0
  295. package/build/tools/agentic-context.d.ts.map +1 -0
  296. package/build/tools/agentic-context.js +1546 -0
  297. package/build/tools/agentic-context.js.map +1 -0
  298. package/build/tools/blast-radius.d.ts +24 -0
  299. package/build/tools/blast-radius.d.ts.map +1 -0
  300. package/build/tools/blast-radius.js +231 -0
  301. package/build/tools/blast-radius.js.map +1 -0
  302. package/build/tools/changed-since.d.ts +7 -0
  303. package/build/tools/changed-since.d.ts.map +1 -0
  304. package/build/tools/changed-since.js +68 -0
  305. package/build/tools/changed-since.js.map +1 -0
  306. package/build/tools/compiler-actions.d.ts +26 -0
  307. package/build/tools/compiler-actions.d.ts.map +1 -0
  308. package/build/tools/compiler-actions.js +121 -0
  309. package/build/tools/compiler-actions.js.map +1 -0
  310. package/build/tools/context-tree.d.ts +9 -0
  311. package/build/tools/context-tree.d.ts.map +1 -0
  312. package/build/tools/context-tree.js +131 -0
  313. package/build/tools/context-tree.js.map +1 -0
  314. package/build/tools/context.d.ts +37 -0
  315. package/build/tools/context.d.ts.map +1 -0
  316. package/build/tools/context.js +800 -0
  317. package/build/tools/context.js.map +1 -0
  318. package/build/tools/diff.d.ts +9 -0
  319. package/build/tools/diff.d.ts.map +1 -0
  320. package/build/tools/diff.js +176 -0
  321. package/build/tools/diff.js.map +1 -0
  322. package/build/tools/directory-prior.d.ts +16 -0
  323. package/build/tools/directory-prior.d.ts.map +1 -0
  324. package/build/tools/directory-prior.js +121 -0
  325. package/build/tools/directory-prior.js.map +1 -0
  326. package/build/tools/discovery-documents.d.ts +11 -0
  327. package/build/tools/discovery-documents.d.ts.map +1 -0
  328. package/build/tools/discovery-documents.js +71 -0
  329. package/build/tools/discovery-documents.js.map +1 -0
  330. package/build/tools/discovery-hints.d.ts +25 -0
  331. package/build/tools/discovery-hints.d.ts.map +1 -0
  332. package/build/tools/discovery-hints.js +329 -0
  333. package/build/tools/discovery-hints.js.map +1 -0
  334. package/build/tools/efficiency-report.d.ts +13 -0
  335. package/build/tools/efficiency-report.d.ts.map +1 -0
  336. package/build/tools/efficiency-report.js +78 -0
  337. package/build/tools/efficiency-report.js.map +1 -0
  338. package/build/tools/evidence-retrieval.d.ts +33 -0
  339. package/build/tools/evidence-retrieval.d.ts.map +1 -0
  340. package/build/tools/evidence-retrieval.js +200 -0
  341. package/build/tools/evidence-retrieval.js.map +1 -0
  342. package/build/tools/exact-references.d.ts +10 -0
  343. package/build/tools/exact-references.d.ts.map +1 -0
  344. package/build/tools/exact-references.js +349 -0
  345. package/build/tools/exact-references.js.map +1 -0
  346. package/build/tools/explain.d.ts +14 -0
  347. package/build/tools/explain.d.ts.map +1 -0
  348. package/build/tools/explain.js +370 -0
  349. package/build/tools/explain.js.map +1 -0
  350. package/build/tools/feature-brief.d.ts +7 -0
  351. package/build/tools/feature-brief.d.ts.map +1 -0
  352. package/build/tools/feature-brief.js +89 -0
  353. package/build/tools/feature-brief.js.map +1 -0
  354. package/build/tools/feature-hub.d.ts +15 -0
  355. package/build/tools/feature-hub.d.ts.map +1 -0
  356. package/build/tools/feature-hub.js +404 -0
  357. package/build/tools/feature-hub.js.map +1 -0
  358. package/build/tools/file-skeleton.d.ts +7 -0
  359. package/build/tools/file-skeleton.d.ts.map +1 -0
  360. package/build/tools/file-skeleton.js +190 -0
  361. package/build/tools/file-skeleton.js.map +1 -0
  362. package/build/tools/grep.d.ts +25 -0
  363. package/build/tools/grep.d.ts.map +1 -0
  364. package/build/tools/grep.js +341 -0
  365. package/build/tools/grep.js.map +1 -0
  366. package/build/tools/impact-delta.d.ts +7 -0
  367. package/build/tools/impact-delta.d.ts.map +1 -0
  368. package/build/tools/impact-delta.js +102 -0
  369. package/build/tools/impact-delta.js.map +1 -0
  370. package/build/tools/memory-tools.d.ts +69 -0
  371. package/build/tools/memory-tools.d.ts.map +1 -0
  372. package/build/tools/memory-tools.js +366 -0
  373. package/build/tools/memory-tools.js.map +1 -0
  374. package/build/tools/planning-helpers.d.ts +26 -0
  375. package/build/tools/planning-helpers.d.ts.map +1 -0
  376. package/build/tools/planning-helpers.js +269 -0
  377. package/build/tools/planning-helpers.js.map +1 -0
  378. package/build/tools/project-graph.d.ts +19 -0
  379. package/build/tools/project-graph.d.ts.map +1 -0
  380. package/build/tools/project-graph.js +259 -0
  381. package/build/tools/project-graph.js.map +1 -0
  382. package/build/tools/propose-commit.d.ts +9 -0
  383. package/build/tools/propose-commit.d.ts.map +1 -0
  384. package/build/tools/propose-commit.js +156 -0
  385. package/build/tools/propose-commit.js.map +1 -0
  386. package/build/tools/register-tools.d.ts +18 -0
  387. package/build/tools/register-tools.d.ts.map +1 -0
  388. package/build/tools/register-tools.js +1131 -0
  389. package/build/tools/register-tools.js.map +1 -0
  390. package/build/tools/reindex.d.ts +76 -0
  391. package/build/tools/reindex.d.ts.map +1 -0
  392. package/build/tools/reindex.js +871 -0
  393. package/build/tools/reindex.js.map +1 -0
  394. package/build/tools/related.d.ts +6 -0
  395. package/build/tools/related.d.ts.map +1 -0
  396. package/build/tools/related.js +143 -0
  397. package/build/tools/related.js.map +1 -0
  398. package/build/tools/repo-map.d.ts +59 -0
  399. package/build/tools/repo-map.d.ts.map +1 -0
  400. package/build/tools/repo-map.js +270 -0
  401. package/build/tools/repo-map.js.map +1 -0
  402. package/build/tools/resolve-symbol.d.ts +41 -0
  403. package/build/tools/resolve-symbol.d.ts.map +1 -0
  404. package/build/tools/resolve-symbol.js +250 -0
  405. package/build/tools/resolve-symbol.js.map +1 -0
  406. package/build/tools/retrieval-helpers.d.ts +35 -0
  407. package/build/tools/retrieval-helpers.d.ts.map +1 -0
  408. package/build/tools/retrieval-helpers.js +302 -0
  409. package/build/tools/retrieval-helpers.js.map +1 -0
  410. package/build/tools/scip-index.d.ts +12 -0
  411. package/build/tools/scip-index.d.ts.map +1 -0
  412. package/build/tools/scip-index.js +66 -0
  413. package/build/tools/scip-index.js.map +1 -0
  414. package/build/tools/scoped-discovery.d.ts +7 -0
  415. package/build/tools/scoped-discovery.d.ts.map +1 -0
  416. package/build/tools/scoped-discovery.js +89 -0
  417. package/build/tools/scoped-discovery.js.map +1 -0
  418. package/build/tools/search.d.ts +62 -0
  419. package/build/tools/search.d.ts.map +1 -0
  420. package/build/tools/search.js +419 -0
  421. package/build/tools/search.js.map +1 -0
  422. package/build/tools/semantic-diff.d.ts +7 -0
  423. package/build/tools/semantic-diff.d.ts.map +1 -0
  424. package/build/tools/semantic-diff.js +45 -0
  425. package/build/tools/semantic-diff.js.map +1 -0
  426. package/build/tools/semantic-identifiers.d.ts +51 -0
  427. package/build/tools/semantic-identifiers.d.ts.map +1 -0
  428. package/build/tools/semantic-identifiers.js +522 -0
  429. package/build/tools/semantic-identifiers.js.map +1 -0
  430. package/build/tools/semantic-navigate.d.ts +18 -0
  431. package/build/tools/semantic-navigate.d.ts.map +1 -0
  432. package/build/tools/semantic-navigate.js +413 -0
  433. package/build/tools/semantic-navigate.js.map +1 -0
  434. package/build/tools/semantic-search.d.ts +30 -0
  435. package/build/tools/semantic-search.d.ts.map +1 -0
  436. package/build/tools/semantic-search.js +466 -0
  437. package/build/tools/semantic-search.js.map +1 -0
  438. package/build/tools/sql-schema.d.ts +23 -0
  439. package/build/tools/sql-schema.d.ts.map +1 -0
  440. package/build/tools/sql-schema.js +155 -0
  441. package/build/tools/sql-schema.js.map +1 -0
  442. package/build/tools/static-analysis.d.ts +7 -0
  443. package/build/tools/static-analysis.d.ts.map +1 -0
  444. package/build/tools/static-analysis.js +331 -0
  445. package/build/tools/static-analysis.js.map +1 -0
  446. package/build/tools/status.d.ts +74 -0
  447. package/build/tools/status.d.ts.map +1 -0
  448. package/build/tools/status.js +227 -0
  449. package/build/tools/status.js.map +1 -0
  450. package/build/tools/symbolic-edit.d.ts +57 -0
  451. package/build/tools/symbolic-edit.d.ts.map +1 -0
  452. package/build/tools/symbolic-edit.js +500 -0
  453. package/build/tools/symbolic-edit.js.map +1 -0
  454. package/build/tools/test-impact.d.ts +7 -0
  455. package/build/tools/test-impact.d.ts.map +1 -0
  456. package/build/tools/test-impact.js +34 -0
  457. package/build/tools/test-impact.js.map +1 -0
  458. package/build/tools/test-map.d.ts +8 -0
  459. package/build/tools/test-map.d.ts.map +1 -0
  460. package/build/tools/test-map.js +75 -0
  461. package/build/tools/test-map.js.map +1 -0
  462. package/docs/README.md +26 -0
  463. package/docs/architecture.md +35 -0
  464. package/docs/configuration.md +178 -0
  465. package/docs/maintenance.md +77 -0
  466. package/package.json +80 -4
@@ -0,0 +1,871 @@
1
+ // Re-walk, re-parse, and re-index project files into SQLite database
2
+ // FEATURE: Reindex — persistent file/symbol/import graph population
3
+ import { walkDirectory } from "../core/walker.js";
4
+ import { analyzeContent, flattenSymbols, isSupportedFile, extractImports, extractCallSites } from "../core/parser.js";
5
+ import { getDB, hashContent } from "../core/db.js";
6
+ import { stat } from "fs/promises";
7
+ import { resolve, extname, dirname, relative } from "path";
8
+ import { invalidateSearchCache, refreshFileSearchEmbeddings } from "./semantic-search.js";
9
+ import { invalidateIdentifierSearchCache, refreshIdentifierEmbeddings } from "./semantic-identifiers.js";
10
+ import { autoGenerateHubData } from "./feature-hub.js";
11
+ import { refreshMemoryEmbeddings, toolUpsertMemoryNode } from "./memory-tools.js";
12
+ import { resolveProjectPath } from "../core/path-guard.js";
13
+ import { getEmbedProvider, getEmbedModel } from "../core/embeddings.js";
14
+ import { matchesFileFingerprint, maxSourceFileSize, readSourceSnapshot } from "../core/file-fingerprint.js";
15
+ import { getExtractorStamp } from "../core/extractor-stamp.js";
16
+ import { sharedCompute } from "../core/local-daemon.js";
17
+ /** Whether auto-hub generation is enabled (opt-in via option or env). */
18
+ function isAutoHubsEnabled(option) {
19
+ if (option !== undefined)
20
+ return option;
21
+ const env = (process.env.CONTEXTPRO_AUTO_HUBS ?? process.env.CONTEXTPLUS_AUTO_HUBS ?? "").trim().toLowerCase();
22
+ return ["1", "true", "yes", "on"].includes(env);
23
+ }
24
+ const INDEX_STATUS_KEY = "index_status";
25
+ const INDEX_STARTED_AT_KEY = "index_started_at";
26
+ const INDEX_FINISHED_AT_KEY = "index_finished_at";
27
+ const INDEX_TARGET_PATH_KEY = "index_target_path";
28
+ const INDEX_OWNER_PID_KEY = "index_owner_pid";
29
+ const LAST_INDEX_ERROR_KEY = "last_index_error";
30
+ const INDEXING_STATUS = "indexing";
31
+ const IDLE_STATUS = "idle";
32
+ function tryAcquireIndexLock(db, targetPath) {
33
+ const extra = [
34
+ [INDEX_STARTED_AT_KEY, String(Date.now())],
35
+ [INDEX_TARGET_PATH_KEY, targetPath],
36
+ [INDEX_OWNER_PID_KEY, String(process.pid)],
37
+ [LAST_INDEX_ERROR_KEY, ""],
38
+ ];
39
+ // A brand-new DB has no index_status row yet; null and explicit idle are both
40
+ // unlocked states, acquired atomically through the same compare-and-set path.
41
+ return db.compareAndSetMeta(INDEX_STATUS_KEY, IDLE_STATUS, INDEXING_STATUS, extra)
42
+ || db.compareAndSetMeta(INDEX_STATUS_KEY, null, INDEXING_STATUS, extra);
43
+ }
44
+ function isReindexEmbedDisabled() {
45
+ const raw = (process.env.CONTEXTPRO_REINDEX_NO_EMBED ?? process.env.CONTEXTPLUS_REINDEX_NO_EMBED ?? "").trim().toLowerCase();
46
+ return raw === "1" || raw === "true" || raw === "yes";
47
+ }
48
+ function detectLanguage(filePath) {
49
+ const ext = extname(filePath).toLowerCase();
50
+ const map = {
51
+ ".ts": "typescript", ".tsx": "typescript", ".js": "javascript", ".jsx": "javascript",
52
+ ".mjs": "javascript", ".py": "python", ".rs": "rust", ".go": "go",
53
+ ".java": "java", ".cs": "csharp", ".c": "c", ".cpp": "cpp",
54
+ ".rb": "ruby", ".swift": "swift", ".kt": "kotlin", ".sql": "sql",
55
+ };
56
+ return map[ext] ?? null;
57
+ }
58
+ function toProjectRelative(rootDir, candidate) {
59
+ const rel = relative(rootDir, candidate).replace(/\\/g, "/");
60
+ if (rel.startsWith("../") || rel === ".." || rel.startsWith("/"))
61
+ return null;
62
+ return rel;
63
+ }
64
+ function firstExistingPath(candidates, allFiles, rootDir) {
65
+ for (const candidate of candidates) {
66
+ const rel = toProjectRelative(rootDir, candidate);
67
+ if (rel && allFiles.has(rel))
68
+ return rel;
69
+ }
70
+ return null;
71
+ }
72
+ function findUniqueSuffixPath(suffixes, allFiles) {
73
+ const matches = [...allFiles].filter((file) => suffixes.some((suffix) => file === suffix || file.endsWith(`/${suffix}`)));
74
+ return matches.length === 1 ? matches[0] : null;
75
+ }
76
+ function pythonModuleCandidates(moduleSource, fromFile, rootDir) {
77
+ const fromDir = dirname(resolve(rootDir, fromFile));
78
+ const leadingDots = moduleSource.match(/^\.+/)?.[0].length ?? 0;
79
+ const moduleName = moduleSource.slice(leadingDots);
80
+ const modulePath = moduleName.replace(/\./g, "/");
81
+ let relativeBase = fromDir;
82
+ if (leadingDots > 0) {
83
+ for (let i = 1; i < leadingDots; i++)
84
+ relativeBase = resolve(relativeBase, "..");
85
+ }
86
+ const base = leadingDots > 0 ? relativeBase : resolve(rootDir);
87
+ const absolute = modulePath.length > 0
88
+ ? [
89
+ resolve(base, `${modulePath}.py`),
90
+ resolve(base, modulePath, "__init__.py"),
91
+ ]
92
+ : [resolve(base, "__init__.py")];
93
+ if (leadingDots === 0 && !moduleName.includes(".")) {
94
+ absolute.unshift(resolve(fromDir, `${modulePath}.py`), resolve(fromDir, modulePath, "__init__.py"));
95
+ }
96
+ const suffixes = modulePath.length > 0
97
+ ? [`${modulePath}.py`, `${modulePath}/__init__.py`]
98
+ : [];
99
+ return { absolute, suffixes };
100
+ }
101
+ function resolvePythonImportPath(importSource, fromFile, allFiles, rootDir, specifiers = []) {
102
+ const moduleSources = [
103
+ importSource,
104
+ ...specifiers
105
+ .filter((specifier) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(specifier))
106
+ .map((specifier) => importSource === "." ? `.${specifier}` : `${importSource}.${specifier}`),
107
+ ];
108
+ for (const moduleSource of moduleSources) {
109
+ const candidates = pythonModuleCandidates(moduleSource, fromFile, rootDir);
110
+ const direct = firstExistingPath(candidates.absolute, allFiles, rootDir);
111
+ if (direct)
112
+ return direct;
113
+ if (!moduleSource.startsWith(".")) {
114
+ const suffix = findUniqueSuffixPath(candidates.suffixes, allFiles);
115
+ if (suffix)
116
+ return suffix;
117
+ }
118
+ }
119
+ return null;
120
+ }
121
+ function resolveImportPath(importSource, fromFile, allFiles, rootDir, specifiers = []) {
122
+ const fromExt = extname(fromFile).toLowerCase();
123
+ if (fromExt === ".py") {
124
+ const pythonTarget = resolvePythonImportPath(importSource, fromFile, allFiles, rootDir, specifiers);
125
+ if (pythonTarget)
126
+ return pythonTarget;
127
+ }
128
+ if (!importSource.startsWith("."))
129
+ return null;
130
+ const fromDir = dirname(resolve(rootDir, fromFile));
131
+ const candidates = [
132
+ resolve(fromDir, importSource),
133
+ resolve(fromDir, importSource + ".ts"),
134
+ resolve(fromDir, importSource + ".tsx"),
135
+ resolve(fromDir, importSource + ".js"),
136
+ resolve(fromDir, importSource + ".jsx"),
137
+ resolve(fromDir, importSource + ".py"),
138
+ resolve(fromDir, importSource.replace(/\.js$/, ".ts")),
139
+ resolve(fromDir, importSource + "/index.ts"),
140
+ resolve(fromDir, importSource + "/index.js"),
141
+ resolve(fromDir, importSource, "__init__.py"),
142
+ ];
143
+ return firstExistingPath(candidates, allFiles, rootDir);
144
+ }
145
+ /** Called inside the source-file transaction: never publish a current content
146
+ * hash with missing or previous-version imports if a write fails. */
147
+ function replaceFileImports(db, rootDir, fileId, path, imports, resolvablePaths) {
148
+ db.deleteImportsForFile(fileId);
149
+ for (const imp of imports) {
150
+ const targetPath = resolveImportPath(imp.source, path, resolvablePaths, rootDir, imp.specifiers);
151
+ const targetId = targetPath ? db.getFile(targetPath)?.id ?? null : null;
152
+ db.upsertImport(fileId, imp.source, targetId, imp.type, imp.specifiers.length ? imp.specifiers : undefined);
153
+ }
154
+ }
155
+ /** A failed multi-file finalization must be retried even when file hashes match. */
156
+ function requireIndexRepair(db) {
157
+ db.setMeta("index_repair_required", "1");
158
+ db.bumpIndexGeneration();
159
+ invalidateSearchCache();
160
+ invalidateIdentifierSearchCache();
161
+ }
162
+ /** File additions/deletions can change edges of UNCHANGED importers, including
163
+ * resolved edges when extension/index precedence changes. Re-resolve stored
164
+ * import metadata only when the file set changes; no importer reads or parses. */
165
+ function reconnectStoredImports(db, rootDir) {
166
+ const files = db.getAllFiles();
167
+ const filesById = new Map(files.map((file) => [file.id, file]));
168
+ const filesByPath = new Map(files.map((file) => [file.path, file]));
169
+ const paths = new Set(filesByPath.keys());
170
+ db.transaction(() => {
171
+ for (const imp of db.getAllImports()) {
172
+ const source = filesById.get(imp.source_file_id);
173
+ if (!source)
174
+ continue;
175
+ let specifiers = [];
176
+ if (imp.symbols_imported) {
177
+ try {
178
+ const parsed = JSON.parse(imp.symbols_imported);
179
+ if (Array.isArray(parsed))
180
+ specifiers = parsed.filter((value) => typeof value === "string");
181
+ }
182
+ catch { /* Older indexes may contain malformed optional specifiers. */ }
183
+ }
184
+ const targetPath = resolveImportPath(imp.target_path, source.path, paths, rootDir, specifiers);
185
+ const targetId = targetPath ? filesByPath.get(targetPath)?.id ?? null : null;
186
+ if (targetId !== imp.target_file_id)
187
+ db.updateImportTarget(imp.id, targetId);
188
+ }
189
+ });
190
+ }
191
+ function setIndexStatus(db, status, input) {
192
+ if (status === INDEXING_STATUS) {
193
+ db.setMetaBatch([
194
+ [INDEX_STATUS_KEY, status],
195
+ [INDEX_STARTED_AT_KEY, String(Date.now())],
196
+ [INDEX_TARGET_PATH_KEY, input?.targetPath ?? ""],
197
+ [INDEX_OWNER_PID_KEY, String(process.pid)],
198
+ [LAST_INDEX_ERROR_KEY, ""],
199
+ ]);
200
+ return;
201
+ }
202
+ db.setMetaBatch([
203
+ [INDEX_STATUS_KEY, status],
204
+ [INDEX_FINISHED_AT_KEY, String(Date.now())],
205
+ [INDEX_TARGET_PATH_KEY, ""],
206
+ [INDEX_OWNER_PID_KEY, ""],
207
+ [LAST_INDEX_ERROR_KEY, input?.error ?? ""],
208
+ ]);
209
+ }
210
+ function sleep(ms) {
211
+ return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
212
+ }
213
+ function processIsAlive(pid) {
214
+ if (!Number.isInteger(pid) || pid <= 0)
215
+ return false;
216
+ try {
217
+ process.kill(pid, 0);
218
+ return true;
219
+ }
220
+ catch {
221
+ return false;
222
+ }
223
+ }
224
+ function recoverAbandonedIndexLock(db) {
225
+ if (db.getMeta(INDEX_STATUS_KEY) !== INDEXING_STATUS)
226
+ return false;
227
+ const ownerPid = Number.parseInt(db.getMeta(INDEX_OWNER_PID_KEY) ?? "", 10);
228
+ const startedAt = Number.parseInt(db.getMeta(INDEX_STARTED_AT_KEY) ?? "", 10);
229
+ const ownerKnownDead = Number.isInteger(ownerPid) && ownerPid > 0 && !processIsAlive(ownerPid);
230
+ // Pre-owner-version locks cannot be attributed. Only reclaim those after ten
231
+ // minutes so a legitimate long-running legacy reindex is not interrupted.
232
+ const legacyLockExpired = (!Number.isInteger(ownerPid) || ownerPid <= 0)
233
+ && Number.isFinite(startedAt)
234
+ && Date.now() - startedAt > 10 * 60_000;
235
+ if (!ownerKnownDead && !legacyLockExpired)
236
+ return false;
237
+ return db.compareAndSetMeta(INDEX_STATUS_KEY, INDEXING_STATUS, IDLE_STATUS, [
238
+ [INDEX_OWNER_PID_KEY, ""],
239
+ [LAST_INDEX_ERROR_KEY, "Recovered an abandoned index lock."],
240
+ ]);
241
+ }
242
+ export async function waitForIndexingCompletion(rootDir, options = {}) {
243
+ const timeoutMs = Math.max(500, options.timeoutMs ?? 30_000);
244
+ const pollMs = Math.max(50, options.pollMs ?? 250);
245
+ const db = await getDB(rootDir);
246
+ const startedAt = Date.now();
247
+ let waited = false;
248
+ while (db.getMeta(INDEX_STATUS_KEY) === INDEXING_STATUS) {
249
+ if (recoverAbandonedIndexLock(db))
250
+ break;
251
+ waited = true;
252
+ if (Date.now() - startedAt >= timeoutMs) {
253
+ return { waited, timedOut: true };
254
+ }
255
+ await sleep(pollMs);
256
+ }
257
+ return { waited, timedOut: false };
258
+ }
259
+ export async function reindex(options) {
260
+ const shared = await sharedCompute("reindex", options);
261
+ if (shared)
262
+ return shared.value;
263
+ const start = Date.now();
264
+ const db = await getDB(options.rootDir);
265
+ // Atomic check-and-set so two parallel reindex calls don't race past the guard.
266
+ const acquired = tryAcquireIndexLock(db, options.targetPath ?? "");
267
+ if (!acquired) {
268
+ const current = db.getMeta(INDEX_STATUS_KEY);
269
+ if (current === INDEXING_STATUS) {
270
+ const wait = await waitForIndexingCompletion(options.rootDir, { timeoutMs: 60_000 });
271
+ if (wait.timedOut) {
272
+ return "Reindex skipped: another reindex is already in progress and did not finish within 60s.";
273
+ }
274
+ // Retry the acquisition once the other reindex finishes.
275
+ const retried = tryAcquireIndexLock(db, options.targetPath ?? "");
276
+ if (!retried) {
277
+ return "Reindex skipped: index lock could not be acquired after the prior reindex finished. Try again in a moment.";
278
+ }
279
+ }
280
+ else {
281
+ // The index was in an unexpected state (e.g., "stale" from a prior crash). Force it
282
+ // forward — the existing setIndexStatus path will still log the error if anything goes wrong.
283
+ setIndexStatus(db, INDEXING_STATUS, { targetPath: options.targetPath });
284
+ }
285
+ }
286
+ try {
287
+ const entries = await walkDirectory({ rootDir: options.rootDir, targetPath: options.targetPath, depthLimit: 0 });
288
+ if (options.targetPath && entries.length === 0) {
289
+ setIndexStatus(db, IDLE_STATUS, { targetPath: options.targetPath, error: null });
290
+ return `Reindex: target_path "${options.targetPath}" matched no files (path may not exist, or is fully gitignored). No-op.`;
291
+ }
292
+ // GUARD: if the unscoped reindex walks 0 source files but the DB already contains files,
293
+ // this almost certainly means the user reindexed from the wrong cwd or the project moved.
294
+ // Require force=true to delete previously indexed files in that case.
295
+ const files = entries.filter((e) => !e.isDirectory);
296
+ const sourceFiles = files.filter((file) => isSupportedFile(file.path));
297
+ const indexedPathsBefore = new Set(db.getAllFiles().map((file) => file.path));
298
+ const previouslyIndexed = indexedPathsBefore.size;
299
+ const extractorStamp = getExtractorStamp();
300
+ const extractorChanged = !options.targetPath
301
+ && previouslyIndexed > 0
302
+ && db.getMeta("extractor_stamp") !== extractorStamp;
303
+ const force = options.force === true || extractorChanged || db.getMeta("index_repair_required") === "1";
304
+ if (!options.targetPath && sourceFiles.length === 0 && previouslyIndexed > 0 && !options.force) {
305
+ setIndexStatus(db, IDLE_STATUS, { targetPath: options.targetPath, error: null });
306
+ return (`Reindex: walker found 0 source files but ${previouslyIndexed} files are already indexed. ` +
307
+ `Refusing to delete the existing index. If the project moved or files were truly removed, ` +
308
+ `pass force: true.`);
309
+ }
310
+ const currentPaths = new Set(sourceFiles.map((f) => f.relativePath));
311
+ const currentHashes = new Map();
312
+ let totalSymbols = 0;
313
+ let totalImports = 0;
314
+ let indexed = 0;
315
+ let degradedCount = 0;
316
+ let skipped = files.length - sourceFiles.length;
317
+ const resolvablePaths = new Set([...indexedPathsBefore, ...currentPaths]);
318
+ for (const file of sourceFiles) {
319
+ try {
320
+ // Hard size cap to defend against checked-in minified bundles and other megabyte-scale
321
+ // pseudo-source files. Default 5MB; env-var override for projects that genuinely need
322
+ // a higher cap.
323
+ const sizeCap = maxSourceFileSize();
324
+ const fileStats = await stat(file.path).catch(() => null);
325
+ if (fileStats && fileStats.size > sizeCap) {
326
+ process.stderr.write(`[contextpro/reindex] skipping oversized file (${(fileStats.size / 1024 / 1024).toFixed(2)} MB > ${(sizeCap / 1024 / 1024).toFixed(2)} MB cap): ${file.relativePath}\n`);
327
+ currentHashes.set(file.relativePath, hashContent(`oversize:${file.relativePath}:${fileStats.size}`));
328
+ skipped++;
329
+ continue;
330
+ }
331
+ // Sniff the first 8KB for null bytes before reading as UTF-8. Files like images or
332
+ // compiled binaries renamed with a supported extension would otherwise crash readFile
333
+ // and leave currentHashes without an entry — making the file look "removed" on next
334
+ // reindex even though it still exists on disk.
335
+ const snapshot = await readSourceSnapshot(file.path);
336
+ const buf = Buffer.from(snapshot.content, "utf8");
337
+ const sniff = buf.subarray(0, Math.min(buf.length, 8192));
338
+ let looksBinary = false;
339
+ for (let i = 0; i < sniff.length; i++) {
340
+ if (sniff[i] === 0) {
341
+ looksBinary = true;
342
+ break;
343
+ }
344
+ }
345
+ if (looksBinary) {
346
+ process.stderr.write(`[contextpro/reindex] skipping binary file: ${file.relativePath}\n`);
347
+ // Still mark the path as "current" so it isn't deleted from the index next pass.
348
+ currentHashes.set(file.relativePath, hashContent(`binary:${file.relativePath}:${buf.length}`));
349
+ skipped++;
350
+ continue;
351
+ }
352
+ const content = buf.toString("utf-8");
353
+ const hash = hashContent(content);
354
+ currentHashes.set(file.relativePath, hash);
355
+ const existing = db.getFile(file.relativePath);
356
+ if (existing && existing.content_hash === hash && !force) {
357
+ db.updateFileFingerprint(file.relativePath, snapshot.stats.size, snapshot.stats.mtimeMs, snapshot.stats.ctimeMs, hash);
358
+ totalImports += db.getImportsFrom(existing.id).length;
359
+ skipped++;
360
+ continue;
361
+ }
362
+ const analysis = await analyzeContent(file.path, content);
363
+ const flatSymbols = flattenSymbols(analysis.symbols);
364
+ const lang = detectLanguage(file.path);
365
+ const fileSize = snapshot.stats.size;
366
+ const fileMtimeMs = snapshot.stats.mtimeMs;
367
+ const imports = extractImports(content, file.path);
368
+ const callSites = extractCallSites(content, file.relativePath);
369
+ const fileId = db.transaction(() => {
370
+ const fileId = db.upsertFile(file.relativePath, hash, analysis.header, lang, fileSize, fileMtimeMs, snapshot.stats.ctimeMs);
371
+ db.upsertSymbols(fileId, flatSymbols.map((s) => ({
372
+ name: s.name,
373
+ kind: s.kind,
374
+ lineStart: s.line,
375
+ lineEnd: s.endLine,
376
+ signature: s.signature,
377
+ parentIndex: s.parentIndex,
378
+ })));
379
+ db.upsertFileSearchDocument({
380
+ path: file.relativePath,
381
+ header: analysis.header,
382
+ language: lang,
383
+ symbols: flatSymbols.map((s) => ({ name: s.name, kind: s.kind, signature: s.signature })),
384
+ content, // Full source for local FTS; embedding summaries stay separate.
385
+ });
386
+ replaceFileImports(db, options.rootDir, fileId, file.relativePath, imports, resolvablePaths);
387
+ db.upsertCallSites(fileId, callSites);
388
+ return fileId;
389
+ });
390
+ totalImports += imports.length;
391
+ totalSymbols += flatSymbols.length;
392
+ indexed++;
393
+ }
394
+ catch (error) {
395
+ const message = error instanceof Error ? error.message : String(error);
396
+ process.stderr.write(`[contextpro/reindex] degraded: failed to process ${file.relativePath}: ${message}\n`);
397
+ degradedCount++;
398
+ skipped++;
399
+ }
400
+ }
401
+ const removed = options.targetPath ? [] : db.getRemovedFiles(currentPaths);
402
+ for (const path of removed)
403
+ db.deleteFile(path);
404
+ if (removed.length > 0 || db.getAllFiles().some((file) => !indexedPathsBefore.has(file.path))) {
405
+ reconnectStoredImports(db, options.rootDir);
406
+ }
407
+ let autoHubCount = 0;
408
+ // Auto-hub generation makes a chat-model call per hub — off by default so reindex
409
+ // (including the background reconcile-driven path) stays fast, quiet, and free.
410
+ if (!options.targetPath && isAutoHubsEnabled(options.autoHubs)) {
411
+ const hubResult = await autoGenerateHubData(options.rootDir).catch(() => null);
412
+ autoHubCount = hubResult?.generated.length ?? 0;
413
+ }
414
+ const indexedRelativePaths = sourceFiles.map((f) => f.relativePath);
415
+ const noEmbed = options.noEmbed === true || isReindexEmbedDisabled();
416
+ let fileEmbedCount = 0;
417
+ let identifierEmbedCount = 0;
418
+ let memoryEmbedCount = 0;
419
+ let embedError = null;
420
+ if (!noEmbed) {
421
+ // Only a semantic rebuild owns the vector-space identity. A structural-only
422
+ // worker may use another model; it must not erase another session's vectors.
423
+ // Validate before content-hash checks to prevent cross-model vector reuse.
424
+ db.recordEmbeddingProvider(getEmbedProvider(), getEmbedModel());
425
+ try {
426
+ const [files, identifiers] = await Promise.all([
427
+ refreshFileSearchEmbeddings({ rootDir: options.rootDir, relativePaths: indexedRelativePaths }),
428
+ refreshIdentifierEmbeddings({ rootDir: options.rootDir, relativePaths: indexedRelativePaths }),
429
+ ]);
430
+ fileEmbedCount = files;
431
+ identifierEmbedCount = identifiers;
432
+ memoryEmbedCount = await refreshMemoryEmbeddings(options.rootDir);
433
+ }
434
+ catch (error) {
435
+ embedError = error instanceof Error ? error.message : String(error);
436
+ db.setMeta("embeddings_stale", "true");
437
+ db.setMeta("embeddings_stale_reason", embedError);
438
+ process.stderr.write(`[contextpro/reindex] degraded: embedding population failed (${embedError}); index is structural-only until next reindex\n`);
439
+ }
440
+ }
441
+ const duration = Date.now() - start;
442
+ const structuralChanged = force || indexed > 0 || removed.length > 0;
443
+ if (degradedCount > 0)
444
+ requireIndexRepair(db);
445
+ else if (structuralChanged)
446
+ db.bumpIndexGeneration();
447
+ db.setMeta("last_reindex_at", String(Date.now()));
448
+ db.setMeta("last_reindex_duration_ms", String(duration));
449
+ db.setMeta("last_auto_hub_count", String(autoHubCount));
450
+ db.setMeta("last_embed_files_populated", String(fileEmbedCount));
451
+ db.setMeta("last_embed_identifiers_populated", String(identifierEmbedCount));
452
+ db.setMeta("last_embed_memories_populated", String(memoryEmbedCount));
453
+ db.setMeta("last_embed_skipped", noEmbed ? "true" : "false");
454
+ db.setMeta("last_embed_error", embedError ?? "");
455
+ if (!embedError && !noEmbed && !options.targetPath)
456
+ db.clearEmbeddingsStaleFlag();
457
+ setIndexStatus(db, IDLE_STATUS, { targetPath: options.targetPath, error: null });
458
+ invalidateSearchCache();
459
+ invalidateIdentifierSearchCache();
460
+ if (!options.targetPath) {
461
+ db.setMeta("extractor_stamp", extractorStamp);
462
+ if (degradedCount === 0)
463
+ db.setMeta("index_repair_required", "");
464
+ await toolUpsertMemoryNode({
465
+ rootDir: options.rootDir,
466
+ type: "note",
467
+ label: "index-snapshot",
468
+ content: `Reindex completed at ${new Date().toISOString()}. Files: ${files.length} total, ${indexed} indexed, ${skipped} skipped. Symbols: ${totalSymbols}. Imports: ${totalImports}. Duration: ${duration}ms.`,
469
+ scope: "project",
470
+ skipEmbedding: noEmbed,
471
+ }).catch(() => undefined);
472
+ }
473
+ const lines = [
474
+ `Reindex complete in ${duration}ms`,
475
+ ` Files: ${files.length} total, ${indexed} indexed, ${skipped} skipped, ${removed.length} removed`,
476
+ ` Symbols: ${totalSymbols} extracted`,
477
+ ` Imports: ${totalImports} resolved`,
478
+ ];
479
+ if (!options.targetPath)
480
+ lines.push(` Auto hubs: ${autoHubCount}`);
481
+ if (noEmbed) {
482
+ lines.push(` Embeddings: skipped (noEmbed=true or CONTEXTPRO_REINDEX_NO_EMBED set)`);
483
+ }
484
+ else if (embedError) {
485
+ lines.push(` Embeddings: failed (${embedError.slice(0, 120)}); search will fall back to keyword ranking`);
486
+ }
487
+ else {
488
+ lines.push(` Embeddings: ${fileEmbedCount} files, ${identifierEmbedCount} identifiers, ${memoryEmbedCount} memories`);
489
+ }
490
+ if (force)
491
+ lines.push(extractorChanged
492
+ ? " Mode: force (extractor changed; all files re-processed)"
493
+ : " Mode: force (all files re-processed)");
494
+ if (degradedCount > 0)
495
+ lines.push(` Index repair required: ${degradedCount} files failed to process`);
496
+ else if (!structuralChanged)
497
+ lines.push(" Index generation: unchanged (no structural changes)");
498
+ if (options.targetPath)
499
+ lines.push(` Scope: ${options.targetPath}`);
500
+ return lines.join("\n");
501
+ }
502
+ catch (error) {
503
+ requireIndexRepair(db);
504
+ setIndexStatus(db, IDLE_STATUS, {
505
+ targetPath: options.targetPath,
506
+ error: error instanceof Error ? error.message : String(error),
507
+ });
508
+ throw error;
509
+ }
510
+ }
511
+ export async function quickRefresh(rootDir, options = {}) {
512
+ const shared = await sharedCompute("quickRefresh", { rootDir, options });
513
+ if (shared)
514
+ return shared.value;
515
+ return quickRefreshAttempt(rootDir, options, 0);
516
+ }
517
+ async function quickRefreshAttempt(rootDir, options, retry) {
518
+ const startedAt = Date.now();
519
+ const waitState = await waitForIndexingCompletion(rootDir);
520
+ if (waitState.timedOut) {
521
+ return {
522
+ staleCount: 0,
523
+ remainingStaleCount: 0,
524
+ refreshed: false,
525
+ complete: false,
526
+ waitedForIndexing: waitState.waited,
527
+ indexingTimedOut: true,
528
+ };
529
+ }
530
+ const db = await getDB(rootDir);
531
+ const indexedBefore = db.getAllFiles();
532
+ const extractorStamp = getExtractorStamp();
533
+ if (db.getMeta("index_repair_required") === "1"
534
+ || (indexedBefore.length > 0 && db.getMeta("extractor_stamp") !== extractorStamp)) {
535
+ const generationBefore = db.getIndexGeneration();
536
+ await reindex({ rootDir, force: true, noEmbed: true, autoHubs: false });
537
+ return {
538
+ staleCount: indexedBefore.length,
539
+ remainingStaleCount: db.getMeta("extractor_stamp") === extractorStamp && db.getMeta("index_repair_required") !== "1" ? 0 : Math.max(1, indexedBefore.length),
540
+ refreshed: db.getIndexGeneration() > generationBefore,
541
+ complete: db.getMeta("extractor_stamp") === extractorStamp && db.getMeta("index_repair_required") !== "1",
542
+ fullReindex: true,
543
+ waitedForIndexing: waitState.waited,
544
+ indexingTimedOut: false,
545
+ };
546
+ }
547
+ const entries = await walkDirectory({ rootDir, depthLimit: 0 });
548
+ const files = entries.filter((e) => !e.isDirectory && isSupportedFile(e.path));
549
+ const indexedByPath = new Map(indexedBefore.map((file) => [file.path, file]));
550
+ const hashMode = (process.env.CONTEXTPRO_REFRESH ?? process.env.CONTEXTPLUS_REFRESH ?? "").trim().toLowerCase() === "hash";
551
+ const currentHashes = new Map();
552
+ const currentPaths = new Set(files.map((f) => f.relativePath));
553
+ const fingerprintUpdates = [];
554
+ for (const file of files) {
555
+ try {
556
+ const fileStats = await stat(file.path);
557
+ if (fileStats.size > maxSourceFileSize()) {
558
+ // Exclude newly oversized files and remove any prior indexed contents.
559
+ // Do this before fingerprint/hash reads, including explicit hash mode.
560
+ currentPaths.delete(file.relativePath);
561
+ continue;
562
+ }
563
+ const existing = indexedByPath.get(file.relativePath);
564
+ if (!hashMode && existing && matchesFileFingerprint(existing, fileStats)) {
565
+ currentHashes.set(file.relativePath, existing.content_hash);
566
+ continue;
567
+ }
568
+ const snapshot = await readSourceSnapshot(file.path);
569
+ const hash = hashContent(snapshot.content);
570
+ currentHashes.set(file.relativePath, hash);
571
+ if (existing?.content_hash === hash
572
+ && !matchesFileFingerprint(existing, snapshot.stats)) {
573
+ fingerprintUpdates.push({ path: file.relativePath, size: snapshot.stats.size, mtimeMs: snapshot.stats.mtimeMs, ctimeMs: snapshot.stats.ctimeMs, hash });
574
+ }
575
+ }
576
+ catch (error) {
577
+ const message = error instanceof Error ? error.message : String(error);
578
+ process.stderr.write(`[contextpro/quickRefresh] read failed for ${file.relativePath}: ${message}\n`);
579
+ }
580
+ }
581
+ const stale = db.getStaleFiles(currentHashes);
582
+ const added = [...currentPaths].filter((path) => !indexedByPath.has(path));
583
+ const removed = db.getRemovedFiles(currentPaths);
584
+ let totalStale = new Set([...stale, ...added, ...removed]).size;
585
+ if (totalStale === 0) {
586
+ for (const update of fingerprintUpdates)
587
+ db.updateFileFingerprint(update.path, update.size, update.mtimeMs, update.ctimeMs, update.hash);
588
+ return { staleCount: 0, remainingStaleCount: 0, refreshed: false, complete: true, waitedForIndexing: waitState.waited, indexingTimedOut: false };
589
+ }
590
+ const acquired = tryAcquireIndexLock(db, "<automatic-refresh>");
591
+ if (!acquired) {
592
+ const waited = await waitForIndexingCompletion(rootDir);
593
+ if (waited.timedOut || retry >= 2) {
594
+ return { staleCount: totalStale, remainingStaleCount: totalStale, refreshed: false, complete: false, waitedForIndexing: true, indexingTimedOut: waited.timedOut };
595
+ }
596
+ return quickRefreshAttempt(rootDir, options, retry + 1);
597
+ }
598
+ let refreshError = null;
599
+ try {
600
+ // Another process may have refreshed between our initial probe and lock
601
+ // acquisition. Re-check the hashes against the current DB before doing work.
602
+ const staleAfterLock = db.getStaleFiles(currentHashes);
603
+ const indexedPathsAfterLock = new Set(db.getAllFiles().map((file) => file.path));
604
+ const addedAfterLock = [...currentPaths].filter((path) => !indexedPathsAfterLock.has(path));
605
+ const removedAfterLock = db.getRemovedFiles(currentPaths);
606
+ const removedPathSet = new Set(removedAfterLock);
607
+ // getStaleFiles also reports rows absent from currentHashes. Those paths are
608
+ // deletions, not readable changed files; process them only via deleteFile.
609
+ const changedAfterLock = [...new Set([...staleAfterLock, ...addedAfterLock])]
610
+ .filter((path) => !removedPathSet.has(path));
611
+ totalStale = new Set([...changedAfterLock, ...removedAfterLock]).size;
612
+ if (totalStale === 0) {
613
+ for (const update of fingerprintUpdates)
614
+ db.updateFileFingerprint(update.path, update.size, update.mtimeMs, update.ctimeMs, update.hash);
615
+ return { staleCount: 0, remainingStaleCount: 0, refreshed: false, complete: true, waitedForIndexing: true, indexingTimedOut: false };
616
+ }
617
+ const maxStale = Math.max(1, parseInt(process.env.CONTEXTPRO_QUICKREFRESH_MAX_FILES ?? process.env.CONTEXTPLUS_QUICKREFRESH_MAX_FILES ?? "30", 10));
618
+ if (totalStale <= maxStale) {
619
+ const changedSymbols = [];
620
+ const resolvablePaths = new Set([...indexedPathsAfterLock, ...currentPaths]);
621
+ const indexedPaths = [];
622
+ let degradedCount = 0;
623
+ for (const path of changedAfterLock) {
624
+ try {
625
+ const fullPath = resolveProjectPath(rootDir, path, "indexed file");
626
+ if ((await stat(fullPath)).size > maxSourceFileSize()) {
627
+ // The file may have grown since the initial scan while awaiting the lock.
628
+ if (db.getFile(path)) {
629
+ db.deleteFile(path);
630
+ removedAfterLock.push(path);
631
+ }
632
+ continue;
633
+ }
634
+ const snapshot = await readSourceSnapshot(fullPath);
635
+ const content = snapshot.content;
636
+ const hash = hashContent(content);
637
+ const analysis = await analyzeContent(fullPath, content);
638
+ const flatSymbols = flattenSymbols(analysis.symbols);
639
+ const lang = detectLanguage(fullPath);
640
+ const fileStats = snapshot.stats;
641
+ const fileSize = fileStats.size;
642
+ const fileMtimeMs = fileStats.mtimeMs;
643
+ const existingFile = db.getFile(path);
644
+ const oldSymbolNames = existingFile ? new Set(db.getSymbolsByFile(existingFile.id).map((s) => s.name)) : new Set();
645
+ const newSymbolNames = new Set(flatSymbols.map((s) => s.name));
646
+ const imports = extractImports(content, fullPath);
647
+ const callSites = extractCallSites(content, path);
648
+ const fileId = db.transaction(() => {
649
+ const fileId = db.upsertFile(path, hash, analysis.header, lang, fileSize, fileMtimeMs, snapshot.stats.ctimeMs);
650
+ db.upsertSymbols(fileId, flatSymbols.map((s) => ({
651
+ name: s.name, kind: s.kind, lineStart: s.line, lineEnd: s.endLine, signature: s.signature, parentIndex: s.parentIndex,
652
+ })));
653
+ db.upsertFileSearchDocument({
654
+ path,
655
+ header: analysis.header,
656
+ language: lang,
657
+ symbols: flatSymbols.map((s) => ({ name: s.name, kind: s.kind, signature: s.signature })),
658
+ content, // Full source for local FTS; embedding summaries stay separate.
659
+ });
660
+ replaceFileImports(db, rootDir, fileId, path, imports, resolvablePaths);
661
+ db.upsertCallSites(fileId, callSites);
662
+ return fileId;
663
+ });
664
+ for (const name of newSymbolNames) {
665
+ if (!oldSymbolNames.has(name))
666
+ changedSymbols.push(`+${path}:${name}`);
667
+ }
668
+ for (const name of oldSymbolNames) {
669
+ if (!newSymbolNames.has(name))
670
+ changedSymbols.push(`-${path}:${name}`);
671
+ }
672
+ indexedPaths.push(path);
673
+ }
674
+ catch (error) {
675
+ // A failed quick-refresh of one file leaves its prior (stale) imports intact. Log it so
676
+ // the user knows the graph for that file may be out of date until the next full reindex.
677
+ degradedCount++;
678
+ const message = error instanceof Error ? error.message : String(error);
679
+ process.stderr.write(`[contextpro/quickrefresh] skipped ${path}: ${message}\n`);
680
+ }
681
+ }
682
+ if (degradedCount > 0) {
683
+ process.stderr.write(`[contextpro/quickrefresh] ${degradedCount} file(s) failed quick refresh; run reindex --force for a clean rebuild\n`);
684
+ }
685
+ for (const path of removedAfterLock)
686
+ db.deleteFile(path);
687
+ if (removedAfterLock.length > 0 || indexedPaths.some((path) => !indexedPathsAfterLock.has(path))) {
688
+ reconnectStoredImports(db, rootDir);
689
+ }
690
+ const structuralChanged = indexedPaths.length > 0 || removedAfterLock.length > 0;
691
+ if (structuralChanged) {
692
+ db.bumpIndexGeneration();
693
+ invalidateSearchCache();
694
+ invalidateIdentifierSearchCache();
695
+ }
696
+ db.setMeta("extractor_stamp", extractorStamp);
697
+ db.setMeta("last_reindex_at", String(Date.now()));
698
+ db.setMeta("last_reindex_duration_ms", String(Date.now() - startedAt));
699
+ if (options.includeEmbeddings !== false) {
700
+ db.recordEmbeddingProvider(getEmbedProvider(), getEmbedModel());
701
+ // Deleted paths cannot be reread structurally, but embedding refreshers use
702
+ // them to purge their persistent vectors and invalidate cached indexes.
703
+ const embeddingRefreshPaths = [...new Set([...changedAfterLock, ...removedAfterLock])];
704
+ await Promise.all([
705
+ refreshFileSearchEmbeddings({ rootDir, relativePaths: embeddingRefreshPaths }).catch(() => 0),
706
+ refreshIdentifierEmbeddings({ rootDir, relativePaths: embeddingRefreshPaths }).catch(() => 0),
707
+ ]);
708
+ }
709
+ return {
710
+ staleCount: totalStale,
711
+ remainingStaleCount: degradedCount,
712
+ refreshed: structuralChanged,
713
+ complete: degradedCount === 0,
714
+ waitedForIndexing: waitState.waited,
715
+ indexingTimedOut: false,
716
+ changedSymbols,
717
+ };
718
+ }
719
+ return { staleCount: totalStale, remainingStaleCount: totalStale, refreshed: false, complete: false, waitedForIndexing: waitState.waited, indexingTimedOut: false };
720
+ }
721
+ catch (error) {
722
+ refreshError = error instanceof Error ? error.message : String(error);
723
+ requireIndexRepair(db);
724
+ throw error;
725
+ }
726
+ finally {
727
+ setIndexStatus(db, IDLE_STATUS, { error: refreshError });
728
+ }
729
+ }
730
+ /**
731
+ * Re-index the structural graph (symbols, imports, call sites) plus embeddings for
732
+ * an EXPLICIT list of changed files — no directory walk, no whole-tree hashing.
733
+ *
734
+ * This is the per-file core of quickRefresh, exposed so the embedding tracker can
735
+ * keep related/blast_radius/test_map current as agents edit, scaling with the size
736
+ * of the edit rather than the size of the repo. Missing/unsupported files are
737
+ * counted as skipped, never thrown.
738
+ */
739
+ export async function refreshFilesStructural(rootDir, relativePaths, options = {}) {
740
+ const unique = [...new Set(relativePaths.map((p) => p.replace(/\\/g, "/")))].filter((p) => p.length > 0);
741
+ const targets = unique.filter((p) => isSupportedFile(p));
742
+ const removalCandidates = [...new Set(options.removedPaths ?? [])].filter(isSupportedFile);
743
+ if (targets.length === 0 && removalCandidates.length === 0) {
744
+ return { indexed: 0, skipped: unique.length, changedSymbols: [] };
745
+ }
746
+ const initialWait = await waitForIndexingCompletion(rootDir);
747
+ if (initialWait.timedOut)
748
+ return { indexed: 0, skipped: unique.length, changedSymbols: [] };
749
+ const db = await getDB(rootDir);
750
+ let acquired = tryAcquireIndexLock(db, "<tracker-refresh>");
751
+ if (!acquired) {
752
+ const waited = await waitForIndexingCompletion(rootDir);
753
+ if (waited.timedOut)
754
+ return { indexed: 0, skipped: unique.length, changedSymbols: [] };
755
+ acquired = tryAcquireIndexLock(db, "<tracker-refresh>");
756
+ if (!acquired)
757
+ return { indexed: 0, skipped: unique.length, changedSymbols: [] };
758
+ }
759
+ let refreshError = null;
760
+ try {
761
+ const changedSymbols = [];
762
+ const indexedPaths = [];
763
+ const indexedPathsBefore = new Set(db.getAllFiles().map((file) => file.path));
764
+ const resolvablePaths = new Set([...indexedPathsBefore, ...targets]);
765
+ const removedPaths = [];
766
+ let skipped = unique.length - targets.length;
767
+ for (const path of removalCandidates) {
768
+ const fullPath = resolveProjectPath(rootDir, path, "removed indexed file");
769
+ try {
770
+ await stat(fullPath);
771
+ // It was recreated while this refresh waited. Leave it for the next sweep.
772
+ continue;
773
+ }
774
+ catch (error) {
775
+ const code = error.code;
776
+ if (code !== "ENOENT" && code !== "ENOTDIR")
777
+ continue;
778
+ }
779
+ const file = db.getFile(path);
780
+ if (!file)
781
+ continue;
782
+ for (const symbol of db.getSymbolsByFile(file.id))
783
+ changedSymbols.push(`-${path}:${symbol.name}`);
784
+ db.deleteFile(path);
785
+ removedPaths.push(path);
786
+ resolvablePaths.delete(path);
787
+ }
788
+ let processed = 0;
789
+ for (const path of targets) {
790
+ // Yield to the event loop between files. tree-sitter parsing is synchronous
791
+ // and blocks the single thread; yielding lets concurrent tool calls and
792
+ // transport heartbeats interleave so a large refresh never stalls the server.
793
+ if (++processed % 16 === 0)
794
+ await new Promise((resolve) => setImmediate(resolve));
795
+ try {
796
+ const fullPath = resolveProjectPath(rootDir, path, "indexed file");
797
+ const snapshot = await readSourceSnapshot(fullPath);
798
+ const content = snapshot.content;
799
+ const hash = hashContent(content);
800
+ const analysis = await analyzeContent(fullPath, content);
801
+ const flatSymbols = flattenSymbols(analysis.symbols);
802
+ const lang = detectLanguage(fullPath);
803
+ const fileStats = snapshot.stats;
804
+ const fileSize = fileStats.size;
805
+ const fileMtimeMs = fileStats.mtimeMs;
806
+ const existingFile = db.getFile(path);
807
+ const oldSymbolNames = existingFile ? new Set(db.getSymbolsByFile(existingFile.id).map((s) => s.name)) : new Set();
808
+ const newSymbolNames = new Set(flatSymbols.map((s) => s.name));
809
+ const imports = extractImports(content, fullPath);
810
+ const callSites = extractCallSites(content, path);
811
+ const fileId = db.transaction(() => {
812
+ const fileId = db.upsertFile(path, hash, analysis.header, lang, fileSize, fileMtimeMs, snapshot.stats.ctimeMs);
813
+ db.upsertSymbols(fileId, flatSymbols.map((s) => ({
814
+ name: s.name, kind: s.kind, lineStart: s.line, lineEnd: s.endLine, signature: s.signature, parentIndex: s.parentIndex,
815
+ })));
816
+ db.upsertFileSearchDocument({
817
+ path,
818
+ header: analysis.header,
819
+ language: lang,
820
+ symbols: flatSymbols.map((s) => ({ name: s.name, kind: s.kind, signature: s.signature })),
821
+ content, // Full source for local FTS; embedding summaries stay separate.
822
+ });
823
+ replaceFileImports(db, rootDir, fileId, path, imports, resolvablePaths);
824
+ db.upsertCallSites(fileId, callSites);
825
+ return fileId;
826
+ });
827
+ for (const name of newSymbolNames) {
828
+ if (!oldSymbolNames.has(name))
829
+ changedSymbols.push(`+${path}:${name}`);
830
+ }
831
+ for (const name of oldSymbolNames) {
832
+ if (!newSymbolNames.has(name))
833
+ changedSymbols.push(`-${path}:${name}`);
834
+ }
835
+ indexedPaths.push(path);
836
+ }
837
+ catch (error) {
838
+ skipped++;
839
+ const message = error instanceof Error ? error.message : String(error);
840
+ process.stderr.write(`[contextpro/structural-refresh] skipped ${path}: ${message}\n`);
841
+ }
842
+ }
843
+ if (removedPaths.length > 0 || indexedPaths.some((path) => !indexedPathsBefore.has(path))) {
844
+ reconnectStoredImports(db, rootDir);
845
+ }
846
+ if (indexedPaths.length > 0 || removedPaths.length > 0) {
847
+ db.bumpIndexGeneration();
848
+ db.setMeta("last_reindex_at", String(Date.now()));
849
+ invalidateSearchCache();
850
+ invalidateIdentifierSearchCache();
851
+ if (options.includeEmbeddings !== false && !isReindexEmbedDisabled()) {
852
+ db.recordEmbeddingProvider(getEmbedProvider(), getEmbedModel());
853
+ const embeddingPaths = [...indexedPaths, ...removedPaths];
854
+ await Promise.all([
855
+ refreshFileSearchEmbeddings({ rootDir, relativePaths: embeddingPaths }).catch(() => 0),
856
+ refreshIdentifierEmbeddings({ rootDir, relativePaths: embeddingPaths }).catch(() => 0),
857
+ ]);
858
+ }
859
+ }
860
+ return { indexed: indexedPaths.length, skipped, removed: removedPaths.length, changedSymbols };
861
+ }
862
+ catch (error) {
863
+ refreshError = error instanceof Error ? error.message : String(error);
864
+ requireIndexRepair(db);
865
+ throw error;
866
+ }
867
+ finally {
868
+ setIndexStatus(db, IDLE_STATUS, { error: refreshError });
869
+ }
870
+ }
871
+ //# sourceMappingURL=reindex.js.map