gitnexus 1.6.8-rc.2 → 1.6.8-rc.21

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 (193) hide show
  1. package/README.md +20 -0
  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 +1 -0
  7. package/dist/cli/analyze-config.js +40 -0
  8. package/dist/cli/analyze.d.ts +22 -0
  9. package/dist/cli/analyze.js +36 -6
  10. package/dist/cli/clean.d.ts +1 -0
  11. package/dist/cli/clean.js +43 -1
  12. package/dist/cli/eval-server.js +43 -0
  13. package/dist/cli/help-i18n.js +11 -0
  14. package/dist/cli/i18n/en.d.ts +13 -0
  15. package/dist/cli/i18n/en.js +13 -0
  16. package/dist/cli/i18n/resources.d.ts +26 -0
  17. package/dist/cli/i18n/zh-CN.d.ts +13 -0
  18. package/dist/cli/i18n/zh-CN.js +13 -0
  19. package/dist/cli/index.js +19 -0
  20. package/dist/cli/list.js +12 -0
  21. package/dist/cli/optional-grammars.d.ts +6 -8
  22. package/dist/cli/optional-grammars.js +8 -11
  23. package/dist/cli/skill-gen.js +1 -0
  24. package/dist/cli/status.js +26 -5
  25. package/dist/cli/tool.d.ts +12 -1
  26. package/dist/cli/tool.js +41 -1
  27. package/dist/core/embeddings/embedder.js +4 -0
  28. package/dist/core/embeddings/embedding-pipeline.js +27 -16
  29. package/dist/core/embeddings/onnxruntime-common-resolver.d.ts +6 -0
  30. package/dist/core/embeddings/onnxruntime-common-resolver.js +130 -0
  31. package/dist/core/graph/import-cycles.d.ts +10 -0
  32. package/dist/core/graph/import-cycles.js +103 -0
  33. package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
  34. package/dist/core/group/extractors/http-patterns/java.js +3 -50
  35. package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
  36. package/dist/core/group/extractors/include-extractor.js +7 -7
  37. package/dist/core/ingestion/cfg/cfg-builder.d.ts +64 -0
  38. package/dist/core/ingestion/cfg/cfg-builder.js +129 -0
  39. package/dist/core/ingestion/cfg/collect.d.ts +30 -0
  40. package/dist/core/ingestion/cfg/collect.js +34 -0
  41. package/dist/core/ingestion/cfg/control-flow-context.d.ts +97 -0
  42. package/dist/core/ingestion/cfg/control-flow-context.js +113 -0
  43. package/dist/core/ingestion/cfg/emit.d.ts +144 -0
  44. package/dist/core/ingestion/cfg/emit.js +315 -0
  45. package/dist/core/ingestion/cfg/reaching-defs.d.ts +90 -0
  46. package/dist/core/ingestion/cfg/reaching-defs.js +364 -0
  47. package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
  48. package/dist/core/ingestion/cfg/traversal-result.js +2 -0
  49. package/dist/core/ingestion/cfg/types.d.ts +231 -0
  50. package/dist/core/ingestion/cfg/types.js +13 -0
  51. package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +144 -0
  52. package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1032 -0
  53. package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
  54. package/dist/core/ingestion/cfg/visitors/typescript.js +584 -0
  55. package/dist/core/ingestion/language-provider.d.ts +36 -2
  56. package/dist/core/ingestion/languages/c-cpp.js +11 -4
  57. package/dist/core/ingestion/languages/cpp/arity-metadata.js +6 -2
  58. package/dist/core/ingestion/languages/cpp/captures.js +24 -1
  59. package/dist/core/ingestion/languages/cpp/query.js +23 -0
  60. package/dist/core/ingestion/languages/java.js +3 -0
  61. package/dist/core/ingestion/languages/kotlin/query.js +3 -2
  62. package/dist/core/ingestion/languages/kotlin.js +5 -1
  63. package/dist/core/ingestion/languages/typescript.js +5 -0
  64. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
  65. package/dist/core/ingestion/method-extractors/generic.js +1 -0
  66. package/dist/core/ingestion/method-types.d.ts +2 -0
  67. package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
  68. package/dist/core/ingestion/model/symbol-table.js +1 -0
  69. package/dist/core/ingestion/parsing-processor.js +22 -0
  70. package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
  71. package/dist/core/ingestion/pipeline-phases/routes.js +64 -14
  72. package/dist/core/ingestion/pipeline.d.ts +57 -0
  73. package/dist/core/ingestion/route-extractors/spring-shared.d.ts +50 -0
  74. package/dist/core/ingestion/route-extractors/spring-shared.js +80 -0
  75. package/dist/core/ingestion/route-extractors/spring.d.ts +35 -0
  76. package/dist/core/ingestion/route-extractors/spring.js +136 -0
  77. package/dist/core/ingestion/scope-extractor.js +3 -0
  78. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +12 -0
  79. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +145 -24
  80. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +6 -0
  81. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
  82. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +21 -0
  83. package/dist/core/ingestion/scope-resolution/pipeline/run.js +207 -0
  84. package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
  85. package/dist/core/ingestion/taint/emit.d.ts +124 -0
  86. package/dist/core/ingestion/taint/emit.js +204 -0
  87. package/dist/core/ingestion/taint/match.d.ts +153 -0
  88. package/dist/core/ingestion/taint/match.js +278 -0
  89. package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
  90. package/dist/core/ingestion/taint/path-codec.js +190 -0
  91. package/dist/core/ingestion/taint/propagate.d.ts +216 -0
  92. package/dist/core/ingestion/taint/propagate.js +664 -0
  93. package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
  94. package/dist/core/ingestion/taint/site-safety.js +98 -0
  95. package/dist/core/ingestion/taint/source-sink-config.d.ts +94 -23
  96. package/dist/core/ingestion/taint/source-sink-config.js +11 -11
  97. package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
  98. package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
  99. package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
  100. package/dist/core/ingestion/taint/typescript-model.js +102 -0
  101. package/dist/core/ingestion/utils/method-props.js +1 -0
  102. package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
  103. package/dist/core/ingestion/workers/clone-safety.js +465 -0
  104. package/dist/core/ingestion/workers/parse-worker.d.ts +11 -0
  105. package/dist/core/ingestion/workers/parse-worker.js +70 -53
  106. package/dist/core/ingestion/workers/post-result.d.ts +22 -0
  107. package/dist/core/ingestion/workers/post-result.js +87 -0
  108. package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
  109. package/dist/core/ingestion/workers/result-merge.js +43 -0
  110. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
  111. package/dist/core/ingestion/workers/worker-pool.js +33 -16
  112. package/dist/core/lbug/lbug-adapter.d.ts +19 -0
  113. package/dist/core/lbug/lbug-adapter.js +56 -1
  114. package/dist/core/run-analyze.d.ts +87 -0
  115. package/dist/core/run-analyze.js +280 -25
  116. package/dist/core/tree-sitter/parser-loader.js +5 -4
  117. package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
  118. package/dist/core/tree-sitter/vendored-grammars.js +57 -0
  119. package/dist/mcp/core/embedder.js +4 -0
  120. package/dist/mcp/local/local-backend.d.ts +86 -2
  121. package/dist/mcp/local/local-backend.js +657 -27
  122. package/dist/mcp/resources.js +1 -0
  123. package/dist/mcp/tools.d.ts +9 -0
  124. package/dist/mcp/tools.js +109 -0
  125. package/dist/server/analyze-launch.d.ts +29 -0
  126. package/dist/server/analyze-launch.js +137 -0
  127. package/dist/server/analyze-upload.d.ts +33 -0
  128. package/dist/server/analyze-upload.js +123 -0
  129. package/dist/server/analyze-worker-ipc.d.ts +58 -0
  130. package/dist/server/analyze-worker-ipc.js +16 -0
  131. package/dist/server/analyze-worker.d.ts +17 -1
  132. package/dist/server/analyze-worker.js +7 -1
  133. package/dist/server/api.js +46 -140
  134. package/dist/server/git-clone.d.ts +1 -0
  135. package/dist/server/git-clone.js +1 -1
  136. package/dist/server/middleware.d.ts +11 -0
  137. package/dist/server/middleware.js +27 -0
  138. package/dist/server/upload-ingest.d.ts +56 -0
  139. package/dist/server/upload-ingest.js +276 -0
  140. package/dist/server/upload-paths.d.ts +31 -0
  141. package/dist/server/upload-paths.js +51 -0
  142. package/dist/server/upload-sweep.d.ts +20 -0
  143. package/dist/server/upload-sweep.js +57 -0
  144. package/dist/storage/branch-index.d.ts +52 -0
  145. package/dist/storage/branch-index.js +65 -0
  146. package/dist/storage/git.d.ts +11 -0
  147. package/dist/storage/git.js +28 -0
  148. package/dist/storage/parse-cache.d.ts +22 -1
  149. package/dist/storage/parse-cache.js +32 -10
  150. package/dist/storage/repo-manager.d.ts +122 -10
  151. package/dist/storage/repo-manager.js +162 -21
  152. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +42 -8
  153. package/hooks/claude/gitnexus-hook.cjs +36 -8
  154. package/hooks/claude/hook-db-lock-probe.cjs +123 -2
  155. package/package.json +4 -2
  156. package/scripts/assert-publish-grammar-coverage.cjs +31 -0
  157. package/scripts/build-tree-sitter-grammars.cjs +16 -10
  158. package/skills/gitnexus-guide.md +11 -0
  159. package/vendor/tree-sitter-c/package.json +1 -1
  160. package/vendor/tree-sitter-dart/package.json +1 -1
  161. package/vendor/tree-sitter-kotlin/package.json +1 -1
  162. package/vendor/tree-sitter-proto/package.json +1 -1
  163. package/vendor/tree-sitter-swift/package.json +1 -1
  164. package/web/assets/{agent-CKPMqImC.js → agent-ay4LD70X.js} +1 -1
  165. package/web/assets/{architectureDiagram-UL44E2DR-D1EXI0zA.js → architectureDiagram-UL44E2DR-Dc-viYhd.js} +1 -1
  166. package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-4rpojOyj.js} +1 -1
  167. package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-BnOy944n.js} +1 -1
  168. package/web/assets/{classDiagram-KGZ6W3CR-Bv93af_b.js → classDiagram-KGZ6W3CR-Bsgpy98Q.js} +1 -1
  169. package/web/assets/{classDiagram-v2-72OJOZXJ-CLfEqHUa.js → classDiagram-v2-72OJOZXJ-D5atDGjc.js} +1 -1
  170. package/web/assets/{diagram-3NCE3AQN-BqAtKUpW.js → diagram-3NCE3AQN-Cz1OEMVi.js} +1 -1
  171. package/web/assets/{diagram-GF46GFSD-CaBG6n6o.js → diagram-GF46GFSD-CWYwzfP8.js} +1 -1
  172. package/web/assets/{diagram-QXG6HAR7-CZ-O3rcV.js → diagram-QXG6HAR7-DtBObr8L.js} +1 -1
  173. package/web/assets/{diagram-WEQXMOUZ-P4lSL4GH.js → diagram-WEQXMOUZ-BUV44Ov_.js} +1 -1
  174. package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-Ds9s-sRF.js} +1 -1
  175. package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-CupStHQb.js} +1 -1
  176. package/web/assets/index-COMMmbxW.css +2 -0
  177. package/web/assets/{index-CG6q8eTs.js → index-Do8AE5yF.js} +85 -85
  178. package/web/assets/{infoDiagram-3YFTVSEB-DmLICZx1.js → infoDiagram-3YFTVSEB-DPTXSbgu.js} +1 -1
  179. package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CeK-Qhet.js} +1 -1
  180. package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-D4tx6QQ-.js} +1 -1
  181. package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-Bcto_kGa.js} +1 -1
  182. package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-DPIeBTG6.js} +1 -1
  183. package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-DDGXjVKO.js} +1 -1
  184. package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-BzDqpcVW.js} +1 -1
  185. package/web/assets/{stateDiagram-7D4R322I-CB2nABwH.js → stateDiagram-7D4R322I-Bx__als3.js} +1 -1
  186. package/web/assets/{stateDiagram-v2-36443NZ5-COBGd2RL.js → stateDiagram-v2-36443NZ5-QqOh2yO-.js} +1 -1
  187. package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-C1eogTOG.js} +1 -1
  188. package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-D20F4rSW.js} +1 -1
  189. package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-BeBHg7ST.js} +1 -1
  190. package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-CdOWeoNI.js} +1 -1
  191. package/web/index.html +2 -2
  192. package/scripts/materialize-vendor-grammars.cjs +0 -97
  193. package/web/assets/index-BKWA-m7o.css +0 -2
