gitnexus 1.6.5-rc.2 → 1.6.5-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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/cli/ai-context.d.ts +1 -0
- package/dist/cli/ai-context.js +30 -12
- package/dist/cli/analyze.d.ts +18 -0
- package/dist/cli/analyze.js +33 -4
- package/dist/cli/augment.d.ts +1 -1
- package/dist/cli/augment.js +1 -1
- package/dist/cli/index.js +6 -1
- package/dist/cli/setup.js +6 -0
- package/dist/core/augmentation/engine.d.ts +2 -2
- package/dist/core/augmentation/engine.js +26 -6
- package/dist/core/embeddings/ast-utils.js +2 -1
- package/dist/core/embeddings/http-client.js +22 -6
- package/dist/core/group/extractors/grpc-extractor.js +2 -1
- package/dist/core/group/extractors/http-patterns/python.js +175 -0
- package/dist/core/group/extractors/http-route-extractor.js +2 -1
- package/dist/core/group/extractors/include-extractor.js +2 -1
- package/dist/core/group/extractors/thrift-extractor.js +2 -1
- package/dist/core/group/extractors/tree-sitter-scanner.js +2 -1
- 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.js +166 -47
- package/dist/core/ingestion/community-processor.js +18 -0
- package/dist/core/ingestion/heritage-processor.js +3 -2
- package/dist/core/ingestion/import-processor.js +2 -1
- package/dist/core/ingestion/import-resolvers/standard.d.ts +5 -0
- package/dist/core/ingestion/import-resolvers/standard.js +27 -1
- package/dist/core/ingestion/import-resolvers/utils.js +4 -0
- package/dist/core/ingestion/languages/c/arity-metadata.d.ts +14 -0
- package/dist/core/ingestion/languages/c/arity-metadata.js +94 -0
- package/dist/core/ingestion/languages/c/arity.d.ts +6 -0
- package/dist/core/ingestion/languages/c/arity.js +18 -0
- package/dist/core/ingestion/languages/c/captures.d.ts +2 -0
- package/dist/core/ingestion/languages/c/captures.js +105 -0
- package/dist/core/ingestion/languages/c/header-scan.d.ts +7 -0
- package/dist/core/ingestion/languages/c/header-scan.js +55 -0
- package/dist/core/ingestion/languages/c/import-decomposer.d.ts +8 -0
- package/dist/core/ingestion/languages/c/import-decomposer.js +50 -0
- package/dist/core/ingestion/languages/c/import-target.d.ts +14 -0
- package/dist/core/ingestion/languages/c/import-target.js +57 -0
- package/dist/core/ingestion/languages/c/index.d.ts +11 -0
- package/dist/core/ingestion/languages/c/index.js +11 -0
- package/dist/core/ingestion/languages/c/interpret.d.ts +14 -0
- package/dist/core/ingestion/languages/c/interpret.js +48 -0
- package/dist/core/ingestion/languages/c/merge-bindings.d.ts +7 -0
- package/dist/core/ingestion/languages/c/merge-bindings.js +23 -0
- package/dist/core/ingestion/languages/c/query.d.ts +3 -0
- package/dist/core/ingestion/languages/c/query.js +161 -0
- package/dist/core/ingestion/languages/c/scope-resolver.d.ts +13 -0
- package/dist/core/ingestion/languages/c/scope-resolver.js +60 -0
- package/dist/core/ingestion/languages/c/simple-hooks.d.ts +14 -0
- package/dist/core/ingestion/languages/c/simple-hooks.js +19 -0
- package/dist/core/ingestion/languages/c/static-linkage.d.ts +13 -0
- package/dist/core/ingestion/languages/c/static-linkage.js +57 -0
- package/dist/core/ingestion/languages/c-cpp.js +10 -0
- package/dist/core/ingestion/languages/csharp/captures.js +2 -1
- package/dist/core/ingestion/languages/csharp/namespace-siblings.js +3 -2
- package/dist/core/ingestion/languages/go/captures.js +2 -1
- package/dist/core/ingestion/languages/go/range-binding.js +2 -1
- 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/languages/python/captures.js +2 -1
- package/dist/core/ingestion/languages/typescript/captures.js +2 -1
- package/dist/core/ingestion/parsing-processor.d.ts +22 -2
- package/dist/core/ingestion/parsing-processor.js +85 -50
- 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-resolution/contract/scope-resolver.d.ts +65 -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 +5 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +25 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +79 -7
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +7 -3
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +24 -4
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +21 -2
- 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 +31 -7
- package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
- package/dist/core/ingestion/tree-sitter-queries.js +10 -0
- package/dist/core/ingestion/workers/parse-worker.js +2 -1
- package/dist/core/lbug/lbug-adapter.d.ts +26 -0
- package/dist/core/lbug/lbug-adapter.js +174 -40
- package/dist/core/lbug/pool-adapter.js +19 -2
- package/dist/core/run-analyze.d.ts +2 -0
- package/dist/core/run-analyze.js +323 -29
- package/dist/core/tree-sitter/safe-parse.d.ts +6 -0
- package/dist/core/tree-sitter/safe-parse.js +32 -0
- package/dist/server/git-clone.js +10 -13
- package/dist/storage/file-hash.d.ts +47 -0
- package/dist/storage/file-hash.js +86 -0
- package/dist/storage/git.d.ts +17 -6
- package/dist/storage/git.js +46 -12
- package/dist/storage/parse-cache.d.ts +67 -0
- package/dist/storage/parse-cache.js +182 -0
- package/dist/storage/repo-manager.d.ts +41 -1
- package/dist/storage/repo-manager.js +17 -2
- package/hooks/claude/gitnexus-hook.cjs +8 -1
- package/hooks/claude/hook-lock.cjs +119 -0
- package/package.json +1 -1
- package/web/assets/{agent-BpHQleHE.js → agent-BG6RCOzz.js} +1 -1
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-F-tujre-.js +1 -0
- package/web/assets/architectureDiagram-UL44E2DR-2GL7nOvO.js +36 -0
- package/web/assets/blockDiagram-7IZFK4PR-5AjX0pPU.js +132 -0
- package/web/assets/{c4Diagram-DFAF54RM-DeJV1rCn.js → c4Diagram-Y2BXMSZH-hLOeq7xC.js} +2 -2
- package/web/assets/chunk-2T2R6R2M-B_pq_2nf.js +4 -0
- package/web/assets/chunk-2UTLFMKG-CpoohCFN.js +1 -0
- package/web/assets/{chunk-7RZVMHOQ-BdIU-RGO.js → chunk-3SSMPTDK-CfqpR3Nr.js} +1 -1
- package/web/assets/chunk-4R4BOZG6-D3gm7tYj.js +159 -0
- package/web/assets/chunk-6764PJDD-WcI965eO.js +1 -0
- package/web/assets/chunk-AQ6EADP3-BpMj3rQl.js +1 -0
- package/web/assets/{chunk-KSICW3F5-C2tZmXwv.js → chunk-AZZRMDJM-DSoCJlUA.js} +1 -1
- package/web/assets/chunk-C62D2QBJ-CvFF2PnB.js +1 -0
- package/web/assets/chunk-CEXFNPSA-DdNtJtrF.js +1 -0
- package/web/assets/chunk-J5EP6P6S-DtcVaAJI.js +1 -0
- package/web/assets/{chunk-AEOMTBSW-D7qjBMHW.js → chunk-JQRUD6KW-DElaNjc0.js} +1 -1
- package/web/assets/chunk-KGYTTC2M-D0K5or7e.js +1 -0
- package/web/assets/chunk-KRXBNO2N-DpR8iDt8.js +1 -0
- package/web/assets/chunk-LCXTWHL2-DdMObOEk.js +231 -0
- package/web/assets/chunk-LII3EMHJ-I78zXtzi.js +1 -0
- package/web/assets/chunk-RERM46MO-Betu1cSy.js +1 -0
- package/web/assets/chunk-RG4AUYOV-BUCJ2JP5.js +206 -0
- package/web/assets/chunk-RKZBBQEN-Cutpza0t.js +1 -0
- package/web/assets/chunk-RLI5ZMPA-BFUxCvX2.js +1 -0
- package/web/assets/chunk-T5OCTHI4-DKwlkuvB.js +1 -0
- package/web/assets/chunk-UP6H54XL-0YWa0NDg.js +1 -0
- package/web/assets/chunk-UXSXWOXI-CSz0OXpz.js +1 -0
- package/web/assets/chunk-W44A43WB-AOmgoT-X.js +1 -0
- package/web/assets/{chunk-RWUO3TPN-DTWzAhQ1.js → chunk-ZXARS5L4-DIzgxjMw.js} +1 -1
- package/web/assets/classDiagram-KGZ6W3CR-BT-rn7LF.js +1 -0
- package/web/assets/classDiagram-v2-72OJOZXJ-uR3nDadY.js +1 -0
- package/web/assets/{cose-bilkent-PNC4W37J-DdZq7Qz_.js → cose-bilkent-UX7MHV2Q-DCSCD1TK.js} +1 -1
- package/web/assets/dagre-ND4H6XIP-CsciHSij.js +4 -0
- package/web/assets/diagram-3NCE3AQN-BKUT5Rpc.js +43 -0
- package/web/assets/diagram-GF46GFSD-Dxb4hZSo.js +24 -0
- package/web/assets/diagram-HNR7UZ2L-D3qus9Cn.js +3 -0
- package/web/assets/diagram-QXG6HAR7-r9BMLGGP.js +24 -0
- package/web/assets/diagram-WEQXMOUZ-BGXqs7-m.js +10 -0
- package/web/assets/{erDiagram-GCSMX5X6-DfdN6iqF.js → erDiagram-L5TCEMPS-LgSWVrkZ.js} +2 -2
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-By7r3bla.js +1 -0
- package/web/assets/flowDiagram-H6V6AXG4-Bj_vV5ky.js +162 -0
- package/web/assets/ganttDiagram-JCBTUEKG-B1eVITSh.js +292 -0
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-ClEP6RdE.js +1 -0
- package/web/assets/gitGraphDiagram-S2ZK5IYY-BP0R-_FD.js +106 -0
- package/web/assets/{index-3NUCUW2s.js → index-oL-ZNYwb.js} +80 -81
- package/web/assets/info-J43DQDTF-KCYPFFUO-bBJRjmP5.js +1 -0
- package/web/assets/infoDiagram-3YFTVSEB-Dmj4OEO3.js +2 -0
- package/web/assets/{ishikawaDiagram-YMYX4NHK-BF8AtDaj.js → ishikawaDiagram-BNXS4ZKH-B3NgQ4lJ.js} +2 -2
- package/web/assets/{journeyDiagram-SO5T7YLQ-BW8oKa3N.js → journeyDiagram-M6C3CM3L-DALmL4_N.js} +3 -3
- package/web/assets/{kanban-definition-LJHFXRCJ-DzbOsR-2.js → kanban-definition-75IXJCU3-AvVbHpnN.js} +4 -4
- package/web/assets/{katex-GD7MH7QM-CJiOjBBJ.js → katex-K3KEBU37-CqF6_ZN-.js} +1 -1
- package/web/assets/{mindmap-definition-2EUWGEK5-BIXGQALq.js → mindmap-definition-2TDM6QVE-B5c9VOzs.js} +28 -28
- package/web/assets/packet-YPE3B663-LP52Z2RK-1ZCldnu3.js +1 -0
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-EhjHC5EE.js +1 -0
- package/web/assets/pieDiagram-CU6KROY3-Po2HSSR4.js +30 -0
- package/web/assets/quadrantDiagram-VICAPDV7-C47UxSZD.js +7 -0
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-CvXjgxlW.js +1 -0
- package/web/assets/{requirementDiagram-M5DCFWZL-CzUUrhQD.js → requirementDiagram-JXO7QTGE-CpECN_Hq.js} +6 -6
- package/web/assets/sankeyDiagram-URQDO5SZ-B3b7cLrS.js +40 -0
- package/web/assets/sequenceDiagram-VS2MUI6T-CRJ95oVP.js +162 -0
- package/web/assets/stateDiagram-7D4R322I-C7U9b3ks.js +1 -0
- package/web/assets/stateDiagram-v2-36443NZ5-BQajbtov.js +1 -0
- package/web/assets/{timeline-definition-5SPVSISX-DkMiGj4Z.js → timeline-definition-O6YCAMPW-rtbk6BBo.js} +4 -4
- package/web/assets/treeView-BLDUP644-QA4HXRO3-R2o5gwfP.js +1 -0
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-Cesmac_u.js +1 -0
- package/web/assets/{vennDiagram-IE5QUKF5-BCxMmf9q.js → vennDiagram-MWXL3ELB-W8Yoaayh.js} +3 -3
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-B9HYoRgA.js +1 -0
- package/web/assets/wardleyDiagram-CUQ6CDDI-DIZw5Abj.js +78 -0
- package/web/assets/{xychartDiagram-ZHJ5623Y-DZ7fP1_3.js → xychartDiagram-N2JHSOCM-DGRtrj4P.js} +3 -3
- package/web/index.html +2 -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-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/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/flowDiagram-OTCZ4VVT-aAZw4c_M.js +0 -162
- 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/info-OMHHGYJF-BF2H5H6G-BIOsZy0W.js +0 -1
- package/web/assets/infoDiagram-MN7RKWGX-DUoMH-pq.js +0 -2
- 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/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/treeView-SZITEDCU-5DXDK3XO-Cy6P0_CZ.js +0 -1
- package/web/assets/treemap-W4RFUUIX-WYLRDWKO-C_pp1R7A.js +0 -1
- 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-CG-ydtp4.js} +0 -0
package/dist/core/run-analyze.js
CHANGED
|
@@ -10,10 +10,15 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import fs from 'fs/promises';
|
|
13
|
+
import { execFileSync } from 'child_process';
|
|
13
14
|
import { runPipelineFromRepo } from './ingestion/pipeline.js';
|
|
14
|
-
import { initLbug, loadGraphToLbug, getLbugStats, executeQuery, executeWithReusedStatement, closeLbug, loadCachedEmbeddings, } from './lbug/lbug-adapter.js';
|
|
15
|
+
import { initLbug, loadGraphToLbug, getLbugStats, executeQuery, executeWithReusedStatement, closeLbug, loadCachedEmbeddings, deleteNodesForFile, deleteAllCommunitiesAndProcesses, queryImporters, } from './lbug/lbug-adapter.js';
|
|
15
16
|
import { createSearchFTSIndexes } from './search/fts-indexes.js';
|
|
16
|
-
import { getStoragePaths, saveMeta, loadMeta, ensureGitNexusIgnored, registerRepo, cleanupOldKuzuFiles, } from '../storage/repo-manager.js';
|
|
17
|
+
import { getStoragePaths, saveMeta, loadMeta, ensureGitNexusIgnored, registerRepo, cleanupOldKuzuFiles, INCREMENTAL_SCHEMA_VERSION, } from '../storage/repo-manager.js';
|
|
18
|
+
import { computeFileHashes, diffFileHashes } from '../storage/file-hash.js';
|
|
19
|
+
import { extractChangedSubgraph, computeEffectiveWriteSet, } from './incremental/subgraph-extract.js';
|
|
20
|
+
import { shadowCandidatesFor } from './incremental/shadow-candidates.js';
|
|
21
|
+
import { loadParseCache, saveParseCache, pruneCache } from '../storage/parse-cache.js';
|
|
17
22
|
import { getCurrentCommit, getRemoteUrl, hasGitDir, getInferredRepoName, resolveRepoIdentityRoot, } from '../storage/git.js';
|
|
18
23
|
import { generateAIContextFiles } from '../cli/ai-context.js';
|
|
19
24
|
import { EMBEDDING_TABLE_NAME } from './lbug/schema.js';
|
|
@@ -63,22 +68,74 @@ export async function runFullAnalysis(repoPath, options, callbacks) {
|
|
|
63
68
|
const repoHasGit = hasGitDir(repoPath);
|
|
64
69
|
const currentCommit = repoHasGit ? getCurrentCommit(repoPath) : '';
|
|
65
70
|
const existingMeta = await loadMeta(storagePath);
|
|
71
|
+
// ── Crash recovery: dirty flag forces full rebuild ────────────────
|
|
72
|
+
// If the previous incremental run set incrementalInProgress and didn't
|
|
73
|
+
// clear it, the on-disk index may be in a half-state. Cheapest path
|
|
74
|
+
// back to a known-good index is to wipe + rebuild from scratch.
|
|
75
|
+
if (existingMeta?.incrementalInProgress) {
|
|
76
|
+
log('Previous incremental run did not complete cleanly (incrementalInProgress flag set); ' +
|
|
77
|
+
'forcing full rebuild to restore a known-good index.');
|
|
78
|
+
options = { ...options, force: true };
|
|
79
|
+
// Reload meta after clearing the flag in-memory; we still want fileHashes
|
|
80
|
+
// for the post-rebuild meta carry-over, but force=true ensures the
|
|
81
|
+
// rebuild path executes.
|
|
82
|
+
}
|
|
66
83
|
// ── Early-return: already up to date ──────────────────────────────
|
|
67
84
|
if (existingMeta && !options.force && existingMeta.lastCommit === currentCommit) {
|
|
68
85
|
// Non-git folders have currentCommit = '' — always rebuild since we can't detect changes
|
|
69
86
|
if (currentCommit !== '') {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
87
|
+
// For git repos, even if HEAD matches lastCommit, the working tree
|
|
88
|
+
// may have uncommitted changes. Only short-circuit when the working
|
|
89
|
+
// tree is also clean — otherwise fall through to the incremental
|
|
90
|
+
// path which will hash-diff and update only changed files.
|
|
91
|
+
//
|
|
92
|
+
// We exclude paths that GitNexus itself writes during analyze:
|
|
93
|
+
// .gitnexus/ — db / parse cache / meta.json
|
|
94
|
+
// .claude/, .cursor/ — auto-generated agent skill files
|
|
95
|
+
// AGENTS.md, CLAUDE.md — auto-updated stats blocks
|
|
96
|
+
// Counting them as dirty would perpetually defeat the up-to-date
|
|
97
|
+
// fast path because the previous analyze just wrote them
|
|
98
|
+
// (regression vs PR #1233 behavior).
|
|
99
|
+
const dirty = (() => {
|
|
100
|
+
try {
|
|
101
|
+
const out = execFileSync('git', [
|
|
102
|
+
'status',
|
|
103
|
+
'--porcelain',
|
|
104
|
+
'--',
|
|
105
|
+
'.',
|
|
106
|
+
':(exclude).gitnexus',
|
|
107
|
+
':(exclude).gitnexus/**',
|
|
108
|
+
':(exclude).claude',
|
|
109
|
+
':(exclude).claude/**',
|
|
110
|
+
':(exclude).cursor',
|
|
111
|
+
':(exclude).cursor/**',
|
|
112
|
+
':(exclude)AGENTS.md',
|
|
113
|
+
':(exclude)CLAUDE.md',
|
|
114
|
+
], {
|
|
115
|
+
cwd: repoPath,
|
|
116
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
117
|
+
encoding: 'utf8',
|
|
118
|
+
});
|
|
119
|
+
return out.trim().length > 0;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return true; // conservative on git failure
|
|
123
|
+
}
|
|
124
|
+
})();
|
|
125
|
+
if (!dirty) {
|
|
126
|
+
await ensureGitNexusIgnored(repoPath);
|
|
127
|
+
return {
|
|
128
|
+
// `resolveRepoIdentityRoot` collapses worktree roots to the
|
|
129
|
+
// canonical repo basename (#1259) but leaves arbitrary subdirs
|
|
130
|
+
// and `--skip-git` paths unchanged (#1232/#1233 intent preserved).
|
|
131
|
+
repoName: options.registryName ??
|
|
132
|
+
getInferredRepoName(repoPath) ??
|
|
133
|
+
path.basename(resolveRepoIdentityRoot(repoPath)),
|
|
134
|
+
repoPath,
|
|
135
|
+
stats: existingMeta.stats ?? {},
|
|
136
|
+
alreadyUpToDate: true,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
82
139
|
}
|
|
83
140
|
}
|
|
84
141
|
// ── Cache embeddings from existing index before rebuild ────────────
|
|
@@ -114,6 +171,14 @@ export async function runFullAnalysis(repoPath, options, callbacks) {
|
|
|
114
171
|
`Pass --embeddings to also generate embeddings for new/changed nodes, ` +
|
|
115
172
|
`or --drop-embeddings to wipe them.`);
|
|
116
173
|
}
|
|
174
|
+
// We *always* load the embedding cache when one is requested (regardless
|
|
175
|
+
// of the predicted `willTryIncremental`). The post-pipeline branch may
|
|
176
|
+
// disagree with the prediction (e.g. when the pipeline produces zero
|
|
177
|
+
// File nodes, `isIncremental` flips false and the full-rebuild path
|
|
178
|
+
// wipes the DB) — loading unconditionally is cheap insurance against
|
|
179
|
+
// silently dropping embeddings on a mispredicted run. The re-insert
|
|
180
|
+
// step gates itself on the actual `isIncremental` value to avoid
|
|
181
|
+
// PK-conflicts when the incremental writeback path keeps the rows.
|
|
117
182
|
if (shouldLoadCache && existingMeta) {
|
|
118
183
|
try {
|
|
119
184
|
progress('embeddings', 0, 'Caching embeddings...');
|
|
@@ -140,23 +205,77 @@ export async function runFullAnalysis(repoPath, options, callbacks) {
|
|
|
140
205
|
}
|
|
141
206
|
}
|
|
142
207
|
}
|
|
208
|
+
// ── Load incremental parse cache ──────────────────────────────────
|
|
209
|
+
// Content-addressed: safe to reuse across `--force` runs (chunks whose
|
|
210
|
+
// file contents haven't changed produce identical worker output).
|
|
211
|
+
// Loaded into a single ParseCache object that the pipeline mutates
|
|
212
|
+
// in-place (cache hits leave entries unchanged; misses add new ones).
|
|
213
|
+
const parseCache = await loadParseCache(storagePath);
|
|
143
214
|
// ── Phase 1: Full Pipeline (0–60%) ────────────────────────────────
|
|
144
215
|
const pipelineResult = await runPipelineFromRepo(repoPath, (p) => {
|
|
145
216
|
const phaseLabel = PHASE_LABELS[p.phase] || p.phase;
|
|
146
217
|
const scaled = Math.round(p.percent * 0.6);
|
|
147
|
-
const message = p.detail
|
|
218
|
+
const message = p.detail
|
|
219
|
+
? `${p.message || phaseLabel} (${p.detail})`
|
|
220
|
+
: p.message || phaseLabel;
|
|
148
221
|
progress(p.phase, scaled, message);
|
|
149
|
-
});
|
|
222
|
+
}, { parseCache });
|
|
150
223
|
// ── Phase 2: LadybugDB (60–85%) ──────────────────────────────────
|
|
151
224
|
progress('lbug', 60, 'Loading into LadybugDB...');
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
225
|
+
// Compute current per-file content hashes from the pipeline's File nodes.
|
|
226
|
+
// Used both to drive the incremental DB writeback (when eligible) and to
|
|
227
|
+
// populate meta.json.fileHashes for the next run.
|
|
228
|
+
const allFilePaths = [];
|
|
229
|
+
pipelineResult.graph.forEachNode((n) => {
|
|
230
|
+
if (n.label === 'File') {
|
|
231
|
+
const fp = n.properties?.filePath;
|
|
232
|
+
if (fp)
|
|
233
|
+
allFilePaths.push(fp);
|
|
157
234
|
}
|
|
158
|
-
|
|
159
|
-
|
|
235
|
+
});
|
|
236
|
+
const newFileHashes = await computeFileHashes(repoPath, allFilePaths);
|
|
237
|
+
// Decide incremental vs full at THIS point (post-pipeline, pre-DB).
|
|
238
|
+
// All eligibility conditions are checked here against the actual
|
|
239
|
+
// pipeline output — no separate pre-pipeline prediction to desync from
|
|
240
|
+
// (Bugbot review on PR #1479: a prediction that flipped post-pipeline
|
|
241
|
+
// could skip the embedding cache load and then take the full-rebuild
|
|
242
|
+
// path, silently losing embeddings).
|
|
243
|
+
const isIncremental = !options.force &&
|
|
244
|
+
!!existingMeta &&
|
|
245
|
+
existingMeta.schemaVersion === INCREMENTAL_SCHEMA_VERSION &&
|
|
246
|
+
!!existingMeta.fileHashes &&
|
|
247
|
+
Object.keys(existingMeta.fileHashes).length > 0 &&
|
|
248
|
+
repoHasGit &&
|
|
249
|
+
allFilePaths.length > 0;
|
|
250
|
+
const hashDiff = isIncremental
|
|
251
|
+
? diffFileHashes(newFileHashes, existingMeta.fileHashes)
|
|
252
|
+
: undefined;
|
|
253
|
+
if (isIncremental && hashDiff) {
|
|
254
|
+
log(`Incremental: changed=${hashDiff.changed.length}, ` +
|
|
255
|
+
`added=${hashDiff.added.length}, ` +
|
|
256
|
+
`deleted=${hashDiff.deleted.length} ` +
|
|
257
|
+
`(skipping wipe + ${allFilePaths.length - hashDiff.toWrite.length} unchanged file rows preserved)`);
|
|
258
|
+
// Set the dirty flag BEFORE any destructive DB mutation. Cleared on
|
|
259
|
+
// success at the meta-save step.
|
|
260
|
+
await saveMeta(storagePath, {
|
|
261
|
+
...existingMeta,
|
|
262
|
+
incrementalInProgress: {
|
|
263
|
+
startedAt: Date.now(),
|
|
264
|
+
toWriteCount: hashDiff.toWrite.length,
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
// Full rebuild path: wipe DB files first.
|
|
270
|
+
await closeLbug();
|
|
271
|
+
const lbugFiles = [lbugPath, `${lbugPath}.wal`, `${lbugPath}.lock`];
|
|
272
|
+
for (const f of lbugFiles) {
|
|
273
|
+
try {
|
|
274
|
+
await fs.rm(f, { recursive: true, force: true });
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
/* swallow */
|
|
278
|
+
}
|
|
160
279
|
}
|
|
161
280
|
}
|
|
162
281
|
await initLbug(lbugPath);
|
|
@@ -165,16 +284,158 @@ export async function runFullAnalysis(repoPath, options, callbacks) {
|
|
|
165
284
|
// is called even if an error occurs — the module-level singleton DB handle
|
|
166
285
|
// must be released to avoid blocking subsequent invocations.
|
|
167
286
|
let lbugMsgCount = 0;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
287
|
+
if (isIncremental && hashDiff) {
|
|
288
|
+
// ── Incremental DB writeback ───────────────────────────────────
|
|
289
|
+
// 0. Expand the writable set with transitive importers of
|
|
290
|
+
// changed/deleted files (bounded BFS).
|
|
291
|
+
//
|
|
292
|
+
// Reason (Bugbot/Claude review on PR #1479): when a barrel /
|
|
293
|
+
// re-export file C changes, cross-file resolution may update
|
|
294
|
+
// CALLS edges between two unchanged files A and B (A imports
|
|
295
|
+
// from C, C re-exports something from B). Those refined edges
|
|
296
|
+
// live in `ctx.graph` but would be excluded from the subgraph
|
|
297
|
+
// if neither endpoint is in the changed set. To catch this,
|
|
298
|
+
// files that imported (directly OR transitively, through
|
|
299
|
+
// other unchanged intermediaries) any changed file get pulled
|
|
300
|
+
// into the writable set so their rows are deleted + rewritten
|
|
301
|
+
// against the refined edges.
|
|
302
|
+
//
|
|
303
|
+
// BFS bound: MAX_IMPORTER_BFS_DEPTH. Practically sized to
|
|
304
|
+
// catch nested barrel chains (e.g. `index.ts → submodule/index.ts
|
|
305
|
+
// → submodule/impl.ts`) without ballooning into a near-full-
|
|
306
|
+
// rebuild on monorepos with deep re-export pyramids. Beyond
|
|
307
|
+
// this depth, the "incremental ≡ full-rebuild" invariant is
|
|
308
|
+
// self-acknowledged as best-effort; `--force` remains the
|
|
309
|
+
// escape hatch documented in GUARDRAILS.md.
|
|
310
|
+
//
|
|
311
|
+
// `queryImporters` reads `IMPORTS` from the pre-pipeline DB
|
|
312
|
+
// state, so the result is "files that USED TO import the
|
|
313
|
+
// target" — exactly the set whose previously-stored edges may
|
|
314
|
+
// no longer match what cross-file resolution produces this run.
|
|
315
|
+
const MAX_IMPORTER_BFS_DEPTH = 4;
|
|
316
|
+
const writableFiles = new Set(hashDiff.toWrite);
|
|
317
|
+
const directlyChangedCount = writableFiles.size;
|
|
318
|
+
// Shadow-seed: for ADDED files, queryImporters returns 0 (the new
|
|
319
|
+
// file has no IMPORTS rows in the pre-pipeline DB yet). But pre-
|
|
320
|
+
// existing unchanged files may have IMPORTS edges whose module-
|
|
321
|
+
// resolution claim the newcomer can steal under standard JS/TS
|
|
322
|
+
// resolution (Bugbot review on PR #1479). For each added file we
|
|
323
|
+
// derive the shadow candidates and, if the candidate was a known
|
|
324
|
+
// file in the prior meta, seed it into the BFS frontier so its
|
|
325
|
+
// importers — surfaced via queryImporters — get their CALLS edges
|
|
326
|
+
// re-resolved against the new file. See shadow-candidates.ts for
|
|
327
|
+
// the full pattern catalogue.
|
|
328
|
+
const priorFileSet = new Set(existingMeta?.fileHashes ? Object.keys(existingMeta.fileHashes) : []);
|
|
329
|
+
const shadowSeed = [];
|
|
330
|
+
for (const added of hashDiff.added) {
|
|
331
|
+
for (const cand of shadowCandidatesFor(added)) {
|
|
332
|
+
if (priorFileSet.has(cand) && !writableFiles.has(cand)) {
|
|
333
|
+
shadowSeed.push(cand);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
{
|
|
338
|
+
let frontier = [...hashDiff.toWrite, ...hashDiff.deleted, ...shadowSeed];
|
|
339
|
+
for (let depth = 0; depth < MAX_IMPORTER_BFS_DEPTH && frontier.length > 0; depth++) {
|
|
340
|
+
const nextFrontier = [];
|
|
341
|
+
for (const f of frontier) {
|
|
342
|
+
try {
|
|
343
|
+
const importers = await queryImporters(f);
|
|
344
|
+
for (const i of importers) {
|
|
345
|
+
if (!writableFiles.has(i)) {
|
|
346
|
+
writableFiles.add(i);
|
|
347
|
+
nextFrontier.push(i);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
/* per-file importer query failure → skip; correctness degrades on
|
|
353
|
+
that branch, but DB stays writable. */
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
frontier = nextFrontier;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const importerExpansion = writableFiles.size - directlyChangedCount;
|
|
360
|
+
if (importerExpansion > 0) {
|
|
361
|
+
log(`Incremental: +${importerExpansion} importer(s) added to writable set ` +
|
|
362
|
+
`(BFS depth ≤ ${MAX_IMPORTER_BFS_DEPTH}` +
|
|
363
|
+
(shadowSeed.length > 0 ? `, ${shadowSeed.length} shadow-seed(s)` : '') +
|
|
364
|
+
`)`);
|
|
365
|
+
}
|
|
366
|
+
// 1. Compute the EFFECTIVE write-set (Finding 1). Two layers,
|
|
367
|
+
// composed:
|
|
368
|
+
// (a) `writableFiles` — toWrite ∪ transitive importers of
|
|
369
|
+
// changed/deleted files (the bounded BFS above, reading
|
|
370
|
+
// IMPORTS from the pre-pipeline DB).
|
|
371
|
+
// (b) `computeEffectiveWriteSet` — walks the NEW graph's
|
|
372
|
+
// edges and pulls in any unchanged-side file that sits
|
|
373
|
+
// on a writable-boundary-crossing edge (catches refined
|
|
374
|
+
// cross-file CALLS edges that the pre-run DB couldn't
|
|
375
|
+
// predict, e.g. a barrel re-export shifting `foo` from
|
|
376
|
+
// B to D).
|
|
377
|
+
// The composed set is the input to BOTH deleteNodesForFile
|
|
378
|
+
// and extractChangedSubgraph — asymmetry between the two would
|
|
379
|
+
// leave stale rows or PK-conflict at COPY time.
|
|
380
|
+
const effectiveWriteSet = computeEffectiveWriteSet(pipelineResult.graph, writableFiles);
|
|
381
|
+
// Deduped: deleted entries may already appear via importer-BFS
|
|
382
|
+
// expansion (queryImporters can return a now-deleted path), which
|
|
383
|
+
// would otherwise call deleteNodesForFile twice for the same file
|
|
384
|
+
// (Bugbot LOW finding on PR #1479).
|
|
385
|
+
const filesToDelete = [...new Set([...effectiveWriteSet, ...hashDiff.deleted])];
|
|
386
|
+
for (let i = 0; i < filesToDelete.length; i++) {
|
|
387
|
+
const f = filesToDelete[i];
|
|
388
|
+
try {
|
|
389
|
+
await deleteNodesForFile(f);
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
/* file may not have rows (e.g. an unparseable file) — fine */
|
|
393
|
+
}
|
|
394
|
+
if (i % 20 === 0) {
|
|
395
|
+
progress('lbug', 62, `Removing rows for changed files (${i}/${filesToDelete.length})...`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// 2. Drop graph-wide nodes (Community, Process). They'll be re-inserted
|
|
399
|
+
// from the fresh pipeline output below. Required for the
|
|
400
|
+
// "Leiden runs on the FULL graph" correctness invariant.
|
|
401
|
+
await deleteAllCommunitiesAndProcesses();
|
|
402
|
+
// 3. Extract the changed subgraph from the FULL ctx.graph and write
|
|
403
|
+
// only that. Unchanged-file rows in the DB stay untouched. Pass
|
|
404
|
+
// the SAME effectiveWriteSet so the subgraph and the deletes
|
|
405
|
+
// cover identical files (asymmetry would silently corrupt).
|
|
406
|
+
const subgraph = extractChangedSubgraph(pipelineResult.graph, effectiveWriteSet);
|
|
407
|
+
await loadGraphToLbug(subgraph, pipelineResult.repoPath, storagePath, (msg) => {
|
|
408
|
+
lbugMsgCount++;
|
|
409
|
+
const pct = Math.min(84, 65 + Math.round((lbugMsgCount / (lbugMsgCount + 10)) * 19));
|
|
410
|
+
progress('lbug', pct, msg);
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
// ── Full rebuild ───────────────────────────────────────────────
|
|
415
|
+
await loadGraphToLbug(pipelineResult.graph, pipelineResult.repoPath, storagePath, (msg) => {
|
|
416
|
+
lbugMsgCount++;
|
|
417
|
+
const pct = Math.min(84, 60 + Math.round((lbugMsgCount / (lbugMsgCount + 10)) * 24));
|
|
418
|
+
progress('lbug', pct, msg);
|
|
419
|
+
});
|
|
420
|
+
}
|
|
173
421
|
// ── Phase 3: FTS (85–90%) ─────────────────────────────────────────
|
|
174
422
|
progress('fts', 85, 'Creating search indexes...');
|
|
175
423
|
await createSearchFTSIndexes();
|
|
176
424
|
progress('fts', 90, 'Search indexes ready');
|
|
177
425
|
// ── Phase 3.5: Re-insert cached embeddings ────────────────────────
|
|
426
|
+
// Runs on BOTH the full-rebuild path and the incremental path:
|
|
427
|
+
// - Full rebuild: DB was wiped, every cached row needs to come back.
|
|
428
|
+
// - Incremental: changed-file rows were just deleted by
|
|
429
|
+
// deleteNodesForFile (which cascades to their
|
|
430
|
+
// embedding rows) — so their cached vectors need
|
|
431
|
+
// to come back too. Unchanged-file rows still
|
|
432
|
+
// exist; re-inserting their cached vectors would
|
|
433
|
+
// PK-conflict, but the per-batch try/catch below
|
|
434
|
+
// silently ignores those (matches the existing
|
|
435
|
+
// "some may fail if node was removed, that's
|
|
436
|
+
// fine" semantics). Bugbot review on PR #1479
|
|
437
|
+
// flagged that gating this on `!isIncremental`
|
|
438
|
+
// silently lost changed-file embeddings.
|
|
178
439
|
if (cachedEmbeddings.length > 0) {
|
|
179
440
|
const cachedDims = cachedEmbeddings[0].embedding.length;
|
|
180
441
|
const { EMBEDDING_DIMS } = await import('./lbug/schema.js');
|
|
@@ -286,6 +547,11 @@ export async function runFullAnalysis(repoPath, options, callbacks) {
|
|
|
286
547
|
const runtimeCapabilities = getRuntimeCapabilities();
|
|
287
548
|
const effectiveSemanticMode = semanticMode ??
|
|
288
549
|
(runtimeCapabilities.semanticMode === 'vector-index' ? 'vector-index' : 'exact-scan');
|
|
550
|
+
// Convert the post-run file-hash map to the on-disk Record<string,string>
|
|
551
|
+
// shape consumed by RepoMeta.fileHashes.
|
|
552
|
+
const newFileHashesRecord = {};
|
|
553
|
+
for (const [k, v] of newFileHashes)
|
|
554
|
+
newFileHashesRecord[k] = v;
|
|
289
555
|
const meta = {
|
|
290
556
|
repoPath,
|
|
291
557
|
lastCommit: currentCommit,
|
|
@@ -315,8 +581,32 @@ export async function runFullAnalysis(repoPath, options, callbacks) {
|
|
|
315
581
|
reason: runtimeCapabilities.reason,
|
|
316
582
|
},
|
|
317
583
|
},
|
|
584
|
+
// Incremental-indexing fields. Populated for git repos so the next
|
|
585
|
+
// analyze run can take the incremental DB-writeback path. Setting
|
|
586
|
+
// incrementalInProgress to undefined explicitly clears any prior
|
|
587
|
+
// dirty flag (full and incremental success paths converge here).
|
|
588
|
+
schemaVersion: hasGitDir(repoPath) ? INCREMENTAL_SCHEMA_VERSION : undefined,
|
|
589
|
+
fileHashes: hasGitDir(repoPath) ? newFileHashesRecord : undefined,
|
|
590
|
+
incrementalInProgress: undefined,
|
|
318
591
|
};
|
|
319
592
|
await saveMeta(storagePath, meta);
|
|
593
|
+
// Persist the incremental parse cache for the next run. Wraps in
|
|
594
|
+
// try/catch so a cache-write failure never breaks an otherwise
|
|
595
|
+
// successful indexing run. Prune stale chunk-hash entries first so
|
|
596
|
+
// the cache file size stays bounded across runs (chunks whose
|
|
597
|
+
// composition no longer matches anything in the current scan are
|
|
598
|
+
// dead weight; the parse phase populates `usedKeys` as it processes
|
|
599
|
+
// chunks).
|
|
600
|
+
try {
|
|
601
|
+
const pruned = pruneCache(parseCache, parseCache.usedKeys);
|
|
602
|
+
if (pruned > 0) {
|
|
603
|
+
log(`Parse cache: pruned ${pruned} stale chunk entries`);
|
|
604
|
+
}
|
|
605
|
+
await saveParseCache(storagePath, parseCache);
|
|
606
|
+
}
|
|
607
|
+
catch (e) {
|
|
608
|
+
log(`Warning: could not save parse cache (${e.message}); continuing.`);
|
|
609
|
+
}
|
|
320
610
|
// Forward the --name alias and the registry-collision bypass bit.
|
|
321
611
|
// `allowDuplicateName` is its own concern — independent from the
|
|
322
612
|
// pipeline `force` above. The CLI maps it from
|
|
@@ -350,7 +640,11 @@ export async function runFullAnalysis(repoPath, options, callbacks) {
|
|
|
350
640
|
communities: pipelineResult.communityResult?.stats.totalCommunities,
|
|
351
641
|
clusters: aggregatedClusterCount,
|
|
352
642
|
processes: pipelineResult.processResult?.stats.totalProcesses,
|
|
353
|
-
}, undefined, {
|
|
643
|
+
}, undefined, {
|
|
644
|
+
skipAgentsMd: options.skipAgentsMd,
|
|
645
|
+
skipSkills: options.skipSkills,
|
|
646
|
+
noStats: options.noStats,
|
|
647
|
+
});
|
|
354
648
|
}
|
|
355
649
|
catch {
|
|
356
650
|
// Best-effort — don't fail the entire analysis for context file issues
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type Parser from 'tree-sitter';
|
|
2
|
+
/**
|
|
3
|
+
* Parse `sourceText` safely on every platform. See {@link SAFE_PARSE_CHUNK_CHARS}
|
|
4
|
+
* for the underlying tree-sitter binding bug this works around.
|
|
5
|
+
*/
|
|
6
|
+
export declare function parseSourceSafe(parser: Parser, sourceText: string, oldTree?: Parser.Tree, options?: Parser.Options): Parser.Tree;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tree-sitter 0.21.x's Node native binding crashes (SIGSEGV) on Windows when
|
|
3
|
+
* `parser.parse(string, …)` is handed a JS string longer than 32 767 chars.
|
|
4
|
+
* The crash happens inside the binding's V8 string-to-buffer conversion and
|
|
5
|
+
* cannot be intercepted from JavaScript. The callback (`Parser.Input`) overload
|
|
6
|
+
* pulls source in fixed-size chunks via repeated callback invocations and
|
|
7
|
+
* bypasses that conversion path entirely.
|
|
8
|
+
*
|
|
9
|
+
* Chunk size is comfortably below the boundary; any value < 32 767 works.
|
|
10
|
+
*/
|
|
11
|
+
const SAFE_PARSE_CHUNK_CHARS = 16 * 1024;
|
|
12
|
+
/**
|
|
13
|
+
* Files at or below this length skip the callback machinery and use the
|
|
14
|
+
* direct string overload — the bug only manifests above the int16 boundary,
|
|
15
|
+
* so small inputs save the cost of N callback invocations per parse.
|
|
16
|
+
*/
|
|
17
|
+
const DIRECT_PARSE_LIMIT_CHARS = 16 * 1024;
|
|
18
|
+
/**
|
|
19
|
+
* Parse `sourceText` safely on every platform. See {@link SAFE_PARSE_CHUNK_CHARS}
|
|
20
|
+
* for the underlying tree-sitter binding bug this works around.
|
|
21
|
+
*/
|
|
22
|
+
export function parseSourceSafe(parser, sourceText, oldTree, options) {
|
|
23
|
+
if (sourceText.length <= DIRECT_PARSE_LIMIT_CHARS) {
|
|
24
|
+
return parser.parse(sourceText, oldTree, options);
|
|
25
|
+
}
|
|
26
|
+
const input = (index) => {
|
|
27
|
+
if (index >= sourceText.length)
|
|
28
|
+
return null;
|
|
29
|
+
return sourceText.slice(index, index + SAFE_PARSE_CHUNK_CHARS);
|
|
30
|
+
};
|
|
31
|
+
return parser.parse(input, oldTree, options);
|
|
32
|
+
}
|
package/dist/server/git-clone.js
CHANGED
|
@@ -10,6 +10,7 @@ import os from 'os';
|
|
|
10
10
|
import fs from 'fs/promises';
|
|
11
11
|
import { isIP } from 'net';
|
|
12
12
|
import { logger } from '../core/logger.js';
|
|
13
|
+
import { parseRepoNameFromUrl } from '../storage/git.js';
|
|
13
14
|
/** Root directory for all cloned repositories. Targets must resolve inside this. */
|
|
14
15
|
const CLONE_ROOT = path.resolve(path.join(os.homedir(), '.gitnexus', 'repos'));
|
|
15
16
|
// A valid git repository name is filesystem-safe: alphanumerics plus `. _ -`.
|
|
@@ -25,19 +26,15 @@ const REPO_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;
|
|
|
25
26
|
* clone root via path traversal.
|
|
26
27
|
*/
|
|
27
28
|
export function extractRepoName(url) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const cleaned = url.slice(0, end);
|
|
35
|
-
const lastSegment = cleaned.split(/[/:]/).pop() || '';
|
|
36
|
-
const stripped = lastSegment.endsWith('.git') ? lastSegment.slice(0, -4) : lastSegment;
|
|
37
|
-
if (!stripped || stripped === '.' || stripped === '..' || !REPO_NAME_PATTERN.test(stripped)) {
|
|
29
|
+
const name = parseRepoNameFromUrl(url);
|
|
30
|
+
if (!name ||
|
|
31
|
+
name === '.' ||
|
|
32
|
+
name === '..' ||
|
|
33
|
+
name === 'unknown' ||
|
|
34
|
+
!REPO_NAME_PATTERN.test(name)) {
|
|
38
35
|
throw new Error('Could not extract a valid repository name from URL');
|
|
39
36
|
}
|
|
40
|
-
return
|
|
37
|
+
return name;
|
|
41
38
|
}
|
|
42
39
|
/** Get the clone target directory for a repo name. */
|
|
43
40
|
export function getCloneDir(repoName) {
|
|
@@ -347,8 +344,8 @@ export async function cloneOrPull(url, targetDir, onProgress) {
|
|
|
347
344
|
throw new Error(`Clone target must be a subdirectory of ${CLONE_ROOT}`);
|
|
348
345
|
}
|
|
349
346
|
// Always validate the requested URL — the prior shape only ran this in
|
|
350
|
-
// the
|
|
351
|
-
//
|
|
347
|
+
// the code path where the repo was cloned. Now it runs unconditionally,
|
|
348
|
+
// preventing SSRF / blocked-host bypasses even when targetDir already exists.
|
|
352
349
|
validateGitUrl(url);
|
|
353
350
|
const exists = await fs.access(path.join(safeTarget, '.git')).then(() => true, () => false);
|
|
354
351
|
if (exists) {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-file content hashing for incremental DB writeback.
|
|
3
|
+
*
|
|
4
|
+
* On every analyze run we compute SHA-256 of every file's content and
|
|
5
|
+
* store the map in meta.json. The next run compares disk against the
|
|
6
|
+
* stored map and produces:
|
|
7
|
+
* - `changed` — content differs (re-emit DB rows for this file)
|
|
8
|
+
* - `added` — file is new on disk (insert DB rows)
|
|
9
|
+
* - `deleted` — file was in last meta but no longer on disk (drop rows)
|
|
10
|
+
*
|
|
11
|
+
* The pipeline still parses every file (correctness invariant: cross-file
|
|
12
|
+
* resolution needs full data). What this enables is a SELECTIVE DB
|
|
13
|
+
* writeback: instead of wipe-and-reload of the whole graph (~50s of CSV
|
|
14
|
+
* COPY on a 25K-node repo), we only delete-and-rewrite rows for the
|
|
15
|
+
* changed/added/deleted set.
|
|
16
|
+
*
|
|
17
|
+
* See docs/superpowers/specs/2026-05-10-incremental-indexing-design.md
|
|
18
|
+
* (Option B revision).
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Compute SHA-256 of a single file. Returns null when the file can't be
|
|
22
|
+
* read — caller treats that as "no signature, assume changed".
|
|
23
|
+
*/
|
|
24
|
+
export declare const computeFileHash: (absPath: string) => Promise<string | null>;
|
|
25
|
+
/**
|
|
26
|
+
* Compute SHA-256 hashes for many files in parallel batches. Files that
|
|
27
|
+
* fail to read are omitted from the result map.
|
|
28
|
+
*/
|
|
29
|
+
export declare const computeFileHashes: (repoPath: string, relPaths: readonly string[]) => Promise<Map<string, string>>;
|
|
30
|
+
/** Result of comparing the current on-disk hashes against stored ones. */
|
|
31
|
+
export interface FileHashDiff {
|
|
32
|
+
/** Files whose content hash differs from stored. */
|
|
33
|
+
changed: string[];
|
|
34
|
+
/** Files in the current scan that weren't in the stored map. */
|
|
35
|
+
added: string[];
|
|
36
|
+
/** Files in the stored map that aren't in the current scan. */
|
|
37
|
+
deleted: string[];
|
|
38
|
+
/** All files whose DB rows must be replaced (changed ∪ added). */
|
|
39
|
+
toWrite: string[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Diff a current hash map against a previously stored one.
|
|
43
|
+
*
|
|
44
|
+
* Sorted output so two runs produce identical diff arrays for the same
|
|
45
|
+
* changes — useful for stable logging / equivalence checks.
|
|
46
|
+
*/
|
|
47
|
+
export declare const diffFileHashes: (current: ReadonlyMap<string, string>, stored: Readonly<Record<string, string>> | undefined) => FileHashDiff;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-file content hashing for incremental DB writeback.
|
|
3
|
+
*
|
|
4
|
+
* On every analyze run we compute SHA-256 of every file's content and
|
|
5
|
+
* store the map in meta.json. The next run compares disk against the
|
|
6
|
+
* stored map and produces:
|
|
7
|
+
* - `changed` — content differs (re-emit DB rows for this file)
|
|
8
|
+
* - `added` — file is new on disk (insert DB rows)
|
|
9
|
+
* - `deleted` — file was in last meta but no longer on disk (drop rows)
|
|
10
|
+
*
|
|
11
|
+
* The pipeline still parses every file (correctness invariant: cross-file
|
|
12
|
+
* resolution needs full data). What this enables is a SELECTIVE DB
|
|
13
|
+
* writeback: instead of wipe-and-reload of the whole graph (~50s of CSV
|
|
14
|
+
* COPY on a 25K-node repo), we only delete-and-rewrite rows for the
|
|
15
|
+
* changed/added/deleted set.
|
|
16
|
+
*
|
|
17
|
+
* See docs/superpowers/specs/2026-05-10-incremental-indexing-design.md
|
|
18
|
+
* (Option B revision).
|
|
19
|
+
*/
|
|
20
|
+
import { createHash } from 'crypto';
|
|
21
|
+
import fs from 'fs/promises';
|
|
22
|
+
import path from 'path';
|
|
23
|
+
/**
|
|
24
|
+
* Compute SHA-256 of a single file. Returns null when the file can't be
|
|
25
|
+
* read — caller treats that as "no signature, assume changed".
|
|
26
|
+
*/
|
|
27
|
+
export const computeFileHash = async (absPath) => {
|
|
28
|
+
try {
|
|
29
|
+
const buf = await fs.readFile(absPath);
|
|
30
|
+
return createHash('sha256').update(buf).digest('hex');
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Compute SHA-256 hashes for many files in parallel batches. Files that
|
|
38
|
+
* fail to read are omitted from the result map.
|
|
39
|
+
*/
|
|
40
|
+
export const computeFileHashes = async (repoPath, relPaths) => {
|
|
41
|
+
const out = new Map();
|
|
42
|
+
const BATCH = 100;
|
|
43
|
+
for (let i = 0; i < relPaths.length; i += BATCH) {
|
|
44
|
+
const batch = relPaths.slice(i, i + BATCH);
|
|
45
|
+
const results = await Promise.all(batch.map(async (rel) => {
|
|
46
|
+
const h = await computeFileHash(path.join(repoPath, rel));
|
|
47
|
+
return h ? [rel, h] : null;
|
|
48
|
+
}));
|
|
49
|
+
for (const r of results)
|
|
50
|
+
if (r)
|
|
51
|
+
out.set(r[0], r[1]);
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Diff a current hash map against a previously stored one.
|
|
57
|
+
*
|
|
58
|
+
* Sorted output so two runs produce identical diff arrays for the same
|
|
59
|
+
* changes — useful for stable logging / equivalence checks.
|
|
60
|
+
*/
|
|
61
|
+
export const diffFileHashes = (current, stored) => {
|
|
62
|
+
const storedMap = new Map(stored ? Object.entries(stored) : []);
|
|
63
|
+
const changed = [];
|
|
64
|
+
const added = [];
|
|
65
|
+
for (const [p, h] of current) {
|
|
66
|
+
const prev = storedMap.get(p);
|
|
67
|
+
if (prev === undefined)
|
|
68
|
+
added.push(p);
|
|
69
|
+
else if (prev !== h)
|
|
70
|
+
changed.push(p);
|
|
71
|
+
}
|
|
72
|
+
const deleted = [];
|
|
73
|
+
for (const p of storedMap.keys()) {
|
|
74
|
+
if (!current.has(p))
|
|
75
|
+
deleted.push(p);
|
|
76
|
+
}
|
|
77
|
+
changed.sort();
|
|
78
|
+
added.sort();
|
|
79
|
+
deleted.sort();
|
|
80
|
+
return {
|
|
81
|
+
changed,
|
|
82
|
+
added,
|
|
83
|
+
deleted,
|
|
84
|
+
toWrite: [...changed, ...added].sort(),
|
|
85
|
+
};
|
|
86
|
+
};
|