@codragraph/cli 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (909) hide show
  1. package/README.md +341 -0
  2. package/dist/_shared/graph/types.d.ts +81 -0
  3. package/dist/_shared/graph/types.d.ts.map +1 -0
  4. package/dist/_shared/graph/types.js +8 -0
  5. package/dist/_shared/graph/types.js.map +1 -0
  6. package/dist/_shared/index.d.ts +55 -0
  7. package/dist/_shared/index.d.ts.map +1 -0
  8. package/dist/_shared/index.js +39 -0
  9. package/dist/_shared/index.js.map +1 -0
  10. package/dist/_shared/language-detection.d.ts +23 -0
  11. package/dist/_shared/language-detection.d.ts.map +1 -0
  12. package/dist/_shared/language-detection.js +139 -0
  13. package/dist/_shared/language-detection.js.map +1 -0
  14. package/dist/_shared/languages.d.ts +26 -0
  15. package/dist/_shared/languages.d.ts.map +1 -0
  16. package/dist/_shared/languages.js +27 -0
  17. package/dist/_shared/languages.js.map +1 -0
  18. package/dist/_shared/lbug/schema-constants.d.ts +16 -0
  19. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -0
  20. package/dist/_shared/lbug/schema-constants.js +67 -0
  21. package/dist/_shared/lbug/schema-constants.js.map +1 -0
  22. package/dist/_shared/mro-strategy.d.ts +41 -0
  23. package/dist/_shared/mro-strategy.d.ts.map +1 -0
  24. package/dist/_shared/mro-strategy.js +2 -0
  25. package/dist/_shared/mro-strategy.js.map +1 -0
  26. package/dist/_shared/pipeline.d.ts +16 -0
  27. package/dist/_shared/pipeline.d.ts.map +1 -0
  28. package/dist/_shared/pipeline.js +5 -0
  29. package/dist/_shared/pipeline.js.map +1 -0
  30. package/dist/_shared/scope-resolution/def-index.d.ts +36 -0
  31. package/dist/_shared/scope-resolution/def-index.d.ts.map +1 -0
  32. package/dist/_shared/scope-resolution/def-index.js +51 -0
  33. package/dist/_shared/scope-resolution/def-index.js.map +1 -0
  34. package/dist/_shared/scope-resolution/evidence-weights.d.ts +69 -0
  35. package/dist/_shared/scope-resolution/evidence-weights.d.ts.map +1 -0
  36. package/dist/_shared/scope-resolution/evidence-weights.js +84 -0
  37. package/dist/_shared/scope-resolution/evidence-weights.js.map +1 -0
  38. package/dist/_shared/scope-resolution/finalize-algorithm.d.ts +139 -0
  39. package/dist/_shared/scope-resolution/finalize-algorithm.d.ts.map +1 -0
  40. package/dist/_shared/scope-resolution/finalize-algorithm.js +479 -0
  41. package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -0
  42. package/dist/_shared/scope-resolution/language-classification.d.ts +26 -0
  43. package/dist/_shared/scope-resolution/language-classification.d.ts.map +1 -0
  44. package/dist/_shared/scope-resolution/language-classification.js +44 -0
  45. package/dist/_shared/scope-resolution/language-classification.js.map +1 -0
  46. package/dist/_shared/scope-resolution/method-dispatch-index.d.ts +80 -0
  47. package/dist/_shared/scope-resolution/method-dispatch-index.d.ts.map +1 -0
  48. package/dist/_shared/scope-resolution/method-dispatch-index.js +79 -0
  49. package/dist/_shared/scope-resolution/method-dispatch-index.js.map +1 -0
  50. package/dist/_shared/scope-resolution/module-scope-index.d.ts +46 -0
  51. package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -0
  52. package/dist/_shared/scope-resolution/module-scope-index.js +58 -0
  53. package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -0
  54. package/dist/_shared/scope-resolution/origin-priority.d.ts +14 -0
  55. package/dist/_shared/scope-resolution/origin-priority.d.ts.map +1 -0
  56. package/dist/_shared/scope-resolution/origin-priority.js +21 -0
  57. package/dist/_shared/scope-resolution/origin-priority.js.map +1 -0
  58. package/dist/_shared/scope-resolution/parsed-file.d.ts +76 -0
  59. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -0
  60. package/dist/_shared/scope-resolution/parsed-file.js +54 -0
  61. package/dist/_shared/scope-resolution/parsed-file.js.map +1 -0
  62. package/dist/_shared/scope-resolution/position-index.d.ts +62 -0
  63. package/dist/_shared/scope-resolution/position-index.d.ts.map +1 -0
  64. package/dist/_shared/scope-resolution/position-index.js +134 -0
  65. package/dist/_shared/scope-resolution/position-index.js.map +1 -0
  66. package/dist/_shared/scope-resolution/qualified-name-index.d.ts +44 -0
  67. package/dist/_shared/scope-resolution/qualified-name-index.d.ts.map +1 -0
  68. package/dist/_shared/scope-resolution/qualified-name-index.js +75 -0
  69. package/dist/_shared/scope-resolution/qualified-name-index.js.map +1 -0
  70. package/dist/_shared/scope-resolution/reference-site.d.ts +75 -0
  71. package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -0
  72. package/dist/_shared/scope-resolution/reference-site.js +24 -0
  73. package/dist/_shared/scope-resolution/reference-site.js.map +1 -0
  74. package/dist/_shared/scope-resolution/registries/class-registry.d.ts +27 -0
  75. package/dist/_shared/scope-resolution/registries/class-registry.d.ts.map +1 -0
  76. package/dist/_shared/scope-resolution/registries/class-registry.js +30 -0
  77. package/dist/_shared/scope-resolution/registries/class-registry.js.map +1 -0
  78. package/dist/_shared/scope-resolution/registries/context.d.ts +69 -0
  79. package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -0
  80. package/dist/_shared/scope-resolution/registries/context.js +44 -0
  81. package/dist/_shared/scope-resolution/registries/context.js.map +1 -0
  82. package/dist/_shared/scope-resolution/registries/evidence.d.ts +56 -0
  83. package/dist/_shared/scope-resolution/registries/evidence.d.ts.map +1 -0
  84. package/dist/_shared/scope-resolution/registries/evidence.js +150 -0
  85. package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -0
  86. package/dist/_shared/scope-resolution/registries/field-registry.d.ts +26 -0
  87. package/dist/_shared/scope-resolution/registries/field-registry.d.ts.map +1 -0
  88. package/dist/_shared/scope-resolution/registries/field-registry.js +31 -0
  89. package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -0
  90. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts +81 -0
  91. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -0
  92. package/dist/_shared/scope-resolution/registries/lookup-core.js +332 -0
  93. package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -0
  94. package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts +33 -0
  95. package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts.map +1 -0
  96. package/dist/_shared/scope-resolution/registries/lookup-qualified.js +56 -0
  97. package/dist/_shared/scope-resolution/registries/lookup-qualified.js.map +1 -0
  98. package/dist/_shared/scope-resolution/registries/method-registry.d.ts +36 -0
  99. package/dist/_shared/scope-resolution/registries/method-registry.d.ts.map +1 -0
  100. package/dist/_shared/scope-resolution/registries/method-registry.js +32 -0
  101. package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -0
  102. package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts +43 -0
  103. package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts.map +1 -0
  104. package/dist/_shared/scope-resolution/registries/tie-breaks.js +60 -0
  105. package/dist/_shared/scope-resolution/registries/tie-breaks.js.map +1 -0
  106. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts +53 -0
  107. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -0
  108. package/dist/_shared/scope-resolution/resolve-type-ref.js +126 -0
  109. package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -0
  110. package/dist/_shared/scope-resolution/scope-id.d.ts +43 -0
  111. package/dist/_shared/scope-resolution/scope-id.d.ts.map +1 -0
  112. package/dist/_shared/scope-resolution/scope-id.js +46 -0
  113. package/dist/_shared/scope-resolution/scope-id.js.map +1 -0
  114. package/dist/_shared/scope-resolution/scope-tree.d.ts +61 -0
  115. package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -0
  116. package/dist/_shared/scope-resolution/scope-tree.js +186 -0
  117. package/dist/_shared/scope-resolution/scope-tree.js.map +1 -0
  118. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +63 -0
  119. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +1 -0
  120. package/dist/_shared/scope-resolution/shadow/aggregate.js +122 -0
  121. package/dist/_shared/scope-resolution/shadow/aggregate.js.map +1 -0
  122. package/dist/_shared/scope-resolution/shadow/diff.d.ts +59 -0
  123. package/dist/_shared/scope-resolution/shadow/diff.d.ts.map +1 -0
  124. package/dist/_shared/scope-resolution/shadow/diff.js +79 -0
  125. package/dist/_shared/scope-resolution/shadow/diff.js.map +1 -0
  126. package/dist/_shared/scope-resolution/symbol-definition.d.ts +34 -0
  127. package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -0
  128. package/dist/_shared/scope-resolution/symbol-definition.js +12 -0
  129. package/dist/_shared/scope-resolution/symbol-definition.js.map +1 -0
  130. package/dist/_shared/scope-resolution/types.d.ts +356 -0
  131. package/dist/_shared/scope-resolution/types.d.ts.map +1 -0
  132. package/dist/_shared/scope-resolution/types.js +17 -0
  133. package/dist/_shared/scope-resolution/types.js.map +1 -0
  134. package/dist/cli/ai-context.d.ts +27 -0
  135. package/dist/cli/ai-context.js +270 -0
  136. package/dist/cli/analyze.d.ts +43 -0
  137. package/dist/cli/analyze.js +312 -0
  138. package/dist/cli/augment.d.ts +13 -0
  139. package/dist/cli/augment.js +33 -0
  140. package/dist/cli/clean.d.ts +10 -0
  141. package/dist/cli/clean.js +78 -0
  142. package/dist/cli/config.d.ts +27 -0
  143. package/dist/cli/config.js +106 -0
  144. package/dist/cli/eval-server.d.ts +37 -0
  145. package/dist/cli/eval-server.js +398 -0
  146. package/dist/cli/graphstore.d.ts +40 -0
  147. package/dist/cli/graphstore.js +639 -0
  148. package/dist/cli/group.d.ts +2 -0
  149. package/dist/cli/group.js +306 -0
  150. package/dist/cli/index-repo.d.ts +15 -0
  151. package/dist/cli/index-repo.js +120 -0
  152. package/dist/cli/index.d.ts +2 -0
  153. package/dist/cli/index.js +236 -0
  154. package/dist/cli/lazy-action.d.ts +6 -0
  155. package/dist/cli/lazy-action.js +18 -0
  156. package/dist/cli/list.d.ts +6 -0
  157. package/dist/cli/list.js +40 -0
  158. package/dist/cli/mcp.d.ts +8 -0
  159. package/dist/cli/mcp.js +36 -0
  160. package/dist/cli/remove.d.ts +30 -0
  161. package/dist/cli/remove.js +99 -0
  162. package/dist/cli/serve.d.ts +4 -0
  163. package/dist/cli/serve.js +37 -0
  164. package/dist/cli/setup.d.ts +8 -0
  165. package/dist/cli/setup.js +543 -0
  166. package/dist/cli/skill-gen.d.ts +26 -0
  167. package/dist/cli/skill-gen.js +555 -0
  168. package/dist/cli/status.d.ts +6 -0
  169. package/dist/cli/status.js +36 -0
  170. package/dist/cli/tool.d.ts +43 -0
  171. package/dist/cli/tool.js +168 -0
  172. package/dist/cli/wiki.d.ts +21 -0
  173. package/dist/cli/wiki.js +579 -0
  174. package/dist/config/ignore-service.d.ts +35 -0
  175. package/dist/config/ignore-service.js +436 -0
  176. package/dist/config/supported-languages.d.ts +13 -0
  177. package/dist/config/supported-languages.js +13 -0
  178. package/dist/core/augmentation/engine.d.ts +26 -0
  179. package/dist/core/augmentation/engine.js +252 -0
  180. package/dist/core/embeddings/ast-utils.d.ts +22 -0
  181. package/dist/core/embeddings/ast-utils.js +105 -0
  182. package/dist/core/embeddings/character-chunk.d.ts +12 -0
  183. package/dist/core/embeddings/character-chunk.js +43 -0
  184. package/dist/core/embeddings/chunker.d.ts +14 -0
  185. package/dist/core/embeddings/chunker.js +239 -0
  186. package/dist/core/embeddings/embedder.d.ts +65 -0
  187. package/dist/core/embeddings/embedder.js +320 -0
  188. package/dist/core/embeddings/embedding-pipeline.d.ts +62 -0
  189. package/dist/core/embeddings/embedding-pipeline.js +486 -0
  190. package/dist/core/embeddings/http-client.d.ts +31 -0
  191. package/dist/core/embeddings/http-client.js +179 -0
  192. package/dist/core/embeddings/index.d.ts +10 -0
  193. package/dist/core/embeddings/index.js +10 -0
  194. package/dist/core/embeddings/line-index.d.ts +7 -0
  195. package/dist/core/embeddings/line-index.js +42 -0
  196. package/dist/core/embeddings/server-mapping.d.ts +15 -0
  197. package/dist/core/embeddings/server-mapping.js +33 -0
  198. package/dist/core/embeddings/structural-extractor.d.ts +15 -0
  199. package/dist/core/embeddings/structural-extractor.js +58 -0
  200. package/dist/core/embeddings/text-generator.d.ts +31 -0
  201. package/dist/core/embeddings/text-generator.js +208 -0
  202. package/dist/core/embeddings/types.d.ts +207 -0
  203. package/dist/core/embeddings/types.js +200 -0
  204. package/dist/core/git-staleness.d.ts +31 -0
  205. package/dist/core/git-staleness.js +137 -0
  206. package/dist/core/graph/graph.d.ts +2 -0
  207. package/dist/core/graph/graph.js +173 -0
  208. package/dist/core/graph/types.d.ts +36 -0
  209. package/dist/core/graph/types.js +1 -0
  210. package/dist/core/graphstore/index.d.ts +46 -0
  211. package/dist/core/graphstore/index.js +80 -0
  212. package/dist/core/graphstore/lbug-row-source.d.ts +19 -0
  213. package/dist/core/graphstore/lbug-row-source.js +141 -0
  214. package/dist/core/group/bridge-db.d.ts +82 -0
  215. package/dist/core/group/bridge-db.js +460 -0
  216. package/dist/core/group/bridge-schema.d.ts +27 -0
  217. package/dist/core/group/bridge-schema.js +55 -0
  218. package/dist/core/group/config-parser.d.ts +7 -0
  219. package/dist/core/group/config-parser.js +100 -0
  220. package/dist/core/group/contract-extractor.d.ts +7 -0
  221. package/dist/core/group/contract-extractor.js +1 -0
  222. package/dist/core/group/cross-impact.d.ts +41 -0
  223. package/dist/core/group/cross-impact.js +441 -0
  224. package/dist/core/group/extractors/fs-utils.d.ts +10 -0
  225. package/dist/core/group/extractors/fs-utils.js +24 -0
  226. package/dist/core/group/extractors/grpc-extractor.d.ts +25 -0
  227. package/dist/core/group/extractors/grpc-extractor.js +401 -0
  228. package/dist/core/group/extractors/grpc-patterns/go.d.ts +2 -0
  229. package/dist/core/group/extractors/grpc-patterns/go.js +97 -0
  230. package/dist/core/group/extractors/grpc-patterns/index.d.ts +19 -0
  231. package/dist/core/group/extractors/grpc-patterns/index.js +46 -0
  232. package/dist/core/group/extractors/grpc-patterns/java.d.ts +2 -0
  233. package/dist/core/group/extractors/grpc-patterns/java.js +173 -0
  234. package/dist/core/group/extractors/grpc-patterns/node.d.ts +4 -0
  235. package/dist/core/group/extractors/grpc-patterns/node.js +290 -0
  236. package/dist/core/group/extractors/grpc-patterns/proto.d.ts +9 -0
  237. package/dist/core/group/extractors/grpc-patterns/proto.js +134 -0
  238. package/dist/core/group/extractors/grpc-patterns/python.d.ts +2 -0
  239. package/dist/core/group/extractors/grpc-patterns/python.js +67 -0
  240. package/dist/core/group/extractors/grpc-patterns/types.d.ts +50 -0
  241. package/dist/core/group/extractors/grpc-patterns/types.js +1 -0
  242. package/dist/core/group/extractors/http-patterns/go.d.ts +2 -0
  243. package/dist/core/group/extractors/http-patterns/go.js +215 -0
  244. package/dist/core/group/extractors/http-patterns/index.d.ts +17 -0
  245. package/dist/core/group/extractors/http-patterns/index.js +44 -0
  246. package/dist/core/group/extractors/http-patterns/java.d.ts +2 -0
  247. package/dist/core/group/extractors/http-patterns/java.js +253 -0
  248. package/dist/core/group/extractors/http-patterns/node.d.ts +4 -0
  249. package/dist/core/group/extractors/http-patterns/node.js +484 -0
  250. package/dist/core/group/extractors/http-patterns/php.d.ts +2 -0
  251. package/dist/core/group/extractors/http-patterns/php.js +178 -0
  252. package/dist/core/group/extractors/http-patterns/python.d.ts +2 -0
  253. package/dist/core/group/extractors/http-patterns/python.js +133 -0
  254. package/dist/core/group/extractors/http-patterns/types.d.ts +61 -0
  255. package/dist/core/group/extractors/http-patterns/types.js +1 -0
  256. package/dist/core/group/extractors/http-route-extractor.d.ts +21 -0
  257. package/dist/core/group/extractors/http-route-extractor.js +421 -0
  258. package/dist/core/group/extractors/manifest-extractor.d.ts +54 -0
  259. package/dist/core/group/extractors/manifest-extractor.js +292 -0
  260. package/dist/core/group/extractors/topic-extractor.d.ts +8 -0
  261. package/dist/core/group/extractors/topic-extractor.js +97 -0
  262. package/dist/core/group/extractors/topic-patterns/go.d.ts +2 -0
  263. package/dist/core/group/extractors/topic-patterns/go.js +120 -0
  264. package/dist/core/group/extractors/topic-patterns/index.d.ts +14 -0
  265. package/dist/core/group/extractors/topic-patterns/index.js +38 -0
  266. package/dist/core/group/extractors/topic-patterns/java.d.ts +2 -0
  267. package/dist/core/group/extractors/topic-patterns/java.js +80 -0
  268. package/dist/core/group/extractors/topic-patterns/node.d.ts +4 -0
  269. package/dist/core/group/extractors/topic-patterns/node.js +155 -0
  270. package/dist/core/group/extractors/topic-patterns/python.d.ts +2 -0
  271. package/dist/core/group/extractors/topic-patterns/python.js +116 -0
  272. package/dist/core/group/extractors/topic-patterns/types.d.ts +25 -0
  273. package/dist/core/group/extractors/topic-patterns/types.js +10 -0
  274. package/dist/core/group/extractors/tree-sitter-scanner.d.ts +113 -0
  275. package/dist/core/group/extractors/tree-sitter-scanner.js +94 -0
  276. package/dist/core/group/group-path-utils.d.ts +17 -0
  277. package/dist/core/group/group-path-utils.js +40 -0
  278. package/dist/core/group/matching.d.ts +13 -0
  279. package/dist/core/group/matching.js +198 -0
  280. package/dist/core/group/normalization.d.ts +3 -0
  281. package/dist/core/group/normalization.js +115 -0
  282. package/dist/core/group/resolve-at-member.d.ts +10 -0
  283. package/dist/core/group/resolve-at-member.js +31 -0
  284. package/dist/core/group/service-boundary-detector.d.ts +8 -0
  285. package/dist/core/group/service-boundary-detector.js +155 -0
  286. package/dist/core/group/service.d.ts +55 -0
  287. package/dist/core/group/service.js +394 -0
  288. package/dist/core/group/storage.d.ts +9 -0
  289. package/dist/core/group/storage.js +91 -0
  290. package/dist/core/group/sync.d.ts +21 -0
  291. package/dist/core/group/sync.js +196 -0
  292. package/dist/core/group/types.d.ts +160 -0
  293. package/dist/core/group/types.js +1 -0
  294. package/dist/core/ingestion/ast-cache.d.ts +26 -0
  295. package/dist/core/ingestion/ast-cache.js +47 -0
  296. package/dist/core/ingestion/binding-accumulator.d.ts +212 -0
  297. package/dist/core/ingestion/binding-accumulator.js +336 -0
  298. package/dist/core/ingestion/call-extractors/configs/c-cpp.d.ts +3 -0
  299. package/dist/core/ingestion/call-extractors/configs/c-cpp.js +8 -0
  300. package/dist/core/ingestion/call-extractors/configs/csharp.d.ts +2 -0
  301. package/dist/core/ingestion/call-extractors/configs/csharp.js +6 -0
  302. package/dist/core/ingestion/call-extractors/configs/dart.d.ts +2 -0
  303. package/dist/core/ingestion/call-extractors/configs/dart.js +5 -0
  304. package/dist/core/ingestion/call-extractors/configs/go.d.ts +2 -0
  305. package/dist/core/ingestion/call-extractors/configs/go.js +5 -0
  306. package/dist/core/ingestion/call-extractors/configs/jvm.d.ts +3 -0
  307. package/dist/core/ingestion/call-extractors/configs/jvm.js +51 -0
  308. package/dist/core/ingestion/call-extractors/configs/php.d.ts +2 -0
  309. package/dist/core/ingestion/call-extractors/configs/php.js +5 -0
  310. package/dist/core/ingestion/call-extractors/configs/python.d.ts +2 -0
  311. package/dist/core/ingestion/call-extractors/configs/python.js +5 -0
  312. package/dist/core/ingestion/call-extractors/configs/ruby.d.ts +2 -0
  313. package/dist/core/ingestion/call-extractors/configs/ruby.js +5 -0
  314. package/dist/core/ingestion/call-extractors/configs/rust.d.ts +2 -0
  315. package/dist/core/ingestion/call-extractors/configs/rust.js +5 -0
  316. package/dist/core/ingestion/call-extractors/configs/swift.d.ts +2 -0
  317. package/dist/core/ingestion/call-extractors/configs/swift.js +5 -0
  318. package/dist/core/ingestion/call-extractors/configs/typescript-javascript.d.ts +3 -0
  319. package/dist/core/ingestion/call-extractors/configs/typescript-javascript.js +8 -0
  320. package/dist/core/ingestion/call-extractors/generic.d.ts +5 -0
  321. package/dist/core/ingestion/call-extractors/generic.js +59 -0
  322. package/dist/core/ingestion/call-processor.d.ts +235 -0
  323. package/dist/core/ingestion/call-processor.js +2639 -0
  324. package/dist/core/ingestion/call-routing.d.ts +55 -0
  325. package/dist/core/ingestion/call-routing.js +95 -0
  326. package/dist/core/ingestion/call-types.d.ts +135 -0
  327. package/dist/core/ingestion/call-types.js +2 -0
  328. package/dist/core/ingestion/class-extractors/configs/c-cpp.d.ts +3 -0
  329. package/dist/core/ingestion/class-extractors/configs/c-cpp.js +11 -0
  330. package/dist/core/ingestion/class-extractors/configs/csharp.d.ts +2 -0
  331. package/dist/core/ingestion/class-extractors/configs/csharp.js +21 -0
  332. package/dist/core/ingestion/class-extractors/configs/dart.d.ts +2 -0
  333. package/dist/core/ingestion/class-extractors/configs/dart.js +7 -0
  334. package/dist/core/ingestion/class-extractors/configs/go.d.ts +2 -0
  335. package/dist/core/ingestion/class-extractors/configs/go.js +20 -0
  336. package/dist/core/ingestion/class-extractors/configs/jvm.d.ts +3 -0
  337. package/dist/core/ingestion/class-extractors/configs/jvm.js +35 -0
  338. package/dist/core/ingestion/class-extractors/configs/php.d.ts +2 -0
  339. package/dist/core/ingestion/class-extractors/configs/php.js +7 -0
  340. package/dist/core/ingestion/class-extractors/configs/python.d.ts +2 -0
  341. package/dist/core/ingestion/class-extractors/configs/python.js +7 -0
  342. package/dist/core/ingestion/class-extractors/configs/ruby.d.ts +2 -0
  343. package/dist/core/ingestion/class-extractors/configs/ruby.js +7 -0
  344. package/dist/core/ingestion/class-extractors/configs/rust.d.ts +2 -0
  345. package/dist/core/ingestion/class-extractors/configs/rust.js +7 -0
  346. package/dist/core/ingestion/class-extractors/configs/swift.d.ts +2 -0
  347. package/dist/core/ingestion/class-extractors/configs/swift.js +18 -0
  348. package/dist/core/ingestion/class-extractors/configs/typescript-javascript.d.ts +4 -0
  349. package/dist/core/ingestion/class-extractors/configs/typescript-javascript.js +28 -0
  350. package/dist/core/ingestion/class-extractors/generic.d.ts +2 -0
  351. package/dist/core/ingestion/class-extractors/generic.js +135 -0
  352. package/dist/core/ingestion/class-types.d.ts +34 -0
  353. package/dist/core/ingestion/class-types.js +1 -0
  354. package/dist/core/ingestion/cluster-enricher.d.ts +38 -0
  355. package/dist/core/ingestion/cluster-enricher.js +168 -0
  356. package/dist/core/ingestion/cobol/cobol-copy-expander.d.ts +57 -0
  357. package/dist/core/ingestion/cobol/cobol-copy-expander.js +392 -0
  358. package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +210 -0
  359. package/dist/core/ingestion/cobol/cobol-preprocessor.js +1715 -0
  360. package/dist/core/ingestion/cobol/jcl-parser.d.ts +68 -0
  361. package/dist/core/ingestion/cobol/jcl-parser.js +217 -0
  362. package/dist/core/ingestion/cobol/jcl-processor.d.ts +33 -0
  363. package/dist/core/ingestion/cobol/jcl-processor.js +229 -0
  364. package/dist/core/ingestion/cobol-processor.d.ts +54 -0
  365. package/dist/core/ingestion/cobol-processor.js +1232 -0
  366. package/dist/core/ingestion/community-processor.d.ts +39 -0
  367. package/dist/core/ingestion/community-processor.js +318 -0
  368. package/dist/core/ingestion/constants.d.ts +16 -0
  369. package/dist/core/ingestion/constants.js +16 -0
  370. package/dist/core/ingestion/emit-references.d.ts +88 -0
  371. package/dist/core/ingestion/emit-references.js +229 -0
  372. package/dist/core/ingestion/entry-point-scoring.d.ts +58 -0
  373. package/dist/core/ingestion/entry-point-scoring.js +380 -0
  374. package/dist/core/ingestion/export-detection.d.ts +57 -0
  375. package/dist/core/ingestion/export-detection.js +233 -0
  376. package/dist/core/ingestion/field-extractor.d.ts +29 -0
  377. package/dist/core/ingestion/field-extractor.js +25 -0
  378. package/dist/core/ingestion/field-extractors/configs/c-cpp.d.ts +3 -0
  379. package/dist/core/ingestion/field-extractors/configs/c-cpp.js +104 -0
  380. package/dist/core/ingestion/field-extractors/configs/csharp.d.ts +8 -0
  381. package/dist/core/ingestion/field-extractors/configs/csharp.js +116 -0
  382. package/dist/core/ingestion/field-extractors/configs/dart.d.ts +8 -0
  383. package/dist/core/ingestion/field-extractors/configs/dart.js +78 -0
  384. package/dist/core/ingestion/field-extractors/configs/go.d.ts +11 -0
  385. package/dist/core/ingestion/field-extractors/configs/go.js +60 -0
  386. package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +53 -0
  387. package/dist/core/ingestion/field-extractors/configs/helpers.js +158 -0
  388. package/dist/core/ingestion/field-extractors/configs/jvm.d.ts +3 -0
  389. package/dist/core/ingestion/field-extractors/configs/jvm.js +118 -0
  390. package/dist/core/ingestion/field-extractors/configs/php.d.ts +8 -0
  391. package/dist/core/ingestion/field-extractors/configs/php.js +65 -0
  392. package/dist/core/ingestion/field-extractors/configs/python.d.ts +12 -0
  393. package/dist/core/ingestion/field-extractors/configs/python.js +91 -0
  394. package/dist/core/ingestion/field-extractors/configs/ruby.d.ts +16 -0
  395. package/dist/core/ingestion/field-extractors/configs/ruby.js +76 -0
  396. package/dist/core/ingestion/field-extractors/configs/rust.d.ts +9 -0
  397. package/dist/core/ingestion/field-extractors/configs/rust.js +52 -0
  398. package/dist/core/ingestion/field-extractors/configs/swift.d.ts +8 -0
  399. package/dist/core/ingestion/field-extractors/configs/swift.js +65 -0
  400. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.d.ts +3 -0
  401. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +56 -0
  402. package/dist/core/ingestion/field-extractors/generic.d.ts +49 -0
  403. package/dist/core/ingestion/field-extractors/generic.js +117 -0
  404. package/dist/core/ingestion/field-extractors/typescript.d.ts +77 -0
  405. package/dist/core/ingestion/field-extractors/typescript.js +291 -0
  406. package/dist/core/ingestion/field-types.d.ts +61 -0
  407. package/dist/core/ingestion/field-types.js +2 -0
  408. package/dist/core/ingestion/filesystem-walker.d.ts +28 -0
  409. package/dist/core/ingestion/filesystem-walker.js +91 -0
  410. package/dist/core/ingestion/finalize-orchestrator.d.ts +63 -0
  411. package/dist/core/ingestion/finalize-orchestrator.js +139 -0
  412. package/dist/core/ingestion/framework-detection.d.ts +150 -0
  413. package/dist/core/ingestion/framework-detection.js +786 -0
  414. package/dist/core/ingestion/heritage-extractors/configs/go.d.ts +13 -0
  415. package/dist/core/ingestion/heritage-extractors/configs/go.js +20 -0
  416. package/dist/core/ingestion/heritage-extractors/configs/ruby.d.ts +18 -0
  417. package/dist/core/ingestion/heritage-extractors/configs/ruby.js +65 -0
  418. package/dist/core/ingestion/heritage-extractors/generic.d.ts +23 -0
  419. package/dist/core/ingestion/heritage-extractors/generic.js +47 -0
  420. package/dist/core/ingestion/heritage-processor.d.ts +54 -0
  421. package/dist/core/ingestion/heritage-processor.js +360 -0
  422. package/dist/core/ingestion/heritage-types.d.ts +73 -0
  423. package/dist/core/ingestion/heritage-types.js +2 -0
  424. package/dist/core/ingestion/import-processor.d.ts +23 -0
  425. package/dist/core/ingestion/import-processor.js +373 -0
  426. package/dist/core/ingestion/import-resolvers/configs/c-cpp.d.ts +7 -0
  427. package/dist/core/ingestion/import-resolvers/configs/c-cpp.js +14 -0
  428. package/dist/core/ingestion/import-resolvers/configs/csharp.d.ts +8 -0
  429. package/dist/core/ingestion/import-resolvers/configs/csharp.js +27 -0
  430. package/dist/core/ingestion/import-resolvers/configs/dart.d.ts +17 -0
  431. package/dist/core/ingestion/import-resolvers/configs/dart.js +54 -0
  432. package/dist/core/ingestion/import-resolvers/configs/go.d.ts +8 -0
  433. package/dist/core/ingestion/import-resolvers/configs/go.js +26 -0
  434. package/dist/core/ingestion/import-resolvers/configs/jvm.d.ts +13 -0
  435. package/dist/core/ingestion/import-resolvers/configs/jvm.js +68 -0
  436. package/dist/core/ingestion/import-resolvers/configs/php.d.ts +8 -0
  437. package/dist/core/ingestion/import-resolvers/configs/php.js +15 -0
  438. package/dist/core/ingestion/import-resolvers/configs/python.d.ts +12 -0
  439. package/dist/core/ingestion/import-resolvers/configs/python.js +41 -0
  440. package/dist/core/ingestion/import-resolvers/configs/ruby.d.ts +8 -0
  441. package/dist/core/ingestion/import-resolvers/configs/ruby.js +16 -0
  442. package/dist/core/ingestion/import-resolvers/configs/rust.d.ts +8 -0
  443. package/dist/core/ingestion/import-resolvers/configs/rust.js +54 -0
  444. package/dist/core/ingestion/import-resolvers/configs/swift.d.ts +8 -0
  445. package/dist/core/ingestion/import-resolvers/configs/swift.js +29 -0
  446. package/dist/core/ingestion/import-resolvers/configs/typescript-javascript.d.ts +9 -0
  447. package/dist/core/ingestion/import-resolvers/configs/typescript-javascript.js +23 -0
  448. package/dist/core/ingestion/import-resolvers/csharp.d.ts +18 -0
  449. package/dist/core/ingestion/import-resolvers/csharp.js +115 -0
  450. package/dist/core/ingestion/import-resolvers/go.d.ts +17 -0
  451. package/dist/core/ingestion/import-resolvers/go.js +46 -0
  452. package/dist/core/ingestion/import-resolvers/jvm.d.ts +27 -0
  453. package/dist/core/ingestion/import-resolvers/jvm.js +106 -0
  454. package/dist/core/ingestion/import-resolvers/php.d.ts +24 -0
  455. package/dist/core/ingestion/import-resolvers/php.js +77 -0
  456. package/dist/core/ingestion/import-resolvers/python.d.ts +22 -0
  457. package/dist/core/ingestion/import-resolvers/python.js +72 -0
  458. package/dist/core/ingestion/import-resolvers/resolver-factory.d.ts +24 -0
  459. package/dist/core/ingestion/import-resolvers/resolver-factory.js +33 -0
  460. package/dist/core/ingestion/import-resolvers/ruby.d.ts +14 -0
  461. package/dist/core/ingestion/import-resolvers/ruby.js +17 -0
  462. package/dist/core/ingestion/import-resolvers/rust.d.ts +17 -0
  463. package/dist/core/ingestion/import-resolvers/rust.js +75 -0
  464. package/dist/core/ingestion/import-resolvers/standard.d.ts +30 -0
  465. package/dist/core/ingestion/import-resolvers/standard.js +142 -0
  466. package/dist/core/ingestion/import-resolvers/types.d.ts +68 -0
  467. package/dist/core/ingestion/import-resolvers/types.js +6 -0
  468. package/dist/core/ingestion/import-resolvers/utils.d.ts +35 -0
  469. package/dist/core/ingestion/import-resolvers/utils.js +149 -0
  470. package/dist/core/ingestion/import-target-adapter.d.ts +73 -0
  471. package/dist/core/ingestion/import-target-adapter.js +95 -0
  472. package/dist/core/ingestion/language-config.d.ts +52 -0
  473. package/dist/core/ingestion/language-config.js +181 -0
  474. package/dist/core/ingestion/language-provider.d.ts +410 -0
  475. package/dist/core/ingestion/language-provider.js +24 -0
  476. package/dist/core/ingestion/languages/c-cpp.d.ts +12 -0
  477. package/dist/core/ingestion/languages/c-cpp.js +329 -0
  478. package/dist/core/ingestion/languages/cobol.d.ts +1 -0
  479. package/dist/core/ingestion/languages/cobol.js +26 -0
  480. package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +21 -0
  481. package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +56 -0
  482. package/dist/core/ingestion/languages/csharp/arity-metadata.d.ts +26 -0
  483. package/dist/core/ingestion/languages/csharp/arity-metadata.js +46 -0
  484. package/dist/core/ingestion/languages/csharp/arity.d.ts +23 -0
  485. package/dist/core/ingestion/languages/csharp/arity.js +37 -0
  486. package/dist/core/ingestion/languages/csharp/cache-stats.d.ts +15 -0
  487. package/dist/core/ingestion/languages/csharp/cache-stats.js +26 -0
  488. package/dist/core/ingestion/languages/csharp/captures.d.ts +19 -0
  489. package/dist/core/ingestion/languages/csharp/captures.js +249 -0
  490. package/dist/core/ingestion/languages/csharp/import-decomposer.d.ts +19 -0
  491. package/dist/core/ingestion/languages/csharp/import-decomposer.js +93 -0
  492. package/dist/core/ingestion/languages/csharp/import-target.d.ts +25 -0
  493. package/dist/core/ingestion/languages/csharp/import-target.js +123 -0
  494. package/dist/core/ingestion/languages/csharp/index.d.ts +82 -0
  495. package/dist/core/ingestion/languages/csharp/index.js +82 -0
  496. package/dist/core/ingestion/languages/csharp/interpret.d.ts +15 -0
  497. package/dist/core/ingestion/languages/csharp/interpret.js +132 -0
  498. package/dist/core/ingestion/languages/csharp/merge-bindings.d.ts +27 -0
  499. package/dist/core/ingestion/languages/csharp/merge-bindings.js +55 -0
  500. package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +50 -0
  501. package/dist/core/ingestion/languages/csharp/namespace-siblings.js +374 -0
  502. package/dist/core/ingestion/languages/csharp/query.d.ts +35 -0
  503. package/dist/core/ingestion/languages/csharp/query.js +515 -0
  504. package/dist/core/ingestion/languages/csharp/receiver-binding.d.ts +31 -0
  505. package/dist/core/ingestion/languages/csharp/receiver-binding.js +135 -0
  506. package/dist/core/ingestion/languages/csharp/scope-resolver.d.ts +10 -0
  507. package/dist/core/ingestion/languages/csharp/scope-resolver.js +63 -0
  508. package/dist/core/ingestion/languages/csharp/simple-hooks.d.ts +53 -0
  509. package/dist/core/ingestion/languages/csharp/simple-hooks.js +76 -0
  510. package/dist/core/ingestion/languages/csharp.d.ts +8 -0
  511. package/dist/core/ingestion/languages/csharp.js +152 -0
  512. package/dist/core/ingestion/languages/dart.d.ts +12 -0
  513. package/dist/core/ingestion/languages/dart.js +102 -0
  514. package/dist/core/ingestion/languages/go.d.ts +11 -0
  515. package/dist/core/ingestion/languages/go.js +44 -0
  516. package/dist/core/ingestion/languages/index.d.ts +39 -0
  517. package/dist/core/ingestion/languages/index.js +64 -0
  518. package/dist/core/ingestion/languages/java.d.ts +9 -0
  519. package/dist/core/ingestion/languages/java.js +44 -0
  520. package/dist/core/ingestion/languages/kotlin.d.ts +9 -0
  521. package/dist/core/ingestion/languages/kotlin.js +123 -0
  522. package/dist/core/ingestion/languages/php.d.ts +8 -0
  523. package/dist/core/ingestion/languages/php.js +240 -0
  524. package/dist/core/ingestion/languages/python/arity-metadata.d.ts +24 -0
  525. package/dist/core/ingestion/languages/python/arity-metadata.js +45 -0
  526. package/dist/core/ingestion/languages/python/arity.d.ts +22 -0
  527. package/dist/core/ingestion/languages/python/arity.js +38 -0
  528. package/dist/core/ingestion/languages/python/cache-stats.d.ts +17 -0
  529. package/dist/core/ingestion/languages/python/cache-stats.js +28 -0
  530. package/dist/core/ingestion/languages/python/captures.d.ts +19 -0
  531. package/dist/core/ingestion/languages/python/captures.js +106 -0
  532. package/dist/core/ingestion/languages/python/import-decomposer.d.ts +15 -0
  533. package/dist/core/ingestion/languages/python/import-decomposer.js +112 -0
  534. package/dist/core/ingestion/languages/python/import-target.d.ts +21 -0
  535. package/dist/core/ingestion/languages/python/import-target.js +99 -0
  536. package/dist/core/ingestion/languages/python/index.d.ts +80 -0
  537. package/dist/core/ingestion/languages/python/index.js +80 -0
  538. package/dist/core/ingestion/languages/python/interpret.d.ts +15 -0
  539. package/dist/core/ingestion/languages/python/interpret.js +191 -0
  540. package/dist/core/ingestion/languages/python/merge-bindings.d.ts +16 -0
  541. package/dist/core/ingestion/languages/python/merge-bindings.js +44 -0
  542. package/dist/core/ingestion/languages/python/query.d.ts +9 -0
  543. package/dist/core/ingestion/languages/python/query.js +267 -0
  544. package/dist/core/ingestion/languages/python/receiver-binding.d.ts +21 -0
  545. package/dist/core/ingestion/languages/python/receiver-binding.js +116 -0
  546. package/dist/core/ingestion/languages/python/scope-resolver.d.ts +16 -0
  547. package/dist/core/ingestion/languages/python/scope-resolver.js +53 -0
  548. package/dist/core/ingestion/languages/python/simple-hooks.d.ts +23 -0
  549. package/dist/core/ingestion/languages/python/simple-hooks.js +35 -0
  550. package/dist/core/ingestion/languages/python.d.ts +12 -0
  551. package/dist/core/ingestion/languages/python.js +91 -0
  552. package/dist/core/ingestion/languages/ruby.d.ts +9 -0
  553. package/dist/core/ingestion/languages/ruby.js +210 -0
  554. package/dist/core/ingestion/languages/rust.d.ts +12 -0
  555. package/dist/core/ingestion/languages/rust.js +132 -0
  556. package/dist/core/ingestion/languages/swift.d.ts +12 -0
  557. package/dist/core/ingestion/languages/swift.js +244 -0
  558. package/dist/core/ingestion/languages/typescript.d.ts +11 -0
  559. package/dist/core/ingestion/languages/typescript.js +184 -0
  560. package/dist/core/ingestion/languages/vue.d.ts +13 -0
  561. package/dist/core/ingestion/languages/vue.js +77 -0
  562. package/dist/core/ingestion/markdown-processor.d.ts +17 -0
  563. package/dist/core/ingestion/markdown-processor.js +124 -0
  564. package/dist/core/ingestion/method-extractors/configs/c-cpp.d.ts +3 -0
  565. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
  566. package/dist/core/ingestion/method-extractors/configs/csharp.d.ts +2 -0
  567. package/dist/core/ingestion/method-extractors/configs/csharp.js +287 -0
  568. package/dist/core/ingestion/method-extractors/configs/dart.d.ts +2 -0
  569. package/dist/core/ingestion/method-extractors/configs/dart.js +376 -0
  570. package/dist/core/ingestion/method-extractors/configs/go.d.ts +2 -0
  571. package/dist/core/ingestion/method-extractors/configs/go.js +176 -0
  572. package/dist/core/ingestion/method-extractors/configs/jvm.d.ts +3 -0
  573. package/dist/core/ingestion/method-extractors/configs/jvm.js +336 -0
  574. package/dist/core/ingestion/method-extractors/configs/php.d.ts +2 -0
  575. package/dist/core/ingestion/method-extractors/configs/php.js +304 -0
  576. package/dist/core/ingestion/method-extractors/configs/python.d.ts +2 -0
  577. package/dist/core/ingestion/method-extractors/configs/python.js +309 -0
  578. package/dist/core/ingestion/method-extractors/configs/ruby.d.ts +2 -0
  579. package/dist/core/ingestion/method-extractors/configs/ruby.js +286 -0
  580. package/dist/core/ingestion/method-extractors/configs/rust.d.ts +2 -0
  581. package/dist/core/ingestion/method-extractors/configs/rust.js +195 -0
  582. package/dist/core/ingestion/method-extractors/configs/swift.d.ts +2 -0
  583. package/dist/core/ingestion/method-extractors/configs/swift.js +277 -0
  584. package/dist/core/ingestion/method-extractors/configs/typescript-javascript.d.ts +3 -0
  585. package/dist/core/ingestion/method-extractors/configs/typescript-javascript.js +338 -0
  586. package/dist/core/ingestion/method-extractors/generic.d.ts +11 -0
  587. package/dist/core/ingestion/method-extractors/generic.js +204 -0
  588. package/dist/core/ingestion/method-types.d.ts +90 -0
  589. package/dist/core/ingestion/method-types.js +2 -0
  590. package/dist/core/ingestion/model/field-registry.d.ts +18 -0
  591. package/dist/core/ingestion/model/field-registry.js +22 -0
  592. package/dist/core/ingestion/model/heritage-map.d.ts +105 -0
  593. package/dist/core/ingestion/model/heritage-map.js +260 -0
  594. package/dist/core/ingestion/model/index.d.ts +20 -0
  595. package/dist/core/ingestion/model/index.js +43 -0
  596. package/dist/core/ingestion/model/method-registry.d.ts +71 -0
  597. package/dist/core/ingestion/model/method-registry.js +134 -0
  598. package/dist/core/ingestion/model/registration-table.d.ts +138 -0
  599. package/dist/core/ingestion/model/registration-table.js +224 -0
  600. package/dist/core/ingestion/model/resolution-context.d.ts +93 -0
  601. package/dist/core/ingestion/model/resolution-context.js +337 -0
  602. package/dist/core/ingestion/model/resolve.d.ts +61 -0
  603. package/dist/core/ingestion/model/resolve.js +381 -0
  604. package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +59 -0
  605. package/dist/core/ingestion/model/scope-resolution-indexes.js +42 -0
  606. package/dist/core/ingestion/model/semantic-model.d.ts +150 -0
  607. package/dist/core/ingestion/model/semantic-model.js +175 -0
  608. package/dist/core/ingestion/model/symbol-table.d.ts +200 -0
  609. package/dist/core/ingestion/model/symbol-table.js +206 -0
  610. package/dist/core/ingestion/model/type-registry.d.ts +39 -0
  611. package/dist/core/ingestion/model/type-registry.js +62 -0
  612. package/dist/core/ingestion/mro-processor.d.ts +46 -0
  613. package/dist/core/ingestion/mro-processor.js +597 -0
  614. package/dist/core/ingestion/named-bindings/csharp.d.ts +3 -0
  615. package/dist/core/ingestion/named-bindings/csharp.js +37 -0
  616. package/dist/core/ingestion/named-bindings/java.d.ts +3 -0
  617. package/dist/core/ingestion/named-bindings/java.js +29 -0
  618. package/dist/core/ingestion/named-bindings/kotlin.d.ts +3 -0
  619. package/dist/core/ingestion/named-bindings/kotlin.js +36 -0
  620. package/dist/core/ingestion/named-bindings/php.d.ts +3 -0
  621. package/dist/core/ingestion/named-bindings/php.js +61 -0
  622. package/dist/core/ingestion/named-bindings/python.d.ts +3 -0
  623. package/dist/core/ingestion/named-bindings/python.js +49 -0
  624. package/dist/core/ingestion/named-bindings/rust.d.ts +3 -0
  625. package/dist/core/ingestion/named-bindings/rust.js +66 -0
  626. package/dist/core/ingestion/named-bindings/types.d.ts +16 -0
  627. package/dist/core/ingestion/named-bindings/types.js +6 -0
  628. package/dist/core/ingestion/named-bindings/typescript.d.ts +3 -0
  629. package/dist/core/ingestion/named-bindings/typescript.js +58 -0
  630. package/dist/core/ingestion/parsing-processor.d.ts +40 -0
  631. package/dist/core/ingestion/parsing-processor.js +576 -0
  632. package/dist/core/ingestion/pipeline-phases/cobol.d.ts +16 -0
  633. package/dist/core/ingestion/pipeline-phases/cobol.js +45 -0
  634. package/dist/core/ingestion/pipeline-phases/communities.d.ts +16 -0
  635. package/dist/core/ingestion/pipeline-phases/communities.js +62 -0
  636. package/dist/core/ingestion/pipeline-phases/cross-file-impl.d.ts +17 -0
  637. package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +156 -0
  638. package/dist/core/ingestion/pipeline-phases/cross-file.d.ts +37 -0
  639. package/dist/core/ingestion/pipeline-phases/cross-file.js +63 -0
  640. package/dist/core/ingestion/pipeline-phases/index.d.ts +22 -0
  641. package/dist/core/ingestion/pipeline-phases/index.js +23 -0
  642. package/dist/core/ingestion/pipeline-phases/markdown.d.ts +17 -0
  643. package/dist/core/ingestion/pipeline-phases/markdown.js +33 -0
  644. package/dist/core/ingestion/pipeline-phases/mro.d.ts +18 -0
  645. package/dist/core/ingestion/pipeline-phases/mro.js +36 -0
  646. package/dist/core/ingestion/pipeline-phases/orm-extraction.d.ts +22 -0
  647. package/dist/core/ingestion/pipeline-phases/orm-extraction.js +92 -0
  648. package/dist/core/ingestion/pipeline-phases/orm.d.ts +15 -0
  649. package/dist/core/ingestion/pipeline-phases/orm.js +74 -0
  650. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +58 -0
  651. package/dist/core/ingestion/pipeline-phases/parse-impl.js +458 -0
  652. package/dist/core/ingestion/pipeline-phases/parse.d.ts +74 -0
  653. package/dist/core/ingestion/pipeline-phases/parse.js +33 -0
  654. package/dist/core/ingestion/pipeline-phases/processes.d.ts +16 -0
  655. package/dist/core/ingestion/pipeline-phases/processes.js +143 -0
  656. package/dist/core/ingestion/pipeline-phases/routes.d.ts +21 -0
  657. package/dist/core/ingestion/pipeline-phases/routes.js +243 -0
  658. package/dist/core/ingestion/pipeline-phases/runner.d.ts +22 -0
  659. package/dist/core/ingestion/pipeline-phases/runner.js +203 -0
  660. package/dist/core/ingestion/pipeline-phases/scan.d.ts +21 -0
  661. package/dist/core/ingestion/pipeline-phases/scan.js +46 -0
  662. package/dist/core/ingestion/pipeline-phases/structure.d.ts +27 -0
  663. package/dist/core/ingestion/pipeline-phases/structure.js +35 -0
  664. package/dist/core/ingestion/pipeline-phases/tools.d.ts +20 -0
  665. package/dist/core/ingestion/pipeline-phases/tools.js +79 -0
  666. package/dist/core/ingestion/pipeline-phases/types.d.ts +79 -0
  667. package/dist/core/ingestion/pipeline-phases/types.js +37 -0
  668. package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.d.ts +70 -0
  669. package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.js +312 -0
  670. package/dist/core/ingestion/pipeline.d.ts +36 -0
  671. package/dist/core/ingestion/pipeline.js +89 -0
  672. package/dist/core/ingestion/process-processor.d.ts +51 -0
  673. package/dist/core/ingestion/process-processor.js +317 -0
  674. package/dist/core/ingestion/registry-primary-flag.d.ts +86 -0
  675. package/dist/core/ingestion/registry-primary-flag.js +111 -0
  676. package/dist/core/ingestion/resolve-references.d.ts +63 -0
  677. package/dist/core/ingestion/resolve-references.js +175 -0
  678. package/dist/core/ingestion/route-extractors/expo.d.ts +1 -0
  679. package/dist/core/ingestion/route-extractors/expo.js +36 -0
  680. package/dist/core/ingestion/route-extractors/middleware.d.ts +47 -0
  681. package/dist/core/ingestion/route-extractors/middleware.js +167 -0
  682. package/dist/core/ingestion/route-extractors/nextjs.d.ts +3 -0
  683. package/dist/core/ingestion/route-extractors/nextjs.js +76 -0
  684. package/dist/core/ingestion/route-extractors/php.d.ts +7 -0
  685. package/dist/core/ingestion/route-extractors/php.js +22 -0
  686. package/dist/core/ingestion/route-extractors/response-shapes.d.ts +20 -0
  687. package/dist/core/ingestion/route-extractors/response-shapes.js +294 -0
  688. package/dist/core/ingestion/scope-extractor-bridge.d.ts +32 -0
  689. package/dist/core/ingestion/scope-extractor-bridge.js +44 -0
  690. package/dist/core/ingestion/scope-extractor.d.ts +86 -0
  691. package/dist/core/ingestion/scope-extractor.js +758 -0
  692. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +372 -0
  693. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +212 -0
  694. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +43 -0
  695. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +79 -0
  696. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +57 -0
  697. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +112 -0
  698. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +17 -0
  699. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +46 -0
  700. package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.d.ts +19 -0
  701. package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +30 -0
  702. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +37 -0
  703. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +113 -0
  704. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +38 -0
  705. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +73 -0
  706. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +42 -0
  707. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +198 -0
  708. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +27 -0
  709. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +131 -0
  710. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +48 -0
  711. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +130 -0
  712. package/dist/core/ingestion/scope-resolution/passes/mro.d.ts +42 -0
  713. package/dist/core/ingestion/scope-resolution/passes/mro.js +99 -0
  714. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +26 -0
  715. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +61 -0
  716. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +46 -0
  717. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +327 -0
  718. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +47 -0
  719. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +130 -0
  720. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +68 -0
  721. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +125 -0
  722. package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +17 -0
  723. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +21 -0
  724. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +66 -0
  725. package/dist/core/ingestion/scope-resolution/pipeline/run.js +157 -0
  726. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +36 -0
  727. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +52 -0
  728. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +127 -0
  729. package/dist/core/ingestion/scope-resolution/scope/walkers.js +349 -0
  730. package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +52 -0
  731. package/dist/core/ingestion/scope-resolution/workspace-index.js +61 -0
  732. package/dist/core/ingestion/shadow-harness.d.ts +113 -0
  733. package/dist/core/ingestion/shadow-harness.js +148 -0
  734. package/dist/core/ingestion/structure-processor.d.ts +2 -0
  735. package/dist/core/ingestion/structure-processor.js +36 -0
  736. package/dist/core/ingestion/tree-sitter-queries.d.ts +16 -0
  737. package/dist/core/ingestion/tree-sitter-queries.js +1338 -0
  738. package/dist/core/ingestion/type-env.d.ts +86 -0
  739. package/dist/core/ingestion/type-env.js +1128 -0
  740. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +7 -0
  741. package/dist/core/ingestion/type-extractors/c-cpp.js +532 -0
  742. package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
  743. package/dist/core/ingestion/type-extractors/csharp.js +583 -0
  744. package/dist/core/ingestion/type-extractors/dart.d.ts +15 -0
  745. package/dist/core/ingestion/type-extractors/dart.js +369 -0
  746. package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
  747. package/dist/core/ingestion/type-extractors/go.js +513 -0
  748. package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
  749. package/dist/core/ingestion/type-extractors/jvm.js +856 -0
  750. package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
  751. package/dist/core/ingestion/type-extractors/php.js +534 -0
  752. package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
  753. package/dist/core/ingestion/type-extractors/python.js +474 -0
  754. package/dist/core/ingestion/type-extractors/ruby.d.ts +2 -0
  755. package/dist/core/ingestion/type-extractors/ruby.js +377 -0
  756. package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
  757. package/dist/core/ingestion/type-extractors/rust.js +515 -0
  758. package/dist/core/ingestion/type-extractors/shared.d.ts +131 -0
  759. package/dist/core/ingestion/type-extractors/shared.js +796 -0
  760. package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
  761. package/dist/core/ingestion/type-extractors/swift.js +484 -0
  762. package/dist/core/ingestion/type-extractors/types.d.ts +172 -0
  763. package/dist/core/ingestion/type-extractors/types.js +1 -0
  764. package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
  765. package/dist/core/ingestion/type-extractors/typescript.js +661 -0
  766. package/dist/core/ingestion/utils/ast-helpers.d.ts +89 -0
  767. package/dist/core/ingestion/utils/ast-helpers.js +535 -0
  768. package/dist/core/ingestion/utils/call-analysis.d.ts +75 -0
  769. package/dist/core/ingestion/utils/call-analysis.js +574 -0
  770. package/dist/core/ingestion/utils/env.d.ts +10 -0
  771. package/dist/core/ingestion/utils/env.js +10 -0
  772. package/dist/core/ingestion/utils/event-loop.d.ts +5 -0
  773. package/dist/core/ingestion/utils/event-loop.js +5 -0
  774. package/dist/core/ingestion/utils/graph-sort.d.ts +58 -0
  775. package/dist/core/ingestion/utils/graph-sort.js +100 -0
  776. package/dist/core/ingestion/utils/max-file-size.d.ts +20 -0
  777. package/dist/core/ingestion/utils/max-file-size.js +52 -0
  778. package/dist/core/ingestion/utils/method-props.d.ts +32 -0
  779. package/dist/core/ingestion/utils/method-props.js +147 -0
  780. package/dist/core/ingestion/utils/ruby-self-call.d.ts +52 -0
  781. package/dist/core/ingestion/utils/ruby-self-call.js +59 -0
  782. package/dist/core/ingestion/utils/verbose.d.ts +1 -0
  783. package/dist/core/ingestion/utils/verbose.js +7 -0
  784. package/dist/core/ingestion/variable-extractors/configs/c-cpp.d.ts +3 -0
  785. package/dist/core/ingestion/variable-extractors/configs/c-cpp.js +81 -0
  786. package/dist/core/ingestion/variable-extractors/configs/csharp.d.ts +9 -0
  787. package/dist/core/ingestion/variable-extractors/configs/csharp.js +63 -0
  788. package/dist/core/ingestion/variable-extractors/configs/dart.d.ts +2 -0
  789. package/dist/core/ingestion/variable-extractors/configs/dart.js +94 -0
  790. package/dist/core/ingestion/variable-extractors/configs/go.d.ts +2 -0
  791. package/dist/core/ingestion/variable-extractors/configs/go.js +83 -0
  792. package/dist/core/ingestion/variable-extractors/configs/jvm.d.ts +18 -0
  793. package/dist/core/ingestion/variable-extractors/configs/jvm.js +115 -0
  794. package/dist/core/ingestion/variable-extractors/configs/php.d.ts +14 -0
  795. package/dist/core/ingestion/variable-extractors/configs/php.js +58 -0
  796. package/dist/core/ingestion/variable-extractors/configs/python.d.ts +2 -0
  797. package/dist/core/ingestion/variable-extractors/configs/python.js +101 -0
  798. package/dist/core/ingestion/variable-extractors/configs/ruby.d.ts +11 -0
  799. package/dist/core/ingestion/variable-extractors/configs/ruby.js +52 -0
  800. package/dist/core/ingestion/variable-extractors/configs/rust.d.ts +2 -0
  801. package/dist/core/ingestion/variable-extractors/configs/rust.js +76 -0
  802. package/dist/core/ingestion/variable-extractors/configs/swift.d.ts +2 -0
  803. package/dist/core/ingestion/variable-extractors/configs/swift.js +88 -0
  804. package/dist/core/ingestion/variable-extractors/configs/typescript-javascript.d.ts +3 -0
  805. package/dist/core/ingestion/variable-extractors/configs/typescript-javascript.js +83 -0
  806. package/dist/core/ingestion/variable-extractors/generic.d.ts +5 -0
  807. package/dist/core/ingestion/variable-extractors/generic.js +80 -0
  808. package/dist/core/ingestion/variable-types.d.ts +82 -0
  809. package/dist/core/ingestion/variable-types.js +2 -0
  810. package/dist/core/ingestion/vue-sfc-extractor.d.ts +44 -0
  811. package/dist/core/ingestion/vue-sfc-extractor.js +94 -0
  812. package/dist/core/ingestion/workers/parse-worker.d.ts +198 -0
  813. package/dist/core/ingestion/workers/parse-worker.js +1928 -0
  814. package/dist/core/ingestion/workers/worker-pool.d.ts +16 -0
  815. package/dist/core/ingestion/workers/worker-pool.js +126 -0
  816. package/dist/core/lbug/csv-generator.d.ts +33 -0
  817. package/dist/core/lbug/csv-generator.js +459 -0
  818. package/dist/core/lbug/lbug-adapter.d.ts +173 -0
  819. package/dist/core/lbug/lbug-adapter.js +1188 -0
  820. package/dist/core/lbug/pool-adapter.d.ts +93 -0
  821. package/dist/core/lbug/pool-adapter.js +543 -0
  822. package/dist/core/lbug/schema.d.ts +62 -0
  823. package/dist/core/lbug/schema.js +484 -0
  824. package/dist/core/run-analyze.d.ts +72 -0
  825. package/dist/core/run-analyze.js +315 -0
  826. package/dist/core/search/bm25-index.d.ts +41 -0
  827. package/dist/core/search/bm25-index.js +209 -0
  828. package/dist/core/search/hybrid-search.d.ts +49 -0
  829. package/dist/core/search/hybrid-search.js +118 -0
  830. package/dist/core/search/phase-timer.d.ts +72 -0
  831. package/dist/core/search/phase-timer.js +106 -0
  832. package/dist/core/tree-sitter/parser-loader.d.ts +8 -0
  833. package/dist/core/tree-sitter/parser-loader.js +84 -0
  834. package/dist/core/wiki/cursor-client.d.ts +31 -0
  835. package/dist/core/wiki/cursor-client.js +122 -0
  836. package/dist/core/wiki/generator.d.ts +129 -0
  837. package/dist/core/wiki/generator.js +898 -0
  838. package/dist/core/wiki/graph-queries.d.ts +84 -0
  839. package/dist/core/wiki/graph-queries.js +244 -0
  840. package/dist/core/wiki/html-viewer.d.ts +10 -0
  841. package/dist/core/wiki/html-viewer.js +303 -0
  842. package/dist/core/wiki/llm-client.d.ts +63 -0
  843. package/dist/core/wiki/llm-client.js +234 -0
  844. package/dist/core/wiki/prompts.d.ts +53 -0
  845. package/dist/core/wiki/prompts.js +181 -0
  846. package/dist/lib/utils.d.ts +1 -0
  847. package/dist/lib/utils.js +3 -0
  848. package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
  849. package/dist/mcp/compatible-stdio-transport.js +200 -0
  850. package/dist/mcp/core/embedder.d.ts +27 -0
  851. package/dist/mcp/core/embedder.js +122 -0
  852. package/dist/mcp/core/lbug-adapter.d.ts +5 -0
  853. package/dist/mcp/core/lbug-adapter.js +5 -0
  854. package/dist/mcp/local/graphstore-handler.d.ts +214 -0
  855. package/dist/mcp/local/graphstore-handler.js +272 -0
  856. package/dist/mcp/local/local-backend.d.ts +347 -0
  857. package/dist/mcp/local/local-backend.js +3218 -0
  858. package/dist/mcp/resources.d.ts +62 -0
  859. package/dist/mcp/resources.js +696 -0
  860. package/dist/mcp/server.d.ts +23 -0
  861. package/dist/mcp/server.js +533 -0
  862. package/dist/mcp/staleness.d.ts +5 -0
  863. package/dist/mcp/staleness.js +4 -0
  864. package/dist/mcp/tools.d.ts +27 -0
  865. package/dist/mcp/tools.js +823 -0
  866. package/dist/server/analyze-job.d.ts +55 -0
  867. package/dist/server/analyze-job.js +150 -0
  868. package/dist/server/analyze-worker.d.ts +13 -0
  869. package/dist/server/analyze-worker.js +59 -0
  870. package/dist/server/api.d.ts +47 -0
  871. package/dist/server/api.js +1727 -0
  872. package/dist/server/git-clone.d.ts +26 -0
  873. package/dist/server/git-clone.js +184 -0
  874. package/dist/server/mcp-http.d.ts +13 -0
  875. package/dist/server/mcp-http.js +100 -0
  876. package/dist/storage/git.d.ts +80 -0
  877. package/dist/storage/git.js +190 -0
  878. package/dist/storage/repo-manager.d.ts +458 -0
  879. package/dist/storage/repo-manager.js +766 -0
  880. package/dist/types/pipeline.d.ts +18 -0
  881. package/dist/types/pipeline.js +1 -0
  882. package/hooks/claude/codragraph-hook.cjs +268 -0
  883. package/hooks/claude/pre-tool-use.sh +79 -0
  884. package/hooks/claude/session-start.sh +42 -0
  885. package/package.json +127 -0
  886. package/scripts/bench-scope-resolution.ts +134 -0
  887. package/scripts/build-tree-sitter-proto.cjs +82 -0
  888. package/scripts/build.js +90 -0
  889. package/scripts/ci-list-migrated-languages.ts +24 -0
  890. package/scripts/patch-tree-sitter-swift.cjs +78 -0
  891. package/skills/codragraph-cli.md +82 -0
  892. package/skills/codragraph-debugging.md +89 -0
  893. package/skills/codragraph-exploring.md +78 -0
  894. package/skills/codragraph-guide.md +64 -0
  895. package/skills/codragraph-impact-analysis.md +97 -0
  896. package/skills/codragraph-pr-review.md +163 -0
  897. package/skills/codragraph-refactoring.md +121 -0
  898. package/vendor/leiden/index.cjs +355 -0
  899. package/vendor/leiden/utils.cjs +392 -0
  900. package/vendor/tree-sitter-proto/binding.gyp +30 -0
  901. package/vendor/tree-sitter-proto/bindings/node/binding.cc +20 -0
  902. package/vendor/tree-sitter-proto/bindings/node/index.d.ts +28 -0
  903. package/vendor/tree-sitter-proto/bindings/node/index.js +7 -0
  904. package/vendor/tree-sitter-proto/package.json +12 -0
  905. package/vendor/tree-sitter-proto/src/node-types.json +1145 -0
  906. package/vendor/tree-sitter-proto/src/parser.c +10149 -0
  907. package/vendor/tree-sitter-proto/src/tree_sitter/alloc.h +54 -0
  908. package/vendor/tree-sitter-proto/src/tree_sitter/array.h +291 -0
  909. package/vendor/tree-sitter-proto/src/tree_sitter/parser.h +266 -0
