gitnexus 1.6.8-rc.8 → 1.6.8

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 (439) hide show
  1. package/README.md +3 -3
  2. package/dist/_shared/graph/types.d.ts +31 -1
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/language-detection.d.ts.map +1 -1
  5. package/dist/_shared/language-detection.js +11 -1
  6. package/dist/_shared/language-detection.js.map +1 -1
  7. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  8. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  9. package/dist/_shared/lbug/schema-constants.js +6 -0
  10. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  11. package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
  12. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
  13. package/dist/_shared/scope-resolution/symbol-definition.d.ts +6 -0
  14. package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
  15. package/dist/cli/ai-context.d.ts +31 -1
  16. package/dist/cli/ai-context.js +18 -16
  17. package/dist/cli/analyze-config.js +11 -0
  18. package/dist/cli/analyze.d.ts +14 -0
  19. package/dist/cli/analyze.js +98 -0
  20. package/dist/cli/embedding-dims.d.ts +33 -0
  21. package/dist/cli/embedding-dims.js +39 -0
  22. package/dist/cli/eval-server.d.ts +8 -0
  23. package/dist/cli/eval-server.js +242 -2
  24. package/dist/cli/help-i18n.js +19 -0
  25. package/dist/cli/i18n/en.d.ts +15 -1
  26. package/dist/cli/i18n/en.js +15 -1
  27. package/dist/cli/i18n/resources.d.ts +29 -1
  28. package/dist/cli/i18n/zh-CN.d.ts +14 -0
  29. package/dist/cli/i18n/zh-CN.js +15 -1
  30. package/dist/cli/index.js +89 -1
  31. package/dist/cli/mcp.d.ts +6 -1
  32. package/dist/cli/mcp.js +26 -1
  33. package/dist/cli/optional-grammars.d.ts +6 -8
  34. package/dist/cli/optional-grammars.js +8 -11
  35. package/dist/cli/setup.d.ts +3 -1
  36. package/dist/cli/setup.js +67 -17
  37. package/dist/cli/skill-gen.js +2 -1
  38. package/dist/cli/tool.d.ts +19 -1
  39. package/dist/cli/tool.js +96 -3
  40. package/dist/core/embeddings/hf-env.d.ts +0 -66
  41. package/dist/core/embeddings/http-client.d.ts +7 -0
  42. package/dist/core/embeddings/http-client.js +5 -3
  43. package/dist/core/embeddings/server-mapping.d.ts +1 -1
  44. package/dist/core/embeddings/server-mapping.js +7 -3
  45. package/dist/core/graph/import-cycles.d.ts +10 -0
  46. package/dist/core/graph/import-cycles.js +103 -0
  47. package/dist/core/group/cross-impact.d.ts +2 -1
  48. package/dist/core/group/cross-impact.js +5 -1
  49. package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
  50. package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
  51. package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
  52. package/dist/core/group/extractors/http-route-extractor.js +22 -4
  53. package/dist/core/group/extractors/include-extractor.js +22 -13
  54. package/dist/core/group/sync.d.ts +27 -1
  55. package/dist/core/group/sync.js +0 -0
  56. package/dist/core/incremental/subgraph-extract.js +18 -1
  57. package/dist/core/ingestion/cfg/cfg-builder.d.ts +114 -0
  58. package/dist/core/ingestion/cfg/cfg-builder.js +196 -0
  59. package/dist/core/ingestion/cfg/collect.d.ts +51 -0
  60. package/dist/core/ingestion/cfg/collect.js +77 -0
  61. package/dist/core/ingestion/cfg/control-dependence.d.ts +58 -0
  62. package/dist/core/ingestion/cfg/control-dependence.js +182 -0
  63. package/dist/core/ingestion/cfg/control-flow-context.d.ts +106 -0
  64. package/dist/core/ingestion/cfg/control-flow-context.js +124 -0
  65. package/dist/core/ingestion/cfg/emit.d.ts +293 -0
  66. package/dist/core/ingestion/cfg/emit.js +646 -0
  67. package/dist/core/ingestion/cfg/post-dominators.d.ts +79 -0
  68. package/dist/core/ingestion/cfg/post-dominators.js +176 -0
  69. package/dist/core/ingestion/cfg/reaching-def-reason-codec.d.ts +106 -0
  70. package/dist/core/ingestion/cfg/reaching-def-reason-codec.js +133 -0
  71. package/dist/core/ingestion/cfg/reaching-defs-graph.d.ts +18 -0
  72. package/dist/core/ingestion/cfg/reaching-defs-graph.js +312 -0
  73. package/dist/core/ingestion/cfg/reaching-defs.d.ts +162 -0
  74. package/dist/core/ingestion/cfg/reaching-defs.js +785 -0
  75. package/dist/core/ingestion/cfg/synthetic-escape.d.ts +124 -0
  76. package/dist/core/ingestion/cfg/synthetic-escape.js +300 -0
  77. package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
  78. package/dist/core/ingestion/cfg/traversal-result.js +2 -0
  79. package/dist/core/ingestion/cfg/types.d.ts +274 -0
  80. package/dist/core/ingestion/cfg/types.js +13 -0
  81. package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.d.ts +111 -0
  82. package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.js +541 -0
  83. package/dist/core/ingestion/cfg/visitors/c-cpp.d.ts +74 -0
  84. package/dist/core/ingestion/cfg/visitors/c-cpp.js +584 -0
  85. package/dist/core/ingestion/cfg/visitors/call-site-harvest.d.ts +195 -0
  86. package/dist/core/ingestion/cfg/visitors/call-site-harvest.js +350 -0
  87. package/dist/core/ingestion/cfg/visitors/csharp-harvest.d.ts +124 -0
  88. package/dist/core/ingestion/cfg/visitors/csharp-harvest.js +589 -0
  89. package/dist/core/ingestion/cfg/visitors/csharp.d.ts +87 -0
  90. package/dist/core/ingestion/cfg/visitors/csharp.js +867 -0
  91. package/dist/core/ingestion/cfg/visitors/dart-harvest.d.ts +278 -0
  92. package/dist/core/ingestion/cfg/visitors/dart-harvest.js +870 -0
  93. package/dist/core/ingestion/cfg/visitors/dart.d.ts +119 -0
  94. package/dist/core/ingestion/cfg/visitors/dart.js +836 -0
  95. package/dist/core/ingestion/cfg/visitors/go-harvest.d.ts +160 -0
  96. package/dist/core/ingestion/cfg/visitors/go-harvest.js +621 -0
  97. package/dist/core/ingestion/cfg/visitors/go.d.ts +108 -0
  98. package/dist/core/ingestion/cfg/visitors/go.js +638 -0
  99. package/dist/core/ingestion/cfg/visitors/java-harvest.d.ts +108 -0
  100. package/dist/core/ingestion/cfg/visitors/java-harvest.js +513 -0
  101. package/dist/core/ingestion/cfg/visitors/java.d.ts +101 -0
  102. package/dist/core/ingestion/cfg/visitors/java.js +812 -0
  103. package/dist/core/ingestion/cfg/visitors/kotlin-harvest.d.ts +253 -0
  104. package/dist/core/ingestion/cfg/visitors/kotlin-harvest.js +719 -0
  105. package/dist/core/ingestion/cfg/visitors/kotlin.d.ts +121 -0
  106. package/dist/core/ingestion/cfg/visitors/kotlin.js +809 -0
  107. package/dist/core/ingestion/cfg/visitors/php-harvest.d.ts +172 -0
  108. package/dist/core/ingestion/cfg/visitors/php-harvest.js +626 -0
  109. package/dist/core/ingestion/cfg/visitors/php.d.ts +96 -0
  110. package/dist/core/ingestion/cfg/visitors/php.js +721 -0
  111. package/dist/core/ingestion/cfg/visitors/python-harvest.d.ts +212 -0
  112. package/dist/core/ingestion/cfg/visitors/python-harvest.js +739 -0
  113. package/dist/core/ingestion/cfg/visitors/python.d.ts +103 -0
  114. package/dist/core/ingestion/cfg/visitors/python.js +558 -0
  115. package/dist/core/ingestion/cfg/visitors/ruby-harvest.d.ts +223 -0
  116. package/dist/core/ingestion/cfg/visitors/ruby-harvest.js +587 -0
  117. package/dist/core/ingestion/cfg/visitors/ruby.d.ts +112 -0
  118. package/dist/core/ingestion/cfg/visitors/ruby.js +756 -0
  119. package/dist/core/ingestion/cfg/visitors/rust-harvest.d.ts +324 -0
  120. package/dist/core/ingestion/cfg/visitors/rust-harvest.js +873 -0
  121. package/dist/core/ingestion/cfg/visitors/rust.d.ts +95 -0
  122. package/dist/core/ingestion/cfg/visitors/rust.js +558 -0
  123. package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.d.ts +87 -0
  124. package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.js +116 -0
  125. package/dist/core/ingestion/cfg/visitors/swift-harvest.d.ts +255 -0
  126. package/dist/core/ingestion/cfg/visitors/swift-harvest.js +679 -0
  127. package/dist/core/ingestion/cfg/visitors/swift.d.ts +116 -0
  128. package/dist/core/ingestion/cfg/visitors/swift.js +787 -0
  129. package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +149 -0
  130. package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1056 -0
  131. package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
  132. package/dist/core/ingestion/cfg/visitors/typescript.js +583 -0
  133. package/dist/core/ingestion/import-resolvers/utils.js +2 -0
  134. package/dist/core/ingestion/language-provider.d.ts +23 -2
  135. package/dist/core/ingestion/languages/c-cpp.js +17 -5
  136. package/dist/core/ingestion/languages/cobol.js +4 -0
  137. package/dist/core/ingestion/languages/cpp/arity-metadata.js +41 -2
  138. package/dist/core/ingestion/languages/cpp/captures.js +50 -1
  139. package/dist/core/ingestion/languages/cpp/conversion-rank.d.ts +2 -0
  140. package/dist/core/ingestion/languages/cpp/conversion-rank.js +89 -0
  141. package/dist/core/ingestion/languages/cpp/header-scan.d.ts +1 -1
  142. package/dist/core/ingestion/languages/cpp/header-scan.js +2 -2
  143. package/dist/core/ingestion/languages/cpp/inline-namespaces.js +7 -2
  144. package/dist/core/ingestion/languages/cpp/member-lookup.js +2 -1
  145. package/dist/core/ingestion/languages/cpp/query.js +23 -0
  146. package/dist/core/ingestion/languages/cpp/scope-resolver.js +2 -1
  147. package/dist/core/ingestion/languages/csharp.js +2 -0
  148. package/dist/core/ingestion/languages/dart.js +2 -0
  149. package/dist/core/ingestion/languages/go.js +2 -0
  150. package/dist/core/ingestion/languages/java.js +3 -0
  151. package/dist/core/ingestion/languages/kotlin/query.js +3 -2
  152. package/dist/core/ingestion/languages/kotlin.js +8 -1
  153. package/dist/core/ingestion/languages/php.js +2 -0
  154. package/dist/core/ingestion/languages/python.js +2 -0
  155. package/dist/core/ingestion/languages/ruby.js +2 -0
  156. package/dist/core/ingestion/languages/rust.js +2 -0
  157. package/dist/core/ingestion/languages/swift.js +2 -0
  158. package/dist/core/ingestion/languages/typescript.js +5 -0
  159. package/dist/core/ingestion/languages/vue.js +6 -0
  160. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
  161. package/dist/core/ingestion/method-extractors/generic.js +1 -0
  162. package/dist/core/ingestion/method-types.d.ts +2 -0
  163. package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
  164. package/dist/core/ingestion/model/symbol-table.js +1 -0
  165. package/dist/core/ingestion/mro-processor.js +109 -0
  166. package/dist/core/ingestion/parsing-processor.js +44 -0
  167. package/dist/core/ingestion/pipeline-phases/call-summaries.d.ts +26 -0
  168. package/dist/core/ingestion/pipeline-phases/call-summaries.js +46 -0
  169. package/dist/core/ingestion/pipeline-phases/index.d.ts +2 -0
  170. package/dist/core/ingestion/pipeline-phases/index.js +2 -0
  171. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +0 -21
  172. package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
  173. package/dist/core/ingestion/pipeline-phases/routes.d.ts +10 -0
  174. package/dist/core/ingestion/pipeline-phases/routes.js +30 -1
  175. package/dist/core/ingestion/pipeline-phases/taint-summaries.d.ts +30 -0
  176. package/dist/core/ingestion/pipeline-phases/taint-summaries.js +81 -0
  177. package/dist/core/ingestion/pipeline.d.ts +92 -10
  178. package/dist/core/ingestion/pipeline.js +11 -2
  179. package/dist/core/ingestion/scope-extractor.js +12 -2
  180. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +9 -0
  181. package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.d.ts +75 -0
  182. package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +67 -0
  183. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +12 -2
  184. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +14 -2
  185. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +6 -1
  186. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +13 -1
  187. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +9 -1
  188. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +28 -2
  189. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +4 -0
  190. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +9 -0
  191. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +6 -1
  192. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +174 -40
  193. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +24 -0
  194. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +276 -190
  195. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
  196. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +75 -0
  197. package/dist/core/ingestion/scope-resolution/pipeline/run.js +349 -1
  198. package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
  199. package/dist/core/ingestion/taint/call-summary-codec.d.ts +75 -0
  200. package/dist/core/ingestion/taint/call-summary-codec.js +128 -0
  201. package/dist/core/ingestion/taint/call-summary-emit.d.ts +43 -0
  202. package/dist/core/ingestion/taint/call-summary-emit.js +70 -0
  203. package/dist/core/ingestion/taint/call-summary-harvest.d.ts +77 -0
  204. package/dist/core/ingestion/taint/call-summary-harvest.js +179 -0
  205. package/dist/core/ingestion/taint/call-summary-model.d.ts +47 -0
  206. package/dist/core/ingestion/taint/call-summary-model.js +25 -0
  207. package/dist/core/ingestion/taint/emit.d.ts +125 -0
  208. package/dist/core/ingestion/taint/emit.js +208 -0
  209. package/dist/core/ingestion/taint/interproc-emit.d.ts +50 -0
  210. package/dist/core/ingestion/taint/interproc-emit.js +88 -0
  211. package/dist/core/ingestion/taint/interproc-solver.d.ts +110 -0
  212. package/dist/core/ingestion/taint/interproc-solver.js +312 -0
  213. package/dist/core/ingestion/taint/match.d.ts +153 -0
  214. package/dist/core/ingestion/taint/match.js +278 -0
  215. package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
  216. package/dist/core/ingestion/taint/path-codec.js +190 -0
  217. package/dist/core/ingestion/taint/propagate.d.ts +216 -0
  218. package/dist/core/ingestion/taint/propagate.js +659 -0
  219. package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
  220. package/dist/core/ingestion/taint/site-safety.js +98 -0
  221. package/dist/core/ingestion/taint/source-sink-config.d.ts +107 -23
  222. package/dist/core/ingestion/taint/source-sink-config.js +35 -12
  223. package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
  224. package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
  225. package/dist/core/ingestion/taint/summary-harvest-driver.d.ts +80 -0
  226. package/dist/core/ingestion/taint/summary-harvest-driver.js +179 -0
  227. package/dist/core/ingestion/taint/summary-harvest.d.ts +89 -0
  228. package/dist/core/ingestion/taint/summary-harvest.js +537 -0
  229. package/dist/core/ingestion/taint/summary-model.d.ts +200 -0
  230. package/dist/core/ingestion/taint/summary-model.js +86 -0
  231. package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
  232. package/dist/core/ingestion/taint/typescript-model.js +102 -0
  233. package/dist/core/ingestion/type-extractors/c-cpp.js +21 -0
  234. package/dist/core/ingestion/utils/env.d.ts +7 -0
  235. package/dist/core/ingestion/utils/env.js +12 -0
  236. package/dist/core/ingestion/utils/method-props.js +1 -0
  237. package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
  238. package/dist/core/ingestion/workers/clone-safety.js +465 -0
  239. package/dist/core/ingestion/workers/parse-worker.d.ts +23 -0
  240. package/dist/core/ingestion/workers/parse-worker.js +81 -53
  241. package/dist/core/ingestion/workers/post-result.d.ts +22 -0
  242. package/dist/core/ingestion/workers/post-result.js +87 -0
  243. package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
  244. package/dist/core/ingestion/workers/result-merge.js +43 -0
  245. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -40
  246. package/dist/core/ingestion/workers/worker-pool.js +43 -16
  247. package/dist/core/lbug/csv-generator.d.ts +51 -3
  248. package/dist/core/lbug/csv-generator.js +369 -255
  249. package/dist/core/lbug/lbug-adapter.d.ts +58 -18
  250. package/dist/core/lbug/lbug-adapter.js +346 -84
  251. package/dist/core/lbug/lbug-config.d.ts +19 -0
  252. package/dist/core/lbug/lbug-config.js +29 -0
  253. package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
  254. package/dist/core/lbug/pdg-emit-sink.js +358 -0
  255. package/dist/core/lbug/pool-adapter.d.ts +35 -0
  256. package/dist/core/lbug/pool-adapter.js +90 -1
  257. package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
  258. package/dist/core/lbug/rel-pair-routing.js +140 -0
  259. package/dist/core/lbug/schema.d.ts +2 -2
  260. package/dist/core/lbug/schema.js +3 -0
  261. package/dist/core/logger.d.ts +0 -5
  262. package/dist/core/run-analyze.d.ts +96 -0
  263. package/dist/core/run-analyze.js +207 -3
  264. package/dist/core/tree-sitter/parser-loader.js +5 -4
  265. package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
  266. package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
  267. package/dist/core/tree-sitter/vendored-grammars.js +57 -0
  268. package/dist/core/wiki/generator.js +3 -1
  269. package/dist/core/wiki/graph-queries.d.ts +6 -0
  270. package/dist/core/wiki/graph-queries.js +9 -1
  271. package/dist/mcp/http-transport.d.ts +134 -0
  272. package/dist/mcp/http-transport.js +503 -0
  273. package/dist/mcp/local/local-backend.d.ts +110 -24
  274. package/dist/mcp/local/local-backend.js +1225 -19
  275. package/dist/mcp/local/pdg-impact.d.ts +384 -0
  276. package/dist/mcp/local/pdg-impact.js +1972 -0
  277. package/dist/mcp/resources.js +13 -1
  278. package/dist/mcp/tools.d.ts +12 -0
  279. package/dist/mcp/tools.js +214 -7
  280. package/dist/server/analyze-launch.d.ts +29 -0
  281. package/dist/server/analyze-launch.js +137 -0
  282. package/dist/server/analyze-upload.d.ts +33 -0
  283. package/dist/server/analyze-upload.js +123 -0
  284. package/dist/server/analyze-worker-ipc.d.ts +58 -0
  285. package/dist/server/analyze-worker-ipc.js +16 -0
  286. package/dist/server/analyze-worker.d.ts +17 -1
  287. package/dist/server/analyze-worker.js +7 -1
  288. package/dist/server/api.d.ts +17 -0
  289. package/dist/server/api.js +132 -168
  290. package/dist/server/git-clone.d.ts +43 -2
  291. package/dist/server/git-clone.js +211 -16
  292. package/dist/server/mcp-http.d.ts +5 -6
  293. package/dist/server/mcp-http.js +9 -82
  294. package/dist/server/middleware.d.ts +40 -0
  295. package/dist/server/middleware.js +92 -0
  296. package/dist/server/private-ip.d.ts +1 -0
  297. package/dist/server/private-ip.js +15 -0
  298. package/dist/server/upload-ingest.d.ts +56 -0
  299. package/dist/server/upload-ingest.js +276 -0
  300. package/dist/server/upload-paths.d.ts +37 -0
  301. package/dist/server/upload-paths.js +57 -0
  302. package/dist/server/upload-sweep.d.ts +20 -0
  303. package/dist/server/upload-sweep.js +57 -0
  304. package/dist/storage/git.js +5 -8
  305. package/dist/storage/parse-cache.d.ts +22 -1
  306. package/dist/storage/parse-cache.js +44 -10
  307. package/dist/storage/repo-manager.d.ts +121 -10
  308. package/dist/storage/repo-manager.js +43 -5
  309. package/dist/types/pipeline.d.ts +9 -0
  310. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
  311. package/hooks/claude/gitnexus-hook.cjs +110 -13
  312. package/hooks/claude/hook-db-lock-probe.cjs +513 -26
  313. package/package.json +8 -2
  314. package/scripts/assert-publish-grammar-coverage.cjs +31 -0
  315. package/scripts/build-tree-sitter-grammars.cjs +16 -10
  316. package/scripts/cross-platform-tests.ts +1 -0
  317. package/skills/gitnexus-debugging.md +16 -4
  318. package/skills/gitnexus-exploring.md +3 -3
  319. package/skills/gitnexus-guide.md +33 -0
  320. package/skills/gitnexus-pdg-query.md +89 -0
  321. package/skills/gitnexus-refactoring.md +1 -1
  322. package/skills/gitnexus-taint-analysis.md +178 -0
  323. package/vendor/tree-sitter-c/package.json +1 -1
  324. package/vendor/tree-sitter-dart/package.json +1 -1
  325. package/vendor/tree-sitter-kotlin/package.json +1 -1
  326. package/vendor/tree-sitter-proto/package.json +1 -1
  327. package/vendor/tree-sitter-swift/package.json +1 -1
  328. package/web/assets/{agent-CKPMqImC.js → agent-D3Xd7zZE.js} +123 -123
  329. package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
  330. package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
  331. package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
  332. package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
  333. package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
  334. package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
  335. package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
  336. package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
  337. package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
  338. package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
  339. package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
  340. package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
  341. package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
  342. package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
  343. package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
  344. package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
  345. package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
  346. package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
  347. package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
  348. package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
  349. package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
  350. package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
  351. package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
  352. package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
  353. package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
  354. package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
  355. package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
  356. package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
  357. package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
  358. package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
  359. package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
  360. package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
  361. package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
  362. package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
  363. package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
  364. package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
  365. package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
  366. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
  367. package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
  368. package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
  369. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
  370. package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
  371. package/web/assets/index-BvYev1UJ.js +635 -0
  372. package/web/assets/index-_lgn7hs5.css +2 -0
  373. package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
  374. package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
  375. package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
  376. package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
  377. package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
  378. package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
  379. package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
  380. package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
  381. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
  382. package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
  383. package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
  384. package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
  385. package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
  386. package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
  387. package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
  388. package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
  389. package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
  390. package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
  391. package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
  392. package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
  393. package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
  394. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
  395. package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
  396. package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
  397. package/web/index.html +13 -15
  398. package/scripts/materialize-vendor-grammars.cjs +0 -97
  399. package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
  400. package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
  401. package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
  402. package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
  403. package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
  404. package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
  405. package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
  406. package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
  407. package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
  408. package/web/assets/chunk-CilyBKbf.js +0 -1
  409. package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
  410. package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
  411. package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
  412. package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
  413. package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
  414. package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
  415. package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
  416. package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
  417. package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
  418. package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
  419. package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
  420. package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
  421. package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
  422. package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
  423. package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
  424. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
  425. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
  426. package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
  427. package/web/assets/index-BKWA-m7o.css +0 -2
  428. package/web/assets/index-CG6q8eTs.js +0 -626
  429. package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
  430. package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
  431. package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
  432. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
  433. package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
  434. package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
  435. package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
  436. package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
  437. package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
  438. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
  439. /package/web/assets/{chunk-AQ6EADP3-CCFje6lL.js → chunk-AQ6EADP3-CZhslHi-.js} +0 -0
