@plebeiantech/lat.md 0.12.3-fork.8 → 0.13.4-fork.1

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 (330) hide show
  1. package/README.md +27 -3
  2. package/dist/src/cli/agent-invocation.d.ts +6 -0
  3. package/dist/src/cli/agent-invocation.js +23 -0
  4. package/dist/src/cli/checklist-menu.d.ts +2 -1
  5. package/dist/src/cli/checklist-menu.js +3 -2
  6. package/dist/src/cli/fork-instructions.d.ts +1 -0
  7. package/dist/src/cli/fork-instructions.js +8 -32
  8. package/dist/src/cli/fork-scaffold.js +2 -2
  9. package/dist/src/cli/graph.d.ts +1 -1
  10. package/dist/src/cli/hook.d.ts +26 -0
  11. package/dist/src/cli/hook.js +115 -64
  12. package/dist/src/cli/index.js +147 -70
  13. package/dist/src/cli/init-preferences.d.ts +4 -0
  14. package/dist/src/cli/init-preferences.js +36 -0
  15. package/dist/src/cli/init.d.ts +2 -1
  16. package/dist/src/cli/init.js +100 -60
  17. package/dist/src/cli/reindex.d.ts +1 -1
  18. package/dist/src/cli/reindex.js +7 -10
  19. package/dist/src/cli/search.d.ts +14 -4
  20. package/dist/src/cli/search.js +115 -101
  21. package/dist/src/cli/ui-build-server.d.ts +12 -0
  22. package/dist/src/cli/ui-build-server.js +26 -0
  23. package/dist/src/cli/ui-build.d.ts +1 -1
  24. package/dist/src/cli/ui-build.js +1 -1
  25. package/dist/src/cli/ui.d.ts +1 -1
  26. package/dist/src/cli/ui.js +1 -1
  27. package/dist/src/fork/antigravity-hook.d.ts +47 -0
  28. package/dist/src/fork/antigravity-hook.js +424 -0
  29. package/dist/src/fork/antigravity-init.d.ts +17 -0
  30. package/dist/src/fork/antigravity-init.js +124 -0
  31. package/dist/src/fork/upstream-drift.js +1 -1
  32. package/dist/src/fork/upstream-guard.d.ts +1 -1
  33. package/dist/src/fork/upstream-guard.js +8 -7
  34. package/dist/src/graph-export.js +7 -7
  35. package/dist/src/knowledge/index.js +2 -2
  36. package/dist/src/mcp/server.js +34 -18
  37. package/dist/src/search/cache.d.ts +3 -0
  38. package/dist/src/search/cache.js +104 -0
  39. package/dist/src/search/chunks.d.ts +23 -0
  40. package/dist/src/search/chunks.js +200 -0
  41. package/dist/src/search/db.d.ts +28 -16
  42. package/dist/src/search/db.js +100 -40
  43. package/dist/src/search/embedder.d.ts +2 -1
  44. package/dist/src/search/embedder.js +7 -6
  45. package/dist/src/search/index.d.ts +6 -2
  46. package/dist/src/search/index.js +176 -53
  47. package/dist/src/search/lexical.d.ts +6 -0
  48. package/dist/src/search/lexical.js +36 -0
  49. package/dist/src/search/lock.d.ts +5 -0
  50. package/dist/src/search/lock.js +45 -0
  51. package/dist/src/search/query.d.ts +26 -0
  52. package/dist/src/search/query.js +103 -0
  53. package/dist/src/search/search.d.ts +16 -7
  54. package/dist/src/search/search.js +165 -21
  55. package/dist/src/search/types.d.ts +15 -0
  56. package/dist/src/view/client/assets/GraphView-D_xnk0bP.js +456 -0
  57. package/dist/src/view/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  58. package/dist/src/view/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  59. package/dist/src/view/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  60. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  61. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  62. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  63. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  64. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  65. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  66. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  67. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  68. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  69. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  70. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  71. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  72. package/dist/src/view/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  73. package/dist/src/view/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  74. package/dist/src/view/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  75. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  76. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  77. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  78. package/dist/src/view/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  79. package/dist/src/view/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  80. package/dist/src/view/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  81. package/dist/src/view/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  82. package/dist/src/view/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  83. package/dist/src/view/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  84. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  85. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  86. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  87. package/dist/src/view/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  88. package/dist/src/view/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  89. package/dist/src/view/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  90. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  91. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  92. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  93. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  94. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  95. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  96. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  97. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  98. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  99. package/dist/src/view/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  100. package/dist/src/view/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  101. package/dist/src/view/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  102. package/dist/src/view/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  103. package/dist/src/view/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  104. package/dist/src/view/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  105. package/dist/src/view/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  106. package/dist/src/view/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  107. package/dist/src/view/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  108. package/dist/src/view/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  109. package/dist/src/view/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  110. package/dist/src/view/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  111. package/dist/src/view/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  112. package/dist/src/view/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  113. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  114. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  115. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  116. package/dist/src/view/client/assets/MarkdownEditor-bjqt_FoF.js +28 -0
  117. package/dist/src/view/client/assets/OrbitControls-DwcpQNja.js +1 -0
  118. package/dist/src/view/client/assets/STLLoader-CLWTKt5i.js +1 -0
  119. package/dist/src/view/client/assets/_commonjsHelpers-CqkleIqs.js +1 -0
  120. package/dist/src/view/client/assets/abnfDiagram-VCTEODGH-kWGvtRnL.js +1 -0
  121. package/dist/src/view/client/assets/arc-BPAFd7cP.js +1 -0
  122. package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-CsY39oIR.js +36 -0
  123. package/dist/src/view/client/assets/blockDiagram-I7D4REHJ-DHGBG7BJ.js +129 -0
  124. package/dist/src/view/client/assets/c4Diagram-7LVT6UL2-vywK4eb7.js +38 -0
  125. package/dist/src/view/client/assets/channel-CX71L_qV.js +1 -0
  126. package/dist/src/view/client/assets/chunk-2Q5K7J3B-Do3Hz_ci.js +1 -0
  127. package/dist/src/view/client/assets/chunk-5VM5RSS4-B5R1lSeY.js +15 -0
  128. package/dist/src/view/client/assets/chunk-F27PBJKO-Dm55FM_W.js +1 -0
  129. package/dist/src/view/client/assets/chunk-IMKFNOWR-ChptrvvM.js +231 -0
  130. package/dist/src/view/client/assets/chunk-JWPE2WC7-BRQBjrKS.js +1 -0
  131. package/dist/src/view/client/assets/chunk-POPQ4Y6H-CainVZIL.js +1 -0
  132. package/dist/src/view/client/assets/chunk-SVP7TREG-DsKsNGmM.js +88 -0
  133. package/dist/src/view/client/assets/chunk-TICWLB2K-BctkMcJc.js +206 -0
  134. package/dist/src/view/client/assets/chunk-XXDRQBXY-BN7SgOUT.js +1 -0
  135. package/dist/src/view/client/assets/classDiagram-ZZMXUADV-B1VyU6zO.js +1 -0
  136. package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-B1VyU6zO.js +1 -0
  137. package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-BszzwrMe.js +1 -0
  138. package/dist/src/view/client/assets/cynefin-OW5HDTMX-CdRyAkPs.js +166 -0
  139. package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-BLvKPZ1u.js +62 -0
  140. package/dist/src/view/client/assets/cytoscape.esm-DzSz-X2X.js +321 -0
  141. package/dist/src/view/client/assets/dagre-GXQ25YYZ-V68w_l65.js +4 -0
  142. package/dist/src/view/client/assets/defaultLocale-DX6XiGOO.js +1 -0
  143. package/dist/src/view/client/assets/diagram-S7CK7UJ4-C2i4eEMg.js +30 -0
  144. package/dist/src/view/client/assets/diagram-UQ7AKVKN-BiwWh5jC.js +41 -0
  145. package/dist/src/view/client/assets/diagram-VSXAHHWV-hkKR_vyS.js +3 -0
  146. package/dist/src/view/client/assets/diagram-VX7I27RA-DzRCSMAc.js +24 -0
  147. package/dist/src/view/client/assets/diagram-Z3DM3KII-fhSFPxyK.js +24 -0
  148. package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-CCEUDKSA.js +1 -0
  149. package/dist/src/view/client/assets/erDiagram-RLTQ6QDP-FJ2IMyTi.js +99 -0
  150. package/dist/src/view/client/assets/flowDiagram-HODETNUW-BvasdLeK.js +168 -0
  151. package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-CiwkBZvY.js +292 -0
  152. package/dist/src/view/client/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  153. package/dist/src/view/client/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  154. package/dist/src/view/client/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  155. package/dist/src/view/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  156. package/dist/src/view/client/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  157. package/dist/src/view/client/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  158. package/dist/src/view/client/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  159. package/dist/src/view/client/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  160. package/dist/src/view/client/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  161. package/dist/src/view/client/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  162. package/dist/src/view/client/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  163. package/dist/src/view/client/assets/gitGraphDiagram-WWUBYQGX-DYlP2_js.js +106 -0
  164. package/dist/src/view/client/assets/index-BnENo9bV.js +1 -0
  165. package/dist/src/view/client/assets/index-CQ7EOox7.css +1 -0
  166. package/dist/src/view/client/assets/index-CynNDkxt.js +13 -0
  167. package/dist/src/view/client/assets/infoDiagram-27XIBGKW-DRPB2c48.js +2 -0
  168. package/dist/src/view/client/assets/init-Gi6I4Gst.js +1 -0
  169. package/dist/src/view/client/assets/ishikawaDiagram-5VMMS53U-8VV429Cv.js +70 -0
  170. package/dist/src/view/client/assets/journeyDiagram-3NMN7TZE-Dzmwnf83.js +139 -0
  171. package/dist/src/view/client/assets/kanban-definition-UXKFOSKX-pYyZZ1i-.js +89 -0
  172. package/dist/src/view/client/assets/katex-HP8lGamR.js +257 -0
  173. package/dist/src/view/client/assets/layout-GF9k6yGo.js +1 -0
  174. package/dist/src/view/client/assets/linear-Yt2sSOYV.js +1 -0
  175. package/dist/src/view/client/assets/logo-RR_KBOAT.svg +149 -0
  176. package/dist/src/view/client/assets/maplibre-gl-CDX6Zuow.js +800 -0
  177. package/dist/src/view/client/assets/mermaid.core-ChWYCHN0.js +310 -0
  178. package/dist/src/view/client/assets/mindmap-definition-YA3MSWOX-Cc4fQvf1.js +96 -0
  179. package/dist/src/view/client/assets/ordinal-Cboi1Yqb.js +1 -0
  180. package/dist/src/view/client/assets/pegDiagram-XKGWAZYB-C0SsB8bZ.js +1 -0
  181. package/dist/src/view/client/assets/pieDiagram-E7YTZNPT-DIJpqERZ.js +39 -0
  182. package/dist/src/view/client/assets/quadrantDiagram-AXDQQJYC--d-gl6j-.js +7 -0
  183. package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-BQxwTt7z.js +1 -0
  184. package/dist/src/view/client/assets/requirementDiagram-BXWQKSXE-B_oTeD2A.js +84 -0
  185. package/dist/src/view/client/assets/sankeyDiagram-P5KCCOFB-DLukmE8X.js +40 -0
  186. package/dist/src/view/client/assets/sequenceDiagram-WJ2MYXX4-BNjVB2sc.js +162 -0
  187. package/dist/src/view/client/assets/sizeCapture-INFHLROL-BZecQTOw.js +1 -0
  188. package/dist/src/view/client/assets/stateDiagram-D77RDMKH-Bu1urvXC.js +1 -0
  189. package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-5pc_697G.js +1 -0
  190. package/dist/src/view/client/assets/swimlanes-42K2YHIH-bLAlcepB.js +1 -0
  191. package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-BbOpZjpp.js +8 -0
  192. package/dist/src/view/client/assets/three.module-PDSP0dbZ.js +4108 -0
  193. package/dist/src/view/client/assets/timeline-definition-24CTP7MA-tbwCNvcA.js +120 -0
  194. package/dist/src/view/client/assets/vennDiagram-4TSXK5OY-D8U8Fk0N.js +34 -0
  195. package/dist/src/view/client/assets/wardleyDiagram-VM6X3IG4-LHBVIeNb.js +78 -0
  196. package/dist/src/view/client/assets/xychartDiagram-S5SC5T6Z-CN4tfaFV.js +7 -0
  197. package/dist/src/view/client/index.html +2 -2
  198. package/dist/src/view/client-shell.d.ts +2 -0
  199. package/dist/src/view/client-shell.js +5 -0
  200. package/dist/src/view/diagnostics.d.ts +3 -2
  201. package/dist/src/view/diagnostics.js +49 -58
  202. package/dist/src/view/document-edit.d.ts +8 -0
  203. package/dist/src/view/document-edit.js +25 -0
  204. package/dist/src/view/document-route.d.ts +14 -0
  205. package/dist/src/view/document-route.js +137 -0
  206. package/dist/src/view/document-tree.d.ts +18 -0
  207. package/dist/src/view/document-tree.js +139 -0
  208. package/dist/src/view/external-document-tree.d.ts +3 -0
  209. package/dist/src/view/external-document-tree.js +674 -0
  210. package/dist/src/view/git-diff.js +202 -2
  211. package/dist/src/view/git.js +1 -1
  212. package/dist/src/view/graph.d.ts +4 -3
  213. package/dist/src/view/graph.js +100 -9
  214. package/dist/src/view/highlight.d.ts +10 -2
  215. package/dist/src/view/highlight.js +50648 -3166
  216. package/dist/src/view/markdown.d.ts +3 -2
  217. package/dist/src/view/markdown.js +256 -54
  218. package/dist/src/view/preindexed-search.d.ts +15 -0
  219. package/dist/src/view/preindexed-search.js +25 -0
  220. package/dist/src/view/protocol.d.ts +53 -7
  221. package/dist/src/view/publication.d.ts +2 -0
  222. package/dist/src/view/publication.js +78 -0
  223. package/dist/src/view/references.d.ts +11 -26
  224. package/dist/src/view/references.js +121 -112
  225. package/dist/src/view/repository.d.ts +14 -4
  226. package/dist/src/view/repository.js +260 -19
  227. package/dist/src/view/request-security.d.ts +5 -0
  228. package/dist/src/view/request-security.js +46 -0
  229. package/dist/src/view/search-result.d.ts +4 -0
  230. package/dist/src/view/search-result.js +25 -0
  231. package/dist/src/view/search.d.ts +7 -2
  232. package/dist/src/view/search.js +23 -34
  233. package/dist/src/view/server-build.d.ts +21 -0
  234. package/dist/src/view/server-build.js +145 -0
  235. package/dist/src/view/server-deployment.d.ts +31 -0
  236. package/dist/src/view/server-deployment.js +123 -0
  237. package/dist/src/view/server-index-worker.d.ts +6 -0
  238. package/dist/src/view/server-index-worker.js +14 -0
  239. package/dist/src/view/server.d.ts +11 -2
  240. package/dist/src/view/server.js +245 -73
  241. package/dist/src/view/source-target.d.ts +2 -0
  242. package/dist/src/view/source-target.js +49 -8
  243. package/dist/src/view/static-build.d.ts +18 -2
  244. package/dist/src/view/static-build.js +449 -110
  245. package/dist/src/view/static-protocol.d.ts +26 -2
  246. package/dist/src/view/static-protocol.js +5 -0
  247. package/dist/src/view/store.d.ts +20 -3
  248. package/dist/src/view/store.js +304 -48
  249. package/dist/src/view/table-of-contents.d.ts +2 -2
  250. package/dist/src/view/table-of-contents.js +2 -22
  251. package/dist/src/view/vercel-build.d.ts +24 -0
  252. package/dist/src/view/vercel-build.js +152 -0
  253. package/dist/src/view/vercel-server-build.d.ts +10 -0
  254. package/dist/src/view/vercel-server-build.js +61 -0
  255. package/dist/vendor/lat.md-server/cli.d.ts +2 -0
  256. package/dist/vendor/lat.md-server/cli.js +12 -0
  257. package/dist/vendor/lat.md-server/index.d.ts +38 -0
  258. package/dist/vendor/lat.md-server/index.js +128 -0
  259. package/package.json +68 -22
  260. package/plugins/lat-md/README.md +61 -0
  261. package/plugins/lat-md/hooks.json +38 -0
  262. package/plugins/lat-md/plugin.json +9 -0
  263. package/plugins/lat-md/rules/AGENTS.md +344 -0
  264. package/plugins/lat-md/skills/lat-md/SKILL.md +210 -0
  265. package/plugins/lat-md/skills/lat-md-conventions/SKILL.md +226 -0
  266. package/templates/AGENTS.md +26 -7
  267. package/templates/cursor-rules.md +23 -7
  268. package/templates/fork/conventions.md +133 -11
  269. package/templates/opencode-plugin.ts +9 -9
  270. package/templates/pi-extension.ts +11 -11
  271. package/templates/skill/SKILL.md +35 -3
  272. package/dist/src/cli/check-coverage.d.ts +0 -2
  273. package/dist/src/cli/check-coverage.js +0 -71
  274. package/dist/src/cli/check-frontmatter.d.ts +0 -12
  275. package/dist/src/cli/check-frontmatter.js +0 -44
  276. package/dist/src/cli/check-mode.d.ts +0 -21
  277. package/dist/src/cli/check-mode.js +0 -364
  278. package/dist/src/cli/check-status.d.ts +0 -74
  279. package/dist/src/cli/check-status.js +0 -217
  280. package/dist/src/cli/check.d.ts +0 -45
  281. package/dist/src/cli/check.js +0 -781
  282. package/dist/src/cli/context.d.ts +0 -11
  283. package/dist/src/cli/context.js +0 -58
  284. package/dist/src/cli/expand.d.ts +0 -8
  285. package/dist/src/cli/expand.js +0 -94
  286. package/dist/src/cli/gen-index.d.ts +0 -45
  287. package/dist/src/cli/gen-index.js +0 -235
  288. package/dist/src/cli/link-scheme.d.ts +0 -54
  289. package/dist/src/cli/link-scheme.js +0 -101
  290. package/dist/src/cli/locate.d.ts +0 -2
  291. package/dist/src/cli/locate.js +0 -17
  292. package/dist/src/cli/refs.d.ts +0 -22
  293. package/dist/src/cli/refs.js +0 -229
  294. package/dist/src/cli/section.d.ts +0 -43
  295. package/dist/src/cli/section.js +0 -252
  296. package/dist/src/cli/select-menu.d.ts +0 -13
  297. package/dist/src/cli/select-menu.js +0 -100
  298. package/dist/src/code-refs.d.ts +0 -17
  299. package/dist/src/code-refs.js +0 -298
  300. package/dist/src/config.d.ts +0 -29
  301. package/dist/src/config.js +0 -83
  302. package/dist/src/context.d.ts +0 -23
  303. package/dist/src/context.js +0 -11
  304. package/dist/src/extensions/wiki-link/from-markdown.d.ts +0 -5
  305. package/dist/src/extensions/wiki-link/from-markdown.js +0 -34
  306. package/dist/src/extensions/wiki-link/index.d.ts +0 -4
  307. package/dist/src/extensions/wiki-link/index.js +0 -3
  308. package/dist/src/extensions/wiki-link/syntax.d.ts +0 -14
  309. package/dist/src/extensions/wiki-link/syntax.js +0 -121
  310. package/dist/src/extensions/wiki-link/to-markdown.d.ts +0 -5
  311. package/dist/src/extensions/wiki-link/to-markdown.js +0 -28
  312. package/dist/src/extensions/wiki-link/types.d.ts +0 -34
  313. package/dist/src/format.d.ts +0 -8
  314. package/dist/src/format.js +0 -43
  315. package/dist/src/init-version.d.ts +0 -10
  316. package/dist/src/init-version.js +0 -49
  317. package/dist/src/lattice.d.ts +0 -109
  318. package/dist/src/lattice.js +0 -817
  319. package/dist/src/parser.d.ts +0 -3
  320. package/dist/src/parser.js +0 -18
  321. package/dist/src/source-parser.d.ts +0 -27
  322. package/dist/src/source-parser.js +0 -916
  323. package/dist/src/untrusted.d.ts +0 -18
  324. package/dist/src/untrusted.js +0 -93
  325. package/dist/src/view/client/assets/index-VZwPPiFE.js +0 -334
  326. package/dist/src/view/client/assets/index-W8ktvNj9.css +0 -1
  327. package/dist/src/view/client/assets/logo-CNF8zs47.svg +0 -182
  328. package/dist/src/walk.d.ts +0 -18
  329. package/dist/src/walk.js +0 -27
  330. /package/dist/src/{extensions/wiki-link → search}/types.js +0 -0
