@plebeiantech/lat.md 0.12.3-fork.2 → 0.12.3-fork.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 +31 -2
- package/dist/src/cli/agent-invocation.d.ts +6 -0
- package/dist/src/cli/agent-invocation.js +23 -0
- package/dist/src/cli/checklist-menu.d.ts +2 -1
- package/dist/src/cli/checklist-menu.js +3 -2
- package/dist/src/cli/comment-guard.d.ts +14 -0
- package/dist/src/cli/comment-guard.js +10 -33
- package/dist/src/cli/comment-reminder.d.ts +65 -6
- package/dist/src/cli/comment-reminder.js +164 -28
- package/dist/src/cli/fork-instructions.d.ts +20 -0
- package/dist/src/cli/fork-instructions.js +184 -0
- package/dist/src/cli/fork-scaffold.d.ts +106 -0
- package/dist/src/cli/fork-scaffold.js +490 -0
- package/dist/src/cli/graph.d.ts +1 -1
- package/dist/src/cli/hook.d.ts +26 -0
- package/dist/src/cli/hook.js +115 -64
- package/dist/src/cli/index.js +166 -64
- package/dist/src/cli/init-preferences.d.ts +4 -0
- package/dist/src/cli/init-preferences.js +36 -0
- package/dist/src/cli/init.d.ts +2 -1
- package/dist/src/cli/init.js +107 -50
- package/dist/src/cli/reindex.d.ts +1 -1
- package/dist/src/cli/reindex.js +7 -10
- package/dist/src/cli/search.d.ts +14 -4
- package/dist/src/cli/search.js +116 -100
- package/dist/src/cli/ui-build-server.d.ts +12 -0
- package/dist/src/cli/ui-build-server.js +26 -0
- package/dist/src/cli/ui-build.d.ts +4 -0
- package/dist/src/cli/ui-build.js +13 -0
- package/dist/src/cli/ui.d.ts +1 -1
- package/dist/src/cli/ui.js +18 -2
- package/dist/src/fork/antigravity-hook.d.ts +47 -0
- package/dist/src/fork/antigravity-hook.js +424 -0
- package/dist/src/fork/antigravity-init.d.ts +17 -0
- package/dist/src/fork/antigravity-init.js +124 -0
- package/dist/src/fork/upstream-drift-cli.js +2 -0
- package/dist/src/fork/upstream-drift.d.ts +26 -0
- package/dist/src/fork/upstream-drift.js +185 -0
- package/dist/src/fork/upstream-guard-cli.d.ts +1 -0
- package/dist/src/fork/upstream-guard-cli.js +2 -0
- package/dist/src/fork/upstream-guard.d.ts +72 -0
- package/dist/src/fork/upstream-guard.js +445 -0
- package/dist/src/graph-export.d.ts +2 -0
- package/dist/src/graph-export.js +38 -19
- package/dist/src/knowledge/claude-memory.d.ts +1 -0
- package/dist/src/knowledge/claude-memory.js +33 -4
- package/dist/src/knowledge/index.js +2 -2
- package/dist/src/mcp/server.js +34 -18
- package/dist/src/search/cache.d.ts +3 -0
- package/dist/src/search/cache.js +104 -0
- package/dist/src/search/chunks.d.ts +23 -0
- package/dist/src/search/chunks.js +200 -0
- package/dist/src/search/db.d.ts +28 -16
- package/dist/src/search/db.js +106 -40
- package/dist/src/search/embedder.d.ts +2 -1
- package/dist/src/search/embedder.js +7 -6
- package/dist/src/search/index.d.ts +6 -2
- package/dist/src/search/index.js +176 -53
- package/dist/src/search/lexical.d.ts +6 -0
- package/dist/src/search/lexical.js +36 -0
- package/dist/src/search/lock.d.ts +5 -0
- package/dist/src/search/lock.js +45 -0
- package/dist/src/search/query.d.ts +26 -0
- package/dist/src/search/query.js +103 -0
- package/dist/src/search/search.d.ts +17 -7
- package/dist/src/search/search.js +164 -14
- package/dist/src/search/types.d.ts +15 -0
- package/dist/src/search/types.js +1 -0
- package/dist/src/view/client/assets/GraphView-0bPe_Q5I.js +456 -0
- package/dist/src/view/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/src/view/client/assets/MarkdownEditor-1_8mI7lO.js +28 -0
- package/dist/src/view/client/assets/OrbitControls-DwcpQNja.js +1 -0
- package/dist/src/view/client/assets/STLLoader-CLWTKt5i.js +1 -0
- package/dist/src/view/client/assets/_commonjsHelpers-CqkleIqs.js +1 -0
- package/dist/src/view/client/assets/abnfDiagram-VCTEODGH-BLIuikwY.js +1 -0
- package/dist/src/view/client/assets/arc-D4PzdKSA.js +1 -0
- package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-35Egww62.js +36 -0
- package/dist/src/view/client/assets/blockDiagram-I7D4REHJ-tqNy53-W.js +129 -0
- package/dist/src/view/client/assets/c4Diagram-7LVT6UL2-DZ_GcD9K.js +38 -0
- package/dist/src/view/client/assets/channel-DensupHE.js +1 -0
- package/dist/src/view/client/assets/chunk-2Q5K7J3B-BUjq4Q7b.js +1 -0
- package/dist/src/view/client/assets/chunk-5VM5RSS4-BsIix6I5.js +15 -0
- package/dist/src/view/client/assets/chunk-F27PBJKO-Dxo7UheG.js +1 -0
- package/dist/src/view/client/assets/chunk-IMKFNOWR-C5lzUDK5.js +231 -0
- package/dist/src/view/client/assets/chunk-JWPE2WC7-qCHQlKfY.js +1 -0
- package/dist/src/view/client/assets/chunk-POPQ4Y6H-CDPKw21I.js +1 -0
- package/dist/src/view/client/assets/chunk-SVP7TREG-CD_C-9M-.js +88 -0
- package/dist/src/view/client/assets/chunk-TICWLB2K-ZMatRvad.js +206 -0
- package/dist/src/view/client/assets/chunk-XXDRQBXY-dDs4quwg.js +1 -0
- package/dist/src/view/client/assets/classDiagram-ZZMXUADV-CV7ArHLW.js +1 -0
- package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-CV7ArHLW.js +1 -0
- package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-CwH3-T1N.js +1 -0
- package/dist/src/view/client/assets/cynefin-OW5HDTMX-DodS6r00.js +166 -0
- package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-D7WDoy0K.js +62 -0
- package/dist/src/view/client/assets/cytoscape.esm-DzSz-X2X.js +321 -0
- package/dist/src/view/client/assets/dagre-GXQ25YYZ-yc2GTi5h.js +4 -0
- package/dist/src/view/client/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/src/view/client/assets/diagram-S7CK7UJ4-CEP0FGL8.js +30 -0
- package/dist/src/view/client/assets/diagram-UQ7AKVKN-BZIEojBf.js +41 -0
- package/dist/src/view/client/assets/diagram-VSXAHHWV-BSB6COmM.js +3 -0
- package/dist/src/view/client/assets/diagram-VX7I27RA-DqfR3psV.js +24 -0
- package/dist/src/view/client/assets/diagram-Z3DM3KII-B79t0s5P.js +24 -0
- package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-CqbCQhE1.js +1 -0
- package/dist/src/view/client/assets/erDiagram-RLTQ6QDP-BVfoYIhJ.js +99 -0
- package/dist/src/view/client/assets/flowDiagram-HODETNUW-Dfyjm6ib.js +168 -0
- package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-Da9ta_k3.js +292 -0
- package/dist/src/view/client/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/src/view/client/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/src/view/client/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/src/view/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/src/view/client/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/src/view/client/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/src/view/client/assets/gitGraphDiagram-WWUBYQGX-BDqlQ6LG.js +106 -0
- package/dist/src/view/client/assets/index-BnENo9bV.js +1 -0
- package/dist/src/view/client/assets/index-DG0Ez15D.css +1 -0
- package/dist/src/view/client/assets/index-heNSjszK.js +13 -0
- package/dist/src/view/client/assets/infoDiagram-27XIBGKW-CozIUYm1.js +2 -0
- package/dist/src/view/client/assets/init-Gi6I4Gst.js +1 -0
- package/dist/src/view/client/assets/ishikawaDiagram-5VMMS53U-tkBLr-Tn.js +70 -0
- package/dist/src/view/client/assets/journeyDiagram-3NMN7TZE-Dcd_Ofwc.js +139 -0
- package/dist/src/view/client/assets/kanban-definition-UXKFOSKX-CQvZmdAf.js +89 -0
- package/dist/src/view/client/assets/katex-HP8lGamR.js +257 -0
- package/dist/src/view/client/assets/layout-BxuGL0Ip.js +1 -0
- package/dist/src/view/client/assets/linear-D8tR6G5K.js +1 -0
- package/dist/src/view/client/assets/logo-RR_KBOAT.svg +149 -0
- package/dist/src/view/client/assets/maplibre-gl-CDX6Zuow.js +800 -0
- package/dist/src/view/client/assets/mermaid.core-BlHZooLK.js +310 -0
- package/dist/src/view/client/assets/mindmap-definition-YA3MSWOX-BqvVz5lC.js +96 -0
- package/dist/src/view/client/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/src/view/client/assets/pegDiagram-XKGWAZYB-BkbOOTxX.js +1 -0
- package/dist/src/view/client/assets/pieDiagram-E7YTZNPT-BToTpG0m.js +39 -0
- package/dist/src/view/client/assets/quadrantDiagram-AXDQQJYC-CerD2TfT.js +7 -0
- package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-B2oElPYD.js +1 -0
- package/dist/src/view/client/assets/requirementDiagram-BXWQKSXE-BCL8RGFd.js +84 -0
- package/dist/src/view/client/assets/sankeyDiagram-P5KCCOFB-BqnkNRPm.js +40 -0
- package/dist/src/view/client/assets/sequenceDiagram-WJ2MYXX4-DKSKYknp.js +162 -0
- package/dist/src/view/client/assets/sizeCapture-INFHLROL-D8reQ7IM.js +1 -0
- package/dist/src/view/client/assets/stateDiagram-D77RDMKH-B-Eme-BN.js +1 -0
- package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-yXL-Y-Ym.js +1 -0
- package/dist/src/view/client/assets/swimlanes-42K2YHIH-lK0158X4.js +1 -0
- package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-CGn5WOGw.js +8 -0
- package/dist/src/view/client/assets/three.module-PDSP0dbZ.js +4108 -0
- package/dist/src/view/client/assets/timeline-definition-24CTP7MA-imh-edhJ.js +120 -0
- package/dist/src/view/client/assets/vennDiagram-4TSXK5OY-Ixwhcd6r.js +34 -0
- package/dist/src/view/client/assets/wardleyDiagram-VM6X3IG4-Cvr_zzKI.js +78 -0
- package/dist/src/view/client/assets/xychartDiagram-S5SC5T6Z-BvcmHKlQ.js +7 -0
- package/dist/src/view/client/index.html +2 -2
- package/dist/src/view/client-shell.d.ts +2 -0
- package/dist/src/view/client-shell.js +5 -0
- package/dist/src/view/diagnostics.d.ts +3 -2
- package/dist/src/view/diagnostics.js +49 -58
- package/dist/src/view/document-edit.d.ts +8 -0
- package/dist/src/view/document-edit.js +25 -0
- package/dist/src/view/document-route.d.ts +14 -0
- package/dist/src/view/document-route.js +126 -0
- package/dist/src/view/document-tree.d.ts +18 -0
- package/dist/src/view/document-tree.js +139 -0
- package/dist/src/view/external-document-tree.d.ts +3 -0
- package/dist/src/view/external-document-tree.js +674 -0
- package/dist/src/view/git-diff.js +205 -1
- package/dist/src/view/git.js +1 -1
- package/dist/src/view/graph.d.ts +7 -0
- package/dist/src/view/graph.js +267 -0
- package/dist/src/view/highlight.d.ts +10 -2
- package/dist/src/view/highlight.js +50648 -3166
- package/dist/src/view/markdown.d.ts +8 -3
- package/dist/src/view/markdown.js +311 -41
- package/dist/src/view/preindexed-search.d.ts +15 -0
- package/dist/src/view/preindexed-search.js +25 -0
- package/dist/src/view/protocol.d.ts +95 -6
- package/dist/src/view/protocol.js +1 -1
- package/dist/src/view/publication.d.ts +2 -0
- package/dist/src/view/publication.js +78 -0
- package/dist/src/view/references.d.ts +13 -26
- package/dist/src/view/references.js +150 -119
- package/dist/src/view/repository.d.ts +18 -5
- package/dist/src/view/repository.js +269 -27
- package/dist/src/view/request-security.d.ts +5 -0
- package/dist/src/view/request-security.js +46 -0
- package/dist/src/view/search-result.d.ts +4 -0
- package/dist/src/view/search-result.js +25 -0
- package/dist/src/view/search.d.ts +7 -2
- package/dist/src/view/search.js +23 -33
- package/dist/src/view/server-build.d.ts +21 -0
- package/dist/src/view/server-build.js +145 -0
- package/dist/src/view/server-deployment.d.ts +31 -0
- package/dist/src/view/server-deployment.js +94 -0
- package/dist/src/view/server-index-worker.d.ts +6 -0
- package/dist/src/view/server-index-worker.js +14 -0
- package/dist/src/view/server.d.ts +14 -2
- package/dist/src/view/server.js +254 -51
- package/dist/src/view/source-target.d.ts +10 -0
- package/dist/src/view/source-target.js +43 -0
- package/dist/src/view/static-build.d.ts +32 -0
- package/dist/src/view/static-build.js +741 -0
- package/dist/src/view/static-protocol.d.ts +47 -0
- package/dist/src/view/static-protocol.js +15 -0
- package/dist/src/view/store.d.ts +26 -3
- package/dist/src/view/store.js +325 -44
- package/dist/src/view/table-of-contents.d.ts +9 -0
- package/dist/src/view/table-of-contents.js +83 -0
- package/dist/src/view/vercel-build.d.ts +24 -0
- package/dist/src/view/vercel-build.js +140 -0
- package/dist/src/view/vercel-server-build.d.ts +10 -0
- package/dist/src/view/vercel-server-build.js +61 -0
- package/dist/vendor/lat.md-server/cli.d.ts +2 -0
- package/dist/vendor/lat.md-server/cli.js +12 -0
- package/dist/vendor/lat.md-server/index.d.ts +38 -0
- package/dist/vendor/lat.md-server/index.js +128 -0
- package/package.json +72 -25
- package/plugins/lat-md/README.md +61 -0
- package/plugins/lat-md/hooks.json +38 -0
- package/plugins/lat-md/plugin.json +9 -0
- package/plugins/lat-md/rules/AGENTS.md +118 -0
- package/plugins/lat-md/skills/lat-md/SKILL.md +178 -0
- package/plugins/lat-md/skills/lat-md-conventions/SKILL.md +182 -0
- package/templates/AGENTS.md +19 -20
- package/templates/cursor-rules.md +14 -7
- package/templates/fork/conventions.md +173 -0
- package/templates/opencode-plugin.ts +9 -9
- package/templates/pi-extension.ts +11 -11
- package/templates/skill/SKILL.md +28 -17
- package/dist/src/cli/check-frontmatter.d.ts +0 -12
- package/dist/src/cli/check-frontmatter.js +0 -44
- package/dist/src/cli/check-mode.d.ts +0 -6
- package/dist/src/cli/check-mode.js +0 -303
- package/dist/src/cli/check-status.d.ts +0 -74
- package/dist/src/cli/check-status.js +0 -217
- package/dist/src/cli/check.d.ts +0 -45
- package/dist/src/cli/check.js +0 -777
- package/dist/src/cli/context.d.ts +0 -11
- package/dist/src/cli/context.js +0 -58
- package/dist/src/cli/expand.d.ts +0 -8
- package/dist/src/cli/expand.js +0 -94
- package/dist/src/cli/gen-index.d.ts +0 -45
- package/dist/src/cli/gen-index.js +0 -235
- package/dist/src/cli/link-scheme.d.ts +0 -54
- package/dist/src/cli/link-scheme.js +0 -101
- package/dist/src/cli/locate.d.ts +0 -2
- package/dist/src/cli/locate.js +0 -17
- package/dist/src/cli/refs.d.ts +0 -22
- package/dist/src/cli/refs.js +0 -229
- package/dist/src/cli/section.d.ts +0 -43
- package/dist/src/cli/section.js +0 -252
- package/dist/src/cli/select-menu.d.ts +0 -13
- package/dist/src/cli/select-menu.js +0 -100
- package/dist/src/code-refs.d.ts +0 -17
- package/dist/src/code-refs.js +0 -277
- package/dist/src/config.d.ts +0 -29
- package/dist/src/config.js +0 -83
- package/dist/src/context.d.ts +0 -23
- package/dist/src/context.js +0 -11
- package/dist/src/extensions/wiki-link/from-markdown.d.ts +0 -5
- package/dist/src/extensions/wiki-link/from-markdown.js +0 -34
- package/dist/src/extensions/wiki-link/index.d.ts +0 -4
- package/dist/src/extensions/wiki-link/index.js +0 -3
- package/dist/src/extensions/wiki-link/syntax.d.ts +0 -14
- package/dist/src/extensions/wiki-link/syntax.js +0 -121
- package/dist/src/extensions/wiki-link/to-markdown.d.ts +0 -5
- package/dist/src/extensions/wiki-link/to-markdown.js +0 -28
- package/dist/src/extensions/wiki-link/types.d.ts +0 -34
- package/dist/src/format.d.ts +0 -8
- package/dist/src/format.js +0 -43
- package/dist/src/init-version.d.ts +0 -10
- package/dist/src/init-version.js +0 -49
- package/dist/src/lattice.d.ts +0 -108
- package/dist/src/lattice.js +0 -816
- package/dist/src/parser.d.ts +0 -3
- package/dist/src/parser.js +0 -18
- package/dist/src/source-parser.d.ts +0 -27
- package/dist/src/source-parser.js +0 -916
- package/dist/src/untrusted.d.ts +0 -24
- package/dist/src/untrusted.js +0 -97
- package/dist/src/view/client/assets/index-B0n0u03y.css +0 -1
- package/dist/src/view/client/assets/index-xm2_px4q.js +0 -23
- package/dist/src/walk.d.ts +0 -18
- package/dist/src/walk.js +0 -27
- /package/dist/src/{extensions/wiki-link/types.js → fork/upstream-drift-cli.d.ts} +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createEmbedder, EmbeddingAuthError, } from '@lat.md/embed';
|
|
2
2
|
import minilm from '@lat.md/embed-minilm-fp16';
|
|
3
|
-
import { getLlmKey, getRepoEmbedding } from '
|
|
3
|
+
import { getLlmKey, getRepoEmbedding } from '@plebeiantech/lat.md-core/config';
|
|
4
4
|
export { EmbeddingAuthError };
|
|
5
|
+
const defaultCreateSearchEngine = (key) => key ? createEmbedder({ key }) : createEmbedder({ model: minilm });
|
|
5
6
|
/** `meta.embedding_model` value for an embedder, e.g. `local:minilm-l6-v2:384`. */
|
|
6
7
|
export function modelKey(embedder) {
|
|
7
8
|
return `${embedder.name}:${embedder.dimensions}`;
|
|
@@ -33,15 +34,15 @@ export function localEmbedder() {
|
|
|
33
34
|
* regenerable `.cache` was wiped) the durable per-repo preference wins over the
|
|
34
35
|
* env, so a repo switched to local stays local across cache loss.
|
|
35
36
|
*/
|
|
36
|
-
export async function embedderForIndex(storedModel, latDir) {
|
|
37
|
+
export async function embedderForIndex(storedModel, latDir, createSearchEngine = defaultCreateSearchEngine) {
|
|
37
38
|
if (storedModel === null) {
|
|
38
39
|
if (getRepoEmbedding(latDir) === 'local')
|
|
39
|
-
return
|
|
40
|
+
return createSearchEngine();
|
|
40
41
|
// No durable preference — decide from the environment, record it later.
|
|
41
|
-
return
|
|
42
|
+
return createSearchEngine(getLlmKey());
|
|
42
43
|
}
|
|
43
44
|
if (storedModel.startsWith('local:')) {
|
|
44
|
-
return
|
|
45
|
+
return createSearchEngine(); // ignores LAT_LLM_KEY entirely
|
|
45
46
|
}
|
|
46
47
|
// Remote-pinned index: needs a key that resolves to the same model.
|
|
47
48
|
const key = getLlmKey();
|
|
@@ -49,7 +50,7 @@ export async function embedderForIndex(storedModel, latDir) {
|
|
|
49
50
|
throw new ReindexRequiredError(`This index was built with '${storedModel}', but no LAT_LLM_KEY is set. ` +
|
|
50
51
|
`Run 'lat reindex' to switch to the local model or restore the key.`);
|
|
51
52
|
}
|
|
52
|
-
const embedder = await
|
|
53
|
+
const embedder = await createSearchEngine(key);
|
|
53
54
|
if (modelKey(embedder) !== storedModel) {
|
|
54
55
|
throw new ReindexRequiredError(`This index was built with '${storedModel}', but the current key resolves ` +
|
|
55
56
|
`to '${modelKey(embedder)}'. Run 'lat reindex' to rebuild.`);
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SearchDb } from './db.js';
|
|
2
|
+
import { type MarkdownProjectAnalysis } from '@plebeiantech/lat.md-core/project-analysis';
|
|
2
3
|
import type { Embedder } from './embedder.js';
|
|
4
|
+
export declare function projectFingerprint(project: MarkdownProjectAnalysis): string;
|
|
3
5
|
export type IndexStats = {
|
|
4
6
|
added: number;
|
|
5
7
|
updated: number;
|
|
6
8
|
removed: number;
|
|
7
9
|
unchanged: number;
|
|
8
10
|
};
|
|
9
|
-
|
|
11
|
+
/** Exact identifier tokens complement FTS's punctuation and long-token handling. */
|
|
12
|
+
export declare function identifierTokens(text: string): string[];
|
|
13
|
+
export declare function indexSections(latDir: string, db: SearchDb, embedder: Embedder, onProgress?: (done: number, total: number) => void, analyzedProject?: MarkdownProjectAnalysis): Promise<IndexStats>;
|
package/dist/src/search/index.js
CHANGED
|
@@ -1,66 +1,189 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { dirname
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { PARSER_CACHE_VERSION } from '@plebeiantech/lat.md-core/parser-cache';
|
|
2
|
+
import { synchronizeLexical } from './lexical.js';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
4
|
+
import { CREATE_PASSAGE_FTS } from './db.js';
|
|
5
|
+
import { analyzeMarkdownProject, } from '@plebeiantech/lat.md-core/project-analysis';
|
|
6
|
+
import { chunkFile, digest, embeddingFingerprint } from './chunks.js';
|
|
7
|
+
export function projectFingerprint(project) {
|
|
8
|
+
return digest(`parser:${PARSER_CACHE_VERSION}\n` +
|
|
9
|
+
[...project.files.values()]
|
|
10
|
+
.map((f) => `${f.projectPath}\0${digest(f.content)}`)
|
|
11
|
+
.sort()
|
|
12
|
+
.join('\n'));
|
|
7
13
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
/** Exact identifier tokens complement FTS's punctuation and long-token handling. */
|
|
15
|
+
export function identifierTokens(text) {
|
|
16
|
+
return [
|
|
17
|
+
...new Set((text.match(/[\p{L}\p{N}_][\p{L}\p{N}_./:#@-]*/gu) ?? [])
|
|
18
|
+
.map((t) => t.toLowerCase())
|
|
19
|
+
.filter((t) => /[_./:#@-]/.test(t) || t.length >= 40)),
|
|
20
|
+
];
|
|
13
21
|
}
|
|
14
|
-
export async function indexSections(latDir, db, embedder, onProgress) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
export async function indexSections(latDir, db, embedder, onProgress, analyzedProject) {
|
|
23
|
+
const project = analyzedProject ??
|
|
24
|
+
(await analyzeMarkdownProject(latDir, dirname(latDir), {
|
|
25
|
+
executor: 'auto',
|
|
26
|
+
}));
|
|
27
|
+
const fingerprint = embeddingFingerprint(embedder);
|
|
28
|
+
const projectHash = projectFingerprint(project);
|
|
29
|
+
const oldFingerprint = (await db.execute("SELECT value FROM meta WHERE key='fingerprint'")).rows[0]?.value;
|
|
30
|
+
if (oldFingerprint && oldFingerprint !== fingerprint)
|
|
31
|
+
throw new Error('Search chunking or embedding model changed; run lat reindex.');
|
|
32
|
+
const byFile = new Map();
|
|
33
|
+
for (const s of project.sections) {
|
|
34
|
+
const list = byFile.get(s.filePath) ?? [];
|
|
35
|
+
list.push(s);
|
|
36
|
+
byFile.set(s.filePath, list);
|
|
23
37
|
}
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
38
|
+
const files = new Map([...project.files.values()].map((f) => [f.projectPath, f]));
|
|
39
|
+
const passages = [...byFile].flatMap(([path, sections]) => {
|
|
40
|
+
const file = files.get(path);
|
|
41
|
+
if (!file)
|
|
42
|
+
throw new Error(`Missing analyzed file: ${path}`);
|
|
43
|
+
return chunkFile(file, sections, embedder);
|
|
44
|
+
});
|
|
45
|
+
const existing = new Map((await db.execute('SELECT id,content_hash FROM sections')).rows.map((r) => [
|
|
46
|
+
r.id,
|
|
47
|
+
r.content_hash,
|
|
48
|
+
]));
|
|
49
|
+
const sectionHashes = new Map();
|
|
50
|
+
const owned = new Map();
|
|
51
|
+
for (const p of passages) {
|
|
52
|
+
const list = owned.get(p.sectionId) ?? [];
|
|
53
|
+
list.push(p);
|
|
54
|
+
owned.set(p.sectionId, list);
|
|
29
55
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
56
|
+
for (const s of project.sections) {
|
|
57
|
+
sectionHashes.set(s.id, digest(JSON.stringify([
|
|
58
|
+
{ ...s, children: undefined },
|
|
59
|
+
(owned.get(s.id) ?? []).map((p) => [p.inputHash, p.spans]),
|
|
60
|
+
])));
|
|
61
|
+
}
|
|
62
|
+
const changed = new Set([...sectionHashes]
|
|
63
|
+
.filter(([id, hash]) => existing.get(id) !== hash)
|
|
64
|
+
.map(([id]) => id));
|
|
65
|
+
const removed = [...existing.keys()].filter((id) => !sectionHashes.has(id));
|
|
66
|
+
const stats = {
|
|
67
|
+
added: [...changed].filter((id) => !existing.has(id)).length,
|
|
68
|
+
updated: [...changed].filter((id) => existing.has(id)).length,
|
|
69
|
+
removed: removed.length,
|
|
70
|
+
unchanged: project.sections.length - changed.size,
|
|
71
|
+
};
|
|
72
|
+
if (!changed.size && !removed.length && oldFingerprint === fingerprint) {
|
|
73
|
+
// Lexical maintenance can repair old generations without embedding work.
|
|
74
|
+
await db.execute('BEGIN');
|
|
75
|
+
try {
|
|
76
|
+
await synchronizeLexical(db);
|
|
77
|
+
await db.execute({
|
|
78
|
+
sql: "UPDATE meta SET value=? WHERE key='project_hash' AND value<>?",
|
|
79
|
+
args: [projectHash, projectHash],
|
|
80
|
+
});
|
|
81
|
+
await db.execute('COMMIT');
|
|
37
82
|
}
|
|
38
|
-
|
|
39
|
-
|
|
83
|
+
catch (error) {
|
|
84
|
+
await db.execute('ROLLBACK');
|
|
85
|
+
throw error;
|
|
40
86
|
}
|
|
87
|
+
return stats;
|
|
41
88
|
}
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
89
|
+
const storedHashes = new Set((await db.execute('SELECT hash FROM embeddings')).rows.map((r) => r.hash));
|
|
90
|
+
const missing = new Map(passages
|
|
91
|
+
.filter((p) => !storedHashes.has(p.inputHash))
|
|
92
|
+
.map((p) => [p.inputHash, p.input]));
|
|
93
|
+
const entries = [...missing];
|
|
94
|
+
const vectors = entries.length
|
|
95
|
+
? await embedder.embed(entries.map(([, input]) => input), onProgress)
|
|
96
|
+
: [];
|
|
97
|
+
if (vectors.length !== entries.length ||
|
|
98
|
+
vectors.some((v) => v.length !== embedder.dimensions || v.some((n) => !Number.isFinite(n))))
|
|
99
|
+
throw new Error('Embedding backend returned invalid vectors');
|
|
100
|
+
const rebuildFts = !existing.size ||
|
|
101
|
+
// Tantivy retains deleted versions in BM25 statistics until rebuilt.
|
|
102
|
+
stats.updated > 0 ||
|
|
103
|
+
removed.length > 0 ||
|
|
104
|
+
passages.filter((p) => changed.has(p.sectionId)).length > 512;
|
|
105
|
+
await db.execute('BEGIN');
|
|
106
|
+
try {
|
|
107
|
+
if (rebuildFts)
|
|
108
|
+
await db.execute('DROP INDEX IF EXISTS chunks_fts');
|
|
109
|
+
for (let i = 0; i < entries.length; i++)
|
|
110
|
+
await db.execute({
|
|
111
|
+
sql: 'INSERT INTO embeddings VALUES (?,vector32(?))',
|
|
112
|
+
args: [entries[i][0], JSON.stringify(vectors[i])],
|
|
113
|
+
});
|
|
114
|
+
for (const id of [...changed, ...removed]) {
|
|
115
|
+
await db.execute({
|
|
116
|
+
sql: 'DELETE FROM lexical_chunks WHERE id IN (SELECT id FROM chunks WHERE section_id=?)',
|
|
117
|
+
args: [id],
|
|
118
|
+
});
|
|
119
|
+
await db.execute({
|
|
120
|
+
sql: 'DELETE FROM identifiers WHERE chunk_id IN (SELECT id FROM chunks WHERE section_id=?)',
|
|
121
|
+
args: [id],
|
|
122
|
+
});
|
|
52
123
|
await db.execute({
|
|
53
|
-
sql:
|
|
54
|
-
|
|
55
|
-
args: [id, section.file, section.heading, content, hash, vecJson, now],
|
|
124
|
+
sql: 'DELETE FROM chunks WHERE section_id=?',
|
|
125
|
+
args: [id],
|
|
56
126
|
});
|
|
127
|
+
await db.execute({ sql: 'DELETE FROM sections WHERE id=?', args: [id] });
|
|
57
128
|
}
|
|
129
|
+
const parents = new Map();
|
|
130
|
+
for (const s of project.sections)
|
|
131
|
+
for (const child of s.children)
|
|
132
|
+
parents.set(child.id, s.id);
|
|
133
|
+
for (const s of project.sections)
|
|
134
|
+
if (changed.has(s.id))
|
|
135
|
+
await db.execute({
|
|
136
|
+
sql: 'INSERT INTO sections VALUES (?,?,?,?,?,?,?,?)',
|
|
137
|
+
args: [
|
|
138
|
+
s.id,
|
|
139
|
+
s.file,
|
|
140
|
+
s.heading,
|
|
141
|
+
s.firstParagraph,
|
|
142
|
+
sectionHashes.get(s.id),
|
|
143
|
+
parents.get(s.id) ?? null,
|
|
144
|
+
s.startLine,
|
|
145
|
+
s.endLine,
|
|
146
|
+
],
|
|
147
|
+
});
|
|
148
|
+
for (const p of passages)
|
|
149
|
+
if (changed.has(p.sectionId)) {
|
|
150
|
+
const row = (await db.execute({
|
|
151
|
+
sql: 'INSERT INTO chunks(source_id,section_id,ordinal,type,spans,body,heading,path,input_hash) VALUES (?,?,?,?,?,?,?,?,?) RETURNING id',
|
|
152
|
+
args: [
|
|
153
|
+
p.id,
|
|
154
|
+
p.sectionId,
|
|
155
|
+
p.ordinal,
|
|
156
|
+
p.type,
|
|
157
|
+
JSON.stringify(p.spans),
|
|
158
|
+
p.text,
|
|
159
|
+
p.heading,
|
|
160
|
+
p.path,
|
|
161
|
+
p.inputHash,
|
|
162
|
+
],
|
|
163
|
+
})).rows[0];
|
|
164
|
+
for (const token of identifierTokens(`${p.sectionId}\n${project.sectionById.get(p.sectionId.toLowerCase())?.filePath ?? ''}\n${p.heading}\n${p.path}\n${p.text}`))
|
|
165
|
+
await db.execute({
|
|
166
|
+
sql: 'INSERT INTO identifiers VALUES (?,?)',
|
|
167
|
+
args: [token, row.id],
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
await db.execute('DELETE FROM embeddings WHERE hash NOT IN (SELECT input_hash FROM chunks)');
|
|
171
|
+
await db.execute({
|
|
172
|
+
sql: 'INSERT OR REPLACE INTO meta VALUES (?,?)',
|
|
173
|
+
args: ['fingerprint', fingerprint],
|
|
174
|
+
});
|
|
175
|
+
await db.execute({
|
|
176
|
+
sql: 'INSERT OR REPLACE INTO meta VALUES (?,?)',
|
|
177
|
+
args: ['project_hash', projectHash],
|
|
178
|
+
});
|
|
179
|
+
await synchronizeLexical(db);
|
|
180
|
+
if (rebuildFts)
|
|
181
|
+
await db.execute(CREATE_PASSAGE_FTS);
|
|
182
|
+
await db.execute('COMMIT');
|
|
58
183
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
184
|
+
catch (error) {
|
|
185
|
+
await db.execute('ROLLBACK');
|
|
186
|
+
throw error;
|
|
62
187
|
}
|
|
63
|
-
|
|
64
|
-
const updated = toEmbed.filter((e) => existing.has(e.id)).length;
|
|
65
|
-
return { added, updated, removed: toDelete.length, unchanged };
|
|
188
|
+
return stats;
|
|
66
189
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type SearchDb } from './db.js';
|
|
2
|
+
export declare const LEXICAL_VERSION = "snowball-english-rust-stemmers-1.2.0-v1:unicode-words-v1:live-statistics-v1";
|
|
3
|
+
/** Same analysis for indexed fields and queries; exact identifiers bypass it. */
|
|
4
|
+
export declare function lexicalTokens(text: string): string[];
|
|
5
|
+
/** Populate derived lexical rows, reusing original passages and vectors. */
|
|
6
|
+
export declare function synchronizeLexical(db: SearchDb): Promise<void>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { stemWords, STEMMER_VERSION } from '@lat.md/stemmer';
|
|
2
|
+
import { CREATE_PASSAGE_FTS } from './db.js';
|
|
3
|
+
export const LEXICAL_VERSION = `${STEMMER_VERSION}:unicode-words-v1:live-statistics-v1`;
|
|
4
|
+
/** Same analysis for indexed fields and queries; exact identifiers bypass it. */
|
|
5
|
+
export function lexicalTokens(text) {
|
|
6
|
+
const tokens = text.toLowerCase().match(/[\p{L}\p{N}_]+/gu) ?? [];
|
|
7
|
+
const english = tokens.filter((token) => /^[a-z]+$/.test(token));
|
|
8
|
+
const stems = stemWords(english);
|
|
9
|
+
let i = 0;
|
|
10
|
+
return tokens.map((token) => (/^[a-z]+$/.test(token) ? stems[i++] : token));
|
|
11
|
+
}
|
|
12
|
+
/** Populate derived lexical rows, reusing original passages and vectors. */
|
|
13
|
+
export async function synchronizeLexical(db) {
|
|
14
|
+
const version = (await db.execute("SELECT value FROM meta WHERE key='lexical_version'")).rows[0]?.value;
|
|
15
|
+
if (version !== LEXICAL_VERSION) {
|
|
16
|
+
await db.execute('DROP INDEX IF EXISTS chunks_fts');
|
|
17
|
+
await db.execute('DELETE FROM lexical_chunks');
|
|
18
|
+
}
|
|
19
|
+
await db.execute('DELETE FROM lexical_chunks WHERE id NOT IN (SELECT id FROM chunks)');
|
|
20
|
+
const rows = (await db.execute('SELECT id,body,heading,path FROM chunks WHERE id NOT IN (SELECT id FROM lexical_chunks)')).rows;
|
|
21
|
+
for (const row of rows)
|
|
22
|
+
await db.execute({
|
|
23
|
+
sql: 'INSERT INTO lexical_chunks VALUES (?,?,?,?)',
|
|
24
|
+
args: [
|
|
25
|
+
row.id,
|
|
26
|
+
...[row.body, row.heading, row.path].map((text) => lexicalTokens(text).join(' ')),
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
if (version !== LEXICAL_VERSION) {
|
|
30
|
+
await db.execute(CREATE_PASSAGE_FTS);
|
|
31
|
+
await db.execute({
|
|
32
|
+
sql: 'INSERT OR REPLACE INTO meta VALUES (?,?)',
|
|
33
|
+
args: ['lexical_version', LEXICAL_VERSION],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type LockMode = 'shared' | 'exclusive';
|
|
2
|
+
export declare function acquireSearchLock(cacheDir: string, timeoutMs?: number): Promise<() => Promise<void>>;
|
|
3
|
+
/** A turnstile prevents new readers bypassing a writer waiting for readers to drain. */
|
|
4
|
+
export declare function acquireSearchAccess(cacheDir: string, mode: LockMode, timeoutMs?: number): Promise<() => Promise<void>>;
|
|
5
|
+
export declare function withSearchAccess<T>(cacheDir: string, mode: LockMode, work: () => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { open } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { setTimeout } from 'node:timers/promises';
|
|
4
|
+
/** Lock files stay in place so every process locks the same inode. */
|
|
5
|
+
async function acquireFileLock(path, mode, deadline, message) {
|
|
6
|
+
const { tryLock } = await import('fs-native-extensions');
|
|
7
|
+
const handle = await open(path, 'a+');
|
|
8
|
+
try {
|
|
9
|
+
while (!tryLock(handle.fd, { shared: mode === 'shared' })) {
|
|
10
|
+
const remaining = deadline - performance.now();
|
|
11
|
+
if (remaining <= 0)
|
|
12
|
+
throw new Error(message);
|
|
13
|
+
await setTimeout(Math.min(10, remaining));
|
|
14
|
+
}
|
|
15
|
+
return () => handle.close();
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
await handle.close();
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function acquireSearchLock(cacheDir, timeoutMs = 120_000) {
|
|
23
|
+
return acquireFileLock(join(cacheDir, 'search-write.lock'), 'exclusive', performance.now() + timeoutMs, 'Search index writer is busy; retry shortly.');
|
|
24
|
+
}
|
|
25
|
+
/** A turnstile prevents new readers bypassing a writer waiting for readers to drain. */
|
|
26
|
+
export async function acquireSearchAccess(cacheDir, mode, timeoutMs = 120_000) {
|
|
27
|
+
const deadline = performance.now() + timeoutMs;
|
|
28
|
+
const message = 'Search database is busy; retry shortly.';
|
|
29
|
+
const releaseGate = await acquireFileLock(join(cacheDir, 'search-access-gate.lock'), 'exclusive', deadline, message);
|
|
30
|
+
try {
|
|
31
|
+
return await acquireFileLock(join(cacheDir, 'search-access.lock'), mode, deadline, message);
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await releaseGate();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function withSearchAccess(cacheDir, mode, work) {
|
|
38
|
+
const release = await acquireSearchAccess(cacheDir, mode);
|
|
39
|
+
try {
|
|
40
|
+
return await work();
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
await release();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Section, SectionMatch } from '@plebeiantech/lat.md-core/lattice-model';
|
|
2
|
+
import { type SearchDb } from './db.js';
|
|
3
|
+
import { type CreateSearchEngine } from './embedder.js';
|
|
4
|
+
import { type SearchResult } from './search.js';
|
|
5
|
+
export type IndexedSearchResult = {
|
|
6
|
+
query: string;
|
|
7
|
+
matches: SectionMatch[];
|
|
8
|
+
};
|
|
9
|
+
export type IndexedSearchSession = {
|
|
10
|
+
search: (query: string, limit: number, minSimilarity?: number) => Promise<SearchResult[]>;
|
|
11
|
+
close: () => Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
/** Resolve scored index rows against one already-analyzed project snapshot. */
|
|
14
|
+
export declare function resolveSearchMatches(results: readonly SearchResult[], sectionById: ReadonlyMap<string, Section>): SectionMatch[];
|
|
15
|
+
/** Reuse an embedder with scoped database access for a sequence of index queries. */
|
|
16
|
+
export declare function openIndexedSearchSession(latDir: string, options?: {
|
|
17
|
+
cacheDir?: string;
|
|
18
|
+
createSearchEngine?: CreateSearchEngine;
|
|
19
|
+
}): Promise<IndexedSearchSession>;
|
|
20
|
+
/** Query a finished index and resolve its ids from precomputed section data. */
|
|
21
|
+
export declare function searchIndexedSections(latDir: string, query: string, limit: number, sectionById: ReadonlyMap<string, Section>, options?: {
|
|
22
|
+
cacheDir?: string;
|
|
23
|
+
minSimilarity?: number;
|
|
24
|
+
}): Promise<IndexedSearchResult>;
|
|
25
|
+
export declare function indexMetadata(db: SearchDb): Promise<Map<string, string>>;
|
|
26
|
+
export declare function sameIndexMetadata(a: Map<string, string>, b: Map<string, string>): boolean;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { LEXICAL_VERSION } from './lexical.js';
|
|
2
|
+
import { ReindexRequiredError } from './embedder.js';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { hasIndex } from './db.js';
|
|
6
|
+
import { closeDb, openDb } from './db.js';
|
|
7
|
+
import { embedderForIndex } from './embedder.js';
|
|
8
|
+
import { searchSections, prepareSearchQuery, } from './search.js';
|
|
9
|
+
/** Resolve scored index rows against one already-analyzed project snapshot. */
|
|
10
|
+
export function resolveSearchMatches(results, sectionById) {
|
|
11
|
+
return results.flatMap((result) => {
|
|
12
|
+
const section = sectionById.get(result.id.toLowerCase());
|
|
13
|
+
return section
|
|
14
|
+
? [
|
|
15
|
+
{
|
|
16
|
+
section,
|
|
17
|
+
reason: 'hybrid match',
|
|
18
|
+
rankScore: result.rankScore,
|
|
19
|
+
semanticSimilarity: result.semanticSimilarity,
|
|
20
|
+
lexicalScore: result.lexicalScore,
|
|
21
|
+
semanticRank: result.semanticRank,
|
|
22
|
+
lexicalRank: result.lexicalRank,
|
|
23
|
+
evidence: result.evidence,
|
|
24
|
+
diagnostics: result.diagnostics,
|
|
25
|
+
},
|
|
26
|
+
]
|
|
27
|
+
: [];
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/** Reuse an embedder with scoped database access for a sequence of index queries. */
|
|
31
|
+
export async function openIndexedSearchSession(latDir, options = {}) {
|
|
32
|
+
const cacheDir = options.cacheDir ?? join(latDir, '.cache');
|
|
33
|
+
if (!existsSync(cacheDir) || !hasIndex(cacheDir))
|
|
34
|
+
return { search: async () => [], close: async () => { } };
|
|
35
|
+
const readMetadata = async () => {
|
|
36
|
+
const db = openDb(latDir, options.cacheDir, true);
|
|
37
|
+
try {
|
|
38
|
+
return await indexMetadata(db);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
await closeDb(db);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const initial = await readMetadata();
|
|
45
|
+
const stored = initial.get('embedding_model') ?? null;
|
|
46
|
+
if (stored && initial.get('lexical_version') !== LEXICAL_VERSION)
|
|
47
|
+
throw new ReindexRequiredError('Search lexical index changed; run lat search to update it.');
|
|
48
|
+
const embedder = stored
|
|
49
|
+
? await embedderForIndex(stored, latDir, options.createSearchEngine)
|
|
50
|
+
: null;
|
|
51
|
+
let closed = false;
|
|
52
|
+
const active = new Set();
|
|
53
|
+
return {
|
|
54
|
+
search(query, limit, minSimilarity) {
|
|
55
|
+
if (closed)
|
|
56
|
+
return Promise.reject(new Error('Search session is closed'));
|
|
57
|
+
const operation = (async () => {
|
|
58
|
+
if (!embedder)
|
|
59
|
+
return [];
|
|
60
|
+
const vector = await prepareSearchQuery(query, embedder, limit, minSimilarity);
|
|
61
|
+
const db = openDb(latDir, options.cacheDir, true);
|
|
62
|
+
try {
|
|
63
|
+
if (!sameIndexMetadata(initial, await indexMetadata(db)))
|
|
64
|
+
throw new ReindexRequiredError('Search index changed; reopen the search session to refresh its documents and model.');
|
|
65
|
+
return await searchSections(db, query, embedder, limit, minSimilarity, vector);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
await closeDb(db);
|
|
69
|
+
}
|
|
70
|
+
})();
|
|
71
|
+
active.add(operation);
|
|
72
|
+
void operation.finally(() => active.delete(operation)).catch(() => { });
|
|
73
|
+
return operation;
|
|
74
|
+
},
|
|
75
|
+
async close() {
|
|
76
|
+
closed = true;
|
|
77
|
+
await Promise.allSettled([...active]);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** Query a finished index and resolve its ids from precomputed section data. */
|
|
82
|
+
export async function searchIndexedSections(latDir, query, limit, sectionById, options = {}) {
|
|
83
|
+
const session = await openIndexedSearchSession(latDir, options);
|
|
84
|
+
try {
|
|
85
|
+
const results = await session.search(query, limit, options.minSimilarity);
|
|
86
|
+
return {
|
|
87
|
+
query,
|
|
88
|
+
matches: resolveSearchMatches(results, sectionById),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
await session.close();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export async function indexMetadata(db) {
|
|
96
|
+
return new Map((await db.execute('SELECT key,value FROM meta')).rows.map((row) => [
|
|
97
|
+
row.key,
|
|
98
|
+
row.value,
|
|
99
|
+
]));
|
|
100
|
+
}
|
|
101
|
+
export function sameIndexMetadata(a, b) {
|
|
102
|
+
return (a.size === b.size && [...a].every(([key, value]) => b.get(key) === value));
|
|
103
|
+
}
|
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SearchDb } from './db.js';
|
|
2
2
|
import type { Embedder } from './embedder.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import type { SearchResult } from './types.js';
|
|
4
|
+
export type { SearchResult, SearchEvidence, SearchDiagnostics, } from './types.js';
|
|
5
|
+
export declare const DEFAULT_SEARCH_LIMIT = 5;
|
|
6
|
+
export declare const DEFAULT_MIN_SIMILARITY = 0.2;
|
|
7
|
+
export type Candidate = {
|
|
8
|
+
id: number;
|
|
9
|
+
sectionId: string;
|
|
10
|
+
score: number;
|
|
11
|
+
exact?: boolean;
|
|
8
12
|
};
|
|
9
|
-
export declare function
|
|
13
|
+
export declare function collapse(candidates: Candidate[]): Map<string, Candidate & {
|
|
14
|
+
rank: number;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function literalFtsQuery(query: string): string;
|
|
17
|
+
export declare function searchSections(db: SearchDb, query: string, embedder: Embedder, limit?: number, minSimilarity?: number, preparedVector?: number[]): Promise<SearchResult[]>;
|
|
18
|
+
/** Prepare embeddings before acquiring a database access lock. */
|
|
19
|
+
export declare function prepareSearchQuery(query: string, embedder: Embedder, limit?: number, minSimilarity?: number): Promise<number[]>;
|