@plebeiantech/lat.md 0.12.3-fork.13 → 0.12.3-fork.15

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.
Files changed (278) hide show
  1. package/README.md +24 -0
  2. package/dist/src/cli/check-context.d.ts +56 -0
  3. package/dist/src/cli/check-context.js +215 -0
  4. package/dist/src/cli/check-coverage.js +9 -5
  5. package/dist/src/cli/check-frontmatter.js +1 -1
  6. package/dist/src/cli/check-mode.js +1 -1
  7. package/dist/src/cli/check-status.js +1 -1
  8. package/dist/src/cli/check.d.ts +19 -12
  9. package/dist/src/cli/check.js +259 -206
  10. package/dist/src/cli/context.js +1 -1
  11. package/dist/src/cli/expand.d.ts +1 -2
  12. package/dist/src/cli/expand.js +52 -5
  13. package/dist/src/cli/external.d.ts +11 -0
  14. package/dist/src/cli/external.js +185 -0
  15. package/dist/src/cli/fork-instructions.d.ts +1 -0
  16. package/dist/src/cli/fork-instructions.js +3 -2
  17. package/dist/src/cli/hook.d.ts +26 -0
  18. package/dist/src/cli/hook.js +112 -40
  19. package/dist/src/cli/index.js +55 -3
  20. package/dist/src/cli/init.d.ts +1 -0
  21. package/dist/src/cli/init.js +40 -2
  22. package/dist/src/cli/locate.js +4 -3
  23. package/dist/src/cli/refs.d.ts +2 -1
  24. package/dist/src/cli/refs.js +88 -43
  25. package/dist/src/cli/reindex.js +2 -1
  26. package/dist/src/cli/search.d.ts +12 -3
  27. package/dist/src/cli/search.js +45 -49
  28. package/dist/src/cli/section.d.ts +3 -1
  29. package/dist/src/cli/section.js +114 -38
  30. package/dist/src/code-refs.d.ts +17 -5
  31. package/dist/src/code-refs.js +246 -65
  32. package/dist/src/context.d.ts +3 -0
  33. package/dist/src/document-formats.d.ts +7 -0
  34. package/dist/src/document-formats.js +39 -0
  35. package/dist/src/extensions/alert-marker.d.ts +25 -0
  36. package/dist/src/extensions/alert-marker.js +55 -0
  37. package/dist/src/external-documents.d.ts +56 -0
  38. package/dist/src/external-documents.js +404 -0
  39. package/dist/src/external-sources.d.ts +136 -0
  40. package/dist/src/external-sources.js +1098 -0
  41. package/dist/src/fork/antigravity-hook.d.ts +47 -0
  42. package/dist/src/fork/antigravity-hook.js +424 -0
  43. package/dist/src/fork/antigravity-init.d.ts +17 -0
  44. package/dist/src/fork/antigravity-init.js +124 -0
  45. package/dist/src/fork/frontmatter-fields.d.ts +36 -0
  46. package/dist/src/fork/frontmatter-fields.js +23 -0
  47. package/dist/src/format.d.ts +5 -2
  48. package/dist/src/format.js +8 -3
  49. package/dist/src/graph-export.js +1 -1
  50. package/dist/src/lattice-model.d.ts +77 -0
  51. package/dist/src/lattice-model.js +464 -0
  52. package/dist/src/lattice.d.ts +9 -104
  53. package/dist/src/lattice.js +69 -516
  54. package/dist/src/markdown-analysis-cache.d.ts +20 -0
  55. package/dist/src/markdown-analysis-cache.js +139 -0
  56. package/dist/src/markdown-analysis-worker.d.ts +16 -0
  57. package/dist/src/markdown-analysis-worker.js +33 -0
  58. package/dist/src/markdown-analysis.d.ts +50 -0
  59. package/dist/src/markdown-analysis.js +139 -0
  60. package/dist/src/markdown-analyzer-loader.d.ts +4 -0
  61. package/dist/src/markdown-analyzer-loader.js +13 -0
  62. package/dist/src/markdown-validation.d.ts +25 -0
  63. package/dist/src/markdown-validation.js +104 -0
  64. package/dist/src/mcp/server.js +27 -11
  65. package/dist/src/parser-cache.d.ts +17 -0
  66. package/dist/src/parser-cache.js +65 -0
  67. package/dist/src/parser-import.d.ts +8 -0
  68. package/dist/src/parser-import.js +1 -0
  69. package/dist/src/parser.js +80 -5
  70. package/dist/src/path.d.ts +2 -0
  71. package/dist/src/path.js +4 -0
  72. package/dist/src/profiler.d.ts +16 -0
  73. package/dist/src/profiler.js +81 -0
  74. package/dist/src/project-analysis.d.ts +59 -0
  75. package/dist/src/project-analysis.js +233 -0
  76. package/dist/src/project-discovery.d.ts +6 -0
  77. package/dist/src/project-discovery.js +30 -0
  78. package/dist/src/repository-path.d.ts +11 -0
  79. package/dist/src/repository-path.js +52 -0
  80. package/dist/src/search/db.d.ts +1 -1
  81. package/dist/src/search/db.js +2 -2
  82. package/dist/src/search/embedder.d.ts +2 -1
  83. package/dist/src/search/embedder.js +6 -5
  84. package/dist/src/search/index.d.ts +2 -1
  85. package/dist/src/search/index.js +10 -8
  86. package/dist/src/search/query.d.ts +20 -0
  87. package/dist/src/search/query.js +67 -0
  88. package/dist/src/search/search.d.ts +3 -1
  89. package/dist/src/search/search.js +5 -2
  90. package/dist/src/source-formats.d.ts +7 -0
  91. package/dist/src/source-formats.js +26 -0
  92. package/dist/src/source-parser.d.ts +44 -4
  93. package/dist/src/source-parser.js +494 -57
  94. package/dist/src/view/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  95. package/dist/src/view/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  96. package/dist/src/view/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  97. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  98. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  99. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  100. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  101. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  102. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  103. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  104. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  105. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  106. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  107. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  108. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  109. package/dist/src/view/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  110. package/dist/src/view/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  111. package/dist/src/view/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  112. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  113. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  114. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  115. package/dist/src/view/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  116. package/dist/src/view/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  117. package/dist/src/view/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  118. package/dist/src/view/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  119. package/dist/src/view/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  120. package/dist/src/view/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  121. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  122. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  123. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  124. package/dist/src/view/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  125. package/dist/src/view/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  126. package/dist/src/view/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  127. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  128. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  129. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  130. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  131. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  132. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  133. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  134. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  135. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  136. package/dist/src/view/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  137. package/dist/src/view/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  138. package/dist/src/view/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  139. package/dist/src/view/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  140. package/dist/src/view/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  141. package/dist/src/view/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  142. package/dist/src/view/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  143. package/dist/src/view/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  144. package/dist/src/view/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  145. package/dist/src/view/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  146. package/dist/src/view/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  147. package/dist/src/view/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  148. package/dist/src/view/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  149. package/dist/src/view/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  150. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  151. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  152. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  153. package/dist/src/view/client/assets/MarkdownEditor-B7quqPb8.js +28 -0
  154. package/dist/src/view/client/assets/OrbitControls-DwcpQNja.js +1 -0
  155. package/dist/src/view/client/assets/STLLoader-CLWTKt5i.js +1 -0
  156. package/dist/src/view/client/assets/abnfDiagram-VCTEODGH-V7-T6qsl.js +1 -0
  157. package/dist/src/view/client/assets/arc-BnwR8Bu7.js +1 -0
  158. package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-u97vDna9.js +36 -0
  159. package/dist/src/view/client/assets/blockDiagram-I7D4REHJ-DETl8e_H.js +129 -0
  160. package/dist/src/view/client/assets/c4Diagram-7LVT6UL2-DJRAT1Ve.js +38 -0
  161. package/dist/src/view/client/assets/channel-BwQbnWCN.js +1 -0
  162. package/dist/src/view/client/assets/chunk-2Q5K7J3B-C0TmOC1Q.js +1 -0
  163. package/dist/src/view/client/assets/chunk-5VM5RSS4-dfBOrYE6.js +15 -0
  164. package/dist/src/view/client/assets/chunk-F27PBJKO-DAnks99K.js +1 -0
  165. package/dist/src/view/client/assets/chunk-IMKFNOWR-CPjB07if.js +231 -0
  166. package/dist/src/view/client/assets/chunk-JWPE2WC7-VvU_gZI6.js +1 -0
  167. package/dist/src/view/client/assets/chunk-POPQ4Y6H-DDUbVF55.js +1 -0
  168. package/dist/src/view/client/assets/chunk-SVP7TREG-BHROkr6D.js +88 -0
  169. package/dist/src/view/client/assets/chunk-TICWLB2K-95PzGIOg.js +206 -0
  170. package/dist/src/view/client/assets/chunk-XXDRQBXY-MdmuLhIs.js +1 -0
  171. package/dist/src/view/client/assets/classDiagram-ZZMXUADV-HvQKkIS-.js +1 -0
  172. package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-HvQKkIS-.js +1 -0
  173. package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-CauUf_rN.js +1 -0
  174. package/dist/src/view/client/assets/cynefin-OW5HDTMX-CeA8IKWo.js +166 -0
  175. package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-L1rtcT8l.js +62 -0
  176. package/dist/src/view/client/assets/cytoscape.esm-DzSz-X2X.js +321 -0
  177. package/dist/src/view/client/assets/dagre-GXQ25YYZ-DLQwTa3H.js +4 -0
  178. package/dist/src/view/client/assets/defaultLocale-DX6XiGOO.js +1 -0
  179. package/dist/src/view/client/assets/diagram-S7CK7UJ4-kbKdIyXG.js +30 -0
  180. package/dist/src/view/client/assets/diagram-UQ7AKVKN-kmzp92RC.js +41 -0
  181. package/dist/src/view/client/assets/diagram-VSXAHHWV-EXJTU8qq.js +3 -0
  182. package/dist/src/view/client/assets/diagram-VX7I27RA-DAiIyMCG.js +24 -0
  183. package/dist/src/view/client/assets/diagram-Z3DM3KII-G9wz50ZV.js +24 -0
  184. package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-D2tVRCNA.js +1 -0
  185. package/dist/src/view/client/assets/erDiagram-RLTQ6QDP-C925J5A3.js +99 -0
  186. package/dist/src/view/client/assets/flowDiagram-HODETNUW-DzoCa-Nq.js +168 -0
  187. package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-D2ebbg4p.js +292 -0
  188. package/dist/src/view/client/assets/gitGraphDiagram-WWUBYQGX-B4QratYy.js +106 -0
  189. package/dist/src/view/client/assets/index-BnENo9bV.js +1 -0
  190. package/dist/src/view/client/assets/index-D0ajr_Ja.css +1 -0
  191. package/dist/src/view/client/assets/index-KZcnpXJH.js +321 -0
  192. package/dist/src/view/client/assets/infoDiagram-27XIBGKW-DL4mtHkt.js +2 -0
  193. package/dist/src/view/client/assets/init-Gi6I4Gst.js +1 -0
  194. package/dist/src/view/client/assets/ishikawaDiagram-5VMMS53U-CW4mto2d.js +70 -0
  195. package/dist/src/view/client/assets/journeyDiagram-3NMN7TZE-8R4eacFW.js +139 -0
  196. package/dist/src/view/client/assets/kanban-definition-UXKFOSKX-Cy9ioXEJ.js +89 -0
  197. package/dist/src/view/client/assets/katex-HP8lGamR.js +257 -0
  198. package/dist/src/view/client/assets/layout-C0MFuhth.js +1 -0
  199. package/dist/src/view/client/assets/linear-Dv3N4dQR.js +1 -0
  200. package/dist/src/view/client/assets/logo-small-RR_KBOAT.svg +149 -0
  201. package/dist/src/view/client/assets/maplibre-gl-DnqZgfUS.js +800 -0
  202. package/dist/src/view/client/assets/mermaid.core-bds9A7e2.js +310 -0
  203. package/dist/src/view/client/assets/mindmap-definition-YA3MSWOX-DjmGW355.js +96 -0
  204. package/dist/src/view/client/assets/ordinal-Cboi1Yqb.js +1 -0
  205. package/dist/src/view/client/assets/pegDiagram-XKGWAZYB-BWsZp0ab.js +1 -0
  206. package/dist/src/view/client/assets/pieDiagram-E7YTZNPT-CCH2gGR-.js +39 -0
  207. package/dist/src/view/client/assets/quadrantDiagram-AXDQQJYC-CjocPJFz.js +7 -0
  208. package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-BjLeb2KF.js +1 -0
  209. package/dist/src/view/client/assets/requirementDiagram-BXWQKSXE-Ct6AoXRr.js +84 -0
  210. package/dist/src/view/client/assets/sankeyDiagram-P5KCCOFB-CIpgGN_w.js +40 -0
  211. package/dist/src/view/client/assets/sequenceDiagram-WJ2MYXX4-DrmLFj6w.js +162 -0
  212. package/dist/src/view/client/assets/sizeCapture-INFHLROL-BdFMe2Lr.js +1 -0
  213. package/dist/src/view/client/assets/stateDiagram-D77RDMKH-Zd9F1goW.js +1 -0
  214. package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-ByIKCftl.js +1 -0
  215. package/dist/src/view/client/assets/swimlanes-42K2YHIH-a7VFwi00.js +1 -0
  216. package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-CzxaUHB-.js +8 -0
  217. package/dist/src/view/client/assets/three.module-PDSP0dbZ.js +4108 -0
  218. package/dist/src/view/client/assets/timeline-definition-24CTP7MA-BExPu6X5.js +120 -0
  219. package/dist/src/view/client/assets/vennDiagram-4TSXK5OY-PC1MU3si.js +34 -0
  220. package/dist/src/view/client/assets/wardleyDiagram-VM6X3IG4-DNWqU3fa.js +78 -0
  221. package/dist/src/view/client/assets/xychartDiagram-S5SC5T6Z-E1uA28_D.js +7 -0
  222. package/dist/src/view/client/index.html +2 -2
  223. package/dist/src/view/client-shell.d.ts +2 -0
  224. package/dist/src/view/client-shell.js +5 -0
  225. package/dist/src/view/diagnostics.d.ts +3 -2
  226. package/dist/src/view/diagnostics.js +49 -58
  227. package/dist/src/view/document-edit.d.ts +8 -0
  228. package/dist/src/view/document-edit.js +25 -0
  229. package/dist/src/view/document-route.d.ts +12 -0
  230. package/dist/src/view/document-route.js +93 -0
  231. package/dist/src/view/document-tree.d.ts +18 -0
  232. package/dist/src/view/document-tree.js +139 -0
  233. package/dist/src/view/external-document-tree.d.ts +3 -0
  234. package/dist/src/view/external-document-tree.js +674 -0
  235. package/dist/src/view/git-diff.js +187 -0
  236. package/dist/src/view/git.js +1 -1
  237. package/dist/src/view/graph.d.ts +3 -2
  238. package/dist/src/view/graph.js +94 -7
  239. package/dist/src/view/highlight.d.ts +10 -2
  240. package/dist/src/view/highlight.js +4540 -1360
  241. package/dist/src/view/markdown.d.ts +3 -2
  242. package/dist/src/view/markdown.js +214 -53
  243. package/dist/src/view/protocol.d.ts +59 -6
  244. package/dist/src/view/references.d.ts +10 -25
  245. package/dist/src/view/references.js +117 -107
  246. package/dist/src/view/repository.d.ts +13 -3
  247. package/dist/src/view/repository.js +234 -15
  248. package/dist/src/view/search.d.ts +1 -1
  249. package/dist/src/view/search.js +3 -7
  250. package/dist/src/view/server.d.ts +11 -2
  251. package/dist/src/view/server.js +212 -70
  252. package/dist/src/view/source-target.js +5 -8
  253. package/dist/src/view/static-build.d.ts +1 -0
  254. package/dist/src/view/static-build.js +245 -42
  255. package/dist/src/view/static-protocol.d.ts +19 -2
  256. package/dist/src/view/store.d.ts +16 -3
  257. package/dist/src/view/store.js +228 -36
  258. package/dist/src/view/table-of-contents.d.ts +2 -2
  259. package/dist/src/view/table-of-contents.js +2 -22
  260. package/dist/src/walk.d.ts +5 -3
  261. package/dist/src/walk.js +102 -8
  262. package/dist/vendor/lat.md-server/cli.d.ts +2 -0
  263. package/dist/vendor/lat.md-server/cli.js +12 -0
  264. package/dist/vendor/lat.md-server/index.d.ts +38 -0
  265. package/dist/vendor/lat.md-server/index.js +128 -0
  266. package/package.json +52 -12
  267. package/plugins/lat-md/README.md +61 -0
  268. package/plugins/lat-md/hooks.json +38 -0
  269. package/plugins/lat-md/plugin.json +9 -0
  270. package/plugins/lat-md/rules/AGENTS.md +118 -0
  271. package/plugins/lat-md/skills/lat-md/SKILL.md +178 -0
  272. package/plugins/lat-md/skills/lat-md-conventions/SKILL.md +182 -0
  273. package/templates/AGENTS.md +15 -3
  274. package/templates/cursor-rules.md +12 -3
  275. package/templates/skill/SKILL.md +22 -1
  276. package/dist/src/view/client/assets/index-C3iaZyYT.css +0 -1
  277. package/dist/src/view/client/assets/index-KWi67IMz.js +0 -334
  278. package/dist/src/view/client/assets/logo-CNF8zs47.svg +0 -182