@@ -1,781 +0,0 @@
1
- import { readFile, writeFile, lstat, rename, unlink } from 'node:fs/promises';
2
- import { existsSync } from 'node:fs';
3
- import { basename, dirname, extname, join, relative, resolve } from 'node:path';
4
- import { listLatticeFiles, loadAllSections, extractLinks, extractRefs, flattenSections, parseFrontmatter, parseSections, buildFileIndex, buildSectionSlugIndex, resolveRef, } from '../lattice.js';
5
- import { scanCodeRefs } from '../code-refs.js';
6
- import { checkMode } from './check-mode.js';
7
- import { checkStatus } from './check-status.js';
8
- import { checkFrontmatter } from './check-frontmatter.js';
9
- import { checkCoverage } from './check-coverage.js';
10
- import { indexEntryNameFromDest } from './link-scheme.js';
11
- import { fencedLineMask } from './gen-index.js';
12
- import { SOURCE_EXTENSIONS, clearSymbolCache } from '../source-parser.js';
13
- import { toPosix, walkEntries } from '../walk.js';
14
- import { INIT_VERSION, readInitVersion } from '../init-version.js';
15
- function filePart(id) {
16
- const h = id.indexOf('#');
17
- return h === -1 ? id : id.slice(0, h);
18
- }
19
- /** Format an ambiguous-ref error as structured markdown-like text. */
20
- export function ambiguousRefMessage(target, candidates, suggested) {
21
- const shortName = filePart(target);
22
- const fileList = candidates.map((c) => ` - "${filePart(c)}.md"`).join('\n');
23
- const lines = [];
24
- if (suggested) {
25
- lines.push(`ambiguous link '[[${target}]]' — did you mean '[[${suggested}]]'?`);
26
- }
27
- else {
28
- const options = candidates.map((a) => `'[[${a}]]'`).join(', ');
29
- lines.push(`ambiguous link '[[${target}]]' — multiple paths match, use either of: ${options}`);
30
- }
31
- lines.push(` The short path "${shortName}" is ambiguous — ${candidates.length} files match:`, fileList, ` Please fix the link to use a fully qualified path.`);
32
- return lines.join('\n');
33
- }
34
- function countByExt(paths) {
35
- const stats = {};
36
- for (const p of paths) {
37
- const ext = extname(p) || '(no ext)';
38
- stats[ext] = (stats[ext] || 0) + 1;
39
- }
40
- return stats;
41
- }
42
- function isSourcePath(target) {
43
- const hashIdx = target.indexOf('#');
44
- const filePart = hashIdx === -1 ? target : target.slice(0, hashIdx);
45
- const ext = extname(filePart);
46
- return SOURCE_EXTENSIONS.has(ext);
47
- }
48
- /**
49
- * Try resolving a wiki link target as a source code reference.
50
- * Returns null if the reference is valid, or an error message string.
51
- */
52
- export async function sourceRefError(target, projectRoot) {
53
- if (!isSourcePath(target)) {
54
- // Check if it looks like a file path with an unsupported extension
55
- const hashIdx = target.indexOf('#');
56
- const filePart = hashIdx === -1 ? target : target.slice(0, hashIdx);
57
- const ext = extname(filePart);
58
- if (ext && hashIdx !== -1) {
59
- const supported = [...SOURCE_EXTENSIONS].sort().join(', ');
60
- return `broken link [[${target}]] — unsupported file extension "${ext}". Supported: ${supported}`;
61
- }
62
- return `broken link [[${target}]] — no matching section found`;
63
- }
64
- const hashIdx = target.indexOf('#');
65
- const filePart = hashIdx === -1 ? target : target.slice(0, hashIdx);
66
- const symbolPart = hashIdx === -1 ? '' : target.slice(hashIdx + 1);
67
- const absPath = join(projectRoot, filePart);
68
- if (!existsSync(absPath)) {
69
- return `broken link [[${target}]] — file "${filePart}" not found`;
70
- }
71
- if (!symbolPart) {
72
- // File-only link with no symbol — valid as long as file exists
73
- return null;
74
- }
75
- try {
76
- const { resolveSourceSymbol } = await import('../source-parser.js');
77
- const { found, error } = await resolveSourceSymbol(filePart, symbolPart, projectRoot);
78
- if (error) {
79
- return `broken link [[${target}]] — ${error}`;
80
- }
81
- if (!found) {
82
- return `broken link [[${target}]] — symbol "${symbolPart}" not found in "${filePart}"`;
83
- }
84
- return null;
85
- }
86
- catch (err) {
87
- return `broken link [[${target}]] — failed to parse "${filePart}": ${err instanceof Error ? err.message : String(err)}`;
88
- }
89
- }
90
- export async function checkMd(latticeDir, projectRoot = dirname(latticeDir)) {
91
- clearSymbolCache();
92
- const files = await listLatticeFiles(latticeDir);
93
- const allSections = await loadAllSections(latticeDir, projectRoot);
94
- const flat = flattenSections(allSections);
95
- const sectionIds = new Set(flat.map((s) => s.id.toLowerCase()));
96
- const fileIndex = buildFileIndex(allSections);
97
- const slugIndex = buildSectionSlugIndex(allSections);
98
- const errors = [];
99
- for (const file of files) {
100
- const content = await readFile(file, 'utf-8');
101
- const refs = extractRefs(file, content, projectRoot);
102
- const relPath = relative(process.cwd(), file);
103
- for (const ref of refs) {
104
- const { resolved, ambiguous, suggested } = resolveRef(ref.target, sectionIds, fileIndex, slugIndex);
105
- if (ambiguous) {
106
- errors.push({
107
- file: relPath,
108
- line: ref.line,
109
- target: ref.target,
110
- message: ambiguousRefMessage(ref.target, ambiguous, suggested),
111
- });
112
- }
113
- else if (!sectionIds.has(resolved.toLowerCase())) {
114
- // Try resolving as a source code reference (e.g. [[src/foo.ts#bar]])
115
- const sourceErr = await sourceRefError(ref.target, projectRoot);
116
- if (sourceErr !== null) {
117
- errors.push({
118
- file: relPath,
119
- line: ref.line,
120
- target: ref.target,
121
- message: sourceErr,
122
- });
123
- }
124
- }
125
- }
126
- }
127
- return { errors, files: countByExt(files) };
128
- }
129
- function decodeLinkPart(value) {
130
- try {
131
- return decodeURIComponent(value);
132
- }
133
- catch {
134
- return value;
135
- }
136
- }
137
- /** Parse a local markdown destination without confusing escaped #/? in paths. */
138
- function localLinkTarget(url) {
139
- const u = url.trim();
140
- if (u.startsWith('/'))
141
- return null;
142
- const windowsDrivePath = /^[a-zA-Z]:\\/.test(u);
143
- if (!windowsDrivePath && /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(u)) {
144
- return null;
145
- }
146
- // Split before decoding: `%23` and `%3F` decode to `#` and `?`, which would
147
- // then truncate a filename that legitimately contains them.
148
- const queryAt = u.indexOf('?');
149
- const fragmentAt = u.indexOf('#');
150
- const pathEnd = Math.min(queryAt === -1 ? u.length : queryAt, fragmentAt === -1 ? u.length : fragmentAt);
151
- const rawPath = u.slice(0, pathEnd);
152
- const path = rawPath === '' ? null : decodeLinkPart(rawPath);
153
- const fragment = fragmentAt === -1 ? null : decodeLinkPart(u.slice(fragmentAt + 1));
154
- if (rawPath === '' && fragment === null)
155
- return null;
156
- if (path?.includes('\\'))
157
- return { kind: 'invalid-backslash' };
158
- return {
159
- kind: 'target',
160
- path,
161
- fragment,
162
- };
163
- }
164
- export async function checkLinks(latticeDir) {
165
- const files = await listLatticeFiles(latticeDir);
166
- const errors = [];
167
- const headingCache = new Map();
168
- const headingsFor = async (file) => {
169
- const cached = headingCache.get(file);
170
- if (cached)
171
- return cached;
172
- const content = await readFile(file, 'utf-8');
173
- const headings = new Set(flattenSections(parseSections(file, content)).map((section) => section.githubSlug));
174
- headingCache.set(file, headings);
175
- return headings;
176
- };
177
- for (const file of files) {
178
- const content = await readFile(file, 'utf-8');
179
- const relPath = toPosix(relative(process.cwd(), file));
180
- for (const link of extractLinks(content)) {
181
- if ('identifier' in link) {
182
- const kind = link.kind === 'imageReference' ? 'image' : 'link';
183
- errors.push({
184
- file: relPath,
185
- line: link.line,
186
- target: link.identifier,
187
- message: `undefined ${kind} reference (${link.source}) — definition "[${link.identifier}]" not found`,
188
- });
189
- continue;
190
- }
191
- const target = localLinkTarget(link.url);
192
- if (target === null)
193
- continue;
194
- if (target.kind === 'invalid-backslash') {
195
- const kind = link.kind === 'image' ? 'image' : 'link';
196
- errors.push({
197
- file: relPath,
198
- line: link.line,
199
- target: link.url,
200
- message: `invalid ${kind} (${link.url}) — backslashes are not path ` +
201
- 'separators in Markdown; use "/" instead',
202
- });
203
- continue;
204
- }
205
- const abs = target.path ? resolve(dirname(file), target.path) : file;
206
- if (!existsSync(abs)) {
207
- const kind = link.kind === 'image' ? 'image' : 'link';
208
- const shown = toPosix(relative(process.cwd(), abs));
209
- errors.push({
210
- file: relPath,
211
- line: link.line,
212
- target: link.url,
213
- message: `broken ${kind} (${link.url}) — file "${shown}" not found`,
214
- });
215
- continue;
216
- }
217
- if (target.fragment &&
218
- extname(abs).toLowerCase() === '.md' &&
219
- link.kind !== 'image') {
220
- const headings = await headingsFor(abs);
221
- if (!headings.has(target.fragment)) {
222
- const shown = toPosix(relative(process.cwd(), abs));
223
- errors.push({
224
- file: relPath,
225
- line: link.line,
226
- target: link.url,
227
- message: `broken link (${link.url}) — heading "#${target.fragment}" not found in "${shown}"`,
228
- });
229
- }
230
- }
231
- }
232
- }
233
- return errors;
234
- }
235
- export async function checkCodeRefs(latticeDir, projectRoot = dirname(latticeDir)) {
236
- const allSections = await loadAllSections(latticeDir, projectRoot);
237
- const flat = flattenSections(allSections);
238
- const sectionIds = new Set(flat.map((s) => s.id.toLowerCase()));
239
- const fileIndex = buildFileIndex(allSections);
240
- const slugIndex = buildSectionSlugIndex(allSections);
241
- const scan = await scanCodeRefs(projectRoot);
242
- const errors = [];
243
- const mentionedSections = new Set();
244
- for (const ref of scan.refs) {
245
- const { resolved, ambiguous, suggested } = resolveRef(ref.target, sectionIds, fileIndex, slugIndex);
246
- mentionedSections.add(resolved.toLowerCase());
247
- const displayPath = relative(process.cwd(), join(projectRoot, ref.file));
248
- if (ambiguous) {
249
- errors.push({
250
- file: displayPath,
251
- line: ref.line,
252
- target: ref.target,
253
- message: ambiguousRefMessage(ref.target, ambiguous, suggested),
254
- });
255
- }
256
- else if (!sectionIds.has(resolved.toLowerCase())) {
257
- errors.push({
258
- file: displayPath,
259
- line: ref.line,
260
- target: ref.target,
261
- message: `@lat: [[${ref.target}]] — no matching section found`,
262
- });
263
- }
264
- }
265
- const files = await listLatticeFiles(latticeDir);
266
- for (const file of files) {
267
- const content = await readFile(file, 'utf-8');
268
- const fm = parseFrontmatter(content);
269
- if (!fm.requireCodeMention)
270
- continue;
271
- const sections = parseSections(file, content, projectRoot);
272
- const fileSections = flattenSections(sections);
273
- const leafSections = fileSections.filter((s) => s.children.length === 0);
274
- const relPath = relative(process.cwd(), file);
275
- for (const leaf of leafSections) {
276
- if (!mentionedSections.has(leaf.id.toLowerCase())) {
277
- errors.push({
278
- file: relPath,
279
- line: leaf.startLine,
280
- target: leaf.id,
281
- message: `section "${leaf.id}" requires a code mention but none found`,
282
- });
283
- }
284
- }
285
- }
286
- return { errors, files: countByExt(scan.files) };
287
- }
288
- /**
289
- * Extract the immediate (first-level) entries from walkEntries results.
290
- * Returns unique file and directory names visible in a given directory.
291
- */
292
- function immediateEntries(walkedPaths) {
293
- const entries = new Set();
294
- for (const p of walkedPaths) {
295
- const slash = p.indexOf('/');
296
- entries.add(slash === -1 ? p : p.slice(0, slash));
297
- }
298
- return [...entries].sort();
299
- }
300
- /**
301
- * Parse bullet items from an index file. Recognizes both the legacy
302
- * hand-written form (`- [[name]] — description`) and the generated form
303
- * written by `lat check --fix` / `lat check index --fix`
304
- * (`- [Title](name) — summary`) — the two do not collide because the second
305
- * only matches when a `(` immediately follows the closing `]`, which never
306
- * happens in a `[[...]]` wiki link.
307
- */
308
- function parseIndexEntries(content) {
309
- const names = new Set();
310
- const lines = content.split('\n');
311
- const fenced = fencedLineMask(lines);
312
- const unfencedContent = lines.map((l, i) => (fenced[i] ? '' : l)).join('\n');
313
- const wikiRe = /^- \[\[([^\]]+?)(?:\|[^\]]+)?\]\]/gm;
314
- let match;
315
- while ((match = wikiRe.exec(unfencedContent)) !== null) {
316
- names.add(match[1]);
317
- }
318
- // Label allows escaped brackets (`\]`, `\[`) without ending the match early.
319
- const mdRe = /^- \[((?:\\.|[^\]])*)\]\(([^)]*)\)/gm;
320
- while ((match = mdRe.exec(unfencedContent)) !== null) {
321
- const name = indexEntryNameFromDest(match[2]);
322
- if (name !== null)
323
- names.add(entryToStem(name));
324
- }
325
- return names;
326
- }
327
- /**
328
- * Convert a filesystem entry name to its wiki link stem.
329
- * Strips `.md` extension from files; directories stay as-is.
330
- */
331
- function entryToStem(name) {
332
- return name.endsWith('.md') ? name.slice(0, -3) : name;
333
- }
334
- /** Generate a bullet-list snippet for the given entry names. */
335
- function indexSnippet(entries) {
336
- return entries.map((e) => `- [[${entryToStem(e)}]] — <describe>`).join('\n');
337
- }
338
- export async function checkIndex(latticeDir, opts = {}) {
339
- const errors = [];
340
- const allPaths = await walkEntries(latticeDir);
341
- // Flag non-.md files — only markdown belongs in the checked directory.
342
- for (const p of allPaths) {
343
- const name = p.includes('/') ? p.slice(p.lastIndexOf('/') + 1) : p;
344
- if (!name.endsWith('.md')) {
345
- const relDir = basename(latticeDir) + '/';
346
- errors.push({
347
- dir: relDir,
348
- message: `"${p}" is not a .md file — only markdown files belong in the checked directory`,
349
- });
350
- }
351
- }
352
- // Only .md files participate in index validation
353
- const mdPaths = allPaths.filter((p) => p.endsWith('.md'));
354
- // Collect all directories to check (including root, represented as '')
355
- const dirs = new Set(['']);
356
- for (const p of mdPaths) {
357
- const parts = p.split('/');
358
- // Add every directory prefix
359
- for (let i = 1; i < parts.length; i++) {
360
- dirs.add(parts.slice(0, i).join('/'));
361
- }
362
- }
363
- // Deepest directories first: a subdirectory's index must already be
364
- // regenerated before its parent index reads a title/summary from it.
365
- const orderedDirs = [...dirs].sort((a, b) => b.split('/').length - a.split('/').length);
366
- for (const dir of orderedDirs) {
367
- // Determine the index file name and its expected path.
368
- // The index file shares the directory's name — for `lat.md/` it's `lat.md`,
369
- // for a subdir `api/` it's `api.md`.
370
- const dirName = dir === '' ? basename(latticeDir) : dir.split('/').pop();
371
- const indexFileName = dirName.endsWith('.md') ? dirName : dirName + '.md';
372
- const indexRelPath = dir === '' ? indexFileName : dir + '/' + indexFileName;
373
- // Get the immediate children of this directory
374
- const prefix = dir === '' ? '' : dir + '/';
375
- const childPaths = mdPaths
376
- .filter((p) => p.startsWith(prefix) && p !== indexRelPath)
377
- .map((p) => p.slice(prefix.length));
378
- const children = immediateEntries(childPaths);
379
- if (children.length === 0)
380
- continue;
381
- // Check if the index file exists
382
- const indexFullPath = join(latticeDir, indexRelPath);
383
- let content;
384
- try {
385
- content = await readFile(indexFullPath, 'utf-8');
386
- }
387
- catch {
388
- content = null;
389
- }
390
- if (content === null) {
391
- if (opts.fix) {
392
- const refusal = await writeGeneratedIndex(latticeDir, dir, indexFullPath, dirName, null, children);
393
- if (refusal)
394
- errors.push(refusal);
395
- continue;
396
- }
397
- const relDir = dir === '' ? basename(latticeDir) + '/' : dir + '/';
398
- errors.push({
399
- dir: relDir,
400
- message: `missing index file "${indexRelPath}" — create it with a directory listing:\n\n${indexSnippet(children)}`,
401
- snippet: indexSnippet(children),
402
- });
403
- continue;
404
- }
405
- // Parse existing entries and validate.
406
- // Listed entries are wiki link stems (no .md extension).
407
- // Children are filesystem names (with .md for files, bare for dirs).
408
- const listed = parseIndexEntries(content);
409
- const childStems = new Set(children.map(entryToStem));
410
- const relDir = dir === '' ? basename(latticeDir) + '/' : dir + '/';
411
- const missing = [];
412
- for (const child of children) {
413
- if (!listed.has(entryToStem(child))) {
414
- missing.push(child);
415
- }
416
- }
417
- const indexStem = entryToStem(indexFileName);
418
- const stale = [];
419
- for (const name of listed) {
420
- if (!childStems.has(name) && name !== indexStem) {
421
- stale.push(name);
422
- }
423
- }
424
- if (missing.length === 0 && stale.length === 0)
425
- continue;
426
- if (opts.fix) {
427
- const refusal = await writeGeneratedIndex(latticeDir, dir, indexFullPath, dirName, content, children);
428
- if (refusal)
429
- errors.push(refusal);
430
- continue;
431
- }
432
- if (missing.length > 0) {
433
- errors.push({
434
- dir: relDir,
435
- message: `"${indexRelPath}" is missing entries — add:\n\n${indexSnippet(missing)}`,
436
- snippet: indexSnippet(missing),
437
- });
438
- }
439
- for (const name of stale) {
440
- errors.push({
441
- dir: relDir,
442
- message: `"${indexRelPath}" lists "[[${name}]]" but it does not exist`,
443
- });
444
- }
445
- }
446
- return errors;
447
- }
448
- /**
449
- * Build entry sources for a directory's children and write the generated
450
- * index. Returns an IndexError describing a refusal if the index path (or
451
- * the temporary path used to write it) is a symlink; returns null on
452
- * success.
453
- *
454
- * The write never follows a symlink: it writes to a temp file in the same
455
- * directory (created with O_EXCL, so a pre-planted symlink there is refused
456
- * rather than written through) and renames it over the target, since rename
457
- * replaces a symlink rather than following it.
458
- */
459
- async function writeGeneratedIndex(latticeDir, dir, indexFullPath, dirName, existingContent, children) {
460
- const relIndexPath = relative(latticeDir, indexFullPath);
461
- const relDir = dir === '' ? basename(latticeDir) + '/' : dir + '/';
462
- try {
463
- const stat = await lstat(indexFullPath);
464
- if (stat.isSymbolicLink()) {
465
- return {
466
- dir: relDir,
467
- message: `refusing to write generated index "${relIndexPath}" — it is a symlink`,
468
- };
469
- }
470
- }
471
- catch {
472
- // Does not exist yet — fine, we're creating it.
473
- }
474
- const { renderIndexEntries, spliceIndexContent } = await import('./gen-index.js');
475
- const entries = children.map((child) => {
476
- const isDir = !child.endsWith('.md');
477
- const dest = isDir ? `${child}/${child}.md` : child;
478
- const readFrom = isDir
479
- ? join(latticeDir, dir, child, `${child}.md`)
480
- : join(latticeDir, dir, child);
481
- return { name: child, dest, readFrom };
482
- });
483
- const rendered = await renderIndexEntries(entries);
484
- const dirLabel = dirName.endsWith('.md') ? dirName.slice(0, -3) : dirName;
485
- const spliced = spliceIndexContent(existingContent, dirLabel, rendered);
486
- if (!spliced.ok) {
487
- return {
488
- dir: relDir,
489
- message: `refusing to write generated index "${relIndexPath}" — ${spliced.message}`,
490
- };
491
- }
492
- const newContent = spliced.content;
493
- const tempPath = `${indexFullPath}.tmp`;
494
- try {
495
- const tempStat = await lstat(tempPath);
496
- if (tempStat.isSymbolicLink() || tempStat.isFile()) {
497
- // Something is already at the temp path (possibly a pre-planted
498
- // symlink). Refuse rather than write through or over it.
499
- //
500
- // This also catches a leftover temp file from a run killed between the
501
- // write and the rename, which would otherwise block --fix for this
502
- // directory forever. That case is harmless to clear, but the reader
503
- // cannot tell the two apart from here, so say what to do rather than
504
- // deleting a file this command was never asked to remove.
505
- return {
506
- dir: relDir,
507
- message: `refusing to write generated index "${relIndexPath}" — its temporary write path "${relative(latticeDir, tempPath)}" already exists. Inspect it, then delete it to allow --fix to proceed.`,
508
- };
509
- }
510
- }
511
- catch {
512
- // Nothing at the temp path — fine.
513
- }
514
- try {
515
- // 'wx' is the load-bearing flag, not a detail: it fails rather than
516
- // follows if anything appeared at the temp path since the lstat above,
517
- // which closes the window between the two checks.
518
- await writeFile(tempPath, newContent, { encoding: 'utf-8', flag: 'wx' });
519
- }
520
- catch (err) {
521
- return {
522
- dir: relDir,
523
- message: `refusing to write generated index "${relIndexPath}" — could not create temporary write path "${relative(latticeDir, tempPath)}" (${err.message})`,
524
- };
525
- }
526
- try {
527
- await rename(tempPath, indexFullPath);
528
- }
529
- catch (err) {
530
- await unlink(tempPath).catch(() => { });
531
- throw err;
532
- }
533
- return null;
534
- }
535
- // --- Section structure validation ---
536
- /** Max characters for the first paragraph of a section (excluding [[wiki links]]). */
537
- const MAX_BODY_LENGTH = 250;
538
- /** Count body text length excluding `[[...]]` wiki link markers and content. */
539
- function bodyTextLength(body) {
540
- return body.replace(/\[\[[^\]]*\]\]/g, '').length;
541
- }
542
- export async function checkSections(latticeDir, projectRoot = dirname(latticeDir)) {
543
- const files = await listLatticeFiles(latticeDir);
544
- const errors = [];
545
- for (const file of files) {
546
- const content = await readFile(file, 'utf-8');
547
- const sections = parseSections(file, content, projectRoot);
548
- const flat = flattenSections(sections);
549
- const relPath = relative(process.cwd(), file);
550
- for (const section of flat) {
551
- if (!section.firstParagraph) {
552
- errors.push({
553
- file: relPath,
554
- line: section.startLine,
555
- target: section.id,
556
- message: `section "${section.id}" has no leading paragraph. ` +
557
- `Every section must start with a brief overview (≤${MAX_BODY_LENGTH} chars) ` +
558
- `summarizing what it documents — this powers search snippets and command output.`,
559
- });
560
- continue;
561
- }
562
- const len = bodyTextLength(section.firstParagraph);
563
- if (len > MAX_BODY_LENGTH) {
564
- errors.push({
565
- file: relPath,
566
- line: section.startLine,
567
- target: section.id,
568
- message: `section "${section.id}" leading paragraph is ${len} characters ` +
569
- `(max ${MAX_BODY_LENGTH}, excluding [[wiki links]]). ` +
570
- `Keep the first paragraph brief — it serves as the section's summary ` +
571
- `in search results and command output. Use subsequent paragraphs for details.`,
572
- });
573
- }
574
- }
575
- }
576
- return errors;
577
- }
578
- // --- Formatting helpers (shared by all check commands) ---
579
- function formatFileStats(files, s) {
580
- const entries = Object.entries(files).sort(([a], [b]) => a.localeCompare(b));
581
- return s.dim(`Scanned ${entries.map(([ext, n]) => `${n} ${ext}`).join(', ')}`);
582
- }
583
- function formatCheckErrors(errors, s) {
584
- const lines = [];
585
- for (const err of errors) {
586
- lines.push('');
587
- const loc = s.cyan(err.file + ':' + err.line);
588
- const [first, ...rest] = err.message.split('\n');
589
- lines.push(`- ${loc}: ${s.red(first)}`);
590
- for (const line of rest) {
591
- lines.push(` ${s.red(line)}`);
592
- }
593
- }
594
- return lines;
595
- }
596
- function formatCheckIndexErrors(errors, s) {
597
- const lines = [];
598
- for (const err of errors) {
599
- lines.push('');
600
- const loc = s.cyan(err.dir);
601
- const [first, ...rest] = err.message.split('\n');
602
- lines.push(`- ${loc}: ${s.red(first)}`);
603
- for (const line of rest) {
604
- lines.push(` ${s.red(line)}`);
605
- }
606
- }
607
- return lines;
608
- }
609
- function formatErrorCount(count, s) {
610
- return s.red(`\n${count} error${count === 1 ? '' : 's'} found`);
611
- }
612
- // --- Unified command functions ---
613
- export async function checkAllCommand(ctx, opts = {}) {
614
- const startTime = Date.now();
615
- const md = await checkMd(ctx.latDir, ctx.projectRoot);
616
- const linkErrors = await checkLinks(ctx.latDir);
617
- const code = await checkCodeRefs(ctx.latDir, ctx.projectRoot);
618
- // `--fix` is index-only: an index is derived from frontmatter, so it can be
619
- // regenerated. Nothing else `lat check` reports has a mechanical fix.
620
- const indexErrors = await checkIndex(ctx.latDir, opts);
621
- const sectionErrors = await checkSections(ctx.latDir, ctx.projectRoot);
622
- const modeErrors = await checkMode(ctx.latDir, ctx.projectRoot);
623
- const statusErrors = await checkStatus(ctx.latDir, ctx.projectRoot);
624
- const fmErrors = await checkFrontmatter(ctx.latDir, ctx.projectRoot);
625
- const coverageErrors = await checkCoverage(ctx.latDir, ctx.projectRoot);
626
- const elapsed = Date.now() - startTime;
627
- const allErrors = [...md.errors, ...linkErrors, ...code.errors];
628
- const allFiles = { ...md.files };
629
- for (const [ext, n] of Object.entries(code.files)) {
630
- allFiles[ext] = (allFiles[ext] || 0) + n;
631
- }
632
- const s = ctx.styler;
633
- const elapsedStr = elapsed < 1000 ? `${elapsed}ms` : `${(elapsed / 1000).toFixed(1)}s`;
634
- const lines = [
635
- formatFileStats(allFiles, s) + s.dim(` in ${elapsedStr}`),
636
- ];
637
- // Init version warning first — user should fix setup before addressing errors
638
- if (!ctx.headless) {
639
- const storedVersion = readInitVersion(ctx.latDir);
640
- if (storedVersion === null) {
641
- lines.push('', s.yellow('Warning:') +
642
- ' No init version recorded — run ' +
643
- s.cyan('lat init') +
644
- ' to set up agent hooks and configuration.');
645
- }
646
- else if (storedVersion < INIT_VERSION) {
647
- lines.push('', s.yellow('Warning:') +
648
- ' Your setup is outdated (v' +
649
- storedVersion +
650
- ' → v' +
651
- INIT_VERSION +
652
- '). Re-run ' +
653
- s.cyan('lat init') +
654
- ' to update agent hooks and configuration.');
655
- }
656
- }
657
- lines.push(...formatCheckErrors(allErrors, s));
658
- lines.push(...formatCheckIndexErrors(indexErrors, s));
659
- lines.push(...formatCheckErrors(sectionErrors, s));
660
- lines.push(...formatCheckErrors(modeErrors, s));
661
- lines.push(...formatCheckErrors(statusErrors, s));
662
- lines.push(...formatCheckErrors(fmErrors, s));
663
- lines.push(...formatCheckErrors(coverageErrors, s));
664
- const totalErrors = allErrors.length +
665
- indexErrors.length +
666
- sectionErrors.length +
667
- modeErrors.length +
668
- statusErrors.length +
669
- fmErrors.length +
670
- coverageErrors.length;
671
- if (totalErrors > 0) {
672
- lines.push(formatErrorCount(totalErrors, s));
673
- return { output: lines.join('\n'), isError: true };
674
- }
675
- lines.push(s.green('All checks passed'));
676
- // Suggest ripgrep if check was slow (>1s) and rg is not available
677
- if (elapsed > 1000) {
678
- const { hasRipgrep } = await import('../code-refs.js');
679
- if (!(await hasRipgrep())) {
680
- lines.push(s.yellow('Tip:') +
681
- ' Install ' +
682
- s.cyan('ripgrep') +
683
- ' (rg) for faster code scanning.' +
684
- ' See https://github.com/BurntSushi/ripgrep#installation');
685
- }
686
- }
687
- return { output: lines.join('\n') };
688
- }
689
- export async function checkMdCommand(ctx) {
690
- const { errors, files } = await checkMd(ctx.latDir, ctx.projectRoot);
691
- const s = ctx.styler;
692
- const lines = [formatFileStats(files, s)];
693
- lines.push(...formatCheckErrors(errors, s));
694
- if (errors.length > 0) {
695
- lines.push(formatErrorCount(errors.length, s));
696
- return { output: lines.join('\n'), isError: true };
697
- }
698
- lines.push(s.green('md: All links OK'));
699
- return { output: lines.join('\n') };
700
- }
701
- export async function checkLinksCommand(ctx) {
702
- const errors = await checkLinks(ctx.latDir);
703
- const s = ctx.styler;
704
- const lines = [];
705
- lines.push(...formatCheckErrors(errors, s));
706
- if (errors.length > 0) {
707
- lines.push(formatErrorCount(errors.length, s));
708
- return { output: lines.join('\n'), isError: true };
709
- }
710
- lines.push(s.green('links: All relative links resolve'));
711
- return { output: lines.join('\n') };
712
- }
713
- export async function checkCodeRefsCommand(ctx) {
714
- const { errors, files } = await checkCodeRefs(ctx.latDir, ctx.projectRoot);
715
- const s = ctx.styler;
716
- const lines = [formatFileStats(files, s)];
717
- lines.push(...formatCheckErrors(errors, s));
718
- if (errors.length > 0) {
719
- lines.push(formatErrorCount(errors.length, s));
720
- return { output: lines.join('\n'), isError: true };
721
- }
722
- lines.push(s.green('code-refs: All references OK'));
723
- return { output: lines.join('\n') };
724
- }
725
- export async function checkIndexCommand(ctx, opts = {}) {
726
- const errors = await checkIndex(ctx.latDir, opts);
727
- const s = ctx.styler;
728
- const lines = [];
729
- if (opts.fix) {
730
- lines.push(s.green('index: directory index files regenerated'));
731
- if (errors.length > 0) {
732
- lines.push(...formatCheckIndexErrors(errors, s));
733
- lines.push(formatErrorCount(errors.length, s));
734
- return { output: lines.join('\n'), isError: true };
735
- }
736
- return { output: lines.join('\n') };
737
- }
738
- lines.push(...formatCheckIndexErrors(errors, s));
739
- if (errors.length > 0) {
740
- lines.push(formatErrorCount(errors.length, s));
741
- return { output: lines.join('\n'), isError: true };
742
- }
743
- lines.push(s.green('index: All directory index files OK'));
744
- return { output: lines.join('\n') };
745
- }
746
- export async function checkSectionsCommand(ctx) {
747
- const errors = await checkSections(ctx.latDir, ctx.projectRoot);
748
- const s = ctx.styler;
749
- const lines = [];
750
- lines.push(...formatCheckErrors(errors, s));
751
- if (errors.length > 0) {
752
- lines.push(formatErrorCount(errors.length, s));
753
- return { output: lines.join('\n'), isError: true };
754
- }
755
- lines.push(s.green('sections: All sections have valid leading paragraphs'));
756
- return { output: lines.join('\n') };
757
- }
758
- export async function checkModeCommand(ctx) {
759
- const errors = await checkMode(ctx.latDir, ctx.projectRoot);
760
- const s = ctx.styler;
761
- const lines = [];
762
- lines.push(...formatCheckErrors(errors, s));
763
- if (errors.length > 0) {
764
- lines.push(formatErrorCount(errors.length, s));
765
- return { output: lines.join('\n'), isError: true };
766
- }
767
- lines.push(s.green('mode: All documents match their Diátaxis mode'));
768
- return { output: lines.join('\n') };
769
- }
770
- export async function checkStatusCommand(ctx) {
771
- const errors = await checkStatus(ctx.latDir, ctx.projectRoot);
772
- const s = ctx.styler;
773
- const lines = [];
774
- lines.push(...formatCheckErrors(errors, s));
775
- if (errors.length > 0) {
776
- lines.push(formatErrorCount(errors.length, s));
777
- return { output: lines.join('\n'), isError: true };
778
- }
779
- lines.push(s.green('status: Every recorded review matches its document'));
780
- return { output: lines.join('\n') };
781
- }