@@ -0,0 +1,1727 @@
1
+ /**
2
+ * HTTP API Server
3
+ *
4
+ * REST API for browser-based clients to query the local .codragraph/ index.
5
+ * Also hosts the MCP server over StreamableHTTP for remote AI tool access.
6
+ *
7
+ * Security: binds to localhost by default (use --host to override).
8
+ * CORS is restricted to localhost, private/LAN networks, and the deployed site.
9
+ */
10
+ import express from 'express';
11
+ import cors from 'cors';
12
+ import path from 'path';
13
+ import fs from 'fs/promises';
14
+ import { createRequire } from 'node:module';
15
+ import { loadMeta, listRegisteredRepos, getStoragePath } from '../storage/repo-manager.js';
16
+ import { executeQuery, executePrepared, executeWithReusedStatement, streamQuery, closeLbug, withLbugDb, } from '../core/lbug/lbug-adapter.js';
17
+ import { isWriteQuery } from '../core/lbug/pool-adapter.js';
18
+ import { NODE_TABLES } from '../_shared/index.js';
19
+ import { searchFTSFromLbug } from '../core/search/bm25-index.js';
20
+ import { hybridSearch } from '../core/search/hybrid-search.js';
21
+ // Embedding imports are lazy (dynamic import) to avoid loading onnxruntime-node
22
+ // at server startup — crashes on unsupported Node ABI versions (#89)
23
+ import { LocalBackend } from '../mcp/local/local-backend.js';
24
+ import { mountMCPEndpoints } from './mcp-http.js';
25
+ import { fork } from 'child_process';
26
+ import { fileURLToPath, pathToFileURL } from 'url';
27
+ import { JobManager } from './analyze-job.js';
28
+ import { extractRepoName, getCloneDir, cloneOrPull } from './git-clone.js';
29
+ const _require = createRequire(import.meta.url);
30
+ const pkg = _require('../../package.json');
31
+ /**
32
+ * Determine whether an HTTP Origin header value is allowed by CORS policy.
33
+ *
34
+ * Permitted origins:
35
+ * - No origin (non-browser requests such as curl or server-to-server calls)
36
+ * - http://localhost:<port> — local development
37
+ * - http://127.0.0.1:<port> — loopback alias
38
+ * - RFC 1918 private/LAN networks (any port):
39
+ * 10.0.0.0/8 → 10.x.x.x
40
+ * 172.16.0.0/12 → 172.16.x.x – 172.31.x.x
41
+ * 192.168.0.0/16 → 192.168.x.x
42
+ * - https://codragraph.vercel.app — the deployed CodraGraph web UI
43
+ *
44
+ * @param origin - The value of the HTTP `Origin` request header, or `undefined`
45
+ * when the header is absent (non-browser request).
46
+ * @returns `true` if the origin is allowed, `false` otherwise.
47
+ */
48
+ export const isAllowedOrigin = (origin) => {
49
+ if (origin === undefined) {
50
+ // Non-browser requests (curl, server-to-server) have no Origin header
51
+ return true;
52
+ }
53
+ if (origin.startsWith('http://localhost:') ||
54
+ origin === 'http://localhost' ||
55
+ origin.startsWith('http://127.0.0.1:') ||
56
+ origin === 'http://127.0.0.1' ||
57
+ origin.startsWith('http://[::1]:') ||
58
+ origin === 'http://[::1]' ||
59
+ origin === 'https://codragraph.vercel.app') {
60
+ return true;
61
+ }
62
+ // RFC 1918 private network ranges — allow any port on these hosts.
63
+ // We parse the hostname out of the origin URL and check against each range.
64
+ let hostname;
65
+ let protocol;
66
+ try {
67
+ const parsed = new URL(origin);
68
+ hostname = parsed.hostname;
69
+ protocol = parsed.protocol;
70
+ }
71
+ catch {
72
+ // Malformed origin — reject
73
+ return false;
74
+ }
75
+ // Only allow HTTP(S) origins — reject ftp://, file://, etc.
76
+ if (protocol !== 'http:' && protocol !== 'https:')
77
+ return false;
78
+ const octets = hostname.split('.').map(Number);
79
+ if (octets.length !== 4 || octets.some((o) => !Number.isInteger(o) || o < 0 || o > 255)) {
80
+ return false;
81
+ }
82
+ const [a, b] = octets;
83
+ // 10.0.0.0/8
84
+ if (a === 10)
85
+ return true;
86
+ // 172.16.0.0/12 → 172.16.x.x – 172.31.x.x
87
+ if (a === 172 && b >= 16 && b <= 31)
88
+ return true;
89
+ // 192.168.0.0/16
90
+ if (a === 192 && b === 168)
91
+ return true;
92
+ return false;
93
+ };
94
+ export class ClientDisconnectedError extends Error {
95
+ constructor() {
96
+ super('Client disconnected during graph stream');
97
+ this.name = 'ClientDisconnectedError';
98
+ }
99
+ }
100
+ export const isIgnorableGraphQueryError = (err) => {
101
+ const message = err instanceof Error ? err.message : String(err);
102
+ return (message.includes('does not exist') ||
103
+ message.includes('not found') ||
104
+ message.includes('No table named'));
105
+ };
106
+ const ensureStreamIsWritable = (res, signal) => {
107
+ if (signal?.aborted || res.destroyed || res.writableEnded) {
108
+ throw new ClientDisconnectedError();
109
+ }
110
+ };
111
+ const waitForDrain = async (res, signal) => {
112
+ ensureStreamIsWritable(res, signal);
113
+ await new Promise((resolve, reject) => {
114
+ const cleanup = () => {
115
+ res.off('drain', onDrain);
116
+ res.off('close', onClose);
117
+ signal?.removeEventListener('abort', onAbort);
118
+ };
119
+ const onDrain = () => {
120
+ cleanup();
121
+ resolve();
122
+ };
123
+ const onClose = () => {
124
+ cleanup();
125
+ reject(new ClientDisconnectedError());
126
+ };
127
+ const onAbort = () => {
128
+ cleanup();
129
+ reject(new ClientDisconnectedError());
130
+ };
131
+ res.once('drain', onDrain);
132
+ res.once('close', onClose);
133
+ signal?.addEventListener('abort', onAbort, { once: true });
134
+ if (signal?.aborted || res.destroyed || res.writableEnded) {
135
+ onAbort();
136
+ }
137
+ });
138
+ ensureStreamIsWritable(res, signal);
139
+ };
140
+ const isClientDisconnectWriteError = (err) => {
141
+ if (!(err instanceof Error))
142
+ return false;
143
+ return (err.code === 'ERR_STREAM_DESTROYED' ||
144
+ err.code === 'EPIPE' ||
145
+ err.code === 'ECONNRESET' ||
146
+ err.message.includes('write after end'));
147
+ };
148
+ export const writeNdjsonRecord = async (res, record, signal) => {
149
+ ensureStreamIsWritable(res, signal);
150
+ try {
151
+ const canContinue = res.write(JSON.stringify(record) + '\n');
152
+ if (!canContinue) {
153
+ await waitForDrain(res, signal);
154
+ }
155
+ }
156
+ catch (err) {
157
+ if (isClientDisconnectWriteError(err)) {
158
+ throw new ClientDisconnectedError();
159
+ }
160
+ throw err;
161
+ }
162
+ };
163
+ const buildGraph = async (includeContent = false) => {
164
+ const nodes = [];
165
+ for (const table of NODE_TABLES) {
166
+ try {
167
+ const rows = await executeQuery(getNodeQuery(table, includeContent));
168
+ for (const row of rows) {
169
+ nodes.push(mapGraphNodeRow(table, row, includeContent));
170
+ }
171
+ }
172
+ catch (err) {
173
+ if (!isIgnorableGraphQueryError(err)) {
174
+ throw err;
175
+ }
176
+ }
177
+ }
178
+ const relationships = [];
179
+ const relRows = await executeQuery(GRAPH_RELATIONSHIP_QUERY);
180
+ for (const row of relRows) {
181
+ relationships.push(mapGraphRelationshipRow(row));
182
+ }
183
+ return { nodes, relationships };
184
+ };
185
+ const GRAPH_RELATIONSHIP_QUERY = `MATCH (a)-[r:CodeRelation]->(b) RETURN a.id AS sourceId, b.id AS targetId, ` +
186
+ `r.type AS type, r.confidence AS confidence, r.reason AS reason, r.step AS step`;
187
+ const quoteNodeTable = (table) => `\`${table.replace(/`/g, '``')}\``;
188
+ const getNodeQuery = (table, includeContent) => {
189
+ const tableLabel = quoteNodeTable(table);
190
+ if (table === 'File') {
191
+ return includeContent
192
+ ? `MATCH (n:${tableLabel}) RETURN n.id AS id, n.name AS name, n.filePath AS filePath, n.content AS content`
193
+ : `MATCH (n:${tableLabel}) RETURN n.id AS id, n.name AS name, n.filePath AS filePath`;
194
+ }
195
+ if (table === 'Folder') {
196
+ return `MATCH (n:${tableLabel}) RETURN n.id AS id, n.name AS name, n.filePath AS filePath`;
197
+ }
198
+ if (table === 'Community') {
199
+ return `MATCH (n:${tableLabel}) RETURN n.id AS id, n.label AS label, n.heuristicLabel AS heuristicLabel, n.cohesion AS cohesion, n.symbolCount AS symbolCount`;
200
+ }
201
+ if (table === 'Process') {
202
+ return `MATCH (n:${tableLabel}) RETURN n.id AS id, n.label AS label, n.heuristicLabel AS heuristicLabel, n.processType AS processType, n.stepCount AS stepCount, n.communities AS communities, n.entryPointId AS entryPointId, n.terminalId AS terminalId`;
203
+ }
204
+ if (table === 'Route') {
205
+ return `MATCH (n:${tableLabel}) RETURN n.id AS id, n.name AS name, n.filePath AS filePath, n.responseKeys AS responseKeys, n.errorKeys AS errorKeys, n.middleware AS middleware`;
206
+ }
207
+ if (table === 'Tool') {
208
+ return `MATCH (n:${tableLabel}) RETURN n.id AS id, n.name AS name, n.filePath AS filePath, n.description AS description`;
209
+ }
210
+ return includeContent
211
+ ? `MATCH (n:${tableLabel}) RETURN n.id AS id, n.name AS name, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine, n.content AS content`
212
+ : `MATCH (n:${tableLabel}) RETURN n.id AS id, n.name AS name, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine`;
213
+ };
214
+ const mapGraphNodeRow = (table, row, includeContent) => ({
215
+ id: row.id ?? row[0],
216
+ label: table,
217
+ properties: {
218
+ name: row.name ?? row.label ?? row[1],
219
+ filePath: row.filePath ?? row[2],
220
+ startLine: row.startLine,
221
+ endLine: row.endLine,
222
+ content: includeContent ? row.content : undefined,
223
+ responseKeys: row.responseKeys,
224
+ errorKeys: row.errorKeys,
225
+ middleware: row.middleware,
226
+ heuristicLabel: row.heuristicLabel,
227
+ cohesion: row.cohesion,
228
+ symbolCount: row.symbolCount,
229
+ description: row.description,
230
+ processType: row.processType,
231
+ stepCount: row.stepCount,
232
+ communities: row.communities,
233
+ entryPointId: row.entryPointId,
234
+ terminalId: row.terminalId,
235
+ },
236
+ });
237
+ const mapGraphRelationshipRow = (row) => ({
238
+ id: `${row.sourceId}_${row.type}_${row.targetId}`,
239
+ type: row.type,
240
+ sourceId: row.sourceId,
241
+ targetId: row.targetId,
242
+ confidence: row.confidence,
243
+ reason: row.reason,
244
+ step: row.step,
245
+ });
246
+ export const streamGraphNdjson = async (res, includeContent = false, signal) => {
247
+ for (const table of NODE_TABLES) {
248
+ try {
249
+ await streamQuery(getNodeQuery(table, includeContent), async (row) => {
250
+ await writeNdjsonRecord(res, {
251
+ type: 'node',
252
+ data: mapGraphNodeRow(table, row, includeContent),
253
+ }, signal);
254
+ });
255
+ }
256
+ catch (err) {
257
+ if (!isIgnorableGraphQueryError(err)) {
258
+ throw err;
259
+ }
260
+ }
261
+ }
262
+ await streamQuery(GRAPH_RELATIONSHIP_QUERY, async (row) => {
263
+ await writeNdjsonRecord(res, {
264
+ type: 'relationship',
265
+ data: mapGraphRelationshipRow(row),
266
+ }, signal);
267
+ });
268
+ };
269
+ /**
270
+ * Mount an SSE progress endpoint for a JobManager.
271
+ * Handles: initial state, terminal events, heartbeat, event IDs, client disconnect.
272
+ */
273
+ const mountSSEProgress = (app, routePath, jm) => {
274
+ app.get(routePath, (req, res) => {
275
+ const job = jm.getJob(req.params.jobId);
276
+ if (!job) {
277
+ res.status(404).json({ error: 'Job not found' });
278
+ return;
279
+ }
280
+ let eventId = 0;
281
+ res.writeHead(200, {
282
+ 'Content-Type': 'text/event-stream',
283
+ 'Cache-Control': 'no-cache',
284
+ Connection: 'keep-alive',
285
+ 'X-Accel-Buffering': 'no',
286
+ });
287
+ // Send current state immediately
288
+ eventId++;
289
+ res.write(`id: ${eventId}\ndata: ${JSON.stringify(job.progress)}\n\n`);
290
+ // If already terminal, send event and close
291
+ if (job.status === 'complete' || job.status === 'failed') {
292
+ eventId++;
293
+ res.write(`id: ${eventId}\nevent: ${job.status}\ndata: ${JSON.stringify({
294
+ repoName: job.repoName,
295
+ error: job.error,
296
+ })}\n\n`);
297
+ res.end();
298
+ return;
299
+ }
300
+ // Heartbeat to detect zombie connections
301
+ const heartbeat = setInterval(() => {
302
+ try {
303
+ res.write(':heartbeat\n\n');
304
+ }
305
+ catch {
306
+ clearInterval(heartbeat);
307
+ unsubscribe();
308
+ }
309
+ }, 30_000);
310
+ // Subscribe to progress updates
311
+ const unsubscribe = jm.onProgress(job.id, (progress) => {
312
+ try {
313
+ eventId++;
314
+ if (progress.phase === 'complete' || progress.phase === 'failed') {
315
+ const eventJob = jm.getJob(req.params.jobId);
316
+ res.write(`id: ${eventId}\nevent: ${progress.phase}\ndata: ${JSON.stringify({
317
+ repoName: eventJob?.repoName,
318
+ error: eventJob?.error,
319
+ })}\n\n`);
320
+ clearInterval(heartbeat);
321
+ res.end();
322
+ unsubscribe();
323
+ }
324
+ else {
325
+ res.write(`id: ${eventId}\ndata: ${JSON.stringify(progress)}\n\n`);
326
+ }
327
+ }
328
+ catch {
329
+ clearInterval(heartbeat);
330
+ unsubscribe();
331
+ }
332
+ });
333
+ req.on('close', () => {
334
+ clearInterval(heartbeat);
335
+ unsubscribe();
336
+ });
337
+ });
338
+ };
339
+ const statusFromError = (err) => {
340
+ const msg = String(err?.message ?? '');
341
+ if (msg.includes('No indexed repositories') || msg.includes('not found'))
342
+ return 404;
343
+ if (msg.includes('Multiple repositories'))
344
+ return 400;
345
+ return 500;
346
+ };
347
+ const requestedRepo = (req) => {
348
+ const fromQuery = typeof req.query.repo === 'string' ? req.query.repo : undefined;
349
+ if (fromQuery)
350
+ return fromQuery;
351
+ if (req.body && typeof req.body === 'object' && typeof req.body.repo === 'string') {
352
+ return req.body.repo;
353
+ }
354
+ return undefined;
355
+ };
356
+ export const createServer = async (port, host = '127.0.0.1') => {
357
+ const app = express();
358
+ app.disable('x-powered-by');
359
+ // CORS: allow localhost, private/LAN networks, and the deployed site.
360
+ // Non-browser requests (curl, server-to-server) have no origin and are allowed.
361
+ // Disallowed origins get the response without Access-Control-Allow-Origin,
362
+ // so the browser blocks it. We pass `false` instead of throwing an Error to
363
+ // avoid crashing into Express's default error handler (which returned 500).
364
+ app.use(cors({
365
+ origin: (origin, callback) => {
366
+ callback(null, isAllowedOrigin(origin));
367
+ },
368
+ }));
369
+ app.use(express.json({ limit: '10mb' }));
370
+ // Support Chromium Private Network Access (required since Chrome 130+).
371
+ // Without this header, Chrome/Edge/Brave/Arc block public->loopback requests
372
+ // which breaks bridge mode entirely.
373
+ app.use((_req, res, next) => {
374
+ res.setHeader('Access-Control-Allow-Private-Network', 'true');
375
+ next();
376
+ });
377
+ // Handle PNA preflight: Chromium sends Access-Control-Request-Private-Network
378
+ // on OPTIONS requests and expects the allow header in the response.
379
+ // Note: the actual Allow-Private-Network header is already set by the global
380
+ // middleware above, so we just need to call next() here.
381
+ app.options('*', (_req, res, next) => {
382
+ next();
383
+ });
384
+ // Initialize MCP backend (multi-repo, shared across all MCP sessions)
385
+ const backend = new LocalBackend();
386
+ await backend.init();
387
+ const cleanupMcp = mountMCPEndpoints(app, backend);
388
+ const jobManager = new JobManager();
389
+ // Shared repo lock — prevents concurrent analyze + embed on the same repo path,
390
+ // which would corrupt LadybugDB (analyze calls closeLbug + initLbug while embed has queries in flight).
391
+ const activeRepoPaths = new Set();
392
+ const acquireRepoLock = (repoPath) => {
393
+ if (activeRepoPaths.has(repoPath)) {
394
+ return `Another job is already active for this repository`;
395
+ }
396
+ activeRepoPaths.add(repoPath);
397
+ return null;
398
+ };
399
+ const releaseRepoLock = (repoPath) => {
400
+ activeRepoPaths.delete(repoPath);
401
+ };
402
+ /**
403
+ * Maximum time the hold-queue will wait for an active analysis job to complete.
404
+ * Must stay in sync with the frontend's `fetchRepoInfo({ awaitAnalysis: true })` timeout.
405
+ */
406
+ const HOLD_QUEUE_TIMEOUT_SECS = 300; // 5 minutes
407
+ // Helper: resolve a repo by name from the global registry, or default to first.
408
+ // Pass `req` to enable early exit if the client disconnects during the hold-queue wait.
409
+ const resolveRepo = async (repoName, isRetry = false, req) => {
410
+ const repos = await listRegisteredRepos();
411
+ let found = null;
412
+ // Normalize: if a full path is passed, extract just the basename.
413
+ // e.g. "C:\Users\LENOVO\.codragraph\repos\todo.txt-cli" -> "todo.txt-cli"
414
+ const normalizedName = repoName ? path.basename(repoName) : undefined;
415
+ if (normalizedName) {
416
+ found =
417
+ repos.find((r) => r.name === normalizedName) ||
418
+ repos.find((r) => r.name.toLowerCase() === normalizedName.toLowerCase()) ||
419
+ null;
420
+ }
421
+ else if (repos.length > 0) {
422
+ found = repos[0]; // default to first repo
423
+ }
424
+ // If not yet in the registry, check whether a background job is actively cloning or
425
+ // analyzing this repo. Hold the connection open (up to 5 minutes) until it completes.
426
+ // We only wait for in-progress jobs ('queued'|'cloning'|'analyzing') — a 'complete' job
427
+ // whose repo is still missing means the registry sync failed; the fallback below handles it.
428
+ if (!found && normalizedName) {
429
+ const lower = normalizedName.toLowerCase();
430
+ // Track client disconnect to cancel the wait early
431
+ let clientGone = false;
432
+ req?.on('close', () => {
433
+ clientGone = true;
434
+ });
435
+ for (const job of jobManager.listJobs()) {
436
+ const isMatch = job.repoName?.toLowerCase() === lower ||
437
+ (job.repoUrl && path.basename(job.repoUrl).replace('.git', '').toLowerCase() === lower) ||
438
+ (job.repoPath && path.basename(job.repoPath).toLowerCase() === lower);
439
+ if (isMatch && ['queued', 'cloning', 'analyzing'].includes(job.status)) {
440
+ if (process.env.DEBUG) {
441
+ console.log(`[debug] resolveRepo waiting for active job ${job.id} (${normalizedName})...`);
442
+ }
443
+ for (let wait = 0; wait < HOLD_QUEUE_TIMEOUT_SECS; wait++) {
444
+ if (clientGone)
445
+ return null; // client disconnected — stop polling
446
+ const currentJob = jobManager.getJob(job.id);
447
+ if (!currentJob || currentJob.status === 'failed')
448
+ break;
449
+ if (currentJob.status === 'complete') {
450
+ await backend.init();
451
+ const freshRepos = await listRegisteredRepos();
452
+ return freshRepos.find((r) => r.name === normalizedName) || null;
453
+ }
454
+ await new Promise((r) => setTimeout(r, 1000));
455
+ }
456
+ // Timed out — signal to the caller with a specific message
457
+ return { __timedOut: true, repoName: normalizedName };
458
+ }
459
+ }
460
+ }
461
+ // Emergency fallback: re-sync the registry to handle Windows file-system race conditions
462
+ // (e.g. registry file not yet flushed after clone completes).
463
+ if (!found && normalizedName && !isRetry) {
464
+ if (process.env.DEBUG) {
465
+ console.log(`[debug] resolveRepo 404 for "${normalizedName}". Triggering deep init...`);
466
+ }
467
+ await backend.init();
468
+ return await resolveRepo(normalizedName, true, req);
469
+ }
470
+ return found;
471
+ };
472
+ // SSE heartbeat — clients connect to detect server liveness instantly.
473
+ // When the server shuts down, the TCP connection drops and the client's
474
+ // EventSource fires onerror immediately (no polling delay).
475
+ app.get('/api/heartbeat', (_req, res) => {
476
+ // Use res.set() instead of res.writeHead() to preserve CORS headers from middleware
477
+ res.set({
478
+ 'Content-Type': 'text/event-stream',
479
+ 'Cache-Control': 'no-cache',
480
+ Connection: 'keep-alive',
481
+ });
482
+ res.flushHeaders();
483
+ // Send initial ping so the client knows it connected
484
+ res.write(':ok\n\n');
485
+ // Keep-alive ping every 15s to prevent proxy/firewall timeout
486
+ const interval = setInterval(() => res.write(':ping\n\n'), 15_000);
487
+ _req.on('close', () => clearInterval(interval));
488
+ });
489
+ // Server info: version and launch context (npx / global / local dev)
490
+ app.get('/api/info', (_req, res) => {
491
+ const execPath = process.env.npm_execpath ?? '';
492
+ const argv0 = process.argv[1] ?? '';
493
+ let launchContext;
494
+ if (execPath.includes('npx') ||
495
+ argv0.includes('_npx') ||
496
+ process.env.npm_config_prefix?.includes('_npx')) {
497
+ launchContext = 'npx';
498
+ }
499
+ else if (argv0.includes('node_modules')) {
500
+ launchContext = 'local';
501
+ }
502
+ else {
503
+ launchContext = 'global';
504
+ }
505
+ res.json({ version: pkg.version, launchContext, nodeVersion: process.version });
506
+ });
507
+ // List all registered repos
508
+ app.get('/api/repos', async (_req, res) => {
509
+ try {
510
+ const repos = await listRegisteredRepos();
511
+ res.json(repos.map((r) => ({
512
+ name: r.name,
513
+ path: r.path,
514
+ indexedAt: r.indexedAt,
515
+ lastCommit: r.lastCommit,
516
+ stats: r.stats,
517
+ })));
518
+ }
519
+ catch (err) {
520
+ res.status(500).json({ error: err.message || 'Failed to list repos' });
521
+ }
522
+ });
523
+ // Get repo info
524
+ app.get('/api/repo', async (req, res) => {
525
+ try {
526
+ const entry = await resolveRepo(requestedRepo(req), false, req);
527
+ if (!entry) {
528
+ res.status(404).json({ error: 'Repository not found. Run: codragraph analyze' });
529
+ return;
530
+ }
531
+ // Timed out waiting for an active analysis job
532
+ if (entry.__timedOut) {
533
+ res.status(503).json({
534
+ error: `Repository analysis for "${entry.repoName}" is taking longer than expected. Please try again in a moment.`,
535
+ });
536
+ return;
537
+ }
538
+ const meta = await loadMeta(entry.storagePath);
539
+ res.json({
540
+ name: entry.name,
541
+ repoPath: entry.path,
542
+ indexedAt: meta?.indexedAt ?? entry.indexedAt,
543
+ stats: meta?.stats ?? entry.stats ?? {},
544
+ });
545
+ }
546
+ catch (err) {
547
+ res.status(500).json({ error: err.message || 'Failed to get repo info' });
548
+ }
549
+ });
550
+ // Delete a repo — removes index, clone dir (if any), and unregisters it
551
+ app.delete('/api/repo', async (req, res) => {
552
+ try {
553
+ const repoName = requestedRepo(req);
554
+ if (!repoName) {
555
+ res.status(400).json({ error: 'Missing repo name' });
556
+ return;
557
+ }
558
+ const entry = await resolveRepo(repoName);
559
+ if (!entry) {
560
+ res.status(404).json({ error: 'Repository not found' });
561
+ return;
562
+ }
563
+ // Acquire repo lock — prevents deleting while analyze/embed is in flight
564
+ const lockKey = getStoragePath(entry.path);
565
+ const lockErr = acquireRepoLock(lockKey);
566
+ if (lockErr) {
567
+ res.status(409).json({ error: lockErr });
568
+ return;
569
+ }
570
+ try {
571
+ // Close any open LadybugDB handle before deleting files
572
+ try {
573
+ await closeLbug();
574
+ }
575
+ catch { }
576
+ // 1. Delete the .codragraph index/storage directory
577
+ const storagePath = getStoragePath(entry.path);
578
+ await fs.rm(storagePath, { recursive: true, force: true }).catch(() => { });
579
+ // 2. Delete the cloned repo dir if it lives under ~/.codragraph/repos/
580
+ const cloneDir = getCloneDir(entry.name);
581
+ try {
582
+ const stat = await fs.stat(cloneDir);
583
+ if (stat.isDirectory()) {
584
+ await fs.rm(cloneDir, { recursive: true, force: true });
585
+ }
586
+ }
587
+ catch {
588
+ /* clone dir may not exist (local repos) */
589
+ }
590
+ // 3. Unregister from the global registry
591
+ const { unregisterRepo } = await import('../storage/repo-manager.js');
592
+ await unregisterRepo(entry.path);
593
+ // 4. Reinitialize backend to reflect the removal
594
+ await backend.init().catch(() => { });
595
+ res.json({ deleted: entry.name });
596
+ }
597
+ finally {
598
+ releaseRepoLock(lockKey);
599
+ }
600
+ }
601
+ catch (err) {
602
+ res.status(500).json({ error: err.message || 'Failed to delete repo' });
603
+ }
604
+ });
605
+ // ─── Phase 4: versioned graph (graphstore) ─────────────────────────
606
+ //
607
+ // Each route resolves the repo, then delegates to the same handler
608
+ // module the MCP server uses (mcp/local/graphstore-handler.ts) so
609
+ // there is a single source of truth for serialization.
610
+ app.get('/api/graphstore/log', async (req, res) => {
611
+ try {
612
+ const entry = await resolveRepo(requestedRepo(req));
613
+ if (!entry) {
614
+ res.status(404).json({ error: 'Repository not found' });
615
+ return;
616
+ }
617
+ const { handleGraphstoreLog } = await import('../mcp/local/graphstore-handler.js');
618
+ const limitParam = typeof req.query.limit === 'string' ? Number.parseInt(req.query.limit, 10) : undefined;
619
+ const result = await handleGraphstoreLog({
620
+ storagePath: entry.storagePath,
621
+ from: typeof req.query.from === 'string' ? req.query.from : undefined,
622
+ limit: Number.isFinite(limitParam) ? limitParam : undefined,
623
+ });
624
+ res.json(result);
625
+ }
626
+ catch (err) {
627
+ res.status(500).json({ error: err.message || 'graphstore log failed' });
628
+ }
629
+ });
630
+ app.get('/api/graphstore/branches', async (req, res) => {
631
+ try {
632
+ const entry = await resolveRepo(requestedRepo(req));
633
+ if (!entry) {
634
+ res.status(404).json({ error: 'Repository not found' });
635
+ return;
636
+ }
637
+ const { handleGraphstoreBranches } = await import('../mcp/local/graphstore-handler.js');
638
+ const result = await handleGraphstoreBranches({ storagePath: entry.storagePath });
639
+ res.json(result);
640
+ }
641
+ catch (err) {
642
+ res.status(500).json({ error: err.message || 'graphstore branches failed' });
643
+ }
644
+ });
645
+ app.get('/api/graphstore/diff', async (req, res) => {
646
+ try {
647
+ const entry = await resolveRepo(requestedRepo(req));
648
+ if (!entry) {
649
+ res.status(404).json({ error: 'Repository not found' });
650
+ return;
651
+ }
652
+ const from = typeof req.query.from === 'string' ? req.query.from : '';
653
+ const to = typeof req.query.to === 'string' ? req.query.to : '';
654
+ if (!from || !to) {
655
+ res
656
+ .status(400)
657
+ .json({ error: 'graphstore diff requires both `from` and `to` query params' });
658
+ return;
659
+ }
660
+ const { handleGraphstoreDiff } = await import('../mcp/local/graphstore-handler.js');
661
+ const result = await handleGraphstoreDiff({ storagePath: entry.storagePath, from, to });
662
+ res.json(result);
663
+ }
664
+ catch (err) {
665
+ res.status(500).json({ error: err.message || 'graphstore diff failed' });
666
+ }
667
+ });
668
+ // Higher-fidelity diff: classifies modifications + surfaces added /
669
+ // removed exported APIs and Processes. The dashboard's "what broke
670
+ // / what fixed" panel renders this.
671
+ app.get('/api/graphstore/semantic-diff', async (req, res) => {
672
+ try {
673
+ const entry = await resolveRepo(requestedRepo(req));
674
+ if (!entry) {
675
+ res.status(404).json({ error: 'Repository not found' });
676
+ return;
677
+ }
678
+ const from = typeof req.query.from === 'string' ? req.query.from : '';
679
+ const to = typeof req.query.to === 'string' ? req.query.to : '';
680
+ if (!from || !to) {
681
+ res.status(400).json({
682
+ error: 'graphstore semantic-diff requires both `from` and `to` query params',
683
+ });
684
+ return;
685
+ }
686
+ const { handleGraphstoreSemanticDiff } = await import('../mcp/local/graphstore-handler.js');
687
+ const result = await handleGraphstoreSemanticDiff({
688
+ storagePath: entry.storagePath,
689
+ from,
690
+ to,
691
+ });
692
+ res.json(result);
693
+ }
694
+ catch (err) {
695
+ res.status(500).json({ error: err.message || 'graphstore semantic-diff failed' });
696
+ }
697
+ });
698
+ // ─── Phase 4 × Phase 3 moat: harness recipes ───────────────────────
699
+ //
700
+ // Lazy-imports the handler from codragraph-harness so the package
701
+ // stays optional at runtime — same pattern as harness_run /
702
+ // harness_swarm_run in local-backend.ts.
703
+ const importRecipeHandler = async (handlerName) => {
704
+ const moduleId = '@codragraph/harness/mcp/handler';
705
+ try {
706
+ const mod = (await import(/* @vite-ignore */ moduleId));
707
+ const fn = mod[handlerName];
708
+ if (typeof fn !== 'function') {
709
+ throw new Error(`codragraph-harness does not export ${handlerName}`);
710
+ }
711
+ return fn;
712
+ }
713
+ catch (err) {
714
+ throw new Error(`recipes endpoint requires codragraph-harness to be installed: ${err?.message ?? String(err)}`);
715
+ }
716
+ };
717
+ app.get('/api/recipes', async (req, res) => {
718
+ try {
719
+ const entry = await resolveRepo(requestedRepo(req));
720
+ if (!entry) {
721
+ res.status(404).json({ error: 'Repository not found' });
722
+ return;
723
+ }
724
+ const limitParam = typeof req.query.limit === 'string' ? Number.parseInt(req.query.limit, 10) : undefined;
725
+ const handler = await importRecipeHandler('handleHarnessRecipesList');
726
+ const result = await handler({
727
+ recipe_store: path.join(entry.storagePath, 'recipes'),
728
+ task_family: typeof req.query.task_family === 'string' ? req.query.task_family : undefined,
729
+ snapshot_id: typeof req.query.snapshot_id === 'string' ? req.query.snapshot_id : undefined,
730
+ limit: Number.isFinite(limitParam) ? limitParam : undefined,
731
+ });
732
+ res.json(result);
733
+ }
734
+ catch (err) {
735
+ res.status(500).json({ error: err.message || 'recipes list failed' });
736
+ }
737
+ });
738
+ app.get('/api/recipes/lookup', async (req, res) => {
739
+ try {
740
+ const entry = await resolveRepo(requestedRepo(req));
741
+ if (!entry) {
742
+ res.status(404).json({ error: 'Repository not found' });
743
+ return;
744
+ }
745
+ const taskFamily = typeof req.query.task_family === 'string' ? req.query.task_family : '';
746
+ const snapshotId = typeof req.query.snapshot_id === 'string' ? req.query.snapshot_id : '';
747
+ if (!taskFamily || !snapshotId) {
748
+ res.status(400).json({
749
+ error: 'recipes lookup requires `task_family` and `snapshot_id` query params',
750
+ });
751
+ return;
752
+ }
753
+ const handler = await importRecipeHandler('handleHarnessRecipesLookup');
754
+ const result = await handler({
755
+ recipe_store: path.join(entry.storagePath, 'recipes'),
756
+ task_family: taskFamily,
757
+ snapshot_id: snapshotId,
758
+ limit: typeof req.query.limit === 'string' ? Number.parseInt(req.query.limit, 10) : undefined,
759
+ });
760
+ res.json(result);
761
+ }
762
+ catch (err) {
763
+ res.status(500).json({ error: err.message || 'recipes lookup failed' });
764
+ }
765
+ });
766
+ // Read one recipe's full content (harness body, scores, provenance)
767
+ // by id. Powers the dashboard's recipe-detail drawer. Reads the file
768
+ // directly from the FsRecipeStore layout under
769
+ // `<storagePath>/recipes/by-id/<sanitized-id>.json` rather than going
770
+ // through the MCP handler — the listing endpoint already covers
771
+ // search and we want this route to be cheap and focused.
772
+ app.get('/api/recipes/by-id/:id', async (req, res) => {
773
+ try {
774
+ const entry = await resolveRepo(requestedRepo(req));
775
+ if (!entry) {
776
+ res.status(404).json({ error: 'Repository not found' });
777
+ return;
778
+ }
779
+ const safeId = req.params.id.replace(/[^A-Za-z0-9._-]/g, '_');
780
+ const recipePath = path.join(entry.storagePath, 'recipes', 'by-id', `${safeId}.json`);
781
+ try {
782
+ const raw = await fs.readFile(recipePath, 'utf-8');
783
+ res.type('application/json').send(raw);
784
+ }
785
+ catch (err) {
786
+ if (err?.code === 'ENOENT') {
787
+ res.status(404).json({ error: `recipe ${req.params.id} not found` });
788
+ return;
789
+ }
790
+ throw err;
791
+ }
792
+ }
793
+ catch (err) {
794
+ res.status(500).json({ error: err.message || 'recipe by-id failed' });
795
+ }
796
+ });
797
+ // ─── Cross-repo groups (multi-repo + monorepo bridges) ─────────────
798
+ //
799
+ // Groups bundle multiple registered repos under a single name and
800
+ // declare contract links between them (HTTP/gRPC/topic/lib). The
801
+ // dashboard's Projects tab consumes these to render: which repos a
802
+ // user's project is part of, what contracts cross between them, and
803
+ // which sibling clones are stale.
804
+ //
805
+ // Each route delegates to the same group service the MCP layer uses
806
+ // (LocalBackend.getGroupService) so there is one source of truth.
807
+ const getGroupSvc = () => backend.getGroupService();
808
+ app.get('/api/groups', async (_req, res) => {
809
+ try {
810
+ const result = (await getGroupSvc().groupList({}));
811
+ res.json({ groups: result.groups ?? [] });
812
+ }
813
+ catch (err) {
814
+ res.status(500).json({ error: err.message || 'group list failed' });
815
+ }
816
+ });
817
+ // Reverse lookup: MUST be registered BEFORE `/api/groups/:name` so
818
+ // Express matches the literal `for-repo` segment first instead of
819
+ // treating it as a group name parameter.
820
+ app.get('/api/groups/for-repo', async (req, res) => {
821
+ try {
822
+ const repoName = typeof req.query.repo === 'string' ? req.query.repo : '';
823
+ if (!repoName) {
824
+ res.status(400).json({ error: 'repo query param is required' });
825
+ return;
826
+ }
827
+ const svc = getGroupSvc();
828
+ const list = (await svc.groupList({}));
829
+ const matches = [];
830
+ for (const groupName of list.groups ?? []) {
831
+ const details = (await svc.groupList({ name: groupName }));
832
+ if (!details.repos)
833
+ continue;
834
+ for (const [groupPath, registryName] of Object.entries(details.repos)) {
835
+ if (registryName === repoName) {
836
+ matches.push({ group: groupName, groupPath });
837
+ }
838
+ }
839
+ }
840
+ res.json({ repo: repoName, groups: matches });
841
+ }
842
+ catch (err) {
843
+ res.status(500).json({ error: err.message || 'groups-for-repo failed' });
844
+ }
845
+ });
846
+ app.get('/api/groups/:name', async (req, res) => {
847
+ try {
848
+ const result = await getGroupSvc().groupList({ name: req.params.name });
849
+ if (result.error) {
850
+ res.status(404).json(result);
851
+ return;
852
+ }
853
+ res.json(result);
854
+ }
855
+ catch (err) {
856
+ res.status(500).json({ error: err.message || 'group details failed' });
857
+ }
858
+ });
859
+ app.get('/api/groups/:name/contracts', async (req, res) => {
860
+ try {
861
+ const params = { name: req.params.name };
862
+ if (typeof req.query.type === 'string')
863
+ params.type = req.query.type;
864
+ if (typeof req.query.repo === 'string')
865
+ params.repo = req.query.repo;
866
+ if (req.query.unmatchedOnly === 'true')
867
+ params.unmatchedOnly = true;
868
+ const result = await getGroupSvc().groupContracts(params);
869
+ const e = result.error;
870
+ if (e) {
871
+ res.status(e.toLowerCase().includes('not found') ? 404 : 400).json(result);
872
+ return;
873
+ }
874
+ res.json(result);
875
+ }
876
+ catch (err) {
877
+ res.status(500).json({ error: err.message || 'group contracts failed' });
878
+ }
879
+ });
880
+ app.get('/api/groups/:name/status', async (req, res) => {
881
+ try {
882
+ const yaml = await backend.readGroupStatusResource(req.params.name);
883
+ res.type('text/yaml').send(yaml);
884
+ }
885
+ catch (err) {
886
+ res.status(500).json({ error: err.message || 'group status failed' });
887
+ }
888
+ });
889
+ // Get full graph
890
+ app.get('/api/graph', async (req, res) => {
891
+ try {
892
+ const entry = await resolveRepo(requestedRepo(req));
893
+ if (!entry) {
894
+ res.status(404).json({ error: 'Repository not found' });
895
+ return;
896
+ }
897
+ const lbugPath = path.join(entry.storagePath, 'lbug');
898
+ const includeContent = req.query.includeContent === 'true';
899
+ const stream = req.query.stream === 'true';
900
+ // Guard: when a repo has no materialized lbug schema (fixture-
901
+ // seeded CAS-only repos), or the lbug WAL is corrupt/stale from
902
+ // a prior failed analyze, LadybugDB native will abort with
903
+ // UNREACHABLE_CODE or an ANY-vector exception. Detect both
904
+ // shapes — missing file (cheap fs.access) AND empty/4096-byte
905
+ // schema-only file (fs.stat) — and return an empty graph so
906
+ // the dashboard doesn't blow up. The Graph tab keeps working
907
+ // for repos that actually have a real lbug.
908
+ const isLbugMaterialized = await (async () => {
909
+ try {
910
+ const stat = await fs.stat(lbugPath);
911
+ // Schema-only lbug is exactly 4096 bytes (one page, no real
912
+ // data). Real graphs are larger.
913
+ return stat.isFile() && stat.size > 4096;
914
+ }
915
+ catch {
916
+ return false;
917
+ }
918
+ })();
919
+ if (!isLbugMaterialized) {
920
+ if (stream) {
921
+ res.setHeader('Content-Type', 'application/x-ndjson; charset=utf-8');
922
+ res.flushHeaders();
923
+ res.write(JSON.stringify({
924
+ type: 'meta',
925
+ repoName: entry.name,
926
+ note: 'no lbug file — graph not yet materialized',
927
+ nodeCount: 0,
928
+ relationshipCount: 0,
929
+ }) + '\n');
930
+ res.end();
931
+ return;
932
+ }
933
+ res.json({
934
+ repoName: entry.name,
935
+ repoPath: entry.path,
936
+ nodes: [],
937
+ relationships: [],
938
+ stats: { nodes: 0, edges: 0 },
939
+ note: 'no lbug file — graph not yet materialized',
940
+ });
941
+ return;
942
+ }
943
+ if (stream) {
944
+ const abortController = new AbortController();
945
+ let responseFinished = false;
946
+ const markFinished = () => {
947
+ responseFinished = true;
948
+ };
949
+ const abortStreaming = () => {
950
+ if (!responseFinished) {
951
+ abortController.abort();
952
+ }
953
+ };
954
+ res.setHeader('Content-Type', 'application/x-ndjson; charset=utf-8');
955
+ res.setHeader('Cache-Control', 'no-cache');
956
+ res.flushHeaders();
957
+ req.once('aborted', abortStreaming);
958
+ res.once('finish', markFinished);
959
+ res.once('close', abortStreaming);
960
+ try {
961
+ await withLbugDb(lbugPath, async () => streamGraphNdjson(res, includeContent, abortController.signal));
962
+ if (!abortController.signal.aborted && !res.writableEnded) {
963
+ res.end();
964
+ }
965
+ }
966
+ finally {
967
+ req.off('aborted', abortStreaming);
968
+ res.off('finish', markFinished);
969
+ res.off('close', abortStreaming);
970
+ }
971
+ return;
972
+ }
973
+ const graph = await withLbugDb(lbugPath, async () => buildGraph(includeContent));
974
+ res.json(graph);
975
+ }
976
+ catch (err) {
977
+ if (err instanceof ClientDisconnectedError) {
978
+ return;
979
+ }
980
+ const message = err.message || 'Failed to build graph';
981
+ if (res.headersSent) {
982
+ try {
983
+ res.write(JSON.stringify({ type: 'error', error: message }) + '\n');
984
+ }
985
+ catch {
986
+ // Best-effort only after streaming has started.
987
+ }
988
+ res.end();
989
+ return;
990
+ }
991
+ res.status(500).json({ error: message });
992
+ }
993
+ });
994
+ // Execute Cypher query
995
+ app.post('/api/query', async (req, res) => {
996
+ try {
997
+ const cypher = req.body.cypher;
998
+ if (!cypher) {
999
+ res.status(400).json({ error: 'Missing "cypher" in request body' });
1000
+ return;
1001
+ }
1002
+ if (isWriteQuery(cypher)) {
1003
+ res.status(403).json({ error: 'Write queries are not allowed via the HTTP API' });
1004
+ return;
1005
+ }
1006
+ const entry = await resolveRepo(requestedRepo(req));
1007
+ if (!entry) {
1008
+ res.status(404).json({ error: 'Repository not found' });
1009
+ return;
1010
+ }
1011
+ const lbugPath = path.join(entry.storagePath, 'lbug');
1012
+ const result = await withLbugDb(lbugPath, () => executeQuery(cypher));
1013
+ res.json({ result });
1014
+ }
1015
+ catch (err) {
1016
+ res.status(500).json({ error: err.message || 'Query failed' });
1017
+ }
1018
+ });
1019
+ // Search (supports mode: 'hybrid' | 'semantic' | 'bm25', and optional enrichment)
1020
+ app.post('/api/search', async (req, res) => {
1021
+ try {
1022
+ const query = (req.body.query ?? '').trim();
1023
+ if (!query) {
1024
+ res.status(400).json({ error: 'Missing "query" in request body' });
1025
+ return;
1026
+ }
1027
+ const entry = await resolveRepo(requestedRepo(req));
1028
+ if (!entry) {
1029
+ res.status(404).json({ error: 'Repository not found' });
1030
+ return;
1031
+ }
1032
+ const lbugPath = path.join(entry.storagePath, 'lbug');
1033
+ const parsedLimit = Number(req.body.limit ?? 10);
1034
+ const limit = Number.isFinite(parsedLimit)
1035
+ ? Math.max(1, Math.min(100, Math.trunc(parsedLimit)))
1036
+ : 10;
1037
+ const mode = req.body.mode ?? 'hybrid';
1038
+ const enrich = req.body.enrich !== false; // default true
1039
+ const results = await withLbugDb(lbugPath, async () => {
1040
+ let searchResults;
1041
+ if (mode === 'semantic') {
1042
+ const { isEmbedderReady } = await import('../core/embeddings/embedder.js');
1043
+ if (!isEmbedderReady()) {
1044
+ return [];
1045
+ }
1046
+ const { semanticSearch: semSearch } = await import('../core/embeddings/embedding-pipeline.js');
1047
+ searchResults = await semSearch(executeQuery, query, limit);
1048
+ // Normalize semantic results to HybridSearchResult shape
1049
+ searchResults = searchResults.map((r, i) => ({
1050
+ ...r,
1051
+ score: r.score ?? 1 - (r.distance ?? 0),
1052
+ rank: i + 1,
1053
+ sources: ['semantic'],
1054
+ }));
1055
+ }
1056
+ else if (mode === 'bm25') {
1057
+ searchResults = await searchFTSFromLbug(query, limit);
1058
+ searchResults = searchResults.map((r, i) => ({
1059
+ ...r,
1060
+ rank: i + 1,
1061
+ sources: ['bm25'],
1062
+ }));
1063
+ }
1064
+ else {
1065
+ // hybrid (default)
1066
+ const { isEmbedderReady } = await import('../core/embeddings/embedder.js');
1067
+ if (isEmbedderReady()) {
1068
+ const { semanticSearch: semSearch } = await import('../core/embeddings/embedding-pipeline.js');
1069
+ searchResults = await hybridSearch(query, limit, executeQuery, semSearch);
1070
+ }
1071
+ else {
1072
+ searchResults = await searchFTSFromLbug(query, limit);
1073
+ }
1074
+ }
1075
+ if (!enrich)
1076
+ return searchResults;
1077
+ // Server-side enrichment: add connections, cluster, processes per result
1078
+ // Uses parameterized queries to prevent Cypher injection via nodeId
1079
+ const validLabel = (label) => NODE_TABLES.includes(label);
1080
+ const enriched = await Promise.all(searchResults.slice(0, limit).map(async (r) => {
1081
+ const nodeId = r.nodeId || r.id || '';
1082
+ const nodeLabel = nodeId.split(':')[0];
1083
+ const enrichment = {};
1084
+ if (!nodeId || !validLabel(nodeLabel))
1085
+ return { ...r, ...enrichment };
1086
+ // Run connections, cluster, and process queries in parallel
1087
+ // Label is validated against NODE_TABLES (compile-time safe identifiers);
1088
+ // nodeId uses $nid parameter binding to prevent injection
1089
+ const [connRes, clusterRes, procRes] = await Promise.all([
1090
+ executePrepared(`
1091
+ MATCH (n:${nodeLabel} {id: $nid})
1092
+ OPTIONAL MATCH (n)-[r1:CodeRelation]->(dst)
1093
+ OPTIONAL MATCH (src)-[r2:CodeRelation]->(n)
1094
+ RETURN
1095
+ collect(DISTINCT {name: dst.name, type: r1.type, confidence: r1.confidence}) AS outgoing,
1096
+ collect(DISTINCT {name: src.name, type: r2.type, confidence: r2.confidence}) AS incoming
1097
+ LIMIT 1
1098
+ `, { nid: nodeId }).catch(() => []),
1099
+ executePrepared(`
1100
+ MATCH (n:${nodeLabel} {id: $nid})
1101
+ MATCH (n)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
1102
+ RETURN c.label AS label, c.description AS description
1103
+ LIMIT 1
1104
+ `, { nid: nodeId }).catch(() => []),
1105
+ executePrepared(`
1106
+ MATCH (n:${nodeLabel} {id: $nid})
1107
+ MATCH (n)-[rel:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
1108
+ RETURN p.id AS id, p.label AS label, rel.step AS step, p.stepCount AS stepCount
1109
+ ORDER BY rel.step
1110
+ `, { nid: nodeId }).catch(() => []),
1111
+ ]);
1112
+ if (connRes.length > 0) {
1113
+ const row = connRes[0];
1114
+ const outgoing = (Array.isArray(row) ? row[0] : row.outgoing || [])
1115
+ .filter((c) => c?.name)
1116
+ .slice(0, 5);
1117
+ const incoming = (Array.isArray(row) ? row[1] : row.incoming || [])
1118
+ .filter((c) => c?.name)
1119
+ .slice(0, 5);
1120
+ enrichment.connections = { outgoing, incoming };
1121
+ }
1122
+ if (clusterRes.length > 0) {
1123
+ const row = clusterRes[0];
1124
+ enrichment.cluster = Array.isArray(row) ? row[0] : row.label;
1125
+ }
1126
+ if (procRes.length > 0) {
1127
+ enrichment.processes = procRes
1128
+ .map((row) => ({
1129
+ id: Array.isArray(row) ? row[0] : row.id,
1130
+ label: Array.isArray(row) ? row[1] : row.label,
1131
+ step: Array.isArray(row) ? row[2] : row.step,
1132
+ stepCount: Array.isArray(row) ? row[3] : row.stepCount,
1133
+ }))
1134
+ .filter((p) => p.id && p.label);
1135
+ }
1136
+ return { ...r, ...enrichment };
1137
+ }));
1138
+ return enriched;
1139
+ });
1140
+ res.json({ results });
1141
+ }
1142
+ catch (err) {
1143
+ res.status(500).json({ error: err.message || 'Search failed' });
1144
+ }
1145
+ });
1146
+ // Read file — with path traversal guard
1147
+ app.get('/api/file', async (req, res) => {
1148
+ try {
1149
+ const entry = await resolveRepo(requestedRepo(req));
1150
+ if (!entry) {
1151
+ res.status(404).json({ error: 'Repository not found' });
1152
+ return;
1153
+ }
1154
+ const filePath = req.query.path;
1155
+ if (!filePath) {
1156
+ res.status(400).json({ error: 'Missing path' });
1157
+ return;
1158
+ }
1159
+ // Prevent path traversal — resolve and verify the path stays within the repo root
1160
+ const repoRoot = path.resolve(entry.path);
1161
+ const fullPath = path.resolve(repoRoot, filePath);
1162
+ if (!fullPath.startsWith(repoRoot + path.sep) && fullPath !== repoRoot) {
1163
+ res.status(403).json({ error: 'Path traversal denied' });
1164
+ return;
1165
+ }
1166
+ const raw = await fs.readFile(fullPath, 'utf-8');
1167
+ // Optional line-range support: ?startLine=10&endLine=50
1168
+ // Returns only the requested slice (0-indexed), plus metadata.
1169
+ const startLine = req.query.startLine !== undefined ? Number(req.query.startLine) : undefined;
1170
+ const endLine = req.query.endLine !== undefined ? Number(req.query.endLine) : undefined;
1171
+ if (startLine !== undefined && Number.isFinite(startLine)) {
1172
+ const lines = raw.split('\n');
1173
+ const start = Math.max(0, startLine);
1174
+ const end = endLine !== undefined && Number.isFinite(endLine)
1175
+ ? Math.min(lines.length, endLine + 1)
1176
+ : lines.length;
1177
+ res.json({
1178
+ content: lines.slice(start, end).join('\n'),
1179
+ startLine: start,
1180
+ endLine: end - 1,
1181
+ totalLines: lines.length,
1182
+ });
1183
+ }
1184
+ else {
1185
+ res.json({ content: raw, totalLines: raw.split('\n').length });
1186
+ }
1187
+ }
1188
+ catch (err) {
1189
+ if (err.code === 'ENOENT') {
1190
+ res.status(404).json({ error: 'File not found' });
1191
+ }
1192
+ else {
1193
+ res.status(500).json({ error: err.message || 'Failed to read file' });
1194
+ }
1195
+ }
1196
+ });
1197
+ // Grep — regex search across file contents in the indexed repo
1198
+ // Uses filesystem-based search for memory efficiency (never loads all files into memory)
1199
+ app.get('/api/grep', async (req, res) => {
1200
+ try {
1201
+ const entry = await resolveRepo(requestedRepo(req));
1202
+ if (!entry) {
1203
+ res.status(404).json({ error: 'Repository not found' });
1204
+ return;
1205
+ }
1206
+ const pattern = req.query.pattern;
1207
+ if (!pattern) {
1208
+ res.status(400).json({ error: 'Missing "pattern" query parameter' });
1209
+ return;
1210
+ }
1211
+ // ReDoS protection: reject overly long or dangerous patterns
1212
+ if (pattern.length > 200) {
1213
+ res.status(400).json({ error: 'Pattern too long (max 200 characters)' });
1214
+ return;
1215
+ }
1216
+ // Validate regex syntax
1217
+ let regex;
1218
+ try {
1219
+ regex = new RegExp(pattern, 'gim');
1220
+ }
1221
+ catch {
1222
+ res.status(400).json({ error: 'Invalid regex pattern' });
1223
+ return;
1224
+ }
1225
+ const parsedLimit = Number(req.query.limit ?? 50);
1226
+ const limit = Number.isFinite(parsedLimit)
1227
+ ? Math.max(1, Math.min(200, Math.trunc(parsedLimit)))
1228
+ : 50;
1229
+ const results = [];
1230
+ const repoRoot = path.resolve(entry.path);
1231
+ // Get file paths from the graph (lightweight — no content loaded)
1232
+ const lbugPath = path.join(entry.storagePath, 'lbug');
1233
+ const fileRows = await withLbugDb(lbugPath, () => executeQuery(`MATCH (n:File) WHERE n.content IS NOT NULL RETURN n.filePath AS filePath`));
1234
+ // Search files on disk one at a time (constant memory)
1235
+ for (const row of fileRows) {
1236
+ if (results.length >= limit)
1237
+ break;
1238
+ const filePath = row.filePath || '';
1239
+ const fullPath = path.resolve(repoRoot, filePath);
1240
+ // Path traversal guard
1241
+ if (!fullPath.startsWith(repoRoot + path.sep) && fullPath !== repoRoot)
1242
+ continue;
1243
+ let content;
1244
+ try {
1245
+ content = await fs.readFile(fullPath, 'utf-8');
1246
+ }
1247
+ catch {
1248
+ continue; // File may have been deleted since indexing
1249
+ }
1250
+ const lines = content.split('\n');
1251
+ for (let i = 0; i < lines.length; i++) {
1252
+ if (results.length >= limit)
1253
+ break;
1254
+ if (regex.test(lines[i])) {
1255
+ results.push({ filePath, line: i + 1, text: lines[i].trim().slice(0, 200) });
1256
+ }
1257
+ regex.lastIndex = 0;
1258
+ }
1259
+ }
1260
+ res.json({ results });
1261
+ }
1262
+ catch (err) {
1263
+ res.status(500).json({ error: err.message || 'Grep failed' });
1264
+ }
1265
+ });
1266
+ // List all processes
1267
+ app.get('/api/processes', async (req, res) => {
1268
+ try {
1269
+ const result = await backend.queryProcesses(requestedRepo(req));
1270
+ res.json(result);
1271
+ }
1272
+ catch (err) {
1273
+ res.status(statusFromError(err)).json({ error: err.message || 'Failed to query processes' });
1274
+ }
1275
+ });
1276
+ // Process detail
1277
+ app.get('/api/process', async (req, res) => {
1278
+ try {
1279
+ const name = String(req.query.name ?? '').trim();
1280
+ if (!name) {
1281
+ res.status(400).json({ error: 'Missing "name" query parameter' });
1282
+ return;
1283
+ }
1284
+ const result = await backend.queryProcessDetail(name, requestedRepo(req));
1285
+ if (result?.error) {
1286
+ res.status(404).json({ error: result.error });
1287
+ return;
1288
+ }
1289
+ res.json(result);
1290
+ }
1291
+ catch (err) {
1292
+ res
1293
+ .status(statusFromError(err))
1294
+ .json({ error: err.message || 'Failed to query process detail' });
1295
+ }
1296
+ });
1297
+ // List all clusters
1298
+ app.get('/api/clusters', async (req, res) => {
1299
+ try {
1300
+ const result = await backend.queryClusters(requestedRepo(req));
1301
+ res.json(result);
1302
+ }
1303
+ catch (err) {
1304
+ res.status(statusFromError(err)).json({ error: err.message || 'Failed to query clusters' });
1305
+ }
1306
+ });
1307
+ // Cluster detail
1308
+ app.get('/api/cluster', async (req, res) => {
1309
+ try {
1310
+ const name = String(req.query.name ?? '').trim();
1311
+ if (!name) {
1312
+ res.status(400).json({ error: 'Missing "name" query parameter' });
1313
+ return;
1314
+ }
1315
+ const result = await backend.queryClusterDetail(name, requestedRepo(req));
1316
+ if (result?.error) {
1317
+ res.status(404).json({ error: result.error });
1318
+ return;
1319
+ }
1320
+ res.json(result);
1321
+ }
1322
+ catch (err) {
1323
+ res
1324
+ .status(statusFromError(err))
1325
+ .json({ error: err.message || 'Failed to query cluster detail' });
1326
+ }
1327
+ });
1328
+ // ── Analyze API ──────────────────────────────────────────────────────
1329
+ // POST /api/analyze — start a new analysis job
1330
+ app.post('/api/analyze', async (req, res) => {
1331
+ try {
1332
+ const { url: repoUrl, path: repoLocalPath, force, embeddings } = req.body;
1333
+ // Input type validation
1334
+ if (repoUrl !== undefined && typeof repoUrl !== 'string') {
1335
+ res.status(400).json({ error: '"url" must be a string' });
1336
+ return;
1337
+ }
1338
+ if (repoLocalPath !== undefined && typeof repoLocalPath !== 'string') {
1339
+ res.status(400).json({ error: '"path" must be a string' });
1340
+ return;
1341
+ }
1342
+ if (!repoUrl && !repoLocalPath) {
1343
+ res.status(400).json({ error: 'Provide "url" (git URL) or "path" (local path)' });
1344
+ return;
1345
+ }
1346
+ // Path validation: require absolute path, reject traversal (e.g. /tmp/../etc/passwd)
1347
+ if (repoLocalPath) {
1348
+ if (!path.isAbsolute(repoLocalPath)) {
1349
+ res.status(400).json({ error: '"path" must be an absolute path' });
1350
+ return;
1351
+ }
1352
+ if (path.normalize(repoLocalPath) !== path.resolve(repoLocalPath)) {
1353
+ res.status(400).json({ error: '"path" must not contain traversal sequences' });
1354
+ return;
1355
+ }
1356
+ }
1357
+ const job = jobManager.createJob({ repoUrl, repoPath: repoLocalPath });
1358
+ // If job was already running (dedup), just return its id
1359
+ if (job.status !== 'queued') {
1360
+ res.status(202).json({ jobId: job.id, status: job.status });
1361
+ return;
1362
+ }
1363
+ // Mark as active synchronously to prevent race with concurrent requests
1364
+ jobManager.updateJob(job.id, { status: 'cloning' });
1365
+ // Start async work — don't await
1366
+ (async () => {
1367
+ let targetPath = repoLocalPath;
1368
+ try {
1369
+ // Clone if URL provided
1370
+ if (repoUrl && !repoLocalPath) {
1371
+ const repoName = extractRepoName(repoUrl);
1372
+ targetPath = getCloneDir(repoName);
1373
+ jobManager.updateJob(job.id, {
1374
+ status: 'cloning',
1375
+ repoName,
1376
+ progress: { phase: 'cloning', percent: 0, message: `Cloning ${repoUrl}...` },
1377
+ });
1378
+ await cloneOrPull(repoUrl, targetPath, (progress) => {
1379
+ jobManager.updateJob(job.id, {
1380
+ progress: { phase: progress.phase, percent: 5, message: progress.message },
1381
+ });
1382
+ });
1383
+ }
1384
+ if (!targetPath) {
1385
+ throw new Error('No target path resolved');
1386
+ }
1387
+ // Acquire shared repo lock (keyed on storagePath to match embed handler)
1388
+ const analyzeLockKey = getStoragePath(targetPath);
1389
+ const lockErr = acquireRepoLock(analyzeLockKey);
1390
+ if (lockErr) {
1391
+ jobManager.updateJob(job.id, { status: 'failed', error: lockErr });
1392
+ return;
1393
+ }
1394
+ jobManager.updateJob(job.id, { repoPath: targetPath, status: 'analyzing' });
1395
+ // ── Worker fork with auto-retry ──────────────────────────────
1396
+ //
1397
+ // Forks a child process with 8GB heap. If the worker crashes
1398
+ // (OOM, native addon segfault, etc.), it retries up to
1399
+ // MAX_WORKER_RETRIES times with exponential backoff before
1400
+ // marking the job as permanently failed.
1401
+ //
1402
+ // In dev mode (tsx), registers the tsx ESM hook via a file://
1403
+ // URL so the child can compile TypeScript on-the-fly.
1404
+ const MAX_WORKER_RETRIES = 2;
1405
+ const callerPath = fileURLToPath(import.meta.url);
1406
+ const isDev = callerPath.endsWith('.ts');
1407
+ const workerFile = isDev ? 'analyze-worker.ts' : 'analyze-worker.js';
1408
+ const workerPath = path.join(path.dirname(callerPath), workerFile);
1409
+ const tsxHookArgs = isDev
1410
+ ? ['--import', pathToFileURL(_require.resolve('tsx/esm')).href]
1411
+ : [];
1412
+ const forkWorker = () => {
1413
+ const currentJob = jobManager.getJob(job.id);
1414
+ if (!currentJob || currentJob.status === 'complete' || currentJob.status === 'failed')
1415
+ return;
1416
+ const child = fork(workerPath, [], {
1417
+ execArgv: [...tsxHookArgs, '--max-old-space-size=8192'],
1418
+ stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
1419
+ });
1420
+ // Capture stderr for crash diagnostics
1421
+ let stderrChunks = '';
1422
+ child.stderr?.on('data', (chunk) => {
1423
+ stderrChunks += chunk.toString();
1424
+ if (stderrChunks.length > 4096)
1425
+ stderrChunks = stderrChunks.slice(-4096);
1426
+ });
1427
+ child.on('message', (msg) => {
1428
+ if (msg.type === 'progress') {
1429
+ jobManager.updateJob(job.id, {
1430
+ status: 'analyzing',
1431
+ progress: { phase: msg.phase, percent: msg.percent, message: msg.message },
1432
+ });
1433
+ }
1434
+ else if (msg.type === 'complete') {
1435
+ releaseRepoLock(analyzeLockKey);
1436
+ // Reinitialize backend BEFORE marking complete — ensures the new
1437
+ // repo is queryable when the client receives the SSE complete event.
1438
+ backend
1439
+ .init()
1440
+ .then(() => {
1441
+ jobManager.updateJob(job.id, {
1442
+ status: 'complete',
1443
+ repoName: msg.result.repoName,
1444
+ });
1445
+ })
1446
+ .catch((err) => {
1447
+ console.error('backend.init() failed after analyze:', err);
1448
+ jobManager.updateJob(job.id, {
1449
+ status: 'failed',
1450
+ error: 'Server failed to reload after analysis. Try again.',
1451
+ });
1452
+ });
1453
+ }
1454
+ else if (msg.type === 'error') {
1455
+ releaseRepoLock(analyzeLockKey);
1456
+ jobManager.updateJob(job.id, {
1457
+ status: 'failed',
1458
+ error: msg.message,
1459
+ });
1460
+ }
1461
+ });
1462
+ child.on('error', (err) => {
1463
+ releaseRepoLock(analyzeLockKey);
1464
+ jobManager.updateJob(job.id, {
1465
+ status: 'failed',
1466
+ error: `Worker process error: ${err.message}`,
1467
+ });
1468
+ });
1469
+ child.on('exit', (code) => {
1470
+ const j = jobManager.getJob(job.id);
1471
+ if (!j || j.status === 'complete' || j.status === 'failed')
1472
+ return;
1473
+ // Worker crashed — attempt retry if under the limit
1474
+ if (j.retryCount < MAX_WORKER_RETRIES) {
1475
+ j.retryCount++;
1476
+ const delay = 1000 * Math.pow(2, j.retryCount - 1); // 1s, 2s
1477
+ const lastErr = stderrChunks.trim().split('\n').pop() || '';
1478
+ console.warn(`Analyze worker crashed (code ${code}), retry ${j.retryCount}/${MAX_WORKER_RETRIES} in ${delay}ms` +
1479
+ (lastErr ? `: ${lastErr}` : ''));
1480
+ jobManager.updateJob(job.id, {
1481
+ status: 'analyzing',
1482
+ progress: {
1483
+ phase: 'retrying',
1484
+ percent: j.progress.percent,
1485
+ message: `Worker crashed, retrying (${j.retryCount}/${MAX_WORKER_RETRIES})...`,
1486
+ },
1487
+ });
1488
+ stderrChunks = '';
1489
+ setTimeout(forkWorker, delay);
1490
+ }
1491
+ else {
1492
+ // Exhausted retries — permanent failure
1493
+ releaseRepoLock(analyzeLockKey);
1494
+ jobManager.updateJob(job.id, {
1495
+ status: 'failed',
1496
+ error: `Worker crashed ${MAX_WORKER_RETRIES + 1} times (code ${code})${stderrChunks ? ': ' + stderrChunks.trim().split('\n').pop() : ''}`,
1497
+ });
1498
+ }
1499
+ });
1500
+ // Register child for cancellation + timeout tracking
1501
+ jobManager.registerChild(job.id, child);
1502
+ // Send start command to child
1503
+ child.send({
1504
+ type: 'start',
1505
+ repoPath: targetPath,
1506
+ options: { force: !!force, embeddings: !!embeddings },
1507
+ });
1508
+ };
1509
+ forkWorker();
1510
+ }
1511
+ catch (err) {
1512
+ if (targetPath)
1513
+ releaseRepoLock(getStoragePath(targetPath));
1514
+ jobManager.updateJob(job.id, {
1515
+ status: 'failed',
1516
+ error: err.message || 'Analysis failed',
1517
+ });
1518
+ }
1519
+ })();
1520
+ res.status(202).json({ jobId: job.id, status: job.status });
1521
+ }
1522
+ catch (err) {
1523
+ if (err.message?.includes('already in progress')) {
1524
+ res.status(409).json({ error: err.message });
1525
+ }
1526
+ else {
1527
+ res.status(500).json({ error: err.message || 'Failed to start analysis' });
1528
+ }
1529
+ }
1530
+ });
1531
+ // GET /api/analyze/:jobId — poll job status
1532
+ app.get('/api/analyze/:jobId', (req, res) => {
1533
+ const job = jobManager.getJob(req.params.jobId);
1534
+ if (!job) {
1535
+ res.status(404).json({ error: 'Job not found' });
1536
+ return;
1537
+ }
1538
+ res.json({
1539
+ id: job.id,
1540
+ status: job.status,
1541
+ repoUrl: job.repoUrl,
1542
+ repoPath: job.repoPath,
1543
+ repoName: job.repoName,
1544
+ progress: job.progress,
1545
+ error: job.error,
1546
+ startedAt: job.startedAt,
1547
+ completedAt: job.completedAt,
1548
+ });
1549
+ });
1550
+ // GET /api/analyze/:jobId/progress — SSE stream (shared helper)
1551
+ mountSSEProgress(app, '/api/analyze/:jobId/progress', jobManager);
1552
+ // DELETE /api/analyze/:jobId — cancel a running analysis job
1553
+ app.delete('/api/analyze/:jobId', (req, res) => {
1554
+ const job = jobManager.getJob(req.params.jobId);
1555
+ if (!job) {
1556
+ res.status(404).json({ error: 'Job not found' });
1557
+ return;
1558
+ }
1559
+ if (job.status === 'complete' || job.status === 'failed') {
1560
+ res.status(400).json({ error: `Job already ${job.status}` });
1561
+ return;
1562
+ }
1563
+ jobManager.cancelJob(req.params.jobId, 'Cancelled by user');
1564
+ res.json({ id: job.id, status: 'failed', error: 'Cancelled by user' });
1565
+ });
1566
+ // ── Embedding endpoints ────────────────────────────────────────────
1567
+ const embedJobManager = new JobManager();
1568
+ // POST /api/embed — trigger server-side embedding generation
1569
+ app.post('/api/embed', async (req, res) => {
1570
+ try {
1571
+ const entry = await resolveRepo(requestedRepo(req));
1572
+ if (!entry) {
1573
+ res.status(404).json({ error: 'Repository not found' });
1574
+ return;
1575
+ }
1576
+ // Check shared repo lock — prevent concurrent analyze + embed on same repo
1577
+ const repoLockPath = entry.storagePath;
1578
+ const lockErr = acquireRepoLock(repoLockPath);
1579
+ if (lockErr) {
1580
+ res.status(409).json({ error: lockErr });
1581
+ return;
1582
+ }
1583
+ const job = embedJobManager.createJob({ repoPath: entry.storagePath });
1584
+ embedJobManager.updateJob(job.id, {
1585
+ repoName: entry.name,
1586
+ status: 'analyzing',
1587
+ progress: { phase: 'analyzing', percent: 0, message: 'Starting embedding generation...' },
1588
+ });
1589
+ // 30-minute timeout for embedding jobs (same as analyze jobs)
1590
+ const EMBED_TIMEOUT_MS = 30 * 60 * 1000;
1591
+ const embedTimeout = setTimeout(() => {
1592
+ const current = embedJobManager.getJob(job.id);
1593
+ if (current && current.status !== 'complete' && current.status !== 'failed') {
1594
+ releaseRepoLock(repoLockPath);
1595
+ embedJobManager.updateJob(job.id, {
1596
+ status: 'failed',
1597
+ error: 'Embedding timed out (30 minute limit)',
1598
+ });
1599
+ }
1600
+ }, EMBED_TIMEOUT_MS);
1601
+ // Run embedding pipeline asynchronously
1602
+ (async () => {
1603
+ try {
1604
+ const lbugPath = path.join(entry.storagePath, 'lbug');
1605
+ await withLbugDb(lbugPath, async () => {
1606
+ const { runEmbeddingPipeline } = await import('../core/embeddings/embedding-pipeline.js');
1607
+ // Fetch existing content hashes for incremental embedding.
1608
+ // Delegated to lbug-adapter which owns the DB query logic and legacy-fallback handling.
1609
+ const { fetchExistingEmbeddingHashes } = await import('../core/lbug/lbug-adapter.js');
1610
+ const existingEmbeddings = await fetchExistingEmbeddingHashes(executeQuery);
1611
+ if (existingEmbeddings && existingEmbeddings.size > 0) {
1612
+ console.log(`[embed] ${existingEmbeddings.size} nodes already embedded — incremental run with content-hash comparison`);
1613
+ }
1614
+ await runEmbeddingPipeline(executeQuery, executeWithReusedStatement, (p) => {
1615
+ embedJobManager.updateJob(job.id, {
1616
+ progress: {
1617
+ phase: p.phase === 'ready' ? 'complete' : p.phase === 'error' ? 'failed' : p.phase,
1618
+ percent: p.percent,
1619
+ message: p.phase === 'loading-model'
1620
+ ? 'Loading embedding model...'
1621
+ : p.phase === 'embedding'
1622
+ ? `Embedding nodes (${p.percent}%)...`
1623
+ : p.phase === 'indexing'
1624
+ ? 'Creating vector index...'
1625
+ : p.phase === 'ready'
1626
+ ? 'Embeddings complete'
1627
+ : `${p.phase} (${p.percent}%)`,
1628
+ },
1629
+ });
1630
+ }, {}, // config: use defaults
1631
+ undefined, // skipNodeIds
1632
+ undefined, // context
1633
+ existingEmbeddings);
1634
+ });
1635
+ clearTimeout(embedTimeout);
1636
+ releaseRepoLock(repoLockPath);
1637
+ // Don't overwrite 'failed' if the job was cancelled while the pipeline was running
1638
+ const current = embedJobManager.getJob(job.id);
1639
+ if (!current || current.status !== 'failed') {
1640
+ embedJobManager.updateJob(job.id, { status: 'complete' });
1641
+ }
1642
+ }
1643
+ catch (err) {
1644
+ clearTimeout(embedTimeout);
1645
+ releaseRepoLock(repoLockPath);
1646
+ const current = embedJobManager.getJob(job.id);
1647
+ if (!current || current.status !== 'failed') {
1648
+ embedJobManager.updateJob(job.id, {
1649
+ status: 'failed',
1650
+ error: err.message || 'Embedding generation failed',
1651
+ });
1652
+ }
1653
+ }
1654
+ })();
1655
+ res.status(202).json({ jobId: job.id, status: 'analyzing' });
1656
+ }
1657
+ catch (err) {
1658
+ if (err.message?.includes('already in progress')) {
1659
+ res.status(409).json({ error: err.message });
1660
+ }
1661
+ else {
1662
+ res.status(500).json({ error: err.message || 'Failed to start embedding generation' });
1663
+ }
1664
+ }
1665
+ });
1666
+ // GET /api/embed/:jobId — poll embedding job status
1667
+ app.get('/api/embed/:jobId', (req, res) => {
1668
+ const job = embedJobManager.getJob(req.params.jobId);
1669
+ if (!job) {
1670
+ res.status(404).json({ error: 'Job not found' });
1671
+ return;
1672
+ }
1673
+ res.json({
1674
+ id: job.id,
1675
+ status: job.status,
1676
+ repoName: job.repoName,
1677
+ progress: job.progress,
1678
+ error: job.error,
1679
+ startedAt: job.startedAt,
1680
+ completedAt: job.completedAt,
1681
+ });
1682
+ });
1683
+ // GET /api/embed/:jobId/progress — SSE stream (shared helper)
1684
+ mountSSEProgress(app, '/api/embed/:jobId/progress', embedJobManager);
1685
+ // DELETE /api/embed/:jobId — cancel embedding job
1686
+ app.delete('/api/embed/:jobId', (req, res) => {
1687
+ const job = embedJobManager.getJob(req.params.jobId);
1688
+ if (!job) {
1689
+ res.status(404).json({ error: 'Job not found' });
1690
+ return;
1691
+ }
1692
+ if (job.status === 'complete' || job.status === 'failed') {
1693
+ res.status(400).json({ error: `Job already ${job.status}` });
1694
+ return;
1695
+ }
1696
+ embedJobManager.cancelJob(req.params.jobId, 'Cancelled by user');
1697
+ res.json({ id: job.id, status: 'failed', error: 'Cancelled by user' });
1698
+ });
1699
+ // Global error handler — catch anything the route handlers miss
1700
+ app.use((err, _req, res, _next) => {
1701
+ console.error('Unhandled error:', err);
1702
+ res.status(500).json({ error: 'Internal server error' });
1703
+ });
1704
+ // Wrap listen in a promise so errors (EADDRINUSE, EACCES, etc.) propagate
1705
+ // to the caller instead of crashing with an unhandled 'error' event.
1706
+ await new Promise((resolve, reject) => {
1707
+ const server = app.listen(port, host, () => {
1708
+ const displayHost = host === '::' || host === '0.0.0.0' ? 'localhost' : host;
1709
+ console.log(`CodraGraph server running on http://${displayHost}:${port}`);
1710
+ resolve();
1711
+ });
1712
+ server.on('error', (err) => reject(err));
1713
+ // Graceful shutdown — close Express + LadybugDB cleanly
1714
+ const shutdown = async () => {
1715
+ console.log('\nShutting down...');
1716
+ server.close();
1717
+ jobManager.dispose();
1718
+ embedJobManager.dispose();
1719
+ await cleanupMcp();
1720
+ await closeLbug();
1721
+ await backend.disconnect();
1722
+ process.exit(0);
1723
+ };
1724
+ process.once('SIGINT', shutdown);
1725
+ process.once('SIGTERM', shutdown);
1726
+ });
1727
+ };