gitnexus 1.6.8-rc.3 → 1.6.8-rc.30

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 (230) hide show
  1. package/README.md +23 -3
  2. package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
  3. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
  4. package/dist/_shared/scope-resolution/symbol-definition.d.ts +4 -0
  5. package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
  6. package/dist/cli/ai-context.js +2 -1
  7. package/dist/cli/analyze-config.js +50 -0
  8. package/dist/cli/analyze.d.ts +30 -0
  9. package/dist/cli/analyze.js +131 -6
  10. package/dist/cli/clean.d.ts +1 -0
  11. package/dist/cli/clean.js +43 -1
  12. package/dist/cli/embedding-dims.d.ts +33 -0
  13. package/dist/cli/embedding-dims.js +39 -0
  14. package/dist/cli/eval-server.js +43 -0
  15. package/dist/cli/help-i18n.js +16 -0
  16. package/dist/cli/i18n/en.d.ts +18 -1
  17. package/dist/cli/i18n/en.js +18 -1
  18. package/dist/cli/i18n/resources.d.ts +35 -1
  19. package/dist/cli/i18n/zh-CN.d.ts +17 -0
  20. package/dist/cli/i18n/zh-CN.js +18 -1
  21. package/dist/cli/index.js +84 -1
  22. package/dist/cli/list.js +12 -0
  23. package/dist/cli/mcp.d.ts +6 -1
  24. package/dist/cli/mcp.js +26 -1
  25. package/dist/cli/optional-grammars.d.ts +6 -8
  26. package/dist/cli/optional-grammars.js +8 -11
  27. package/dist/cli/setup.d.ts +3 -1
  28. package/dist/cli/setup.js +67 -17
  29. package/dist/cli/skill-gen.js +2 -1
  30. package/dist/cli/status.js +26 -5
  31. package/dist/cli/tool.d.ts +12 -1
  32. package/dist/cli/tool.js +45 -3
  33. package/dist/core/embeddings/embedder.js +4 -0
  34. package/dist/core/embeddings/http-client.d.ts +7 -0
  35. package/dist/core/embeddings/http-client.js +5 -3
  36. package/dist/core/embeddings/onnxruntime-common-resolver.d.ts +6 -0
  37. package/dist/core/embeddings/onnxruntime-common-resolver.js +130 -0
  38. package/dist/core/graph/import-cycles.d.ts +10 -0
  39. package/dist/core/graph/import-cycles.js +103 -0
  40. package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
  41. package/dist/core/group/extractors/http-patterns/java.js +3 -50
  42. package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
  43. package/dist/core/group/extractors/include-extractor.js +7 -7
  44. package/dist/core/incremental/subgraph-extract.js +12 -1
  45. package/dist/core/ingestion/cfg/cfg-builder.d.ts +64 -0
  46. package/dist/core/ingestion/cfg/cfg-builder.js +129 -0
  47. package/dist/core/ingestion/cfg/collect.d.ts +30 -0
  48. package/dist/core/ingestion/cfg/collect.js +34 -0
  49. package/dist/core/ingestion/cfg/control-flow-context.d.ts +97 -0
  50. package/dist/core/ingestion/cfg/control-flow-context.js +113 -0
  51. package/dist/core/ingestion/cfg/emit.d.ts +144 -0
  52. package/dist/core/ingestion/cfg/emit.js +315 -0
  53. package/dist/core/ingestion/cfg/reaching-defs.d.ts +90 -0
  54. package/dist/core/ingestion/cfg/reaching-defs.js +364 -0
  55. package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
  56. package/dist/core/ingestion/cfg/traversal-result.js +2 -0
  57. package/dist/core/ingestion/cfg/types.d.ts +231 -0
  58. package/dist/core/ingestion/cfg/types.js +13 -0
  59. package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +144 -0
  60. package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1032 -0
  61. package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
  62. package/dist/core/ingestion/cfg/visitors/typescript.js +584 -0
  63. package/dist/core/ingestion/language-provider.d.ts +36 -2
  64. package/dist/core/ingestion/languages/c-cpp.js +11 -4
  65. package/dist/core/ingestion/languages/cpp/arity-metadata.js +6 -2
  66. package/dist/core/ingestion/languages/cpp/captures.js +24 -1
  67. package/dist/core/ingestion/languages/cpp/query.js +23 -0
  68. package/dist/core/ingestion/languages/java.js +3 -0
  69. package/dist/core/ingestion/languages/kotlin/query.js +3 -2
  70. package/dist/core/ingestion/languages/kotlin.js +5 -1
  71. package/dist/core/ingestion/languages/typescript.js +5 -0
  72. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
  73. package/dist/core/ingestion/method-extractors/generic.js +1 -0
  74. package/dist/core/ingestion/method-types.d.ts +2 -0
  75. package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
  76. package/dist/core/ingestion/model/symbol-table.js +1 -0
  77. package/dist/core/ingestion/parsing-processor.js +22 -0
  78. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  79. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  80. package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
  81. package/dist/core/ingestion/pipeline-phases/routes.js +64 -14
  82. package/dist/core/ingestion/pipeline-phases/taint-summaries.d.ts +30 -0
  83. package/dist/core/ingestion/pipeline-phases/taint-summaries.js +81 -0
  84. package/dist/core/ingestion/pipeline.d.ts +70 -0
  85. package/dist/core/ingestion/pipeline.js +5 -1
  86. package/dist/core/ingestion/route-extractors/spring-shared.d.ts +50 -0
  87. package/dist/core/ingestion/route-extractors/spring-shared.js +80 -0
  88. package/dist/core/ingestion/route-extractors/spring.d.ts +35 -0
  89. package/dist/core/ingestion/route-extractors/spring.js +136 -0
  90. package/dist/core/ingestion/scope-extractor.js +3 -0
  91. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +12 -0
  92. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +145 -24
  93. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +7 -0
  94. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +21 -0
  95. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
  96. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +38 -0
  97. package/dist/core/ingestion/scope-resolution/pipeline/run.js +243 -0
  98. package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
  99. package/dist/core/ingestion/taint/emit.d.ts +124 -0
  100. package/dist/core/ingestion/taint/emit.js +204 -0
  101. package/dist/core/ingestion/taint/interproc-emit.d.ts +50 -0
  102. package/dist/core/ingestion/taint/interproc-emit.js +88 -0
  103. package/dist/core/ingestion/taint/interproc-solver.d.ts +110 -0
  104. package/dist/core/ingestion/taint/interproc-solver.js +312 -0
  105. package/dist/core/ingestion/taint/match.d.ts +153 -0
  106. package/dist/core/ingestion/taint/match.js +278 -0
  107. package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
  108. package/dist/core/ingestion/taint/path-codec.js +190 -0
  109. package/dist/core/ingestion/taint/propagate.d.ts +216 -0
  110. package/dist/core/ingestion/taint/propagate.js +659 -0
  111. package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
  112. package/dist/core/ingestion/taint/site-safety.js +98 -0
  113. package/dist/core/ingestion/taint/source-sink-config.d.ts +107 -23
  114. package/dist/core/ingestion/taint/source-sink-config.js +35 -12
  115. package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
  116. package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
  117. package/dist/core/ingestion/taint/summary-harvest-driver.d.ts +56 -0
  118. package/dist/core/ingestion/taint/summary-harvest-driver.js +118 -0
  119. package/dist/core/ingestion/taint/summary-harvest.d.ts +89 -0
  120. package/dist/core/ingestion/taint/summary-harvest.js +537 -0
  121. package/dist/core/ingestion/taint/summary-model.d.ts +200 -0
  122. package/dist/core/ingestion/taint/summary-model.js +86 -0
  123. package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
  124. package/dist/core/ingestion/taint/typescript-model.js +102 -0
  125. package/dist/core/ingestion/utils/method-props.js +1 -0
  126. package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
  127. package/dist/core/ingestion/workers/clone-safety.js +465 -0
  128. package/dist/core/ingestion/workers/parse-worker.d.ts +11 -0
  129. package/dist/core/ingestion/workers/parse-worker.js +70 -53
  130. package/dist/core/ingestion/workers/post-result.d.ts +22 -0
  131. package/dist/core/ingestion/workers/post-result.js +87 -0
  132. package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
  133. package/dist/core/ingestion/workers/result-merge.js +43 -0
  134. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
  135. package/dist/core/ingestion/workers/worker-pool.js +33 -16
  136. package/dist/core/lbug/lbug-adapter.d.ts +18 -0
  137. package/dist/core/lbug/lbug-adapter.js +55 -0
  138. package/dist/core/run-analyze.d.ts +93 -0
  139. package/dist/core/run-analyze.js +301 -26
  140. package/dist/core/tree-sitter/parser-loader.js +5 -4
  141. package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
  142. package/dist/core/tree-sitter/vendored-grammars.js +57 -0
  143. package/dist/mcp/core/embedder.js +4 -0
  144. package/dist/mcp/http-transport.d.ts +134 -0
  145. package/dist/mcp/http-transport.js +503 -0
  146. package/dist/mcp/local/local-backend.d.ts +86 -2
  147. package/dist/mcp/local/local-backend.js +788 -33
  148. package/dist/mcp/resources.js +2 -1
  149. package/dist/mcp/tools.d.ts +9 -0
  150. package/dist/mcp/tools.js +128 -4
  151. package/dist/server/analyze-launch.d.ts +29 -0
  152. package/dist/server/analyze-launch.js +137 -0
  153. package/dist/server/analyze-upload.d.ts +33 -0
  154. package/dist/server/analyze-upload.js +123 -0
  155. package/dist/server/analyze-worker-ipc.d.ts +58 -0
  156. package/dist/server/analyze-worker-ipc.js +16 -0
  157. package/dist/server/analyze-worker.d.ts +17 -1
  158. package/dist/server/analyze-worker.js +7 -1
  159. package/dist/server/api.js +71 -163
  160. package/dist/server/git-clone.d.ts +1 -0
  161. package/dist/server/git-clone.js +1 -1
  162. package/dist/server/mcp-http.d.ts +5 -6
  163. package/dist/server/mcp-http.js +9 -82
  164. package/dist/server/middleware.d.ts +40 -0
  165. package/dist/server/middleware.js +92 -0
  166. package/dist/server/private-ip.d.ts +1 -0
  167. package/dist/server/private-ip.js +15 -0
  168. package/dist/server/upload-ingest.d.ts +56 -0
  169. package/dist/server/upload-ingest.js +276 -0
  170. package/dist/server/upload-paths.d.ts +31 -0
  171. package/dist/server/upload-paths.js +51 -0
  172. package/dist/server/upload-sweep.d.ts +20 -0
  173. package/dist/server/upload-sweep.js +57 -0
  174. package/dist/storage/branch-index.d.ts +52 -0
  175. package/dist/storage/branch-index.js +65 -0
  176. package/dist/storage/git.d.ts +11 -0
  177. package/dist/storage/git.js +28 -0
  178. package/dist/storage/parse-cache.d.ts +22 -1
  179. package/dist/storage/parse-cache.js +32 -10
  180. package/dist/storage/repo-manager.d.ts +132 -10
  181. package/dist/storage/repo-manager.js +162 -21
  182. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +131 -11
  183. package/hooks/claude/gitnexus-hook.cjs +124 -12
  184. package/hooks/claude/hook-db-lock-probe.cjs +496 -26
  185. package/package.json +4 -2
  186. package/scripts/assert-publish-grammar-coverage.cjs +31 -0
  187. package/scripts/build-tree-sitter-grammars.cjs +16 -10
  188. package/scripts/cross-platform-tests.ts +1 -0
  189. package/skills/gitnexus-debugging.md +4 -4
  190. package/skills/gitnexus-exploring.md +3 -3
  191. package/skills/gitnexus-guide.md +11 -0
  192. package/skills/gitnexus-refactoring.md +1 -1
  193. package/skills/gitnexus-taint-analysis.md +178 -0
  194. package/vendor/tree-sitter-c/package.json +1 -1
  195. package/vendor/tree-sitter-dart/package.json +1 -1
  196. package/vendor/tree-sitter-kotlin/package.json +1 -1
  197. package/vendor/tree-sitter-proto/package.json +1 -1
  198. package/vendor/tree-sitter-swift/package.json +1 -1
  199. package/web/assets/{agent-CKPMqImC.js → agent-C01BpbCi.js} +91 -91
  200. package/web/assets/{architectureDiagram-UL44E2DR-D1EXI0zA.js → architectureDiagram-UL44E2DR-BETib0Ig.js} +1 -1
  201. package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-ECvRIdTA.js} +1 -1
  202. package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-htm8QilK.js} +1 -1
  203. package/web/assets/{classDiagram-KGZ6W3CR-Bv93af_b.js → classDiagram-KGZ6W3CR-vJcJG7SW.js} +1 -1
  204. package/web/assets/{classDiagram-v2-72OJOZXJ-CLfEqHUa.js → classDiagram-v2-72OJOZXJ-BCJH9y5K.js} +1 -1
  205. package/web/assets/{context-builder-_HS0v2ma.js → context-builder-C6wAZwss.js} +10 -2
  206. package/web/assets/{diagram-3NCE3AQN-BqAtKUpW.js → diagram-3NCE3AQN-DgbGdjct.js} +1 -1
  207. package/web/assets/{diagram-GF46GFSD-CaBG6n6o.js → diagram-GF46GFSD-DkQKRTzf.js} +1 -1
  208. package/web/assets/{diagram-QXG6HAR7-CZ-O3rcV.js → diagram-QXG6HAR7-fO9iaqTW.js} +1 -1
  209. package/web/assets/{diagram-WEQXMOUZ-P4lSL4GH.js → diagram-WEQXMOUZ-BFGWbiEm.js} +1 -1
  210. package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-IErmkD3i.js} +1 -1
  211. package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-BOtRGXXs.js} +1 -1
  212. package/web/assets/index-DKmSSk0F.js +626 -0
  213. package/web/assets/index-DeuHlyzm.css +2 -0
  214. package/web/assets/{infoDiagram-3YFTVSEB-DmLICZx1.js → infoDiagram-3YFTVSEB-DKRlh1as.js} +1 -1
  215. package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-B07zhx7A.js} +1 -1
  216. package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-_saj34_J.js} +1 -1
  217. package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-iyCoM6pR.js} +1 -1
  218. package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-DOeaMPvU.js} +1 -1
  219. package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-DMhAsO2o.js} +1 -1
  220. package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-CQZ5FzAV.js} +1 -1
  221. package/web/assets/{stateDiagram-7D4R322I-CB2nABwH.js → stateDiagram-7D4R322I-kYVKDLkw.js} +1 -1
  222. package/web/assets/{stateDiagram-v2-36443NZ5-COBGd2RL.js → stateDiagram-v2-36443NZ5-DvvdUOce.js} +1 -1
  223. package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-CxBfIWWp.js} +1 -1
  224. package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-D0m2r7IU.js} +1 -1
  225. package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-yBZJHfwp.js} +1 -1
  226. package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-CQJMSAIO.js} +1 -1
  227. package/web/index.html +2 -2
  228. package/scripts/materialize-vendor-grammars.cjs +0 -97
  229. package/web/assets/index-BKWA-m7o.css +0 -2
  230. package/web/assets/index-CG6q8eTs.js +0 -626
