gitnexus 1.6.4-rc.2 → 1.6.4-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 (243) hide show
  1. package/README.md +35 -0
  2. package/dist/_shared/index.d.ts +1 -1
  3. package/dist/_shared/index.d.ts.map +1 -1
  4. package/dist/_shared/index.js +1 -1
  5. package/dist/_shared/index.js.map +1 -1
  6. package/dist/_shared/scope-resolution/finalize-algorithm.d.ts +22 -14
  7. package/dist/_shared/scope-resolution/finalize-algorithm.d.ts.map +1 -1
  8. package/dist/_shared/scope-resolution/finalize-algorithm.js +298 -37
  9. package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
  10. package/dist/_shared/scope-resolution/scope-tree.d.ts +23 -1
  11. package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
  12. package/dist/_shared/scope-resolution/scope-tree.js +36 -2
  13. package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
  14. package/dist/_shared/scope-resolution/types.d.ts +47 -3
  15. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  16. package/dist/_shared/scope-resolution/types.js +10 -2
  17. package/dist/_shared/scope-resolution/types.js.map +1 -1
  18. package/dist/cli/analyze.d.ts +6 -0
  19. package/dist/cli/analyze.js +35 -0
  20. package/dist/cli/doctor.d.ts +1 -0
  21. package/dist/cli/doctor.js +31 -0
  22. package/dist/cli/index.js +13 -0
  23. package/dist/cli/setup.js +2 -2
  24. package/dist/core/embeddings/config.d.ts +2 -0
  25. package/dist/core/embeddings/config.js +36 -0
  26. package/dist/core/embeddings/embedder.js +11 -6
  27. package/dist/core/embeddings/embedding-pipeline.d.ts +7 -1
  28. package/dist/core/embeddings/embedding-pipeline.js +93 -29
  29. package/dist/core/embeddings/exact-search.d.ts +15 -0
  30. package/dist/core/embeddings/exact-search.js +27 -0
  31. package/dist/core/embeddings/types.d.ts +4 -0
  32. package/dist/core/embeddings/types.js +2 -0
  33. package/dist/core/group/config-parser.js +2 -0
  34. package/dist/core/group/matching.d.ts +3 -3
  35. package/dist/core/group/matching.js +46 -6
  36. package/dist/core/group/storage.js +2 -0
  37. package/dist/core/group/sync.js +1 -1
  38. package/dist/core/group/types.d.ts +18 -0
  39. package/dist/core/ingestion/call-processor.d.ts +3 -3
  40. package/dist/core/ingestion/call-processor.js +58 -65
  41. package/dist/core/ingestion/constants.d.ts +4 -3
  42. package/dist/core/ingestion/constants.js +8 -3
  43. package/dist/core/ingestion/finalize-orchestrator.js +6 -3
  44. package/dist/core/ingestion/heritage-processor.js +2 -2
  45. package/dist/core/ingestion/import-processor.js +1 -1
  46. package/dist/core/ingestion/language-provider.d.ts +8 -0
  47. package/dist/core/ingestion/languages/csharp/captures.js +4 -1
  48. package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +14 -13
  49. package/dist/core/ingestion/languages/csharp/namespace-siblings.js +62 -50
  50. package/dist/core/ingestion/languages/python/captures.js +9 -1
  51. package/dist/core/ingestion/languages/python/index.d.ts +1 -1
  52. package/dist/core/ingestion/languages/python/index.js +1 -1
  53. package/dist/core/ingestion/languages/python/simple-hooks.d.ts +3 -1
  54. package/dist/core/ingestion/languages/python/simple-hooks.js +8 -0
  55. package/dist/core/ingestion/languages/python.js +28 -1
  56. package/dist/core/ingestion/languages/swift.js +14 -0
  57. package/dist/core/ingestion/languages/typescript/arity-metadata.d.ts +59 -0
  58. package/dist/core/ingestion/languages/typescript/arity-metadata.js +103 -0
  59. package/dist/core/ingestion/languages/typescript/arity.d.ts +37 -0
  60. package/dist/core/ingestion/languages/typescript/arity.js +54 -0
  61. package/dist/core/ingestion/languages/typescript/cache-stats.d.ts +17 -0
  62. package/dist/core/ingestion/languages/typescript/cache-stats.js +28 -0
  63. package/dist/core/ingestion/languages/typescript/captures.d.ts +28 -0
  64. package/dist/core/ingestion/languages/typescript/captures.js +451 -0
  65. package/dist/core/ingestion/languages/typescript/import-decomposer.d.ts +49 -0
  66. package/dist/core/ingestion/languages/typescript/import-decomposer.js +371 -0
  67. package/dist/core/ingestion/languages/typescript/import-target.d.ts +50 -0
  68. package/dist/core/ingestion/languages/typescript/import-target.js +61 -0
  69. package/dist/core/ingestion/languages/typescript/index.d.ts +94 -0
  70. package/dist/core/ingestion/languages/typescript/index.js +94 -0
  71. package/dist/core/ingestion/languages/typescript/interpret.d.ts +35 -0
  72. package/dist/core/ingestion/languages/typescript/interpret.js +317 -0
  73. package/dist/core/ingestion/languages/typescript/merge-bindings.d.ts +62 -0
  74. package/dist/core/ingestion/languages/typescript/merge-bindings.js +158 -0
  75. package/dist/core/ingestion/languages/typescript/query.d.ts +77 -0
  76. package/dist/core/ingestion/languages/typescript/query.js +778 -0
  77. package/dist/core/ingestion/languages/typescript/receiver-binding.d.ts +59 -0
  78. package/dist/core/ingestion/languages/typescript/receiver-binding.js +171 -0
  79. package/dist/core/ingestion/languages/typescript/scope-resolver.d.ts +16 -0
  80. package/dist/core/ingestion/languages/typescript/scope-resolver.js +113 -0
  81. package/dist/core/ingestion/languages/typescript/simple-hooks.d.ts +71 -0
  82. package/dist/core/ingestion/languages/typescript/simple-hooks.js +131 -0
  83. package/dist/core/ingestion/languages/typescript.js +19 -0
  84. package/dist/core/ingestion/method-extractors/configs/swift.js +3 -4
  85. package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +14 -1
  86. package/dist/core/ingestion/parsing-processor.js +20 -9
  87. package/dist/core/ingestion/pipeline-phases/processes.js +9 -4
  88. package/dist/core/ingestion/pipeline-phases/tools.d.ts +1 -0
  89. package/dist/core/ingestion/pipeline-phases/tools.js +10 -4
  90. package/dist/core/ingestion/registry-primary-flag.d.ts +3 -1
  91. package/dist/core/ingestion/registry-primary-flag.js +4 -1
  92. package/dist/core/ingestion/scope-extractor-bridge.d.ts +5 -2
  93. package/dist/core/ingestion/scope-extractor-bridge.js +7 -2
  94. package/dist/core/ingestion/scope-extractor.js +19 -18
  95. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +73 -11
  96. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +48 -10
  97. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +283 -14
  98. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +23 -2
  99. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +109 -37
  100. package/dist/core/ingestion/scope-resolution/passes/mro.js +3 -1
  101. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +13 -5
  102. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +11 -2
  103. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
  104. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +8 -0
  105. package/dist/core/ingestion/scope-resolution/pipeline/run.js +21 -5
  106. package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.d.ts +39 -0
  107. package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +65 -0
  108. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +54 -11
  109. package/dist/core/ingestion/scope-resolution/scope/walkers.js +105 -30
  110. package/dist/core/ingestion/type-extractors/swift.js +7 -4
  111. package/dist/core/ingestion/utils/ast-helpers.d.ts +2 -0
  112. package/dist/core/ingestion/utils/ast-helpers.js +12 -0
  113. package/dist/core/ingestion/utils/env.d.ts +10 -0
  114. package/dist/core/ingestion/utils/env.js +14 -0
  115. package/dist/core/ingestion/workers/parse-worker.d.ts +1 -0
  116. package/dist/core/ingestion/workers/parse-worker.js +15 -9
  117. package/dist/core/ingestion/workers/worker-pool.d.ts +11 -4
  118. package/dist/core/ingestion/workers/worker-pool.js +244 -48
  119. package/dist/core/lbug/extension-loader.d.ts +86 -0
  120. package/dist/core/lbug/extension-loader.js +184 -0
  121. package/dist/core/lbug/lbug-adapter.d.ts +18 -17
  122. package/dist/core/lbug/lbug-adapter.js +45 -73
  123. package/dist/core/lbug/pool-adapter.js +10 -28
  124. package/dist/core/platform/capabilities.d.ts +24 -0
  125. package/dist/core/platform/capabilities.js +54 -0
  126. package/dist/core/run-analyze.js +36 -9
  127. package/dist/core/search/bm25-index.d.ts +0 -17
  128. package/dist/core/search/bm25-index.js +10 -118
  129. package/dist/core/search/fts-indexes.d.ts +1 -0
  130. package/dist/core/search/fts-indexes.js +7 -0
  131. package/dist/core/search/fts-schema.d.ts +6 -0
  132. package/dist/core/search/fts-schema.js +7 -0
  133. package/dist/mcp/core/embedder.js +11 -4
  134. package/dist/mcp/local/local-backend.js +50 -15
  135. package/dist/server/api.d.ts +5 -0
  136. package/dist/server/api.js +113 -0
  137. package/hooks/claude/gitnexus-hook.cjs +11 -1
  138. package/package.json +6 -5
  139. package/scripts/build-tree-sitter-dart.cjs +42 -0
  140. package/scripts/build-tree-sitter-proto.cjs +1 -1
  141. package/scripts/build.js +22 -2
  142. package/scripts/install-duckdb-extension.mjs +37 -0
  143. package/vendor/tree-sitter-dart/README.md +18 -0
  144. package/vendor/tree-sitter-dart/binding.gyp +31 -0
  145. package/vendor/tree-sitter-dart/bindings/node/binding.cc +20 -0
  146. package/vendor/tree-sitter-dart/bindings/node/index.d.ts +28 -0
  147. package/vendor/tree-sitter-dart/bindings/node/index.js +7 -0
  148. package/vendor/tree-sitter-dart/grammar.js +2895 -0
  149. package/vendor/tree-sitter-dart/package.json +18 -0
  150. package/vendor/tree-sitter-dart/queries/highlights.scm +246 -0
  151. package/vendor/tree-sitter-dart/queries/tags.scm +92 -0
  152. package/vendor/tree-sitter-dart/queries/test.scm +1 -0
  153. package/vendor/tree-sitter-dart/src/grammar.json +12459 -0
  154. package/vendor/tree-sitter-dart/src/node-types.json +15055 -0
  155. package/vendor/tree-sitter-dart/src/parser.c +196127 -0
  156. package/vendor/tree-sitter-dart/src/scanner.c +130 -0
  157. package/vendor/tree-sitter-dart/src/tree_sitter/alloc.h +54 -0
  158. package/vendor/tree-sitter-dart/src/tree_sitter/array.h +290 -0
  159. package/vendor/tree-sitter-dart/src/tree_sitter/parser.h +265 -0
  160. package/vendor/tree-sitter-swift/LICENSE +21 -0
  161. package/vendor/tree-sitter-swift/README.md +139 -0
  162. package/vendor/tree-sitter-swift/bindings/node/index.d.ts +28 -0
  163. package/vendor/tree-sitter-swift/bindings/node/index.js +7 -0
  164. package/vendor/tree-sitter-swift/package.json +28 -0
  165. package/vendor/tree-sitter-swift/prebuilds/darwin-arm64/tree-sitter-swift.node +0 -0
  166. package/vendor/tree-sitter-swift/prebuilds/darwin-x64/tree-sitter-swift.node +0 -0
  167. package/vendor/tree-sitter-swift/prebuilds/linux-arm64/tree-sitter-swift.node +0 -0
  168. package/vendor/tree-sitter-swift/prebuilds/linux-x64/tree-sitter-swift.node +0 -0
  169. package/vendor/tree-sitter-swift/prebuilds/win32-arm64/tree-sitter-swift.node +0 -0
  170. package/vendor/tree-sitter-swift/prebuilds/win32-x64/tree-sitter-swift.node +0 -0
  171. package/vendor/tree-sitter-swift/src/node-types.json +30694 -0
  172. package/web/assets/agent-DaprsFSX.js +597 -0
  173. package/web/assets/architecture-YZFGNWBL-S5CXDPWN-DEdGaPg2.js +1 -0
  174. package/web/assets/architectureDiagram-EMZXCZ2Q-Domyk_gO.js +36 -0
  175. package/web/assets/blockDiagram-IGV67L2C-B_2kD7tM.js +132 -0
  176. package/web/assets/c4Diagram-DFAF54RM-BhJJW8Gg.js +10 -0
  177. package/web/assets/chunk-3GS5O3IE-jlWIjPsl.js +231 -0
  178. package/web/assets/chunk-3YCYZ6SJ-Blq_IzZs.js +1 -0
  179. package/web/assets/chunk-6NTNNK5N-DyPc58pp.js +1 -0
  180. package/web/assets/chunk-7RZVMHOQ-BdIU-RGO.js +321 -0
  181. package/web/assets/chunk-A34GCYZU-BI2i_LdU.js +1 -0
  182. package/web/assets/chunk-AEOMTBSW-D7qjBMHW.js +1 -0
  183. package/web/assets/chunk-CilyBKbf.js +1 -0
  184. package/web/assets/chunk-DJ7UZH7F-i11ywiBl.js +1 -0
  185. package/web/assets/chunk-DKKBVRCY-1SffGI1N.js +4 -0
  186. package/web/assets/chunk-DU5LTGQ6-DaPeiwD5.js +1 -0
  187. package/web/assets/chunk-FXACKDTF-uhhi2PC2.js +159 -0
  188. package/web/assets/chunk-H3VCZNTA-IchcISDt.js +1 -0
  189. package/web/assets/chunk-HN6EAY2L-D7ZFMNrB.js +1 -0
  190. package/web/assets/chunk-KSICW3F5-C2tZmXwv.js +15 -0
  191. package/web/assets/chunk-O5ABG6QK-Bt-Km84H.js +1 -0
  192. package/web/assets/chunk-PK6DOVAG-ChlWY0BQ.js +206 -0
  193. package/web/assets/chunk-RNJOYNJ4-B724K7cW.js +1 -0
  194. package/web/assets/chunk-RWUO3TPN-DYn1XriD.js +1 -0
  195. package/web/assets/chunk-TBF5ZNIQ-DKtDz6ae.js +1 -0
  196. package/web/assets/chunk-TU3PZOEN-DE5Qhc0N.js +1 -0
  197. package/web/assets/chunk-TYMNRAUI-g1h33cq-.js +1 -0
  198. package/web/assets/chunk-VELTKBKT-C9dVN39o.js +1 -0
  199. package/web/assets/chunk-W7ZLLLMY-Du-Hb9yb.js +1 -0
  200. package/web/assets/chunk-WSB5WSVC-B123clsZ.js +1 -0
  201. package/web/assets/chunk-XGPFEOL4-BR7Eue38.js +1 -0
  202. package/web/assets/classDiagram-PPOCWD7C-BglfKSs_.js +1 -0
  203. package/web/assets/classDiagram-v2-23LJLIIU-BSzTM28O.js +1 -0
  204. package/web/assets/context-builder-CqQNhRj1.js +15 -0
  205. package/web/assets/cose-bilkent-PNC4W37J-DCfErU-A.js +1 -0
  206. package/web/assets/dagre-E77IOHMT-tDRRhDoN.js +4 -0
  207. package/web/assets/diagram-H7BISOXX-CUVHlmAh.js +43 -0
  208. package/web/assets/diagram-JC5VWROH-BoyOxulB.js +24 -0
  209. package/web/assets/diagram-LXUTUG65-osr9hb7N.js +10 -0
  210. package/web/assets/diagram-WEHSV5V5-d8nUqS39.js +24 -0
  211. package/web/assets/erDiagram-GCSMX5X6-b-IwOhPS.js +85 -0
  212. package/web/assets/flowDiagram-OTCZ4VVT-Ott2Q0AP.js +162 -0
  213. package/web/assets/ganttDiagram-MUNLMDZQ-BYtgN_5s.js +292 -0
  214. package/web/assets/gitGraph-7Q5UKJZL-54BCDZD5-CFyBIGZq.js +1 -0
  215. package/web/assets/gitGraphDiagram-3HKGZ4G3-CsVD2gn4.js +106 -0
  216. package/web/assets/index-BleGLU8S.css +2 -0
  217. package/web/assets/index-C_xK08EW.js +885 -0
  218. package/web/assets/info-OMHHGYJF-BF2H5H6G-yjAxKEzh.js +1 -0
  219. package/web/assets/infoDiagram-MN7RKWGX-DXK0Unn5.js +2 -0
  220. package/web/assets/ishikawaDiagram-YMYX4NHK-CXsnC2FA.js +70 -0
  221. package/web/assets/journeyDiagram-SO5T7YLQ-BzZ07B-X.js +139 -0
  222. package/web/assets/kanban-definition-LJHFXRCJ-C6_EpAd9.js +89 -0
  223. package/web/assets/katex-GD7MH7QM-CJiOjBBJ.js +261 -0
  224. package/web/assets/mindmap-definition-2EUWGEK5-CCYGWZ1m.js +96 -0
  225. package/web/assets/packet-4T2RLAQJ-EV4IVRXR-B8k4E3IT.js +1 -0
  226. package/web/assets/pie-ZZUOXDRM-N23DN5KN-DdvfY118.js +1 -0
  227. package/web/assets/pieDiagram-3IATQBI2-RyvRlQb4.js +30 -0
  228. package/web/assets/quadrantDiagram-E256RVCF-Bfb6sxCx.js +7 -0
  229. package/web/assets/radar-PYXPWWZC-P6TP7ZYP-1EEDC_yU.js +1 -0
  230. package/web/assets/requirementDiagram-M5DCFWZL-DjvHDyvN.js +84 -0
  231. package/web/assets/sankeyDiagram-L3NBLAOT-CBCbbl8s.js +10 -0
  232. package/web/assets/sequenceDiagram-ZOUHS735-BscU8TUR.js +157 -0
  233. package/web/assets/stateDiagram-MLPALWAM-CJusEK2D.js +1 -0
  234. package/web/assets/stateDiagram-v2-B5LQ5ZB2-DImJ3PXD.js +1 -0
  235. package/web/assets/timeline-definition-5SPVSISX-DigPA1X8.js +120 -0
  236. package/web/assets/treeView-SZITEDCU-5DXDK3XO-CzPDt3aG.js +1 -0
  237. package/web/assets/treemap-W4RFUUIX-WYLRDWKO-B9Iqiorr.js +1 -0
  238. package/web/assets/vennDiagram-IE5QUKF5-C91UkZIf.js +34 -0
  239. package/web/assets/wardley-RL74JXVD-BCRCBASE-x42Qw7hp.js +1 -0
  240. package/web/assets/wardleyDiagram-XU3VSMPF-DloBhI0U.js +20 -0
  241. package/web/assets/xychartDiagram-ZHJ5623Y-BGWJvgwI.js +7 -0
  242. package/web/index.html +21 -0
  243. package/scripts/patch-tree-sitter-swift.cjs +0 -78
