gitnexus 1.6.5-rc.8 → 1.6.5
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.
- package/dist/_shared/scope-resolution/finalize-algorithm.js +10 -2
- package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
- package/dist/_shared/scope-resolution/method-dispatch-index.d.ts +26 -0
- package/dist/_shared/scope-resolution/method-dispatch-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/method-dispatch-index.js +22 -3
- package/dist/_shared/scope-resolution/method-dispatch-index.js.map +1 -1
- package/dist/_shared/scope-resolution/registries/lookup-core.js +18 -1
- package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -1
- package/dist/_shared/scope-resolution/symbol-definition.d.ts +2 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
- package/dist/cli/ai-context.d.ts +1 -0
- package/dist/cli/ai-context.js +70 -16
- package/dist/cli/analyze.d.ts +30 -2
- package/dist/cli/analyze.js +45 -5
- package/dist/cli/index.js +8 -1
- package/dist/cli/setup.js +18 -0
- package/dist/cli/wiki.d.ts +2 -0
- package/dist/cli/wiki.js +11 -0
- package/dist/core/augmentation/engine.js +24 -4
- package/dist/core/embeddings/http-client.js +22 -6
- package/dist/core/incremental/shadow-candidates.d.ts +44 -0
- package/dist/core/incremental/shadow-candidates.js +74 -0
- package/dist/core/incremental/subgraph-extract.d.ts +64 -0
- package/dist/core/incremental/subgraph-extract.js +111 -0
- package/dist/core/ingestion/call-processor.d.ts +12 -1
- package/dist/core/ingestion/call-processor.js +183 -47
- package/dist/core/ingestion/class-extractors/configs/c-cpp.js +38 -0
- package/dist/core/ingestion/class-extractors/generic.js +8 -0
- package/dist/core/ingestion/class-types.d.ts +15 -0
- package/dist/core/ingestion/community-processor.js +18 -0
- package/dist/core/ingestion/import-resolvers/standard.d.ts +5 -0
- package/dist/core/ingestion/import-resolvers/standard.js +31 -1
- package/dist/core/ingestion/import-resolvers/utils.js +4 -0
- package/dist/core/ingestion/languages/c-cpp.js +20 -2
- package/dist/core/ingestion/languages/cpp/adl.d.ts +141 -0
- package/dist/core/ingestion/languages/cpp/adl.js +478 -0
- package/dist/core/ingestion/languages/cpp/arity-metadata.d.ts +18 -0
- package/dist/core/ingestion/languages/cpp/arity-metadata.js +170 -0
- package/dist/core/ingestion/languages/cpp/arity.d.ts +17 -0
- package/dist/core/ingestion/languages/cpp/arity.js +31 -0
- package/dist/core/ingestion/languages/cpp/captures.d.ts +2 -0
- package/dist/core/ingestion/languages/cpp/captures.js +1131 -0
- package/dist/core/ingestion/languages/cpp/conversion-rank.d.ts +31 -0
- package/dist/core/ingestion/languages/cpp/conversion-rank.js +47 -0
- package/dist/core/ingestion/languages/cpp/file-local-linkage.d.ts +118 -0
- package/dist/core/ingestion/languages/cpp/file-local-linkage.js +266 -0
- package/dist/core/ingestion/languages/cpp/header-scan.d.ts +8 -0
- package/dist/core/ingestion/languages/cpp/header-scan.js +50 -0
- package/dist/core/ingestion/languages/cpp/import-decomposer.d.ts +19 -0
- package/dist/core/ingestion/languages/cpp/import-decomposer.js +111 -0
- package/dist/core/ingestion/languages/cpp/import-target.d.ts +10 -0
- package/dist/core/ingestion/languages/cpp/import-target.js +13 -0
- package/dist/core/ingestion/languages/cpp/index.d.ts +11 -0
- package/dist/core/ingestion/languages/cpp/index.js +11 -0
- package/dist/core/ingestion/languages/cpp/inline-namespaces.d.ts +67 -0
- package/dist/core/ingestion/languages/cpp/inline-namespaces.js +176 -0
- package/dist/core/ingestion/languages/cpp/interpret.d.ts +35 -0
- package/dist/core/ingestion/languages/cpp/interpret.js +113 -0
- package/dist/core/ingestion/languages/cpp/merge-bindings.d.ts +13 -0
- package/dist/core/ingestion/languages/cpp/merge-bindings.js +29 -0
- package/dist/core/ingestion/languages/cpp/query.d.ts +3 -0
- package/dist/core/ingestion/languages/cpp/query.js +508 -0
- package/dist/core/ingestion/languages/cpp/range-bindings.d.ts +20 -0
- package/dist/core/ingestion/languages/cpp/range-bindings.js +212 -0
- package/dist/core/ingestion/languages/cpp/scope-resolver.d.ts +17 -0
- package/dist/core/ingestion/languages/cpp/scope-resolver.js +246 -0
- package/dist/core/ingestion/languages/cpp/simple-hooks.d.ts +24 -0
- package/dist/core/ingestion/languages/cpp/simple-hooks.js +62 -0
- package/dist/core/ingestion/languages/cpp/two-phase-lookup.d.ts +76 -0
- package/dist/core/ingestion/languages/cpp/two-phase-lookup.js +195 -0
- package/dist/core/ingestion/languages/java/arity-metadata.d.ts +18 -0
- package/dist/core/ingestion/languages/java/arity-metadata.js +40 -0
- package/dist/core/ingestion/languages/java/arity.d.ts +10 -0
- package/dist/core/ingestion/languages/java/arity.js +24 -0
- package/dist/core/ingestion/languages/java/cache-stats.d.ts +15 -0
- package/dist/core/ingestion/languages/java/cache-stats.js +26 -0
- package/dist/core/ingestion/languages/java/captures.d.ts +17 -0
- package/dist/core/ingestion/languages/java/captures.js +187 -0
- package/dist/core/ingestion/languages/java/import-decomposer.d.ts +18 -0
- package/dist/core/ingestion/languages/java/import-decomposer.js +85 -0
- package/dist/core/ingestion/languages/java/import-target.d.ts +17 -0
- package/dist/core/ingestion/languages/java/import-target.js +100 -0
- package/dist/core/ingestion/languages/java/index.d.ts +29 -0
- package/dist/core/ingestion/languages/java/index.js +29 -0
- package/dist/core/ingestion/languages/java/interpret.d.ts +13 -0
- package/dist/core/ingestion/languages/java/interpret.js +131 -0
- package/dist/core/ingestion/languages/java/merge-bindings.d.ts +12 -0
- package/dist/core/ingestion/languages/java/merge-bindings.js +40 -0
- package/dist/core/ingestion/languages/java/query.d.ts +30 -0
- package/dist/core/ingestion/languages/java/query.js +192 -0
- package/dist/core/ingestion/languages/java/receiver-binding.d.ts +11 -0
- package/dist/core/ingestion/languages/java/receiver-binding.js +95 -0
- package/dist/core/ingestion/languages/java/scope-resolver.d.ts +50 -0
- package/dist/core/ingestion/languages/java/scope-resolver.js +74 -0
- package/dist/core/ingestion/languages/java/simple-hooks.d.ts +13 -0
- package/dist/core/ingestion/languages/java/simple-hooks.js +34 -0
- package/dist/core/ingestion/languages/java.js +11 -0
- package/dist/core/ingestion/languages/php/arity-metadata.d.ts +28 -0
- package/dist/core/ingestion/languages/php/arity-metadata.js +63 -0
- package/dist/core/ingestion/languages/php/arity.d.ts +25 -0
- package/dist/core/ingestion/languages/php/arity.js +40 -0
- package/dist/core/ingestion/languages/php/cache-stats.d.ts +15 -0
- package/dist/core/ingestion/languages/php/cache-stats.js +26 -0
- package/dist/core/ingestion/languages/php/captures.d.ts +34 -0
- package/dist/core/ingestion/languages/php/captures.js +739 -0
- package/dist/core/ingestion/languages/php/import-decomposer.d.ts +28 -0
- package/dist/core/ingestion/languages/php/import-decomposer.js +265 -0
- package/dist/core/ingestion/languages/php/import-target.d.ts +47 -0
- package/dist/core/ingestion/languages/php/import-target.js +100 -0
- package/dist/core/ingestion/languages/php/index.d.ts +68 -0
- package/dist/core/ingestion/languages/php/index.js +72 -0
- package/dist/core/ingestion/languages/php/interpret.d.ts +36 -0
- package/dist/core/ingestion/languages/php/interpret.js +241 -0
- package/dist/core/ingestion/languages/php/merge-bindings.d.ts +19 -0
- package/dist/core/ingestion/languages/php/merge-bindings.js +47 -0
- package/dist/core/ingestion/languages/php/namespace-siblings.d.ts +51 -0
- package/dist/core/ingestion/languages/php/namespace-siblings.js +288 -0
- package/dist/core/ingestion/languages/php/query.d.ts +32 -0
- package/dist/core/ingestion/languages/php/query.js +326 -0
- package/dist/core/ingestion/languages/php/receiver-binding.d.ts +36 -0
- package/dist/core/ingestion/languages/php/receiver-binding.js +128 -0
- package/dist/core/ingestion/languages/php/scope-resolver.d.ts +23 -0
- package/dist/core/ingestion/languages/php/scope-resolver.js +358 -0
- package/dist/core/ingestion/languages/php/simple-hooks.d.ts +42 -0
- package/dist/core/ingestion/languages/php/simple-hooks.js +111 -0
- package/dist/core/ingestion/languages/php.d.ts +0 -7
- package/dist/core/ingestion/languages/php.js +15 -0
- package/dist/core/ingestion/markdown-processor.js +6 -1
- package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
- package/dist/core/ingestion/model/symbol-table.js +3 -0
- package/dist/core/ingestion/parsing-processor.d.ts +22 -2
- package/dist/core/ingestion/parsing-processor.js +123 -50
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +54 -2
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +5 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +119 -11
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +14 -0
- package/dist/core/ingestion/pipeline.d.ts +13 -0
- package/dist/core/ingestion/registry-primary-flag.js +2 -0
- package/dist/core/ingestion/scope-extractor.js +4 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +167 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +3 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +1 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +12 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.d.ts +1 -1
- package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +12 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +16 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +36 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +204 -13
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +1 -1
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +55 -4
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +186 -5
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +10 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +207 -9
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +13 -1
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +6 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +17 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +86 -8
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +30 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +90 -8
- package/dist/core/ingestion/tree-sitter-queries.d.ts +2 -2
- package/dist/core/ingestion/tree-sitter-queries.js +23 -0
- package/dist/core/ingestion/utils/ast-helpers.js +6 -1
- package/dist/core/ingestion/utils/template-arguments.d.ts +11 -0
- package/dist/core/ingestion/utils/template-arguments.js +65 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +3 -0
- package/dist/core/ingestion/workers/parse-worker.js +42 -1
- package/dist/core/lbug/lbug-adapter.d.ts +39 -0
- package/dist/core/lbug/lbug-adapter.js +348 -48
- package/dist/core/run-analyze.d.ts +2 -0
- package/dist/core/run-analyze.js +323 -29
- package/dist/core/search/hybrid-search.d.ts +3 -0
- package/dist/core/search/hybrid-search.js +23 -6
- package/dist/core/wiki/generator.js +4 -3
- package/dist/core/wiki/html-viewer.js +2 -1
- package/dist/core/wiki/llm-client.d.ts +4 -0
- package/dist/core/wiki/llm-client.js +3 -3
- package/dist/core/wiki/mermaid-sanitizer.d.ts +2 -0
- package/dist/core/wiki/mermaid-sanitizer.js +100 -0
- package/dist/mcp/local/local-backend.js +20 -7
- package/dist/storage/file-hash.d.ts +47 -0
- package/dist/storage/file-hash.js +86 -0
- package/dist/storage/parse-cache.d.ts +73 -0
- package/dist/storage/parse-cache.js +253 -0
- package/dist/storage/repo-manager.d.ts +41 -1
- package/dist/storage/repo-manager.js +61 -6
- package/hooks/claude/gitnexus-hook.cjs +42 -4
- package/hooks/claude/hook-db-lock-probe.cjs +238 -0
- package/hooks/claude/hook-lock.cjs +119 -0
- package/hooks/claude/win-rm-list-json.ps1 +76 -0
- package/package.json +1 -1
- package/scripts/build.js +6 -2
- package/vendor/node_modules/node-addon-api/node_addon_api.target.mk +16 -16
- package/vendor/node_modules/node-addon-api/node_addon_api_except.target.mk +16 -16
- package/vendor/node_modules/node-addon-api/node_addon_api_except_all.target.mk +16 -16
- package/vendor/node_modules/node-addon-api/node_addon_api_maybe.target.mk +16 -16
- package/web/assets/{agent-BpHQleHE.js → agent-D2kLt6Dl.js} +91 -91
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +1 -0
- package/web/assets/architectureDiagram-UL44E2DR-DvIkEGkp.js +36 -0
- package/web/assets/blockDiagram-7IZFK4PR-B4niSLvR.js +132 -0
- package/web/assets/{c4Diagram-DFAF54RM-DeJV1rCn.js → c4Diagram-Y2BXMSZH-DIzEXalR.js} +6 -6
- package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +4 -0
- package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +1 -0
- package/web/assets/{chunk-7RZVMHOQ-BdIU-RGO.js → chunk-3SSMPTDK-BzGeeheI.js} +1 -1
- package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +159 -0
- package/web/assets/chunk-5IMINLNL-CnTLsXIV.js +1 -0
- package/web/assets/chunk-5VCL7Z4A-w1BCx98Z.js +1 -0
- package/web/assets/chunk-6764PJDD-ChwMM2z4.js +1 -0
- package/web/assets/chunk-67TQ5CYL-CChC2cAv.js +124 -0
- package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +10 -0
- package/web/assets/chunk-7W6UQGC5-CgFyfZ6k.js +1 -0
- package/web/assets/chunk-AQ6EADP3-CCFje6lL.js +1 -0
- package/web/assets/{chunk-KSICW3F5-C2tZmXwv.js → chunk-AZZRMDJM-5E9ARdDQ.js} +1 -1
- package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +1 -0
- package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +1 -0
- package/web/assets/chunk-INKRHTLW-ELpfuk9h.js +70 -0
- package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +1 -0
- package/web/assets/{chunk-AEOMTBSW-D7qjBMHW.js → chunk-JQRUD6KW-Dcq_Qnyy.js} +1 -1
- package/web/assets/chunk-KGFNY3KK-W-VR57xb.js +62 -0
- package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +1 -0
- package/web/assets/chunk-KNLZD3CH-BuZBOcso.js +1 -0
- package/web/assets/chunk-KRXBNO2N-DH2NPM7I.js +1 -0
- package/web/assets/chunk-LCXTWHL2-CqJ0oo1x.js +231 -0
- package/web/assets/chunk-LII3EMHJ-3X33tCpU.js +1 -0
- package/web/assets/chunk-QA3QBVWF-D5Ha3GgB.js +2 -0
- package/web/assets/chunk-RERM46MO-DQNbXtfw.js +1 -0
- package/web/assets/chunk-RG4AUYOV-D0M6vdq9.js +206 -0
- package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +1 -0
- package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +1 -0
- package/web/assets/chunk-T2UQINTJ-kGYG7ppP.js +1 -0
- package/web/assets/chunk-T5OCTHI4--9wWpVws.js +1 -0
- package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +1 -0
- package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +1 -0
- package/web/assets/chunk-UY5QBCOK-Du-c56gs.js +1 -0
- package/web/assets/chunk-W44A43WB-lrCpuupA.js +1 -0
- package/web/assets/chunk-ZXARS5L4-CSZRiyOf.js +1 -0
- package/web/assets/classDiagram-KGZ6W3CR-BB-saqC1.js +1 -0
- package/web/assets/classDiagram-v2-72OJOZXJ-Bp2bAlS5.js +1 -0
- package/web/assets/cose-bilkent-UX7MHV2Q-BHv2bbtl.js +1 -0
- package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +4 -0
- package/web/assets/diagram-3NCE3AQN-BUxnDPOY.js +43 -0
- package/web/assets/diagram-GF46GFSD-BJTyqqvI.js +24 -0
- package/web/assets/diagram-HNR7UZ2L-fUWreZzx.js +3 -0
- package/web/assets/diagram-QXG6HAR7-DilMHamP.js +24 -0
- package/web/assets/diagram-WEQXMOUZ-8KUrarg1.js +10 -0
- package/web/assets/erDiagram-L5TCEMPS-Doj9mSiW.js +85 -0
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +1 -0
- package/web/assets/{flowDiagram-OTCZ4VVT-aAZw4c_M.js → flowDiagram-H6V6AXG4-BOSR-i3p.js} +12 -12
- package/web/assets/ganttDiagram-JCBTUEKG-BHQssTz6.js +292 -0
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +1 -0
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +106 -0
- package/web/assets/index-j4-NuleT.js +623 -0
- package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +1 -0
- package/web/assets/infoDiagram-3YFTVSEB-Ce_TvtaV.js +2 -0
- package/web/assets/{ishikawaDiagram-YMYX4NHK-BF8AtDaj.js → ishikawaDiagram-BNXS4ZKH-4MXIDCSF.js} +6 -6
- package/web/assets/journeyDiagram-M6C3CM3L-DNPYCpvq.js +139 -0
- package/web/assets/kanban-definition-75IXJCU3-yYdfm8Kk.js +89 -0
- package/web/assets/{katex-GD7MH7QM-CJiOjBBJ.js → katex-K3KEBU37-CrVLz8l7.js} +1 -1
- package/web/assets/mindmap-definition-2TDM6QVE-DFNEKM77.js +96 -0
- package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +1 -0
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +1 -0
- package/web/assets/pieDiagram-CU6KROY3-iZAqpvhd.js +30 -0
- package/web/assets/quadrantDiagram-VICAPDV7-bAm6Hi_5.js +7 -0
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +1 -0
- package/web/assets/requirementDiagram-JXO7QTGE-CtDijhnf.js +84 -0
- package/web/assets/sankeyDiagram-URQDO5SZ-LFJz53u5.js +40 -0
- package/web/assets/sequenceDiagram-VS2MUI6T-BdjvyCHM.js +162 -0
- package/web/assets/stateDiagram-7D4R322I-e5odN9fG.js +1 -0
- package/web/assets/stateDiagram-v2-36443NZ5-vPsvjR2D.js +1 -0
- package/web/assets/timeline-definition-O6YCAMPW-DPAksNQp.js +120 -0
- package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +1 -0
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +1 -0
- package/web/assets/vennDiagram-MWXL3ELB-Dtlfdt7J.js +34 -0
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +1 -0
- package/web/assets/wardleyDiagram-CUQ6CDDI-B23uRMUC.js +78 -0
- package/web/assets/{xychartDiagram-ZHJ5623Y-DZ7fP1_3.js → xychartDiagram-N2JHSOCM-DxwHeksW.js} +6 -6
- package/web/index.html +13 -2
- package/web/assets/architecture-YZFGNWBL-S5CXDPWN-BUzkWgDl.js +0 -1
- package/web/assets/architectureDiagram-EMZXCZ2Q-CXbzok1G.js +0 -36
- package/web/assets/blockDiagram-IGV67L2C-DA1QJ4ZB.js +0 -132
- package/web/assets/chunk-3GS5O3IE-B8hpnia8.js +0 -231
- package/web/assets/chunk-3YCYZ6SJ-B-BVu21C.js +0 -1
- package/web/assets/chunk-6NTNNK5N-DW-jYSDr.js +0 -1
- package/web/assets/chunk-A34GCYZU-BbZl-IKy.js +0 -1
- package/web/assets/chunk-DJ7UZH7F-B5tMfF87.js +0 -1
- package/web/assets/chunk-DKKBVRCY-Djm-6K_e.js +0 -4
- package/web/assets/chunk-DU5LTGQ6-QPoynYcp.js +0 -1
- package/web/assets/chunk-FXACKDTF-jieSmbe2.js +0 -159
- package/web/assets/chunk-H3VCZNTA-8EMHZ3SV.js +0 -1
- package/web/assets/chunk-HN6EAY2L-CgE_agsD.js +0 -1
- package/web/assets/chunk-O5ABG6QK-DEZV1O19.js +0 -1
- package/web/assets/chunk-PK6DOVAG-BXv0JnsJ.js +0 -206
- package/web/assets/chunk-RNJOYNJ4-BiBkF9dN.js +0 -1
- package/web/assets/chunk-RWUO3TPN-DTWzAhQ1.js +0 -1
- package/web/assets/chunk-TBF5ZNIQ-BSl6s9lT.js +0 -1
- package/web/assets/chunk-TU3PZOEN-DE5Qhc0N.js +0 -1
- package/web/assets/chunk-TYMNRAUI-D1NDLIMG.js +0 -1
- package/web/assets/chunk-VELTKBKT-C9dVN39o.js +0 -1
- package/web/assets/chunk-W7ZLLLMY-Bu-cdLzs.js +0 -1
- package/web/assets/chunk-WSB5WSVC-Dor71SOz.js +0 -1
- package/web/assets/chunk-XGPFEOL4-CUonLRle.js +0 -1
- package/web/assets/classDiagram-PPOCWD7C-Cdd6UdVv.js +0 -1
- package/web/assets/classDiagram-v2-23LJLIIU-DNhxY2Ib.js +0 -1
- package/web/assets/cose-bilkent-PNC4W37J-DdZq7Qz_.js +0 -1
- package/web/assets/dagre-E77IOHMT-Cfj_6e17.js +0 -4
- package/web/assets/diagram-H7BISOXX-BNvpsrBv.js +0 -43
- package/web/assets/diagram-JC5VWROH-Cn0rtDbJ.js +0 -24
- package/web/assets/diagram-LXUTUG65-CHfoVKNm.js +0 -10
- package/web/assets/diagram-WEHSV5V5-BLxHksnu.js +0 -24
- package/web/assets/erDiagram-GCSMX5X6-DfdN6iqF.js +0 -85
- package/web/assets/ganttDiagram-MUNLMDZQ-BEfC_7Or.js +0 -292
- package/web/assets/gitGraph-7Q5UKJZL-54BCDZD5-B6ZeRLP0.js +0 -1
- package/web/assets/gitGraphDiagram-3HKGZ4G3-CG0kC8Q5.js +0 -106
- package/web/assets/index-3NUCUW2s.js +0 -886
- package/web/assets/info-OMHHGYJF-BF2H5H6G-BIOsZy0W.js +0 -1
- package/web/assets/infoDiagram-MN7RKWGX-DUoMH-pq.js +0 -2
- package/web/assets/journeyDiagram-SO5T7YLQ-BW8oKa3N.js +0 -139
- package/web/assets/kanban-definition-LJHFXRCJ-DzbOsR-2.js +0 -89
- package/web/assets/mindmap-definition-2EUWGEK5-BIXGQALq.js +0 -96
- package/web/assets/packet-4T2RLAQJ-EV4IVRXR-Bs7ysNMU.js +0 -1
- package/web/assets/pie-ZZUOXDRM-N23DN5KN-Csi8nnbH.js +0 -1
- package/web/assets/pieDiagram-3IATQBI2-QY-u7P6a.js +0 -30
- package/web/assets/quadrantDiagram-E256RVCF-Di4aNiU8.js +0 -7
- package/web/assets/radar-PYXPWWZC-P6TP7ZYP-DJlyXh2-.js +0 -1
- package/web/assets/requirementDiagram-M5DCFWZL-CzUUrhQD.js +0 -84
- package/web/assets/sankeyDiagram-L3NBLAOT-D2fbMiIU.js +0 -10
- package/web/assets/sequenceDiagram-ZOUHS735-BNdkqguh.js +0 -157
- package/web/assets/stateDiagram-MLPALWAM-51_Q2w2L.js +0 -1
- package/web/assets/stateDiagram-v2-B5LQ5ZB2-8nHs2aNE.js +0 -1
- package/web/assets/timeline-definition-5SPVSISX-DkMiGj4Z.js +0 -120
- package/web/assets/treeView-SZITEDCU-5DXDK3XO-Cy6P0_CZ.js +0 -1
- package/web/assets/treemap-W4RFUUIX-WYLRDWKO-C_pp1R7A.js +0 -1
- package/web/assets/vennDiagram-IE5QUKF5-BCxMmf9q.js +0 -34
- package/web/assets/wardley-RL74JXVD-BCRCBASE-CMStJhpn.js +0 -1
- package/web/assets/wardleyDiagram-XU3VSMPF-ijlLF77s.js +0 -20
- /package/web/assets/{context-builder-CqQNhRj1.js → context-builder-_HS0v2ma.js} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
|
-
import { createReadStream, createWriteStream } from 'fs';
|
|
2
|
+
import { createReadStream, createWriteStream, constants as fsConstants } from 'fs';
|
|
3
3
|
import { createInterface } from 'readline';
|
|
4
4
|
import { once } from 'events';
|
|
5
5
|
import { finished } from 'stream/promises';
|
|
@@ -152,6 +152,136 @@ export const isReadOnlyDbError = (err) => {
|
|
|
152
152
|
const msg = err instanceof Error ? err.message : String(err);
|
|
153
153
|
return /read-only database/i.test(msg);
|
|
154
154
|
};
|
|
155
|
+
const isMissingFileError = (err) => {
|
|
156
|
+
const errno = err;
|
|
157
|
+
return errno?.code === 'ENOENT';
|
|
158
|
+
};
|
|
159
|
+
const extractErrnoCode = (err) => {
|
|
160
|
+
const errno = err;
|
|
161
|
+
return errno?.code;
|
|
162
|
+
};
|
|
163
|
+
const MAX_LOGGED_ERROR_MESSAGE_LENGTH = 160;
|
|
164
|
+
const summarizeError = (err) => (err instanceof Error ? err.message : String(err)).slice(0, MAX_LOGGED_ERROR_MESSAGE_LENGTH);
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
// Cross-process init lock
|
|
167
|
+
//
|
|
168
|
+
// Prevents a TOCTOU race in orphan sidecar cleanup: between checking that
|
|
169
|
+
// the main DB file is missing and unlinking sidecars, another process could
|
|
170
|
+
// create a fresh DB. The lock file (`${dbPath}.init.lock`) is created with
|
|
171
|
+
// O_CREAT | O_EXCL (atomic create-or-fail) and contains the owning PID +
|
|
172
|
+
// timestamp so stale locks from crashed processes can be reclaimed.
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
/** Maximum age (ms) before an init lock is considered stale. */
|
|
175
|
+
const INIT_LOCK_STALE_MS = 30_000;
|
|
176
|
+
/** Maximum attempts to acquire the init lock before giving up. */
|
|
177
|
+
const INIT_LOCK_MAX_ATTEMPTS = 6;
|
|
178
|
+
/** Delay between lock-acquisition retries (ms). */
|
|
179
|
+
const INIT_LOCK_RETRY_DELAY_MS = 500;
|
|
180
|
+
const initLockPath = (dbPath) => `${dbPath}.init.lock`;
|
|
181
|
+
/**
|
|
182
|
+
* Returns true when the process identified by `pid` is still running.
|
|
183
|
+
* Uses `process.kill(pid, 0)` which sends signal 0 (a no-op probe) —
|
|
184
|
+
* it throws ESRCH when the process does not exist.
|
|
185
|
+
*/
|
|
186
|
+
const isProcessAlive = (pid) => {
|
|
187
|
+
try {
|
|
188
|
+
process.kill(pid, 0);
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Try to break a stale lock whose owning process has exited.
|
|
197
|
+
* Returns `true` if the stale lock was removed (caller should retry acquire).
|
|
198
|
+
* Returns `false` if the lock is still valid (another live process owns it).
|
|
199
|
+
*/
|
|
200
|
+
const tryBreakStaleLock = async (lockPath) => {
|
|
201
|
+
try {
|
|
202
|
+
const content = await fs.readFile(lockPath, 'utf-8');
|
|
203
|
+
const parsed = JSON.parse(content);
|
|
204
|
+
// If the owning process is still alive AND the lock is not stale, don't break.
|
|
205
|
+
if (typeof parsed.pid === 'number' && isProcessAlive(parsed.pid)) {
|
|
206
|
+
// Even a live process's lock can be stale if it's been held too long
|
|
207
|
+
// (e.g. the process is hung). Check the timestamp.
|
|
208
|
+
if (typeof parsed.ts === 'number' && Date.now() - parsed.ts < INIT_LOCK_STALE_MS) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// PID is gone or lock exceeded INIT_LOCK_STALE_MS — reclaim it.
|
|
213
|
+
await fs.unlink(lockPath);
|
|
214
|
+
logger.warn(`GitNexus: removed stale init lock (pid=${parsed.pid ?? '?'}, age=${typeof parsed.ts === 'number' ? `${Date.now() - parsed.ts}ms` : '?'})`);
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
// Lock file disappeared between our read and unlink, or is unreadable.
|
|
219
|
+
// Either way, let the caller retry the acquire.
|
|
220
|
+
if (isMissingFileError(err))
|
|
221
|
+
return true;
|
|
222
|
+
// Permission error or corrupt content — log and let caller retry.
|
|
223
|
+
const code = extractErrnoCode(err);
|
|
224
|
+
logger.warn(`GitNexus: unable to inspect init lock (${code ?? 'UNKNOWN'}): ${summarizeError(err)}`);
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* Acquire a cross-process init lock for `dbPath`.
|
|
230
|
+
* Uses `O_CREAT | O_EXCL` for atomic create-or-fail semantics.
|
|
231
|
+
*
|
|
232
|
+
* Returns a release function that removes the lock file. The release
|
|
233
|
+
* function is idempotent and safe to call even if the lock was already
|
|
234
|
+
* cleaned up externally.
|
|
235
|
+
*
|
|
236
|
+
* Throws if the lock cannot be acquired after `INIT_LOCK_MAX_ATTEMPTS`.
|
|
237
|
+
*/
|
|
238
|
+
export const acquireInitLock = async (dbPath) => {
|
|
239
|
+
const lockPath = initLockPath(dbPath);
|
|
240
|
+
const payload = JSON.stringify({ pid: process.pid, ts: Date.now() });
|
|
241
|
+
// Ensure the parent directory exists before creating the lock file.
|
|
242
|
+
// On a fresh repo the `.gitnexus/` directory may not exist yet, and
|
|
243
|
+
// fs.open with O_CREAT | O_EXCL would fail with ENOENT.
|
|
244
|
+
await fs.mkdir(path.dirname(lockPath), { recursive: true });
|
|
245
|
+
for (let attempt = 1; attempt <= INIT_LOCK_MAX_ATTEMPTS; attempt++) {
|
|
246
|
+
try {
|
|
247
|
+
const handle = await fs.open(lockPath, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY);
|
|
248
|
+
await handle.writeFile(payload);
|
|
249
|
+
await handle.close();
|
|
250
|
+
// Return the idempotent release function
|
|
251
|
+
return async () => {
|
|
252
|
+
try {
|
|
253
|
+
await fs.unlink(lockPath);
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
if (!isMissingFileError(err)) {
|
|
257
|
+
const code = extractErrnoCode(err);
|
|
258
|
+
logger.warn(`GitNexus: failed to release init lock (${code ?? 'UNKNOWN'}): ${summarizeError(err)}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
if (err?.code !== 'EEXIST') {
|
|
265
|
+
throw err; // Unexpected error — propagate immediately
|
|
266
|
+
}
|
|
267
|
+
// Lock file exists — check if it's stale
|
|
268
|
+
const broken = await tryBreakStaleLock(lockPath);
|
|
269
|
+
if (broken && attempt < INIT_LOCK_MAX_ATTEMPTS) {
|
|
270
|
+
continue; // Stale lock removed — retry immediately
|
|
271
|
+
}
|
|
272
|
+
if (attempt === INIT_LOCK_MAX_ATTEMPTS) {
|
|
273
|
+
throw new Error(`GitNexus: unable to acquire init lock after ${INIT_LOCK_MAX_ATTEMPTS} attempts — ` +
|
|
274
|
+
`another gitnexus process may be initializing the same database (${lockPath})`);
|
|
275
|
+
}
|
|
276
|
+
// Live process holds the lock — wait and retry
|
|
277
|
+
await new Promise((resolve) => setTimeout(resolve, INIT_LOCK_RETRY_DELAY_MS));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// Unreachable — loop always throws or returns
|
|
281
|
+
throw new Error('GitNexus: init lock acquisition failed unexpectedly');
|
|
282
|
+
};
|
|
283
|
+
/** Exported for testing — returns the lock file path for a given dbPath. */
|
|
284
|
+
export const _initLockPathForTest = initLockPath;
|
|
155
285
|
const runWithSessionLock = async (operation) => {
|
|
156
286
|
const previous = sessionLock;
|
|
157
287
|
let release = null;
|
|
@@ -167,6 +297,65 @@ const runWithSessionLock = async (operation) => {
|
|
|
167
297
|
}
|
|
168
298
|
};
|
|
169
299
|
const normalizeCopyPath = (filePath) => filePath.replace(/\\/g, '/');
|
|
300
|
+
const closeQueryResult = async (result) => {
|
|
301
|
+
try {
|
|
302
|
+
await result.close();
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
// Best-effort cleanup only.
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
const drainQueryResult = async (queryResult) => {
|
|
309
|
+
const results = Array.isArray(queryResult) ? queryResult : [queryResult];
|
|
310
|
+
let firstError;
|
|
311
|
+
let hasError = false;
|
|
312
|
+
for (const result of results) {
|
|
313
|
+
try {
|
|
314
|
+
await result.getAll();
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
if (!hasError) {
|
|
318
|
+
firstError = err;
|
|
319
|
+
hasError = true;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
await closeQueryResult(result);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (hasError)
|
|
327
|
+
throw firstError;
|
|
328
|
+
};
|
|
329
|
+
const readQueryRows = async (queryResult) => {
|
|
330
|
+
const results = Array.isArray(queryResult) ? queryResult : [queryResult];
|
|
331
|
+
let rows = [];
|
|
332
|
+
let firstError;
|
|
333
|
+
let hasError = false;
|
|
334
|
+
for (let i = 0; i < results.length; i++) {
|
|
335
|
+
const result = results[i];
|
|
336
|
+
try {
|
|
337
|
+
const resultRows = await result.getAll();
|
|
338
|
+
if (i === 0)
|
|
339
|
+
rows = resultRows;
|
|
340
|
+
}
|
|
341
|
+
catch (err) {
|
|
342
|
+
if (!hasError) {
|
|
343
|
+
firstError = err;
|
|
344
|
+
hasError = true;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
finally {
|
|
348
|
+
await closeQueryResult(result);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (hasError)
|
|
352
|
+
throw firstError;
|
|
353
|
+
return rows;
|
|
354
|
+
};
|
|
355
|
+
const queryAndDrain = async (targetConn, cypher) => {
|
|
356
|
+
const queryResult = await targetConn.query(cypher);
|
|
357
|
+
await drainQueryResult(queryResult);
|
|
358
|
+
};
|
|
170
359
|
export const initLbug = async (dbPath) => {
|
|
171
360
|
return runWithSessionLock(() => ensureLbugInitialized(dbPath));
|
|
172
361
|
};
|
|
@@ -251,18 +440,62 @@ const doInitLbug = async (dbPath) => {
|
|
|
251
440
|
}
|
|
252
441
|
// If it's a file, assume it's an existing LadybugDB database - LadybugDB will open it
|
|
253
442
|
}
|
|
254
|
-
catch {
|
|
443
|
+
catch (err) {
|
|
444
|
+
if (!isMissingFileError(err)) {
|
|
445
|
+
throw err;
|
|
446
|
+
}
|
|
255
447
|
// Path doesn't exist, which is what LadybugDB wants for a new database
|
|
256
448
|
}
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
449
|
+
// ---------------------------------------------------------------------------
|
|
450
|
+
// Cross-process critical section: acquire init lock, clean orphan sidecars,
|
|
451
|
+
// and open the database. The lock prevents a TOCTOU race where another
|
|
452
|
+
// process could create a fresh DB between our access() check and the
|
|
453
|
+
// unlink() of stale sidecars.
|
|
454
|
+
// ---------------------------------------------------------------------------
|
|
455
|
+
const releaseInitLock = await acquireInitLock(dbPath);
|
|
456
|
+
try {
|
|
457
|
+
// Crash-recovery cleanup: if the main DB file is missing, stale sidecars
|
|
458
|
+
// from an interrupted run can block fresh opens indefinitely.
|
|
459
|
+
try {
|
|
460
|
+
await fs.access(dbPath);
|
|
461
|
+
}
|
|
462
|
+
catch (err) {
|
|
463
|
+
if (isMissingFileError(err)) {
|
|
464
|
+
// `.shadow` is documented by LadybugDB checkpointing and `.wal.checkpoint`
|
|
465
|
+
// was observed in the #1618 crash loop that motivated this recovery path.
|
|
466
|
+
const orphanSidecars = [`${dbPath}.shadow`, `${dbPath}.wal.checkpoint`];
|
|
467
|
+
for (const sidecar of orphanSidecars) {
|
|
468
|
+
try {
|
|
469
|
+
await fs.unlink(sidecar);
|
|
470
|
+
logger.warn(`GitNexus: removed orphan sidecar ${path.basename(sidecar)} (no main DB file present)`);
|
|
471
|
+
}
|
|
472
|
+
catch (err) {
|
|
473
|
+
if (isMissingFileError(err)) {
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
const code = extractErrnoCode(err);
|
|
477
|
+
logger.warn(`GitNexus: failed to remove orphan sidecar ${path.basename(sidecar)} (${code ?? 'UNKNOWN'}) while main DB file is missing; LadybugDB open may still fail: ${summarizeError(err)}`);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
const code = extractErrnoCode(err);
|
|
483
|
+
logger.warn(`GitNexus: unable to verify main DB file before orphan sidecar cleanup (${code ?? 'UNKNOWN'}); skipping cleanup: ${summarizeError(err)}`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
// Ensure parent directory exists
|
|
487
|
+
const parentDir = path.dirname(dbPath);
|
|
488
|
+
await fs.mkdir(parentDir, { recursive: true });
|
|
489
|
+
const opened = await openLbugConnection(lbug, dbPath);
|
|
490
|
+
db = opened.db;
|
|
491
|
+
conn = opened.conn;
|
|
492
|
+
}
|
|
493
|
+
finally {
|
|
494
|
+
await releaseInitLock();
|
|
495
|
+
}
|
|
263
496
|
for (const schemaQuery of SCHEMA_QUERIES) {
|
|
264
497
|
try {
|
|
265
|
-
await conn
|
|
498
|
+
await queryAndDrain(conn, schemaQuery);
|
|
266
499
|
}
|
|
267
500
|
catch (err) {
|
|
268
501
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -312,12 +545,12 @@ export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress)
|
|
|
312
545
|
const normalizedPath = normalizeCopyPath(csvPath);
|
|
313
546
|
const copyQuery = getCopyQuery(table, normalizedPath);
|
|
314
547
|
try {
|
|
315
|
-
await conn
|
|
548
|
+
await queryAndDrain(conn, copyQuery);
|
|
316
549
|
}
|
|
317
550
|
catch (err) {
|
|
318
551
|
try {
|
|
319
552
|
const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)');
|
|
320
|
-
await conn
|
|
553
|
+
await queryAndDrain(conn, retryQuery);
|
|
321
554
|
}
|
|
322
555
|
catch (retryErr) {
|
|
323
556
|
const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
|
@@ -350,12 +583,12 @@ export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress)
|
|
|
350
583
|
log(`Loading edges: ${pairIdx}/${relsByPairMeta.size} types (${fromLabel} -> ${toLabel})`);
|
|
351
584
|
}
|
|
352
585
|
try {
|
|
353
|
-
await conn
|
|
586
|
+
await queryAndDrain(conn, copyQuery);
|
|
354
587
|
}
|
|
355
588
|
catch (err) {
|
|
356
589
|
try {
|
|
357
590
|
const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)');
|
|
358
|
-
await conn
|
|
591
|
+
await queryAndDrain(conn, retryQuery);
|
|
359
592
|
}
|
|
360
593
|
catch (retryErr) {
|
|
361
594
|
const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
|
@@ -475,7 +708,7 @@ const fallbackRelationshipInserts = async (validRelLines, validTables, getNodeLa
|
|
|
475
708
|
const confidence = parseFloat(confidenceStr) || 1.0;
|
|
476
709
|
const step = parseInt(stepStr) || 0;
|
|
477
710
|
const esc = (s) => s.replace(/'/g, "''").replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/\r/g, '\\r');
|
|
478
|
-
await conn
|
|
711
|
+
await queryAndDrain(conn, `
|
|
479
712
|
MATCH (a:${escapeLabel(fromLabel)} {id: '${esc(fromId)}' }),
|
|
480
713
|
(b:${escapeLabel(toLabel)} {id: '${esc(toId)}' })
|
|
481
714
|
CREATE (a)-[:${REL_TABLE_NAME} {type: '${esc(relType)}', confidence: ${confidence}, reason: '${esc(reason)}', step: ${step}}]->(b)
|
|
@@ -589,7 +822,7 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
|
|
|
589
822
|
if (targetDbPath) {
|
|
590
823
|
const tempHandle = await openLbugConnection(lbug, targetDbPath);
|
|
591
824
|
try {
|
|
592
|
-
await tempHandle.conn
|
|
825
|
+
await queryAndDrain(tempHandle.conn, query);
|
|
593
826
|
return true;
|
|
594
827
|
}
|
|
595
828
|
finally {
|
|
@@ -598,7 +831,7 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
|
|
|
598
831
|
}
|
|
599
832
|
else if (conn) {
|
|
600
833
|
// Use existing persistent connection (when called from analyze)
|
|
601
|
-
await conn
|
|
834
|
+
await queryAndDrain(conn, query);
|
|
602
835
|
return true;
|
|
603
836
|
}
|
|
604
837
|
return false;
|
|
@@ -667,7 +900,7 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
|
|
|
667
900
|
: '';
|
|
668
901
|
query = `MERGE (n:${t} {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.content = ${escapeValue(properties.content || '')}${descPart}`;
|
|
669
902
|
}
|
|
670
|
-
await tempConn
|
|
903
|
+
await queryAndDrain(tempConn, query);
|
|
671
904
|
inserted++;
|
|
672
905
|
}
|
|
673
906
|
catch (e) {
|
|
@@ -686,19 +919,17 @@ export const executeQuery = async (cypher) => {
|
|
|
686
919
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
687
920
|
}
|
|
688
921
|
const queryResult = await conn.query(cypher);
|
|
689
|
-
|
|
690
|
-
// Query returns QueryResult for single queries, QueryResult[] for multi-statement
|
|
691
|
-
const result = Array.isArray(queryResult) ? queryResult[0] : queryResult;
|
|
692
|
-
const rows = await result.getAll();
|
|
693
|
-
return rows;
|
|
922
|
+
return await readQueryRows(queryResult);
|
|
694
923
|
};
|
|
695
924
|
export const streamQuery = async (cypher, onRow) => {
|
|
696
925
|
if (!conn) {
|
|
697
926
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
698
927
|
}
|
|
699
928
|
const queryResult = await conn.query(cypher);
|
|
700
|
-
const
|
|
929
|
+
const results = Array.isArray(queryResult) ? queryResult : [queryResult];
|
|
930
|
+
const result = results[0];
|
|
701
931
|
let rowCount = 0;
|
|
932
|
+
let streamError;
|
|
702
933
|
try {
|
|
703
934
|
while (await result.hasNext()) {
|
|
704
935
|
const row = await result.getNext();
|
|
@@ -707,12 +938,17 @@ export const streamQuery = async (cypher, onRow) => {
|
|
|
707
938
|
}
|
|
708
939
|
return rowCount;
|
|
709
940
|
}
|
|
941
|
+
catch (err) {
|
|
942
|
+
streamError = err;
|
|
943
|
+
throw err;
|
|
944
|
+
}
|
|
710
945
|
finally {
|
|
711
946
|
try {
|
|
712
|
-
await
|
|
947
|
+
await drainQueryResult(results);
|
|
713
948
|
}
|
|
714
|
-
catch {
|
|
715
|
-
|
|
949
|
+
catch (err) {
|
|
950
|
+
if (streamError === undefined)
|
|
951
|
+
throw err;
|
|
716
952
|
}
|
|
717
953
|
}
|
|
718
954
|
};
|
|
@@ -730,8 +966,7 @@ export const executePrepared = async (cypher, params) => {
|
|
|
730
966
|
throw new Error(`Prepare failed: ${errMsg}`);
|
|
731
967
|
}
|
|
732
968
|
const queryResult = await conn.execute(stmt, params);
|
|
733
|
-
|
|
734
|
-
return await result.getAll();
|
|
969
|
+
return await readQueryRows(queryResult);
|
|
735
970
|
};
|
|
736
971
|
export const executeWithReusedStatement = async (cypher, paramsList) => {
|
|
737
972
|
if (!conn) {
|
|
@@ -749,7 +984,7 @@ export const executeWithReusedStatement = async (cypher, paramsList) => {
|
|
|
749
984
|
}
|
|
750
985
|
try {
|
|
751
986
|
for (const params of subBatch) {
|
|
752
|
-
await conn.execute(stmt, params);
|
|
987
|
+
await drainQueryResult(await conn.execute(stmt, params));
|
|
753
988
|
}
|
|
754
989
|
}
|
|
755
990
|
catch (e) {
|
|
@@ -767,8 +1002,7 @@ export const getLbugStats = async () => {
|
|
|
767
1002
|
for (const tableName of NODE_TABLES) {
|
|
768
1003
|
try {
|
|
769
1004
|
const queryResult = await conn.query(`MATCH (n:${escapeTableName(tableName)}) RETURN count(n) AS cnt`);
|
|
770
|
-
const
|
|
771
|
-
const nodeRows = await nodeResult.getAll();
|
|
1005
|
+
const nodeRows = await readQueryRows(queryResult);
|
|
772
1006
|
if (nodeRows.length > 0) {
|
|
773
1007
|
totalNodes += Number(nodeRows[0]?.cnt ?? nodeRows[0]?.[0] ?? 0);
|
|
774
1008
|
}
|
|
@@ -780,8 +1014,7 @@ export const getLbugStats = async () => {
|
|
|
780
1014
|
let totalEdges = 0;
|
|
781
1015
|
try {
|
|
782
1016
|
const queryResult = await conn.query(`MATCH ()-[r:${REL_TABLE_NAME}]->() RETURN count(r) AS cnt`);
|
|
783
|
-
const
|
|
784
|
-
const edgeRows = await edgeResult.getAll();
|
|
1017
|
+
const edgeRows = await readQueryRows(queryResult);
|
|
785
1018
|
if (edgeRows.length > 0) {
|
|
786
1019
|
totalEdges = Number(edgeRows[0]?.cnt ?? edgeRows[0]?.[0] ?? 0);
|
|
787
1020
|
}
|
|
@@ -810,8 +1043,7 @@ export const loadCachedEmbeddings = async () => {
|
|
|
810
1043
|
// If the query fails (column missing), we return empty cache to force a full rebuild.
|
|
811
1044
|
try {
|
|
812
1045
|
const check = await conn.query(`MATCH (e:${EMBEDDING_TABLE_NAME}) RETURN e.nodeId AS nodeId, e.chunkIndex AS chunkIndex LIMIT 1`);
|
|
813
|
-
|
|
814
|
-
await checkResult.getAll();
|
|
1046
|
+
await readQueryRows(check);
|
|
815
1047
|
}
|
|
816
1048
|
catch {
|
|
817
1049
|
return { embeddingNodeIds: new Set(), embeddings: [] };
|
|
@@ -833,8 +1065,7 @@ export const loadCachedEmbeddings = async () => {
|
|
|
833
1065
|
throw err;
|
|
834
1066
|
}
|
|
835
1067
|
}
|
|
836
|
-
const
|
|
837
|
-
for (const row of await result.getAll()) {
|
|
1068
|
+
for (const row of await readQueryRows(rows)) {
|
|
838
1069
|
const nodeId = String(row.nodeId ?? row[0] ?? '');
|
|
839
1070
|
if (!nodeId)
|
|
840
1071
|
continue;
|
|
@@ -938,7 +1169,8 @@ export const flushWAL = async () => {
|
|
|
938
1169
|
if (!conn)
|
|
939
1170
|
return;
|
|
940
1171
|
try {
|
|
941
|
-
await conn.query('CHECKPOINT');
|
|
1172
|
+
const checkpointResult = await conn.query('CHECKPOINT');
|
|
1173
|
+
await drainQueryResult(checkpointResult);
|
|
942
1174
|
}
|
|
943
1175
|
catch {
|
|
944
1176
|
/* ignore — older LadybugDB or schemaless DB may not accept it */
|
|
@@ -1036,12 +1268,11 @@ export const deleteNodesForFile = async (filePath, dbPath) => {
|
|
|
1036
1268
|
// First count how many we'll delete
|
|
1037
1269
|
const tn = escapeTableName(tableName);
|
|
1038
1270
|
const countResult = await targetConn.query(`MATCH (n:${tn}) WHERE n.filePath = '${escapedPath}' RETURN count(n) AS cnt`);
|
|
1039
|
-
const
|
|
1040
|
-
const rows = await result.getAll();
|
|
1271
|
+
const rows = await readQueryRows(countResult);
|
|
1041
1272
|
const count = Number(rows[0]?.cnt ?? rows[0]?.[0] ?? 0);
|
|
1042
1273
|
if (count > 0) {
|
|
1043
1274
|
// Delete nodes (and implicitly their relationships via DETACH)
|
|
1044
|
-
await targetConn
|
|
1275
|
+
await queryAndDrain(targetConn, `MATCH (n:${tn}) WHERE n.filePath = '${escapedPath}' DETACH DELETE n`);
|
|
1045
1276
|
deletedNodes += count;
|
|
1046
1277
|
}
|
|
1047
1278
|
}
|
|
@@ -1051,7 +1282,7 @@ export const deleteNodesForFile = async (filePath, dbPath) => {
|
|
|
1051
1282
|
}
|
|
1052
1283
|
// Also delete any embeddings for nodes in this file
|
|
1053
1284
|
try {
|
|
1054
|
-
await targetConn
|
|
1285
|
+
await queryAndDrain(targetConn, `MATCH (e:${EMBEDDING_TABLE_NAME}) WHERE e.nodeId STARTS WITH '${escapedPath}' DELETE e`);
|
|
1055
1286
|
}
|
|
1056
1287
|
catch {
|
|
1057
1288
|
// Embedding table may not exist or nodeId format may differ
|
|
@@ -1065,6 +1296,76 @@ export const deleteNodesForFile = async (filePath, dbPath) => {
|
|
|
1065
1296
|
}
|
|
1066
1297
|
};
|
|
1067
1298
|
export const getEmbeddingTableName = () => EMBEDDING_TABLE_NAME;
|
|
1299
|
+
/**
|
|
1300
|
+
* Return the distinct repo-relative paths of files that import
|
|
1301
|
+
* `targetFilePath` according to the IMPORTS edges currently in the
|
|
1302
|
+
* DB. Used by the incremental writeback path to expand the
|
|
1303
|
+
* "files-to-rewrite" set so that files importing a changed file get
|
|
1304
|
+
* their edges (which may have been refined by cross-file resolution)
|
|
1305
|
+
* re-emitted, rather than left stale in the DB.
|
|
1306
|
+
*
|
|
1307
|
+
* The DB query reads the *previous* run's state — pre-pipeline, before
|
|
1308
|
+
* any nodes are deleted — so the returned importers are "files that
|
|
1309
|
+
* USED TO import the target". That's the right set to invalidate:
|
|
1310
|
+
* those are the files whose edges in the DB might no longer match
|
|
1311
|
+
* what cross-file resolution produces given the changed file's new
|
|
1312
|
+
* exports.
|
|
1313
|
+
*/
|
|
1314
|
+
export const queryImporters = async (targetFilePath) => {
|
|
1315
|
+
if (!conn) {
|
|
1316
|
+
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
1317
|
+
}
|
|
1318
|
+
const escaped = targetFilePath.replace(/'/g, "''");
|
|
1319
|
+
const cypher = `
|
|
1320
|
+
MATCH (a)-[r:${REL_TABLE_NAME}]->(b)
|
|
1321
|
+
WHERE r.type = 'IMPORTS' AND b.filePath = '${escaped}'
|
|
1322
|
+
RETURN DISTINCT a.filePath AS importer
|
|
1323
|
+
`;
|
|
1324
|
+
try {
|
|
1325
|
+
const queryResult = await conn.query(cypher);
|
|
1326
|
+
const result = Array.isArray(queryResult) ? queryResult[0] : queryResult;
|
|
1327
|
+
const rows = await result.getAll();
|
|
1328
|
+
const out = [];
|
|
1329
|
+
for (const row of rows) {
|
|
1330
|
+
const v = row.importer;
|
|
1331
|
+
if (typeof v === 'string' && v.length > 0)
|
|
1332
|
+
out.push(v);
|
|
1333
|
+
}
|
|
1334
|
+
return out;
|
|
1335
|
+
}
|
|
1336
|
+
catch {
|
|
1337
|
+
return [];
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1340
|
+
/**
|
|
1341
|
+
* Drop every Community and Process node (and their MEMBER_OF /
|
|
1342
|
+
* STEP_IN_PROCESS edges via DETACH DELETE). Used at the start of an
|
|
1343
|
+
* incremental run so the communities and processes phases regenerate
|
|
1344
|
+
* them from scratch on the merged graph — required for the
|
|
1345
|
+
* "Leiden runs on the FULL graph" correctness invariant.
|
|
1346
|
+
*/
|
|
1347
|
+
export const deleteAllCommunitiesAndProcesses = async () => {
|
|
1348
|
+
if (!conn) {
|
|
1349
|
+
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
1350
|
+
}
|
|
1351
|
+
let nodesDeleted = 0;
|
|
1352
|
+
for (const label of ['Community', 'Process']) {
|
|
1353
|
+
try {
|
|
1354
|
+
const countResult = await conn.query(`MATCH (n:${label}) RETURN count(n) AS cnt`);
|
|
1355
|
+
const result = Array.isArray(countResult) ? countResult[0] : countResult;
|
|
1356
|
+
const rows = await result.getAll();
|
|
1357
|
+
const count = Number(rows[0]?.cnt ?? rows[0]?.[0] ?? 0);
|
|
1358
|
+
if (count > 0) {
|
|
1359
|
+
await conn.query(`MATCH (n:${label}) DETACH DELETE n`);
|
|
1360
|
+
nodesDeleted += count;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
catch {
|
|
1364
|
+
// Table may not exist yet on a freshly-initialized DB — fine.
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
return { nodesDeleted };
|
|
1368
|
+
};
|
|
1068
1369
|
// ============================================================================
|
|
1069
1370
|
// Full-Text Search (FTS) Functions
|
|
1070
1371
|
// ============================================================================
|
|
@@ -1088,7 +1389,7 @@ export const loadFTSExtension = async (targetConn, opts = {}) => {
|
|
|
1088
1389
|
if (!c) {
|
|
1089
1390
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
1090
1391
|
}
|
|
1091
|
-
const loaded = await extensionManager.ensure((sql) => c
|
|
1392
|
+
const loaded = await extensionManager.ensure((sql) => queryAndDrain(c, sql), 'fts', 'FTS', opts);
|
|
1092
1393
|
if (loaded && useModuleState)
|
|
1093
1394
|
ftsLoaded = true;
|
|
1094
1395
|
return loaded;
|
|
@@ -1116,7 +1417,7 @@ export const loadVectorExtension = async (targetConn, opts = {}) => {
|
|
|
1116
1417
|
if (!c) {
|
|
1117
1418
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
1118
1419
|
}
|
|
1119
|
-
const loaded = await extensionManager.ensure((sql) => c
|
|
1420
|
+
const loaded = await extensionManager.ensure((sql) => queryAndDrain(c, sql), 'VECTOR', 'VECTOR', opts);
|
|
1120
1421
|
if (loaded && useModuleState)
|
|
1121
1422
|
vectorExtensionLoaded = true;
|
|
1122
1423
|
return loaded;
|
|
@@ -1141,7 +1442,7 @@ export const createFTSIndex = async (tableName, indexName, properties, stemmer =
|
|
|
1141
1442
|
const propList = properties.map((p) => `'${p}'`).join(', ');
|
|
1142
1443
|
const query = `CALL CREATE_FTS_INDEX('${tableName}', '${indexName}', [${propList}], stemmer := '${stemmer}')`;
|
|
1143
1444
|
try {
|
|
1144
|
-
await conn
|
|
1445
|
+
await queryAndDrain(conn, query);
|
|
1145
1446
|
ensuredFTSIndexes.add(key);
|
|
1146
1447
|
}
|
|
1147
1448
|
catch (e) {
|
|
@@ -1210,8 +1511,7 @@ export const queryFTS = async (tableName, indexName, query, limit = 20, conjunct
|
|
|
1210
1511
|
`;
|
|
1211
1512
|
try {
|
|
1212
1513
|
const queryResult = await conn.query(cypher);
|
|
1213
|
-
const
|
|
1214
|
-
const rows = await result.getAll();
|
|
1514
|
+
const rows = await readQueryRows(queryResult);
|
|
1215
1515
|
return rows.map((row) => {
|
|
1216
1516
|
const node = row.node || row[0] || {};
|
|
1217
1517
|
const score = row.score ?? row[1] ?? 0;
|
|
@@ -1240,7 +1540,7 @@ export const dropFTSIndex = async (tableName, indexName) => {
|
|
|
1240
1540
|
throw new Error('LadybugDB not initialized. Call initLbug first.');
|
|
1241
1541
|
}
|
|
1242
1542
|
try {
|
|
1243
|
-
await conn
|
|
1543
|
+
await queryAndDrain(conn, `CALL DROP_FTS_INDEX('${tableName}', '${indexName}')`);
|
|
1244
1544
|
}
|
|
1245
1545
|
catch {
|
|
1246
1546
|
// Index may not exist
|
|
@@ -42,6 +42,8 @@ export interface AnalyzeOptions {
|
|
|
42
42
|
skipAgentsMd?: boolean;
|
|
43
43
|
/** Omit volatile symbol/relationship counts from AGENTS.md and CLAUDE.md. */
|
|
44
44
|
noStats?: boolean;
|
|
45
|
+
/** Skip installing standard GitNexus skill files to .claude/skills/gitnexus/. */
|
|
46
|
+
skipSkills?: boolean;
|
|
45
47
|
/**
|
|
46
48
|
* User-provided alias for the registry `name` (#829). When set,
|
|
47
49
|
* forwarded to `registerRepo` so the indexed repo is stored under
|