@@ -0,0 +1,503 @@
1
+ /**
2
+ * Dedicated MCP HTTP server.
3
+ *
4
+ * Provides HTTP-based MCP transport supporting:
5
+ * - Modern Streamable HTTP: POST /mcp
6
+ * - Legacy SSE transport: GET /sse + POST /messages
7
+ *
8
+ * Started via `gitnexus mcp --http`.
9
+ * stdio remains the default mode for `gitnexus mcp` (no breaking change).
10
+ *
11
+ * Exports createStreamableHttpHandler and createSseHandlers so that
12
+ * server/mcp-http.ts (web-UI route mount) can reuse them without inverting
13
+ * the established server/ → mcp/ dependency direction.
14
+ *
15
+ * Security considerations:
16
+ * - Default binds to 127.0.0.1 (loopback only).
17
+ * - Use --auth-token to enable Bearer Token authentication.
18
+ * - Use --host 0.0.0.0 to expose to all interfaces (requires --auth-token — refuses to start otherwise).
19
+ * - CORS is restricted to loopback origins when no auth token is configured.
20
+ * - PNA (Private Network Access) header is emitted only in response to browser preflight requests.
21
+ */
22
+ import { timingSafeEqual, randomUUID } from 'crypto';
23
+ import express from 'express';
24
+ import cors from 'cors';
25
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
26
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
27
+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
28
+ import { createMCPServer, installSignalShutdown } from './server.js';
29
+ import { logger } from '../core/logger.js';
30
+ /** Sessions idle longer than this are evicted. */
31
+ const SESSION_TTL_MS = 30 * 60 * 1000;
32
+ /** Cleanup sweep runs every 5 minutes. */
33
+ const CLEANUP_INTERVAL_MS = 5 * 60 * 1000;
34
+ /** Hard cap on concurrent sessions — guards against initialize-flood DoS. */
35
+ const MAX_SESSIONS = 1000;
36
+ /**
37
+ * Creates a Bearer Token authentication middleware.
38
+ *
39
+ * - When authToken is not set, all requests pass through.
40
+ * - When authToken is set, checks the Authorization: Bearer <token> header.
41
+ * - Uses constant-time comparison to prevent timing oracle attacks.
42
+ * - Returns a JSON-RPC formatted 401 on failure.
43
+ */
44
+ export function createAuthMiddleware(authToken) {
45
+ return (req, res, next) => {
46
+ if (!authToken) {
47
+ next();
48
+ return;
49
+ }
50
+ const header = req.headers['authorization'];
51
+ const expected = `Bearer ${authToken}`;
52
+ // Constant-time comparison — prevents timing oracle on bearer token.
53
+ // Buffers must be the same byte-length for timingSafeEqual; mismatch means
54
+ // we create a same-length dummy so the comparison always runs in full.
55
+ let valid = false;
56
+ if (typeof header === 'string') {
57
+ const a = Buffer.from(header);
58
+ const b = Buffer.from(expected);
59
+ if (a.length === b.length) {
60
+ valid = timingSafeEqual(a, b);
61
+ }
62
+ else {
63
+ // Different lengths — run dummy comparison to preserve constant time.
64
+ timingSafeEqual(Buffer.alloc(b.length), b);
65
+ }
66
+ }
67
+ if (valid) {
68
+ next();
69
+ return;
70
+ }
71
+ res.status(401).json({
72
+ jsonrpc: '2.0',
73
+ error: { code: -32001, message: 'Unauthorized' },
74
+ id: null,
75
+ });
76
+ };
77
+ }
78
+ /**
79
+ * Returns true when an Origin should be allowed by the no-auth (loopback-only)
80
+ * CORS policy — i.e. it is absent (non-browser caller) or a loopback origin.
81
+ *
82
+ * WHATWG URL keeps the brackets on IPv6 literals
83
+ * (`new URL('http://[::1]/').hostname === '[::1]'`) and canonicalizes the
84
+ * IPv4-mapped loopback to `[::ffff:7f00:1]`; loopback IPv4 is the whole
85
+ * 127.0.0.0/8 block — so all of those forms are matched explicitly.
86
+ */
87
+ export function isLoopbackOrigin(origin) {
88
+ if (!origin)
89
+ return true; // no Origin → non-browser caller; CORS is not the control there
90
+ let hostname;
91
+ try {
92
+ ({ hostname } = new URL(origin));
93
+ }
94
+ catch {
95
+ return false;
96
+ }
97
+ return (hostname === 'localhost' ||
98
+ hostname === '[::1]' ||
99
+ hostname === '[::ffff:7f00:1]' ||
100
+ /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname));
101
+ }
102
+ /** True for the exact loopback bind addresses. */
103
+ export function isLoopbackHost(host) {
104
+ return host === '127.0.0.1' || host === 'localhost' || host === '::1';
105
+ }
106
+ /** True for any-interface wildcard binds, whose externally-used Host is unknowable. */
107
+ export function isWildcardHost(host) {
108
+ return host === '0.0.0.0' || host === '::';
109
+ }
110
+ /**
111
+ * Computes the SDK DNS-rebinding `allowedHosts` list (a Host-header allowlist) for a
112
+ * bind host/port, or `undefined` when protection should stay off.
113
+ *
114
+ * Wildcard binds (`0.0.0.0` / `::`) return `undefined` — the Host a client
115
+ * legitimately uses is unknowable, so the bearer token (required for non-loopback
116
+ * binds) is the control. Loopback binds allow all three loopback host forms
117
+ * (bare + `:port`); a specific host (e.g. `192.168.1.50`) allows that host
118
+ * (bare + `:port`), which is knowable and a free defence-in-depth win.
119
+ */
120
+ export function computeAllowedHosts(host, port) {
121
+ if (isWildcardHost(host))
122
+ return undefined;
123
+ const hosts = isLoopbackHost(host) ? ['127.0.0.1', 'localhost', '[::1]'] : [host];
124
+ return hosts.flatMap((h) => [h, `${h}:${port}`]);
125
+ }
126
+ /**
127
+ * Resolves the MCP HTTP bearer token from the `--auth-token` flag or the
128
+ * `GITNEXUS_MCP_AUTH_TOKEN` env var (the flag wins). An empty or whitespace-only
129
+ * value is treated as "no token" so a blank env var cannot silently disable auth
130
+ * (and slip past the non-loopback hard-fail).
131
+ */
132
+ export function resolveAuthToken(optToken, env) {
133
+ return (optToken ?? env.GITNEXUS_MCP_AUTH_TOKEN)?.trim() || undefined;
134
+ }
135
+ /** Builds the SDK transport DNS-rebinding options from a bind host/port. */
136
+ function dnsRebindingOptions(host, port) {
137
+ if (host === undefined || port === undefined)
138
+ return {};
139
+ const allowedHosts = computeAllowedHosts(host, port);
140
+ return allowedHosts ? { enableDnsRebindingProtection: true, allowedHosts } : {};
141
+ }
142
+ /**
143
+ * Starts a periodic sweep that closes and evicts sessions idle longer than
144
+ * `ttlMs`, returning the (unref'd) timer. Shared by both transport factories to
145
+ * guard against network drops where the per-session onclose never fires.
146
+ */
147
+ export function startIdleSweep(sessions, ttlMs, intervalMs) {
148
+ const timer = setInterval(() => {
149
+ const now = Date.now();
150
+ for (const [id, session] of sessions) {
151
+ if (now - session.lastActivity > ttlMs) {
152
+ try {
153
+ session.server.close();
154
+ }
155
+ catch { }
156
+ sessions.delete(id);
157
+ }
158
+ }
159
+ }, intervalMs);
160
+ if (timer && typeof timer === 'object' && 'unref' in timer) {
161
+ timer.unref();
162
+ }
163
+ return timer;
164
+ }
165
+ /**
166
+ * Creates a reusable StreamableHTTP request handler.
167
+ *
168
+ * Encapsulates the session map and request-dispatch logic as an independent
169
+ * factory, reused by both startMcpHttpServer (POST /mcp) and the web-UI server
170
+ * route mount in server/mcp-http.ts (/api/mcp).
171
+ */
172
+ export function createStreamableHttpHandler(backend, opts = {}) {
173
+ // Seam: tests inject createServer to observe the per-session Server lifecycle.
174
+ const createServer = opts.createServer ?? (() => createMCPServer(backend));
175
+ // DNS-rebinding protection (Host-header allowlist) when the bind host is known.
176
+ const dnsRebinding = dnsRebindingOptions(opts.host, opts.port);
177
+ const sessions = new Map();
178
+ const cleanupTimer = startIdleSweep(sessions, SESSION_TTL_MS, CLEANUP_INTERVAL_MS);
179
+ const handler = async (req, res) => {
180
+ const sessionId = req.headers['mcp-session-id'];
181
+ if (sessionId && sessions.has(sessionId)) {
182
+ // Existing session — delegate to its transport and refresh activity timestamp.
183
+ // `has` just returned true and the map is not mutated before `get`, so the
184
+ // lookup is non-null.
185
+ const session = sessions.get(sessionId);
186
+ session.lastActivity = Date.now();
187
+ await session.transport.handleRequest(req, res, req.body);
188
+ }
189
+ else if (sessionId) {
190
+ // Unknown / expired session ID — tell the client to re-initialize (per MCP spec).
191
+ res.status(404).json({
192
+ jsonrpc: '2.0',
193
+ error: { code: -32001, message: 'Session not found. Re-initialize.' },
194
+ id: null,
195
+ });
196
+ }
197
+ else if (req.method === 'POST') {
198
+ // No session ID — new client. Only accept initialize requests to avoid
199
+ // orphaned Server instances that can never be reclaimed by the TTL sweep.
200
+ // Use the SDK's isInitializeRequest so a single-element JSON-RPC batch is
201
+ // recognised too, rather than a brittle `body.method === 'initialize'` check.
202
+ const body = req.body;
203
+ const messages = Array.isArray(body) ? body : [body];
204
+ if (!messages.some(isInitializeRequest)) {
205
+ res.status(400).json({
206
+ jsonrpc: '2.0',
207
+ error: {
208
+ code: -32000,
209
+ message: 'First request must be initialize. No session ID provided.',
210
+ },
211
+ id: null,
212
+ });
213
+ return;
214
+ }
215
+ // Reject when the session cap is reached — prevents memory exhaustion via
216
+ // an initialize flood (each session holds a live Server + Transport).
217
+ if (sessions.size >= MAX_SESSIONS) {
218
+ res.status(503).json({
219
+ jsonrpc: '2.0',
220
+ error: { code: -32000, message: 'Server at session capacity. Try again later.' },
221
+ id: null,
222
+ });
223
+ return;
224
+ }
225
+ const transport = new StreamableHTTPServerTransport({
226
+ sessionIdGenerator: () => randomUUID(),
227
+ ...dnsRebinding,
228
+ });
229
+ const server = createServer();
230
+ await server.connect(transport);
231
+ await transport.handleRequest(req, res, req.body);
232
+ if (transport.sessionId) {
233
+ sessions.set(transport.sessionId, { server, transport, lastActivity: Date.now() });
234
+ const sid = transport.sessionId;
235
+ transport.onclose = () => {
236
+ sessions.delete(sid);
237
+ };
238
+ }
239
+ else {
240
+ // The SDK rejected this request (e.g. 406 on a missing/invalid Accept header,
241
+ // 415 on a bad Content-Type) before assigning a session id. The Server was
242
+ // already connected but will never be stored, so the TTL sweep and cleanup()
243
+ // can't reclaim it — close it now to avoid an orphaned-Server leak.
244
+ try {
245
+ await server.close();
246
+ }
247
+ catch { }
248
+ }
249
+ }
250
+ else {
251
+ res.status(400).json({
252
+ jsonrpc: '2.0',
253
+ error: { code: -32000, message: 'No valid session. Send a POST to initialize.' },
254
+ id: null,
255
+ });
256
+ }
257
+ };
258
+ const cleanup = async () => {
259
+ clearInterval(cleanupTimer);
260
+ const closers = [...sessions.values()].map(async (session) => {
261
+ try {
262
+ await Promise.resolve(session.server.close());
263
+ }
264
+ catch { }
265
+ });
266
+ sessions.clear();
267
+ await Promise.allSettled(closers);
268
+ };
269
+ return { handler, cleanup };
270
+ }
271
+ /**
272
+ * Creates legacy SSE transport handlers.
273
+ *
274
+ * GET /sse (or custom path) establishes the SSE stream;
275
+ * POST /messages (or custom path) receives client JSON-RPC messages.
276
+ *
277
+ * Includes the same idle-TTL eviction as createStreamableHttpHandler to prevent
278
+ * memory leaks when clients drop without closing the SSE connection cleanly.
279
+ *
280
+ * @param backend LocalBackend instance
281
+ * @param messagesPath Path clients POST messages to (default: '/messages')
282
+ */
283
+ export function createSseHandlers(backend, messagesPath = '/messages', opts = {}) {
284
+ const maxSessions = opts.maxSessions ?? MAX_SESSIONS;
285
+ // DNS-rebinding protection (Host-header allowlist) when the bind host is known.
286
+ const dnsRebinding = dnsRebindingOptions(opts.host, opts.port);
287
+ const sseSessions = new Map();
288
+ const cleanupTimer = startIdleSweep(sseSessions, SESSION_TTL_MS, CLEANUP_INTERVAL_MS);
289
+ const sseHandler = async (req, res) => {
290
+ // Cap concurrent SSE sessions — mirrors the streamable handler's MAX_SESSIONS
291
+ // guard so a flood of held-open GET /sse connections cannot allocate unbounded
292
+ // Server instances before the idle sweep reclaims them.
293
+ if (sseSessions.size >= maxSessions) {
294
+ res.status(503).json({
295
+ jsonrpc: '2.0',
296
+ error: { code: -32000, message: 'Server at session capacity. Try again later.' },
297
+ id: null,
298
+ });
299
+ return;
300
+ }
301
+ // SSEServerTransport(endpoint, res, options): endpoint is the path clients POST to.
302
+ const transport = new SSEServerTransport(messagesPath, res, dnsRebinding);
303
+ const server = createMCPServer(backend);
304
+ sseSessions.set(transport.sessionId, { server, transport, lastActivity: Date.now() });
305
+ transport.onclose = () => {
306
+ sseSessions.delete(transport.sessionId);
307
+ };
308
+ res.on('close', () => {
309
+ sseSessions.delete(transport.sessionId);
310
+ try {
311
+ server.close();
312
+ }
313
+ catch { }
314
+ });
315
+ // connect() calls transport.start(), which sends the SSE 'endpoint' event.
316
+ await server.connect(transport);
317
+ };
318
+ const messageHandler = async (req, res) => {
319
+ const sessionId = req.query['sessionId'] ??
320
+ req.headers['mcp-session-id'];
321
+ const entry = sessionId ? sseSessions.get(sessionId) : undefined;
322
+ if (!entry) {
323
+ res.status(404).json({
324
+ jsonrpc: '2.0',
325
+ error: { code: -32001, message: 'SSE session not found. Reconnect to /sse.' },
326
+ id: null,
327
+ });
328
+ return;
329
+ }
330
+ // Refresh activity timestamp so the TTL sweep does not evict an active session.
331
+ entry.lastActivity = Date.now();
332
+ // express.json() has already parsed the body — pass it as the third argument
333
+ // to avoid the SDK re-reading the already-consumed stream.
334
+ await entry.transport.handlePostMessage(req, res, req.body);
335
+ };
336
+ const cleanup = async () => {
337
+ clearInterval(cleanupTimer);
338
+ const closers = [...sseSessions.values()].map(async ({ server }) => {
339
+ try {
340
+ await Promise.resolve(server.close());
341
+ }
342
+ catch { }
343
+ });
344
+ sseSessions.clear();
345
+ await Promise.allSettled(closers);
346
+ };
347
+ return { sseHandler, messageHandler, cleanup };
348
+ }
349
+ /**
350
+ * Creates and starts the dedicated MCP HTTP server.
351
+ *
352
+ * Mounts the following routes:
353
+ * - GET /health — health check (no auth required; for orchestrators/probes)
354
+ * - POST /mcp — Streamable HTTP (modern clients)
355
+ * - GET /sse — legacy SSE stream (old clients)
356
+ * - POST /messages — legacy SSE message endpoint
357
+ *
358
+ * @param backend LocalBackend instance
359
+ * @param options Server configuration
360
+ * @returns The listening http.Server
361
+ */
362
+ export async function startMcpHttpServer(backend, options) {
363
+ const { port, host, authToken } = options;
364
+ // Refuse to start an unauthenticated server on a non-loopback interface — that
365
+ // would silently expose every indexed repo to anyone who can reach the host.
366
+ // Loopback binds stay open by default; non-loopback binds require a token.
367
+ if (!authToken && !isLoopbackHost(host)) {
368
+ throw new Error(`Refusing to start the MCP HTTP server on a non-loopback host (${host}) without ` +
369
+ 'authentication — it would expose all indexed repos to anyone who can reach it. ' +
370
+ 'Pass --auth-token (or set GITNEXUS_MCP_AUTH_TOKEN), or bind --host 127.0.0.1. ' +
371
+ 'This applies to --host 0.0.0.0 and --host :: as well.');
372
+ }
373
+ const app = express();
374
+ // Suppress X-Powered-By to reduce information leakage.
375
+ app.disable('x-powered-by');
376
+ // PNA (Chrome 130+ Private Network Access) preflight support.
377
+ // The browser sends `Access-Control-Request-Private-Network: true` ONLY on the
378
+ // CORS preflight (an OPTIONS request); emit the matching allow header only then,
379
+ // never on actual GET/POST responses. Runs before cors() so the header survives
380
+ // onto the preflight response cors() short-circuits.
381
+ app.use((req, res, next) => {
382
+ if (req.method === 'OPTIONS' &&
383
+ req.headers['access-control-request-private-network'] === 'true') {
384
+ res.setHeader('Access-Control-Allow-Private-Network', 'true');
385
+ }
386
+ next();
387
+ });
388
+ // CORS policy:
389
+ // - With auth token: allow any origin (remote access is intentional and protected).
390
+ // - Without auth token: restrict to loopback origins only to prevent drive-by local exfiltration.
391
+ const corsOrigin = authToken
392
+ ? true
393
+ : (origin, cb) => {
394
+ cb(null, isLoopbackOrigin(origin));
395
+ };
396
+ app.use(cors({
397
+ origin: corsOrigin,
398
+ credentials: false,
399
+ allowedHeaders: ['Content-Type', 'Authorization', 'mcp-session-id', 'last-event-id'],
400
+ exposedHeaders: ['mcp-session-id'],
401
+ }));
402
+ const auth = createAuthMiddleware(authToken);
403
+ // Body parser applied per-route after auth, so unauthenticated requests never
404
+ // trigger the 10 MB parse. Malformed/oversized JSON from authenticated clients
405
+ // is converted to a JSON-RPC error envelope by the terminal error handler
406
+ // registered after the routes (see below).
407
+ const jsonBody = express.json({ limit: '10mb' });
408
+ // Health check — no auth required; safe to expose for probes and orchestrators.
409
+ app.get('/health', (_req, res) => {
410
+ res.json({ status: 'ok' });
411
+ });
412
+ // Streamable HTTP (modern MCP clients) at POST /mcp.
413
+ const streamable = createStreamableHttpHandler(backend, { host, port });
414
+ app.all('/mcp', auth, jsonBody, (req, res) => {
415
+ void streamable.handler(req, res).catch((err) => {
416
+ logger.error({ err }, 'MCP /mcp request failed');
417
+ if (!res.headersSent) {
418
+ res.status(500).json({
419
+ jsonrpc: '2.0',
420
+ error: { code: -32000, message: 'Internal MCP server error' },
421
+ id: null,
422
+ });
423
+ }
424
+ });
425
+ });
426
+ // Legacy SSE: GET /sse opens the stream; POST /messages receives JSON-RPC messages.
427
+ const sse = createSseHandlers(backend, '/messages', { host, port });
428
+ app.get('/sse', auth, (req, res) => {
429
+ void sse.sseHandler(req, res).catch((err) => {
430
+ logger.error({ err }, 'MCP /sse failed');
431
+ });
432
+ });
433
+ app.post('/messages', auth, jsonBody, (req, res) => {
434
+ void sse.messageHandler(req, res).catch((err) => {
435
+ logger.error({ err }, 'MCP /messages failed');
436
+ if (!res.headersSent) {
437
+ res.status(500).json({
438
+ jsonrpc: '2.0',
439
+ error: { code: -32000, message: 'Internal error' },
440
+ id: null,
441
+ });
442
+ }
443
+ });
444
+ });
445
+ // Terminal error handler: body-parser failures (malformed or oversized JSON)
446
+ // reach here via next(err). Without it, Express's default handler returns an
447
+ // HTML error page — leaking a stack trace and absolute install paths when
448
+ // NODE_ENV is unset (the default for a CLI) — instead of the JSON-RPC envelope
449
+ // every other path uses.
450
+ app.use((err, _req, res, _next) => {
451
+ const e = (err ?? {});
452
+ const isBodyParseError = e.type === 'entity.parse.failed' ||
453
+ e.type === 'entity.too.large' ||
454
+ err instanceof SyntaxError;
455
+ logger.error({ err }, 'MCP HTTP request error');
456
+ if (res.headersSent)
457
+ return;
458
+ if (isBodyParseError) {
459
+ res.status(e.status ?? e.statusCode ?? 400).json({
460
+ jsonrpc: '2.0',
461
+ error: { code: -32700, message: 'Parse error' },
462
+ id: null,
463
+ });
464
+ return;
465
+ }
466
+ res.status(500).json({
467
+ jsonrpc: '2.0',
468
+ error: { code: -32000, message: 'Internal MCP server error' },
469
+ id: null,
470
+ });
471
+ });
472
+ return new Promise((resolve, reject) => {
473
+ const server = app.listen(port, host, () => {
474
+ const displayHost = host === '0.0.0.0' || host === '::' ? 'localhost' : host;
475
+ logger.info({ port, host }, `GitNexus MCP HTTP server listening on http://${displayHost}:${port} ` +
476
+ `(Streamable: POST /mcp · legacy SSE: GET /sse + POST /messages)`);
477
+ resolve(server);
478
+ });
479
+ server.on('error', (err) => {
480
+ if (err.code === 'EADDRINUSE') {
481
+ logger.error({ port, host }, `Port ${port} is already in use. ` +
482
+ `Stop the conflicting process or use a different port: gitnexus mcp --http --port <other>`);
483
+ process.exit(1);
484
+ }
485
+ reject(err);
486
+ });
487
+ const shutdown = async (exitCode) => {
488
+ server.close();
489
+ await streamable.cleanup();
490
+ await sse.cleanup();
491
+ try {
492
+ await backend.disconnect();
493
+ }
494
+ catch { }
495
+ const { flushLoggerSync } = await import('../core/logger.js');
496
+ flushLoggerSync();
497
+ process.exit(exitCode);
498
+ };
499
+ // Use the shared signal wiring so SIGINT exits 130 and SIGTERM exits 143
500
+ // (the repo's POSIX 128+signal convention), not a misleading exit(0).
501
+ installSignalShutdown((exitCode = 0) => void shutdown(exitCode));
502
+ });
503
+ }
@@ -140,30 +140,6 @@ export interface ListReposPagination {
140
140
  /** Offset to request next; present only when `hasMore` is true. */
141
141
  nextOffset?: number;
142
142
  }