@@ -15,7 +15,7 @@ import Ruby from 'tree-sitter-ruby';
15
15
  import { createRequire } from 'node:module';
16
16
  import { SupportedLanguages } from '../../../_shared/index.js';
17
17
  import { getProvider } from '../languages/index.js';
18
- import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from '../constants.js';
18
+ import { getTreeSitterBufferSize, getTreeSitterContentByteLength, TREE_SITTER_MAX_BUFFER, } from '../constants.js';
19
19
  // tree-sitter-swift is an optionalDependency — may not be installed
20
20
  const _require = createRequire(import.meta.url);
21
21
  let Swift = null;
@@ -452,7 +452,7 @@ const processBatch = (files, onProgress) => {
452
452
  }
453
453
  let totalProcessed = 0;
454
454
  let lastReported = 0;
455
- const PROGRESS_INTERVAL = 100; // report every 100 files
455
+ const PROGRESS_INTERVAL = Math.max(1, Math.min(100, Math.ceil(files.length / 10)));
456
456
  const onFileProcessed = onProgress
457
457
  ? () => {
458
458
  totalProcessed++;
@@ -519,6 +519,9 @@ const processBatch = (files, onProgress) => {
519
519
  }
520
520
  }
521
521
  }
522
+ if (onProgress && totalProcessed !== lastReported) {
523
+ onProgress(totalProcessed);
524
+ }
522
525
  return result;