@@ -8,6 +8,7 @@ import { dirname, join } from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { Command, InvalidArgumentError } from 'commander';
10
10
  import { resolveCheckContext, resolveContext } from './context.js';
11
+ import { DEFAULT_SEARCH_LIMIT, DEFAULT_SEARCH_THRESHOLD, } from '../search/search.js';
11
12
  function parsePort(value) {
12
13
  if (!/^\d+$/.test(value)) {
13
14
  throw new InvalidArgumentError('port must be an integer from 1 to 65535');
@@ -18,6 +19,13 @@ function parsePort(value) {
18
19
  }
19
20
  return port;
20
21
  }
22
+ function parseSimilarityThreshold(value) {
23
+ const threshold = Number(value);
24
+ if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {
25
+ throw new InvalidArgumentError('threshold must be a number from 0 to 1');
26
+ }
27
+ return threshold;
28
+ }
21
29
  /** Reserve `-- <directory>` for an explicit check target. */
22
30
  function splitCheckTarget(args) {
23
31
  let commandIndex = -1;
@@ -138,15 +146,53 @@ program
138
146
  const { refsCommand } = await import('./refs.js');
139
147
  handleResult(await refsCommand(ctx, query, scope));
140
148
  });
149
+ const external = program
150
+ .command('external')
151
+ .description('Manage pinned external source repositories');
152
+ external
153
+ .command('add')
154
+ .argument('[handle]', 'stable external source handle')
155
+ .argument('[repo]', 'canonical HTTPS Git repository')
156
+ .option('--commit <commit-or-ref>', 'commit, branch, or tag to pin')
157
+ .option('--prefix <path>', 'repository path prefix')
158
+ .option('--default-file-extension <extension>', 'extension for external paths that omit one')
159
+ .option('--strategy <strategy>', 'retrieval strategy: fetch or checkout')
160
+ .option('--fetch-url <template>', 'raw-file URL template')
161
+ .action(async (handle, repo, opts) => {
162
+ const ctx = resolveContext(program.opts());
163
+ const { externalAddCommand } = await import('./external.js');
164
+ handleResult(await externalAddCommand(ctx, handle, repo, opts));
165
+ });
166
+ external
167
+ .command('show')
168
+ .argument('<source>', 'handle or exact external target')
169
+ .option('--json', 'emit structured JSON')
170
+ .action(async (source, opts) => {
171
+ const ctx = resolveContext(program.opts());
172
+ const { externalShowCommand } = await import('./external.js');
173
+ handleResult(await externalShowCommand(ctx, source, !!opts.json));
174
+ });
175
+ external
176
+ .command('list')
177
+ .option('--json', 'emit structured JSON')
178
+ .action(async (opts) => {
179
+ const ctx = resolveContext(program.opts());
180
+ const { externalListCommand } = await import('./external.js');
181
+ handleResult(await externalListCommand(ctx, !!opts.json));
182
+ });
141
183
  const check = program
