contextpro-mcp 0.0.0-stage → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (466) hide show
  1. package/CHANGELOG.md +425 -0
  2. package/INSTALL.md +218 -0
  3. package/INSTRUCTIONS.md +33 -0
  4. package/LICENSE +21 -0
  5. package/NOTICE +12 -0
  6. package/README.md +89 -2
  7. package/SECURITY.md +29 -0
  8. package/agent-guide.md +354 -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 +95 -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 +61 -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 +9 -0
  179. package/build/core/root-guard.d.ts.map +1 -0
  180. package/build/core/root-guard.js +64 -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 +634 -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 +351 -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 +35 -0
  463. package/docs/architecture.md +35 -0
  464. package/docs/configuration.md +207 -0
  465. package/docs/maintenance.md +77 -0
  466. package/package.json +80 -4
@@ -0,0 +1,693 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ // Multi-provider vector embedding engine with cosine similarity search
3
+ // Supports Ollama (local) and OpenAI-compatible APIs (Gemini, OpenAI, etc.)
4
+ // Indexes file headers and symbols, caches embeddings to disk for speed
5
+ import { mkdir } from "fs/promises";
6
+ import { join } from "path";
7
+ import { getProvider, getEmbedModelName, getApiKey, getBaseUrl } from "./provider-config.js";
8
+ import { observeProviderCall } from "./provider-usage.js";
9
+ const EMBED_TIMEOUT_MS = 60_000;
10
+ let embedAbortController = new AbortController();
11
+ export function cancelAllEmbeddings() {
12
+ embedAbortController.abort();
13
+ embedAbortController = new AbortController();
14
+ }
15
+ // Provider configuration is centralized in provider-config.ts. See that file for the full
16
+ // env-var precedence (generic CONTEXTPRO_* → legacy → provider-native → default). Resolved once
17
+ // at module load to preserve prior behavior; restart the server to pick up env changes.
18
+ const EMBED_PROVIDER = getProvider();
19
+ const EMBED_MODEL = getEmbedModelName("ollama");
20
+ const OPENAI_EMBED_MODEL = getEmbedModelName("openai");
21
+ const OPENAI_API_KEY = getApiKey();
22
+ const OPENAI_BASE_URL = getBaseUrl();
23
+ const CACHE_DIR = ".contextpro";
24
+ const MIN_EMBED_BATCH_SIZE = 1;
25
+ const MAX_EMBED_BATCH_SIZE = 128;
26
+ const DEFAULT_OLLAMA_EMBED_BATCH_SIZE = 32;
27
+ const DEFAULT_REMOTE_EMBED_BATCH_SIZE = 8;
28
+ const MIN_EMBED_INPUT_CHARS = 1;
29
+ const SINGLE_INPUT_SHRINK_FACTOR = 0.75;
30
+ const MAX_SINGLE_INPUT_RETRIES = 40;
31
+ const MIN_EMBED_CHUNK_CHARS = 256;
32
+ const DEFAULT_EMBED_CHUNK_CHARS = 2000;
33
+ const MAX_EMBED_CHUNK_CHARS = 8000;
34
+ let ollamaClient = null;
35
+ const ollamaSignals = new AsyncLocalStorage();
36
+ async function getOllamaClient() {
37
+ if (!ollamaClient) {
38
+ const { Ollama } = await import("ollama");
39
+ // Ollama embed() serializes its request body; a `signal` property there does
40
+ // not cancel HTTP. Install the signal on the actual fetch instead.
41
+ ollamaClient = new Ollama({ host: process.env.OLLAMA_HOST,
42
+ fetch: (input, init) => fetch(input, { ...init, signal: ollamaSignals.getStore() ?? init?.signal }),
43
+ });
44
+ }
45
+ return ollamaClient;
46
+ }
47
+ async function callOllamaEmbed(input, signal) {
48
+ const client = await getOllamaClient();
49
+ const options = getEmbedRuntimeOptions();
50
+ const request = { model: EMBED_MODEL, input };
51
+ if (options)
52
+ request.options = options;
53
+ try {
54
+ const response = await observeProviderCall({ kind: "embedding", provider: "ollama", model: EMBED_MODEL }, () => ollamaSignals.run(signal, () => client.embed(request)), result => ({ input: result.prompt_eval_count }));
55
+ return response.embeddings;
56
+ }
57
+ catch (error) {
58
+ throw new Error(formatOllamaModelError(error, "embedding", EMBED_MODEL));
59
+ }
60
+ }
61
+ async function callOpenAIEmbed(input, signal) {
62
+ return observeProviderCall({ kind: "embedding", provider: "openai", model: OPENAI_EMBED_MODEL }, async () => {
63
+ const url = `${OPENAI_BASE_URL.replace(/\/+$/, "")}/embeddings`;
64
+ const response = await fetch(url, {
65
+ method: "POST",
66
+ headers: {
67
+ "Content-Type": "application/json",
68
+ "Authorization": `Bearer ${OPENAI_API_KEY}`,
69
+ },
70
+ body: JSON.stringify({ model: OPENAI_EMBED_MODEL, input }),
71
+ signal,
72
+ });
73
+ if (!response.ok) {
74
+ const body = await response.text().catch(() => "");
75
+ throw new Error(`OpenAI embed API error ${response.status}: ${body}`);
76
+ }
77
+ return await response.json();
78
+ }, result => ({ input: result.usage?.prompt_tokens ?? result.usage?.total_tokens })).then(data => data.data.map(item => item.embedding));
79
+ }
80
+ async function callProviderEmbed(input, signal) {
81
+ const deadline = queryDeadline.getStore();
82
+ // Cold index preparation must not consume the shared provider budget.
83
+ if (deadline)
84
+ signal = AbortSignal.any([signal, deadline.signal ??= AbortSignal.timeout(deadline.timeoutMs)]);
85
+ signal.throwIfAborted();
86
+ if (EMBED_PROVIDER === "openai") {
87
+ return callOpenAIEmbed(input, signal);
88
+ }
89
+ return callOllamaEmbed(input, signal);
90
+ }
91
+ function toIntegerOr(value, fallback) {
92
+ if (!value)
93
+ return fallback;
94
+ const parsed = Number.parseInt(value, 10);
95
+ return Number.isFinite(parsed) ? parsed : fallback;
96
+ }
97
+ function toOptionalInteger(value) {
98
+ if (!value)
99
+ return undefined;
100
+ const parsed = Number.parseInt(value, 10);
101
+ return Number.isFinite(parsed) ? parsed : undefined;
102
+ }
103
+ function toOptionalBoolean(value) {
104
+ if (!value)
105
+ return undefined;
106
+ const normalized = value.trim().toLowerCase();
107
+ if (normalized === "true" || normalized === "1" || normalized === "yes")
108
+ return true;
109
+ if (normalized === "false" || normalized === "0" || normalized === "no")
110
+ return false;
111
+ return undefined;
112
+ }
113
+ function getEmbedRuntimeOptions() {
114
+ if (EMBED_PROVIDER === "openai")
115
+ return undefined;
116
+ const options = {
117
+ num_gpu: toOptionalInteger(process.env.CONTEXTPRO_EMBED_NUM_GPU ?? process.env.CONTEXTPLUS_EMBED_NUM_GPU),
118
+ main_gpu: toOptionalInteger(process.env.CONTEXTPRO_EMBED_MAIN_GPU ?? process.env.CONTEXTPLUS_EMBED_MAIN_GPU),
119
+ num_thread: toOptionalInteger(process.env.CONTEXTPRO_EMBED_NUM_THREAD ?? process.env.CONTEXTPLUS_EMBED_NUM_THREAD),
120
+ num_batch: toOptionalInteger(process.env.CONTEXTPRO_EMBED_NUM_BATCH ?? process.env.CONTEXTPLUS_EMBED_NUM_BATCH),
121
+ num_ctx: toOptionalInteger(process.env.CONTEXTPRO_EMBED_NUM_CTX ?? process.env.CONTEXTPLUS_EMBED_NUM_CTX),
122
+ low_vram: toOptionalBoolean(process.env.CONTEXTPRO_EMBED_LOW_VRAM ?? process.env.CONTEXTPLUS_EMBED_LOW_VRAM),
123
+ };
124
+ if (Object.values(options).every((value) => value === undefined))
125
+ return undefined;
126
+ return options;
127
+ }
128
+ export function getEmbeddingBatchSize() {
129
+ const defaultSize = EMBED_PROVIDER === "ollama"
130
+ ? DEFAULT_OLLAMA_EMBED_BATCH_SIZE
131
+ : DEFAULT_REMOTE_EMBED_BATCH_SIZE;
132
+ const requested = toIntegerOr(process.env.CONTEXTPRO_EMBED_BATCH_SIZE ?? process.env.CONTEXTPLUS_EMBED_BATCH_SIZE, defaultSize);
133
+ return Math.min(MAX_EMBED_BATCH_SIZE, Math.max(MIN_EMBED_BATCH_SIZE, requested));
134
+ }
135
+ export function getEmbedChunkChars() {
136
+ const requested = toIntegerOr(process.env.CONTEXTPRO_EMBED_CHUNK_CHARS ?? process.env.CONTEXTPLUS_EMBED_CHUNK_CHARS, DEFAULT_EMBED_CHUNK_CHARS);
137
+ return Math.min(MAX_EMBED_CHUNK_CHARS, Math.max(MIN_EMBED_CHUNK_CHARS, requested));
138
+ }
139
+ export function getEmbedProvider() {
140
+ return EMBED_PROVIDER;
141
+ }
142
+ export function getEmbedModel() {
143
+ return EMBED_PROVIDER === "openai" ? OPENAI_EMBED_MODEL : EMBED_MODEL;
144
+ }
145
+ export function hasCompatibleEmbeddingModel(db) {
146
+ return db.getMeta("embed_provider") === getEmbedProvider() && db.getMeta("embed_model") === getEmbedModel();
147
+ }
148
+ export function currentEmbeddingModel() {
149
+ return { provider: getEmbedProvider(), model: getEmbedModel() };
150
+ }
151
+ /** Include durable identity/population changes made by other MCP workers. */
152
+ export function embeddingCacheIdentity(db) {
153
+ return JSON.stringify([getEmbedProvider(), getEmbedModel(), db.getMeta("embed_provider"), db.getMeta("embed_model"), db.getMeta("embedding_revision")]);
154
+ }
155
+ /** Memory writes cannot migrate another worker's vector space implicitly. */
156
+ export async function embedMemoryText(db, text) {
157
+ db.transaction(() => {
158
+ if (!db.getMeta("embed_provider") && !db.getMeta("embed_model")) {
159
+ const stats = db.getEmbeddingStats();
160
+ if (!stats.total && !stats.memory)
161
+ db.recordEmbeddingProvider(getEmbedProvider(), getEmbedModel());
162
+ }
163
+ });
164
+ if (!hasCompatibleEmbeddingModel(db))
165
+ return [];
166
+ const [vector] = await fetchEmbedding(text);
167
+ return hasCompatibleEmbeddingModel(db) ? vector : [];
168
+ }
169
+ function getErrorMessage(error) {
170
+ if (error instanceof Error)
171
+ return error.message;
172
+ return String(error);
173
+ }
174
+ function formatOllamaModelError(error, kind, model) {
175
+ const message = getErrorMessage(error);
176
+ const lower = message.toLowerCase();
177
+ const installHint = "Install Ollama from https://ollama.com/download, then run `ollama serve`.";
178
+ const modelHint = `Run \`ollama pull ${model}\`, or set OLLAMA_${kind === "embedding" ? "EMBED" : "CHAT"}_MODEL to any model shown by \`ollama list\`.`;
179
+ if (lower.includes("econnrefused")
180
+ || lower.includes("fetch failed")
181
+ || lower.includes("connection refused")
182
+ || lower.includes("connect")) {
183
+ return `Ollama is not reachable while loading the ${kind} model "${model}". ${installHint} ${modelHint} Original error: ${message}`;
184
+ }
185
+ if (lower.includes("not found")
186
+ || lower.includes("no such model")
187
+ || lower.includes("model")) {
188
+ return `Ollama ${kind} model "${model}" is not available. ${modelHint} Original error: ${message}`;
189
+ }
190
+ return `Ollama ${kind} model "${model}" failed. ${installHint} ${modelHint} Original error: ${message}`;
191
+ }
192
+ function isContextLengthError(error) {
193
+ const message = getErrorMessage(error).toLowerCase();
194
+ return message.includes("input length exceeds context length")
195
+ || (message.includes("context") && message.includes("exceed"))
196
+ || message.includes("maximum context length");
197
+ }
198
+ function shrinkEmbeddingInput(input) {
199
+ if (input.length <= MIN_EMBED_INPUT_CHARS)
200
+ return input;
201
+ const nextLength = Math.max(MIN_EMBED_INPUT_CHARS, Math.floor(input.length * SINGLE_INPUT_SHRINK_FACTOR));
202
+ if (nextLength >= input.length)
203
+ return input.slice(0, input.length - 1);
204
+ return input.slice(0, nextLength);
205
+ }
206
+ async function embedSingleAdaptive(input) {
207
+ let candidate = input;
208
+ for (let attempt = 0; attempt <= MAX_SINGLE_INPUT_RETRIES; attempt++) {
209
+ try {
210
+ const timeoutCtrl = AbortSignal.timeout(EMBED_TIMEOUT_MS);
211
+ const signal = AbortSignal.any([embedAbortController.signal, timeoutCtrl]);
212
+ const embeddings = await callProviderEmbed([candidate], signal);
213
+ if (!embeddings[0])
214
+ throw new Error("Missing embedding vector in response");
215
+ return embeddings[0];
216
+ }
217
+ catch (error) {
218
+ if (!isContextLengthError(error))
219
+ throw error;
220
+ const nextCandidate = shrinkEmbeddingInput(candidate);
221
+ if (nextCandidate.length === candidate.length)
222
+ throw error;
223
+ candidate = nextCandidate;
224
+ }
225
+ }
226
+ throw new Error("Unable to embed oversized input after adaptive retries");
227
+ }
228
+ async function embedBatchAdaptive(batch) {
229
+ try {
230
+ const timeoutCtrl = AbortSignal.timeout(EMBED_TIMEOUT_MS);
231
+ const signal = AbortSignal.any([embedAbortController.signal, timeoutCtrl]);
232
+ const embeddings = await callProviderEmbed(batch, signal);
233
+ if (embeddings.length !== batch.length) {
234
+ throw new Error(`Embedding response size mismatch: expected ${batch.length}, got ${embeddings.length}`);
235
+ }
236
+ return embeddings;
237
+ }
238
+ catch (error) {
239
+ if (!isContextLengthError(error))
240
+ throw error;
241
+ if (batch.length === 1) {
242
+ return [await embedSingleAdaptive(batch[0])];
243
+ }
244
+ const middle = Math.ceil(batch.length / 2);
245
+ const left = await embedBatchAdaptive(batch.slice(0, middle));
246
+ const right = await embedBatchAdaptive(batch.slice(middle));
247
+ return [...left, ...right];
248
+ }
249
+ }
250
+ function splitEmbeddingInput(input) {
251
+ const chunkChars = getEmbedChunkChars();
252
+ if (input.length <= chunkChars)
253
+ return [input];
254
+ const chunks = [];
255
+ for (let start = 0; start < input.length; start += chunkChars) {
256
+ chunks.push(input.slice(start, start + chunkChars));
257
+ }
258
+ return chunks;
259
+ }
260
+ function mergeEmbeddingVectors(vectors, weights) {
261
+ if (vectors.length === 0)
262
+ throw new Error("Cannot merge empty embedding vectors");
263
+ if (vectors.length === 1)
264
+ return vectors[0];
265
+ const dimension = vectors[0].length;
266
+ const merged = new Array(dimension).fill(0);
267
+ let totalWeight = 0;
268
+ for (let i = 0; i < vectors.length; i++) {
269
+ const vector = vectors[i];
270
+ if (vector.length !== dimension) {
271
+ throw new Error(`Embedding dimension mismatch: expected ${dimension}, got ${vector.length}`);
272
+ }
273
+ const weight = Math.max(1, weights[i] ?? 1);
274
+ totalWeight += weight;
275
+ for (let d = 0; d < dimension; d++)
276
+ merged[d] += vector[d] * weight;
277
+ }
278
+ if (totalWeight <= 0)
279
+ return vectors[0];
280
+ for (let d = 0; d < merged.length; d++)
281
+ merged[d] /= totalWeight;
282
+ return merged;
283
+ }
284
+ export async function fetchEmbedding(input) {
285
+ const inputs = Array.isArray(input) ? input : [input];
286
+ if (inputs.length === 0)
287
+ return [];
288
+ const chunkedInputs = inputs.map(splitEmbeddingInput);
289
+ const flattenedInputs = chunkedInputs.flat();
290
+ const batchSize = getEmbeddingBatchSize();
291
+ const flattenedEmbeddings = [];
292
+ for (let i = 0; i < flattenedInputs.length; i += batchSize) {
293
+ const batch = flattenedInputs.slice(i, i + batchSize);
294
+ flattenedEmbeddings.push(...await embedBatchAdaptive(batch));
295
+ }
296
+ const embeddings = [];
297
+ let offset = 0;
298
+ for (const chunks of chunkedInputs) {
299
+ const vectors = flattenedEmbeddings.slice(offset, offset + chunks.length);
300
+ if (vectors.length !== chunks.length) {
301
+ throw new Error(`Merged embedding size mismatch: expected ${chunks.length}, got ${vectors.length}`);
302
+ }
303
+ embeddings.push(mergeEmbeddingVectors(vectors, chunks.map((chunk) => chunk.length)));
304
+ offset += chunks.length;
305
+ }
306
+ return embeddings;
307
+ }
308
+ function hashContent(text) {
309
+ let h = 0;
310
+ for (let i = 0; i < text.length; i++)
311
+ h = ((h << 5) - h + text.charCodeAt(i)) | 0;
312
+ return h.toString(36);
313
+ }
314
+ function cosine(a, b) {
315
+ let dot = 0, normA = 0, normB = 0;
316
+ for (let i = 0; i < a.length; i++) {
317
+ dot += a[i] * b[i];
318
+ normA += a[i] * a[i];
319
+ normB += b[i] * b[i];
320
+ }
321
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
322
+ return denom === 0 ? 0 : dot / denom;
323
+ }
324
+ const SEARCH_STOPWORDS = new Set("a an the and or but for from to of in on at by with without that this these those is are was were be been being as it its after before using use how which when then than into through".split(" "));
325
+ export function searchTerms(text) {
326
+ return splitCamelCase(text).filter(term => !SEARCH_STOPWORDS.has(term));
327
+ }
328
+ function splitCamelCase(text) {
329
+ return text
330
+ .normalize("NFC")
331
+ .replace(/([a-z])([A-Z])/g, "$1 $2")
332
+ .replace(/([A-Z])([A-Z][a-z])/g, "$1 $2")
333
+ .toLowerCase()
334
+ .split(/[^\p{L}\p{M}\p{N}]+/u)
335
+ .filter((t) => t.length > 1 || /[^\x00-\x7F]/u.test(t));
336
+ }
337
+ function clamp01(value) {
338
+ if (value <= 0)
339
+ return 0;
340
+ if (value >= 1)
341
+ return 1;
342
+ return value;
343
+ }
344
+ function normalizeThreshold(value, fallback) {
345
+ if (value === undefined || !Number.isFinite(value))
346
+ return fallback;
347
+ if (value > 1)
348
+ return clamp01(value / 100);
349
+ return clamp01(value);
350
+ }
351
+ function normalizeWeight(value, fallback) {
352
+ if (value === undefined || !Number.isFinite(value) || value < 0)
353
+ return fallback;
354
+ return value;
355
+ }
356
+ function normalizeTopK(value, fallback) {
357
+ if (value === undefined || !Number.isFinite(value))
358
+ return fallback;
359
+ return Math.max(1, Math.floor(value));
360
+ }
361
+ function resolveSearchOptions(optionsOrTopK) {
362
+ const raw = typeof optionsOrTopK === "number" ? { topK: optionsOrTopK } : (optionsOrTopK ?? {});
363
+ return {
364
+ topK: normalizeTopK(raw.topK, 5),
365
+ semanticWeight: normalizeWeight(raw.semanticWeight, 0.72),
366
+ keywordWeight: normalizeWeight(raw.keywordWeight, 0.28),
367
+ minSemanticScore: normalizeThreshold(raw.minSemanticScore, 0),
368
+ minKeywordScore: normalizeThreshold(raw.minKeywordScore, 0),
369
+ minCombinedScore: normalizeThreshold(raw.minCombinedScore, 0.1),
370
+ requireKeywordMatch: raw.requireKeywordMatch ?? false,
371
+ requireSemanticMatch: raw.requireSemanticMatch ?? false,
372
+ fusion: raw.fusion === "weighted" ? "weighted" : "rrf",
373
+ };
374
+ }
375
+ /**
376
+ * Reciprocal rank fusion (Cormack, Clarke & Büttcher, SIGIR 2009): fuse two ranked lists by
377
+ * rank position rather than raw score value, sidestepping the scale mismatch between an
378
+ * unbounded/keyword-style score and a bounded cosine similarity. k=60 is the empirical
379
+ * default from the original paper's TREC evaluation. Result is normalized to [0, 1] by the
380
+ * best-possible score (rank 1 in both lists). That rank signal is subsequently
381
+ * calibrated with the candidate's absolute semantic/keyword evidence before it
382
+ * is exposed as a confidence-like combined score or compared to minCombinedScore.
383
+ */
384
+ const RRF_K = 60;
385
+ function reciprocalRankFusionScores(semanticScores, keywordScores) {
386
+ const n = semanticScores.length;
387
+ const buildRanks = (scores) => {
388
+ const ranks = new Array(n).fill(null);
389
+ const ranked = [...scores.keys()]
390
+ .filter((index) => scores[index] > 0)
391
+ .sort((left, right) => scores[right] - scores[left] || left - right);
392
+ ranked.forEach((index, rank) => { ranks[index] = rank + 1; });
393
+ return ranks;
394
+ };
395
+ const semanticRank = buildRanks(semanticScores.map((score) => Math.max(score, 0)));
396
+ const keywordRank = buildRanks(keywordScores);
397
+ const activeLists = Number(semanticRank.some((rank) => rank !== null)) + Number(keywordRank.some((rank) => rank !== null));
398
+ if (activeLists === 0)
399
+ return new Array(n).fill(0);
400
+ const bestPossible = activeLists / (RRF_K + 1);
401
+ return semanticScores.map((_, index) => {
402
+ const semantic = semanticRank[index] === null ? 0 : 1 / (RRF_K + semanticRank[index]);
403
+ const keyword = keywordRank[index] === null ? 0 : 1 / (RRF_K + keywordRank[index]);
404
+ return (semantic + keyword) / bestPossible;
405
+ });
406
+ }
407
+ function calibrateRrfScore(rankScore, semanticScore, keywordScore) {
408
+ // RRF says which result ranks first; it does not say whether the underlying
409
+ // signal is strong. Without this gate, a lone 0.16 cosine match becomes 100%.
410
+ const absoluteEvidence = Math.max(Math.max(semanticScore, 0), keywordScore);
411
+ return clamp01(rankScore * clamp01(absoluteEvidence));
412
+ }
413
+ const LOW_SEMANTIC_FLOOR = 0.15;
414
+ function dampLowSemanticScore(score, semanticScore, keywordScore, semanticAvailable) {
415
+ const semantic = Math.max(semanticScore, 0);
416
+ if (!semanticAvailable || semantic >= LOW_SEMANTIC_FLOOR)
417
+ return score;
418
+ // A lexical collision should remain visible as a low-confidence fallback, but it must not
419
+ // outrank a genuinely semantic result. The ceiling rises smoothly from 12% at semantic=0
420
+ // to just under 40% at the 0.15 semantic floor.
421
+ const ceiling = keywordScore * (0.12 + 0.28 * (semantic / LOW_SEMANTIC_FLOOR));
422
+ return Math.min(score, ceiling);
423
+ }
424
+ function getTermCoverage(queryTerms, docTerms) {
425
+ if (queryTerms.size === 0)
426
+ return 0;
427
+ let matched = 0;
428
+ for (const term of queryTerms) {
429
+ if (docTerms.has(term))
430
+ matched++;
431
+ }
432
+ return matched / queryTerms.size;
433
+ }
434
+ function getMatchedSymbols(symbols, queryTerms) {
435
+ if (queryTerms.size === 0)
436
+ return [];
437
+ return symbols.filter((symbol) => splitCamelCase(symbol).some((term) => queryTerms.has(term)));
438
+ }
439
+ function computeKeywordScore(query, queryTerms, doc, matchedSymbols, preparedTerms, weights) {
440
+ if (queryTerms.size === 0)
441
+ return 0;
442
+ const docText = `${doc.path} ${doc.header} ${doc.symbols.join(" ")} ${doc.content}`;
443
+ const docTerms = preparedTerms ?? new Set(searchTerms(docText));
444
+ const queryLower = query.trim().toLowerCase();
445
+ const phraseBoost = queryLower.length > 0 && docText.toLowerCase().includes(queryLower) ? 0.15 : 0;
446
+ const symbolTerms = new Set(splitCamelCase(matchedSymbols.join(" ")));
447
+ const coverage = (terms) => {
448
+ let total = 0, matched = 0;
449
+ for (const term of queryTerms) {
450
+ const weight = weights?.get(term) ?? 1;
451
+ total += weight;
452
+ if (terms.has(term))
453
+ matched += weight;
454
+ }
455
+ return total ? matched / total : 0;
456
+ };
457
+ const termCoverage = coverage(docTerms);
458
+ const symbolCoverage = coverage(symbolTerms);
459
+ return clamp01(termCoverage * 0.65 + symbolCoverage * 0.2 + phraseBoost);
460
+ }
461
+ function computeCombinedScore(semanticScore, keywordScore, options) {
462
+ const semanticComponent = Math.max(semanticScore, 0);
463
+ const totalWeight = options.semanticWeight + options.keywordWeight;
464
+ if (totalWeight <= 0)
465
+ return semanticComponent;
466
+ const blended = (options.semanticWeight * semanticComponent + options.keywordWeight * keywordScore) / totalWeight;
467
+ // Trust dampener: when one component is essentially zero, the combined score is misleading.
468
+ // A pure-keyword 94 should not look like a high-confidence hybrid match. Cap at 0.7 when
469
+ // semantic ≈ 0, and at 0.7 when keyword ≈ 0 (allows lexical hits to surface without claiming
470
+ // high hybrid confidence). 0.7 is the "medium" threshold in the qualityLabel UI.
471
+ const SEMANTIC_FLOOR = 0.05;
472
+ const KEYWORD_FLOOR = 0.05;
473
+ const SINGLE_COMPONENT_CAP = 0.7;
474
+ if (semanticComponent < SEMANTIC_FLOOR || keywordScore < KEYWORD_FLOOR) {
475
+ return clamp01(Math.min(blended, SINGLE_COMPONENT_CAP));
476
+ }
477
+ return clamp01(blended);
478
+ }
479
+ const FILE_SEARCH_SOURCE_TYPE = "file_search_cache";
480
+ async function loadCache(rootDir) {
481
+ const { getDB, bufferToVec } = await import("./db.js");
482
+ const db = await getDB(rootDir);
483
+ if (!hasCompatibleEmbeddingModel(db))
484
+ return {};
485
+ const rows = db.getEmbeddingsByType(FILE_SEARCH_SOURCE_TYPE);
486
+ const cache = {};
487
+ for (const r of rows) {
488
+ cache[r.sourceId] = { hash: r.contentHash, vector: bufferToVec(r.vector) };
489
+ }
490
+ return cache;
491
+ }
492
+ async function saveCache(rootDir, cache, dirtyKeys) {
493
+ const { getDB } = await import("./db.js");
494
+ const db = await getDB(rootDir);
495
+ const keys = dirtyKeys ?? new Set(Object.keys(cache));
496
+ const entries = [];
497
+ for (const key of keys) {
498
+ const entry = cache[key];
499
+ if (entry)
500
+ entries.push({ sourceId: key, vector: entry.vector, hash: entry.hash });
501
+ }
502
+ db.upsertEmbeddingsBatch(FILE_SEARCH_SOURCE_TYPE, entries);
503
+ }
504
+ export async function deleteFileSearchEmbedding(rootDir, path) {
505
+ const { getDB } = await import("./db.js");
506
+ const db = await getDB(rootDir);
507
+ db.deleteEmbeddingsByPrefix(FILE_SEARCH_SOURCE_TYPE, path);
508
+ }
509
+ export async function ensureMcpDataDir(rootDir) {
510
+ await mkdir(join(rootDir, CACHE_DIR), { recursive: true });
511
+ }
512
+ function formatLineRange(line, endLine) {
513
+ if (endLine && endLine > line)
514
+ return `L${line}-L${endLine}`;
515
+ return `L${line}`;
516
+ }
517
+ function getMatchedSymbolEntries(symbols, queryTerms) {
518
+ if (queryTerms.size === 0)
519
+ return [];
520
+ return symbols.filter((symbol) => splitCamelCase(symbol.name).some((term) => queryTerms.has(term)));
521
+ }
522
+ // Query vectors are shared only inside one retrieval request, never across projects or turns.
523
+ const queryEmbeddings = new AsyncLocalStorage();
524
+ const queryDeadline = new AsyncLocalStorage();
525
+ export function withQueryEmbeddingCache(fn) {
526
+ const configured = Number(process.env.CONTEXTPRO_QUERY_EMBED_TIMEOUT_MS ?? 1500);
527
+ const timeout = Number.isFinite(configured) ? Math.max(100, Math.min(10000, Math.trunc(configured))) : 1500;
528
+ return queryEmbeddings.getStore() ? fn() : queryDeadline.run({ timeoutMs: timeout }, () => queryEmbeddings.run(new Map(), fn));
529
+ }
530
+ export function fetchQueryEmbedding(query) {
531
+ const cache = queryEmbeddings.getStore();
532
+ let pending = cache?.get(query);
533
+ if (!pending) {
534
+ pending = withQueryEmbeddingCache(() => fetchEmbedding(query).then(([vector]) => vector ?? []));
535
+ cache?.set(query, pending);
536
+ }
537
+ return pending;
538
+ }
539
+ /** Hard constraints use the same public 0..100 scores returned to the caller. */
540
+ export function matchesSearchConstraints(result, options) {
541
+ const percent = (value) => value === undefined ? 0 : value > 1 ? value : value * 100;
542
+ return (!options.requireSemanticMatch || result.semanticScore > 0)
543
+ && (!options.requireKeywordMatch || result.keywordScore > 0)
544
+ && result.semanticScore >= percent(options.minSemanticScore)
545
+ && result.keywordScore >= percent(options.minKeywordScore)
546
+ && result.score >= percent(options.minCombinedScore);
547
+ }
548
+ export class SearchIndex {
549
+ documents = [];
550
+ vectors = [];
551
+ lexicalDocuments = [];
552
+ inverseFrequency = new Map();
553
+ async index(docs, rootDir, skipEmbeddings = false, cacheOnly = false) {
554
+ this.documents = docs;
555
+ this.lexicalDocuments = docs.map(doc => new Set(searchTerms(`${doc.path} ${doc.header} ${doc.symbols.join(" ")} ${doc.content}`)));
556
+ const frequencies = new Map();
557
+ for (const terms of this.lexicalDocuments)
558
+ for (const term of terms)
559
+ frequencies.set(term, (frequencies.get(term) ?? 0) + 1);
560
+ this.inverseFrequency = new Map([...frequencies].map(([term, count]) => [term, Math.log(1 + (docs.length + 1) / (count + 1))]));
561
+ if (skipEmbeddings) {
562
+ this.vectors = new Array(docs.length);
563
+ return;
564
+ }
565
+ if (!cacheOnly) {
566
+ const { getDB } = await import("./db.js");
567
+ (await getDB(rootDir)).recordEmbeddingProvider(getEmbedProvider(), getEmbedModel());
568
+ }
569
+ const cache = await loadCache(rootDir);
570
+ const uncached = [];
571
+ const dirtyKeys = new Set();
572
+ this.vectors = new Array(docs.length);
573
+ for (let i = 0; i < docs.length; i++) {
574
+ const doc = docs[i];
575
+ const rawText = `${doc.header} ${doc.symbols.join(" ")} ${doc.content}`;
576
+ const hash = hashContent(rawText);
577
+ if (cache[doc.path]?.hash === hash) {
578
+ this.vectors[i] = cache[doc.path].vector;
579
+ }
580
+ else {
581
+ uncached.push({ idx: i, text: rawText, hash });
582
+ }
583
+ }
584
+ if (!cacheOnly && uncached.length > 0) {
585
+ const batchSize = getEmbeddingBatchSize();
586
+ const batches = [];
587
+ for (let b = 0; b < uncached.length; b += batchSize) {
588
+ batches.push(uncached.slice(b, b + batchSize));
589
+ }
590
+ const pLimit = (await import("p-limit")).default;
591
+ const limit = pLimit(4);
592
+ await Promise.all(batches.map((batch) => limit(async () => {
593
+ try {
594
+ const embeddings = await fetchEmbedding(batch.map((u) => u.text));
595
+ for (let j = 0; j < batch.length; j++) {
596
+ this.vectors[batch[j].idx] = embeddings[j];
597
+ const key = docs[batch[j].idx].path;
598
+ cache[key] = { hash: batch[j].hash, vector: embeddings[j] };
599
+ dirtyKeys.add(key);
600
+ }
601
+ }
602
+ catch (error) {
603
+ if (!isContextLengthError(error)) {
604
+ const message = error instanceof Error ? error.message : String(error);
605
+ process.stderr.write(`[contextpro/embeddings] degraded: batch failed (${message}); falling back to keyword-only for ${batch.length} files\n`);
606
+ return;
607
+ }
608
+ for (const item of batch) {
609
+ try {
610
+ const [vector] = await fetchEmbedding(item.text);
611
+ this.vectors[item.idx] = vector;
612
+ const key = docs[item.idx].path;
613
+ cache[key] = { hash: item.hash, vector };
614
+ dirtyKeys.add(key);
615
+ }
616
+ catch (innerError) {
617
+ const message = innerError instanceof Error ? innerError.message : String(innerError);
618
+ process.stderr.write(`[contextpro/embeddings] degraded: single-item embed failed for ${docs[item.idx].path} (${message})\n`);
619
+ delete cache[docs[item.idx].path];
620
+ }
621
+ }
622
+ }
623
+ })));
624
+ await saveCache(rootDir, cache, dirtyKeys);
625
+ }
626
+ }
627
+ async search(query, optionsOrTopK) {
628
+ const options = resolveSearchOptions(optionsOrTopK);
629
+ let queryVec;
630
+ try {
631
+ queryVec = options.semanticWeight === 0 || !this.vectors.some(vector => vector?.length) ? [] : await fetchQueryEmbedding(query);
632
+ }
633
+ catch (error) {
634
+ const message = error instanceof Error ? error.message : String(error);
635
+ process.stderr.write(`[contextpro/embeddings] degraded: query embed failed (${message}); using keyword-only ranking\n`);
636
+ queryVec = [];
637
+ }
638
+ const queryTerms = new Set(searchTerms(query));
639
+ const candidates = [];
640
+ for (let i = 0; i < this.vectors.length; i++) {
641
+ const doc = this.documents[i];
642
+ const semanticScore = queryVec.length > 0 && this.vectors[i] ? cosine(queryVec, this.vectors[i]) : 0;
643
+ const matchedEntries = doc.symbolEntries ? getMatchedSymbolEntries(doc.symbolEntries, queryTerms) : [];
644
+ const matchedSymbols = matchedEntries.length > 0
645
+ ? matchedEntries.map((entry) => entry.name)
646
+ : getMatchedSymbols(doc.symbols, queryTerms);
647
+ const matchedSymbolLocations = matchedEntries.map((entry) => `${entry.name}@${formatLineRange(entry.line, entry.endLine)}`);
648
+ const keywordScore = computeKeywordScore(query, queryTerms, doc, matchedSymbols, this.lexicalDocuments[i], this.inverseFrequency);
649
+ if (options.requireSemanticMatch && semanticScore <= 0)
650
+ continue;
651
+ if (options.requireKeywordMatch && keywordScore <= 0)
652
+ continue;
653
+ if (Math.max(semanticScore, 0) < options.minSemanticScore)
654
+ continue;
655
+ if (keywordScore < options.minKeywordScore)
656
+ continue;
657
+ candidates.push({ idx: i, semanticScore, keywordScore, matchedSymbols, matchedSymbolLocations });
658
+ }
659
+ // RRF fuses by rank position across the filtered candidate set, so it's computed once over
660
+ // all of them rather than per-candidate like the weighted blend.
661
+ const rrfScores = options.fusion === "rrf"
662
+ ? reciprocalRankFusionScores(candidates.map((c) => c.semanticScore), candidates.map((c) => c.keywordScore))
663
+ : null;
664
+ const semanticAvailable = candidates.some((candidate) => candidate.semanticScore >= LOW_SEMANTIC_FLOOR);
665
+ const scores = candidates
666
+ .map((candidate, i) => ({
667
+ ...candidate,
668
+ score: dampLowSemanticScore(rrfScores
669
+ ? calibrateRrfScore(rrfScores[i], candidate.semanticScore, candidate.keywordScore)
670
+ : computeCombinedScore(candidate.semanticScore, candidate.keywordScore, options), candidate.semanticScore, candidate.keywordScore, semanticAvailable),
671
+ }))
672
+ .filter((candidate) => candidate.score >= options.minCombinedScore);
673
+ return scores
674
+ .sort((a, b) => b.score - a.score || b.keywordScore - a.keywordScore || b.semanticScore - a.semanticScore)
675
+ .slice(0, options.topK)
676
+ .map(({ idx, score, semanticScore, keywordScore, matchedSymbols, matchedSymbolLocations }) => {
677
+ const doc = this.documents[idx];
678
+ return {
679
+ path: doc.path,
680
+ score: Math.round(score * 10000) / 100,
681
+ semanticScore: Math.round(Math.max(semanticScore, 0) * 1000) / 10,
682
+ keywordScore: Math.round(keywordScore * 1000) / 10,
683
+ header: doc.header,
684
+ matchedSymbols,
685
+ matchedSymbolLocations,
686
+ };
687
+ });
688
+ }
689
+ getDocumentCount() {
690
+ return this.documents.length;
691
+ }
692
+ }
693
+ //# sourceMappingURL=embeddings.js.map