523
526
  };
524
527
  const ROUTE_HTTP_METHODS = new Set([
@@ -1001,7 +1004,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1001
1004
  }
1002
1005
  for (const file of files) {
1003
1006
  // Skip files larger than the max tree-sitter buffer (32 MB)
1004
- if (file.content.length > TREE_SITTER_MAX_BUFFER)
1007
+ if (getTreeSitterContentByteLength(file.content) > TREE_SITTER_MAX_BUFFER)
1005
1008
  continue;
1006
1009
  // Vue SFC preprocessing: extract <script> block content
1007
1010
  let parseContent = file.content;
@@ -1019,7 +1022,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1019
1022
  let tree;
1020
1023
  try {
1021
1024
  tree = parser.parse(parseContent, undefined, {
1022
- bufferSize: getTreeSitterBufferSize(parseContent.length),
1025
+ bufferSize: getTreeSitterBufferSize(parseContent),
1023
1026
  });
1024
1027
  }
1025
1028
  catch (err) {
@@ -1048,7 +1051,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1048
1051
  parentPort.postMessage({ type: 'warning', message });
1049
1052
  else
1050
1053
  console.warn(message);
1051
- });
1054
+ }, tree);
1052
1055
  if (parsedFile !== undefined)
1053
1056
  result.parsedFiles.push(parsedFile);
