@mirnoorata/codexa 0.2.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 (364) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +634 -0
  3. package/dist/artifacts.d.ts +2 -0
  4. package/dist/artifacts.js +375 -0
  5. package/dist/artifacts.js.map +1 -0
  6. package/dist/autonomy.d.ts +17 -0
  7. package/dist/autonomy.js +124 -0
  8. package/dist/autonomy.js.map +1 -0
  9. package/dist/autoverify/policy.d.ts +5 -0
  10. package/dist/autoverify/policy.js +18 -0
  11. package/dist/autoverify/policy.js.map +1 -0
  12. package/dist/autoverify.d.ts +45 -0
  13. package/dist/autoverify.js +1041 -0
  14. package/dist/autoverify.js.map +1 -0
  15. package/dist/cache-lock.d.ts +16 -0
  16. package/dist/cache-lock.js +181 -0
  17. package/dist/cache-lock.js.map +1 -0
  18. package/dist/cli/hooks.d.ts +5 -0
  19. package/dist/cli/hooks.js +264 -0
  20. package/dist/cli/hooks.js.map +1 -0
  21. package/dist/cli.d.ts +2 -0
  22. package/dist/cli.js +1034 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/codex-contract.d.ts +2 -0
  25. package/dist/codex-contract.js +78 -0
  26. package/dist/codex-contract.js.map +1 -0
  27. package/dist/command.d.ts +34 -0
  28. package/dist/command.js +162 -0
  29. package/dist/command.js.map +1 -0
  30. package/dist/doctor.d.ts +112 -0
  31. package/dist/doctor.js +518 -0
  32. package/dist/doctor.js.map +1 -0
  33. package/dist/eval/baseline.d.ts +7 -0
  34. package/dist/eval/baseline.js +146 -0
  35. package/dist/eval/baseline.js.map +1 -0
  36. package/dist/eval/historical.d.ts +4 -0
  37. package/dist/eval/historical.js +663 -0
  38. package/dist/eval/historical.js.map +1 -0
  39. package/dist/eval/render.d.ts +2 -0
  40. package/dist/eval/render.js +53 -0
  41. package/dist/eval/render.js.map +1 -0
  42. package/dist/eval/scoring.d.ts +21 -0
  43. package/dist/eval/scoring.js +618 -0
  44. package/dist/eval/scoring.js.map +1 -0
  45. package/dist/eval/synthetic.d.ts +36 -0
  46. package/dist/eval/synthetic.js +107 -0
  47. package/dist/eval/synthetic.js.map +1 -0
  48. package/dist/eval/types.d.ts +36 -0
  49. package/dist/eval/types.js +2 -0
  50. package/dist/eval/types.js.map +1 -0
  51. package/dist/eval.d.ts +140 -0
  52. package/dist/eval.js +551 -0
  53. package/dist/eval.js.map +1 -0
  54. package/dist/git.d.ts +17 -0
  55. package/dist/git.js +189 -0
  56. package/dist/git.js.map +1 -0
  57. package/dist/github-release.d.ts +47 -0
  58. package/dist/github-release.js +610 -0
  59. package/dist/github-release.js.map +1 -0
  60. package/dist/github-sync.d.ts +68 -0
  61. package/dist/github-sync.js +345 -0
  62. package/dist/github-sync.js.map +1 -0
  63. package/dist/graph.d.ts +10 -0
  64. package/dist/graph.js +665 -0
  65. package/dist/graph.js.map +1 -0
  66. package/dist/indexer/aliases.d.ts +2 -0
  67. package/dist/indexer/aliases.js +190 -0
  68. package/dist/indexer/aliases.js.map +1 -0
  69. package/dist/indexer/artifact-writing.d.ts +3 -0
  70. package/dist/indexer/artifact-writing.js +79 -0
  71. package/dist/indexer/artifact-writing.js.map +1 -0
  72. package/dist/indexer/discovery.d.ts +2 -0
  73. package/dist/indexer/discovery.js +5 -0
  74. package/dist/indexer/discovery.js.map +1 -0
  75. package/dist/indexer/external-facts.d.ts +6 -0
  76. package/dist/indexer/external-facts.js +45 -0
  77. package/dist/indexer/external-facts.js.map +1 -0
  78. package/dist/indexer/freshness.d.ts +8 -0
  79. package/dist/indexer/freshness.js +56 -0
  80. package/dist/indexer/freshness.js.map +1 -0
  81. package/dist/indexer/graph-stage.d.ts +2 -0
  82. package/dist/indexer/graph-stage.js +21 -0
  83. package/dist/indexer/graph-stage.js.map +1 -0
  84. package/dist/indexer/parsing.d.ts +30 -0
  85. package/dist/indexer/parsing.js +177 -0
  86. package/dist/indexer/parsing.js.map +1 -0
  87. package/dist/indexer/pipeline.d.ts +5 -0
  88. package/dist/indexer/pipeline.js +8 -0
  89. package/dist/indexer/pipeline.js.map +1 -0
  90. package/dist/indexer/ranking.d.ts +4 -0
  91. package/dist/indexer/ranking.js +134 -0
  92. package/dist/indexer/ranking.js.map +1 -0
  93. package/dist/indexer.d.ts +13 -0
  94. package/dist/indexer.js +395 -0
  95. package/dist/indexer.js.map +1 -0
  96. package/dist/init.d.ts +24 -0
  97. package/dist/init.js +566 -0
  98. package/dist/init.js.map +1 -0
  99. package/dist/language.d.ts +8 -0
  100. package/dist/language.js +123 -0
  101. package/dist/language.js.map +1 -0
  102. package/dist/live-index.d.ts +68 -0
  103. package/dist/live-index.js +215 -0
  104. package/dist/live-index.js.map +1 -0
  105. package/dist/lsp/assist.d.ts +44 -0
  106. package/dist/lsp/assist.js +331 -0
  107. package/dist/lsp/assist.js.map +1 -0
  108. package/dist/lsp/client.d.ts +59 -0
  109. package/dist/lsp/client.js +208 -0
  110. package/dist/lsp/client.js.map +1 -0
  111. package/dist/mcp/compaction.d.ts +15 -0
  112. package/dist/mcp/compaction.js +1249 -0
  113. package/dist/mcp/compaction.js.map +1 -0
  114. package/dist/mcp/envelope.d.ts +44 -0
  115. package/dist/mcp/envelope.js +425 -0
  116. package/dist/mcp/envelope.js.map +1 -0
  117. package/dist/mcp/prompts.d.ts +2 -0
  118. package/dist/mcp/prompts.js +109 -0
  119. package/dist/mcp/prompts.js.map +1 -0
  120. package/dist/mcp/resources.d.ts +2 -0
  121. package/dist/mcp/resources.js +132 -0
  122. package/dist/mcp/resources.js.map +1 -0
  123. package/dist/mcp/runtime.d.ts +15 -0
  124. package/dist/mcp/runtime.js +122 -0
  125. package/dist/mcp/runtime.js.map +1 -0
  126. package/dist/mcp/session-memory.d.ts +3 -0
  127. package/dist/mcp/session-memory.js +61 -0
  128. package/dist/mcp/session-memory.js.map +1 -0
  129. package/dist/mcp/tool-registry.d.ts +269 -0
  130. package/dist/mcp/tool-registry.js +284 -0
  131. package/dist/mcp/tool-registry.js.map +1 -0
  132. package/dist/mcp/tools.d.ts +53 -0
  133. package/dist/mcp/tools.js +372 -0
  134. package/dist/mcp/tools.js.map +1 -0
  135. package/dist/mcp-repo-root.d.ts +16 -0
  136. package/dist/mcp-repo-root.js +322 -0
  137. package/dist/mcp-repo-root.js.map +1 -0
  138. package/dist/mcp-tool-catalog.d.ts +2 -0
  139. package/dist/mcp-tool-catalog.js +2 -0
  140. package/dist/mcp-tool-catalog.js.map +1 -0
  141. package/dist/mcp.d.ts +11 -0
  142. package/dist/mcp.js +332 -0
  143. package/dist/mcp.js.map +1 -0
  144. package/dist/outcome-ranking.d.ts +5 -0
  145. package/dist/outcome-ranking.js +115 -0
  146. package/dist/outcome-ranking.js.map +1 -0
  147. package/dist/parser/context.d.ts +28 -0
  148. package/dist/parser/context.js +2 -0
  149. package/dist/parser/context.js.map +1 -0
  150. package/dist/parser/ecma.d.ts +5 -0
  151. package/dist/parser/ecma.js +388 -0
  152. package/dist/parser/ecma.js.map +1 -0
  153. package/dist/parser/facts.d.ts +12 -0
  154. package/dist/parser/facts.js +137 -0
  155. package/dist/parser/facts.js.map +1 -0
  156. package/dist/parser/json.d.ts +3 -0
  157. package/dist/parser/json.js +318 -0
  158. package/dist/parser/json.js.map +1 -0
  159. package/dist/parser/markdown.d.ts +3 -0
  160. package/dist/parser/markdown.js +180 -0
  161. package/dist/parser/markdown.js.map +1 -0
  162. package/dist/parser/nodes.d.ts +5 -0
  163. package/dist/parser/nodes.js +75 -0
  164. package/dist/parser/nodes.js.map +1 -0
  165. package/dist/parser/python.d.ts +2 -0
  166. package/dist/parser/python.js +307 -0
  167. package/dist/parser/python.js.map +1 -0
  168. package/dist/parser/references.d.ts +3 -0
  169. package/dist/parser/references.js +204 -0
  170. package/dist/parser/references.js.map +1 -0
  171. package/dist/parser/risks.d.ts +4 -0
  172. package/dist/parser/risks.js +62 -0
  173. package/dist/parser/risks.js.map +1 -0
  174. package/dist/parser/routes.d.ts +5 -0
  175. package/dist/parser/routes.js +97 -0
  176. package/dist/parser/routes.js.map +1 -0
  177. package/dist/parser/shallow.d.ts +3 -0
  178. package/dist/parser/shallow.js +545 -0
  179. package/dist/parser/shallow.js.map +1 -0
  180. package/dist/parser/source.d.ts +4 -0
  181. package/dist/parser/source.js +127 -0
  182. package/dist/parser/source.js.map +1 -0
  183. package/dist/parser.d.ts +2 -0
  184. package/dist/parser.js +2 -0
  185. package/dist/parser.js.map +1 -0
  186. package/dist/placeholder-signals.d.ts +15 -0
  187. package/dist/placeholder-signals.js +511 -0
  188. package/dist/placeholder-signals.js.map +1 -0
  189. package/dist/post-edit-outcomes.d.ts +167 -0
  190. package/dist/post-edit-outcomes.js +484 -0
  191. package/dist/post-edit-outcomes.js.map +1 -0
  192. package/dist/queries.d.ts +12 -0
  193. package/dist/queries.js +13 -0
  194. package/dist/queries.js.map +1 -0
  195. package/dist/query/change-plan.d.ts +48 -0
  196. package/dist/query/change-plan.js +858 -0
  197. package/dist/query/change-plan.js.map +1 -0
  198. package/dist/query/compact-data.d.ts +25 -0
  199. package/dist/query/compact-data.js +74 -0
  200. package/dist/query/compact-data.js.map +1 -0
  201. package/dist/query/context.d.ts +5 -0
  202. package/dist/query/context.js +1162 -0
  203. package/dist/query/context.js.map +1 -0
  204. package/dist/query/diff.d.ts +5 -0
  205. package/dist/query/diff.js +111 -0
  206. package/dist/query/diff.js.map +1 -0
  207. package/dist/query/edge-evidence.d.ts +3 -0
  208. package/dist/query/edge-evidence.js +36 -0
  209. package/dist/query/edge-evidence.js.map +1 -0
  210. package/dist/query/formatting.d.ts +14 -0
  211. package/dist/query/formatting.js +67 -0
  212. package/dist/query/formatting.js.map +1 -0
  213. package/dist/query/graph-traversal.d.ts +22 -0
  214. package/dist/query/graph-traversal.js +218 -0
  215. package/dist/query/graph-traversal.js.map +1 -0
  216. package/dist/query/graph.d.ts +14 -0
  217. package/dist/query/graph.js +102 -0
  218. package/dist/query/graph.js.map +1 -0
  219. package/dist/query/impact.d.ts +28 -0
  220. package/dist/query/impact.js +568 -0
  221. package/dist/query/impact.js.map +1 -0
  222. package/dist/query/inspection.d.ts +9 -0
  223. package/dist/query/inspection.js +290 -0
  224. package/dist/query/inspection.js.map +1 -0
  225. package/dist/query/next-tools.d.ts +3 -0
  226. package/dist/query/next-tools.js +25 -0
  227. package/dist/query/next-tools.js.map +1 -0
  228. package/dist/query/placeholders.d.ts +24 -0
  229. package/dist/query/placeholders.js +121 -0
  230. package/dist/query/placeholders.js.map +1 -0
  231. package/dist/query/post-edit/decision.d.ts +49 -0
  232. package/dist/query/post-edit/decision.js +130 -0
  233. package/dist/query/post-edit/decision.js.map +1 -0
  234. package/dist/query/post-edit/dirty-scope.d.ts +16 -0
  235. package/dist/query/post-edit/dirty-scope.js +21 -0
  236. package/dist/query/post-edit/dirty-scope.js.map +1 -0
  237. package/dist/query/post-edit/next-actions.d.ts +22 -0
  238. package/dist/query/post-edit/next-actions.js +44 -0
  239. package/dist/query/post-edit/next-actions.js.map +1 -0
  240. package/dist/query/post-edit/snapshot-contract.d.ts +8 -0
  241. package/dist/query/post-edit/snapshot-contract.js +111 -0
  242. package/dist/query/post-edit/snapshot-contract.js.map +1 -0
  243. package/dist/query/post-edit.d.ts +5 -0
  244. package/dist/query/post-edit.js +1108 -0
  245. package/dist/query/post-edit.js.map +1 -0
  246. package/dist/query/quality.d.ts +43 -0
  247. package/dist/query/quality.js +134 -0
  248. package/dist/query/quality.js.map +1 -0
  249. package/dist/query/raw-search.d.ts +23 -0
  250. package/dist/query/raw-search.js +147 -0
  251. package/dist/query/raw-search.js.map +1 -0
  252. package/dist/query/runtime.d.ts +11 -0
  253. package/dist/query/runtime.js +79 -0
  254. package/dist/query/runtime.js.map +1 -0
  255. package/dist/query/search.d.ts +25 -0
  256. package/dist/query/search.js +429 -0
  257. package/dist/query/search.js.map +1 -0
  258. package/dist/query/session-memory.d.ts +3 -0
  259. package/dist/query/session-memory.js +108 -0
  260. package/dist/query/session-memory.js.map +1 -0
  261. package/dist/query/session.d.ts +41 -0
  262. package/dist/query/session.js +90 -0
  263. package/dist/query/session.js.map +1 -0
  264. package/dist/query/targets.d.ts +25 -0
  265. package/dist/query/targets.js +97 -0
  266. package/dist/query/targets.js.map +1 -0
  267. package/dist/query/test-commands.d.ts +10 -0
  268. package/dist/query/test-commands.js +110 -0
  269. package/dist/query/test-commands.js.map +1 -0
  270. package/dist/query/test-plan.d.ts +6 -0
  271. package/dist/query/test-plan.js +104 -0
  272. package/dist/query/test-plan.js.map +1 -0
  273. package/dist/query/tests.d.ts +48 -0
  274. package/dist/query/tests.js +444 -0
  275. package/dist/query/tests.js.map +1 -0
  276. package/dist/query/verification/shell.d.ts +20 -0
  277. package/dist/query/verification/shell.js +164 -0
  278. package/dist/query/verification/shell.js.map +1 -0
  279. package/dist/query/verification.d.ts +47 -0
  280. package/dist/query/verification.js +1123 -0
  281. package/dist/query/verification.js.map +1 -0
  282. package/dist/query/workflow.d.ts +17 -0
  283. package/dist/query/workflow.js +252 -0
  284. package/dist/query/workflow.js.map +1 -0
  285. package/dist/query/workspace-guidance.d.ts +26 -0
  286. package/dist/query/workspace-guidance.js +214 -0
  287. package/dist/query/workspace-guidance.js.map +1 -0
  288. package/dist/query/worktree-state.d.ts +22 -0
  289. package/dist/query/worktree-state.js +32 -0
  290. package/dist/query/worktree-state.js.map +1 -0
  291. package/dist/query/worktree.d.ts +16 -0
  292. package/dist/query/worktree.js +194 -0
  293. package/dist/query/worktree.js.map +1 -0
  294. package/dist/query-data.d.ts +4 -0
  295. package/dist/query-data.js +112 -0
  296. package/dist/query-data.js.map +1 -0
  297. package/dist/repo-files.d.ts +24 -0
  298. package/dist/repo-files.js +105 -0
  299. package/dist/repo-files.js.map +1 -0
  300. package/dist/resolver.d.ts +9 -0
  301. package/dist/resolver.js +555 -0
  302. package/dist/resolver.js.map +1 -0
  303. package/dist/retrieval.d.ts +46 -0
  304. package/dist/retrieval.js +783 -0
  305. package/dist/retrieval.js.map +1 -0
  306. package/dist/risk-ingest.d.ts +16 -0
  307. package/dist/risk-ingest.js +458 -0
  308. package/dist/risk-ingest.js.map +1 -0
  309. package/dist/rules.d.ts +10 -0
  310. package/dist/rules.js +107 -0
  311. package/dist/rules.js.map +1 -0
  312. package/dist/semantic/python.d.ts +9 -0
  313. package/dist/semantic/python.js +817 -0
  314. package/dist/semantic/python.js.map +1 -0
  315. package/dist/semantic/typescript.d.ts +10 -0
  316. package/dist/semantic/typescript.js +714 -0
  317. package/dist/semantic/typescript.js.map +1 -0
  318. package/dist/semantic-retrieval.d.ts +53 -0
  319. package/dist/semantic-retrieval.js +673 -0
  320. package/dist/semantic-retrieval.js.map +1 -0
  321. package/dist/session-memory/derivation.d.ts +6 -0
  322. package/dist/session-memory/derivation.js +400 -0
  323. package/dist/session-memory/derivation.js.map +1 -0
  324. package/dist/session-memory/event-log.d.ts +23 -0
  325. package/dist/session-memory/event-log.js +126 -0
  326. package/dist/session-memory/event-log.js.map +1 -0
  327. package/dist/session-memory/formatting.d.ts +7 -0
  328. package/dist/session-memory/formatting.js +86 -0
  329. package/dist/session-memory/formatting.js.map +1 -0
  330. package/dist/session-memory/model.d.ts +94 -0
  331. package/dist/session-memory/model.js +17 -0
  332. package/dist/session-memory/model.js.map +1 -0
  333. package/dist/session-memory/runtime.d.ts +24 -0
  334. package/dist/session-memory/runtime.js +289 -0
  335. package/dist/session-memory/runtime.js.map +1 -0
  336. package/dist/session-memory/store.d.ts +27 -0
  337. package/dist/session-memory/store.js +447 -0
  338. package/dist/session-memory/store.js.map +1 -0
  339. package/dist/session-memory.d.ts +1 -0
  340. package/dist/session-memory.js +2 -0
  341. package/dist/session-memory.js.map +1 -0
  342. package/dist/static-analysis.d.ts +36 -0
  343. package/dist/static-analysis.js +505 -0
  344. package/dist/static-analysis.js.map +1 -0
  345. package/dist/symbol-report-ingest.d.ts +8 -0
  346. package/dist/symbol-report-ingest.js +504 -0
  347. package/dist/symbol-report-ingest.js.map +1 -0
  348. package/dist/task-snapshots.d.ts +41 -0
  349. package/dist/task-snapshots.js +430 -0
  350. package/dist/task-snapshots.js.map +1 -0
  351. package/dist/types.d.ts +848 -0
  352. package/dist/types.js +12 -0
  353. package/dist/types.js.map +1 -0
  354. package/dist/util.d.ts +11 -0
  355. package/dist/util.js +63 -0
  356. package/dist/util.js.map +1 -0
  357. package/dist/version.d.ts +1 -0
  358. package/dist/version.js +5 -0
  359. package/dist/version.js.map +1 -0
  360. package/package.json +81 -0
  361. package/plugins/codexa/.codex-plugin/plugin.json +38 -0
  362. package/plugins/codexa/.mcp.json +20 -0
  363. package/plugins/codexa/scripts/codexa-mcp.js +100 -0
  364. package/plugins/codexa/skills/codexa/SKILL.md +48 -0