142
184
  .command('check')
143
185
  .usage('[subcommand] [-- <directory>]')
144
186
  .description('Validate markdown, links, code references, and structure')
145
187
  .option('--fix', 'generate/update directory index files from frontmatter')
188
+ .option('--profile', 'show detailed validation timing')
146
189
  .action(async (opts) => {
147
190
  const ctx = resolveCheckContext(program.opts(), checkTargetArgs.target);
148
191
  const { checkAllCommand } = await import('./check.js');
149
- handleResult(await checkAllCommand(ctx, { fix: opts.fix }));
192
+ handleResult(await checkAllCommand(ctx, {
193
+ fix: opts.fix,
194
+ profile: !!opts.profile,
195
+ }));
150
196
  });
151
197
  check
152
198
  .command('md')
@@ -258,12 +304,18 @@ program
258
304
  .command('search')
259
305
  .description('Semantic search across lat.md sections')
260
306
  .argument('[query]', 'search query in plain English')
261
- .option('--limit <n>', 'max results', '5')
307
+ .option('--limit <n>', `max results (default: ${DEFAULT_SEARCH_LIMIT})`, String(DEFAULT_SEARCH_LIMIT))
308
+ .option('--debug', 'show result similarity scores')
309
+ .option('--threshold <score>', `minimum cosine similarity score (default: ${DEFAULT_SEARCH_THRESHOLD})`, parseSimilarityThreshold)
262
310
  .action(async (query, opts) => {
263
311
  const ctx = resolveContext(program.opts());
264
312
  const { searchCommand, cliProgress } = await import('./search.js');
265
313
  const progress = cliProgress(ctx.styler);
266
- const result = await searchCommand(ctx, query, { limit: parseInt(opts.limit) }, progress);
314
+ const result = await searchCommand(ctx, query, {
315
+ limit: parseInt(opts.limit),
316
+ debug: opts.debug,
317
+ threshold: opts.threshold,
318
+ }, progress);
267
319
  handleResult(result);
268
320
  });