1054
1057
  // Pre-pass: extract heritage from query matches to build parentMap for buildTypeEnv.
@@ -1612,10 +1615,12 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1612
1615
  }
1613
1616
  }
1614
1617
  }
1615
- // Append #<paramCount> to Method/Constructor IDs to disambiguate overloads.
1616
- // Functions are not suffixed they don't overload by name in the same scope.
1618
+ // Append #<paramCount> to owned callable IDs to disambiguate overloads.
1619
+ // Top-level Function IDs stay stable; functions inside an owner may overload.
1617
1620
  // When same-arity collisions exist, append ~type1,type2 for further disambiguation.
1618
- const needsAritySuffix = nodeLabel === 'Method' || nodeLabel === 'Constructor';
1621
+ const needsAritySuffix = nodeLabel === 'Method' ||
1622
+ nodeLabel === 'Constructor' ||
1623
+ (nodeLabel === 'Function' && enclosingClassId !== null);
1619
1624
  let arityTag = needsAritySuffix && arityForId !== undefined ? `#${arityForId}` : '';
1620
1625
  if (arityTag && defMethodMap && defMethodInfo) {
1621
1626
  const groups = buildCollisionGroups(defMethodMap);
@@ -1668,8 +1673,9 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1668
1673
  result.toolDefs.push({
1669
1674
  filePath: file.path,
1670
1675
  toolName: nodeName,
1671
- description: dec.arg || '',
1676
+ description: (dec.arg || description || '').slice(0, 200),
1672
1677
  lineNumber: definitionNode.startPosition.row + lineOffset,
1678
+ handlerNodeId: nodeId,
1673
1679
  });
1674
1680
  }