package/README.md CHANGED
@@ -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"}
@@ -136,6 +136,7 @@ This project is indexed by GitNexus as **${projectName}**${noStats ? '' : ` (${s
136
136
  - **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
137
137
  - When exploring unfamiliar code, use \`query({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,12 @@ 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' },
81
88
  };
82
89
  /** Top-level container key for the nested form; not itself an `AnalyzeOptions` field. */
83
90
  const NESTED_KEY = 'analyze';
@@ -195,6 +202,39 @@ const normalizeValue = (kind, value, key) => {
195
202
  }
196
203
  return trimmed;
197
204
  }
205
+ case 'string-array': {
206
+ // Generic shared validator — `source` already names the config key, so
207
+ // messages here stay key-agnostic (no fetch-wrapper coupling in the
208
+ // shared normalizer; #1589/#1852 review F7).
209
+ if (!Array.isArray(value)) {
210
+ throw new GitNexusRcError(`${source} must be an array of strings.`);
211
+ }
212
+ const names = [];
213
+ for (const item of value) {
214
+ if (typeof item !== 'string') {
215
+ throw new GitNexusRcError(`${source} entries must all be strings.`);
216
+ }
217
+ const trimmed = item.trim();
218
+ if (!trimmed) {
219
+ throw new GitNexusRcError(`${source} entries must not be empty.`);
220
+ }
221
+ assertNoHiddenChars(trimmed, source);
222
+ // Values may be interpolated into a RegExp downstream. Restrict to
223
+ // identifier / member-access shapes so a config value can never smuggle
224
+ // regex metacharacters into a consumer.
225
+ if (!/^[A-Za-z_$][A-Za-z0-9_$.]*$/.test(trimmed)) {
226
+ throw new GitNexusRcError(`${source} entry "${trimmed}" must be an identifier or member name ` +
227
+ `(letters, digits, _, $, . — e.g. "client.get").`);
228
+ }
229
+ names.push(trimmed);
230
+ }
231
+ if (names.length === 0) {
232
+ throw new GitNexusRcError(`${source} must list at least one string.`);
233
+ }
234
+ // De-duplicate and cap to a sane bound so a pathological config cannot
235
+ // blow up the consumer scan's alternation.
236
+ return Array.from(new Set(names)).slice(0, 100);
237
+ }
198
238
  case 'numeric-string': {
199
239
  // Mirror Commander's contract: these options reach the existing CLI
200
240
  // 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,14 @@ 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[];
96
118
  }
97
119
  /**
98
120
  * Whether the post-index skill step should run.
@@ -551,6 +551,21 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
551
551
  return;
552
552
  }
553
553
  }
554
+ // Validate the index-branch selector (#2106) the same way, so a malformed
555
+ // `--branch` exits before any expensive analysis starts. Capture the TRIMMED
556
+ // return so a whitespace-padded value (e.g. " feature" from shell completion)
557
+ // normalizes before the checked-out-branch mismatch guard and slug — otherwise
558
+ // it would false-reject on-branch or create a ghost index when detached.
559
+ if (cliOptions?.branch !== undefined) {
560
+ try {
561
+ cliOptions.branch = validateBranchName(cliOptions.branch, '--branch');
562
+ }
563
+ catch (err) {
564
+ cliError(` ${err instanceof Error ? err.message : String(err)}\n`);
565
+ process.exitCode = 1;
566
+ return;
567
+ }
568
+ }
554
569
  // ── Load .gitnexusrc and merge: CLI flags override config (#243) ───
555
570
  // Parse/validate before the progress bar so a malformed config produces an
556
571
  // actionable error and exits before any expensive analysis starts.
@@ -826,9 +841,15 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
826
841
  skipGit: options.skipGit,
827
842
  skipAgentsMd,
828
843
  skipSkills,
844
+ // CFG/PDG substrate opt-in (#2081 M1) — threaded to both sinks downstream.
845
+ pdg: options.pdg === true,
829
846
  // Resolved default branch (CLI > .gitnexusrc > auto-detect > "main")
830
847
  // threaded into the generated regression-compare example (#243).
831
848
  defaultBranch: resolvedDefaultBranch,
849
+ // Index-branch selector (#2106). Read straight from the CLI flag (not
850
+ // the .gitnexusrc-merged options) so the cosmetic defaultBranch config
851
+ // can never change index placement. Undefined → auto-detect in pipeline.
852
+ branch: cliOptions?.branch,
832
853
  // commander.js `.option('--no-stats', …)` registers the flag as
833
854
  // `options.stats` (boolean, default true; `false` when the user
834
855
  // passed --no-stats). Reading `options.noStats` here returns
@@ -845,6 +866,9 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
845
866
  // GITNEXUS_WORKER_POOL_SIZE env mutation. `undefined` defers to the
846
867
  // env / auto-formula fallback inside the pipeline.
847
868
  workerPoolSize,
869
+ // Extra fetch-wrapper names from `.gitnexusrc` (#1589/#1852 residual);
870
+ // forwarded to the routes phase consumer scan.
871
+ fetchWrappers: options.fetchWrappers,
848
872
  }, {
849
873
  onProgress: (_phase, percent, message) => {
850
874
  updateBar(percent, message);
@@ -863,13 +887,19 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
863
887
  // preserving the rest of the block (incl. --skills community rows). No-op
864
888
  // when the value already matches, so a routine up-to-date run is silent
865
889
  // (#1996 tri-review P2).
890
+ // Only refresh the repo-root AGENTS.md/CLAUDE.md base_ref for the
891
+ // PRIMARY/flat index (#2106 R2). A non-primary branch's up-to-date
892
+ // analyze must not churn the committed AGENTS.md — this mirrors the
893
+ // in-pipeline `if (!placement.branch)` gate around generateAIContextFiles.
866
894
  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 */
895
+ if (result.isPrimaryBranch !== false) {
896
+ try {
897
+ const { refreshBaseRefLine } = await import('./ai-context.js');
898
+ baseRefRefreshed = (await refreshBaseRefLine(repoPath, resolvedDefaultBranch, { skipAgentsMd })).files;
899
+ }
900
+ catch {
901
+ /* best-effort — never fail the fast path over a context refresh */
902
+ }
873
903
  }
874
904
  clearInterval(elapsedTimer);
875
905
  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);
@@ -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)',
@@ -27,6 +27,7 @@ const COMMAND_DESCRIPTION_KEYS = {
27
27
  impact: 'help.command.impact.description',
28
28
  cypher: 'help.command.cypher.description',
29
29
  'detect-changes': 'help.command.detectChanges.description',
30
+ check: 'help.command.check.description',
30
31
  'eval-server': 'help.command.evalServer.description',
31
32
  group: 'help.command.group.description',
32
33
  'group create': 'help.command.group.create.description',
@@ -69,6 +70,7 @@ const OPTION_DESCRIPTION_KEYS = {
69
70
  'uninstall|-f, --force': 'help.option.uninstall.force',
70
71
  'clean|-f, --force': 'help.option.force.confirmation',
71
72
  'clean|--all': 'help.option.clean.all',
73
+ 'clean|--branch <name>': 'help.option.clean.branch',
72
74
  'clean|--lbug-sidecars': 'help.option.clean.lbugSidecars',
73
75
  'remove|-f, --force': 'help.option.force.confirmation',
74
76
  'wiki|-f, --force': 'help.option.wiki.force',
@@ -89,16 +91,19 @@ const OPTION_DESCRIPTION_KEYS = {
89
91
  'publish|--id <owner/repo>': 'help.option.publish.id',
90
92
  'publish|--skip-git': 'help.option.skipGit',
91
93
  'query|-r, --repo <name>': 'help.option.repo.targetOmitOne',
94
+ 'query|--branch <name>': 'help.option.branch',
92
95
  'query|-c, --context <text>': 'help.option.query.context',
93
96
  'query|-g, --goal <text>': 'help.option.query.goal',
94
97
  'query|-l, --limit <n>': 'help.option.query.limit',
95
98
  'query|--content': 'help.option.content',
96
99
  'context|-r, --repo <name>': 'help.option.repo.target',
100
+ 'context|--branch <name>': 'help.option.branch',
97
101
  'context|-u, --uid <uid>': 'help.option.context.uid',
98
102
  'context|-f, --file <path>': 'help.option.context.file',
99
103
  'context|--content': 'help.option.content',
100
104
  'impact|-d, --direction <dir>': 'help.option.impact.direction',
101
105
  'impact|-r, --repo <name>': 'help.option.repo.target',
106
+ 'impact|--branch <name>': 'help.option.branch',
102
107
  'impact|-u, --uid <uid>': 'help.option.context.uid',
103
108
  'impact|-f, --file <path>': 'help.option.context.file',
104
109
  'impact|--kind <kind>': 'help.option.impact.kind',
@@ -108,9 +113,15 @@ const OPTION_DESCRIPTION_KEYS = {
108
113
  'impact|--offset <n>': 'help.option.impact.offset',
109
114
  'impact|--summary-only': 'help.option.impact.summaryOnly',
110
115
  'cypher|-r, --repo <name>': 'help.option.repo.target',
116
+ 'cypher|--branch <name>': 'help.option.branch',
111
117
  'detect-changes|-s, --scope <scope>': 'help.option.detectChanges.scope',
112
118
  'detect-changes|-b, --base-ref <ref>': 'help.option.detectChanges.baseRef',
113
119
  'detect-changes|-r, --repo <name>': 'help.option.repo.target',
120
+ 'detect-changes|--branch <name>': 'help.option.branch',
121
+ 'check|--cycles': 'help.option.check.cycles',
122
+ 'check|--json': 'help.option.json',
123
+ 'check|-r, --repo <name>': 'help.option.repo.target',
124
+ 'check|--branch <name>': 'help.option.branch',
114
125
  'eval-server|-p, --port <port>': 'help.option.port',
115
126
  'eval-server|--host <host>': 'help.option.evalServer.host',
116
127
  'eval-server|--idle-timeout <seconds>': 'help.option.evalServer.idleTimeout',
@@ -10,6 +10,9 @@ export declare const en: {
10
10
  readonly 'list.title': "Indexed Repositories ({{count}})";
11
11
  readonly 'list.indexed': "Indexed";
12
12
  readonly 'list.commit': "Commit";
13
+ readonly 'list.branch': "Branch";
14
+ readonly 'list.branchIndexes': "Branch indexes";
15
+ readonly 'list.branchLine': "{{branch}} ({{commit}}, {{indexed}})";
13
16
  readonly 'list.stats': "Stats";
14
17
  readonly 'list.statsValue': "{{files}} files, {{symbols}} symbols, {{edges}} edges";
15
18
  readonly 'list.clusters': "Clusters";
@@ -23,6 +26,9 @@ export declare const en: {
23
26
  readonly 'status.indexed': "Indexed";
24
27
  readonly 'status.indexedCommit': "Indexed commit";
25
28
  readonly 'status.currentCommit': "Current commit";
29
+ readonly 'status.branch': "Branch";
30
+ readonly 'status.detached': "(detached HEAD)";
31
+ readonly 'status.branchNotIndexed': "⚠️ current branch not indexed (primary index is for '{{primary}}'; run gitnexus analyze)";
26
32
  readonly 'status.status': "Status";
27
33
  readonly 'status.upToDate': "✅ up-to-date";
28
34
  readonly 'status.stale': "⚠️ stale (re-run gitnexus analyze)";
@@ -30,6 +36,9 @@ export declare const en: {
30
36
  readonly 'clean.deletedRepo': "Deleted: {{name}} ({{storagePath}})";
31
37
  readonly 'clean.notFoundHere': "No indexed repository found in this directory.";
32
38
  readonly 'clean.deleteCurrent': "This will delete the GitNexus index for: {{repoName}}";
39
+ readonly 'clean.branchNotIndexed': "No indexed branch named \"{{branch}}\" for this repository.";
40
+ readonly 'clean.deleteBranch': "This will delete the branch index \"{{branch}}\" at: {{path}}";
41
+ readonly 'clean.deletedBranch': "Deleted branch index: {{branch}}";
33
42
  readonly 'clean.lbugSidecars.state': "LadybugDB sidecar state: {{state}}";
34
43
  readonly 'clean.lbugSidecars.none': "No quarantined LadybugDB missing-shadow WAL sidecars found.";
35
44
  readonly 'clean.lbugSidecars.preview': "This will delete {{count}} quarantined LadybugDB missing-shadow WAL sidecar(s):";
@@ -117,6 +126,7 @@ export declare const en: {
117
126
  readonly 'help.command.impact.description': "Blast radius analysis: what breaks if you change a symbol";
118
127
  readonly 'help.command.cypher.description': "Execute raw Cypher query against the knowledge graph";
119
128
  readonly 'help.command.detectChanges.description': "Map git diff hunks to indexed symbols and affected execution flows";
129
+ readonly 'help.command.check.description': "Run structural checks against the indexed graph";
120
130
  readonly 'help.command.evalServer.description': "Start lightweight HTTP server for fast tool calls during evaluation";
121
131
  readonly 'help.command.group.description': "Manage repository groups for cross-index impact analysis";
122
132
  readonly 'help.command.group.create.description': "Create a new group with template group.yaml";
@@ -156,6 +166,7 @@ export declare const en: {
156
166
  readonly 'help.option.force.confirmation': "Skip confirmation prompt";
157
167
  readonly 'help.option.uninstall.force': "Apply the changes (default is a dry-run preview)";
158
168
  readonly 'help.option.clean.all': "Clean all indexed repos";
169
+ readonly 'help.option.clean.branch': "Delete only the named branch index (not the primary)";
159
170
  readonly 'help.option.clean.lbugSidecars': "Clean quarantined LadybugDB missing-shadow WAL sidecars";
160
171
  readonly 'help.option.wiki.force': "Force full regeneration even if up to date";
161
172
  readonly 'help.option.wiki.provider': "LLM provider: openai, openrouter, azure, custom, cursor, claude, codex, or opencode (default: openai)";
@@ -178,6 +189,7 @@ export declare const en: {
178
189
  readonly 'help.option.query.limit': "Max processes to return (default: 5)";
179
190
  readonly 'help.option.content': "Include full symbol source code";
180
191
  readonly 'help.option.repo.target': "Target repository";
192
+ readonly 'help.option.branch': "Scope to a specific branch index (multi-branch repos)";
181
193
  readonly 'help.option.context.uid': "Direct symbol UID (zero-ambiguity lookup)";
182
194
  readonly 'help.option.context.file': "File path to disambiguate common names";
183
195
  readonly 'help.option.impact.kind': "Kind filter to disambiguate common names (e.g. Function, Class, Method)";
@@ -189,6 +201,7 @@ export declare const en: {
189
201
  readonly 'help.option.impact.summaryOnly': "Return counts and risk only, omit symbol list";
190
202
  readonly 'help.option.detectChanges.scope': "What to analyze: unstaged, staged, all, or compare";
191
203
  readonly 'help.option.detectChanges.baseRef': "Branch/commit for compare scope (e.g. main)";
204
+ readonly 'help.option.check.cycles': "Detect circular imports and fail when any are found";
192
205
  readonly 'help.option.evalServer.host': "Bind address (default: 127.0.0.1, use 0.0.0.0 to expose to all interfaces)";
193
206
  readonly 'help.option.evalServer.idleTimeout': "Auto-shutdown after N seconds idle (0 = disabled)";
194
207
  readonly 'help.option.group.create.force': "Overwrite existing group";
@@ -10,6 +10,9 @@ export const en = {
10
10
  'list.title': 'Indexed Repositories ({{count}})',
11
11
  'list.indexed': 'Indexed',
12
12
  'list.commit': 'Commit',
13
+ 'list.branch': 'Branch',
14
+ 'list.branchIndexes': 'Branch indexes',
15
+ 'list.branchLine': '{{branch}} ({{commit}}, {{indexed}})',
13
16
  'list.stats': 'Stats',
14
17
  'list.statsValue': '{{files}} files, {{symbols}} symbols, {{edges}} edges',
15
18
  'list.clusters': 'Clusters',
@@ -23,6 +26,9 @@ export const en = {
23
26
  'status.indexed': 'Indexed',
24
27
  'status.indexedCommit': 'Indexed commit',
25
28
  'status.currentCommit': 'Current commit',
29
+ 'status.branch': 'Branch',
30
+ 'status.detached': '(detached HEAD)',
31
+ 'status.branchNotIndexed': "⚠️ current branch not indexed (primary index is for '{{primary}}'; run gitnexus analyze)",
26
32
  'status.status': 'Status',
27
33
  'status.upToDate': '✅ up-to-date',
28
34
  'status.stale': '⚠️ stale (re-run gitnexus analyze)',
@@ -30,6 +36,9 @@ export const en = {
30
36
  'clean.deletedRepo': 'Deleted: {{name}} ({{storagePath}})',
31
37
  'clean.notFoundHere': 'No indexed repository found in this directory.',
32
38
  'clean.deleteCurrent': 'This will delete the GitNexus index for: {{repoName}}',
39
+ 'clean.branchNotIndexed': 'No indexed branch named "{{branch}}" for this repository.',
40
+ 'clean.deleteBranch': 'This will delete the branch index "{{branch}}" at: {{path}}',
41
+ 'clean.deletedBranch': 'Deleted branch index: {{branch}}',
33
42
  'clean.lbugSidecars.state': 'LadybugDB sidecar state: {{state}}',
34
43
  'clean.lbugSidecars.none': 'No quarantined LadybugDB missing-shadow WAL sidecars found.',
35
44
  'clean.lbugSidecars.preview': 'This will delete {{count}} quarantined LadybugDB missing-shadow WAL sidecar(s):',
@@ -117,6 +126,7 @@ export const en = {
117
126
  'help.command.impact.description': 'Blast radius analysis: what breaks if you change a symbol',
118
127
  'help.command.cypher.description': 'Execute raw Cypher query against the knowledge graph',
119
128
  'help.command.detectChanges.description': 'Map git diff hunks to indexed symbols and affected execution flows',
129
+ 'help.command.check.description': 'Run structural checks against the indexed graph',
120
130
  'help.command.evalServer.description': 'Start lightweight HTTP server for fast tool calls during evaluation',
121
131
  'help.command.group.description': 'Manage repository groups for cross-index impact analysis',
122
132
  'help.command.group.create.description': 'Create a new group with template group.yaml',
@@ -156,6 +166,7 @@ export const en = {
156
166
  'help.option.force.confirmation': 'Skip confirmation prompt',
157
167
  'help.option.uninstall.force': 'Apply the changes (default is a dry-run preview)',
158
168
  'help.option.clean.all': 'Clean all indexed repos',
169
+ 'help.option.clean.branch': 'Delete only the named branch index (not the primary)',
159
170
  'help.option.clean.lbugSidecars': 'Clean quarantined LadybugDB missing-shadow WAL sidecars',
160
171
  'help.option.wiki.force': 'Force full regeneration even if up to date',
161
172
  'help.option.wiki.provider': 'LLM provider: openai, openrouter, azure, custom, cursor, claude, codex, or opencode (default: openai)',
@@ -178,6 +189,7 @@ export const en = {
178
189
  'help.option.query.limit': 'Max processes to return (default: 5)',
179
190
  'help.option.content': 'Include full symbol source code',
180
191
  'help.option.repo.target': 'Target repository',
192
+ 'help.option.branch': 'Scope to a specific branch index (multi-branch repos)',
181
193
  'help.option.context.uid': 'Direct symbol UID (zero-ambiguity lookup)',
182
194
  'help.option.context.file': 'File path to disambiguate common names',
183
195
  'help.option.impact.kind': 'Kind filter to disambiguate common names (e.g. Function, Class, Method)',
@@ -189,6 +201,7 @@ export const en = {
189
201
  'help.option.impact.summaryOnly': 'Return counts and risk only, omit symbol list',
190
202
  'help.option.detectChanges.scope': 'What to analyze: unstaged, staged, all, or compare',
191
203
  'help.option.detectChanges.baseRef': 'Branch/commit for compare scope (e.g. main)',
204
+ 'help.option.check.cycles': 'Detect circular imports and fail when any are found',
192
205
  'help.option.evalServer.host': 'Bind address (default: 127.0.0.1, use 0.0.0.0 to expose to all interfaces)',
193
206
  'help.option.evalServer.idleTimeout': 'Auto-shutdown after N seconds idle (0 = disabled)',
194
207
  'help.option.group.create.force': 'Overwrite existing group',