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
|
@@ -10,6 +10,7 @@ import { realpathSync } from 'fs';
|
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import os from 'os';
|
|
12
12
|
import { getInferredRepoName, resolveRepoIdentityRoot } from './git.js';
|
|
13
|
+
import { logger } from '../core/logger.js';
|
|
13
14
|
/**
|
|
14
15
|
* Normalise a repo path for registry comparison across platforms
|
|
15
16
|
* (#664 review feedback from @evander-wang).
|
|
@@ -49,6 +50,10 @@ export const canonicalizePath = (p) => {
|
|
|
49
50
|
return resolved;
|
|
50
51
|
}
|
|
51
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* Bumped whenever incremental-indexing invariants change incompatibly.
|
|
55
|
+
*/
|
|
56
|
+
export const INCREMENTAL_SCHEMA_VERSION = 1;
|
|
52
57
|
const GITNEXUS_DIR = '.gitnexus';
|
|
53
58
|
const GITNEXUS_EXCLUDE_ENTRY = `${GITNEXUS_DIR}/`;
|
|
54
59
|
// ─── Local Storage Helpers ─────────────────────────────────────────────
|
|
@@ -137,12 +142,23 @@ export const loadMeta = async (storagePath) => {
|
|
|
137
142
|
}
|
|
138
143
|
};
|
|
139
144
|
/**
|
|
140
|
-
* Save metadata to storage
|
|
145
|
+
* Save metadata to storage.
|
|
146
|
+
*
|
|
147
|
+
* Atomic via tmp-file + rename (matches `saveParseCache`'s pattern). The
|
|
148
|
+
* `incrementalInProgress` dirty flag travels through this file — a crash
|
|
149
|
+
* mid-write would leave a corrupt `meta.json` that the next run's
|
|
150
|
+
* `loadMeta` would silently treat as "no prior index", losing the dirty
|
|
151
|
+
* flag and skipping the recovery full-rebuild. Write-and-rename rules
|
|
152
|
+
* that out: the rename is atomic on POSIX and on Windows (`fs.rename`
|
|
153
|
+
* on `node:fs/promises` uses `MoveFileEx(REPLACE_EXISTING)`), so either
|
|
154
|
+
* the old or the new file is observed at every moment.
|
|
141
155
|
*/
|
|
142
156
|
export const saveMeta = async (storagePath, meta) => {
|
|
143
157
|
await fs.mkdir(storagePath, { recursive: true });
|
|
144
158
|
const metaPath = path.join(storagePath, 'meta.json');
|
|
145
|
-
|
|
159
|
+
const tmpPath = `${metaPath}.tmp`;
|
|
160
|
+
await fs.writeFile(tmpPath, JSON.stringify(meta, null, 2), 'utf-8');
|
|
161
|
+
await fs.rename(tmpPath, metaPath);
|
|
146
162
|
};
|
|
147
163
|
/**
|
|
148
164
|
* Check if a path has a GitNexus index
|
|
@@ -185,13 +201,42 @@ export const findRepo = async (startPath) => {
|
|
|
185
201
|
}
|
|
186
202
|
return null;
|
|
187
203
|
};
|
|
204
|
+
function isReadOnlyFilesystemError(err) {
|
|
205
|
+
const code = err?.code;
|
|
206
|
+
return code === 'EROFS' || code === 'EACCES' || code === 'EPERM';
|
|
207
|
+
}
|
|
188
208
|
/**
|
|
189
209
|
* Keep generated index files ignored without modifying the user's root .gitignore.
|
|
190
210
|
*/
|
|
191
211
|
export const ensureGitNexusIgnored = async (repoPath) => {
|
|
192
212
|
const gitignorePath = path.join(getStoragePath(repoPath), '.gitignore');
|
|
193
|
-
|
|
194
|
-
|
|
213
|
+
const desired = '*\n';
|
|
214
|
+
// Idempotent fast path: skip the write entirely when the file already has
|
|
215
|
+
// the expected content. Lets this run cleanly on read-only mounts (e.g.
|
|
216
|
+
// the documented Docker workflow with WORKSPACE_DIR bound :ro) when an
|
|
217
|
+
// earlier `analyze` already created the file. See issue #1549.
|
|
218
|
+
try {
|
|
219
|
+
if ((await fs.readFile(gitignorePath, 'utf-8')) === desired) {
|
|
220
|
+
await ensureGitInfoExclude(repoPath);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
if (err?.code !== 'ENOENT')
|
|
226
|
+
throw err;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
await fs.mkdir(path.dirname(gitignorePath), { recursive: true });
|
|
230
|
+
await fs.writeFile(gitignorePath, desired, 'utf-8');
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
if (isReadOnlyFilesystemError(err)) {
|
|
234
|
+
logger.warn({ path: gitignorePath, code: err.code }, 'GitNexus storage filesystem is not writable; skipping .gitnexus/.gitignore. Generated files may appear as untracked in this repo locally.');
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
throw err;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
195
240
|
await ensureGitInfoExclude(repoPath);
|
|
196
241
|
};
|
|
197
242
|
const ensureGitInfoExclude = async (repoPath) => {
|
|
@@ -205,7 +250,6 @@ const ensureGitInfoExclude = async (repoPath) => {
|
|
|
205
250
|
catch {
|
|
206
251
|
return;
|
|
207
252
|
}
|
|
208
|
-
await fs.mkdir(path.dirname(excludePath), { recursive: true });
|
|
209
253
|
let content = '';
|
|
210
254
|
try {
|
|
211
255
|
content = await fs.readFile(excludePath, 'utf-8');
|
|
@@ -221,7 +265,18 @@ const ensureGitInfoExclude = async (repoPath) => {
|
|
|
221
265
|
if (excludes.includes(GITNEXUS_DIR) || excludes.includes(GITNEXUS_EXCLUDE_ENTRY))
|
|
222
266
|
return;
|
|
223
267
|
const separator = content.length === 0 || content.endsWith('\n') ? '' : '\n';
|
|
224
|
-
|
|
268
|
+
try {
|
|
269
|
+
await fs.mkdir(path.dirname(excludePath), { recursive: true });
|
|
270
|
+
await fs.writeFile(excludePath, `${content}${separator}${GITNEXUS_EXCLUDE_ENTRY}\n`, 'utf-8');
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
if (isReadOnlyFilesystemError(err)) {
|
|
274
|
+
logger.warn({ path: excludePath, code: err.code }, 'GitNexus storage filesystem is not writable; skipping .git/info/exclude update. .gitnexus/ may appear as untracked in `git status` locally.');
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
throw err;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
225
280
|
};
|
|
226
281
|
// ─── Global Registry (~/.gitnexus/registry.json) ───────────────────────
|
|
227
282
|
/**
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
const fs = require('fs');
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const { spawnSync } = require('child_process');
|
|
17
|
+
const { acquireHookSlot } = require('./hook-lock.cjs');
|
|
18
|
+
const { hasGitNexusDbLockedByGitNexusServer } = require('./hook-db-lock-probe.cjs');
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* Read JSON input from stdin synchronously.
|
|
@@ -102,6 +104,28 @@ function findGitNexusDir(startDir) {
|
|
|
102
104
|
return null;
|
|
103
105
|
}
|
|
104
106
|
|
|
107
|
+
function hasGitNexusServerOwner(gitNexusDir) {
|
|
108
|
+
return hasGitNexusDbLockedByGitNexusServer(path.join(gitNexusDir, 'lbug'), process.pid);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function extractAugmentContext(stderr) {
|
|
112
|
+
const output = (stderr || '').trim();
|
|
113
|
+
const marker = output.indexOf('[GitNexus]');
|
|
114
|
+
const debug = process.env.GITNEXUS_DEBUG === '1' || process.env.GITNEXUS_DEBUG === 'true';
|
|
115
|
+
if (debug && output.length > 0) {
|
|
116
|
+
// Emit the FULL discarded prefix (everything before the marker, or all of
|
|
117
|
+
// it when no marker is present) so suppressed diagnostics — KuzuDB lock
|
|
118
|
+
// warnings, parser errors, etc. — remain recoverable on the hook's own
|
|
119
|
+
// stderr. The untruncated payload lets operators see exactly what was
|
|
120
|
+
// filtered out instead of a 180-char JSON-quoted preview.
|
|
121
|
+
const discarded = marker === -1 ? output : output.slice(0, marker).trim();
|
|
122
|
+
if (discarded.length > 0) {
|
|
123
|
+
process.stderr.write(`[GitNexus hook] augment stderr discarded prefix:\n${discarded}\n`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return marker === -1 ? '' : output.slice(marker).trim();
|
|
127
|
+
}
|
|
128
|
+
|
|
105
129
|
/**
|
|
106
130
|
* Extract search pattern from tool input.
|
|
107
131
|
*/
|
|
@@ -167,6 +191,10 @@ function extractPattern(toolName, toolInput) {
|
|
|
167
191
|
* 3. Fall back to npx (returns empty string)
|
|
168
192
|
*/
|
|
169
193
|
function resolveCliPath() {
|
|
194
|
+
const fromEnv = process.env.GITNEXUS_HOOK_CLI_PATH;
|
|
195
|
+
if (fromEnv !== undefined && String(fromEnv).trim() && fs.existsSync(String(fromEnv))) {
|
|
196
|
+
return String(fromEnv);
|
|
197
|
+
}
|
|
170
198
|
let cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');
|
|
171
199
|
if (!fs.existsSync(cliPath)) {
|
|
172
200
|
try {
|
|
@@ -207,7 +235,8 @@ function runGitNexusCli(cliPath, args, cwd, timeout) {
|
|
|
207
235
|
function handlePreToolUse(input) {
|
|
208
236
|
const cwd = input.cwd || process.cwd();
|
|
209
237
|
if (!path.isAbsolute(cwd)) return;
|
|
210
|
-
|
|
238
|
+
const gitNexusDir = findGitNexusDir(cwd);
|
|
239
|
+
if (!gitNexusDir) return;
|
|
211
240
|
|
|
212
241
|
const toolName = input.tool_name || '';
|
|
213
242
|
const toolInput = input.tool_input || {};
|
|
@@ -216,20 +245,29 @@ function handlePreToolUse(input) {
|
|
|
216
245
|
|
|
217
246
|
const pattern = extractPattern(toolName, toolInput);
|
|
218
247
|
if (!pattern || pattern.length < 3) return;
|
|
248
|
+
if (hasGitNexusServerOwner(gitNexusDir)) {
|
|
249
|
+
process.stderr.write('[GitNexus] augment skipped: MCP server owns DB\n');
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const release = acquireHookSlot(gitNexusDir);
|
|
254
|
+
if (!release) return;
|
|
219
255
|
|
|
220
256
|
const cliPath = resolveCliPath();
|
|
221
257
|
let result = '';
|
|
222
258
|
try {
|
|
223
259
|
const child = runGitNexusCli(cliPath, ['augment', '--', pattern], cwd, 7000);
|
|
224
260
|
if (!child.error && child.status === 0) {
|
|
225
|
-
result = child.stderr || '';
|
|
261
|
+
result = extractAugmentContext(child.stderr || '');
|
|
226
262
|
}
|
|
227
263
|
} catch {
|
|
228
264
|
/* graceful failure */
|
|
265
|
+
} finally {
|
|
266
|
+
release();
|
|
229
267
|
}
|
|
230
268
|
|
|
231
|
-
if (result
|
|
232
|
-
sendHookResponse('PreToolUse', result
|
|
269
|
+
if (result) {
|
|
270
|
+
sendHookResponse('PreToolUse', result);
|
|
233
271
|
}
|
|
234
272
|
}
|
|
235
273
|
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform best-effort probe: does another process hold dbPath open
|
|
3
|
+
* with a command line that looks like a GitNexus MCP/serve server?
|
|
4
|
+
*
|
|
5
|
+
* Backends (no user-installed Sysinternals):
|
|
6
|
+
* - Linux: scan procfs under /proc (per-PID fd entries) via stat(2) (dev+inode); works without lsof;
|
|
7
|
+
* optional lsof fallback when proc scan finds nothing.
|
|
8
|
+
* - macOS / *BSD / etc.: trusted lsof + ps (absolute paths first).
|
|
9
|
+
* - Windows: Restart Manager (rstrtmgr) via bundled PowerShell script +
|
|
10
|
+
* Win32_Process for command lines; trusted powershell.exe under %SystemRoot%.
|
|
11
|
+
*
|
|
12
|
+
* Fail-open on most errors; fail-closed only on lsof ETIMEDOUT (Unix) or
|
|
13
|
+
* PowerShell ETIMEDOUT (Windows), matching the hook contract.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const { spawnSync } = require('child_process');
|
|
19
|
+
|
|
20
|
+
function isGitNexusServerCommand(command) {
|
|
21
|
+
const hasServerMode = /(?:^|\s)(mcp|serve)(?:\s|$)/.test(command);
|
|
22
|
+
const hasGitNexus =
|
|
23
|
+
/(?:^|[/\\\s])gitnexus(?:\.cmd)?(?:\s|$)/.test(command) ||
|
|
24
|
+
/node_modules[/\\]gitnexus[/\\]/.test(command);
|
|
25
|
+
return hasServerMode && hasGitNexus;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function resolveHookBinary(tool) {
|
|
29
|
+
const envKey = tool === 'lsof' ? 'GITNEXUS_HOOK_LSOF_PATH' : 'GITNEXUS_HOOK_PS_PATH';
|
|
30
|
+
const fromEnv = process.env[envKey];
|
|
31
|
+
if (fromEnv && String(fromEnv).trim() && fs.existsSync(String(fromEnv))) {
|
|
32
|
+
return String(fromEnv);
|
|
33
|
+
}
|
|
34
|
+
const candidates =
|
|
35
|
+
tool === 'lsof'
|
|
36
|
+
? ['/usr/bin/lsof', '/usr/sbin/lsof', '/sbin/lsof', tool]
|
|
37
|
+
: ['/bin/ps', '/usr/bin/ps', tool];
|
|
38
|
+
for (const candidate of candidates) {
|
|
39
|
+
if (candidate === tool) return tool;
|
|
40
|
+
try {
|
|
41
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
42
|
+
} catch {
|
|
43
|
+
/* ignore */
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return tool;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resolveWindowsPowerShellPath() {
|
|
50
|
+
const fromEnv = process.env.GITNEXUS_HOOK_POWERSHELL_PATH;
|
|
51
|
+
if (fromEnv && String(fromEnv).trim() && fs.existsSync(String(fromEnv).trim())) {
|
|
52
|
+
return String(fromEnv).trim();
|
|
53
|
+
}
|
|
54
|
+
const root = process.env.SystemRoot || 'C:\\Windows';
|
|
55
|
+
const ps = path.join(root, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
|
56
|
+
if (fs.existsSync(ps)) return ps;
|
|
57
|
+
const psWow = path.join(root, 'SysWOW64', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
|
58
|
+
if (fs.existsSync(psWow)) return psWow;
|
|
59
|
+
return 'powershell.exe';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Sentinel:
|
|
63
|
+
// undefined = not loaded yet (try the read)
|
|
64
|
+
// string = encoded PowerShell command (successful load)
|
|
65
|
+
// null = load attempted and failed (do not retry; warning already emitted)
|
|
66
|
+
let windowsRmListPsEncodedCommandCache;
|
|
67
|
+
let windowsRmListPsLoadFailureWarned = false;
|
|
68
|
+
function getWindowsRmListEncodedCommand() {
|
|
69
|
+
if (windowsRmListPsEncodedCommandCache !== undefined) {
|
|
70
|
+
return windowsRmListPsEncodedCommandCache;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const ps1Path = path.join(__dirname, 'win-rm-list-json.ps1');
|
|
74
|
+
const src = fs
|
|
75
|
+
.readFileSync(ps1Path, 'utf8')
|
|
76
|
+
.replace(/^\uFEFF/, '')
|
|
77
|
+
.replace(/\r\n/g, '\n');
|
|
78
|
+
windowsRmListPsEncodedCommandCache = Buffer.from(src, 'utf16le').toString('base64');
|
|
79
|
+
} catch (err) {
|
|
80
|
+
windowsRmListPsEncodedCommandCache = null;
|
|
81
|
+
if (
|
|
82
|
+
!windowsRmListPsLoadFailureWarned &&
|
|
83
|
+
(process.env.GITNEXUS_DEBUG === '1' || process.env.GITNEXUS_DEBUG === 'true')
|
|
84
|
+
) {
|
|
85
|
+
windowsRmListPsLoadFailureWarned = true;
|
|
86
|
+
const msg = err && err.message ? String(err.message).slice(0, 200) : 'unknown';
|
|
87
|
+
process.stderr.write(`[GitNexus hook] win-rm-list-json.ps1 load failed: ${msg}\n`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return windowsRmListPsEncodedCommandCache;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function hasGitNexusServerOwnerWindows(dbPathAbs, myPid) {
|
|
94
|
+
const encoded = getWindowsRmListEncodedCommand();
|
|
95
|
+
if (!encoded) return false;
|
|
96
|
+
const psExe = resolveWindowsPowerShellPath();
|
|
97
|
+
const r = spawnSync(
|
|
98
|
+
psExe,
|
|
99
|
+
[
|
|
100
|
+
'-NoProfile',
|
|
101
|
+
'-NonInteractive',
|
|
102
|
+
'-ExecutionPolicy',
|
|
103
|
+
'Bypass',
|
|
104
|
+
'-STA',
|
|
105
|
+
'-EncodedCommand',
|
|
106
|
+
encoded,
|
|
107
|
+
],
|
|
108
|
+
{
|
|
109
|
+
encoding: 'utf-8',
|
|
110
|
+
timeout: 6000,
|
|
111
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
112
|
+
env: { ...process.env, GITNEXUS_HOOK_RM_TARGET: dbPathAbs },
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
// ETIMEDOUT means the PowerShell probe didn't return in time; treat as 'unresponsive process holds DB' → fail-closed (skip augment).
|
|
116
|
+
if (r.error) return r.error.code === 'ETIMEDOUT';
|
|
117
|
+
if (r.status !== 0) return false;
|
|
118
|
+
let rows;
|
|
119
|
+
try {
|
|
120
|
+
rows = JSON.parse(String(r.stdout || '').trim() || '[]');
|
|
121
|
+
} catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if (!Array.isArray(rows)) return false;
|
|
125
|
+
for (const row of rows) {
|
|
126
|
+
const procId = Number(row.pid);
|
|
127
|
+
const cmd = String(row.cmd || '');
|
|
128
|
+
if (!Number.isFinite(procId) || procId === myPid) continue;
|
|
129
|
+
if (isGitNexusServerCommand(cmd)) return true;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function readLinuxCmdline(pidStr) {
|
|
135
|
+
try {
|
|
136
|
+
return fs.readFileSync(`/proc/${pidStr}/cmdline`, 'utf8').replace(/\0+/g, ' ').trim();
|
|
137
|
+
} catch {
|
|
138
|
+
return '';
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function linuxProcScanFindGitNexusServer(dbPathAbs, myPid) {
|
|
143
|
+
const raw = process.env.GITNEXUS_HOOK_LINUX_PROC_BUDGET_MS;
|
|
144
|
+
const budget = Number(raw && String(raw).trim()) ? Number.parseInt(String(raw), 10) : 1200;
|
|
145
|
+
const start = Date.now();
|
|
146
|
+
let targetStat;
|
|
147
|
+
try {
|
|
148
|
+
targetStat = fs.statSync(dbPathAbs);
|
|
149
|
+
} catch {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
let procEntries;
|
|
153
|
+
try {
|
|
154
|
+
procEntries = fs.readdirSync('/proc', { withFileTypes: true });
|
|
155
|
+
} catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
for (const ent of procEntries) {
|
|
159
|
+
if (Date.now() - start > budget) return false;
|
|
160
|
+
if (!ent.isDirectory() || !/^\d+$/.test(ent.name)) continue;
|
|
161
|
+
const pid = Number.parseInt(ent.name, 10);
|
|
162
|
+
if (!Number.isFinite(pid) || pid === myPid) continue;
|
|
163
|
+
const fdDir = path.join('/proc', ent.name, 'fd');
|
|
164
|
+
let fds;
|
|
165
|
+
try {
|
|
166
|
+
fds = fs.readdirSync(fdDir);
|
|
167
|
+
} catch {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
let holds = false;
|
|
171
|
+
for (const fd of fds) {
|
|
172
|
+
if (Date.now() - start > budget) return false;
|
|
173
|
+
try {
|
|
174
|
+
const st = fs.statSync(path.join(fdDir, fd));
|
|
175
|
+
if (st.dev === targetStat.dev && st.ino === targetStat.ino) {
|
|
176
|
+
holds = true;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
/* ignore */
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!holds) continue;
|
|
184
|
+
if (isGitNexusServerCommand(readLinuxCmdline(ent.name))) return true;
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function unixLsofPsFindGitNexusServer(dbPathAbs, myPid) {
|
|
190
|
+
const lsofPath = resolveHookBinary('lsof');
|
|
191
|
+
const lsof = spawnSync(lsofPath, ['-nP', '-t', '--', dbPathAbs], {
|
|
192
|
+
encoding: 'utf-8',
|
|
193
|
+
timeout: 1000,
|
|
194
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
195
|
+
});
|
|
196
|
+
if (lsof.error) return lsof.error.code === 'ETIMEDOUT';
|
|
197
|
+
|
|
198
|
+
const pids = (lsof.stdout || '').split(/\s+/).filter(Boolean);
|
|
199
|
+
const psPath = resolveHookBinary('ps');
|
|
200
|
+
for (const pid of pids) {
|
|
201
|
+
if (Number(pid) === myPid) continue;
|
|
202
|
+
const ps = spawnSync(psPath, ['-p', pid, '-o', 'command='], {
|
|
203
|
+
encoding: 'utf-8',
|
|
204
|
+
timeout: 500,
|
|
205
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
206
|
+
});
|
|
207
|
+
if (ps.error) {
|
|
208
|
+
if (ps.error.code === 'ETIMEDOUT') return true;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (isGitNexusServerCommand(ps.stdout || '')) return true;
|
|
212
|
+
}
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @param {string} dbPath Absolute or relative path to the DB file (e.g. .../lbug).
|
|
218
|
+
* @param {number} myPid Current process PID (hook runner), excluded from matches.
|
|
219
|
+
*/
|
|
220
|
+
function hasGitNexusDbLockedByGitNexusServer(dbPath, myPid) {
|
|
221
|
+
if (!fs.existsSync(dbPath)) return false;
|
|
222
|
+
const dbPathAbs = path.resolve(dbPath);
|
|
223
|
+
|
|
224
|
+
if (process.platform === 'win32') {
|
|
225
|
+
return hasGitNexusServerOwnerWindows(dbPathAbs, myPid);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (process.platform === 'linux') {
|
|
229
|
+
if (linuxProcScanFindGitNexusServer(dbPathAbs, myPid)) return true;
|
|
230
|
+
return unixLsofPsFindGitNexusServer(dbPathAbs, myPid);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return unixLsofPsFindGitNexusServer(dbPathAbs, myPid);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = {
|
|
237
|
+
hasGitNexusDbLockedByGitNexusServer,
|
|
238
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const HOOK_LOCK_SUBDIR = '.hook-locks';
|
|
5
|
+
const HOOK_LOCK_MAX_INFLIGHT = 3;
|
|
6
|
+
const HOOK_LOCK_STALE_MS = 30000;
|
|
7
|
+
|
|
8
|
+
function acquireHookSlot(gitNexusDir) {
|
|
9
|
+
const lockDir = path.join(gitNexusDir, HOOK_LOCK_SUBDIR);
|
|
10
|
+
try {
|
|
11
|
+
fs.mkdirSync(lockDir, { recursive: true });
|
|
12
|
+
} catch {
|
|
13
|
+
// Cannot create lock dir (read-only fs, cross-user perm denial, out of
|
|
14
|
+
// inodes, etc.) — fail closed by returning null. Caller skips augment.
|
|
15
|
+
// Fail-open here would let N concurrent hooks all proceed unguarded and
|
|
16
|
+
// reintroduce the #1486 fan-out the guard exists to prevent.
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const myPidStr = String(process.pid);
|
|
21
|
+
|
|
22
|
+
for (let slot = 0; slot < HOOK_LOCK_MAX_INFLIGHT; slot++) {
|
|
23
|
+
const slotPath = path.join(lockDir, `slot-${slot}.lock`);
|
|
24
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
25
|
+
try {
|
|
26
|
+
fs.writeFileSync(slotPath, myPidStr, { flag: 'wx' });
|
|
27
|
+
let released = false;
|
|
28
|
+
const release = () => {
|
|
29
|
+
if (released) return;
|
|
30
|
+
released = true;
|
|
31
|
+
try {
|
|
32
|
+
// Only unlink if we still own the slot. If we appeared stale and
|
|
33
|
+
// another hook took over, the file now belongs to it — leave alone.
|
|
34
|
+
const content = fs.readFileSync(slotPath, 'utf-8').trim();
|
|
35
|
+
if (content === myPidStr) fs.unlinkSync(slotPath);
|
|
36
|
+
} catch {
|
|
37
|
+
/* already removed or unreadable */
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
process.on('exit', release);
|
|
41
|
+
return release;
|
|
42
|
+
} catch {
|
|
43
|
+
// Slot exists. Decide whether to take it over.
|
|
44
|
+
// Open once and inspect mtime + content via the same fd so there's
|
|
45
|
+
// no TOCTOU between the metadata check and the content read
|
|
46
|
+
// (codeql js/file-system-race).
|
|
47
|
+
let fd;
|
|
48
|
+
try {
|
|
49
|
+
fd = fs.openSync(slotPath, 'r');
|
|
50
|
+
} catch {
|
|
51
|
+
continue; // Vanished between EEXIST and open — retry this slot.
|
|
52
|
+
}
|
|
53
|
+
let isLive = false;
|
|
54
|
+
let mtimeMs = Date.now();
|
|
55
|
+
try {
|
|
56
|
+
mtimeMs = fs.fstatSync(fd).mtimeMs;
|
|
57
|
+
const buf = Buffer.alloc(32);
|
|
58
|
+
const n = fs.readSync(fd, buf, 0, 32, 0);
|
|
59
|
+
const ownerStr = buf.slice(0, n).toString('utf-8').trim();
|
|
60
|
+
if (ownerStr === '') {
|
|
61
|
+
// Owner created the file but hasn't written its PID yet. The
|
|
62
|
+
// wx open+write window is microseconds; give it the benefit
|
|
63
|
+
// of the doubt and treat as live.
|
|
64
|
+
isLive = true;
|
|
65
|
+
} else {
|
|
66
|
+
const owner = Number.parseInt(ownerStr, 10);
|
|
67
|
+
if (Number.isFinite(owner) && owner > 0) {
|
|
68
|
+
try {
|
|
69
|
+
process.kill(owner, 0);
|
|
70
|
+
isLive = true;
|
|
71
|
+
} catch (e) {
|
|
72
|
+
// ESRCH = process gone → treat as dead. EPERM = process exists
|
|
73
|
+
// but owned by another user (cross-user lock dir) → still alive,
|
|
74
|
+
// keep the slot. Anything else: be conservative, assume alive.
|
|
75
|
+
if (e && e.code === 'ESRCH') {
|
|
76
|
+
isLive = false;
|
|
77
|
+
} else {
|
|
78
|
+
isLive = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} catch {
|
|
84
|
+
/* unreadable — treat as dead */
|
|
85
|
+
} finally {
|
|
86
|
+
try {
|
|
87
|
+
fs.closeSync(fd);
|
|
88
|
+
} catch {
|
|
89
|
+
/* already closed */
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// For slots younger than HOOK_LOCK_STALE_MS, PID-liveness wins —
|
|
93
|
+
// a slow-but-alive hook is never wrongly evicted. For older slots,
|
|
94
|
+
// age is the final arbiter as a defense against PID reuse on long-
|
|
95
|
+
// abandoned slots. 30s >> the 7s augment timeout, so a healthy run
|
|
96
|
+
// never crosses this threshold.
|
|
97
|
+
if (isLive && Date.now() - mtimeMs > HOOK_LOCK_STALE_MS) {
|
|
98
|
+
isLive = false;
|
|
99
|
+
}
|
|
100
|
+
if (isLive) break; // Try the next slot.
|
|
101
|
+
try {
|
|
102
|
+
fs.unlinkSync(slotPath);
|
|
103
|
+
} catch {
|
|
104
|
+
/* another hook beat us to it — retry will hit EEXIST */
|
|
105
|
+
}
|
|
106
|
+
// Loop and retry this slot.
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
HOOK_LOCK_SUBDIR,
|
|
116
|
+
HOOK_LOCK_MAX_INFLIGHT,
|
|
117
|
+
HOOK_LOCK_STALE_MS,
|
|
118
|
+
acquireHookSlot,
|
|
119
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
$ErrorActionPreference = 'Stop'
|
|
2
|
+
$target = $env:GITNEXUS_HOOK_RM_TARGET
|
|
3
|
+
if ([string]::IsNullOrWhiteSpace($target)) { Write-Output '[]'; exit 0 }
|
|
4
|
+
$target = (Resolve-Path -LiteralPath $target).ProviderPath
|
|
5
|
+
|
|
6
|
+
if (-not ([Management.Automation.PSTypeName]'GitNexusHookRm.Native').Type) {
|
|
7
|
+
Add-Type @'
|
|
8
|
+
using System;
|
|
9
|
+
using System.Runtime.InteropServices;
|
|
10
|
+
namespace GitNexusHookRm {
|
|
11
|
+
public static class Native {
|
|
12
|
+
public const int ErrorMoreData = 234;
|
|
13
|
+
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
14
|
+
public struct RM_UNIQUE_PROCESS {
|
|
15
|
+
public int dwProcessId;
|
|
16
|
+
public long ProcessStartTime;
|
|
17
|
+
}
|
|
18
|
+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
19
|
+
public struct RM_PROCESS_INFO {
|
|
20
|
+
public RM_UNIQUE_PROCESS Process;
|
|
21
|
+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
|
|
22
|
+
public string strAppName;
|
|
23
|
+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
|
|
24
|
+
public string strServiceShortName;
|
|
25
|
+
public uint ApplicationType;
|
|
26
|
+
public uint AppStatus;
|
|
27
|
+
public uint TSSessionId;
|
|
28
|
+
public uint bRestartable;
|
|
29
|
+
}
|
|
30
|
+
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
|
|
31
|
+
public static extern int RmStartSession(out uint pSessionHandle, uint dwSessionFlags, string strSessionKey);
|
|
32
|
+
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
|
|
33
|
+
public static extern int RmRegisterResources(uint pSessionHandle, uint nFiles, string[] rgsFileNames, uint nApplications, IntPtr rgApplications, uint nServices, string[] rgsServiceNames);
|
|
34
|
+
[DllImport("rstrtmgr.dll")]
|
|
35
|
+
public static extern int RmGetList(uint dwSessionHandle, out uint pnProcInfoNeeded, ref uint pnProcInfo, [In, Out] RM_PROCESS_INFO[] rgAffectedApps, ref uint lpdwRebootReasons);
|
|
36
|
+
[DllImport("rstrtmgr.dll")]
|
|
37
|
+
public static extern int RmEndSession(uint pSessionHandle);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
'@
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
$h = [uint32]0
|
|
44
|
+
$key = [guid]::NewGuid().ToString('N')
|
|
45
|
+
$rmErr = [GitNexusHookRm.Native]::RmStartSession([ref]$h, 0, $key)
|
|
46
|
+
if ($rmErr -ne 0) { Write-Output '[]'; exit 0 }
|
|
47
|
+
$files = @($target)
|
|
48
|
+
$err = [GitNexusHookRm.Native]::RmRegisterResources($h, 1, $files, 0, [IntPtr]::Zero, 0, $null)
|
|
49
|
+
if ($err -ne 0) {
|
|
50
|
+
[void][GitNexusHookRm.Native]::RmEndSession($h)
|
|
51
|
+
Write-Output '[]'
|
|
52
|
+
exit 0
|
|
53
|
+
}
|
|
54
|
+
$need = [uint32]0
|
|
55
|
+
$n = [uint32]0
|
|
56
|
+
$reboot = [uint32]0
|
|
57
|
+
$err = [GitNexusHookRm.Native]::RmGetList($h, [ref]$need, [ref]$n, $null, [ref]$reboot)
|
|
58
|
+
if ($err -ne [GitNexusHookRm.Native]::ErrorMoreData) {
|
|
59
|
+
[void][GitNexusHookRm.Native]::RmEndSession($h)
|
|
60
|
+
Write-Output '[]'
|
|
61
|
+
exit 0
|
|
62
|
+
}
|
|
63
|
+
$n = $need
|
|
64
|
+
$buf = New-Object GitNexusHookRm.Native+RM_PROCESS_INFO[] ([int]$n)
|
|
65
|
+
$err = [GitNexusHookRm.Native]::RmGetList($h, [ref]$need, [ref]$n, $buf, [ref]$reboot)
|
|
66
|
+
[void][GitNexusHookRm.Native]::RmEndSession($h)
|
|
67
|
+
if ($err -ne 0) { Write-Output '[]'; exit 0 }
|
|
68
|
+
|
|
69
|
+
$out = @()
|
|
70
|
+
for ($i = 0; $i -lt [int]$n; $i++) {
|
|
71
|
+
$procId = $buf[$i].Process.dwProcessId
|
|
72
|
+
$p = Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$procId" -ErrorAction SilentlyContinue
|
|
73
|
+
$cmd = if ($p) { $p.CommandLine } else { '' }
|
|
74
|
+
$out += [PSCustomObject]@{ pid = [int]$procId; cmd = $cmd }
|
|
75
|
+
}
|
|
76
|
+
ConvertTo-Json -InputObject @($out) -Compress
|
package/package.json
CHANGED
package/scripts/build.js
CHANGED
|
@@ -21,11 +21,15 @@ const SHARED_DEST = path.join(DIST, '_shared');
|
|
|
21
21
|
|
|
22
22
|
// ── 1. Build gitnexus-shared ───────────────────────────────────────
|
|
23
23
|
console.log('[build] compiling gitnexus-shared…');
|
|
24
|
-
|
|
24
|
+
const tscCmd =
|
|
25
|
+
process.platform === 'win32'
|
|
26
|
+
? path.join('node_modules', '.bin', 'tsc.cmd')
|
|
27
|
+
: path.join('node_modules', '.bin', 'tsc');
|
|
28
|
+
execSync(tscCmd, { cwd: SHARED_ROOT, stdio: 'inherit', timeout: 120_000 });
|
|
25
29
|
|
|
26
30
|
// ── 2. Build gitnexus ──────────────────────────────────────────────
|
|
27
31
|
console.log('[build] compiling gitnexus…');
|
|
28
|
-
execSync(
|
|
32
|
+
execSync(tscCmd, { cwd: ROOT, stdio: 'inherit', timeout: 120_000 });
|
|
29
33
|
|
|
30
34
|
// ── 3. Copy shared dist ────────────────────────────────────────────
|
|
31
35
|
console.log('[build] copying shared module into dist/_shared…');
|