@plebeiantech/lat.md 0.12.3-fork.9 → 0.13.4-fork.2
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 +27 -3
- 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/fork-instructions.d.ts +1 -0
- package/dist/src/cli/fork-instructions.js +8 -32
- package/dist/src/cli/fork-scaffold.js +2 -2
- 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 +178 -70
- 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 +100 -60
- 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 +115 -101
- 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 +1 -1
- package/dist/src/cli/ui-build.js +1 -1
- package/dist/src/cli/ui.d.ts +1 -1
- package/dist/src/cli/ui.js +1 -1
- package/dist/src/cli/wiki.d.ts +12 -0
- package/dist/src/cli/wiki.js +85 -0
- 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.js +1 -1
- package/dist/src/fork/upstream-guard.d.ts +1 -1
- package/dist/src/fork/upstream-guard.js +8 -7
- package/dist/src/graph-export.js +7 -7
- 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 +100 -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 -70
- 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 +16 -7
- package/dist/src/search/search.js +165 -21
- package/dist/src/search/types.d.ts +15 -0
- package/dist/src/view/client/assets/GraphView-D_xnk0bP.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-bjqt_FoF.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-kWGvtRnL.js +1 -0
- package/dist/src/view/client/assets/arc-BPAFd7cP.js +1 -0
- package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-CsY39oIR.js +36 -0
- package/dist/src/view/client/assets/blockDiagram-I7D4REHJ-DHGBG7BJ.js +129 -0
- package/dist/src/view/client/assets/c4Diagram-7LVT6UL2-vywK4eb7.js +38 -0
- package/dist/src/view/client/assets/channel-CX71L_qV.js +1 -0
- package/dist/src/view/client/assets/chunk-2Q5K7J3B-Do3Hz_ci.js +1 -0
- package/dist/src/view/client/assets/chunk-5VM5RSS4-B5R1lSeY.js +15 -0
- package/dist/src/view/client/assets/chunk-F27PBJKO-Dm55FM_W.js +1 -0
- package/dist/src/view/client/assets/chunk-IMKFNOWR-ChptrvvM.js +231 -0
- package/dist/src/view/client/assets/chunk-JWPE2WC7-BRQBjrKS.js +1 -0
- package/dist/src/view/client/assets/chunk-POPQ4Y6H-CainVZIL.js +1 -0
- package/dist/src/view/client/assets/chunk-SVP7TREG-DsKsNGmM.js +88 -0
- package/dist/src/view/client/assets/chunk-TICWLB2K-BctkMcJc.js +206 -0
- package/dist/src/view/client/assets/chunk-XXDRQBXY-BN7SgOUT.js +1 -0
- package/dist/src/view/client/assets/classDiagram-ZZMXUADV-B1VyU6zO.js +1 -0
- package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-B1VyU6zO.js +1 -0
- package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-BszzwrMe.js +1 -0
- package/dist/src/view/client/assets/cynefin-OW5HDTMX-CdRyAkPs.js +166 -0
- package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-BLvKPZ1u.js +62 -0
- package/dist/src/view/client/assets/cytoscape.esm-DzSz-X2X.js +321 -0
- package/dist/src/view/client/assets/dagre-GXQ25YYZ-V68w_l65.js +4 -0
- package/dist/src/view/client/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/src/view/client/assets/diagram-S7CK7UJ4-C2i4eEMg.js +30 -0
- package/dist/src/view/client/assets/diagram-UQ7AKVKN-BiwWh5jC.js +41 -0
- package/dist/src/view/client/assets/diagram-VSXAHHWV-hkKR_vyS.js +3 -0
- package/dist/src/view/client/assets/diagram-VX7I27RA-DzRCSMAc.js +24 -0
- package/dist/src/view/client/assets/diagram-Z3DM3KII-fhSFPxyK.js +24 -0
- package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-CCEUDKSA.js +1 -0
- package/dist/src/view/client/assets/erDiagram-RLTQ6QDP-FJ2IMyTi.js +99 -0
- package/dist/src/view/client/assets/flowDiagram-HODETNUW-BvasdLeK.js +168 -0
- package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-CiwkBZvY.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-DYlP2_js.js +106 -0
- package/dist/src/view/client/assets/index-BnENo9bV.js +1 -0
- package/dist/src/view/client/assets/index-CQ7EOox7.css +1 -0
- package/dist/src/view/client/assets/index-CynNDkxt.js +13 -0
- package/dist/src/view/client/assets/infoDiagram-27XIBGKW-DRPB2c48.js +2 -0
- package/dist/src/view/client/assets/init-Gi6I4Gst.js +1 -0
- package/dist/src/view/client/assets/ishikawaDiagram-5VMMS53U-8VV429Cv.js +70 -0
- package/dist/src/view/client/assets/journeyDiagram-3NMN7TZE-Dzmwnf83.js +139 -0
- package/dist/src/view/client/assets/kanban-definition-UXKFOSKX-pYyZZ1i-.js +89 -0
- package/dist/src/view/client/assets/katex-HP8lGamR.js +257 -0
- package/dist/src/view/client/assets/layout-GF9k6yGo.js +1 -0
- package/dist/src/view/client/assets/linear-Yt2sSOYV.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-ChWYCHN0.js +310 -0
- package/dist/src/view/client/assets/mindmap-definition-YA3MSWOX-Cc4fQvf1.js +96 -0
- package/dist/src/view/client/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/src/view/client/assets/pegDiagram-XKGWAZYB-C0SsB8bZ.js +1 -0
- package/dist/src/view/client/assets/pieDiagram-E7YTZNPT-DIJpqERZ.js +39 -0
- package/dist/src/view/client/assets/quadrantDiagram-AXDQQJYC--d-gl6j-.js +7 -0
- package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-BQxwTt7z.js +1 -0
- package/dist/src/view/client/assets/requirementDiagram-BXWQKSXE-B_oTeD2A.js +84 -0
- package/dist/src/view/client/assets/sankeyDiagram-P5KCCOFB-DLukmE8X.js +40 -0
- package/dist/src/view/client/assets/sequenceDiagram-WJ2MYXX4-BNjVB2sc.js +162 -0
- package/dist/src/view/client/assets/sizeCapture-INFHLROL-BZecQTOw.js +1 -0
- package/dist/src/view/client/assets/stateDiagram-D77RDMKH-Bu1urvXC.js +1 -0
- package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-5pc_697G.js +1 -0
- package/dist/src/view/client/assets/swimlanes-42K2YHIH-bLAlcepB.js +1 -0
- package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-BbOpZjpp.js +8 -0
- package/dist/src/view/client/assets/three.module-PDSP0dbZ.js +4108 -0
- package/dist/src/view/client/assets/timeline-definition-24CTP7MA-tbwCNvcA.js +120 -0
- package/dist/src/view/client/assets/vennDiagram-4TSXK5OY-D8U8Fk0N.js +34 -0
- package/dist/src/view/client/assets/wardleyDiagram-VM6X3IG4-LHBVIeNb.js +78 -0
- package/dist/src/view/client/assets/xychartDiagram-S5SC5T6Z-CN4tfaFV.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 +137 -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 +202 -2
- package/dist/src/view/git.js +1 -1
- package/dist/src/view/graph.d.ts +4 -3
- package/dist/src/view/graph.js +100 -9
- package/dist/src/view/highlight.d.ts +10 -2
- package/dist/src/view/highlight.js +50648 -3166
- package/dist/src/view/markdown.d.ts +3 -2
- package/dist/src/view/markdown.js +256 -54
- 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 +53 -7
- package/dist/src/view/publication.d.ts +2 -0
- package/dist/src/view/publication.js +78 -0
- package/dist/src/view/references.d.ts +11 -26
- package/dist/src/view/references.js +121 -112
- package/dist/src/view/repository.d.ts +14 -4
- package/dist/src/view/repository.js +260 -19
- 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 -34
- 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 +123 -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 +11 -2
- package/dist/src/view/server.js +245 -73
- package/dist/src/view/source-target.d.ts +2 -0
- package/dist/src/view/source-target.js +49 -8
- package/dist/src/view/static-build.d.ts +18 -2
- package/dist/src/view/static-build.js +449 -110
- package/dist/src/view/static-protocol.d.ts +26 -2
- package/dist/src/view/static-protocol.js +5 -0
- package/dist/src/view/store.d.ts +20 -3
- package/dist/src/view/store.js +304 -48
- package/dist/src/view/table-of-contents.d.ts +2 -2
- package/dist/src/view/table-of-contents.js +2 -22
- package/dist/src/view/vercel-build.d.ts +24 -0
- package/dist/src/view/vercel-build.js +152 -0
- package/dist/src/view/vercel-server-build.d.ts +10 -0
- package/dist/src/view/vercel-server-build.js +61 -0
- package/dist/src/wiki/catalog.d.ts +21 -0
- package/dist/src/wiki/catalog.js +256 -0
- package/dist/src/wiki/publish.d.ts +27 -0
- package/dist/src/wiki/publish.js +265 -0
- package/dist/src/wiki/render.d.ts +20 -0
- package/dist/src/wiki/render.js +163 -0
- package/dist/src/wiki/transform.d.ts +7 -0
- package/dist/src/wiki/transform.js +264 -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 +68 -22
- 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 +344 -0
- package/plugins/lat-md/skills/lat-md/SKILL.md +210 -0
- package/plugins/lat-md/skills/lat-md-conventions/SKILL.md +226 -0
- package/templates/AGENTS.md +26 -7
- package/templates/cursor-rules.md +23 -7
- package/templates/fork/conventions.md +133 -11
- package/templates/opencode-plugin.ts +9 -9
- package/templates/pi-extension.ts +11 -11
- package/templates/skill/SKILL.md +35 -3
- package/dist/src/cli/check-coverage.d.ts +0 -2
- package/dist/src/cli/check-coverage.js +0 -71
- 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 -21
- package/dist/src/cli/check-mode.js +0 -364
- 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 -781
- 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 -298
- 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 -109
- package/dist/src/lattice.js +0 -817
- 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 -18
- package/dist/src/untrusted.js +0 -93
- package/dist/src/view/client/assets/index-C3iaZyYT.css +0 -1
- package/dist/src/view/client/assets/index-KWi67IMz.js +0 -334
- package/dist/src/view/client/assets/logo-CNF8zs47.svg +0 -182
- package/dist/src/walk.d.ts +0 -18
- package/dist/src/walk.js +0 -27
- /package/dist/src/{extensions/wiki-link → search}/types.js +0 -0
package/dist/src/mcp/server.js
CHANGED
|
@@ -2,14 +2,16 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { dirname } from 'node:path';
|
|
5
|
-
import { findLatticeDir } from '
|
|
6
|
-
import { plainStyler } from '
|
|
7
|
-
import { locateCommand } from '
|
|
8
|
-
import { sectionCommand } from '
|
|
5
|
+
import { findLatticeDir } from '@plebeiantech/lat.md-core/project-discovery';
|
|
6
|
+
import { plainStyler, } from '@plebeiantech/lat.md-core/context';
|
|
7
|
+
import { locateCommand } from '@plebeiantech/lat.md-core/cli/locate';
|
|
8
|
+
import { sectionCommand } from '@plebeiantech/lat.md-core/cli/section';
|
|
9
9
|
import { searchCommand } from '../cli/search.js';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
10
|
+
import { DEFAULT_SEARCH_LIMIT, DEFAULT_MIN_SIMILARITY, } from '../search/search.js';
|
|
11
|
+
import { expandCommand } from '@plebeiantech/lat.md-core/cli/expand';
|
|
12
|
+
import { checkAllCommand } from '@plebeiantech/lat.md-core/cli/check';
|
|
13
|
+
import { refsCommand } from '@plebeiantech/lat.md-core/cli/refs';
|
|
14
|
+
import { externalListCommand, externalShowCommand, } from '@plebeiantech/lat.md-core/cli/external';
|
|
13
15
|
function toMcp(result) {
|
|
14
16
|
const content = [{ type: 'text', text: result.output }];
|
|
15
17
|
return result.isError ? { content, isError: true } : { content };
|
|
@@ -27,24 +29,34 @@ export async function startMcpServer() {
|
|
|
27
29
|
styler: plainStyler,
|
|
28
30
|
mode: 'mcp',
|
|
29
31
|
};
|
|
32
|
+
const requestContext = () => ({ ...ctx, analysis: undefined });
|
|
30
33
|
const server = new McpServer({
|
|
31
34
|
name: 'lat',
|
|
32
35
|
version: '1.0.0',
|
|
33
36
|
});
|
|
34
|
-
server.tool('lat_locate', 'Find sections by name (exact, fuzzy, subsequence matching)', { query: z.string().describe('Section name or id to search for') }, async ({ query }) => toMcp(await locateCommand(
|
|
35
|
-
server.tool('lat_section', 'Show a section
|
|
36
|
-
query: z
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
server.tool('lat_locate', 'Find sections by name (exact, fuzzy, subsequence matching)', { query: z.string().describe('Section name or id to search for') }, async ({ query }) => toMcp(await locateCommand(requestContext(), query)));
|
|
38
|
+
server.tool('lat_section', 'Show a local section or exact external target with content and references', {
|
|
39
|
+
query: z
|
|
40
|
+
.string()
|
|
41
|
+
.describe('Local section id or exact handle:path#fragment target'),
|
|
42
|
+
}, async ({ query }) => toMcp(await sectionCommand(requestContext(), query)));
|
|
43
|
+
server.tool('lat_search', 'Hybrid lexical and semantic search across lat.md sections', {
|
|
39
44
|
query: z.string().describe('Search query in natural language'),
|
|
40
45
|
limit: z
|
|
41
46
|
.number()
|
|
42
47
|
.optional()
|
|
43
|
-
.default(
|
|
44
|
-
.describe(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
.default(DEFAULT_SEARCH_LIMIT)
|
|
49
|
+
.describe(`Max results (default ${DEFAULT_SEARCH_LIMIT})`),
|
|
50
|
+
minSimilarity: z
|
|
51
|
+
.number()
|
|
52
|
+
.min(0)
|
|
53
|
+
.max(1)
|
|
54
|
+
.optional()
|
|
55
|
+
.default(DEFAULT_MIN_SIMILARITY)
|
|
56
|
+
.describe('Minimum cosine similarity score (default 0.20)'),
|
|
57
|
+
}, async ({ query, limit, minSimilarity }) => toMcp(await searchCommand(requestContext(), query, { limit, minSimilarity })));
|
|
58
|
+
server.tool('lat_expand', 'Expand [[refs]] in text to resolved lat.md section paths with context', { text: z.string().describe('Text containing [[refs]] to expand') }, async ({ text: input }) => toMcp(await expandCommand(requestContext(), input)));
|
|
59
|
+
server.tool('lat_check', 'Run full lat.md validation: links, code references, indexes, and section structure', {}, async () => toMcp(await checkAllCommand(requestContext())));
|
|
48
60
|
server.tool('lat_refs', 'Find sections that reference a given section via wiki links or @lat code comments', {
|
|
49
61
|
query: z.string().describe('Section id to find references for'),
|
|
50
62
|
scope: z
|
|
@@ -52,7 +64,11 @@ export async function startMcpServer() {
|
|
|
52
64
|
.optional()
|
|
53
65
|
.default('md+code')
|
|
54
66
|
.describe('Where to search: md, code, or md+code'),
|
|
55
|
-
}, async ({ query, scope }) => toMcp(await refsCommand(
|
|
67
|
+
}, async ({ query, scope }) => toMcp(await refsCommand(requestContext(), query, scope)));
|
|
68
|
+
server.tool('lat_external_list', 'List configured external sources without fetching or changing caches', {}, async () => toMcp(await externalListCommand(ctx, true)));
|
|
69
|
+
server.tool('lat_external_show', 'Show one configured external source or exact external target without fetching it', {
|
|
70
|
+
source: z.string().describe('External source handle or exact target'),
|
|
71
|
+
}, async ({ source }) => toMcp(await externalShowCommand(ctx, source, true)));
|
|
56
72
|
const transport = new StdioServerTransport();
|
|
57
73
|
await server.connect(transport);
|
|
58
74
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { SearchDb } from './db.js';
|
|
2
|
+
/** Build a private staging database; access locks protect copies and publication of search.db. */
|
|
3
|
+
export declare function writeIndex<T>(latDir: string, cacheDir: string | undefined, rebuild: boolean, work: (db: SearchDb, storedModel: string | null) => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { copyFile, mkdir, rename, rm, stat } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { acquireSearchLock, withSearchAccess } from './lock.js';
|
|
4
|
+
import { SearchDb, hasIndex, INDEX_FILE, ensureMeta, getStoredModel, } from './db.js';
|
|
5
|
+
/** Retry transient Windows handles without deleting the usable index first. */
|
|
6
|
+
async function withFileRetry(work) {
|
|
7
|
+
for (let attempt = 0;; attempt++) {
|
|
8
|
+
try {
|
|
9
|
+
await work();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
const code = error.code;
|
|
14
|
+
// Windows may briefly retain a staging handle or a reader handle.
|
|
15
|
+
if (process.platform !== 'win32' ||
|
|
16
|
+
attempt >= 9 ||
|
|
17
|
+
!['EBUSY', 'EPERM', 'EACCES'].includes(code ?? ''))
|
|
18
|
+
throw error;
|
|
19
|
+
await new Promise((resolve) => setTimeout(resolve, 50 * (attempt + 1)));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function removeStaging(path) {
|
|
24
|
+
for (const suffix of ['', '-wal', '-shm', '-journal'])
|
|
25
|
+
await withFileRetry(() => rm(path + suffix, { force: true }));
|
|
26
|
+
}
|
|
27
|
+
/** Build a private staging database; access locks protect copies and publication of search.db. */
|
|
28
|
+
export async function writeIndex(latDir, cacheDir, rebuild, work) {
|
|
29
|
+
const dir = cacheDir ?? join(latDir, '.cache');
|
|
30
|
+
await mkdir(dir, { recursive: true });
|
|
31
|
+
const path = join(dir, '_search.db');
|
|
32
|
+
const activePath = join(dir, INDEX_FILE);
|
|
33
|
+
let db;
|
|
34
|
+
const release = await acquireSearchLock(dir);
|
|
35
|
+
try {
|
|
36
|
+
// A crashed writer may have left a partial database and journal behind.
|
|
37
|
+
await removeStaging(path);
|
|
38
|
+
const existing = hasIndex(dir);
|
|
39
|
+
if (existing && !rebuild) {
|
|
40
|
+
await withSearchAccess(dir, 'exclusive', async () => {
|
|
41
|
+
// Recover/checkpoint a WAL left by a process that died during a query.
|
|
42
|
+
const source = new SearchDb(activePath);
|
|
43
|
+
try {
|
|
44
|
+
await source.checkpoint();
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
await source.close();
|
|
48
|
+
}
|
|
49
|
+
await copyFile(activePath, path);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
db = new SearchDb(path);
|
|
53
|
+
const model = existing && !rebuild ? await getStoredModel(db) : null;
|
|
54
|
+
await ensureMeta(db);
|
|
55
|
+
const changesBefore = (await db.execute('SELECT total_changes() AS n'))
|
|
56
|
+
.rows[0].n;
|
|
57
|
+
const result = await work(db, model);
|
|
58
|
+
const unchanged = existing &&
|
|
59
|
+
!rebuild &&
|
|
60
|
+
(await db.execute('SELECT total_changes() AS n')).rows[0].n ===
|
|
61
|
+
changesBefore;
|
|
62
|
+
await db.checkpoint();
|
|
63
|
+
await db.close();
|
|
64
|
+
db = undefined;
|
|
65
|
+
if (!unchanged)
|
|
66
|
+
await withSearchAccess(dir, 'exclusive', async () => {
|
|
67
|
+
// Recover old sidecars before replacing their database, including after a crash.
|
|
68
|
+
if (existing) {
|
|
69
|
+
const wal = await stat(activePath + '-wal').catch((error) => {
|
|
70
|
+
if (error.code !== 'ENOENT')
|
|
71
|
+
throw error;
|
|
72
|
+
return undefined;
|
|
73
|
+
});
|
|
74
|
+
// A fresh rebuild must also repair an unreadable database with no WAL.
|
|
75
|
+
if (wal && wal.size > 0) {
|
|
76
|
+
const source = new SearchDb(activePath);
|
|
77
|
+
try {
|
|
78
|
+
await source.checkpoint();
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
await source.close();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const suffix of ['-wal', '-shm', '-journal'])
|
|
85
|
+
await withFileRetry(() => rm(activePath + suffix, { force: true }));
|
|
86
|
+
}
|
|
87
|
+
await withFileRetry(() => rename(path, activePath));
|
|
88
|
+
});
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
try {
|
|
93
|
+
await db?.close();
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
try {
|
|
97
|
+
await removeStaging(path);
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
await release();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SourceSpan } from './types.js';
|
|
2
|
+
import type { Embedder } from './embedder.js';
|
|
3
|
+
import type { MarkdownFileAnalysis } from '@plebeiantech/lat.md-core/markdown-analysis';
|
|
4
|
+
import type { Section } from '@plebeiantech/lat.md-core/lattice-model';
|
|
5
|
+
export declare const CHUNK_POLICY = "owned-blocks-v1";
|
|
6
|
+
export type Passage = {
|
|
7
|
+
id: string;
|
|
8
|
+
sectionId: string;
|
|
9
|
+
ordinal: number;
|
|
10
|
+
type: string;
|
|
11
|
+
spans: SourceSpan[];
|
|
12
|
+
text: string;
|
|
13
|
+
input: string;
|
|
14
|
+
inputHash: string;
|
|
15
|
+
heading: string;
|
|
16
|
+
path: string;
|
|
17
|
+
};
|
|
18
|
+
export declare const digest: (text: string) => string;
|
|
19
|
+
export declare function embeddingFingerprint(embedder: Embedder): string;
|
|
20
|
+
/** Find a fitting Unicode-safe prefix without assuming token-count monotonicity. */
|
|
21
|
+
export declare function fittingPrefix(text: string, fits: (text: string) => boolean): number;
|
|
22
|
+
/** Every body block is owned once; headings provide context instead of copied subtrees. */
|
|
23
|
+
export declare function chunkFile(file: MarkdownFileAnalysis, sections: readonly Section[], embedder: Embedder): Passage[];
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
export const CHUNK_POLICY = 'owned-blocks-v1';
|
|
3
|
+
export const digest = (text) => createHash('sha256').update(text).digest('hex');
|
|
4
|
+
export function embeddingFingerprint(embedder) {
|
|
5
|
+
return `${CHUNK_POLICY}:${embedder.name}:${embedder.dimensions}:${embedder.maxInputTokens}:${embedder.tokenizerFingerprint}`;
|
|
6
|
+
}
|
|
7
|
+
/** Find a fitting Unicode-safe prefix without assuming token-count monotonicity. */
|
|
8
|
+
export function fittingPrefix(text, fits) {
|
|
9
|
+
if (fits(text))
|
|
10
|
+
return text.length;
|
|
11
|
+
let size = text.length;
|
|
12
|
+
while (size > 0) {
|
|
13
|
+
size = Math.floor(size / 2);
|
|
14
|
+
if (size && /[\uD800-\uDBFF]/.test(text[size - 1]))
|
|
15
|
+
size--;
|
|
16
|
+
if (size && fits(text.slice(0, size)))
|
|
17
|
+
return size;
|
|
18
|
+
}
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
/** Every body block is owned once; headings provide context instead of copied subtrees. */
|
|
22
|
+
export function chunkFile(file, sections, embedder) {
|
|
23
|
+
const local = embedder.name.startsWith('local:');
|
|
24
|
+
const target = local ? 192 : 512;
|
|
25
|
+
const contextBudget = local ? 48 : 96;
|
|
26
|
+
const source = file.content;
|
|
27
|
+
const lineStarts = [0];
|
|
28
|
+
for (let i = 0; i < source.length; i++)
|
|
29
|
+
if (source[i] === '\n')
|
|
30
|
+
lineStarts.push(i + 1);
|
|
31
|
+
const lineAt = (offset) => {
|
|
32
|
+
let lo = 0, hi = lineStarts.length;
|
|
33
|
+
while (lo + 1 < hi) {
|
|
34
|
+
const mid = (lo + hi) >>> 1;
|
|
35
|
+
if (lineStarts[mid] <= offset)
|
|
36
|
+
lo = mid;
|
|
37
|
+
else
|
|
38
|
+
hi = mid;
|
|
39
|
+
}
|
|
40
|
+
return lo + 1;
|
|
41
|
+
};
|
|
42
|
+
const own = new Map();
|
|
43
|
+
const ordered = [...sections].sort((a, b) => a.startLine - b.startLine || a.depth - b.depth);
|
|
44
|
+
let sectionIndex = 0;
|
|
45
|
+
let ancestors = [];
|
|
46
|
+
for (const block of file.blocks) {
|
|
47
|
+
if (block.type === 'heading' || block.type === 'yaml')
|
|
48
|
+
continue;
|
|
49
|
+
// Content before the first heading is outside the section graph.
|
|
50
|
+
if (!ordered.length || block.endLine < ordered[0].startLine)
|
|
51
|
+
continue;
|
|
52
|
+
while (sectionIndex < ordered.length &&
|
|
53
|
+
ordered[sectionIndex].startLine <= block.startLine) {
|
|
54
|
+
const next = ordered[sectionIndex++];
|
|
55
|
+
ancestors = ancestors.filter((s) => s.endLine >= next.startLine);
|
|
56
|
+
ancestors.push(next);
|
|
57
|
+
}
|
|
58
|
+
ancestors = ancestors.filter((s) => s.endLine >= block.endLine);
|
|
59
|
+
const owner = ancestors.at(-1);
|
|
60
|
+
if (!owner)
|
|
61
|
+
throw new Error(`No section owns ${file.path}:${block.startLine}`);
|
|
62
|
+
const blocks = own.get(owner.id) ?? [];
|
|
63
|
+
blocks.push(block);
|
|
64
|
+
own.set(owner.id, blocks);
|
|
65
|
+
}
|
|
66
|
+
const result = [];
|
|
67
|
+
for (const section of sections) {
|
|
68
|
+
const headings = section.id.split('#').slice(1);
|
|
69
|
+
const rawContext = `Section: ${section.heading}\nPage: ${file.headingTitles[0] ?? file.path}\nPath: ${headings.slice(0, -1).join(' > ')}`;
|
|
70
|
+
const contextLength = fittingPrefix(rawContext, (t) => embedder.countTokens(t) <= contextBudget);
|
|
71
|
+
const context = rawContext.slice(0, contextLength);
|
|
72
|
+
const path = headings.slice(0, -1).join(' > ');
|
|
73
|
+
let ordinal = 0;
|
|
74
|
+
const inputFor = (text, extra) => {
|
|
75
|
+
const extraSize = fittingPrefix(extra, (t) => embedder.countTokens(t) <= contextBudget / 2);
|
|
76
|
+
const fullContext = [extra.slice(0, extraSize), context]
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.join('\n');
|
|
79
|
+
const n = fittingPrefix(fullContext, (c) => embedder.countTokens(c) <= contextBudget &&
|
|
80
|
+
embedder.countTokens(`${c}\n\n${text}`) <= embedder.maxInputTokens);
|
|
81
|
+
return n ? `${fullContext.slice(0, n)}\n\n${text}` : text;
|
|
82
|
+
};
|
|
83
|
+
const fits = (text, extra = '') => embedder.countTokens(text) <= target &&
|
|
84
|
+
embedder.countTokens(inputFor(text, extra)) <= embedder.maxInputTokens;
|
|
85
|
+
const split = (block, extra = '') => {
|
|
86
|
+
const text = source.slice(block.start, block.end);
|
|
87
|
+
if (fits(text, extra))
|
|
88
|
+
return [
|
|
89
|
+
{ start: block.start, end: block.end, type: block.type, extra },
|
|
90
|
+
];
|
|
91
|
+
if (block.children.length) {
|
|
92
|
+
const pieces = [];
|
|
93
|
+
let start = block.start;
|
|
94
|
+
for (let i = 0; i < block.children.length; i++) {
|
|
95
|
+
const child = block.children[i];
|
|
96
|
+
const end = i + 1 < block.children.length
|
|
97
|
+
? block.children[i + 1].start
|
|
98
|
+
: block.end;
|
|
99
|
+
let label = extra;
|
|
100
|
+
if (block.type === 'table')
|
|
101
|
+
label = `Row: ${i}\nTable headers: ${source.slice(block.children[0].start, block.children[0].end)}\n${extra}`;
|
|
102
|
+
if (block.type === 'tableRow')
|
|
103
|
+
label = `Column: ${i + 1}\n${extra}`;
|
|
104
|
+
if (block.type === 'listItem')
|
|
105
|
+
label += `\nList item: ${text.split('\n')[0]}`;
|
|
106
|
+
pieces.push(...split({ ...child, start, end }, label));
|
|
107
|
+
start = end;
|
|
108
|
+
}
|
|
109
|
+
return pieces;
|
|
110
|
+
}
|
|
111
|
+
const pieces = [];
|
|
112
|
+
let start = block.start;
|
|
113
|
+
const label = block.type === 'code'
|
|
114
|
+
? `${extra}\nCode: ${block.language ?? ''}`
|
|
115
|
+
: extra;
|
|
116
|
+
while (start < block.end) {
|
|
117
|
+
const rest = source.slice(start, block.end);
|
|
118
|
+
let n = fittingPrefix(rest, (t) => fits(t, label));
|
|
119
|
+
if (!n)
|
|
120
|
+
throw new Error(`Cannot fit source text at ${file.path}:${lineAt(start)}`);
|
|
121
|
+
if (n < rest.length) {
|
|
122
|
+
const prefix = rest.slice(0, n);
|
|
123
|
+
const boundary = block.type === 'code'
|
|
124
|
+
? prefix.lastIndexOf('\n') + 1
|
|
125
|
+
: [...prefix.matchAll(/[.!?](?:\s+|$)/g)].at(-1)?.index;
|
|
126
|
+
const preferred = typeof boundary === 'number' && boundary > n / 2
|
|
127
|
+
? boundary
|
|
128
|
+
: prefix.search(/\s+\S*$/);
|
|
129
|
+
if (preferred > n / 2 && fits(rest.slice(0, preferred), label))
|
|
130
|
+
n = preferred;
|
|
131
|
+
}
|
|
132
|
+
pieces.push({ start, end: start + n, type: block.type, extra: label });
|
|
133
|
+
start += n;
|
|
134
|
+
}
|
|
135
|
+
return pieces;
|
|
136
|
+
};
|
|
137
|
+
const pieces = (own.get(section.id) ?? []).flatMap((block) => split(block));
|
|
138
|
+
if (!pieces.length) {
|
|
139
|
+
const start = lineStarts[section.startLine - 1] ?? 0;
|
|
140
|
+
const end = source.indexOf('\n', start);
|
|
141
|
+
pieces.push(...split({
|
|
142
|
+
type: 'heading',
|
|
143
|
+
start,
|
|
144
|
+
end: end < 0 ? source.length : end,
|
|
145
|
+
startLine: section.startLine,
|
|
146
|
+
endLine: section.startLine,
|
|
147
|
+
children: [],
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
let pending;
|
|
151
|
+
const emit = (piece) => {
|
|
152
|
+
const text = source.slice(piece.start, piece.end);
|
|
153
|
+
if (!text.trim())
|
|
154
|
+
return;
|
|
155
|
+
const input = inputFor(text, piece.extra);
|
|
156
|
+
if (embedder.countTokens(input) > embedder.maxInputTokens)
|
|
157
|
+
throw new Error('Chunk exceeds embedding limit');
|
|
158
|
+
const inputHash = digest(`${embeddingFingerprint(embedder)}\0${input}`);
|
|
159
|
+
result.push({
|
|
160
|
+
id: `${section.id}:${ordinal}`,
|
|
161
|
+
sectionId: section.id,
|
|
162
|
+
ordinal: ordinal++,
|
|
163
|
+
type: piece.type,
|
|
164
|
+
spans: [
|
|
165
|
+
{
|
|
166
|
+
start: piece.start,
|
|
167
|
+
end: piece.end,
|
|
168
|
+
startLine: lineAt(piece.start),
|
|
169
|
+
endLine: lineAt(Math.max(piece.start, piece.end - 1)),
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
text,
|
|
173
|
+
input,
|
|
174
|
+
inputHash,
|
|
175
|
+
heading: section.heading,
|
|
176
|
+
path,
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
for (const piece of pieces) {
|
|
180
|
+
if (pending &&
|
|
181
|
+
!pending.extra &&
|
|
182
|
+
!piece.extra &&
|
|
183
|
+
!source.slice(pending.end, piece.start).trim() &&
|
|
184
|
+
fits(source.slice(pending.start, piece.end)))
|
|
185
|
+
pending = {
|
|
186
|
+
...pending,
|
|
187
|
+
end: piece.end,
|
|
188
|
+
type: pending.type === piece.type ? piece.type : 'mixed',
|
|
189
|
+
};
|
|
190
|
+
else {
|
|
191
|
+
if (pending)
|
|
192
|
+
emit(pending);
|
|
193
|
+
pending = piece;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (pending)
|
|
197
|
+
emit(pending);
|
|
198
|
+
}
|
|
199
|
+
return result;
|
|
200
|
+
}
|
package/dist/src/search/db.d.ts
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
export declare
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
export declare const INDEX_FILE = "search.db";
|
|
2
|
+
export declare const CREATE_PASSAGE_FTS = "CREATE INDEX IF NOT EXISTS chunks_fts ON lexical_chunks USING fts(body,heading,path) WITH (tokenizer='whitespace',weights='body=1.0,heading=2.0,path=0.5')";
|
|
3
|
+
export declare function hasIndex(cacheDir: string): boolean;
|
|
4
|
+
/** Small SQL adapter; callers never depend on a libSQL connection. */
|
|
5
|
+
export declare class SearchDb {
|
|
6
|
+
readonly path: string;
|
|
7
|
+
private readonly published;
|
|
8
|
+
private connection;
|
|
9
|
+
private release;
|
|
10
|
+
constructor(path: string, published?: boolean);
|
|
11
|
+
private get;
|
|
12
|
+
private connect;
|
|
13
|
+
execute(statement: string | {
|
|
14
|
+
sql: string;
|
|
15
|
+
args?: any[];
|
|
16
|
+
}): Promise<{
|
|
17
|
+
rows: any[];
|
|
18
|
+
}>;
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
checkpoint(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export declare function openDb(latDir: string, requestedCacheDir?: string, published?: boolean): SearchDb;
|
|
23
|
+
export declare function ensureMeta(db: SearchDb): Promise<void>;
|
|
24
|
+
export declare function getStoredModel(db: SearchDb): Promise<string | null>;
|
|
25
|
+
export declare function setStoredModel(db: SearchDb, value: string): Promise<void>;
|
|
26
|
+
export declare function ensureSectionsSchema(db: SearchDb, _dimensions: number): Promise<void>;
|
|
27
|
+
export declare function dropSections(db: SearchDb): Promise<void>;
|
|
28
|
+
export declare function closeDb(db: SearchDb): Promise<void>;
|
package/dist/src/search/db.js
CHANGED
|
@@ -1,54 +1,114 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mkdirSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { connect } from '@tursodatabase/database';
|
|
2
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { acquireSearchAccess } from './lock.js';
|
|
5
|
+
export const INDEX_FILE = 'search.db';
|
|
6
|
+
export const CREATE_PASSAGE_FTS = "CREATE INDEX IF NOT EXISTS chunks_fts ON lexical_chunks USING fts(body,heading,path) WITH (tokenizer='whitespace',weights='body=1.0,heading=2.0,path=0.5')";
|
|
7
|
+
export function hasIndex(cacheDir) {
|
|
8
|
+
return existsSync(join(cacheDir, INDEX_FILE));
|
|
9
|
+
}
|
|
10
|
+
/** Small SQL adapter; callers never depend on a libSQL connection. */
|
|
11
|
+
export class SearchDb {
|
|
12
|
+
path;
|
|
13
|
+
published;
|
|
14
|
+
connection;
|
|
15
|
+
release;
|
|
16
|
+
constructor(path, published = false) {
|
|
17
|
+
this.path = path;
|
|
18
|
+
this.published = published;
|
|
19
|
+
}
|
|
20
|
+
get() {
|
|
21
|
+
return (this.connection ??= this.connect());
|
|
22
|
+
}
|
|
23
|
+
async connect() {
|
|
24
|
+
// Published readers share access; copying and publication require exclusive access.
|
|
25
|
+
if (this.published)
|
|
26
|
+
this.release = await acquireSearchAccess(dirname(this.path), 'shared');
|
|
27
|
+
try {
|
|
28
|
+
return await connect(this.path, {
|
|
29
|
+
experimental: ['index_method'],
|
|
30
|
+
readonly: this.published,
|
|
31
|
+
timeout: 10000,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
await this.release?.();
|
|
36
|
+
this.release = undefined;
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async execute(statement) {
|
|
41
|
+
const { sql, args = [] } = typeof statement === 'string' ? { sql: statement } : statement;
|
|
42
|
+
const db = await this.get();
|
|
43
|
+
const prepared = await db.prepare(sql);
|
|
44
|
+
try {
|
|
45
|
+
return { rows: await prepared.all(...args) };
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
prepared.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async close() {
|
|
52
|
+
try {
|
|
53
|
+
if (this.connection) {
|
|
54
|
+
const db = await this.connection;
|
|
55
|
+
await db.close();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
this.connection = undefined;
|
|
60
|
+
const release = this.release;
|
|
61
|
+
this.release = undefined;
|
|
62
|
+
await release?.();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async checkpoint() {
|
|
66
|
+
await this.execute('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function openDb(latDir, requestedCacheDir, published = false) {
|
|
70
|
+
const cacheDir = requestedCacheDir ?? join(latDir, '.cache');
|
|
6
71
|
mkdirSync(cacheDir, { recursive: true });
|
|
7
|
-
|
|
8
|
-
url: `file:${join(cacheDir, 'vectors.db')}`,
|
|
9
|
-
});
|
|
10
|
-
return client;
|
|
72
|
+
return new SearchDb(join(cacheDir, INDEX_FILE), published);
|
|
11
73
|
}
|
|
12
|
-
/** Create the `meta` key/value table if absent. */
|
|
13
74
|
export async function ensureMeta(db) {
|
|
14
|
-
await db.execute(
|
|
15
|
-
|
|
16
|
-
value TEXT NOT NULL
|
|
17
|
-
)`);
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* The embedding backend this index was built with, e.g. `local:minilm-l6-v2:384`
|
|
21
|
-
* or `openai:1536`. This record is *authoritative*: it governs which backend
|
|
22
|
-
* `lat search` uses. Null when the index has never been built.
|
|
23
|
-
*/
|
|
75
|
+
await db.execute('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)');
|
|
76
|
+
}
|
|
24
77
|
export async function getStoredModel(db) {
|
|
25
|
-
const rows = await db.execute("SELECT value FROM meta WHERE key
|
|
78
|
+
const rows = await db.execute("SELECT value FROM meta WHERE key='embedding_model'");
|
|
26
79
|
return rows.rows[0]?.value ?? null;
|
|
27
80
|
}
|
|
28
81
|
export async function setStoredModel(db, value) {
|
|
29
82
|
await db.execute({
|
|
30
|
-
sql:
|
|
31
|
-
args: [value],
|
|
83
|
+
sql: 'INSERT OR REPLACE INTO meta VALUES (?,?)',
|
|
84
|
+
args: ['embedding_model', value],
|
|
32
85
|
});
|
|
33
86
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
87
|
+
export async function ensureSectionsSchema(db, _dimensions) {
|
|
88
|
+
for (const sql of [
|
|
89
|
+
'CREATE TABLE IF NOT EXISTS sections (id TEXT PRIMARY KEY, file TEXT NOT NULL, heading TEXT NOT NULL, content TEXT NOT NULL, content_hash TEXT NOT NULL, parent_id TEXT, start_line INTEGER, end_line INTEGER)',
|
|
90
|
+
'CREATE TABLE IF NOT EXISTS embeddings (hash TEXT PRIMARY KEY, embedding BLOB NOT NULL)',
|
|
91
|
+
'CREATE TABLE IF NOT EXISTS chunks (id INTEGER PRIMARY KEY, source_id TEXT UNIQUE NOT NULL, section_id TEXT NOT NULL, ordinal INTEGER NOT NULL, type TEXT NOT NULL, spans TEXT NOT NULL, body TEXT NOT NULL, heading TEXT NOT NULL, path TEXT NOT NULL, input_hash TEXT NOT NULL)',
|
|
92
|
+
'CREATE INDEX IF NOT EXISTS chunks_section ON chunks(section_id)',
|
|
93
|
+
'CREATE TABLE IF NOT EXISTS lexical_chunks (id INTEGER PRIMARY KEY, body TEXT NOT NULL, heading TEXT NOT NULL, path TEXT NOT NULL)',
|
|
94
|
+
'CREATE TABLE IF NOT EXISTS identifiers (token TEXT NOT NULL, chunk_id INTEGER NOT NULL, PRIMARY KEY(token,chunk_id))',
|
|
95
|
+
])
|
|
96
|
+
await db.execute(sql);
|
|
97
|
+
const oldIndex = (await db.execute("SELECT tbl_name FROM sqlite_master WHERE name='chunks_fts'")).rows[0];
|
|
98
|
+
if (oldIndex?.tbl_name === 'chunks')
|
|
99
|
+
await db.execute('DROP INDEX chunks_fts');
|
|
100
|
+
await db.execute(CREATE_PASSAGE_FTS);
|
|
101
|
+
}
|
|
49
102
|
export async function dropSections(db) {
|
|
50
|
-
|
|
103
|
+
for (const name of [
|
|
104
|
+
'identifiers',
|
|
105
|
+
'lexical_chunks',
|
|
106
|
+
'chunks',
|
|
107
|
+
'embeddings',
|
|
108
|
+
'sections',
|
|
109
|
+
])
|
|
110
|
+
await db.execute(`DROP TABLE IF EXISTS ${name}`);
|
|
51
111
|
}
|
|
52
112
|
export async function closeDb(db) {
|
|
53
|
-
db.close();
|
|
113
|
+
await db.close();
|
|
54
114
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EmbeddingAuthError, type Embedder } from '@lat.md/embed';
|
|
2
2
|
export type { Embedder };
|
|
3
3
|
export { EmbeddingAuthError };
|
|
4
|
+
export type CreateSearchEngine = (key?: string) => Promise<Embedder>;
|
|
4
5
|
/** `meta.embedding_model` value for an embedder, e.g. `local:minilm-l6-v2:384`. */
|
|
5
6
|
export declare function modelKey(embedder: Embedder): string;
|
|
6
7
|
/** Thrown when the stored index can't be served by the current environment and
|
|
@@ -22,4 +23,4 @@ export declare function localEmbedder(): Promise<Embedder>;
|
|
|
22
23
|
* regenerable `.cache` was wiped) the durable per-repo preference wins over the
|
|
23
24
|
* env, so a repo switched to local stays local across cache loss.
|
|
24
25
|
*/
|
|
25
|
-
export declare function embedderForIndex(storedModel: string | null, latDir: string): Promise<Embedder>;
|
|
26
|
+
export declare function embedderForIndex(storedModel: string | null, latDir: string, createSearchEngine?: CreateSearchEngine): Promise<Embedder>;
|