package/README.md CHANGED
@@ -34,7 +34,7 @@ That's it. This indexes the codebase, installs agent skills, registers Claude Co
34
34
 
35
35
  To configure MCP for your editor, run `npx gitnexus setup` once — or set it up manually below.
36
36
 
37
- `gitnexus setup` auto-detects your editors and writes the correct global MCP config. You only need to run it once.
37
+ `gitnexus setup` auto-detects your editors and writes the correct global MCP config. You only need to run it once. To configure only selected integrations, pass `--coding-agent`/`-c` with a comma-separated list or repeat the option, for example `gitnexus setup -c cursor,codex`.
38
38
 
39
39
  ### Editor Support
40
40
 
@@ -134,7 +134,7 @@ Your AI agent gets these tools automatically:
134
134
  | `rename` | Multi-file coordinated rename with graph + text search | Optional |
135
135
  | `cypher` | Raw Cypher graph queries | Optional |
136
136
 
137
- > With one indexed repo, the `repo` param is optional. With multiple, specify which: `query({query: "auth", repo: "my-app"})`.
137
+ > With one indexed repo, the `repo` param is optional. With multiple, specify which: `query({search_query: "auth", repo: "my-app"})`.
138
138
 
139
139
  ## MCP Resources
140
140
 
@@ -158,7 +158,7 @@ Your AI agent gets these tools automatically:
158
158
  ## CLI Commands
