@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
package/dist/graph.js ADDED
@@ -0,0 +1,665 @@
1
+ import { stableId, uniqueSorted } from "./util.js";
2
+ export function buildGraphEdges(index) {
3
+ const filesByPath = new Map(index.files.map((file) => [file.path, file]));
4
+ const symbolsById = new Map(index.symbols.map((symbol) => [symbol.id, symbol]));
5
+ const edges = [];
6
+ const seen = new Set();
7
+ const add = (edgeKind, from, to, confidence, reason, weight, source = "tree-sitter", usage) => {
8
+ const id = stableId("graph-edge", edgeKind, from.id, to.id, reason, usage?.range?.startByte ?? 0);
9
+ if (seen.has(id)) {
10
+ return;
11
+ }
12
+ seen.add(id);
13
+ edges.push({
14
+ id,
15
+ type: "GraphEdge",
16
+ edgeKind,
17
+ fromId: from.id,
18
+ toId: to.id,
19
+ fromKind: from.kind,
20
+ toKind: to.kind,
21
+ fromPath: from.path,
22
+ toPath: to.path,
23
+ fromSymbolId: from.symbolId,
24
+ toSymbolId: to.symbolId,
25
+ reason,
26
+ weight,
27
+ source,
28
+ confidence,
29
+ snapshotId: index.snapshot.snapshotId,
30
+ indexedAt: index.snapshot.indexedAt,
31
+ range: usage?.range
32
+ });
33
+ };
34
+ for (const symbol of index.symbols) {
35
+ const file = filesByPath.get(symbol.path);
36
+ if (!file) {
37
+ continue;
38
+ }
39
+ add("DEFINES", fileTarget(file), symbolTarget(symbol), symbol.confidence, `defines ${symbol.qualifiedName}`, 3, symbol.source);
40
+ if (symbol.exported) {
41
+ add(symbol.kind === "interface" || symbol.kind === "type" || symbol.kind === "enum" ? "TYPE_EXPORTS" : "EXPORTS", fileTarget(file), symbolTarget(symbol), symbol.confidence, `exports ${symbol.qualifiedName}`, 2.8, symbol.source);
42
+ }
43
+ if (symbol.kind === "route") {
44
+ add("ROUTE", fileTarget(file), symbolTarget(symbol), symbol.confidence, `route entry ${symbol.qualifiedName}`, 4, symbol.source);
45
+ }
46
+ if (isJobSymbol(symbol)) {
47
+ add("JOB", fileTarget(file), symbolTarget(symbol), "heuristic", `job entry ${symbol.qualifiedName}`, 4, "heuristic");
48
+ }
49
+ }
50
+ for (const imp of index.imports) {
51
+ if (!imp.resolvedPath || imp.typeOnly) {
52
+ continue;
53
+ }
54
+ const from = filesByPath.get(imp.path);
55
+ const to = filesByPath.get(imp.resolvedPath);
56
+ if (!from || !to) {
57
+ continue;
58
+ }
59
+ add("IMPORTS", fileTarget(from), fileTarget(to), imp.confidence, `imports ${imp.importedName ?? "*"} from ${imp.specifier}`, 3, imp.source);
60
+ const importedSymbol = importedSymbolForEdge(index.symbols, imp);
61
+ if (importedSymbol) {
62
+ add("IMPORTS", fileTarget(from), symbolTarget(importedSymbol), imp.confidence, `imports ${imp.importedName ?? importedSymbol.name} from ${imp.specifier}`, 3.2, imp.source);
63
+ }
64
+ }
65
+ for (const usage of index.usageSites) {
66
+ if (!usage.targetSymbolId) {
67
+ continue;
68
+ }
69
+ const target = symbolsById.get(usage.targetSymbolId);
70
+ if (!target) {
71
+ continue;
72
+ }
73
+ const fromSymbol = usage.usedBySymbolId ? symbolsById.get(usage.usedBySymbolId) : undefined;
74
+ const fromFile = filesByPath.get(usage.path);
75
+ if (!fromSymbol && !fromFile) {
76
+ continue;
77
+ }
78
+ const from = fromSymbol ? symbolTarget(fromSymbol) : fileTarget(fromFile);
79
+ const kind = usage.kind === "call" ? "CALLS" : "REFERENCES";
80
+ const weight = kind === "CALLS" ? 2.5 : usage.kind === "import" ? 2 : 1.5;
81
+ add(kind, from, symbolTarget(target), usage.confidence, `${usage.kind} ${usage.name}`, weight, usage.source, usage);
82
+ if (usage.kind === "type_reference" && /^extends\s+/i.test(usage.text)) {
83
+ add("EXTENDS", from, symbolTarget(target), usage.confidence, usage.text, 2.5, usage.source, usage);
84
+ }
85
+ if (usage.kind === "type_reference" && /^implements\s+/i.test(usage.text)) {
86
+ add("IMPLEMENTS", from, symbolTarget(target), usage.confidence, usage.text, 2.5, usage.source, usage);
87
+ }
88
+ }
89
+ addTypedWorkflowEdges(index, { filesByPath, symbolsById, add, hasGraphEdge: (predicate) => edges.some(predicate) });
90
+ for (const test of index.testEdges) {
91
+ if (!test.targetPath) {
92
+ continue;
93
+ }
94
+ const from = filesByPath.get(test.path);
95
+ const to = filesByPath.get(test.targetPath);
96
+ if (!from || !to) {
97
+ continue;
98
+ }
99
+ add("TESTS", fileTarget(from), fileTarget(to), test.confidence, test.reason, 3, test.source);
100
+ add("TEST_COVERS_WORKFLOW", fileTarget(from), fileTarget(to), test.confidence, test.reason, 2.8, test.source);
101
+ }
102
+ for (const risk of index.risks) {
103
+ const file = filesByPath.get(risk.path);
104
+ if (!file) {
105
+ continue;
106
+ }
107
+ add("RISK", fileTarget(file), riskTarget(risk), risk.confidence, `${risk.signal}: ${risk.reason}`, Math.max(0.5, risk.score), risk.source);
108
+ }
109
+ return edges.sort((a, b) => a.edgeKind.localeCompare(b.edgeKind) ||
110
+ (a.fromPath ?? "").localeCompare(b.fromPath ?? "") ||
111
+ (a.toPath ?? "").localeCompare(b.toPath ?? "") ||
112
+ a.reason.localeCompare(b.reason));
113
+ }
114
+ function addTypedWorkflowEdges(index, context) {
115
+ const { filesByPath, symbolsById, add, hasGraphEdge } = context;
116
+ const routeEndpointUsages = index.usageSites.filter((usage) => usage.kind === "route_handler" && normalizeEndpointKey(usage.name));
117
+ const endpointToRoute = new Map();
118
+ for (const usage of routeEndpointUsages) {
119
+ const key = normalizeEndpointKey(usage.name);
120
+ if (!key) {
121
+ continue;
122
+ }
123
+ const route = usage.usedBySymbolId ? symbolsById.get(usage.usedBySymbolId) : undefined;
124
+ if (!route) {
125
+ continue;
126
+ }
127
+ const existing = endpointToRoute.get(key) ?? [];
128
+ existing.push({ usage, route });
129
+ endpointToRoute.set(key, existing);
130
+ add("ROUTE_HANDLES", symbolTarget(route), endpointTarget(key, route.path), usage.confidence, `handles endpoint ${key}`, 4.5, usage.source, usage);
131
+ }
132
+ for (const usage of index.usageSites.filter((site) => site.kind === "endpoint_reference")) {
133
+ const key = normalizeEndpointKey(usage.name);
134
+ if (!key) {
135
+ continue;
136
+ }
137
+ const matches = endpointMatches(endpointToRoute, key);
138
+ for (const match of matches.slice(0, 4)) {
139
+ const fromFile = filesByPath.get(usage.path);
140
+ const fromSymbol = usage.usedBySymbolId ? symbolsById.get(usage.usedBySymbolId) : undefined;
141
+ const from = fromSymbol ? symbolTarget(fromSymbol) : fromFile ? fileTarget(fromFile) : undefined;
142
+ if (!from || !match.route) {
143
+ continue;
144
+ }
145
+ const edgeKind = isTestLikePath(usage.path) ? "TEST_COVERS_WORKFLOW" : "UI_CALLS_ENDPOINT";
146
+ add(edgeKind, from, endpointTarget(normalizeEndpointKey(match.usage.name) ?? key, match.route.path, match.route.id), mergeEndpointConfidence(usage.confidence, match.usage.confidence), `${usage.path} references endpoint ${key} handled by ${match.route.qualifiedName}`, edgeKind === "UI_CALLS_ENDPOINT" ? 3.8 : 3.2, usage.source, usage);
147
+ }
148
+ }
149
+ const symbolsByPath = new Map();
150
+ for (const symbol of index.symbols) {
151
+ const list = symbolsByPath.get(symbol.path) ?? [];
152
+ list.push(symbol);
153
+ symbolsByPath.set(symbol.path, list);
154
+ }
155
+ const routeFiles = new Set(index.symbols.filter((symbol) => symbol.kind === "route").map((symbol) => symbol.path));
156
+ const storeFiles = new Set(index.files
157
+ .filter((file) => !routeFiles.has(file.path) && (isStorePath(file.path) || symbolsByPath.get(file.path)?.some(isStoreBoundarySymbol)))
158
+ .map((file) => file.path));
159
+ const importsByPath = new Map();
160
+ for (const imp of index.imports) {
161
+ const list = importsByPath.get(imp.path) ?? [];
162
+ list.push(imp);
163
+ importsByPath.set(imp.path, list);
164
+ }
165
+ const adapterFiles = new Set(index.files.filter((file) => isAdapterPath(file.path)).map((file) => file.path));
166
+ for (const route of index.symbols.filter((symbol) => symbol.kind === "route")) {
167
+ const routeTarget = symbolTarget(route);
168
+ for (const usage of index.usageSites.filter((site) => site.usedBySymbolId === route.id)) {
169
+ const target = usage.targetSymbolId ? symbolsById.get(usage.targetSymbolId) : undefined;
170
+ const targetPath = target?.path;
171
+ if (targetPath && storeFiles.has(targetPath)) {
172
+ add("ROUTE_CALLS_STORE", routeTarget, symbolTarget(target), usage.confidence, `route store usage ${usage.name}`, 3, usage.source, usage);
173
+ }
174
+ else if (!targetPath) {
175
+ const inferred = inferRouteStoreCallTarget(route, usage, storeFiles, symbolsByPath, importsByPath, filesByPath);
176
+ if (inferred) {
177
+ const hasNonHeuristicStoreEdge = hasGraphEdge((edge) => edge.edgeKind === "ROUTE_CALLS_STORE" && edge.fromId === routeTarget.id && edge.toPath === inferred.path && edge.source !== "heuristic");
178
+ if (hasNonHeuristicStoreEdge) {
179
+ continue;
180
+ }
181
+ add("ROUTE_CALLS_STORE", routeTarget, inferred, "heuristic", `route store usage ${usage.name} (inferred)`, 2.2, "heuristic", usage);
182
+ }
183
+ }
184
+ }
185
+ }
186
+ for (const storePath of storeFiles) {
187
+ const storeFile = filesByPath.get(storePath);
188
+ if (!storeFile) {
189
+ continue;
190
+ }
191
+ const adapterEvidence = new Set();
192
+ for (const usage of index.usageSites.filter((site) => site.path === storePath)) {
193
+ const target = usage.targetSymbolId ? symbolsById.get(usage.targetSymbolId) : undefined;
194
+ if (target && adapterFiles.has(target.path)) {
195
+ adapterEvidence.add(target.path);
196
+ add("STORE_DISPATCHES_ADAPTER", fileTarget(storeFile), symbolTarget(target), usage.confidence, `store adapter usage ${usage.name}`, 3.2, usage.source, usage);
197
+ }
198
+ }
199
+ for (const imp of index.imports.filter((candidate) => candidate.path === storePath && candidate.resolvedPath && adapterFiles.has(candidate.resolvedPath))) {
200
+ const adapterFile = imp.resolvedPath ? filesByPath.get(imp.resolvedPath) : undefined;
201
+ if (adapterFile && !adapterEvidence.has(adapterFile.path)) {
202
+ add("STORE_DISPATCHES_ADAPTER", fileTarget(storeFile), fileTarget(adapterFile), "derived", `store imports adapter ${imp.specifier}`, 2.4, imp.source);
203
+ }
204
+ }
205
+ }
206
+ const adapterFileList = [...adapterFiles].map((filePath) => filesByPath.get(filePath)).filter((file) => Boolean(file));
207
+ for (const usage of index.usageSites.filter((site) => isNodeManifestPath(site.path) && /adapter_key\s+/i.test(site.text))) {
208
+ const manifest = filesByPath.get(usage.path);
209
+ if (!manifest) {
210
+ continue;
211
+ }
212
+ for (const adapter of matchingAdaptersForKey(adapterFileList, usage.name, manifest.path).slice(0, 4)) {
213
+ add("ADAPTER_REFERENCED_BY_MANIFEST", fileTarget(manifest), fileTarget(adapter), usage.confidence, `manifest adapter_key ${usage.name}`, 3.6, usage.source, usage);
214
+ }
215
+ }
216
+ }
217
+ function normalizeEndpointKey(value) {
218
+ const match = /^(GET|POST|PUT|PATCH|DELETE|WEBSOCKET|ANY)\s+(\/\S*)$/i.exec(value.trim());
219
+ if (!match) {
220
+ return undefined;
221
+ }
222
+ const method = match[1].toUpperCase();
223
+ const routePath = match[2].split(/[?#]/, 1)[0].replace(/\/+/g, "/").replace(/\/$/, "") || "/";
224
+ return `${method} ${routePath}`;
225
+ }
226
+ function endpointMatches(endpointToRoute, key) {
227
+ const normalized = normalizeEndpointKey(key);
228
+ if (!normalized) {
229
+ return [];
230
+ }
231
+ const [method, routePath] = normalized.split(" ");
232
+ const exact = endpointToRoute.get(normalized) ?? [];
233
+ if (exact.length > 0) {
234
+ return exact;
235
+ }
236
+ const compatible = [...endpointToRoute.entries()].filter(([candidate]) => {
237
+ const [candidateMethod, candidatePath] = candidate.split(" ");
238
+ const methodMatches = method === "ANY" || candidateMethod === "ANY" || candidateMethod === method;
239
+ return methodMatches && endpointPathCompatible(routePath, candidatePath);
240
+ });
241
+ return compatible.flatMap(([, values]) => values);
242
+ }
243
+ function importedSymbolForEdge(symbols, imp) {
244
+ if (!imp.resolvedPath || !imp.importedName || imp.importedName === "*") {
245
+ return undefined;
246
+ }
247
+ const candidates = symbols.filter((symbol) => symbol.path === imp.resolvedPath && symbol.exported);
248
+ if (imp.importedName === "default") {
249
+ return candidates.find((symbol) => symbol.name === "default");
250
+ }
251
+ return candidates.find((symbol) => symbol.name === imp.importedName || symbol.qualifiedName === imp.importedName);
252
+ }
253
+ function endpointTarget(key, pathValue, symbolId) {
254
+ return { id: stableId("endpoint", normalizeEndpointKey(key) ?? key), kind: "endpoint", path: pathValue, symbolId, label: normalizeEndpointKey(key) ?? key };
255
+ }
256
+ function endpointPathCompatible(left, right) {
257
+ if (left === right) {
258
+ return true;
259
+ }
260
+ const leftSegments = left.split("/").filter(Boolean);
261
+ const rightSegments = right.split("/").filter(Boolean);
262
+ if (leftSegments.length !== rightSegments.length) {
263
+ return false;
264
+ }
265
+ return leftSegments.every((segment, index) => pathSegmentCompatible(segment, rightSegments[index]));
266
+ }
267
+ function pathSegmentCompatible(left, right) {
268
+ if (left === right) {
269
+ return true;
270
+ }
271
+ return isDynamicSegment(left) && isDynamicSegment(right);
272
+ }
273
+ function isDynamicSegment(value) {
274
+ return /^\{[^}]+\}$/.test(value) || /^\$\{[^}]+\}$/.test(value) || /^:[A-Za-z_][\w-]*$/.test(value);
275
+ }
276
+ function mergeEndpointConfidence(a, b) {
277
+ if (a === "authoritative" && b === "authoritative") {
278
+ return "authoritative";
279
+ }
280
+ if (a === "heuristic" || b === "heuristic") {
281
+ return "heuristic";
282
+ }
283
+ return "derived";
284
+ }
285
+ function isTestLikePath(filePath) {
286
+ return /(^|\/)(tests?|__tests__)\/|(\.test|\.spec)\.[cm]?[jt]sx?$|(^|\/)test_[^/]+\.py$/.test(filePath);
287
+ }
288
+ function isStorePath(filePath) {
289
+ return /(^|\/)(store|stores|repository|repositories|db|database|models|execution|queue|run-store)(\/|\.|$)/i.test(filePath);
290
+ }
291
+ function isAdapterPath(filePath) {
292
+ return /(^|\/)adapters?\/|(^|\/).*adapter.*\.(py|[cm]?[jt]sx?)$/i.test(filePath);
293
+ }
294
+ function isNodeManifestPath(filePath) {
295
+ return /(^|\/)(packages?|manifests?)\//i.test(filePath) && /\.json$/i.test(filePath);
296
+ }
297
+ function sameManifestAdapterLocality(manifestPath, adapterPath) {
298
+ if (!isNodeManifestPath(manifestPath) || !isAdapterPath(adapterPath)) {
299
+ return false;
300
+ }
301
+ const manifestRoot = localityRoot(manifestPath);
302
+ const adapterRoot = localityRoot(adapterPath);
303
+ return manifestRoot === adapterRoot;
304
+ }
305
+ function localityRoot(filePath) {
306
+ const parts = filePath.split("/").filter(Boolean);
307
+ const adaptersIndex = parts.findIndex((part) => /^adapters?$/iu.test(part));
308
+ if (adaptersIndex >= 0) {
309
+ return parts.slice(0, adaptersIndex).join("/");
310
+ }
311
+ const packageIndex = parts.findIndex((part) => /^packages?$/iu.test(part));
312
+ if (packageIndex >= 0) {
313
+ return parts.at(-1) === "package.json" ? parts.slice(0, -1).join("/") : parts.slice(0, packageIndex).join("/");
314
+ }
315
+ const manifestIndex = parts.findIndex((part) => /^manifests?$/iu.test(part));
316
+ if (manifestIndex >= 0) {
317
+ return parts.slice(0, manifestIndex).join("/");
318
+ }
319
+ return parts.slice(0, -1).join("/");
320
+ }
321
+ function isStoreBoundarySymbol(symbol) {
322
+ if (!["class", "function", "method"].includes(symbol.kind)) {
323
+ return false;
324
+ }
325
+ return isStoreLikeName(symbol.name) || isStoreLikeName(symbol.qualifiedName);
326
+ }
327
+ function isStoreLikeName(value) {
328
+ return /\b(RunStore|Store|Repository|Coordinator|Database)\b/.test(value) || /(Store|Repository|Coordinator)$/.test(value);
329
+ }
330
+ function inferRouteStoreCallTarget(route, usage, storeFiles, symbolsByPath, importsByPath, filesByPath) {
331
+ const match = /^store\.([A-Za-z_][\w]*)$/u.exec(usage.name);
332
+ if (!match) {
333
+ return undefined;
334
+ }
335
+ if (!hasStoreHandleInRouteFile(route, storeFiles, symbolsByPath, importsByPath)) {
336
+ return undefined;
337
+ }
338
+ const methodName = match[1];
339
+ const routePath = route.path;
340
+ const importedStorePaths = uniqueSorted((importsByPath.get(routePath) ?? [])
341
+ .map((imp) => imp.resolvedPath)
342
+ .filter((resolvedPath) => typeof resolvedPath === "string")
343
+ .filter((resolvedPath) => storeFiles.has(resolvedPath)));
344
+ const candidatePaths = importedStorePaths.length > 0 ? importedStorePaths : storeFiles.size === 1 ? [...storeFiles] : [];
345
+ for (const storePath of candidatePaths) {
346
+ const method = (symbolsByPath.get(storePath) ?? []).find((symbol) => symbol.name === methodName || symbol.qualifiedName.endsWith(`.${methodName}`));
347
+ if (method) {
348
+ return symbolTarget(method);
349
+ }
350
+ }
351
+ for (const storePath of candidatePaths) {
352
+ const file = filesByPath.get(storePath);
353
+ if (file) {
354
+ return fileTarget(file);
355
+ }
356
+ }
357
+ return undefined;
358
+ }
359
+ function hasStoreHandleInRouteFile(route, storeFiles, symbolsByPath, importsByPath) {
360
+ const imports = importsByPath.get(route.path) ?? [];
361
+ const storeImports = imports.filter((imp) => typeof imp.resolvedPath === "string" && storeFiles.has(imp.resolvedPath));
362
+ if (storeImports.length === 0) {
363
+ return false;
364
+ }
365
+ if (storeImports.some((imp) => imp.localName === "store" || imp.importedName === "store")) {
366
+ return true;
367
+ }
368
+ return (symbolsByPath.get(route.path) ?? []).some((symbol) => symbol.name === "store" &&
369
+ symbol.kind === "variable" &&
370
+ typeof symbol.range?.startLine === "number" &&
371
+ typeof route.range?.startLine === "number" &&
372
+ symbol.range.startLine < route.range.startLine);
373
+ }
374
+ function matchingAdaptersForKey(adapters, adapterKey, manifestPath) {
375
+ const keyParts = adapterKey
376
+ .toLowerCase()
377
+ .split(/[^a-z0-9]+/)
378
+ .filter((part) => part.length >= 2);
379
+ const compactKey = keyParts.join("");
380
+ const underscoredKey = keyParts.join("_");
381
+ const scored = adapters
382
+ .map((adapter) => {
383
+ const stem = adapter.path
384
+ .toLowerCase()
385
+ .replace(/\.[^.]+$/, "")
386
+ .split("/")
387
+ .at(-1) ?? "";
388
+ const compactStem = stem.replace(/[^a-z0-9]+/g, "");
389
+ const pathText = adapter.path.toLowerCase();
390
+ const exact = stem === adapterKey.toLowerCase() || stem === underscoredKey || compactStem === compactKey;
391
+ const terminalExact = keyParts.at(-1) ? stem === keyParts.at(-1) : false;
392
+ const localityBoost = manifestPath && sameManifestAdapterLocality(manifestPath, adapter.path) ? 20 : 0;
393
+ const score = exact
394
+ ? 100 + localityBoost
395
+ : terminalExact
396
+ ? 12 + localityBoost
397
+ : keyParts.reduce((sum, part) => sum + (stem === part ? 4 : pathText.includes(part) ? 1 : 0), 0) + localityBoost;
398
+ return { adapter, score };
399
+ })
400
+ .filter((entry) => entry.score > 0)
401
+ .sort((a, b) => b.score - a.score || b.adapter.rank - a.adapter.rank || a.adapter.path.localeCompare(b.adapter.path));
402
+ const exact = scored.filter((entry) => entry.score >= 100);
403
+ if (exact.length > 0) {
404
+ return exact.slice(0, 1).map((entry) => entry.adapter);
405
+ }
406
+ const best = scored[0]?.score ?? 0;
407
+ return best > 0 ? scored.slice(0, 1).map((entry) => entry.adapter) : [];
408
+ }
409
+ export function extractWorkflowTraces(index) {
410
+ const symbolsById = new Map(index.symbols.map((symbol) => [symbol.id, symbol]));
411
+ const fileRank = new Map(index.files.map((file) => [file.path, file.rank]));
412
+ const workflows = [];
413
+ const entrySymbols = index.symbols.filter((symbol) => symbol.kind === "route" || isJobSymbol(symbol));
414
+ for (const symbol of entrySymbols) {
415
+ const workflowKind = symbol.kind === "route" ? "route" : "job";
416
+ const steps = entryStepsForSymbol(index, symbol, symbolsById);
417
+ const relatedFiles = uniqueSorted(steps.flatMap((step) => [step.path, step.targetPath]).filter((filePath) => Boolean(filePath)));
418
+ const tests = relatedTests(index, relatedFiles);
419
+ for (const testPath of tests.slice(0, 8)) {
420
+ steps.push({
421
+ kind: "test",
422
+ label: testPath,
423
+ path: testPath,
424
+ targetPath: symbol.path,
425
+ confidence: "authoritative",
426
+ reason: "covers workflow-related file"
427
+ });
428
+ }
429
+ workflows.push(workflowFact(index, {
430
+ workflowKind,
431
+ title: `${workflowKind} ${symbol.qualifiedName}`,
432
+ entryPath: symbol.path,
433
+ entrySymbolId: symbol.id,
434
+ steps,
435
+ relatedFiles: uniqueSorted([...relatedFiles, ...tests]),
436
+ tests,
437
+ rank: workflowRank(relatedFiles, tests, fileRank, workflowKind),
438
+ summary: summarizeWorkflow(workflowKind, symbol.qualifiedName, relatedFiles, tests)
439
+ }));
440
+ }
441
+ for (const symbol of index.symbols.filter((candidate) => candidate.kind === "node")) {
442
+ const steps = [
443
+ {
444
+ kind: "entry",
445
+ label: symbol.qualifiedName,
446
+ path: symbol.path,
447
+ line: symbol.range?.startLine,
448
+ symbolId: symbol.id,
449
+ confidence: symbol.confidence,
450
+ reason: "node manifest"
451
+ }
452
+ ];
453
+ for (const usage of index.usageSites.filter((site) => site.targetSymbolId === symbol.id || site.name === symbol.name).slice(0, 20)) {
454
+ steps.push({
455
+ kind: usage.kind === "call" ? "call" : "reference",
456
+ label: usage.name,
457
+ path: usage.path,
458
+ line: usage.range?.startLine,
459
+ targetSymbolId: usage.targetSymbolId,
460
+ targetPath: usage.path,
461
+ confidence: usage.confidence,
462
+ reason: usage.text
463
+ });
464
+ }
465
+ for (const edge of index.graphEdges.filter((candidate) => candidate.edgeKind === "ADAPTER_REFERENCED_BY_MANIFEST" && candidate.fromPath === symbol.path).slice(0, 12)) {
466
+ steps.push({
467
+ kind: "adapter",
468
+ label: edge.reason,
469
+ path: edge.fromPath ?? symbol.path,
470
+ line: edge.range?.startLine,
471
+ targetPath: edge.toPath,
472
+ confidence: edge.confidence,
473
+ reason: edge.edgeKind
474
+ });
475
+ }
476
+ const relatedFiles = uniqueSorted(steps.flatMap((step) => [step.path, step.targetPath]).filter((filePath) => Boolean(filePath)));
477
+ const tests = relatedTests(index, relatedFiles);
478
+ workflows.push(workflowFact(index, {
479
+ workflowKind: "manifest",
480
+ title: `manifest ${symbol.name}`,
481
+ entryPath: symbol.path,
482
+ entrySymbolId: symbol.id,
483
+ steps,
484
+ relatedFiles: uniqueSorted([...relatedFiles, ...tests]),
485
+ tests,
486
+ rank: workflowRank(relatedFiles, tests, fileRank, "manifest"),
487
+ summary: summarizeWorkflow("manifest", symbol.name, relatedFiles, tests)
488
+ }));
489
+ }
490
+ return workflows
491
+ .filter((workflow) => workflow.steps.length > 0)
492
+ .sort((a, b) => b.rank - a.rank || a.title.localeCompare(b.title));
493
+ }
494
+ function entryStepsForSymbol(index, entry, symbolsById) {
495
+ const steps = [
496
+ {
497
+ kind: "entry",
498
+ label: entry.qualifiedName,
499
+ path: entry.path,
500
+ line: entry.range?.startLine,
501
+ symbolId: entry.id,
502
+ confidence: entry.confidence,
503
+ reason: `${entry.kind} entry`
504
+ }
505
+ ];
506
+ const directUsages = index.usageSites
507
+ .filter((usage) => usage.usedBySymbolId === entry.id)
508
+ .sort((a, b) => (a.range?.startLine ?? 0) - (b.range?.startLine ?? 0) || a.name.localeCompare(b.name))
509
+ .slice(0, 30);
510
+ for (const usage of directUsages) {
511
+ const target = usage.targetSymbolId ? symbolsById.get(usage.targetSymbolId) : undefined;
512
+ steps.push({
513
+ kind: usage.kind === "call" ? "call" : usage.kind === "import" ? "import" : "reference",
514
+ label: target?.qualifiedName ?? usage.name,
515
+ path: usage.path,
516
+ line: usage.range?.startLine,
517
+ symbolId: usage.usedBySymbolId,
518
+ targetSymbolId: usage.targetSymbolId,
519
+ targetPath: target?.path ?? usage.path,
520
+ confidence: usage.confidence,
521
+ reason: usage.text
522
+ });
523
+ }
524
+ steps.push(...typedWorkflowStepsForEntry(index, entry));
525
+ for (const risk of index.risks.filter((candidate) => candidate.path === entry.path && rangesOverlap(candidate.range, entry.range)).slice(0, 12)) {
526
+ steps.push({
527
+ kind: "risk",
528
+ label: risk.signal,
529
+ path: risk.path,
530
+ line: risk.range?.startLine,
531
+ targetPath: risk.path,
532
+ confidence: risk.confidence,
533
+ reason: risk.reason
534
+ });
535
+ }
536
+ return steps;
537
+ }
538
+ function typedWorkflowStepsForEntry(index, entry) {
539
+ const steps = [];
540
+ const routeEdges = index.graphEdges.filter((edge) => edge.fromSymbolId === entry.id && ["ROUTE_HANDLES", "ROUTE_CALLS_STORE"].includes(edge.edgeKind));
541
+ const endpointIds = new Set(routeEdges.filter((edge) => edge.edgeKind === "ROUTE_HANDLES").map((edge) => edge.toId));
542
+ for (const edge of routeEdges) {
543
+ steps.push({
544
+ kind: edge.edgeKind === "ROUTE_HANDLES" ? "endpoint" : "store",
545
+ label: edge.reason,
546
+ path: edge.fromPath ?? entry.path,
547
+ line: edge.range?.startLine,
548
+ symbolId: edge.fromSymbolId,
549
+ targetPath: edge.toPath,
550
+ targetSymbolId: edge.toSymbolId,
551
+ confidence: edge.confidence,
552
+ reason: edge.edgeKind
553
+ });
554
+ }
555
+ for (const edge of index.graphEdges.filter((candidate) => endpointIds.has(candidate.toId) && ["UI_CALLS_ENDPOINT", "TEST_COVERS_WORKFLOW"].includes(candidate.edgeKind))) {
556
+ steps.push({
557
+ kind: edge.edgeKind === "UI_CALLS_ENDPOINT" ? "ui" : "test",
558
+ label: edge.reason,
559
+ path: edge.fromPath ?? entry.path,
560
+ line: edge.range?.startLine,
561
+ targetPath: edge.toPath,
562
+ targetSymbolId: edge.toSymbolId,
563
+ confidence: edge.confidence,
564
+ reason: edge.edgeKind
565
+ });
566
+ if (edge.edgeKind === "UI_CALLS_ENDPOINT" && edge.fromPath) {
567
+ steps.push(...uiFlowExpansionSteps(index, edge.fromPath));
568
+ }
569
+ }
570
+ const storePaths = new Set(routeEdges.filter((edge) => edge.edgeKind === "ROUTE_CALLS_STORE").flatMap((edge) => [edge.toPath].filter((value) => Boolean(value))));
571
+ for (const edge of index.graphEdges.filter((candidate) => storePaths.has(candidate.fromPath ?? "") && candidate.edgeKind === "STORE_DISPATCHES_ADAPTER")) {
572
+ steps.push({
573
+ kind: "adapter",
574
+ label: edge.reason,
575
+ path: edge.fromPath ?? entry.path,
576
+ line: edge.range?.startLine,
577
+ targetPath: edge.toPath,
578
+ targetSymbolId: edge.toSymbolId,
579
+ confidence: edge.confidence,
580
+ reason: edge.edgeKind
581
+ });
582
+ }
583
+ return steps.sort((a, b) => a.kind.localeCompare(b.kind) || a.path.localeCompare(b.path) || (a.line ?? 0) - (b.line ?? 0));
584
+ }
585
+ function uiFlowExpansionSteps(index, uiPath) {
586
+ const interesting = /use[-_]?run[-_]?polling|useRunPolling|queue-dashboard|useQueueDashboard|polling|run-view|QueueDashboard/i;
587
+ return index.graphEdges
588
+ .filter((edge) => edge.fromPath === uiPath && ["CALLS", "REFERENCES", "IMPORTS"].includes(edge.edgeKind))
589
+ .filter((edge) => interesting.test(edge.toPath ?? "") || interesting.test(edge.reason))
590
+ .slice(0, 12)
591
+ .map((edge) => ({
592
+ kind: "ui",
593
+ label: edge.reason,
594
+ path: edge.fromPath ?? uiPath,
595
+ line: edge.range?.startLine,
596
+ targetPath: edge.toPath,
597
+ targetSymbolId: edge.toSymbolId,
598
+ confidence: edge.confidence,
599
+ reason: `ui dependency ${edge.edgeKind}`
600
+ }));
601
+ }
602
+ function workflowFact(index, input) {
603
+ return {
604
+ id: stableId("workflow", input.workflowKind, input.entryPath, input.entrySymbolId, input.title),
605
+ type: "WorkflowTrace",
606
+ source: "heuristic",
607
+ confidence: workflowConfidence(input.steps),
608
+ snapshotId: index.snapshot.snapshotId,
609
+ indexedAt: index.snapshot.indexedAt,
610
+ path: input.entryPath,
611
+ workflowKind: input.workflowKind,
612
+ title: input.title,
613
+ entryPath: input.entryPath,
614
+ entrySymbolId: input.entrySymbolId,
615
+ relatedFiles: input.relatedFiles,
616
+ tests: input.tests,
617
+ steps: input.steps,
618
+ summary: input.summary,
619
+ rank: input.rank
620
+ };
621
+ }
622
+ function workflowConfidence(steps) {
623
+ if (steps.some((step) => step.confidence === "authoritative")) {
624
+ return steps.some((step) => step.confidence === "heuristic") ? "derived" : "authoritative";
625
+ }
626
+ if (steps.some((step) => step.confidence === "derived")) {
627
+ return "derived";
628
+ }
629
+ return "heuristic";
630
+ }
631
+ function workflowRank(files, tests, fileRank, kind) {
632
+ const base = kind === "route" ? 8 : kind === "job" ? 7 : kind === "manifest" ? 6 : 4;
633
+ return base + files.reduce((sum, file) => sum + Math.log2((fileRank.get(file) ?? 0) + 1), 0) + tests.length * 2;
634
+ }
635
+ function summarizeWorkflow(kind, title, relatedFiles, tests) {
636
+ const fileText = relatedFiles.slice(0, 5).join(", ") || "no related files";
637
+ const testText = tests.slice(0, 3).join(", ") || "no direct tests";
638
+ return `${kind} workflow ${title} touches ${relatedFiles.length} file(s): ${fileText}. Tests: ${testText}.`;
639
+ }
640
+ function relatedTests(index, relatedFiles) {
641
+ const related = new Set(relatedFiles);
642
+ return uniqueSorted(index.testEdges
643
+ .filter((edge) => edge.targetPath && related.has(edge.targetPath))
644
+ .map((edge) => edge.path)
645
+ .filter((pathValue) => pathValue));
646
+ }
647
+ function isJobSymbol(symbol) {
648
+ return symbol.decorators.some((decorator) => /(task|job|worker|celery|rq)/i.test(decorator));
649
+ }
650
+ function fileTarget(file) {
651
+ return { id: file.id, kind: "file", path: file.path, label: file.path };
652
+ }
653
+ function symbolTarget(symbol) {
654
+ return { id: symbol.id, kind: "symbol", path: symbol.path, symbolId: symbol.id, label: symbol.qualifiedName };
655
+ }
656
+ function riskTarget(risk) {
657
+ return { id: risk.id, kind: "risk", path: risk.path, label: risk.signal };
658
+ }
659
+ function rangesOverlap(a, b) {
660
+ if (!a || !b) {
661
+ return true;
662
+ }
663
+ return a.startLine <= b.endLine && b.startLine <= a.endLine;
664
+ }
665
+ //# sourceMappingURL=graph.js.map