1675
1681
  fileDecorators.delete(checkLine);
@@ -1,8 +1,7 @@
1
1
  export interface WorkerPool {
2
2
  /**
3
- * Dispatch items across workers. Items are split into chunks (one per worker),
4
- * each worker processes its chunk via sub-batches to limit peak memory,
5
- * and results are concatenated back in order.
3
+ * Dispatch items across workers. Items are split into bounded jobs, each job
4
+ * is committed independently, and stalled jobs are split/retried locally.
6
5
  */
7
6
  dispatch<TInput, TResult>(items: TInput[], onProgress?: (filesProcessed: number) => void): Promise<TResult[]>;
8
7
  /** Terminate all workers. Must be called when done. */
@@ -10,7 +9,15 @@ export interface WorkerPool {
10
9
  /** Number of workers in the pool */
11
10
  readonly size: number;
12
11
  }
12
+ export interface WorkerPoolOptions {
13
+ subBatchSize?: number;
14
+ subBatchMaxBytes?: number;
15
+ subBatchIdleTimeoutMs?: number;
16
+ maxTimeoutRetries?: number;
17
+ timeoutBackoffFactor?: number;
18
+ }
19
+ export declare function resolveWorkerPoolOptions(options?: WorkerPoolOptions): Required<WorkerPoolOptions>;
13
20
  /**
14
21
  * Create a pool of worker threads.
15
22
  */
16
- export declare const createWorkerPool: (workerUrl: URL, poolSize?: number) => WorkerPool;
23
+ export declare const createWorkerPool: (workerUrl: URL, poolSize?: number, options?: WorkerPoolOptions) => WorkerPool;
@@ -7,13 +7,83 @@ import { fileURLToPath } from 'node:url';
7
7
  * Keeps structured-clone memory bounded per sub-batch.
8
8
  */
9
9
  const SUB_BATCH_SIZE = 1500;
10
- /** Per sub-batch timeout. If a single sub-batch takes longer than this,
11
- * likely a pathological file (e.g. minified 50MB JS). Fail fast. */
12
- const SUB_BATCH_TIMEOUT_MS = 30_000;
10
+ const SUB_BATCH_MAX_BYTES = 8 * 1024 * 1024;
11
+ const DEFAULT_SUB_BATCH_IDLE_TIMEOUT_MS = 30_000;
12
+ const DEFAULT_TIMEOUT_RETRIES = 1;
13
+ const DEFAULT_TIMEOUT_BACKOFF_FACTOR = 2;
14
+ function positiveInteger(value) {
15
+ const parsed = typeof value === 'string' ? Number(value) : value;
16
+ return typeof parsed === 'number' && Number.isFinite(parsed) && parsed > 0
17
+ ? Math.floor(parsed)
18
+ : undefined;
19
+ }
20
+ function nonNegativeInteger(value) {
21
+ const parsed = typeof value === 'string' ? Number(value) : value;
22
+ return typeof parsed === 'number' && Number.isFinite(parsed) && parsed >= 0
23
+ ? Math.floor(parsed)
24
+ : undefined;
25
+ }
26
+ export function resolveWorkerPoolOptions(options = {}) {
27
+ return {
28
+ subBatchSize: positiveInteger(options.subBatchSize) ?? SUB_BATCH_SIZE,
29
+ subBatchMaxBytes: positiveInteger(options.subBatchMaxBytes) ??
30
+ positiveInteger(process.env.GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES) ??
31
+ SUB_BATCH_MAX_BYTES,
32
+ subBatchIdleTimeoutMs: positiveInteger(options.subBatchIdleTimeoutMs) ??
33
+ positiveInteger(process.env.GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS) ??
34
+ DEFAULT_SUB_BATCH_IDLE_TIMEOUT_MS,
35
+ maxTimeoutRetries: nonNegativeInteger(options.maxTimeoutRetries) ?? DEFAULT_TIMEOUT_RETRIES,
36
+ timeoutBackoffFactor: positiveInteger(options.timeoutBackoffFactor) ?? DEFAULT_TIMEOUT_BACKOFF_FACTOR,
37
+ };
38
+ }
39
+ function estimateItemBytes(item) {
40
+ if (typeof item !== 'object' || item === null)
41
+ return 0;
42
+ const content = item.content;
43
+ return typeof content === 'string' ? Buffer.byteLength(content, 'utf8') : 0;
44
+ }
45
+ function itemPath(item) {
46
+ if (typeof item !== 'object' || item === null)
47
+ return undefined;
48
+ const path = item.path;
49
+ return typeof path === 'string' ? path : undefined;
50
+ }
51
+ function createJobs(items, maxItems, maxBytes, timeoutMs) {
52
+ const jobs = [];
53
+ let startIndex = 0;
54
+ let batch = [];
55
+ let batchBytes = 0;
56
+ const flush = () => {
57
+ if (batch.length === 0)
58
+ return;
59
+ jobs.push({
60
+ startIndex,
61
+ items: batch,
62
+ estimatedBytes: batchBytes,
63
+ attempt: 0,
64
+ splitDepth: 0,
65
+ timeoutMs,
66
+ });
67
+ startIndex += batch.length;
68
+ batch = [];
69
+ batchBytes = 0;
70
+ };
71
+ for (const item of items) {
72
+ const itemBytes = estimateItemBytes(item);
73
+ const wouldExceedItems = batch.length >= maxItems;
74
+ const wouldExceedBytes = batch.length > 0 && batchBytes + itemBytes > maxBytes;
75
+ if (wouldExceedItems || wouldExceedBytes)
76
+ flush();
77
+ batch.push(item);
78
+ batchBytes += itemBytes;
79
+ }
80
+ flush();
81
+ return jobs;
82
+ }
13
83
  /**
14
84
  * Create a pool of worker threads.
15
85
  */
16
- export const createWorkerPool = (workerUrl, poolSize) => {
86
+ export const createWorkerPool = (workerUrl, poolSize, options) => {
17
87
  // Validate worker script exists before spawning to prevent uncaught
18
88
  // MODULE_NOT_FOUND crashes in worker threads (e.g. when running from src/ via vitest)
19
89
  const workerPath = fileURLToPath(workerUrl);
@@ -21,102 +91,228 @@ export const createWorkerPool = (workerUrl, poolSize) => {
21
91
  throw new Error(`Worker script not found: ${workerPath}`);
22
92
  }
23
93
  const size = poolSize ?? Math.min(8, Math.max(1, os.cpus().length - 1));
94
+ const poolOptions = resolveWorkerPoolOptions(options);
24
95
  const workers = [];
96
+ let poolBroken = false;
97
+ let poolFailure;
25
98
  for (let i = 0; i < size; i++) {
26
99
  workers.push(new Worker(workerUrl));
27
100
  }
28
101
  const dispatch = (items, onProgress) => {
102
+ if (poolBroken) {
103
+ const reason = poolFailure ? `: ${poolFailure.message}` : '';
104
+ return Promise.reject(new Error(`Worker pool is unavailable after a previous failure${reason}`));
105
+ }
29
106
  if (items.length === 0)
30
107
  return Promise.resolve([]);
31
- const chunkSize = Math.ceil(items.length / size);
32
- const chunks = [];
33
- for (let i = 0; i < items.length; i += chunkSize) {
34
- chunks.push(items.slice(i, i + chunkSize));
35
- }
36
- const workerProgress = new Array(chunks.length).fill(0);
37
- const promises = chunks.map((chunk, i) => {
38
- const worker = workers[i];
39
- return new Promise((resolve, reject) => {
108
+ if (workers.length === 0)
109
+ return Promise.reject(new Error('Worker pool has no active workers'));
110
+ const jobs = createJobs(items, poolOptions.subBatchSize, poolOptions.subBatchMaxBytes, poolOptions.subBatchIdleTimeoutMs);
111
+ return new Promise((resolve, reject) => {
112
+ const results = [];
113
+ const inFlightProgress = new Array(workers.length).fill(0);
114
+ let completedFiles = 0;
115
+ let activeWorkers = 0;
116
+ let stopped = false;
117
+ let maxReported = 0;
118
+ const reportProgress = () => {
119
+ if (!onProgress)
120
+ return;
121
+ const inFlight = inFlightProgress.reduce((sum, value) => sum + value, 0);
122
+ const next = Math.min(items.length, Math.max(maxReported, completedFiles + inFlight));
123
+ if (next === maxReported)
124
+ return;
125
+ maxReported = next;
126
+ onProgress(next);
127
+ };
128
+ const replaceWorker = async (workerIndex) => {
129
+ const worker = workers[workerIndex];
130
+ await worker?.terminate().catch(() => undefined);
131
+ if (!stopped)
132
+ workers[workerIndex] = new Worker(workerUrl);
133
+ };
134
+ const fail = async (err) => {
135
+ poolBroken = true;
136
+ poolFailure = err;
137
+ if (stopped)
138
+ return;
139
+ stopped = true;
140
+ await Promise.all(workers.map((worker) => worker.terminate().catch(() => undefined)));
141
+ reject(err);
142
+ };
143
+ const maybeDone = () => {
144
+ if (stopped)
145
+ return;
146
+ if (jobs.length === 0 && activeWorkers === 0) {
147
+ stopped = true;
148
+ results.sort((a, b) => a.startIndex - b.startIndex);
149
+ if (onProgress && maxReported < items.length)
150
+ onProgress(items.length);
151
+ resolve(results.map((result) => result.data));
152
+ }
153
+ };
154
+ const requeueAfterTimeout = (workerIndex, job, lastProgress) => {
155
+ const nextTimeout = Math.ceil(job.timeoutMs * poolOptions.timeoutBackoffFactor);
156
+ if (job.items.length > 1) {
157
+ const midpoint = Math.ceil(job.items.length / 2);
158
+ const firstItems = job.items.slice(0, midpoint);
159
+ const secondItems = job.items.slice(midpoint);
160
+ const first = {
161
+ startIndex: job.startIndex,
162
+ items: firstItems,
163
+ estimatedBytes: firstItems.reduce((sum, item) => sum + estimateItemBytes(item), 0),
164
+ attempt: job.attempt,
165
+ splitDepth: job.splitDepth + 1,
166
+ timeoutMs: nextTimeout,
167
+ };
168
+ const second = {
169
+ startIndex: job.startIndex + midpoint,
170
+ items: secondItems,
171
+ estimatedBytes: secondItems.reduce((sum, item) => sum + estimateItemBytes(item), 0),
172
+ attempt: job.attempt,
173
+ splitDepth: job.splitDepth + 1,
174
+ timeoutMs: nextTimeout,
175
+ };
176
+ console.warn(`Worker ${workerIndex} parse job idle timeout after ${job.timeoutMs / 1000}s ` +
177
+ `(${job.items.length} items, ${job.estimatedBytes} bytes, last progress: ${lastProgress}). ` +
178
+ `Splitting into ${first.items.length}/${second.items.length} item jobs with ` +
179
+ `${nextTimeout / 1000}s timeout.`);
180
+ // Preserve intuitive retry order; final result order is still enforced by startIndex sort.
181
+ jobs.unshift(first, second);
182
+ return true;
183
+ }
184
+ const nextAttempt = job.attempt + 1;
185
+ if (nextAttempt <= poolOptions.maxTimeoutRetries) {
186
+ console.warn(`Worker ${workerIndex} parse job idle timeout after ${job.timeoutMs / 1000}s ` +
187
+ `(single item, attempt ${nextAttempt}/${poolOptions.maxTimeoutRetries + 1}). ` +
188
+ `Retrying with ${nextTimeout / 1000}s timeout.`);
189
+ jobs.unshift({
190
+ ...job,
191
+ attempt: nextAttempt,
192
+ timeoutMs: nextTimeout,
193
+ });
194
+ return true;
195
+ }
196
+ void fail(new Error(`Worker ${workerIndex} parse job idle timeout after ${job.timeoutMs / 1000}s ` +
197
+ `(single item${itemPath(job.items[0]) ? `: ${itemPath(job.items[0])}` : ''}, ` +
198
+ `${job.estimatedBytes} bytes, last progress: ${lastProgress}). ` +
199
+ `Analyze will retry through sequential fallback. Increase with ` +
200
+ `--worker-timeout or GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS.`));
201
+ return false;
202
+ };
203
+ const runWorker = (workerIndex) => {
204
+ if (stopped)
205
+ return;
206
+ const job = jobs.shift();
207
+ if (!job) {
208
+ maybeDone();
209
+ return;
210
+ }
211
+ activeWorkers++;
212
+ inFlightProgress[workerIndex] = 0;
213
+ const worker = workers[workerIndex];
40
214
  let settled = false;
41
- let subBatchTimer = null;
215
+ let waitingForFlush = false;
216
+ let idleTimer = null;
217
+ let lastProgress = 0;
42
218
  const cleanup = () => {
43
- if (subBatchTimer)
44
- clearTimeout(subBatchTimer);
219
+ if (idleTimer)
220
+ clearTimeout(idleTimer);
45
221
  worker.removeListener('message', handler);
46
222
  worker.removeListener('error', errorHandler);
47
223
  worker.removeListener('exit', exitHandler);
48
224
  };
49
- const resetSubBatchTimer = () => {
50
- if (subBatchTimer)
51
- clearTimeout(subBatchTimer);
52
- subBatchTimer = setTimeout(() => {
225
+ const finishJob = () => {
226
+ activeWorkers--;
227
+ inFlightProgress[workerIndex] = 0;
228
+ runWorker(workerIndex);
229
+ maybeDone();
230
+ };
231
+ const resetIdleTimer = () => {
232
+ if (idleTimer)
233
+ clearTimeout(idleTimer);
234
+ idleTimer = setTimeout(async () => {
53
235
  if (!settled) {
54
236
  settled = true;
55
237
  cleanup();
56
- reject(new Error(`Worker ${i} sub-batch timed out after ${SUB_BATCH_TIMEOUT_MS / 1000}s (chunk: ${chunk.length} items).`));
238
+ activeWorkers--;
239
+ inFlightProgress[workerIndex] = 0;
240
+ const shouldContinue = requeueAfterTimeout(workerIndex, job, lastProgress);
241
+ if (!shouldContinue)
242
+ return;
243
+ await replaceWorker(workerIndex);
244
+ reportProgress();
245
+ runWorker(workerIndex);
246
+ maybeDone();
57
247
  }
58
- }, SUB_BATCH_TIMEOUT_MS);
59
- };
60
- let subBatchIdx = 0;
61
- const sendNextSubBatch = () => {
62
- const start = subBatchIdx * SUB_BATCH_SIZE;
63
- if (start >= chunk.length) {
64
- worker.postMessage({ type: 'flush' });
65
- return;
66
- }
67
- const subBatch = chunk.slice(start, start + SUB_BATCH_SIZE);
68
- subBatchIdx++;
69
- resetSubBatchTimer();
70
- worker.postMessage({ type: 'sub-batch', files: subBatch });
248
+ }, job.timeoutMs);
71
249
  };
72
250
  const handler = (msg) => {
73
- if (settled)
251
+ if (settled || stopped)
74
252
  return;
75
253
  if (msg.type === 'progress') {
76
- workerProgress[i] = msg.filesProcessed;
77
- if (onProgress) {
78
- const total = workerProgress.reduce((a, b) => a + b, 0);
79
- onProgress(total);
80
- }
254
+ const bounded = Math.min(job.items.length, Math.max(0, msg.filesProcessed));
255
+ inFlightProgress[workerIndex] = bounded;
256
+ lastProgress = bounded;
257
+ resetIdleTimer();
258
+ reportProgress();
81
259
  }
82
260
  else if (msg.type === 'warning') {
261
+ resetIdleTimer();
83
262
  console.warn(msg.message);
84
263
  }
85
264
  else if (msg.type === 'sub-batch-done') {
86
- sendNextSubBatch();
265
+ waitingForFlush = true;
266
+ resetIdleTimer();
267
+ worker.postMessage({ type: 'flush' });
87
268
  }
88
269
  else if (msg.type === 'error') {
89
270
  settled = true;
90
271
  cleanup();
91
- reject(new Error(`Worker ${i} error: ${msg.error}`));
272
+ void fail(new Error(`Worker ${workerIndex} error: ${msg.error}`));
92
273
  }
93
274
  else if (msg.type === 'result') {
275
+ if (!waitingForFlush) {
276
+ settled = true;
277
+ cleanup();
278
+ void fail(new Error(`Worker ${workerIndex} protocol error: result before flush`));
279
+ return;
280
+ }
94
281
  settled = true;
95
282
  cleanup();
96
- resolve(msg.data);
283
+ results.push({ startIndex: job.startIndex, data: msg.data });
284
+ completedFiles += job.items.length;
285
+ reportProgress();
286
+ finishJob();
97
287
  }
98
288
  };
99
289
  const errorHandler = (err) => {
100
290
  if (!settled) {
101
291
  settled = true;
102
292
  cleanup();
103
- reject(err);
293
+ void fail(err);
104
294
  }
105
295
  };
106
296
  const exitHandler = (code) => {
107
297
  if (!settled) {
108
298
  settled = true;
109
299
  cleanup();
110
- reject(new Error(`Worker ${i} exited with code ${code}. Likely OOM or native addon failure.`));
300
+ void fail(new Error(`Worker ${workerIndex} exited with code ${code}. Likely OOM or native addon failure.`));
111
301
  }
112
302
  };
113
303
  worker.on('message', handler);
114
304
  worker.once('error', errorHandler);
115
305
  worker.once('exit', exitHandler);
116
- sendNextSubBatch();
117
- });
306
+ resetIdleTimer();
307
+ if (stopped) {
308
+ cleanup();
309
+ return;
310
+ }
311
+ worker.postMessage({ type: 'sub-batch', files: job.items });
312
+ };
313
+ for (let i = 0; i < workers.length; i++)
314
+ runWorker(i);
118
315
  });
119
- return Promise.all(promises);
120
316
  };
121
317
  const terminate = async () => {
122
318
  await Promise.all(workers.map((w) => w.terminate()));
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Lifecycle policy for an optional DuckDB extension.
3
+ *
4
+ * - `auto` — try `LOAD`, fall back to one bounded out-of-process `INSTALL`
5
+ * attempt per process if `LOAD` fails. Default for analyze.
6
+ * - `load-only`— try `LOAD` only; never spawn an installer. Used by serve/MCP
7
+ * read paths so user queries never block on a network install.
8
+ * - `never` — skip the extension entirely. Operators can use this to
9
+ * forcibly disable optional search features.
10
+ */
11
+ export type ExtensionInstallPolicy = 'auto' | 'load-only' | 'never';
12
+ export interface ExtensionInstallResult {
13
+ success: boolean;
14
+ timedOut: boolean;
15
+ message: string;
16
+ }
17
+ /** Snapshot of one optional extension's resolved capability state. */
18
+ export interface ExtensionCapability {
19
+ name: string;
20
+ loaded: boolean;
21
+ /** Human-readable reason when `loaded` is false. */
22
+ reason?: string;
23
+ }
24
+ /** Per-call overrides applied on top of `ExtensionManager` defaults. */
25
+ export interface ExtensionEnsureOptions {
26
+ policy?: ExtensionInstallPolicy;
27
+ installTimeoutMs?: number;
28
+ }
29
+ export interface ExtensionManagerOptions {
30
+ policy?: ExtensionInstallPolicy;
31
+ installTimeoutMs?: number;
32
+ installExtension?: (extensionName: string, timeoutMs: number) => Promise<ExtensionInstallResult>;
33
+ warn?: (message: string) => void;
34
+ }
35
+ export declare const getExtensionInstallTimeoutMs: () => number;
36
+ export declare const getExtensionInstallChildProcessArgs: (extensionName: string) => string[];
37
+ /**
38
+ * Run `INSTALL <extension>` in a short-lived child Node process so the parent
39
+ * event loop is never blocked by DuckDB's synchronous network call.
40
+ *
41
+ * The child opens its own scratch LadybugDB, executes the install, and exits.
42
+ * If the child exceeds `timeoutMs` the parent kills it with SIGKILL and
43
+ * resolves with `timedOut: true`.
44
+ */
45
+ export declare const installDuckDbExtensionOutOfProcess: (extensionName: string, timeoutMs?: number) => Promise<ExtensionInstallResult>;
46
+ /**
47
+ * Centralized lifecycle manager for optional LadybugDB extensions.
48
+ *
49
+ * Always tries `LOAD EXTENSION <name>` first — it is per-connection,
50
+ * idempotent, and never touches the network. If `LOAD` fails and the active
51
+ * policy permits, the manager runs a single bounded out-of-process `INSTALL`
52
+ * attempt per process and retries `LOAD`. Capability outcomes are cached so
53
+ * unavailable extensions degrade search features without ever blocking
54
+ * subsequent analyze or query calls.
55
+ *
56
+ * Policy precedence (most specific wins):
57
+ * per-call `opts.policy` → constructor `options.policy` → env → `auto`
58
+ */
59
+ export declare class ExtensionManager {
60
+ private readonly options;
61
+ private readonly capabilities;
62
+ private readonly installAttempted;
63
+ private readonly warnedKeys;
64
+ constructor(options?: ExtensionManagerOptions);
65
+ /** Reset cached capability and install state. Test-only. */
66
+ reset(): void;
67
+ /** Snapshot of currently-known optional extension capabilities. */
68
+ getCapabilities(): ExtensionCapability[];
69
+ /**
70
+ * Ensure an optional extension is loaded on the supplied connection.
71
+ *
72
+ * Returns `true` when the extension is usable on `query`, `false` when it
73
+ * is unavailable. Never throws on install failure — analyze and query
74
+ * paths are expected to degrade gracefully.
75
+ */
76
+ ensure(query: (sql: string) => Promise<unknown>, name: string, label: string, opts?: ExtensionEnsureOptions): Promise<boolean>;
77
+ private tryLoad;
78
+ private markLoaded;
79
+ private markUnavailable;
80
+ }
81
+ /** Process-wide singleton shared by core and pool adapters. */
82
+ export declare const extensionManager: ExtensionManager;
83
+ /** Snapshot of which optional DuckDB extensions are loaded in this process. */
84
+ export declare const getExtensionCapabilities: () => ExtensionCapability[];
85
+ /** Test-only: clear the singleton's cached capability and install state. */
86
+ export declare const resetExtensionState: () => void;