143
- /**
144
- * Validate and normalise `list_repos` pagination arguments.
145
- *
146
- * @internal Exported for unit testing; not part of the public API surface.
147
- *
148
- * There is NO MCP-SDK-level enforcement of a tool's advertised `inputSchema`
149
- * (the SDK validates only the JSON-RPC envelope), and `callTool` is reachable
150
- * directly, so the backend is the real validation boundary. Malformed values —
151
- * non-number, `NaN`, non-integer, `limit < 1`, `limit > maxLimit`, or
152
- * `offset < 0` — are REJECTED with a clear error. `limit` is bounded but NOT
153
- * silently clamped: an over-max value throws (symmetric with the other bounds)
154
- * so a client never receives a smaller page than it asked for without knowing.
155
- * An omitted value (only `undefined`) falls back to the default.
156
- */
157
- export declare function parseListReposPagination(params: {
158
- limit?: unknown;
159
- offset?: unknown;
160
- } | null | undefined, opts: {
161
- defaultLimit: number;
162
- maxLimit: number;
163
- }): {
164
- limit: number;
165
- offset: number;
166
- };
167
143
  export declare class LocalBackend {
168
144
  private repos;
169
145
  private contextCache;
@@ -337,6 +313,8 @@ export declare class LocalBackend {
337
313
  */
338
314
  private maybeWarnSiblingDrift;
339
315
  callTool(method: string, params: any): Promise<any>;
316
+ /** Check repository graph invariants that are suitable for CI gating. */
317
+ private check;
340
318
  /**
341
319
  * Query tool — process-grouped search.
342
320
  *
@@ -422,6 +400,77 @@ export declare class LocalBackend {
422
400
  */
423
401
  private context;
424
402
  private _contextImpl;
403
+ /**
404
+ * Resolve a `target` (file path OR symbol/function name) into a BasicBlock
405
+ * SOURCE-block anchor, shared by `explain` (TAINTED) and `pdg_query`
406
+ * (CDG/REACHING_DEF) — both reconstruct the symbol↔block join the same way
407
+ * (there is no Function→BasicBlock edge). #2188 review: extracted from two
408
+ * near-identical copies that had DRIFTED — `_explainImpl` used a 0-based,
409
+ * un-widened span window that dropped a function's final-line block and could
410
+ * leak a neighbor's line-above block; this single resolver applies the correct
411
+ * `[symStart+1, symEnd+1]` window (1-based BasicBlock startLine vs 0-based
412
+ * symbol span) to BOTH callers.
413
+ *
414
+ * Returns a BARE `anchorClause` (no leading `AND`) so each caller composes its
415
+ * own `WHERE`; `early` carries the not-found/ambiguous payload (caller returns
416
+ * it verbatim). `target` / symbol names flow only through `queryParams` bind
417
+ * params — never interpolated into Cypher.
418
+ */
419
+ private resolveBlockAnchor;
420
+ /**
421
+ * Explain tool (#2083 M3 U6) — persisted taint-finding explanation.
422
+ * WAL-aware wrapper mirroring `context`.
423
+ */
424
+ private explain;
425
+ /**
426
+ * Taint findings are persisted as `TAINTED` rows in CodeRelation whose
427
+ * endpoints are BOTH BasicBlock nodes — the label anchor restricts every
428
+ * query here to the BasicBlock→BasicBlock partition of the rel table
429
+ * (which holds only the sparse, per-function-capped pdg layers), never a
430
+ * global symbol-space scan (the S1 verdict; LadybugDB has no rel-property
431
+ * index, so the label anchor IS the bound).
432
+ *
433
+ * Anchoring granularity:
434
+ * - file target → BasicBlock id prefix (`BasicBlock:<filePath>:` — the
435
+ * shared `basicBlockId` template) with an exact-or-suffix path match so
436
+ * `vuln.ts` finds `src/vuln.ts`.
437
+ * - symbol target → resolved via `resolveSymbolCandidates` (the context()
438
+ * path: ambiguous ⇒ ranked candidates, unknown ⇒ not-found), then the
439
+ * file id-prefix PLUS source-block startLine within the symbol's
440
+ * [startLine, endLine] span. Findings are intra-procedural, so filtering
441
+ * the SOURCE endpoint is sufficient — both endpoints share the function.
442
+ * Symbols without a line span degrade to the file-level filter.
443
+ *
444
+ * The per-finding `sinkKind` and hop path decode from the persisted
445
+ * `reason` via the SHARED `taint/path-codec.ts` (the U4 write path encodes
446
+ * with the same module — `;<kind>` header + ordered `variable:line` hops).
447
+ */
448
+ private _explainImpl;
449
+ private pdgQuery;
450
+ /**
451
+ * Query the persisted PDG (#2086 M6) — the control/data-dependence analog of
452
+ * `explain`. `controls` reads CDG ("under what condition does X run?", branch
453
+ * sense 'T'|'F' in `reason`); `flows` reads REACHING_DEF (def→use, variable
454
+ * name in `reason`). Intra-procedural, basic-block granular.
455
+ *
456
+ * Bounded by construction: the BasicBlock→BasicBlock partition holds only the
457
+ * sparse, per-function-capped pdg layers, the query is anchored to one file/
458
+ * symbol, and the page is LIMIT-bounded (validated integer, interpolated
459
+ * because LadybugDB does not parameterize LIMIT). LadybugDB has no rel-
460
+ * property index, so the anchor IS the bound — there is no anchorless mode.
461
+ *
462
+ * Symbol↔block join: there is no Function→BasicBlock edge; the SOURCE block
463
+ * (`a` — controller for CDG, def for REACHING_DEF) is filtered by the
464
+ * BasicBlock id-prefix (`basicBlockId` template) plus its `startLine` within
465
+ * the symbol's span. BasicBlock `startLine` is 1-based while symbol-node
466
+ * `startLine`/`endLine` are 0-based, so BOTH bounds are shifted +1
467
+ * (`[symStart+1, symEnd+1]`) onto the block basis: the upper +1 keeps a
468
+ * guard/def/use on the function's final line, and the lower +1 excludes an
469
+ * adjacent function's block on the line directly above (#2188 review). Both
470
+ * endpoints share the function (intra-procedural), so filtering the source
471
+ * endpoint suffices.
472
+ */
473
+ private _pdgQueryImpl;
425
474
  /**
426
475
  * Legacy explore — kept for backwards compatibility with resources.ts.
427
476
  * Routes cluster/process types to direct graph queries.
@@ -438,8 +487,45 @@ export declare class LocalBackend {
438
487
  * Additional refs found via text search are tagged "text_search" (lower confidence).
439
488
  */
440
489
  private rename;
490
+ private trace;
491
+ private _traceImpl;
441
492
  private impact;
442
493
  private _impactImpl;
494
+ /**
495
+ * Union of the leaf callee names invoked across a set of dependence-slice
496
+ * blocks (`BasicBlock.callees`, space-joined at emit). Drives statement-precise
497
+ * inter-procedural evidence: a first-hop callee reached from the criterion is
498
+ * "proven" (callgraph-bridge) iff its name is in this set, else unproven-bridge.
499
+ * Empty when the slice blocks call nothing or carry no harvested callees
500
+ * (non-TS/JS or synthetic ENTRY/EXIT blocks) — the bridge then preserves
501
+ * callgraph reach. A query failure is logged and degrades to empty (no proof),
502
+ * never throws (the inter-procedural reach is still returned).
503
+ */
504
+ private calleesOfBlocks;
505
+ /**
506
+ * Union of the RESOLVED callee symbol ids invoked across a set of
507
+ * dependence-slice blocks (`BasicBlock.calleeIds`, space-joined at emit —
508
+ * sibling of `callees`). This is the SOUND key the bridge prefers: a first-hop
509
+ * callee is proven statement-precise iff its resolved id is in this set, which
510
+ * eliminates the same-leaf-name collision (false-positive) and import-alias
511
+ * (false-negative) the name set cannot distinguish. Empty when the slice blocks
512
+ * carry no captured ids (pre-v3 index without the `calleeIds` column, or
513
+ * non-overloading/synthetic blocks) — the bridge then falls back to the
514
+ * leaf-name match per U5. A query failure is logged and degrades to empty (no
515
+ * proof), never throws (the inter-procedural reach is still returned). Mirrors
516
+ * `calleesOfBlocks` exactly — same shape, same swallow-on-error contract.
517
+ */
518
+ private calleeIdsOfBlocks;
519
+ /**
520
+ * Delegates the PDG impact engine to `pdg-impact.ts`.
521
+ *
522
+ * The private method remains as the LocalBackend dispatch seam so existing
523
+ * tests can keep asserting that `mode:'pdg'` routes through the PDG
524
+ * statement engine before LocalBackend attaches interprocedural symbol reach.
525
+ * The traversal/projection/result assembly lives in the extracted helper
526
+ * module.
527
+ */
528
+ private _runImpactPDG;
443
529
  /**
444
530
  * #1858 — epistemic lower-bound detection.
445
531
  *