159
159
 
160
160
  ```bash
161
- gitnexus setup # Configure MCP for your editors (one-time)
161
+ gitnexus setup # Configure MCP for detected editors (one-time; use -c to select)
162
162
  gitnexus uninstall # Preview removal of GitNexus MCP/skills/hooks (add --force to apply)
163
163
  gitnexus analyze [path] # Index a repository (or update stale index)
164
164
  gitnexus analyze --repair-fts # Fast path: rebuild/verify only FTS indexes on existing index data
@@ -436,6 +436,26 @@ After scope resolution, analyze prunes inert block-local value symbols (a functi
436
436
 
437
437
  Programmatic callers can pass `keepLocalValueSymbols: true` in `PipelineOptions` instead of setting the env var.
438
438
 
439
+ ### Hook augmentation/notifications are silently skipped
440
+
441
+ The Claude Code / Antigravity hooks intentionally stay **silent** on normal skip
442
+ paths so strict hook runners (e.g. Codex `PreToolUse`) never see unexpected
443
+ output. A search may not be augmented — or a stale-index reminder may not appear
444
+ on stderr — when the GitNexus MCP server owns the repo DB, when the DB-lock probe
445
+ times out and fails closed, or when the index is already current.
446
+
447
+ To see why a hook skipped, set `GITNEXUS_DEBUG=1` and re-run the action — the hook
448
+ writes the reason (e.g. `[GitNexus] augment skipped: MCP server owns DB`) and the
449
+ stale-index hint to its stderr:
450
+
451
+ ```bash
452
+ GITNEXUS_DEBUG=1 <your command> # surfaces hook skip/diagnostic reasons on stderr
453
+ ```
454
+
455
+ Only `GITNEXUS_DEBUG=1` and `GITNEXUS_DEBUG=true` enable diagnostics; every other
456
+ value (including `0` and `false`) is treated as off. Diagnostics go to stderr
457
+ only — the hook's structured stdout (the JSON the agent consumes) is unaffected.
458
+
439
459
  ## Privacy
440
460
 
441
461
  - All processing happens locally on your machine
@@ -96,5 +96,26 @@ export interface ParsedFile {
96
96
  * side effects — the contract default) leave this undefined.
97
97
  */
98
98
  readonly captureSideChannel?: unknown;
99
+ /**
100
+ * Per-function control-flow graphs for this file (#2081 M1, PDG/taint
101
+ * substrate). A DISTINCT field from {@link captureSideChannel} — different
102
+ * producer, consumer, and lifecycle: the worker builds it from the
103
+ * tree-sitter AST via `LanguageProvider.cfgVisitor` (only on a `--pdg` run),
104
+ * and scope-resolution emits BasicBlock nodes + CFG edges from it while the
105
+ * disk-backed ParsedFile store is still live (it is NOT a capture-time
106
+ * marker the resolver restores into module maps). Kept separate so a future
107
+ * change to either channel's shape invalidates independently.
108
+ *
109
+ * Shared / ingestion code treats this as opaque (`unknown`) per AGENTS.md.
110
+ * Concretely it is a `readonly FunctionCfg[]` (see
111
+ * `core/ingestion/cfg/types.ts`) — plain JSON-serializable data (no AST
112
+ * refs, no class instances) so it round-trips through the parse cache and
113
+ * the `parsedfile-store` (whose interning reviver keys on `nodeId`, which
114
+ * these blocks/edges deliberately lack).
115
+ *
116
+ * Optional: `undefined` on non-`--pdg` runs and for languages with no
117
+ * `cfgVisitor` — the default for every run today.
118
+ */
119
+ readonly cfgSideChannel?: unknown;
99
120
  }
100
121
  //# sourceMappingURL=parsed-file.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parsed-file.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/parsed-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oDAAoD;IACpD,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,SAAS,YAAY,EAAE,CAAC;IAChD;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAChD,QAAQ,CAAC,cAAc,EAAE,SAAS,aAAa,EAAE,CAAC;IAClD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CACvC"}
1
+ {"version":3,"file":"parsed-file.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/parsed-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oDAAoD;IACpD,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,SAAS,YAAY,EAAE,CAAC;IAChD;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAChD,QAAQ,CAAC,cAAc,EAAE,SAAS,aAAa,EAAE,CAAC;IAClD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAEtC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC"}
@@ -54,6 +54,10 @@ export interface SymbolDefinition {
54
54
  * Currently used by C++ overload ranking to exclude explicit constructors
55
55
  * from implicit user-defined conversion candidates. */
56
56
  isExplicit?: boolean;
57
+ /** True when the callable is declared unavailable (for example C++ `= delete`).
58
+ * Unavailable callables still participate in overload selection, but a
59
+ * selected unavailable target must suppress edge emission. */
60
+ isDeleted?: boolean;
57
61
  /** Links Method/Constructor/Property to owning Class/Struct/Trait nodeId */
58
62
  ownerId?: string;
59
63
  /** #1982/#1993: bridge-held enclosing-namespace path (e.g. `NS1`, `Outer.Inner`)
@@ -1 +1 @@
1
- {"version":3,"file":"symbol-definition.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/symbol-definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,WAAW,kBAAkB;IACjC,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,gBAAgB,GAAG,SAAS,CAAC;IACjE,4CAA4C;IAC5C,WAAW,EAAE,OAAO,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;IAC3E,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;IAChB;;+FAE2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;iHAC6G;IAC7G,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;uFACmF;IACnF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;4FACwF;IACxF,oBAAoB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC5C,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gGAAgG;IAChG,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;;yEAKqE;IACrE,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;4DAEwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;iEAO6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B"}
1
+ {"version":3,"file":"symbol-definition.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/symbol-definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,WAAW,kBAAkB;IACjC,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,gBAAgB,GAAG,SAAS,CAAC;IACjE,4CAA4C;IAC5C,WAAW,EAAE,OAAO,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;IAC3E,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;IAChB;;+FAE2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;iHAC6G;IAC7G,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;uFACmF;IACnF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;4FACwF;IACxF,oBAAoB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC5C,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gGAAgG;IAChG,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;;yEAKqE;IACrE,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;4DAEwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;mEAE+D;IAC/D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;iEAO6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B"}
@@ -134,8 +134,9 @@ This project is indexed by GitNexus as **${projectName}**${noStats ? '' : ` (${s
134
134
  - **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run \`impact({target: "symbolName", direction: "upstream"})\` and report the blast radius (direct callers, affected processes, risk level) to the user.
135
135
  - **MUST run \`detect_changes()\` before committing** to verify your changes only affect expected symbols and execution flows. For regression review, compare against the default branch: \`detect_changes({scope: "compare", base_ref: ${JSON.stringify(markdownSafeBranch(defaultBranch))}})\`.
136
136
  - **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
137
- - When exploring unfamiliar code, use \`query({query: "concept"})\` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
137
+ - When exploring unfamiliar code, use \`query({search_query: "concept"})\` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
138
138
  - When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use \`context({name: "symbolName"})\`.
139
+ - For security review, \`explain({target: "fileOrSymbol"})\` lists taint findings (source→sink flows; needs \`analyze --pdg\`).
139
140
 
140
141
  ## Never Do
141
142
 
@@ -63,6 +63,7 @@ const KEY_SPECS = {
63
63
  skipContextFiles: { target: 'skipAgentsMd', kind: 'boolean' },
64
64
  skipAiContext: { target: 'skipAgentsMd', kind: 'boolean' },
65
65
  skipSkills: { target: 'skipSkills', kind: 'boolean' },
66
+ pdg: { target: 'pdg', kind: 'boolean' },
66
67
  indexOnly: { target: 'indexOnly', kind: 'boolean' },
67
68
  stats: { target: 'stats', kind: 'boolean' },
68
69
  noStats: { target: 'stats', kind: 'boolean-negate' },
@@ -78,6 +79,22 @@ const KEY_SPECS = {
78
79
  embeddingBatchSize: { target: 'embeddingBatchSize', kind: 'numeric-string' },
79
80
  embeddingSubBatchSize: { target: 'embeddingSubBatchSize', kind: 'numeric-string' },
80
81
  embeddingDevice: { target: 'embeddingDevice', kind: 'string' },
82
+ // #1589/#1852 residual — extra fetch-wrapper function names to treat as HTTP
83
+ // consumers. The auto-detector only flags functions that call the bare global
84
+ // `fetch()`; a wrapper built on axios / a custom client, or named outside the
85
+ // built-in convention set, is otherwise invisible to route_map consumers.
86
+ // Listing it here adds it to the cross-file consumer scan.
87
+ fetchWrappers: { target: 'fetchWrappers', kind: 'string-array' },
88
+ // Auth token AND dims are intentionally CLI/env-only — no embeddingAuthToken
89
+ // or embeddingDims key here:
90
+ // - the token keeps secrets out of a committed .gitnexusrc;
91
+ // - dims cannot take effect from .gitnexusrc anyway — schema.ts reads
92
+ // GITNEXUS_EMBEDDING_DIMS at module-load (before .gitnexusrc is loaded in
93
+ // analyzeCommandImpl), so a config value would size nothing and silently
94
+ // mismatch the vector column. Use --embedding-dims or GITNEXUS_EMBEDDING_DIMS.
95
+ // (URL/MODEL are safe as config keys: they are read lazily at runtime, not at module-load.)
96
+ embeddingBaseUrl: { target: 'embeddingBaseUrl', kind: 'string' },
97
+ embeddingModel: { target: 'embeddingModel', kind: 'string' },
81
98
  };
82
99
  /** Top-level container key for the nested form; not itself an `AnalyzeOptions` field. */
83
100
  const NESTED_KEY = 'analyze';
@@ -195,6 +212,39 @@ const normalizeValue = (kind, value, key) => {
195
212
  }
196
213
  return trimmed;
197
214
  }
215
+ case 'string-array': {
216
+ // Generic shared validator — `source` already names the config key, so
217
+ // messages here stay key-agnostic (no fetch-wrapper coupling in the
218
+ // shared normalizer; #1589/#1852 review F7).
219
+ if (!Array.isArray(value)) {
220
+ throw new GitNexusRcError(`${source} must be an array of strings.`);
221
+ }
222
+ const names = [];
223
+ for (const item of value) {
224
+ if (typeof item !== 'string') {
225
+ throw new GitNexusRcError(`${source} entries must all be strings.`);
226
+ }
227
+ const trimmed = item.trim();
228
+ if (!trimmed) {
229
+ throw new GitNexusRcError(`${source} entries must not be empty.`);
230
+ }
231
+ assertNoHiddenChars(trimmed, source);
232
+ // Values may be interpolated into a RegExp downstream. Restrict to
233
+ // identifier / member-access shapes so a config value can never smuggle
234
+ // regex metacharacters into a consumer.
235
+ if (!/^[A-Za-z_$][A-Za-z0-9_$.]*$/.test(trimmed)) {
236
+ throw new GitNexusRcError(`${source} entry "${trimmed}" must be an identifier or member name ` +
237
+ `(letters, digits, _, $, . — e.g. "client.get").`);
238
+ }
239
+ names.push(trimmed);
240
+ }
241
+ if (names.length === 0) {
242
+ throw new GitNexusRcError(`${source} must list at least one string.`);
243
+ }
244
+ // De-duplicate and cap to a sane bound so a pathological config cannot
245
+ // blow up the consumer scan's alternation.
246
+ return Array.from(new Set(names)).slice(0, 100);
247
+ }
198
248
  case 'numeric-string': {
199
249
  // Mirror Commander's contract: these options reach the existing CLI
200
250
  // validation as strings. Accept a JSON number or a string; normalize to a
@@ -37,6 +37,12 @@ export interface AnalyzeOptions {
37
37
  verbose?: boolean;
38
38
  /** Skip AGENTS.md and CLAUDE.md gitnexus block updates. */
39
39
  skipAgentsMd?: boolean;
40
+ /**
41
+ * Build the control-flow-graph / PDG substrate (#2081 M1). Opt-in; off by
42
+ * default. Threaded to both the worker (CFG build) and scope-resolution
43
+ * (BasicBlock/CFG emit).
44
+ */
45
+ pdg?: boolean;
40
46
  /**
41
47
  * Stats inclusion in AGENTS.md and CLAUDE.md.
42
48
  *
@@ -58,6 +64,14 @@ export interface AnalyzeOptions {
58
64
  * before being threaded into the generated AGENTS.md / CLAUDE.md content.
59
65
  */
60
66
  defaultBranch?: string;
67
+ /**
68
+ * Index-branch selector (#2106). From `--branch`. Distinct from
69
+ * `defaultBranch` (cosmetic base_ref): this routes the index to a per-branch
70
+ * slot. NOT sourced from `.gitnexusrc` — the `.gitnexusrc` `branch` key is an
71
+ * alias for `defaultBranch` and must not change index placement. Defaults to
72
+ * the checked-out branch inside `runFullAnalysis` when omitted.
73
+ */
74
+ branch?: string;
61
75
  /** Pure index mode: skip all file injection (AGENTS.md, CLAUDE.md, skills). */
62
76
  indexOnly?: boolean;
63
77
  /** Index the folder even when no .git directory is present. */
@@ -93,6 +107,22 @@ export interface AnalyzeOptions {
93
107
  embeddingBatchSize?: string;
94
108
  embeddingSubBatchSize?: string;
95
109
  embeddingDevice?: string;
110
+ /**
111
+ * Extra fetch-wrapper function names to treat as HTTP consumers (#1589/#1852
112
+ * residual). Supplied via `.gitnexusrc` `fetchWrappers: [...]`. Threaded into
113
+ * the routes phase, where the cross-file consumer scan unions them with the
114
+ * auto-detected `fetch()` wrappers so a custom/axios-based wrapper named
115
+ * outside the built-in convention still produces `route_map` consumers.
116
+ */
117
+ fetchWrappers?: string[];
118
+ /** OpenAI-compatible embeddings base URL (incl. /v1). Overrides GITNEXUS_EMBEDDING_URL. */
119
+ embeddingBaseUrl?: string;
120
+ /** Embedding model name. Overrides GITNEXUS_EMBEDDING_MODEL. */
121
+ embeddingModel?: string;
122
+ /** Bearer token for the embeddings endpoint. Overrides GITNEXUS_EMBEDDING_API_KEY. Never logged. */
123
+ embeddingAuthToken?: string;
124
+ /** Embedding vector dimensions (positive integer string). Overrides GITNEXUS_EMBEDDING_DIMS. */
125
+ embeddingDims?: string;
96
126
  }
97
127
  /**
98
128
  * Whether the post-index skill step should run.
@@ -23,8 +23,10 @@ import { warnMissingOptionalGrammars, getOptionalGrammarExtensions } from './opt
23
23
  import { glob } from 'glob';
24
24
  import fs from 'fs/promises';
25
25
  import { cliError } from './cli-message.js';
26
+ import { EMBEDDING_DIMS_ERROR, normalizeEmbeddingDims } from './embedding-dims.js';
26
27
  import { formatElapsed } from './format-elapsed.js';
27
28
  import { isHfDownloadFailure } from '../core/embeddings/hf-env.js';
29
+ import { safeUrl } from '../core/embeddings/http-client.js';
28
30
  import { isLocalEmbeddingRuntimeBlockerMessage } from '../core/embeddings/runtime-support.js';
29
31
  import { warnIfNpm11NpxRisk } from './resolve-invocation.js';
30
32
  // Capture stderr.write at module load BEFORE anything (LadybugDB native
@@ -446,6 +448,10 @@ const ANALYZE_CLI_ENV_KEYS = [
446
448
  'GITNEXUS_EMBEDDING_SUB_BATCH_SIZE',
447
449
  'GITNEXUS_EMBEDDING_DEVICE',
448
450
  'GITNEXUS_ANALYZE_PROGRESS_ACTIVE',
451
+ 'GITNEXUS_EMBEDDING_URL',
452
+ 'GITNEXUS_EMBEDDING_MODEL',
453
+ 'GITNEXUS_EMBEDDING_API_KEY',
454
+ 'GITNEXUS_EMBEDDING_DIMS',
449
455
  ];
450
456
  const snapshotAnalyzeEnv = () => {
451
457
  const snap = {};
@@ -551,6 +557,21 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
551
557
  return;
552
558
  }
553
559
  }
560
+ // Validate the index-branch selector (#2106) the same way, so a malformed
561
+ // `--branch` exits before any expensive analysis starts. Capture the TRIMMED
562
+ // return so a whitespace-padded value (e.g. " feature" from shell completion)
563
+ // normalizes before the checked-out-branch mismatch guard and slug — otherwise
564
+ // it would false-reject on-branch or create a ghost index when detached.
565
+ if (cliOptions?.branch !== undefined) {
566
+ try {
567
+ cliOptions.branch = validateBranchName(cliOptions.branch, '--branch');
568
+ }
569
+ catch (err) {
570
+ cliError(` ${err instanceof Error ? err.message : String(err)}\n`);
571
+ process.exitCode = 1;
572
+ return;
573
+ }
574
+ }
554
575
  // ── Load .gitnexusrc and merge: CLI flags override config (#243) ───
555
576
  // Parse/validate before the progress bar so a malformed config produces an
556
577
  // actionable error and exits before any expensive analysis starts.
@@ -675,6 +696,95 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
675
696
  }
676
697
  process.env.GITNEXUS_EMBEDDING_DEVICE = options.embeddingDevice;
677
698
  }
699
+ // --- Custom HTTP embedding endpoint flags (override GITNEXUS_EMBEDDING_* env vars) ---
700
+ const anyHttpEmbedFlag = options.embeddingBaseUrl !== undefined ||
701
+ options.embeddingModel !== undefined ||
702
+ options.embeddingAuthToken !== undefined ||
703
+ options.embeddingDims !== undefined;
704
+ if (options.embeddingBaseUrl !== undefined) {
705
+ const url = options.embeddingBaseUrl.trim();
706
+ if (url.length === 0) {
707
+ cliError(' --embedding-base-url must not be empty.\n');
708
+ process.exitCode = 1;
709
+ return;
710
+ }
711
+ let parsed;
712
+ try {
713
+ parsed = new URL(url);
714
+ }
715
+ catch {
716
+ cliError(` --embedding-base-url is not a valid URL: "${url}".\n`);
717
+ process.exitCode = 1;
718
+ return;
719
+ }
720
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
721
+ cliError(' --embedding-base-url must use http:// or https://.\n');
722
+ process.exitCode = 1;
723
+ return;
724
+ }
725
+ // http-client strips trailing slashes; store as given (trimmed).
726
+ process.env.GITNEXUS_EMBEDDING_URL = url;
727
+ }
728
+ if (options.embeddingModel !== undefined) {
729
+ const model = options.embeddingModel.trim();
730
+ if (model.length === 0) {
731
+ cliError(' --embedding-model must not be empty.\n');
732
+ process.exitCode = 1;
733
+ return;
734
+ }
735
+ process.env.GITNEXUS_EMBEDDING_MODEL = model;
736
+ }
737
+ if (options.embeddingAuthToken !== undefined) {
738
+ const token = options.embeddingAuthToken.trim();
739
+ if (token.length === 0) {
740
+ cliError(' --embedding-auth-token must not be empty.\n');
741
+ process.exitCode = 1;
742
+ return;
743
+ }
744
+ // Never log the token value.
745
+ process.env.GITNEXUS_EMBEDDING_API_KEY = token;
746
+ }
747
+ // Validate + normalize dims through the same shared helper the preAction
748
+ // hook uses, so the CLI path, this direct/programmatic-call path, schema.ts
749
+ // (parseInt) and http-client (/^\d+$/) all agree on one canonical value.
750
+ if (options.embeddingDims !== undefined) {
751
+ const dims = normalizeEmbeddingDims(options.embeddingDims);
752
+ if (dims === null) {
753
+ cliError(` ${EMBEDDING_DIMS_ERROR}\n`);
754
+ process.exitCode = 1;
755
+ return;
756
+ }
757
+ process.env.GITNEXUS_EMBEDDING_DIMS = dims;
758
+ }
759
+ // Custom-endpoint UX, emitting at most ONE message that reflects THIS run's
760
+ // intent (not ambient env). Order matters — the first matching branch wins:
761
+ // 1. flags given but --embeddings absent: the endpoint won't be used, so
762
+ // say only that (no contradictory "Using…" line).
763
+ // 2. embeddings enabled + a complete endpoint (flags or env): confirm it,
764
+ // masking the URL via safeUrl() since a base URL may carry credentials
765
+ // in userinfo (http://user:pass@host) or a query token (?api_key=…)
766
+ // that must not land in stdout/CI logs. The auth token is never printed.
767
+ // 3. embeddings enabled but only one of URL/MODEL supplied via flags:
768
+ // http-client.isHttpMode() needs BOTH, so warn about the fallback.
769
+ // Gating on embeddingsEnabled also stops the old behaviour of printing
770
+ // "Using custom embedding endpoint" on every analyze run whenever the env
771
+ // vars happened to be set.
772
+ if (anyHttpEmbedFlag && !embeddingsEnabled) {
773
+ console.log(' Note: --embedding-* flags only apply when --embeddings is also passed; ' +
774
+ 'no embeddings will be generated this run.\n');
775
+ }
776
+ else if (embeddingsEnabled &&
777
+ process.env.GITNEXUS_EMBEDDING_URL &&
778
+ process.env.GITNEXUS_EMBEDDING_MODEL) {
779
+ console.log(` Using custom embedding endpoint: ${safeUrl(process.env.GITNEXUS_EMBEDDING_URL)} ` +
780
+ `(model: ${process.env.GITNEXUS_EMBEDDING_MODEL})\n`);
781
+ }
782
+ else if (embeddingsEnabled &&
783
+ anyHttpEmbedFlag &&
784
+ (process.env.GITNEXUS_EMBEDDING_URL || process.env.GITNEXUS_EMBEDDING_MODEL)) {
785
+ console.log(' Note: custom HTTP embeddings require BOTH --embedding-base-url and --embedding-model ' +
786
+ '(or the matching env vars). Falling back to local ONNX embeddings.\n');
787
+ }
678
788
  if (options.repairFts && options.force) {
679
789
  cliError(' Cannot combine `--repair-fts` with `--force`. ' +
680
790
  'Use `--repair-fts` for fast FTS-only repair, or `--force` for a full rebuild.\n');
@@ -826,9 +936,15 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
826
936
  skipGit: options.skipGit,
827
937
  skipAgentsMd,
828
938
  skipSkills,
939
+ // CFG/PDG substrate opt-in (#2081 M1) — threaded to both sinks downstream.
940
+ pdg: options.pdg === true,
829
941
  // Resolved default branch (CLI > .gitnexusrc > auto-detect > "main")
830
942
  // threaded into the generated regression-compare example (#243).
831
943
  defaultBranch: resolvedDefaultBranch,
944
+ // Index-branch selector (#2106). Read straight from the CLI flag (not
945
+ // the .gitnexusrc-merged options) so the cosmetic defaultBranch config
946
+ // can never change index placement. Undefined → auto-detect in pipeline.
947
+ branch: cliOptions?.branch,
832
948
  // commander.js `.option('--no-stats', …)` registers the flag as
833
949
  // `options.stats` (boolean, default true; `false` when the user
834
950
  // passed --no-stats). Reading `options.noStats` here returns
@@ -845,6 +961,9 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
845
961
  // GITNEXUS_WORKER_POOL_SIZE env mutation. `undefined` defers to the
846
962
  // env / auto-formula fallback inside the pipeline.
847
963
  workerPoolSize,
964
+ // Extra fetch-wrapper names from `.gitnexusrc` (#1589/#1852 residual);
965
+ // forwarded to the routes phase consumer scan.
966
+ fetchWrappers: options.fetchWrappers,
848
967
  }, {
849
968
  onProgress: (_phase, percent, message) => {
850
969
  updateBar(percent, message);
@@ -863,13 +982,19 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
863
982
  // preserving the rest of the block (incl. --skills community rows). No-op
864
983
  // when the value already matches, so a routine up-to-date run is silent
865
984
  // (#1996 tri-review P2).
985
+ // Only refresh the repo-root AGENTS.md/CLAUDE.md base_ref for the
986
+ // PRIMARY/flat index (#2106 R2). A non-primary branch's up-to-date
987
+ // analyze must not churn the committed AGENTS.md — this mirrors the
988
+ // in-pipeline `if (!placement.branch)` gate around generateAIContextFiles.
866
989
  let baseRefRefreshed = [];
867
- try {
868
- const { refreshBaseRefLine } = await import('./ai-context.js');
869
- baseRefRefreshed = (await refreshBaseRefLine(repoPath, resolvedDefaultBranch, { skipAgentsMd })).files;
870
- }
871
- catch {
872
- /* best-effort — never fail the fast path over a context refresh */
990
+ if (result.isPrimaryBranch !== false) {
991
+ try {
992
+ const { refreshBaseRefLine } = await import('./ai-context.js');
993
+ baseRefRefreshed = (await refreshBaseRefLine(repoPath, resolvedDefaultBranch, { skipAgentsMd })).files;
994
+ }
995
+ catch {
996
+ /* best-effort — never fail the fast path over a context refresh */
997
+ }
873
998
  }
874
999
  clearInterval(elapsedTimer);
875
1000
  process.removeListener('SIGINT', sigintHandler);
@@ -8,4 +8,5 @@ export declare const cleanCommand: (options?: {
8
8
  force?: boolean;
9
9
  all?: boolean;
10
10
  lbugSidecars?: boolean;
11
+ branch?: string;
11
12
  }) => Promise<void>;
package/dist/cli/clean.js CHANGED
@@ -7,10 +7,52 @@
7
7
  import fs from 'fs/promises';
8
8
  import path from 'path';
9
9
  import { logger } from '../core/logger.js';
10
- import { findRepo, unregisterRepo, listRegisteredRepos, assertSafeStoragePath, UnsafeStoragePathError, } from '../storage/repo-manager.js';
10
+ import { findRepo, unregisterRepo, listRegisteredRepos, assertSafeStoragePath, getStoragePaths, removeBranchIndex, UnsafeStoragePathError, } from '../storage/repo-manager.js';
11
11
  import { cleanQuarantinedMissingShadowWals, inspectLbugSidecars, listQuarantinedMissingShadowWals, } from '../core/lbug/sidecar-recovery.js';
12
12
  import { t } from './i18n/index.js';
13
13
  export const cleanCommand = async (options) => {
14
+ // --branch <name>: remove a single non-primary branch's index (#2106 R7).
15
+ // Resolve against the RECORDED branches[] summary (never by slugging the
16
+ // user's raw input, which can disagree with the index-time-sanitized label).
17
+ if (options?.branch) {
18
+ const cwd = process.cwd();
19
+ const repo = await findRepo(cwd);
20
+ if (!repo) {
21
+ console.log(t('clean.notFoundHere'));
22
+ return;
23
+ }
24
+ const entries = await listRegisteredRepos();
25
+ const entry = entries.find((e) => path.resolve(e.path) === path.resolve(repo.repoPath));
26
+ const summary = entry?.branches?.find((b) => b.branch === options.branch);
27
+ if (!summary) {
28
+ console.log(t('clean.branchNotIndexed', { branch: options.branch }));
29
+ return;
30
+ }
31
+ const { storagePath, lbugPath } = getStoragePaths(repo.repoPath, summary.branch);
32
+ const branchDir = path.dirname(lbugPath);
33
+ // Safety guard: the target MUST live under <repo>/.gitnexus/branches/.
34
+ // assertSafeStoragePath only validates the flat `<repo>/.gitnexus`, so this
35
+ // is a dedicated branches-sub-dir check before any destructive fs.rm.
36
+ const branchesRoot = path.join(storagePath, 'branches') + path.sep;
37
+ if (!branchDir.startsWith(branchesRoot)) {
38
+ logger.error(`Refusing to clean branch index outside .gitnexus/branches: ${branchDir}`);
39
+ return;
40
+ }
41
+ if (!options.force) {
42
+ console.log(t('clean.deleteBranch', { branch: summary.branch, path: branchDir }));
43
+ console.log(`\n${t('common.runForceConfirm')}`);
44
+ return;
45
+ }
46
+ try {
47
+ await fs.rm(branchDir, { recursive: true, force: true });
48
+ await removeBranchIndex(repo.repoPath, summary.branch);
49
+ console.log(t('clean.deletedBranch', { branch: summary.branch }));
50
+ }
51
+ catch (err) {
52
+ logger.error({ err }, 'Failed to delete branch index:');
53
+ }
54
+ return;
55
+ }
14
56
  if (options?.lbugSidecars) {
15
57
  const cwd = process.cwd();
16
58
  const repo = await findRepo(cwd);
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Strict positive-integer normalization for the `--embedding-dims` flag /
3
+ * `GITNEXUS_EMBEDDING_DIMS` value.
4
+ *
5
+ * Single source of truth shared by two write paths:
6
+ * 1. the `analyze` `preAction` hook (CLI path) — it must set the env var
7
+ * BEFORE `schema.ts` reads `GITNEXUS_EMBEDDING_DIMS` at module-load time
8
+ * (the static import chain `analyze.ts → run-analyze.ts → schema.ts`
9
+ * bakes `FLOAT[dims]` into the vector-table DDL), and
10
+ * 2. `analyzeCommandImpl` (direct / programmatic-call path, which bypasses
11
+ * the commander hook).
12
+ *
13
+ * Keep this module dependency-free. `index.ts` imports it eagerly, so pulling
14
+ * in anything that transitively loads `schema.ts` (e.g. `analyze.ts`) — or
15
+ * even `cli-message.ts`, which drags in the logger + i18n — would defeat the
16
+ * lazy `import('./analyze.js')` the hook exists to enable. Callers print the
17
+ * error themselves (the hook to stderr, the impl via `cliError`).
18
+ *
19
+ * Trim-then-validate, matching the sibling URL/MODEL/TOKEN flags: surrounding
20
+ * whitespace is tolerated, but the remaining value must be all digits and
21
+ * `> 0`. This rejects scientific notation (`1e3`), hex (`0x10`), fractions
22
+ * (`3.5`), signs (`+5`/`-5`), and trailing junk (`4096x`) so the three
23
+ * downstream readers — `schema.ts` (`parseInt`), `http-client.ts` (`/^\d+$/`),
24
+ * and this helper — all agree on one canonical value. Without it, `1e3` parsed
25
+ * to `FLOAT[1]` at module-load but requested 1000-dim vectors at runtime.
26
+ */
27
+ /** Shared error message so both call sites surface identical wording. */
28
+ export declare const EMBEDDING_DIMS_ERROR = "--embedding-dims must be a positive integer.";
29
+ /**
30
+ * Returns the canonical positive-integer string (e.g. `"007"` → `"7"`), or
31
+ * `null` when the input is not a strict positive integer.
32
+ */
33
+ export declare const normalizeEmbeddingDims: (raw: string) => string | null;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Strict positive-integer normalization for the `--embedding-dims` flag /
3
+ * `GITNEXUS_EMBEDDING_DIMS` value.
4
+ *
5
+ * Single source of truth shared by two write paths:
6
+ * 1. the `analyze` `preAction` hook (CLI path) — it must set the env var
7
+ * BEFORE `schema.ts` reads `GITNEXUS_EMBEDDING_DIMS` at module-load time
8
+ * (the static import chain `analyze.ts → run-analyze.ts → schema.ts`
9
+ * bakes `FLOAT[dims]` into the vector-table DDL), and
10
+ * 2. `analyzeCommandImpl` (direct / programmatic-call path, which bypasses
11
+ * the commander hook).
12
+ *
13
+ * Keep this module dependency-free. `index.ts` imports it eagerly, so pulling
14
+ * in anything that transitively loads `schema.ts` (e.g. `analyze.ts`) — or
15
+ * even `cli-message.ts`, which drags in the logger + i18n — would defeat the
16
+ * lazy `import('./analyze.js')` the hook exists to enable. Callers print the
17
+ * error themselves (the hook to stderr, the impl via `cliError`).
18
+ *
19
+ * Trim-then-validate, matching the sibling URL/MODEL/TOKEN flags: surrounding
20
+ * whitespace is tolerated, but the remaining value must be all digits and
21
+ * `> 0`. This rejects scientific notation (`1e3`), hex (`0x10`), fractions
22
+ * (`3.5`), signs (`+5`/`-5`), and trailing junk (`4096x`) so the three
23
+ * downstream readers — `schema.ts` (`parseInt`), `http-client.ts` (`/^\d+$/`),
24
+ * and this helper — all agree on one canonical value. Without it, `1e3` parsed
25
+ * to `FLOAT[1]` at module-load but requested 1000-dim vectors at runtime.
26
+ */
27
+ /** Shared error message so both call sites surface identical wording. */
28
+ export const EMBEDDING_DIMS_ERROR = '--embedding-dims must be a positive integer.';
29
+ /**
30
+ * Returns the canonical positive-integer string (e.g. `"007"` → `"7"`), or
31
+ * `null` when the input is not a strict positive integer.
32
+ */
33
+ export const normalizeEmbeddingDims = (raw) => {
34
+ const trimmed = raw.trim();
35
+ if (!/^\d+$/.test(trimmed) || parseInt(trimmed, 10) <= 0) {
36
+ return null;
37
+ }
38
+ return String(parseInt(trimmed, 10));
39
+ };
@@ -155,7 +155,43 @@ export function formatImpactResult(result) {
155
155
  const direction = result.direction;
156
156
  const byDepth = result.byDepth || {};
157
157
  const total = result.impactedCount || 0;
158
+ // #2129 — an ambiguous bare name must not print the "isolated / safe to
159
+ // refactor" headline. Surface the per-candidate blast radius + the maximum,
160
+ // mirroring formatContextResult, so the real impact under whichever symbol the
161
+ // caller meant is visible on the text surface, not just in the JSON.
162
+ if (result.status === 'ambiguous') {
163
+ // #2129 review F11 — report the FULL match count (`totalCandidates`), not the
164
+ // truncated `candidates[]` length; note when the candidate list is capped.
165
+ const shown = result.candidates?.length ?? 0;
166
+ const total = result.totalCandidates ?? shown;
167
+ const countPhrase = total > shown ? `${total} symbols (showing ${shown})` : `${total} symbols`;
168
+ const lines = [
169
+ `${target?.name || '?'}: AMBIGUOUS — ${countPhrase} share this name. ` +
170
+ `Max blast radius ${result.maxImpactedCount ?? 0} (${result.maxRisk ?? 'UNKNOWN'} risk). ` +
171
+ `Disambiguate with --uid for one authoritative result:`,
172
+ ];
173
+ for (const c of result.candidates || []) {
174
+ lines.push(` ${c.kind} ${c.name} → ${c.filePath}:${c.line || '?'} ` +
175
+ `[${c.impactedCount ?? 0} ${direction}, risk ${c.risk ?? 'UNKNOWN'}] (uid: ${c.uid})`);
176
+ }
177
+ // #2129 review F1 — a failed per-candidate probe makes the max a lower bound.
178
+ if (result.partialProbe) {
179
+ lines.push(' ⚠️ One or more candidate probes failed — max blast radius / risk are lower bounds.');
180
+ }
181
+ return lines.join('\n');
182
+ }
158
183
  if (total === 0) {
184
+ // #1858 — "isolated" is a confident claim. If an interface / indirection
185
+ // boundary is on the path, the true count is a lower bound, not zero;
186
+ // callers binding via DI / dynamic dispatch were not traced. Say so instead.
187
+ if (result.epistemic === 'lower-bound') {
188
+ const lines = [
189
+ `${target?.name || '?'}: no direct ${direction} dependencies traced, but this is a LOWER BOUND — unresolved indirection on the path (actual impact may be higher):`,
190
+ ];
191
+ for (const b of result.boundaries || [])
192
+ lines.push(` • ${b}`);
193
+ return lines.join('\n');
194
+ }
159
195
  return `${target?.name || '?'}: No ${direction} dependencies found. This symbol appears isolated.`;
160
196
  }
161
197
  const lines = [];
@@ -164,6 +200,13 @@ export function formatImpactResult(result) {
164
200
  if (result.partial) {
165
201
  lines.push('⚠️ Partial results — graph traversal was interrupted. Deeper impacts may exist.');
166
202
  }
203
+ // #1858 — an interface / indirection boundary on the path makes this a lower
204
+ // bound; surface it so the count is not read as exhaustive.
205
+ if (result.epistemic === 'lower-bound') {
206
+ lines.push('⚠️ Lower bound — unresolved indirection on the path (callers binding via DI / dynamic dispatch are not traced; actual impact may be higher):');
207
+ for (const b of result.boundaries || [])
208
+ lines.push(` • ${b}`);
209
+ }
167
210
  lines.push('');
168
211
  const depthLabels = {
169
212
  1: 'WILL BREAK (direct)',