269
321
  program
@@ -5,5 +5,6 @@ type LatCommandStyle = 'global' | 'local' | 'npx';
5
5
  */
6
6
  export declare function syncLatHooks(settingsPath: string, style: LatCommandStyle, agent?: 'claude' | 'codex'): void;
7
7
  export declare function readLogo(): string;
8
+ export declare function ensureLatLocalConfigIgnored(latDir: string): void;
8
9
  export declare function initCmd(targetDir?: string): Promise<void>;
9
10
  export {};
@@ -17,6 +17,7 @@ import { selectMenu } from './select-menu.js';
17
17
  import { checklistMenu } from './checklist-menu.js';
18
18
  import { writeForkInstructions } from './fork-instructions.js';
19
19
  import { offerRequireMode, writeForkScaffold } from './fork-scaffold.js';
20
+ import { setupAntigravity } from '../fork/antigravity-init.js';
20
21
  async function confirm(rl, message) {
21
22
  while (true) {
22
23
  let answer;
@@ -819,6 +820,7 @@ function printNextSteps(selectedAgents) {
819
820
  pi: 'Pi',
820
821
  opencode: 'OpenCode',
821
822
  codex: 'Codex',
823
+ antigravity: 'Antigravity',
822
824
  };
823
825
  if (!hasClaudeCode && ideAgents.length === 0)
824
826
  return;
@@ -841,6 +843,15 @@ function printNextSteps(selectedAgents) {
841
843
  export function readLogo() {
842
844
  return readFileSync(join(findTemplatesDir(), 'logo.txt'), 'utf-8');
843
845
  }
846
+ export function ensureLatLocalConfigIgnored(latDir) {
847
+ const path = join(latDir, '.gitignore');
848
+ const entry = 'config.local.yaml';
849
+ const current = existsSync(path) ? readFileSync(path, 'utf8') : '';
850
+ if (current.split(/\r?\n/).includes(entry))
851
+ return;
852
+ const prefix = current && !current.endsWith('\n') ? `${current}\n` : current;
853
+ writeFileSync(path, `${prefix}${entry}\n`);
854
+ }
844
855
  export async function initCmd(targetDir) {
845
856
  console.log(styleText('cyan', readLogo()));
846
857
  // Upfront version check — let the user upgrade before proceeding
@@ -904,6 +915,7 @@ export async function initCmd(targetDir) {
904
915
  console.log(styleText('green', 'Created lat.md/'));
905
916
  writeForkScaffold(latDir);
906
917
  }
918
+ ensureLatLocalConfigIgnored(latDir);
907
919
  // Step 2: Configure fresh/outdated setups, ask interactive users about an
908
920
  // available key, and offer to rebuild an index whose backend differs. This
909
921
  // happens before agent selection so "no agents" still completes it.
@@ -917,6 +929,7 @@ export async function initCmd(targetDir) {
917
929
  { label: 'VS Code Copilot', value: 'copilot' },
918
930
  { label: 'OpenCode', value: 'opencode' },
919
931
  { label: 'Codex', value: 'codex' },
932
+ { label: 'Antigravity / Gemini', value: 'antigravity' },
920
933
  ];
921
934
  const selectedAgents = await checklistMenu(allAgents, 'Which coding agents do you use?');
922
935
  const useClaudeCode = selectedAgents.includes('claude');
@@ -925,13 +938,15 @@ export async function initCmd(targetDir) {
925
938
  const useCopilot = selectedAgents.includes('copilot');
926
939
  const useOpenCode = selectedAgents.includes('opencode');
927
940
  const useCodex = selectedAgents.includes('codex');
941
+ const useAntigravity = selectedAgents.includes('antigravity');
928
942
  const anySelected = selectedAgents.length > 0;
929
943
  const needsLatCommand = useClaudeCode ||
930
944
  usePi ||
931
945
  useCursor ||
932
946
  useCopilot ||
933
947
  useOpenCode ||
934
- useCodex;
948
+ useCodex ||
949
+ useAntigravity;
935
950
  // Step 4: How should agents run lat?
936
951
  let commandStyle = 'local';
937
952
  if (anySelected && needsLatCommand && interactive) {
@@ -973,7 +988,12 @@ export async function initCmd(targetDir) {
973
988
  const template = readAgentsTemplate();
974
989
  const fileHashes = {};
975
990
  // Step 5: AGENTS.md (shared by non-Claude agents)
976
- const needsAgentsMd = usePi || useCursor || useCopilot || useOpenCode || useCodex;
991
+ const needsAgentsMd = usePi ||
992
+ useCursor ||
993
+ useCopilot ||
994
+ useOpenCode ||
995
+ useCodex ||
996
+ useAntigravity;
977
997
  if (needsAgentsMd) {
978
998
  await setupAgentsMd(root, latDir, template, fileHashes, ask);
979
999
  }
@@ -1008,6 +1028,24 @@ export async function initCmd(targetDir) {
1008
1028
  console.log(styleText('bold', 'Setting up Codex...'));
1009
1029
  await setupCodex(root, latDir, fileHashes, ask, commandStyle);
1010
1030
  }
1031
+ if (useAntigravity) {
1032
+ console.log('');
1033
+ console.log(styleText('bold', 'Setting up Antigravity / Gemini...'));
1034
+ await setupAntigravity({
1035
+ root,
1036
+ latDir,
1037
+ template,
1038
+ hashes: fileHashes,
1039
+ ask,
1040
+ style: commandStyle,
1041
+ latBin: latBinString(commandStyle),
1042
+ appendTemplateSection,
1043
+ writeTemplateFile,
1044
+ ensureGitignored,
1045
+ hasMcpServer,
1046
+ addMcpServer,
1047
+ });
1048
+ }
1011
1049
  await writeForkInstructions(root, latDir, fileHashes, ask);
1012
1050
  // Record init version and file hashes so `lat check` can detect stale setups
1013
1051
  writeInitMeta(latDir, fileHashes);
@@ -1,9 +1,10 @@
1
- import { loadAllSections, findSections } from '../lattice.js';
1
+ import { findSections } from '../lattice-model.js';
2
2
  import { formatResultList } from '../format.js';
3
+ import { commandProjectAnalysis } from '../project-analysis.js';
3
4
  export async function locateCommand(ctx, query) {
4
5
  const stripped = query.replace(/^\[\[|\]\]$/g, '');
5
- const sections = await loadAllSections(ctx.latDir);
6
- const matches = findSections(sections, stripped);
6
+ const project = await commandProjectAnalysis(ctx);
7
+ const matches = findSections(project.allSections, stripped);
7
8
  if (matches.length === 0) {
8
9
  const s = ctx.styler;
9
10
  return {
@@ -1,4 +1,4 @@
1
- import { type Section, type SectionMatch } from '../lattice.js';
1
+ import { type Section, type SectionMatch } from '../lattice-model.js';
2
2
  import type { CmdContext, CmdResult } from '../context.js';
3
3
  export type Scope = 'md' | 'code' | 'md+code';
4
4
  export type RefsFound = {
@@ -10,6 +10,7 @@ export type RefsFound = {
10
10
  export type RefsError = {
11
11
  kind: 'no-match';
12
12
  suggestions: SectionMatch[];
13
+ message?: string;
13
14
  };
14
15
  export type RefsResult = RefsFound | RefsError;
15
16
  /**
@@ -1,21 +1,9 @@
1
- import { readFile } from 'node:fs/promises';
2
1
  import { existsSync } from 'node:fs';
3
2
  import { extname, join, relative } from 'node:path';
4
- import { listLatticeFiles, loadAllSections, findSections, extractRefs, flattenSections, buildFileIndex, buildSectionSlugIndex, resolveRef, } from '../lattice.js';
3
+ import { findSections, resolveRef, } from '../lattice-model.js';
5
4
  import { formatResultList } from '../format.js';
6
- import { scanCodeRefs } from '../code-refs.js';
7
- /** Extensions recognized as source code for ref queries. */
8
- const SOURCE_EXTS = new Set([
9
- '.ts',
10
- '.tsx',
11
- '.js',
12
- '.jsx',
13
- '.py',
14
- '.rs',
15
- '.go',
16
- '.c',
17
- '.h',
18
- ]);
5
+ import { commandProjectAnalysis, commandProjectSession, } from '../project-analysis.js';
6
+ import { isSourceFileExtension } from '../source-formats.js';
19
7
  /**
20
8
  * Check if a query looks like a source file path (has a recognized extension
21
9
  * and the file exists on disk).
@@ -25,7 +13,7 @@ function isSourceQuery(query, projectRoot) {
25
13
  const filePart = hashIdx === -1 ? query : query.slice(0, hashIdx);
26
14
  const symbolPart = hashIdx === -1 ? '' : query.slice(hashIdx + 1);
27
15
  const ext = extname(filePart);
28
- if (!SOURCE_EXTS.has(ext))
16
+ if (!isSourceFileExtension(ext))
29
17
  return null;
30
18
  if (!existsSync(join(projectRoot, filePart)))
31
19
  return null;
@@ -36,7 +24,8 @@ function isSourceQuery(query, projectRoot) {
36
24
  * For file-level queries (no #symbol), matches all wiki links targeting
37
25
  * that file or any symbol in it.
38
26
  */
39
- async function findSourceRefs(latDir, projectRoot, query, scope) {
27
+ async function findSourceRefs(ctx, query, scope) {
28
+ const { projectRoot } = ctx;
40
29
  const hashIdx = query.indexOf('#');
41
30
  const filePart = hashIdx === -1 ? query : query.slice(0, hashIdx);
42
31
  const isFileLevel = hashIdx === -1;
@@ -59,7 +48,7 @@ async function findSourceRefs(latDir, projectRoot, query, scope) {
59
48
  const { resolveSourceSymbol } = await import('../source-parser.js');
60
49
  if (hashIdx !== -1) {
61
50
  const symbolPart = query.slice(hashIdx + 1);
62
- const { found, symbols } = await resolveSourceSymbol(filePart, symbolPart, projectRoot);
51
+ const { found, symbols } = await resolveSourceSymbol(filePart, symbolPart, projectRoot, commandProjectSession(ctx).sourceSymbolOptions());
63
52
  if (found) {
64
53
  const parts = symbolPart.split('#');
65
54
  const sym = symbols.find((s) => parts.length === 1
@@ -76,17 +65,14 @@ async function findSourceRefs(latDir, projectRoot, query, scope) {
76
65
  catch {
77
66
  // source parser unavailable — proceed without line info
78
67
  }
79
- const allSections = await loadAllSections(latDir);
80
- const flat = flattenSections(allSections);
68
+ const project = await commandProjectAnalysis(ctx);
69
+ const flat = project.sections;
81
70
  const mdRefs = [];
82
71
  const codeRefs = [];
83
72
  if (scope === 'md' || scope === 'md+code') {
84
- const files = await listLatticeFiles(latDir);
85
73
  const matchingFromSections = new Set();
86
- for (const file of files) {
87
- const content = await readFile(file, 'utf-8');
88
- const fileRefs = extractRefs(file, content, projectRoot);
89
- for (const ref of fileRefs) {
74
+ for (const file of project.files.values()) {
75
+ for (const ref of file.wikiRefs) {
90
76
  const targetLower = ref.target.toLowerCase();
91
77
  const matches = isFileLevel
92
78
  ? targetLower === fileLower || targetLower.startsWith(fileLower + '#')
@@ -104,7 +90,7 @@ async function findSourceRefs(latDir, projectRoot, query, scope) {
104
90
  }
105
91
  }
106
92
  if (scope === 'code' || scope === 'md+code') {
107
- const { refs: scannedRefs } = await scanCodeRefs(projectRoot);
93
+ const { refs: scannedRefs } = await commandProjectSession(ctx).codeRefs();
108
94
  for (const ref of scannedRefs) {
109
95
  const targetLower = ref.target.toLowerCase();
110
96
  const matches = isFileLevel
@@ -118,6 +104,56 @@ async function findSourceRefs(latDir, projectRoot, query, scope) {
118
104
  }
119
105
  return { kind: 'found', target, mdRefs, codeRefs };
120
106
  }
107
+ async function findExternalRefs(ctx, query, scope) {
108
+ const external = await commandProjectSession(ctx).external();
109
+ const parsed = external.parse(query);
110
+ const target = {
111
+ id: parsed.identity,
112
+ heading: parsed.fragment || parsed.authoredPath,
113
+ depth: 0,
114
+ file: parsed.identity,
115
+ filePath: parsed.identity,
116
+ children: [],
117
+ startLine: 0,
118
+ endLine: 0,
119
+ firstParagraph: '',
120
+ };
121
+ const project = await commandProjectAnalysis(ctx);
122
+ const flat = project.sections;
123
+ const matchingSections = new Set();
124
+ const codeRefs = [];
125
+ const matchesTarget = (candidate) => {
126
+ try {
127
+ return external.parse(candidate)?.identity === parsed.identity;
128
+ }
129
+ catch {
130
+ return false;
131
+ }
132
+ };
133
+ if (scope !== 'code') {
134
+ for (const file of project.files.values()) {
135
+ for (const ref of file.wikiRefs) {
136
+ if (matchesTarget(ref.target))
137
+ matchingSections.add(ref.fromSection.toLowerCase());
138
+ }
139
+ }
140
+ }
141
+ if (scope !== 'md') {
142
+ for (const ref of (await commandProjectSession(ctx).codeRefs()).refs) {
143
+ if (matchesTarget(ref.target)) {
144
+ codeRefs.push(`${relative(process.cwd(), join(ctx.projectRoot, ref.file))}:${ref.line}`);
145
+ }
146
+ }
147
+ }
148
+ return {
149
+ kind: 'found',
150
+ target,
151
+ mdRefs: flat
152
+ .filter((section) => matchingSections.has(section.id.toLowerCase()))
153
+ .map((section) => ({ section, reason: 'wiki link' })),
154
+ codeRefs,
155
+ };
156
+ }
121
157
  /**
122
158
  * Find all sections and code locations that reference a given section or
123
159
  * source file. Accepts section ids (full-path, short-form) and source file
@@ -126,15 +162,29 @@ async function findSourceRefs(latDir, projectRoot, query, scope) {
126
162
  */
127
163
  export async function findRefs(ctx, query, scope) {
128
164
  query = query.replace(/^\[\[|\]\]$/g, '');
165
+ const external = await commandProjectSession(ctx).external();
166
+ try {
167
+ if (external.parse(query))
168
+ return findExternalRefs(ctx, query, scope);
169
+ }
170
+ catch (error) {
171
+ return {
172
+ kind: 'no-match',
173
+ suggestions: [],
174
+ message: error.message,
175
+ };
176
+ }
177
+ const unknownExternal = external.unknownTargetMessage(query);
178
+ if (unknownExternal) {
179
+ return { kind: 'no-match', suggestions: [], message: unknownExternal };
180
+ }
129
181
  // Source file queries bypass section resolution
130
182
  if (isSourceQuery(query, ctx.projectRoot)) {
131
- return findSourceRefs(ctx.latDir, ctx.projectRoot, query, scope);
183
+ return findSourceRefs(ctx, query, scope);
132
184
  }
133
- const allSections = await loadAllSections(ctx.latDir);
134
- const flat = flattenSections(allSections);
135
- const sectionIds = new Set(flat.map((s) => s.id.toLowerCase()));
136
- const fileIndex = buildFileIndex(allSections);
137
- const slugIndex = buildSectionSlugIndex(allSections);
185
+ const project = await commandProjectAnalysis(ctx);
186
+ const { allSections, sections: flat, fileIndex, slugIndex } = project;
187
+ const sectionIds = new Set(project.sectionIds);
138
188
  const { resolved } = resolveRef(query, sectionIds, fileIndex, slugIndex);
139
189
  const q = resolved.toLowerCase();
140
190
  let exactMatch = flat.find((s) => s.id.toLowerCase() === q);
@@ -157,17 +207,9 @@ export async function findRefs(ctx, query, scope) {
157
207
  const mdRefs = [];
158
208
  const codeRefs = [];
159
209
  if (scope === 'md' || scope === 'md+code') {
160
- const files = await listLatticeFiles(ctx.latDir);
161
210
  const matchingFromSections = new Set();
162
- for (const file of files) {
163
- const content = await readFile(file, 'utf-8');
164
- const fileRefs = extractRefs(file, content, ctx.projectRoot);
165
- for (const ref of fileRefs) {
166
- const { resolved: refResolved } = resolveRef(ref.target, sectionIds, fileIndex, slugIndex);
167
- if (refResolved.toLowerCase() === targetId) {
168
- matchingFromSections.add(ref.fromSection.toLowerCase());
169
- }
170
- }
211
+ for (const ref of project.incomingRefsBySection.get(targetId) ?? []) {
212
+ matchingFromSections.add(ref.fromSection.toLowerCase());
171
213
  }
172
214
  if (matchingFromSections.size > 0) {
173
215
  const referrers = flat.filter((s) => matchingFromSections.has(s.id.toLowerCase()));
@@ -177,7 +219,7 @@ export async function findRefs(ctx, query, scope) {
177
219
  }
178
220
  }
179
221
  if (scope === 'code' || scope === 'md+code') {
180
- const { refs: scannedRefs } = await scanCodeRefs(ctx.projectRoot);
222
+ const { refs: scannedRefs } = await commandProjectSession(ctx).codeRefs();
181
223
  for (const ref of scannedRefs) {
182
224
  const { resolved: codeResolved } = resolveRef(ref.target, sectionIds, fileIndex, slugIndex);
183
225
  if (codeResolved.toLowerCase() === targetId) {
@@ -192,6 +234,9 @@ export async function refsCommand(ctx, query, scope) {
192
234
  const result = await findRefs(ctx, query, scope);
193
235
  if (result.kind === 'no-match') {
194
236
  const s = ctx.styler;
237
+ if (result.message) {
238
+ return { output: s.red(result.message), isError: true };
239
+ }
195
240
  if (result.suggestions.length > 0) {
196
241
  const suggestions = result.suggestions
197
242
  .map((m) => ` ${s.dim('*')} ${s.white(m.section.id)} ${s.dim(`(${m.reason})`)}`)
@@ -3,6 +3,7 @@ import { openDb, ensureMeta, setStoredModel, ensureSectionsSchema, dropSections,
3
3
  import { embedderFromEnv, localEmbedder, modelKey, EmbeddingAuthError, } from '../search/embedder.js';
4
4
  import { getLlmKey, getRepoEmbedding, setRepoEmbedding } from '../config.js';
5
5
  import { indexSections } from '../search/index.js';
6
+ import { commandProjectAnalysis } from '../project-analysis.js';
6
7
  const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
7
8
  async function confirmUseLocal(ctx, status, assumeYes) {
8
9
  const s = ctx.styler;
@@ -106,7 +107,7 @@ export async function reindexCommand(ctx, opts) {
106
107
  process.stderr.write(s.dim(label + '…\n'));
107
108
  let stats;
108
109
  try {
109
- stats = await indexSections(ctx.latDir, db, embedder, onProgress);
110
+ stats = await indexSections(ctx.latDir, db, embedder, onProgress, await commandProjectAnalysis(ctx));
110
111
  // Pin the backend only after a successful build, so a failed reindex never
111
112
  // records a model that doesn't match a completed index.
112
113
  await setStoredModel(db, modelKey(embedder));
@@ -1,6 +1,8 @@
1
1
  import type { CmdContext, CmdResult, Styler } from '../context.js';
2
2
  import { type IndexStats } from '../search/index.js';
3
- import { type Section, type SectionMatch } from '../lattice.js';
3
+ import type { SectionMatch } from '../lattice-model.js';
4
+ import type { Section } from '../lattice-model.js';
5
+ import { type MarkdownProjectAnalysis } from '../project-analysis.js';
4
6
  export type SearchResult = {
5
7
  query: string;
6
8
  matches: SectionMatch[];
@@ -23,14 +25,21 @@ export type IndexProgress = {
23
25
  */
24
26
  export declare function runSearch(latDir: string, query: string, limit: number, progress?: IndexProgress, opts?: {
25
27
  buildIndex?: boolean;
26
- preloadedSections?: Section[];
28
+ project?: MarkdownProjectAnalysis;
29
+ sectionById?: ReadonlyMap<string, Section>;
30
+ threshold?: number;
31
+ cacheDir?: string;
27
32
  }): Promise<SearchResult>;
28
33
  /**
29
34
  * Index-only mode (no query) — builds the index on first use. Rebuilding is
30
35
  * `lat reindex`, not a flag here.
31
36
  */
32
- export declare function runIndex(latDir: string, progress?: IndexProgress): Promise<void>;
37
+ export declare function runIndex(latDir: string, progress?: IndexProgress, analyzedProject?: MarkdownProjectAnalysis, options?: {
38
+ cacheDir?: string;
39
+ }): Promise<void>;
33
40
  export declare function cliProgress(s: Styler): IndexProgress;
34
41
  export declare function searchCommand(ctx: CmdContext, query: string | undefined, opts: {
35
42
  limit: number;
43
+ debug?: boolean;
44
+ threshold?: number;
36
45
  }, progress?: IndexProgress): Promise<CmdResult>;
@@ -1,15 +1,16 @@
1
1
  import { readFile } from 'node:fs/promises';
2
- import { join } from 'node:path';
2
+ import { dirname, join } from 'node:path';
3
3
  import { openDb, ensureMeta, getStoredModel, setStoredModel, ensureSectionsSchema, dropSections, closeDb, } from '../search/db.js';
4
4
  import { getLlmKey } from '../config.js';
5
5
  import { embedderForIndex, modelKey, ReindexRequiredError, EmbeddingAuthError, } from '../search/embedder.js';
6
6
  import { indexSections } from '../search/index.js';
7
7
  import { searchSections } from '../search/search.js';
8
- import { loadAllSections, flattenSections, } from '../lattice.js';
8
+ import { searchIndexedSections } from '../search/query.js';
9
+ import { analyzeMarkdownProject, commandProjectAnalysis, } from '../project-analysis.js';
9
10
  import { formatSectionPreview, formatNavHints } from '../format.js';
10
11
  import { provenanceNote, formatProvenanceNote } from './check-status.js';
11
- async function withDb(latDir, progress, fn) {
12
- const db = openDb(latDir);
12
+ async function withDb(latDir, progress, project, cacheDir, fn) {
13
+ const db = openDb(latDir, cacheDir);
13
14
  try {
14
15
  await ensureMeta(db);
15
16
  const stored = await getStoredModel(db);
@@ -46,7 +47,7 @@ async function withDb(latDir, progress, fn) {
46
47
  }
47
48
  progress?.beforeIndex?.(isEmpty);
48
49
  try {
49
- const stats = await indexSections(latDir, db, embedder);
50
+ const stats = await indexSections(latDir, db, embedder, undefined, project);
50
51
  // Pin the backend only after a successful index, so a failed build never
51
52
  // leaves the repo wrongly pinned to an empty index.
52
53
  if (!stored)
@@ -60,26 +61,12 @@ async function withDb(latDir, progress, fn) {
60
61
  await dropSections(db);
61
62
  throw err;
62
63
  }
63
- return await fn(db, embedder);
64
+ return await fn(db, embedder, project);
64
65
  }
65
66
  finally {
66
67
  await closeDb(db);
67
68
  }
68
69
  }
69
- /** Resolve raw search hits (by id) to full section matches. */
70
- async function resolveMatches(latDir, results, preloadedSections) {
71
- if (results.length === 0)
72
- return [];
73
- const allSections = preloadedSections ?? (await loadAllSections(latDir));
74
- const flat = flattenSections(allSections);
75
- const byId = new Map(flat.map((s) => [s.id, s]));
76
- return results.flatMap((result) => {
77
- const section = byId.get(result.id);
78
- return section
79
- ? [{ section, reason: 'semantic match', score: result.score }]
80
- : [];
81
- });
82
- }
83
70
  /**
84
71
  * Run a semantic search across lat.md sections.
85
72
  * Handles indexing (with optional progress callback). Returns matched sections.
@@ -92,31 +79,30 @@ async function resolveMatches(latDir, results, preloadedSections) {
92
79
  */
93
80
  export async function runSearch(latDir, query, limit, progress, opts) {
94
81
  if (opts?.buildIndex === false) {
95
- const db = openDb(latDir);
96
- try {
97
- await ensureMeta(db);
98
- const stored = await getStoredModel(db);
99
- // Never built (or a legacy pre-versioning cache) — leave building to
100
- // `lat search`; don't load the embedder just to embed the query.
101
- if (stored === null)
102
- return { query, matches: [] };
103
- const embedder = await embedderForIndex(stored, latDir);
104
- await ensureSectionsSchema(db, embedder.dimensions);
105
- const results = await searchSections(db, query, embedder, limit);
106
- return {
107
- query,
108
- matches: await resolveMatches(latDir, results, opts?.preloadedSections),
109
- };
110
- }
111
- finally {
112
- await closeDb(db);
113
- }
82
+ const sectionById = opts.sectionById ??
83
+ opts.project?.sectionById ??
84
+ (await analyzeMarkdownProject(latDir, dirname(latDir), {
85
+ executor: 'auto',
86
+ })).sectionById;
87
+ return searchIndexedSections(latDir, query, limit, sectionById, {
88
+ cacheDir: opts.cacheDir,
89
+ threshold: opts.threshold,
90
+ });
114
91
  }
115
- return withDb(latDir, progress, async (db, embedder) => {
116
- const results = await searchSections(db, query, embedder, limit);
92
+ const project = opts?.project ??
93
+ (await analyzeMarkdownProject(latDir, dirname(latDir), {
94
+ executor: 'auto',
95
+ }));
96
+ return withDb(latDir, progress, project, opts?.cacheDir, async (db, embedder, analyzed) => {
97
+ const results = await searchSections(db, query, embedder, limit, opts?.threshold);
117
98
  return {
118
99
  query,
119
- matches: await resolveMatches(latDir, results, opts?.preloadedSections),
100
+ matches: results.flatMap((result) => {
101
+ const section = analyzed.sectionById.get(result.id.toLowerCase());
102
+ return section
103
+ ? [{ section, reason: 'semantic match', score: result.score }]
104
+ : [];
105
+ }),
120
106
  };
121
107
  });
122
108
  }
@@ -124,8 +110,12 @@ export async function runSearch(latDir, query, limit, progress, opts) {
124
110
  * Index-only mode (no query) — builds the index on first use. Rebuilding is
125
111
  * `lat reindex`, not a flag here.
126
112
  */
127
- export async function runIndex(latDir, progress) {
128
- await withDb(latDir, progress, async () => { });
113
+ export async function runIndex(latDir, progress, analyzedProject, options = {}) {
114
+ const project = analyzedProject ??
115
+ (await analyzeMarkdownProject(latDir, dirname(latDir), {
116
+ executor: 'auto',
117
+ }));
118
+ await withDb(latDir, progress, project, options.cacheDir, async () => { });
129
119
  }
130
120
  export function cliProgress(s) {
131
121
  return {
@@ -153,7 +143,7 @@ export function cliProgress(s) {
153
143
  * say whether a person ever checked that claim. Frontmatter is read once per
154
144
  * file rather than once per match, since several matches routinely share one.
155
145
  */
156
- async function formatSearchMatches(ctx, query, matches) {
146
+ async function formatSearchMatches(ctx, query, matches, opts) {
157
147
  const notes = new Map();
158
148
  for (const match of matches) {
159
149
  const filePath = match.section.filePath;
@@ -173,6 +163,7 @@ async function formatSearchMatches(ctx, query, matches) {
173
163
  lines.push('');
174
164
  lines.push(formatSectionPreview(ctx, matches[i].section, {
175
165
  reason: matches[i].reason,
166
+ score: opts?.showScores ? matches[i].score : undefined,
176
167
  }));
177
168
  const note = notes.get(matches[i].section.filePath);
178
169
  if (note)
@@ -185,16 +176,21 @@ export async function searchCommand(ctx, query, opts, progress) {
185
176
  const s = ctx.styler;
186
177
  try {
187
178
  if (!query) {
188
- await runIndex(ctx.latDir, progress);
179
+ await runIndex(ctx.latDir, progress, await commandProjectAnalysis(ctx));
189
180
  return { output: '' };
190
181
  }
191
- const result = await runSearch(ctx.latDir, query, opts.limit, progress);
182
+ const project = await commandProjectAnalysis(ctx);
183
+ const result = await runSearch(ctx.latDir, query, opts.limit, progress, {
184
+ project,
185
+ threshold: opts.threshold,
186
+ });
192
187
  if (result.matches.length === 0) {
193
188
  return { output: 'No results found.' };
194
189
  }
195
190
  return {
196
- output: (await formatSearchMatches(ctx, query, result.matches)) +
197
- formatNavHints(ctx),
191
+ output: (await formatSearchMatches(ctx, query, result.matches, {
192
+ showScores: opts.debug,
193
+ })) + formatNavHints(ctx),
198
194
  };
199
195
  }
200
196
  catch (err) {