@@ -0,0 +1,783 @@
1
+ import path from "node:path";
2
+ import { isTestPath, moduleNameForPath } from "./language.js";
3
+ import { semanticLaneEntriesForQuery } from "./semantic-retrieval.js";
4
+ import { uniqueSorted } from "./util.js";
5
+ const STOP_WORDS = new Set([
6
+ "a",
7
+ "about",
8
+ "all",
9
+ "an",
10
+ "and",
11
+ "any",
12
+ "are",
13
+ "as",
14
+ "at",
15
+ "be",
16
+ "by",
17
+ "can",
18
+ "code",
19
+ "codex",
20
+ "codexa",
21
+ "does",
22
+ "for",
23
+ "from",
24
+ "get",
25
+ "how",
26
+ "i",
27
+ "in",
28
+ "into",
29
+ "is",
30
+ "it",
31
+ "make",
32
+ "of",
33
+ "on",
34
+ "or",
35
+ "project",
36
+ "safe",
37
+ "safely",
38
+ "should",
39
+ "system",
40
+ "that",
41
+ "the",
42
+ "this",
43
+ "to",
44
+ "update",
45
+ "use",
46
+ "what",
47
+ "when",
48
+ "where",
49
+ "why",
50
+ "with",
51
+ "work",
52
+ "works"
53
+ ]);
54
+ const SYNONYMS = {
55
+ api: ["route", "router", "endpoint", "handler", "server", "app"],
56
+ architecture: ["module", "indexer", "parser", "resolver", "query", "mcp", "graph", "workflow", "artifact"],
57
+ project: ["adapter", "package", "manifest", "node", "workflow", "queue", "run"],
58
+ automatic: ["init", "session", "hook", "config", "mcp"],
59
+ backend: ["api", "route", "server", "python", "adapter", "store"],
60
+ brief: ["task", "context", "pack", "query"],
61
+ caller: ["call", "usage", "import", "reference"],
62
+ change: ["impact", "diff", "risk"],
63
+ dependency: ["import", "edge", "graph", "resolver"],
64
+ frontend: ["tsx", "react", "component", "hook", "web"],
65
+ graph: ["edge", "dependency", "call", "import", "reference", "workflow"],
66
+ mcp: ["server", "tool", "resource", "prompt", "stdio"],
67
+ parser: ["tree", "sitter", "symbol", "import", "usage"],
68
+ polling: ["poll", "run", "queue", "status", "hook"],
69
+ queue: ["polling", "run", "status", "dashboard"],
70
+ route: ["api", "handler", "router", "endpoint"],
71
+ session: ["init", "hook", "focus", "config"],
72
+ test: ["pytest", "vitest", "spec", "fixture", "coverage"],
73
+ workflow: ["route", "job", "flow", "execution", "process", "adapter", "store"]
74
+ };
75
+ const BROAD_WORKFLOW_TERMS = new Set(["api", "app", "backend", "endpoint", "execution", "flow", "frontend", "handler", "path", "process", "route", "server", "workflow"]);
76
+ const SUPPORT_WORKFLOW_TERMS = new Set(["store", "stores", "test", "tests", "pytest", "vitest", "spec", "specs", "verification"]);
77
+ const LANE_WEIGHTS = {
78
+ exact: 4,
79
+ symbol: 3.2,
80
+ bm25: 2,
81
+ semantic: 2.6,
82
+ graph: 0.65,
83
+ workflow: 2.8,
84
+ test: 2.5,
85
+ dirty: 1.7
86
+ };
87
+ const SEMANTIC_ANCHOR_MIN_SCORE = 9;
88
+ const RETRIEVAL_RUNTIME_CACHE_LIMIT = 4;
89
+ const retrievalRuntimeCache = new Map();
90
+ export async function retrieveForTask(index, query, limit = 12, semanticOptions) {
91
+ const rawTerms = tokenize(query);
92
+ const terms = expandedQueryTerms(query);
93
+ const intents = classifyTaskIntent(query, terms);
94
+ const allowDecoys = queryAllowsDecoy(query);
95
+ const runtime = retrievalRuntimeForIndex(index);
96
+ const bm25Entries = runtime.docs
97
+ .map((doc) => scoreDocument(doc, terms, intents, runtime.docFreq, runtime.docs.length, runtime.avgLength))
98
+ .filter((entry) => entry !== null && entry.score > 0);
99
+ const exactEntries = exactLaneEntries(index, query);
100
+ const symbolEntries = symbolLaneEntries(index, query, runtime.fileByPath);
101
+ const semanticResult = semanticOptions
102
+ ? await semanticLaneEntriesForQuery(index, query, runtime.fileByPath, semanticOptions)
103
+ : {
104
+ entries: [],
105
+ summary: { enabled: false, status: "disabled", diagnostics: [] }
106
+ };
107
+ const preliminaryMatches = fuseLaneRankings(index, [
108
+ ["exact", exactEntries],
109
+ ["symbol", symbolEntries],
110
+ ["bm25", bm25Entries],
111
+ ["semantic", semanticResult.entries]
112
+ ])
113
+ .filter((match) => allowDecoys || !isDecoyLikePath(match.file.path))
114
+ .slice(0, Math.max(limit * 2, 20));
115
+ const workflows = rankWorkflows(index, terms, preliminaryMatches).slice(0, Math.max(3, Math.min(10, limit)));
116
+ const workflowEntries = workflowLaneEntries(index, workflows, query, runtime.fileByPath);
117
+ const testEntries = testLaneEntries(index, [...preliminaryMatches.map((match) => match.file.path), ...workflowEntries.map((entry) => entry.file.path)], workflows, query, runtime.fileByPath);
118
+ const dirtyEntries = dirtyLaneEntries(index, intents);
119
+ const graphEntries = graphLaneEntries(index, intents, [
120
+ ...bm25Entries,
121
+ ...exactEntries,
122
+ ...symbolEntries,
123
+ ...workflowEntries,
124
+ ...testEntries,
125
+ ...dirtyEntries
126
+ ], runtime.fileByPath);
127
+ const matches = fuseLaneRankings(index, [
128
+ ["exact", exactEntries],
129
+ ["symbol", symbolEntries],
130
+ ["bm25", bm25Entries],
131
+ ["semantic", semanticResult.entries],
132
+ ["workflow", workflowEntries],
133
+ ["test", testEntries],
134
+ ["dirty", dirtyEntries],
135
+ ["graph", graphEntries]
136
+ ])
137
+ .filter((match) => allowDecoys || !isDecoyLikePath(match.file.path))
138
+ .slice(0, limit);
139
+ const modules = rankModules(index, matches, terms).slice(0, Math.max(3, Math.min(8, limit)));
140
+ const broad = rawTerms.length <= 2 || intents.includes("architecture") || intents.includes("workflow");
141
+ const intentConfidence = analyzeIntentConfidence(query, intents, terms, matches, workflows, broad);
142
+ const diagnostics = uniqueSorted([...retrievalDiagnostics(index, matches, workflows, broad, intentConfidence), ...semanticResult.summary.diagnostics.map((diagnostic) => `semantic: ${diagnostic}`)]);
143
+ return { query, intents, terms, matches, workflows, modules, broad, intentConfidence, diagnostics, semantic: semanticResult.summary };
144
+ }
145
+ function retrievalRuntimeForIndex(index) {
146
+ const key = [
147
+ index.snapshot.snapshotId,
148
+ index.freshness.indexedAt,
149
+ index.files.length,
150
+ index.symbols.length,
151
+ index.usageSites.length,
152
+ index.imports.length,
153
+ index.risks.length,
154
+ index.workflows.length
155
+ ].join(":");
156
+ const cached = retrievalRuntimeCache.get(key);
157
+ if (cached) {
158
+ retrievalRuntimeCache.delete(key);
159
+ retrievalRuntimeCache.set(key, cached);
160
+ return cached;
161
+ }
162
+ const docs = buildDocuments(index);
163
+ const runtime = {
164
+ docs,
165
+ docFreq: documentFrequency(docs),
166
+ avgLength: docs.length > 0 ? docs.reduce((sum, doc) => sum + doc.length, 0) / docs.length : 1,
167
+ fileByPath: new Map(index.files.map((file) => [file.path, file]))
168
+ };
169
+ retrievalRuntimeCache.set(key, runtime);
170
+ while (retrievalRuntimeCache.size > RETRIEVAL_RUNTIME_CACHE_LIMIT) {
171
+ const oldestKey = retrievalRuntimeCache.keys().next().value;
172
+ if (!oldestKey) {
173
+ break;
174
+ }
175
+ retrievalRuntimeCache.delete(oldestKey);
176
+ }
177
+ return runtime;
178
+ }
179
+ export function classifyTaskIntent(query, terms = expandedQueryTerms(query)) {
180
+ const joined = `${query.toLowerCase()} ${terms.join(" ")}`;
181
+ const intents = [];
182
+ const add = (intent, pattern) => {
183
+ if (pattern.test(joined)) {
184
+ intents.push(intent);
185
+ }
186
+ };
187
+ add("architecture", /\b(architecture|understand|overview|map|module|subsystem|competitor|sourcegraph|aider|deepwiki)\b/);
188
+ add("workflow", /\b(workflow|flow|execution|route|endpoint|job|process|queue|polling)\b|\b(?:workflow|dependency|call|execution)\s+path\b/);
189
+ add("debugging", /\b(debug|bug|fix|error|failure|broken|trace|root cause)\b/);
190
+ add("testing", /\b(test|verify|validation|pytest|vitest|coverage|regression)\b/);
191
+ add("frontend", /\b(frontend|react|tsx|component|hook|ui|canvas|web)\b/);
192
+ add("backend", /\b(backend|api|python|server|adapter|store|database|route)\b/);
193
+ add("configuration", /\b(config|env|service|script|deploy|package|manifest|init|hook|session)\b/);
194
+ add("risk", /\b(risk|security|shell|filesystem|sql|danger|unsafe|blast)\b/);
195
+ add("implementation", /\b(add|implement|change|update|refactor|rename|delete|modify)\b/);
196
+ return intents.length > 0 ? uniqueInOrder(intents) : ["unknown"];
197
+ }
198
+ export function expandedQueryTerms(query) {
199
+ const raw = tokenize(query);
200
+ const expanded = [];
201
+ for (const term of raw) {
202
+ expanded.push(term);
203
+ for (const synonym of SYNONYMS[term] ?? []) {
204
+ expanded.push(synonym);
205
+ }
206
+ }
207
+ return uniqueSorted(expanded).slice(0, 40);
208
+ }
209
+ function buildDocuments(index) {
210
+ const symbolsByPath = groupByPath(index.symbols);
211
+ const usagesByPath = groupByPath(index.usageSites);
212
+ const importsByPath = groupByPath(index.imports);
213
+ const risksByPath = groupByPath(index.risks);
214
+ return index.files.map((file) => {
215
+ const reasonsByTerm = new Map();
216
+ const terms = [];
217
+ const add = (value, reason, weight = 1) => {
218
+ for (const term of tokenize(value)) {
219
+ for (let i = 0; i < weight; i += 1) {
220
+ terms.push(term);
221
+ }
222
+ const reasons = reasonsByTerm.get(term) ?? new Set();
223
+ reasons.add(reason);
224
+ reasonsByTerm.set(term, reasons);
225
+ }
226
+ };
227
+ add(file.path, "path", 3);
228
+ add(moduleNameForPath(file.path), "module", 2);
229
+ add(path.posix.basename(file.path).replace(/\.[^.]+$/, ""), "file stem", 3);
230
+ add(file.language, "language");
231
+ if (file.test)
232
+ add("test spec verification", "test file", 2);
233
+ if (file.dirty)
234
+ add("dirty changed worktree", "dirty file", 2);
235
+ for (const symbol of symbolsByPath.get(file.path) ?? []) {
236
+ add(symbol.name, `symbol ${symbol.name}`, symbol.exported ? 3 : 2);
237
+ add(symbol.qualifiedName, `symbol ${symbol.qualifiedName}`, symbol.exported ? 3 : 2);
238
+ add(symbol.kind, `symbol kind ${symbol.kind}`, 2);
239
+ if (symbol.decorators.length > 0)
240
+ add(symbol.decorators.join(" "), `decorator ${symbol.name}`, 2);
241
+ }
242
+ for (const usage of (usagesByPath.get(file.path) ?? []).slice(0, 80)) {
243
+ add(usage.name, `usage ${usage.name}`, usage.confidence === "authoritative" ? 2 : 1);
244
+ add(usage.text, `usage text ${usage.name}`);
245
+ add(usage.kind, `usage kind ${usage.kind}`);
246
+ }
247
+ for (const imp of importsByPath.get(file.path) ?? []) {
248
+ add(imp.specifier, `import ${imp.specifier}`, 2);
249
+ if (imp.importedName)
250
+ add(imp.importedName, `imported ${imp.importedName}`);
251
+ if (imp.localName)
252
+ add(imp.localName, `local import ${imp.localName}`);
253
+ if (imp.resolvedPath)
254
+ add(imp.resolvedPath, `imports ${imp.resolvedPath}`);
255
+ }
256
+ for (const risk of risksByPath.get(file.path) ?? []) {
257
+ add(risk.signal, `risk ${risk.signal}`, 2);
258
+ add(risk.reason, `risk ${risk.signal}`);
259
+ }
260
+ const termCounts = new Map();
261
+ for (const term of terms) {
262
+ termCounts.set(term, (termCounts.get(term) ?? 0) + 1);
263
+ }
264
+ return { file, terms, termCounts, reasonsByTerm, length: Math.max(1, terms.length) };
265
+ });
266
+ }
267
+ function groupByPath(items) {
268
+ const grouped = new Map();
269
+ for (const item of items) {
270
+ const list = grouped.get(item.path) ?? [];
271
+ list.push(item);
272
+ grouped.set(item.path, list);
273
+ }
274
+ return grouped;
275
+ }
276
+ function scoreDocument(doc, queryTerms, intents, docFreq, docCount, avgLength) {
277
+ const k1 = 1.2;
278
+ const b = 0.72;
279
+ const matchedTerms = [];
280
+ let score = 0;
281
+ const reasons = new Set();
282
+ for (const term of queryTerms) {
283
+ const tf = doc.termCounts.get(term) ?? 0;
284
+ if (tf === 0) {
285
+ continue;
286
+ }
287
+ matchedTerms.push(term);
288
+ const df = docFreq.get(term) ?? 0;
289
+ const idf = Math.log(1 + (docCount - df + 0.5) / (df + 0.5));
290
+ score += idf * ((tf * (k1 + 1)) / (tf + k1 * (1 - b + b * (doc.length / avgLength))));
291
+ for (const reason of doc.reasonsByTerm.get(term) ?? []) {
292
+ reasons.add(reason);
293
+ }
294
+ }
295
+ score += intentBoost(doc.file, intents);
296
+ if (matchedTerms.length === 0) {
297
+ return null;
298
+ }
299
+ score += Math.log2(doc.file.rank + 1) * 0.25;
300
+ return {
301
+ file: doc.file,
302
+ score,
303
+ reasons: [...reasons].sort().slice(0, 8),
304
+ matchedTerms: uniqueSorted(matchedTerms)
305
+ };
306
+ }
307
+ function exactLaneEntries(index, query) {
308
+ const entries = [];
309
+ for (const file of index.files) {
310
+ const basename = path.posix.basename(file.path);
311
+ const stem = basename.replace(/\.[^.]+$/, "");
312
+ const score = Math.max(matchScore(query, file.path), matchScore(query, basename), matchScore(query, stem));
313
+ if (score >= 6) {
314
+ entries.push({ file, score: score + 8, reasons: [`exact file/path ${stem}`], matchedTerms: matchedQueryTerms(query, `${file.path} ${basename} ${stem}`) });
315
+ }
316
+ }
317
+ return entries.sort(sortLaneEntry);
318
+ }
319
+ function symbolLaneEntries(index, query, fileByPath) {
320
+ const byPath = new Map();
321
+ const add = (filePath, score, reason, haystack) => {
322
+ const file = fileByPath.get(filePath);
323
+ if (!file || score <= 0) {
324
+ return;
325
+ }
326
+ const existing = byPath.get(file.path) ?? { file, score: 0, reasons: [], matchedTerms: [] };
327
+ existing.score += score;
328
+ existing.reasons.push(reason);
329
+ existing.matchedTerms.push(...matchedQueryTerms(query, haystack));
330
+ byPath.set(file.path, existing);
331
+ };
332
+ for (const symbol of index.symbols) {
333
+ const haystack = `${symbol.name} ${symbol.qualifiedName} ${symbol.kind} ${symbol.decorators.join(" ")}`;
334
+ const score = Math.max(matchScore(query, symbol.name), matchScore(query, symbol.qualifiedName), matchScore(query, symbol.path));
335
+ if (score > 0) {
336
+ add(symbol.path, score + (symbol.exported ? 4 : 1), `symbol ${symbol.qualifiedName}`, haystack);
337
+ }
338
+ }
339
+ for (const edge of index.imports) {
340
+ const haystack = `${edge.specifier} ${edge.importedName ?? ""} ${edge.localName ?? ""} ${edge.resolvedPath ?? ""}`;
341
+ const score = Math.max(matchScore(query, edge.specifier), matchScore(query, edge.importedName ?? ""), matchScore(query, edge.localName ?? ""), matchScore(query, edge.resolvedPath ?? ""));
342
+ if (score > 0) {
343
+ add(edge.path, score + 1, `import ${edge.specifier}`, haystack);
344
+ }
345
+ }
346
+ for (const usage of index.usageSites) {
347
+ const haystack = `${usage.name} ${usage.kind} ${usage.text}`;
348
+ const score = Math.max(matchScore(query, usage.name), matchScore(query, usage.text), matchScore(query, usage.path));
349
+ add(usage.path, Math.max(0, score - 1), `usage ${usage.name}`, haystack);
350
+ }
351
+ return [...byPath.values()]
352
+ .map((entry) => ({ ...entry, reasons: uniqueSorted(entry.reasons).slice(0, 8), matchedTerms: uniqueSorted(entry.matchedTerms) }))
353
+ .sort(sortLaneEntry);
354
+ }
355
+ function workflowLaneEntries(index, workflows, query, fileByPath) {
356
+ const byPath = new Map();
357
+ const add = (filePath, score, reason) => {
358
+ if (!filePath) {
359
+ return;
360
+ }
361
+ const file = fileByPath.get(filePath);
362
+ if (!file) {
363
+ return;
364
+ }
365
+ const existing = byPath.get(file.path) ?? { file, score: 0, reasons: [], matchedTerms: [] };
366
+ existing.score += score;
367
+ existing.reasons.push(reason);
368
+ existing.matchedTerms.push(...matchedQueryTerms(query, `${file.path} ${reason}`));
369
+ byPath.set(file.path, existing);
370
+ };
371
+ for (const workflow of workflows) {
372
+ add(workflow.entryPath, 18 + workflow.rank * 0.1, `workflow entry ${workflow.title}`);
373
+ for (const step of workflow.steps) {
374
+ const score = step.kind === "entry"
375
+ ? 18
376
+ : step.kind === "ui"
377
+ ? 18
378
+ : step.kind === "endpoint"
379
+ ? 16
380
+ : step.kind === "store" || step.kind === "adapter" || step.kind === "manifest"
381
+ ? 15
382
+ : step.kind === "test"
383
+ ? 12
384
+ : 7;
385
+ add(step.path, score, `workflow ${workflow.title}: ${step.kind}`);
386
+ add(step.targetPath, Math.max(5, score - 4), `workflow ${workflow.title}: target ${step.kind}`);
387
+ }
388
+ for (const file of workflow.relatedFiles) {
389
+ add(file, 7, `workflow related ${workflow.title}`);
390
+ }
391
+ }
392
+ return [...byPath.values()]
393
+ .map((entry) => ({ ...entry, reasons: uniqueSorted(entry.reasons).slice(0, 8), matchedTerms: uniqueSorted(entry.matchedTerms) }))
394
+ .sort(sortLaneEntry);
395
+ }
396
+ function testLaneEntries(index, seedPaths, workflows, query, fileByPath) {
397
+ const seeds = new Set(seedPaths);
398
+ for (const workflow of workflows) {
399
+ for (const file of workflow.relatedFiles) {
400
+ seeds.add(file);
401
+ }
402
+ for (const step of workflow.steps) {
403
+ seeds.add(step.path);
404
+ if (step.targetPath) {
405
+ seeds.add(step.targetPath);
406
+ }
407
+ }
408
+ }
409
+ const byPath = new Map();
410
+ const add = (filePath, score, reason) => {
411
+ const file = fileByPath.get(filePath);
412
+ if (!file?.test && !isTestPath(filePath)) {
413
+ return;
414
+ }
415
+ if (!file) {
416
+ return;
417
+ }
418
+ const existing = byPath.get(file.path) ?? { file, score: 0, reasons: [], matchedTerms: [] };
419
+ existing.score += score;
420
+ existing.reasons.push(reason);
421
+ existing.matchedTerms.push(...matchedQueryTerms(query, `${file.path} ${reason}`));
422
+ byPath.set(file.path, existing);
423
+ };
424
+ for (const workflow of workflows) {
425
+ for (const test of workflow.tests) {
426
+ add(test, 22, `workflow test ${workflow.title}`);
427
+ }
428
+ }
429
+ for (const edge of index.testEdges) {
430
+ if (edge.targetPath && seeds.has(edge.targetPath)) {
431
+ add(edge.path, edge.confidence === "authoritative" ? 20 : edge.confidence === "derived" ? 16 : 10, `covers ${edge.targetPath}`);
432
+ }
433
+ }
434
+ for (const file of index.files.filter((candidate) => candidate.test)) {
435
+ const score = Math.max(matchScore(query, file.path), matchScore(query, path.posix.basename(file.path)));
436
+ if (score > 0) {
437
+ add(file.path, score + 4, "test path matches query");
438
+ }
439
+ }
440
+ return [...byPath.values()]
441
+ .map((entry) => ({ ...entry, reasons: uniqueSorted(entry.reasons).slice(0, 8), matchedTerms: uniqueSorted(entry.matchedTerms) }))
442
+ .sort(sortLaneEntry);
443
+ }
444
+ function dirtyLaneEntries(index, intents) {
445
+ if (!intents.some((intent) => intent === "implementation" || intent === "debugging" || intent === "testing")) {
446
+ return [];
447
+ }
448
+ return index.files
449
+ .filter((file) => file.dirty)
450
+ .map((file) => ({ file, score: 8 + file.rank * 0.02, reasons: ["dirty file relevant to edit/debug/test task"], matchedTerms: ["dirty"] }))
451
+ .sort(sortLaneEntry);
452
+ }
453
+ function graphLaneEntries(index, intents, anchoredEntries, fileByPath) {
454
+ const anchored = new Set(anchoredEntries.map((entry) => entry.file.path));
455
+ const anchorScores = new Map();
456
+ for (const entry of anchoredEntries) {
457
+ anchorScores.set(entry.file.path, Math.max(anchorScores.get(entry.file.path) ?? 0, entry.score));
458
+ }
459
+ const includeArchitectureCore = intents.includes("architecture");
460
+ const byPath = new Map();
461
+ const add = (filePath, score, reason) => {
462
+ if (!filePath) {
463
+ return;
464
+ }
465
+ const file = fileByPath.get(filePath);
466
+ if (!file) {
467
+ return;
468
+ }
469
+ const existing = byPath.get(file.path) ?? { file, score: 0, reasons: [], matchedTerms: [] };
470
+ existing.score += score;
471
+ existing.reasons.push(reason);
472
+ byPath.set(file.path, existing);
473
+ };
474
+ for (const file of index.files) {
475
+ if (anchored.has(file.path) || (includeArchitectureCore && /src\/(indexer|parser|resolver|query|queries|mcp|artifacts|eval|init)\.ts$/.test(file.path))) {
476
+ add(file.path, Math.log2(file.rank + 1), "graph centrality tie-breaker");
477
+ }
478
+ }
479
+ const topAnchors = [...anchorScores.entries()]
480
+ .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
481
+ .slice(0, 18);
482
+ const topAnchorSet = new Set(topAnchors.map(([filePath]) => filePath));
483
+ const anchorBoost = (filePath, factor) => Math.min(18, 4 + (anchorScores.get(filePath) ?? 0) * factor);
484
+ for (const edge of index.imports) {
485
+ if (edge.resolvedPath && topAnchorSet.has(edge.resolvedPath)) {
486
+ add(edge.path, anchorBoost(edge.resolvedPath, 0.12), `graph importer of ${edge.resolvedPath}`);
487
+ }
488
+ if (topAnchorSet.has(edge.path) && edge.resolvedPath) {
489
+ add(edge.resolvedPath, anchorBoost(edge.path, 0.1), `graph dependency of ${edge.path}`);
490
+ }
491
+ }
492
+ for (const edge of index.testEdges) {
493
+ if (edge.targetPath && topAnchorSet.has(edge.targetPath)) {
494
+ add(edge.path, anchorBoost(edge.targetPath, 0.1), `graph covering test for ${edge.targetPath}`);
495
+ }
496
+ }
497
+ for (const edge of index.graphEdges.filter(isRetrievalExpansionEdge)) {
498
+ if (edge.toPath && topAnchorSet.has(edge.toPath)) {
499
+ add(edge.fromPath, anchorBoost(edge.toPath, 0.08), `graph ${edge.edgeKind.toLowerCase()} into ${edge.toPath}`);
500
+ }
501
+ if (edge.fromPath && topAnchorSet.has(edge.fromPath)) {
502
+ add(edge.toPath, Math.max(3, anchorBoost(edge.fromPath, 0.06) - 2), `graph ${edge.edgeKind.toLowerCase()} from ${edge.fromPath}`);
503
+ }
504
+ }
505
+ return [...byPath.values()]
506
+ .map((entry) => ({ ...entry, reasons: uniqueSorted(entry.reasons).slice(0, 8), matchedTerms: [] }))
507
+ .sort(sortLaneEntry);
508
+ }
509
+ function isRetrievalExpansionEdge(edge) {
510
+ return [
511
+ "CALLS",
512
+ "REFERENCES",
513
+ "IMPORTS",
514
+ "TESTS",
515
+ "ROUTE_CALLS_STORE",
516
+ "STORE_DISPATCHES_ADAPTER",
517
+ "ADAPTER_REFERENCED_BY_MANIFEST",
518
+ "UI_CALLS_ENDPOINT",
519
+ "TEST_COVERS_WORKFLOW",
520
+ "IMPLEMENTS",
521
+ "EXTENDS"
522
+ ].includes(edge.edgeKind);
523
+ }
524
+ function fuseLaneRankings(index, lanes) {
525
+ const byPath = new Map();
526
+ for (const [lane, entries] of lanes) {
527
+ const sorted = entries.filter((entry) => entry.score > 0).sort(sortLaneEntry);
528
+ for (let indexInLane = 0; indexInLane < sorted.length; indexInLane += 1) {
529
+ const entry = sorted[indexInLane];
530
+ const existing = byPath.get(entry.file.path) ??
531
+ {
532
+ file: entry.file,
533
+ score: 0,
534
+ rawScore: 0,
535
+ reasons: [],
536
+ matchedTerms: [],
537
+ lanes: {}
538
+ };
539
+ const reciprocal = (LANE_WEIGHTS[lane] * 100) / (60 + indexInLane + 1);
540
+ existing.score += reciprocal;
541
+ existing.rawScore += entry.score * LANE_WEIGHTS[lane];
542
+ existing.lanes[lane] = (existing.lanes[lane] ?? 0) + entry.score;
543
+ existing.reasons.push(...entry.reasons);
544
+ existing.matchedTerms.push(...entry.matchedTerms);
545
+ byPath.set(entry.file.path, existing);
546
+ }
547
+ }
548
+ return [...byPath.values()]
549
+ .map((entry) => ({
550
+ file: entry.file,
551
+ score: entry.score + Math.log1p(entry.rawScore) + Math.log2(entry.file.rank + 1) * 0.05,
552
+ reasons: uniqueSorted(entry.reasons).slice(0, 10),
553
+ matchedTerms: uniqueSorted(entry.matchedTerms),
554
+ lanes: entry.lanes
555
+ }))
556
+ .sort((a, b) => b.score - a.score || b.file.rank - a.file.rank || a.file.path.localeCompare(b.file.path));
557
+ }
558
+ function analyzeIntentConfidence(query, intents, terms, matches, workflows, broad) {
559
+ const mode = intents.some((intent) => intent === "implementation" || intent === "debugging") ? "edit" : "orientation";
560
+ const primaryIntent = intents.find((intent) => intent !== "unknown") ?? "unknown";
561
+ const allowTestAnchors = /\b(test|tests|spec|specs|pytest|vitest|coverage|verification|verify)\b/i.test(query);
562
+ const directAnchorMatches = matches.filter((match) => (match.lanes.exact ?? 0) > 0 || (match.lanes.symbol ?? 0) > 0 || (match.lanes.workflow ?? 0) > 0);
563
+ const directAnchorPaths = new Set(directAnchorMatches.map((match) => match.file.path));
564
+ const semanticAnchorMatches = matches.filter((match) => !directAnchorPaths.has(match.file.path) && (match.lanes.semantic ?? 0) >= SEMANTIC_ANCHOR_MIN_SCORE);
565
+ const anchors = uniqueSorted([...directAnchorMatches, ...semanticAnchorMatches]
566
+ .filter((match) => allowTestAnchors || (!match.file.test && !isTestPath(match.file.path)))
567
+ .map((match) => match.file.path)).slice(0, 8);
568
+ const semanticAnchorCount = semanticAnchorMatches.filter((match) => anchors.includes(match.file.path)).length;
569
+ const testOnlyAnchorCount = allowTestAnchors || anchors.length > 0 ? 0 : [...directAnchorMatches, ...semanticAnchorMatches].filter((match) => match.file.test || isTestPath(match.file.path)).length;
570
+ const missingAnchors = [];
571
+ if (matches.length === 0) {
572
+ missingAnchors.push("no retrieval matches");
573
+ }
574
+ if (mode === "edit" && anchors.length === 0) {
575
+ missingAnchors.push("no authoritative or derived edit anchor");
576
+ }
577
+ if (mode === "edit" && !allowTestAnchors && testOnlyAnchorCount > 0) {
578
+ missingAnchors.push("only test anchors for edit prompt");
579
+ }
580
+ if (intents.includes("workflow") && workflows.length === 0) {
581
+ missingAnchors.push("no workflow trace matched");
582
+ }
583
+ if (broad && mode === "edit" && matches.length > 0 && anchors.length === 0) {
584
+ missingAnchors.push("broad prompt matched only weak lexical evidence");
585
+ }
586
+ const rawSearchBetter = missingAnchors.includes("broad prompt matched only weak lexical evidence") || missingAnchors.includes("only test anchors for edit prompt");
587
+ const confidence = Math.max(0, Math.min(1, 0.18 +
588
+ Math.min(0.36, anchors.length * 0.08) +
589
+ (anchors.length > 0 && !broad ? 0.22 : 0) +
590
+ Math.min(0.22, workflows.length * 0.05) +
591
+ (allowTestAnchors && matches.some((match) => (match.lanes.test ?? 0) > 0) ? 0.1 : 0) -
592
+ missingAnchors.length * 0.16 -
593
+ (broad && mode === "edit" ? 0.08 : 0)));
594
+ const editReady = mode === "edit" && missingAnchors.length === 0 && confidence >= 0.48;
595
+ const verdict = editReady ? "edit-ready" : rawSearchBetter ? "raw-search-better" : mode === "orientation" && confidence >= 0.3 ? "orientation-only" : "needs-target";
596
+ const recommendedNextTool = verdict === "needs-target" || verdict === "raw-search-better"
597
+ ? "search"
598
+ : mode === "edit"
599
+ ? "task_brief"
600
+ : intents.includes("workflow")
601
+ ? "workflow_path"
602
+ : "find_context";
603
+ const reasons = [
604
+ `mode ${mode}`,
605
+ `primary intent ${primaryIntent}`,
606
+ broad ? "broad natural-language prompt" : "anchored prompt",
607
+ anchors.length > 0 ? `${anchors.length} direct anchor(s)` : "no direct anchors",
608
+ semanticAnchorCount > 0 ? `${semanticAnchorCount} semantic anchor(s)` : undefined,
609
+ workflows.length > 0 ? `${workflows.length} workflow trace(s)` : undefined,
610
+ ...missingAnchors.map((anchor) => `missing ${anchor}`)
611
+ ].filter((entry) => Boolean(entry));
612
+ return { mode, intent: primaryIntent, confidence, anchors, selectedAnchorCount: anchors.length, discardedAnchorCount: 0, missingAnchors, recommendedNextTool, editReady, verdict, reasons };
613
+ }
614
+ function retrievalDiagnostics(index, matches, workflows, broad, intent) {
615
+ const diagnostics = [];
616
+ if (intent.verdict === "needs-target") {
617
+ diagnostics.push("needs explicit file, symbol, or narrower search before edit planning");
618
+ }
619
+ if (intent.verdict === "raw-search-better") {
620
+ diagnostics.push("raw search likely gives a cleaner first pass than this broad packet");
621
+ }
622
+ if (broad && new Set(matches.slice(0, 8).map((match) => moduleNameForPath(match.file.path))).size > 4) {
623
+ diagnostics.push("top results span many modules");
624
+ }
625
+ const centralThreshold = index.files[Math.min(index.files.length - 1, 5)]?.rank ?? Number.POSITIVE_INFINITY;
626
+ const taskSpecificLanes = (lanes) => Object.keys(lanes).filter((lane) => lane !== "graph");
627
+ if (matches.slice(0, 8).filter((match) => match.file.rank >= centralThreshold && taskSpecificLanes(match.lanes).length <= 1).length >= 4) {
628
+ diagnostics.push("central files dominate without enough task-specific lane evidence");
629
+ }
630
+ if (workflows.length === 0 && intent.intent === "workflow") {
631
+ diagnostics.push("workflow intent had no matching trace");
632
+ }
633
+ return uniqueSorted(diagnostics);
634
+ }
635
+ function sortLaneEntry(a, b) {
636
+ return b.score - a.score || b.file.rank - a.file.rank || a.file.path.localeCompare(b.file.path);
637
+ }
638
+ function matchedQueryTerms(query, haystack) {
639
+ const haystackTokens = new Set(tokenize(haystack));
640
+ return expandedQueryTerms(query).filter((term) => haystackTokens.has(term));
641
+ }
642
+ function matchScore(query, value) {
643
+ const queryText = normalizeForMatch(query);
644
+ const valueText = normalizeForMatch(value);
645
+ if (!queryText || !valueText) {
646
+ return 0;
647
+ }
648
+ if (valueText === queryText) {
649
+ return 14;
650
+ }
651
+ if (valueText.includes(queryText)) {
652
+ return 10;
653
+ }
654
+ const valueTokens = new Set(tokenize(value));
655
+ const terms = expandedQueryTerms(query);
656
+ const tokenHits = terms.filter((term) => valueTokens.has(term)).length;
657
+ if (tokenHits > 0) {
658
+ return Math.min(9, tokenHits * 2 + (tokenHits === terms.length ? 2 : 0));
659
+ }
660
+ const partialHits = terms.filter((term) => term.length >= 4 && valueText.includes(term)).length;
661
+ return partialHits > 0 ? Math.min(6, partialHits * 1.5) : 0;
662
+ }
663
+ function normalizeForMatch(value) {
664
+ return value
665
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
666
+ .toLowerCase()
667
+ .replace(/[^a-z0-9]+/g, " ")
668
+ .trim();
669
+ }
670
+ function intentBoost(file, intents) {
671
+ let boost = 0;
672
+ const p = file.path;
673
+ if (intents.includes("frontend") && (p.startsWith("web/") || /\.(tsx|jsx)$/.test(p)))
674
+ boost += 2.5;
675
+ if (intents.includes("backend") && (/\.py$/.test(p) || p.includes("_api/") || p.includes("/adapters/")))
676
+ boost += 2.5;
677
+ if (intents.includes("testing") && file.test)
678
+ boost += 3;
679
+ if (intents.includes("configuration") && /\.(json|toml|ya?ml|service|sh)$/.test(p))
680
+ boost += 2;
681
+ if (intents.includes("architecture") && /src\/(indexer|parser|resolver|queries|mcp|artifacts|eval|init)\.ts$/.test(p))
682
+ boost += 4;
683
+ if (intents.includes("workflow") && /(app|route|adapter|store|execution|queue|polling|workflow|run)/i.test(p))
684
+ boost += 3;
685
+ if (intents.includes("risk") && file.riskScore > 0)
686
+ boost += Math.min(4, file.riskScore / 4);
687
+ if (file.dirty && intents.some((intent) => intent === "implementation" || intent === "debugging"))
688
+ boost += 1.5;
689
+ return boost;
690
+ }
691
+ function documentFrequency(docs) {
692
+ const result = new Map();
693
+ for (const doc of docs) {
694
+ for (const term of new Set(doc.terms)) {
695
+ result.set(term, (result.get(term) ?? 0) + 1);
696
+ }
697
+ }
698
+ return result;
699
+ }
700
+ function rankWorkflows(index, terms, matches) {
701
+ const matchedFiles = new Set(matches.slice(0, 20).map((match) => match.file.path));
702
+ const specificTerms = terms.filter((term) => !BROAD_WORKFLOW_TERMS.has(term));
703
+ return [...index.workflows]
704
+ .map((workflow) => {
705
+ const titleTokens = new Set(tokenize(`${workflow.title} ${workflow.workflowKind} ${workflow.entryPath}`));
706
+ const fileTokens = new Set(tokenize(workflow.relatedFiles.join(" ")));
707
+ const stepTokens = new Set(tokenize(workflow.steps.map((step) => `${step.kind} ${step.label} ${step.reason}`).join(" ")));
708
+ const primaryTokens = new Set(tokenize([
709
+ workflow.title,
710
+ workflow.workflowKind,
711
+ workflow.entryPath,
712
+ ...workflow.steps
713
+ .filter((step) => !["ui", "test", "risk"].includes(step.kind))
714
+ .map((step) => `${step.kind} ${step.label} ${step.reason} ${step.path} ${step.targetPath ?? ""}`)
715
+ ].join(" ")));
716
+ const titleScore = terms.reduce((sum, term) => sum + (titleTokens.has(term) ? 5 : 0), 0);
717
+ const fileTermScore = terms.reduce((sum, term) => sum + (fileTokens.has(term) ? 2 : 0), 0);
718
+ const stepScore = terms.reduce((sum, term) => sum + (stepTokens.has(term) ? 1.5 : 0), 0);
719
+ const specificHits = specificTerms.filter((term) => titleTokens.has(term) || fileTokens.has(term) || stepTokens.has(term)).length;
720
+ const highSignalTerms = specificTerms.filter((term) => !SUPPORT_WORKFLOW_TERMS.has(term));
721
+ const primaryHighSignalHits = highSignalTerms.filter((term) => primaryTokens.has(term)).length;
722
+ const specificityBoost = specificHits > 0 ? specificHits * 3 : 0;
723
+ const primarySignalBoost = primaryHighSignalHits * 4;
724
+ const primarySignalPenalty = highSignalTerms.length > 0 && primaryHighSignalHits === 0 ? 12 : 0;
725
+ const fileScore = workflow.relatedFiles.filter((file) => matchedFiles.has(file)).length * 3;
726
+ const sizePenalty = Math.log2(workflow.relatedFiles.length + workflow.steps.length + 2) * 0.75;
727
+ const evidenceScore = titleScore + fileTermScore + stepScore + specificityBoost + primarySignalBoost + fileScore - sizePenalty - primarySignalPenalty;
728
+ return { workflow, score: evidenceScore > 0 ? evidenceScore + Math.log2(workflow.rank + 1) * 0.2 : 0 };
729
+ })
730
+ .filter((entry) => entry.score > 0)
731
+ .sort((a, b) => b.score - a.score || a.workflow.title.localeCompare(b.workflow.title))
732
+ .map((entry) => entry.workflow);
733
+ }
734
+ function rankModules(index, matches, terms) {
735
+ const byModule = new Map();
736
+ for (const match of matches) {
737
+ const moduleName = moduleNameForPath(match.file.path);
738
+ const existing = byModule.get(moduleName) ?? { name: moduleName, score: 0, files: new Set(), reasons: new Set() };
739
+ existing.score += match.score;
740
+ existing.files.add(match.file.path);
741
+ for (const term of match.matchedTerms.slice(0, 6)) {
742
+ if (terms.includes(term)) {
743
+ existing.reasons.add(`matched ${term}`);
744
+ }
745
+ }
746
+ byModule.set(moduleName, existing);
747
+ }
748
+ return [...byModule.values()]
749
+ .map((entry) => ({
750
+ name: entry.name,
751
+ score: entry.score,
752
+ files: [...entry.files].sort(),
753
+ reasons: [...entry.reasons].sort().slice(0, 6)
754
+ }))
755
+ .sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
756
+ }
757
+ function tokenize(value) {
758
+ const expandedCamel = value.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
759
+ return uniqueSorted(expandedCamel
760
+ .toLowerCase()
761
+ .split(/[^a-z0-9]+/)
762
+ .map((term) => term.trim())
763
+ .filter((term) => term.length >= 2 && !STOP_WORDS.has(term)));
764
+ }
765
+ function isDecoyLikePath(filePath) {
766
+ const spaced = filePath.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase();
767
+ return /(?:^|\b|[._/-])(decoy|mock|old|backup|copy|fixture)(?:$|\b|[._/-])/.test(spaced) || /(decoy|mock|backup|fixture)/.test(spaced.replace(/[^a-z0-9]+/g, ""));
768
+ }
769
+ function queryAllowsDecoy(query) {
770
+ return /\b(decoy|mock|fixture|backup|old|copy)\b/i.test(query);
771
+ }
772
+ function uniqueInOrder(items) {
773
+ const seen = new Set();
774
+ const result = [];
775
+ for (const item of items) {
776
+ if (!seen.has(item)) {
777
+ seen.add(item);
778
+ result.push(item);
779
+ }
780
+ }
781
+ return result;
782
+ }
783
+ //# sourceMappingURL=retrieval.js.map