@plebeiantech/lat.md 0.12.3-fork.2 → 0.12.3-fork.20

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 (341) hide show
  1. package/README.md +31 -2
  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/comment-guard.d.ts +14 -0
  7. package/dist/src/cli/comment-guard.js +10 -33
  8. package/dist/src/cli/comment-reminder.d.ts +65 -6
  9. package/dist/src/cli/comment-reminder.js +164 -28
  10. package/dist/src/cli/fork-instructions.d.ts +20 -0
  11. package/dist/src/cli/fork-instructions.js +184 -0
  12. package/dist/src/cli/fork-scaffold.d.ts +106 -0
  13. package/dist/src/cli/fork-scaffold.js +490 -0
  14. package/dist/src/cli/graph.d.ts +1 -1
  15. package/dist/src/cli/hook.d.ts +26 -0
  16. package/dist/src/cli/hook.js +118 -46
  17. package/dist/src/cli/index.js +166 -64
  18. package/dist/src/cli/init-preferences.d.ts +4 -0
  19. package/dist/src/cli/init-preferences.js +36 -0
  20. package/dist/src/cli/init.d.ts +2 -1
  21. package/dist/src/cli/init.js +107 -50
  22. package/dist/src/cli/reindex.d.ts +1 -1
  23. package/dist/src/cli/reindex.js +7 -10
  24. package/dist/src/cli/search.d.ts +14 -4
  25. package/dist/src/cli/search.js +116 -100
  26. package/dist/src/cli/ui-build-server.d.ts +12 -0
  27. package/dist/src/cli/ui-build-server.js +26 -0
  28. package/dist/src/cli/ui-build.d.ts +4 -0
  29. package/dist/src/cli/ui-build.js +13 -0
  30. package/dist/src/cli/ui.d.ts +1 -1
  31. package/dist/src/cli/ui.js +18 -2
  32. package/dist/src/fork/antigravity-hook.d.ts +47 -0
  33. package/dist/src/fork/antigravity-hook.js +424 -0
  34. package/dist/src/fork/antigravity-init.d.ts +17 -0
  35. package/dist/src/fork/antigravity-init.js +124 -0
  36. package/dist/src/fork/upstream-drift-cli.js +2 -0
  37. package/dist/src/fork/upstream-drift.d.ts +26 -0
  38. package/dist/src/fork/upstream-drift.js +185 -0
  39. package/dist/src/fork/upstream-guard-cli.d.ts +1 -0
  40. package/dist/src/fork/upstream-guard-cli.js +2 -0
  41. package/dist/src/fork/upstream-guard.d.ts +72 -0
  42. package/dist/src/fork/upstream-guard.js +445 -0
  43. package/dist/src/graph-export.d.ts +2 -0
  44. package/dist/src/graph-export.js +38 -19
  45. package/dist/src/knowledge/claude-memory.d.ts +1 -0
  46. package/dist/src/knowledge/claude-memory.js +33 -4
  47. package/dist/src/knowledge/index.js +2 -2
  48. package/dist/src/mcp/server.js +34 -18
  49. package/dist/src/search/cache.d.ts +3 -0
  50. package/dist/src/search/cache.js +104 -0
  51. package/dist/src/search/chunks.d.ts +23 -0
  52. package/dist/src/search/chunks.js +197 -0
  53. package/dist/src/search/db.d.ts +28 -16
  54. package/dist/src/search/db.js +106 -40
  55. package/dist/src/search/embedder.d.ts +2 -1
  56. package/dist/src/search/embedder.js +7 -6
  57. package/dist/src/search/index.d.ts +6 -2
  58. package/dist/src/search/index.js +176 -53
  59. package/dist/src/search/lexical.d.ts +6 -0
  60. package/dist/src/search/lexical.js +36 -0
  61. package/dist/src/search/lock.d.ts +5 -0
  62. package/dist/src/search/lock.js +45 -0
  63. package/dist/src/search/query.d.ts +26 -0
  64. package/dist/src/search/query.js +103 -0
  65. package/dist/src/search/search.d.ts +17 -7
  66. package/dist/src/search/search.js +164 -14
  67. package/dist/src/search/types.d.ts +15 -0
  68. package/dist/src/search/types.js +1 -0
  69. package/dist/src/view/client/assets/GraphView-Dij8mAA_.js +456 -0
  70. package/dist/src/view/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  71. package/dist/src/view/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  72. package/dist/src/view/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  73. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  74. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  75. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  76. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  77. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  78. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  79. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  80. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  81. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  82. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  83. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  84. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  85. package/dist/src/view/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  86. package/dist/src/view/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  87. package/dist/src/view/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  88. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  89. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  90. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  91. package/dist/src/view/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  92. package/dist/src/view/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  93. package/dist/src/view/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  94. package/dist/src/view/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  95. package/dist/src/view/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  96. package/dist/src/view/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  97. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  98. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  99. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  100. package/dist/src/view/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  101. package/dist/src/view/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  102. package/dist/src/view/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  103. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  104. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  105. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  106. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  107. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  108. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  109. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  110. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  111. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  112. package/dist/src/view/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  113. package/dist/src/view/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  114. package/dist/src/view/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  115. package/dist/src/view/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  116. package/dist/src/view/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  117. package/dist/src/view/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  118. package/dist/src/view/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  119. package/dist/src/view/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  120. package/dist/src/view/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  121. package/dist/src/view/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  122. package/dist/src/view/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  123. package/dist/src/view/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  124. package/dist/src/view/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  125. package/dist/src/view/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  126. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  127. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  128. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  129. package/dist/src/view/client/assets/MarkdownEditor-88Wbt0M1.js +28 -0
  130. package/dist/src/view/client/assets/OrbitControls-DwcpQNja.js +1 -0
  131. package/dist/src/view/client/assets/STLLoader-CLWTKt5i.js +1 -0
  132. package/dist/src/view/client/assets/_commonjsHelpers-CqkleIqs.js +1 -0
  133. package/dist/src/view/client/assets/abnfDiagram-VCTEODGH-y0DlYa8E.js +1 -0
  134. package/dist/src/view/client/assets/arc-FlMu8dnO.js +1 -0
  135. package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-DA_uWZW1.js +36 -0
  136. package/dist/src/view/client/assets/blockDiagram-I7D4REHJ-BHQ5LU85.js +129 -0
  137. package/dist/src/view/client/assets/c4Diagram-7LVT6UL2-BRucKCt2.js +38 -0
  138. package/dist/src/view/client/assets/channel-BgDNA_Ay.js +1 -0
  139. package/dist/src/view/client/assets/chunk-2Q5K7J3B-BJPcFivt.js +1 -0
  140. package/dist/src/view/client/assets/chunk-5VM5RSS4-v2q99bGz.js +15 -0
  141. package/dist/src/view/client/assets/chunk-F27PBJKO-CcHOuJCs.js +1 -0
  142. package/dist/src/view/client/assets/chunk-IMKFNOWR-DNxYuJNq.js +231 -0
  143. package/dist/src/view/client/assets/chunk-JWPE2WC7-C0eHKvLW.js +1 -0
  144. package/dist/src/view/client/assets/chunk-POPQ4Y6H-57CWp2Ve.js +1 -0
  145. package/dist/src/view/client/assets/chunk-SVP7TREG-BqcoxIJw.js +88 -0
  146. package/dist/src/view/client/assets/chunk-TICWLB2K-DRUJR5zH.js +206 -0
  147. package/dist/src/view/client/assets/chunk-XXDRQBXY-Dez0oRoM.js +1 -0
  148. package/dist/src/view/client/assets/classDiagram-ZZMXUADV-CFZ_Sgh5.js +1 -0
  149. package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-CFZ_Sgh5.js +1 -0
  150. package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-BHtud6Hg.js +1 -0
  151. package/dist/src/view/client/assets/cynefin-OW5HDTMX-BjBW8VGk.js +166 -0
  152. package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-DwGuD7oF.js +62 -0
  153. package/dist/src/view/client/assets/cytoscape.esm-DzSz-X2X.js +321 -0
  154. package/dist/src/view/client/assets/dagre-GXQ25YYZ-C5zSlGK9.js +4 -0
  155. package/dist/src/view/client/assets/defaultLocale-DX6XiGOO.js +1 -0
  156. package/dist/src/view/client/assets/diagram-S7CK7UJ4-C_NhA3bj.js +30 -0
  157. package/dist/src/view/client/assets/diagram-UQ7AKVKN-az7QPYNb.js +41 -0
  158. package/dist/src/view/client/assets/diagram-VSXAHHWV-BRvDuM8_.js +3 -0
  159. package/dist/src/view/client/assets/diagram-VX7I27RA-BWLgF6S-.js +24 -0
  160. package/dist/src/view/client/assets/diagram-Z3DM3KII-DV3d6mL5.js +24 -0
  161. package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-yUXrEAvK.js +1 -0
  162. package/dist/src/view/client/assets/erDiagram-RLTQ6QDP-D35FN1Pt.js +99 -0
  163. package/dist/src/view/client/assets/flowDiagram-HODETNUW-BciZqsjT.js +168 -0
  164. package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-BysPGgXS.js +292 -0
  165. package/dist/src/view/client/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  166. package/dist/src/view/client/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  167. package/dist/src/view/client/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  168. package/dist/src/view/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  169. package/dist/src/view/client/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  170. package/dist/src/view/client/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  171. package/dist/src/view/client/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  172. package/dist/src/view/client/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  173. package/dist/src/view/client/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  174. package/dist/src/view/client/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  175. package/dist/src/view/client/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  176. package/dist/src/view/client/assets/gitGraphDiagram-WWUBYQGX-C3DdJ5xG.js +106 -0
  177. package/dist/src/view/client/assets/index-BnENo9bV.js +1 -0
  178. package/dist/src/view/client/assets/index-CJokoRSp.js +13 -0
  179. package/dist/src/view/client/assets/index-DG0Ez15D.css +1 -0
  180. package/dist/src/view/client/assets/infoDiagram-27XIBGKW-D3HZrDRb.js +2 -0
  181. package/dist/src/view/client/assets/init-Gi6I4Gst.js +1 -0
  182. package/dist/src/view/client/assets/ishikawaDiagram-5VMMS53U-BXyVIXTU.js +70 -0
  183. package/dist/src/view/client/assets/journeyDiagram-3NMN7TZE-D4-1NxVU.js +139 -0
  184. package/dist/src/view/client/assets/kanban-definition-UXKFOSKX-4Gu_5yBA.js +89 -0
  185. package/dist/src/view/client/assets/katex-HP8lGamR.js +257 -0
  186. package/dist/src/view/client/assets/layout-Jf1K3TMM.js +1 -0
  187. package/dist/src/view/client/assets/linear-DWfwzUT_.js +1 -0
  188. package/dist/src/view/client/assets/logo-RR_KBOAT.svg +149 -0
  189. package/dist/src/view/client/assets/maplibre-gl-CDX6Zuow.js +800 -0
  190. package/dist/src/view/client/assets/mermaid.core-DMx_DAl8.js +310 -0
  191. package/dist/src/view/client/assets/mindmap-definition-YA3MSWOX-8pxY_RWi.js +96 -0
  192. package/dist/src/view/client/assets/ordinal-Cboi1Yqb.js +1 -0
  193. package/dist/src/view/client/assets/pegDiagram-XKGWAZYB-DNAw_O5t.js +1 -0
  194. package/dist/src/view/client/assets/pieDiagram-E7YTZNPT-uuvRCDkC.js +39 -0
  195. package/dist/src/view/client/assets/quadrantDiagram-AXDQQJYC-BER2O4Aq.js +7 -0
  196. package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-CxEsJ_0n.js +1 -0
  197. package/dist/src/view/client/assets/requirementDiagram-BXWQKSXE-yS9rspca.js +84 -0
  198. package/dist/src/view/client/assets/sankeyDiagram-P5KCCOFB-Bqt70a2j.js +40 -0
  199. package/dist/src/view/client/assets/sequenceDiagram-WJ2MYXX4-BvOOEJNr.js +162 -0
  200. package/dist/src/view/client/assets/sizeCapture-INFHLROL-CvO_Pjjo.js +1 -0
  201. package/dist/src/view/client/assets/stateDiagram-D77RDMKH-KLmVOZkq.js +1 -0
  202. package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-KCS8Mdzu.js +1 -0
  203. package/dist/src/view/client/assets/swimlanes-42K2YHIH-DC1r4_sq.js +1 -0
  204. package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-B2TcAwis.js +8 -0
  205. package/dist/src/view/client/assets/three.module-PDSP0dbZ.js +4108 -0
  206. package/dist/src/view/client/assets/timeline-definition-24CTP7MA-ChXLOuBL.js +120 -0
  207. package/dist/src/view/client/assets/vennDiagram-4TSXK5OY-D9_h25-Y.js +34 -0
  208. package/dist/src/view/client/assets/wardleyDiagram-VM6X3IG4-CsQJGGW0.js +78 -0
  209. package/dist/src/view/client/assets/xychartDiagram-S5SC5T6Z-DGMuPMlm.js +7 -0
  210. package/dist/src/view/client/index.html +2 -2
  211. package/dist/src/view/client-shell.d.ts +2 -0
  212. package/dist/src/view/client-shell.js +5 -0
  213. package/dist/src/view/diagnostics.d.ts +3 -2
  214. package/dist/src/view/diagnostics.js +49 -58
  215. package/dist/src/view/document-edit.d.ts +8 -0
  216. package/dist/src/view/document-edit.js +25 -0
  217. package/dist/src/view/document-route.d.ts +14 -0
  218. package/dist/src/view/document-route.js +126 -0
  219. package/dist/src/view/document-tree.d.ts +18 -0
  220. package/dist/src/view/document-tree.js +139 -0
  221. package/dist/src/view/external-document-tree.d.ts +3 -0
  222. package/dist/src/view/external-document-tree.js +674 -0
  223. package/dist/src/view/git-diff.js +205 -1
  224. package/dist/src/view/git.js +1 -1
  225. package/dist/src/view/graph.d.ts +7 -0
  226. package/dist/src/view/graph.js +267 -0
  227. package/dist/src/view/highlight.d.ts +10 -2
  228. package/dist/src/view/highlight.js +4547 -1363
  229. package/dist/src/view/markdown.d.ts +8 -3
  230. package/dist/src/view/markdown.js +308 -41
  231. package/dist/src/view/preindexed-search.d.ts +15 -0
  232. package/dist/src/view/preindexed-search.js +25 -0
  233. package/dist/src/view/protocol.d.ts +95 -6
  234. package/dist/src/view/protocol.js +1 -1
  235. package/dist/src/view/publication.d.ts +2 -0
  236. package/dist/src/view/publication.js +78 -0
  237. package/dist/src/view/references.d.ts +13 -26
  238. package/dist/src/view/references.js +150 -119
  239. package/dist/src/view/repository.d.ts +18 -5
  240. package/dist/src/view/repository.js +269 -27
  241. package/dist/src/view/request-security.d.ts +5 -0
  242. package/dist/src/view/request-security.js +46 -0
  243. package/dist/src/view/search-result.d.ts +4 -0
  244. package/dist/src/view/search-result.js +25 -0
  245. package/dist/src/view/search.d.ts +7 -2
  246. package/dist/src/view/search.js +23 -33
  247. package/dist/src/view/server-build.d.ts +21 -0
  248. package/dist/src/view/server-build.js +145 -0
  249. package/dist/src/view/server-deployment.d.ts +31 -0
  250. package/dist/src/view/server-deployment.js +94 -0
  251. package/dist/src/view/server-index-worker.d.ts +6 -0
  252. package/dist/src/view/server-index-worker.js +14 -0
  253. package/dist/src/view/server.d.ts +14 -2
  254. package/dist/src/view/server.js +254 -51
  255. package/dist/src/view/source-target.d.ts +10 -0
  256. package/dist/src/view/source-target.js +43 -0
  257. package/dist/src/view/static-build.d.ts +32 -0
  258. package/dist/src/view/static-build.js +741 -0
  259. package/dist/src/view/static-protocol.d.ts +47 -0
  260. package/dist/src/view/static-protocol.js +15 -0
  261. package/dist/src/view/store.d.ts +26 -3
  262. package/dist/src/view/store.js +325 -44
  263. package/dist/src/view/table-of-contents.d.ts +9 -0
  264. package/dist/src/view/table-of-contents.js +83 -0
  265. package/dist/src/view/vercel-build.d.ts +24 -0
  266. package/dist/src/view/vercel-build.js +140 -0
  267. package/dist/src/view/vercel-server-build.d.ts +10 -0
  268. package/dist/src/view/vercel-server-build.js +61 -0
  269. package/dist/vendor/lat.md-server/cli.d.ts +2 -0
  270. package/dist/vendor/lat.md-server/cli.js +12 -0
  271. package/dist/vendor/lat.md-server/index.d.ts +38 -0
  272. package/dist/vendor/lat.md-server/index.js +128 -0
  273. package/package.json +72 -25
  274. package/plugins/lat-md/README.md +61 -0
  275. package/plugins/lat-md/hooks.json +38 -0
  276. package/plugins/lat-md/plugin.json +9 -0
  277. package/plugins/lat-md/rules/AGENTS.md +118 -0
  278. package/plugins/lat-md/skills/lat-md/SKILL.md +178 -0
  279. package/plugins/lat-md/skills/lat-md-conventions/SKILL.md +182 -0
  280. package/templates/AGENTS.md +19 -18
  281. package/templates/cursor-rules.md +14 -5
  282. package/templates/fork/conventions.md +173 -0
  283. package/templates/opencode-plugin.ts +9 -9
  284. package/templates/pi-extension.ts +11 -11
  285. package/templates/skill/SKILL.md +28 -17
  286. package/dist/src/cli/check-frontmatter.d.ts +0 -12
  287. package/dist/src/cli/check-frontmatter.js +0 -44
  288. package/dist/src/cli/check-mode.d.ts +0 -6
  289. package/dist/src/cli/check-mode.js +0 -303
  290. package/dist/src/cli/check-status.d.ts +0 -74
  291. package/dist/src/cli/check-status.js +0 -217
  292. package/dist/src/cli/check.d.ts +0 -45
  293. package/dist/src/cli/check.js +0 -777
  294. package/dist/src/cli/context.d.ts +0 -11
  295. package/dist/src/cli/context.js +0 -58
  296. package/dist/src/cli/expand.d.ts +0 -8
  297. package/dist/src/cli/expand.js +0 -94
  298. package/dist/src/cli/gen-index.d.ts +0 -45
  299. package/dist/src/cli/gen-index.js +0 -235
  300. package/dist/src/cli/link-scheme.d.ts +0 -54
  301. package/dist/src/cli/link-scheme.js +0 -101
  302. package/dist/src/cli/locate.d.ts +0 -2
  303. package/dist/src/cli/locate.js +0 -17
  304. package/dist/src/cli/refs.d.ts +0 -22
  305. package/dist/src/cli/refs.js +0 -229
  306. package/dist/src/cli/section.d.ts +0 -43
  307. package/dist/src/cli/section.js +0 -252
  308. package/dist/src/cli/select-menu.d.ts +0 -13
  309. package/dist/src/cli/select-menu.js +0 -100
  310. package/dist/src/code-refs.d.ts +0 -17
  311. package/dist/src/code-refs.js +0 -277
  312. package/dist/src/config.d.ts +0 -29
  313. package/dist/src/config.js +0 -83
  314. package/dist/src/context.d.ts +0 -23
  315. package/dist/src/context.js +0 -11
  316. package/dist/src/extensions/wiki-link/from-markdown.d.ts +0 -5
  317. package/dist/src/extensions/wiki-link/from-markdown.js +0 -34
  318. package/dist/src/extensions/wiki-link/index.d.ts +0 -4
  319. package/dist/src/extensions/wiki-link/index.js +0 -3
  320. package/dist/src/extensions/wiki-link/syntax.d.ts +0 -14
  321. package/dist/src/extensions/wiki-link/syntax.js +0 -121
  322. package/dist/src/extensions/wiki-link/to-markdown.d.ts +0 -5
  323. package/dist/src/extensions/wiki-link/to-markdown.js +0 -28
  324. package/dist/src/extensions/wiki-link/types.d.ts +0 -34
  325. package/dist/src/format.d.ts +0 -8
  326. package/dist/src/format.js +0 -43
  327. package/dist/src/init-version.d.ts +0 -10
  328. package/dist/src/init-version.js +0 -49
  329. package/dist/src/lattice.d.ts +0 -108
  330. package/dist/src/lattice.js +0 -816
  331. package/dist/src/parser.d.ts +0 -3
  332. package/dist/src/parser.js +0 -18
  333. package/dist/src/source-parser.d.ts +0 -27
  334. package/dist/src/source-parser.js +0 -916
  335. package/dist/src/untrusted.d.ts +0 -24
  336. package/dist/src/untrusted.js +0 -97
  337. package/dist/src/view/client/assets/index-B0n0u03y.css +0 -1
  338. package/dist/src/view/client/assets/index-xm2_px4q.js +0 -23
  339. package/dist/src/walk.d.ts +0 -18
  340. package/dist/src/walk.js +0 -27
  341. /package/dist/src/{extensions/wiki-link/types.js → fork/upstream-drift-cli.d.ts} +0 -0
package/README.md CHANGED
@@ -42,9 +42,12 @@ This is a fork. It publishes as [`@plebeiantech/lat.md`](https://www.npmjs.com/p
42
42
  With [mise](https://mise.jdx.dev/):
43
43
 
44
44
  ```bash
45
+ mise settings add minimum_release_age_excludes npm:@plebeiantech/lat.md
45
46
  mise use -g npm:@plebeiantech/lat.md@latest
46
47
  ```
47
48
 
49
+ The first line is needed once per machine. mise quarantines any npm version published in the last day or so, and a fresh release is invisible until that window passes — the exclude waives it for this package alone and leaves the guard in force for every other tool.
50
+
48
51
  With npm:
49
52
 
50
53
  ```bash
@@ -59,6 +62,8 @@ npm i -g --prefix ~/.local/lat https://github.com/PlebeianTech/lat.md/releases/l
59
62
 
60
63
  That last one wants `~/.local/lat/bin` on your `PATH`. A fixed prefix keeps the install independent of whichever Node version is active.
61
64
 
65
+ Pick one route and stay on it. A Release-route install left on `PATH` shadows a mise-managed one — `mise upgrade` then reports a new version while the shell keeps running the old binary.
66
+
62
67
  Then run `lat init` in the repo you want to use lat in.
63
68
 
64
69
  ### As a Claude Code plugin
@@ -72,9 +77,33 @@ The hooks are also packaged as a plugin, installable straight from this reposito
72
77
 
73
78
  The plugin ships hooks only and finds `lat` on your `PATH`, accepting a build only if its version carries a `-fork` suffix.
74
79
 
80
+ ### As an Antigravity / Gemini CLI plugin
81
+
82
+ The hooks, rules, and skills are packaged as an Antigravity plugin under `plugins/lat-md/`.
83
+
84
+ You can install it directly without cloning the repository:
85
+
86
+ **Global installation (all local projects):**
87
+ ```bash
88
+ mkdir -p ~/.gemini/config/plugins/lat-md && \
89
+ curl -sL https://github.com/PlebeianTech/lat.md/archive/refs/heads/main.tar.gz | \
90
+ tar -xz -C ~/.gemini/config/plugins/lat-md --strip-components=2 lat.md-main/plugins/lat-md
91
+ ```
92
+
93
+ **Workspace installation (single repository):**
94
+ ```bash
95
+ mkdir -p .agents/plugins/lat-md && \
96
+ curl -sL https://github.com/PlebeianTech/lat.md/archive/refs/heads/main.tar.gz | \
97
+ tar -xz -C .agents/plugins/lat-md --strip-components=2 lat.md-main/plugins/lat-md
98
+ ```
99
+
100
+ Alternatively, install `@plebeiantech/lat.md` via npm/mise, run `lat init` in your project, and select **Antigravity / Gemini** from the agent checklist.
101
+
102
+
103
+
75
104
  ## How it works
76
105
 
77
- Run `lat init` to scaffold a `lat.md/` directory, then write markdown files describing your architecture, business logic, test specs — whatever matters. Link between sections using `[[file#Section#Subsection]]` syntax. Link to source code symbols with `[[src/auth.ts#validateToken]]`. Annotate source code with `// @lat: [[section-id]]` (or `# @lat: [[section-id]]` in Python) comments to tie implementation back to concepts.
106
+ Run `lat init` to scaffold a `lat.md/` directory, then write markdown files describing your architecture, business logic, test specs — whatever matters. Link between sections using `[[file#Section#Subsection]]` syntax. Link to source code symbols with `[[src/auth.ts#validateToken]]`. Annotate source code with `// @lat: [[section-id]]` (or `# @lat: [[section-id]]` in Python and PHP) comments to tie implementation back to concepts.
78
107
 
79
108
  ```
80
109
  my-project/
@@ -110,7 +139,7 @@ To use higher-quality hosted embeddings instead, provide an OpenAI (`sk-...`) or
110
139
  1. `LAT_LLM_KEY` env var — direct value
111
140
  2. `LAT_LLM_KEY_FILE` env var — path to a file containing the key
112
141
  3. `LAT_LLM_KEY_HELPER` env var — shell command that prints the key (10s timeout)
113
- 4. Config file — power users can set `llm_key` manually. Run `lat config` to print its location.
142
+ 4. Config file — power users can set `llm_key` manually. Run `lat paths --config` to print its location. Run `lat paths` to see cache and other storage paths with their purposes.
114
143
 
115
144
  Switch backends any time with `lat reindex` (`--local` to force the offline model, `--remote` to use your key).
116
145
 
@@ -0,0 +1,6 @@
1
+ export interface AgentInvocation {
2
+ command: string;
3
+ args: string[];
4
+ }
5
+ /** Generated tools pass user input as argv, never as shell program text. */
6
+ export declare function agentInvocation(style: 'local' | 'global' | 'npx', local: AgentInvocation, platform?: NodeJS.Platform): AgentInvocation;
@@ -0,0 +1,23 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { delimiter, dirname, join } from 'node:path';
3
+ /** Generated tools pass user input as argv, never as shell program text. */
4
+ export function agentInvocation(style, local, platform = process.platform) {
5
+ if (style === 'local')
6
+ return local;
7
+ // Windows npm command shims require cmd.exe. Use the installed entry point
8
+ // directly instead, retaining the Node launcher and its loader arguments.
9
+ if (style === 'global')
10
+ return platform === 'win32' ? local : { command: 'lat', args: [] };
11
+ if (platform !== 'win32')
12
+ return { command: 'npx', args: ['lat.md@latest'] };
13
+ const directories = [
14
+ dirname(process.execPath),
15
+ ...(process.env.PATH ?? '').split(delimiter),
16
+ ];
17
+ for (const directory of directories) {
18
+ const cli = join(directory, 'node_modules', 'npm', 'bin', 'npx-cli.js');
19
+ if (existsSync(cli))
20
+ return { command: process.execPath, args: [cli, 'lat.md@latest'] };
21
+ }
22
+ throw new Error('Cannot find the npm npx entry point. Choose the local command style or install npm.');
23
+ }
@@ -5,8 +5,9 @@ export interface ChecklistOption {
5
5
  /**
6
6
  * Display an interactive multi-select checklist with arrow-key navigation.
7
7
  * Returns an array of checked values.
8
+ * Known initial values start checked; unknown values are ignored.
8
9
  *
9
10
  * Keys: Up/Down (j/k) to move, Space to toggle, Enter to confirm, Ctrl+C to exit.
10
11
  * Non-TTY fallback: returns [].
11
12
  */
12
- export declare function checklistMenu(options: ChecklistOption[], prompt?: string): Promise<string[]>;
13
+ export declare function checklistMenu(options: ChecklistOption[], prompt?: string, initialValues?: readonly string[]): Promise<string[]>;
@@ -2,18 +2,19 @@ import { styleText } from 'node:util';
2
2
  /**
3
3
  * Display an interactive multi-select checklist with arrow-key navigation.
4
4
  * Returns an array of checked values.
5
+ * Known initial values start checked; unknown values are ignored.
5
6
  *
6
7
  * Keys: Up/Down (j/k) to move, Space to toggle, Enter to confirm, Ctrl+C to exit.
7
8
  * Non-TTY fallback: returns [].
8
9
  */
9
- export async function checklistMenu(options, prompt) {
10
+ export async function checklistMenu(options, prompt, initialValues = []) {
10
11
  if (options.length === 0)
11
12
  return [];
12
13
  if (!process.stdin.isTTY)
13
14
  return [];
14
15
  return new Promise((resolve) => {
15
16
  let cursor = 0;
16
- const checked = new Set();
17
+ const checked = new Set(options.flatMap((option, index) => initialValues.includes(option.value) ? [index] : []));
17
18
  const stdin = process.stdin;
18
19
  const wasRaw = stdin.isRaw;
19
20
  stdin.setRawMode(true);
@@ -1,4 +1,18 @@
1
1
  import { type ToolInput } from './comment-reminder.js';
2
+ /**
3
+ * The blocking half of the comment convention. `computeCommentReminder` runs
4
+ * at PostToolUse and only advises: by then the prose is already on disk, and
5
+ * it is deduped to once per file per session, so the second rationale block
6
+ * in a file draws no signal at all.
7
+ *
8
+ * This runs at PreToolUse instead, where `tool_input` is visible before the
9
+ * edit is applied and the hook can answer `deny`. A multi-line comment is
10
+ * refused outright and the agent is told to put the reasoning in `lat.md/`
11
+ * and leave a `@lat:` pointer behind.
12
+ *
13
+ * There is deliberately no per-session dedup here. A gate that fires once is
14
+ * not a gate.
15
+ */
2
16
  export type PreToolUseInput = {
3
17
  hook_event_name?: string;
4
18
  session_id?: string;
@@ -1,28 +1,4 @@
1
- import { candidateCommentLines, extractWrittenText, hasLatTree, matchFamily, resolveProjectRoot, } from './comment-reminder.js';
2
- /**
3
- * The blocking half of the comment convention. `computeCommentReminder` runs
4
- * at PostToolUse and only advises: by then the prose is already on disk, and
5
- * it is deduped to once per file per session, so the second rationale block
6
- * in a file draws no signal at all.
7
- *
8
- * This runs at PreToolUse instead, where `tool_input` is visible before the
9
- * edit is applied and the hook can answer `deny`. A multi-line comment is
10
- * refused outright and the agent is told to put the reasoning in `lat.md/`
11
- * and leave a `@lat:` pointer behind.
12
- *
13
- * There is deliberately no per-session dedup here. A gate that fires once is
14
- * not a gate.
15
- */
16
- /** Minimum candidate comment lines in one write before the edit is refused.
17
- * A single line ("// bytes, not chars") is the bare-fact case the convention
18
- * explicitly allows; two or more is a block of prose. */
19
- const BLOCK_THRESHOLD = 2;
20
- /** Explicit, reviewable opt-out. Spelled the same as the `lat:ignore` token
21
- * [[src/code-refs.ts]] already honours, so a project has one escape hatch to
22
- * learn rather than two. It suppresses only the line it appears on, which
23
- * keeps a deliberate exception visible in the diff instead of silently
24
- * disarming the gate for a whole file. */
25
- const LAT_IGNORE_RE = /(?<![\w])lat:ignore(?![-\w])/;
1
+ import { hasLatTree, judgeWrittenComments, matchFamily, resolveProjectRoot, } from './comment-reminder.js';
26
2
  function buildReason(opts) {
27
3
  const where = opts.hasTree
28
4
  ? ' 1. Put the reasoning in a lat.md/ section. Verbatim is fine — it does\n' +
@@ -43,8 +19,12 @@ function buildReason(opts) {
43
19
  ' 3. Re-apply the edit with the prose replaced by that one pointer line.\n\n' +
44
20
  'If a comment only narrates what the code does, delete it rather than ' +
45
21
  'moving it — a reader could reconstruct it from the code.\n\n' +
46
- `To keep a specific line anyway, put \`lat:ignore\` on it. That is an ` +
47
- 'explicit, reviewable exception, not a way to silence the gate.');
22
+ 'Two exits, if neither applies:\n' +
23
+ ' - Put `lat:ignore` on a specific line to keep it. An explicit, ' +
24
+ 'reviewable exception, not a way to silence the gate.\n' +
25
+ ' - Use `Edit` rather than a whole-file `Write`. Re-emitting a comment ' +
26
+ 'the file already holds is free in either shape, but an `Edit` puts far ' +
27
+ 'less text in front of the count.');
48
28
  }
49
29
  /**
50
30
  * Returns the denial reason for a single Edit/Write/MultiEdit call, or `null`
@@ -93,18 +73,15 @@ export function computeCommentBlock(input) {
93
73
  const family = matchFamily(filePath);
94
74
  if (!family)
95
75
  return null;
96
- const written = extractWrittenText(toolInput);
97
- if (!written)
98
- return null;
99
- const candidates = candidateCommentLines(written, family).filter((line) => !LAT_IGNORE_RE.test(line));
100
- if (candidates.length < BLOCK_THRESHOLD)
76
+ const verdict = judgeWrittenComments(toolInput, family, 'before-write');
77
+ if (!verdict.flagged)
101
78
  return null;
102
79
  const projectRoot = resolveProjectRoot(filePath, input.cwd);
103
80
  if (!projectRoot)
104
81
  return null;
105
82
  return buildReason({
106
83
  base: filePath.split('/').pop() ?? filePath,
107
- count: candidates.length,
84
+ count: verdict.count,
108
85
  marker: family.marker,
109
86
  hasTree: hasLatTree(projectRoot),
110
87
  });
@@ -27,20 +27,79 @@ export declare function matchFamily(filePath: string): CommentFamily | null;
27
27
  export type ToolInput = {
28
28
  file_path?: string;
29
29
  content?: string;
30
+ old_string?: string;
30
31
  new_string?: string;
31
32
  edits?: {
33
+ old_string?: string;
32
34
  new_string?: string;
33
35
  }[];
34
36
  };
35
- /** Only the NEW text — reading the file would fire on every pre-existing
36
- * comment in it, nagging about prose the agent did not write. */
37
- export declare function extractWrittenText(toolInput: ToolInput): string;
38
- /** Comment lines worth reminding about. Dropped before counting:
37
+ /**
38
+ * Which side of the tool call the caller is on.
39
+ *
40
+ * `'before-write'` — PreToolUse. The file on disk is still the pre-edit state,
41
+ * so it is usable as a baseline for a whole-file `Write`, which carries no
42
+ * other record of what it replaced.
43
+ *
44
+ * `'after-write'` — PostToolUse. The write has landed, so the file contains
45
+ * every line of `content` by construction. Diffing against it there cancels
46
+ * the whole payload and the caller sees nothing at all.
47
+ *
48
+ * An `Edit` carries its own `old_string` and needs neither reading: its
49
+ * baseline is phase-independent, which is the shape to prefer.
50
+ *
51
+ * The parameter is required so a third call site has to answer the question
52
+ * rather than inherit whichever default happened to suit the first two.
53
+ */
54
+ export type WritePhase = 'before-write' | 'after-write';
55
+ /** Explicit, reviewable per-line opt-out, spelled the same as the token
56
+ * [[src/code-refs.ts]] already honours so a project has one escape hatch to
57
+ * learn rather than two. It lives here, not in the guard, because the
58
+ * advisory and blocking halves have to agree on it: an exemption that the
59
+ * gate honours but the reminder does not just relocates the nag.
60
+ * It suppresses only the line it appears on, keeping a deliberate exception
61
+ * visible in the diff instead of silently disarming the convention for a
62
+ * whole file. */
63
+ export declare const LAT_IGNORE_RE: RegExp;
64
+ /** Whether one line is a comment worth counting. Not counted:
39
65
  * - a line that already carries a `@lat:` pointer (the compliant state)
66
+ * - a line carrying the explicit opt-out token
40
67
  * - machine directives: shebangs, magic comments, linter/type pragmas
41
68
  * - decoration with no alphanumeric character (`# ----`, `//////`, a bare
42
- * `*​/` closing a block) */
43
- export declare function candidateCommentLines(written: string, family: CommentFamily): string[];
69
+ * `*​/` closing a block)
70
+ *
71
+ * Both halves of the convention call this and nothing else, so the gate and
72
+ * the reminder cannot drift apart on what counts as prose. */
73
+ export declare function isCandidateCommentLine(line: string, family: CommentFamily): boolean;
74
+ export type CommentVerdict = {
75
+ /** Candidate comment lines this call is answerable for. Names the number
76
+ * printed in the denial or the reminder. */
77
+ count: number;
78
+ /** Whether the call warrants a denial from the gate or a reminder from its
79
+ * advisory half. */
80
+ flagged: boolean;
81
+ };
82
+ /**
83
+ * What one Edit/Write/MultiEdit call did to the comments in a file.
84
+ *
85
+ * Two rules, because neither alone survives contact with an agent:
86
+ *
87
+ * A run of `PROSE_THRESHOLD` or more *adjacent* comment lines is flagged as
88
+ * soon as any one of them is new. Counting only the new lines lets a block be
89
+ * grown one line per `Edit` and never reach the threshold — and the denial
90
+ * text itself recommends `Edit` over `Write`, so the technique is advertised.
91
+ * What matters is the block the edit leaves behind, not the increment.
92
+ *
93
+ * Isolated new comment lines are flagged once `PROSE_THRESHOLD` of them
94
+ * accumulate anywhere in the call. Two one-line comments in different places
95
+ * are still two lines of prose; without this a scattered write walks past the
96
+ * adjacency rule.
97
+ *
98
+ * Neither rule counts a line that was already there, so re-emitting a
99
+ * comment — the whole of a `Write`'s payload, or the lines bracketing an
100
+ * `Edit` — stays free.
101
+ */
102
+ export declare function judgeWrittenComments(toolInput: ToolInput, family: CommentFamily, phase: WritePhase): CommentVerdict;
44
103
  /** Resolvable project root, required so a scratch file in /tmp is not treated
45
104
  * as a project. `cwd` is preferred (matches CLAUDE_PROJECT_DIR / payload cwd
46
105
  * conventions); falls back to `git rev-parse --show-toplevel`. */
@@ -1,6 +1,6 @@
1
1
  import { execSync } from 'node:child_process';
2
2
  import { createHash } from 'node:crypto';
3
- import { existsSync, mkdirSync, writeFileSync, statSync, lstatSync, } from 'node:fs';
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync, lstatSync, } from 'node:fs';
4
4
  import { tmpdir } from 'node:os';
5
5
  import { basename, dirname, join } from 'node:path';
6
6
  const FAMILIES = [
@@ -41,35 +41,174 @@ export function matchFamily(filePath) {
41
41
  }
42
42
  return null;
43
43
  }
44
- /** Only the NEW text — reading the file would fire on every pre-existing
45
- * comment in it, nagging about prose the agent did not write. */
46
- export function extractWrittenText(toolInput) {
47
- const parts = [];
44
+ /** Line multiset of `text`, used as the baseline a written fragment is new
45
+ * against. */
46
+ function lineCounts(text) {
47
+ const counts = new Map();
48
+ for (const line of text.split('\n')) {
49
+ counts.set(line, (counts.get(line) ?? 0) + 1);
50
+ }
51
+ return counts;
52
+ }
53
+ /** Baseline taken from the file itself, or `null` when there is no readable
54
+ * file — a brand-new path, a directory, a permission error, a payload naming
55
+ * no file. `null` means every line counts as new, which is what makes an
56
+ * unreadable file fail closed: the gate denies rather than waving the write
57
+ * through on evidence it could not gather. */
58
+ function fileBaseline(filePath) {
59
+ if (!filePath)
60
+ return null;
61
+ try {
62
+ return lineCounts(readFileSync(filePath, 'utf-8'));
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ /**
69
+ * The fragments one tool call writes, each against the narrowest baseline
70
+ * available for it.
71
+ *
72
+ * `old_string` is preferred over the file wherever the payload carries one,
73
+ * because the file is the wrong scope: matching a written line against
74
+ * *anywhere* in the file forgives a genuinely new block whenever one of its
75
+ * lines happens to duplicate an unrelated line elsewhere. `old_string` is the
76
+ * exact text being replaced, so the difference is the real delta.
77
+ *
78
+ * Baselines are per fragment rather than shared across a `MultiEdit`: two
79
+ * hunks that each re-emit the same surrounding comment are both re-emitting
80
+ * it, and charging the second one for it would deny exactly the edit this is
81
+ * here to allow.
82
+ */
83
+ function writtenFragments(toolInput, phase) {
84
+ let diskRead = false;
85
+ let disk = null;
86
+ const fromFile = () => {
87
+ if (phase !== 'before-write')
88
+ return null;
89
+ if (!diskRead) {
90
+ disk = fileBaseline(toolInput.file_path);
91
+ diskRead = true;
92
+ }
93
+ return disk;
94
+ };
95
+ const baselineFor = (oldString) => typeof oldString === 'string' ? lineCounts(oldString) : fromFile();
96
+ const fragments = [];
48
97
  if (typeof toolInput.content === 'string')
49
- parts.push(toolInput.content);
98
+ fragments.push({ text: toolInput.content, baseline: fromFile() });
50
99
  if (typeof toolInput.new_string === 'string')
51
- parts.push(toolInput.new_string);
100
+ fragments.push({
101
+ text: toolInput.new_string,
102
+ baseline: baselineFor(toolInput.old_string),
103
+ });
52
104
  if (Array.isArray(toolInput.edits)) {
53
105
  for (const edit of toolInput.edits) {
54
106
  if (typeof edit?.new_string === 'string')
55
- parts.push(edit.new_string);
107
+ fragments.push({
108
+ text: edit.new_string,
109
+ baseline: baselineFor(edit.old_string),
110
+ });
56
111
  }
57
112
  }
58
- return parts.join('\n');
113
+ return fragments;
59
114
  }
60
- /** Comment lines worth reminding about. Dropped before counting:
115
+ /** Explicit, reviewable per-line opt-out, spelled the same as the token
116
+ * [[src/code-refs.ts]] already honours so a project has one escape hatch to
117
+ * learn rather than two. It lives here, not in the guard, because the
118
+ * advisory and blocking halves have to agree on it: an exemption that the
119
+ * gate honours but the reminder does not just relocates the nag.
120
+ * It suppresses only the line it appears on, keeping a deliberate exception
121
+ * visible in the diff instead of silently disarming the convention for a
122
+ * whole file. */
123
+ export const LAT_IGNORE_RE = /(?<![\w])lat:ignore(?![-\w])/;
124
+ /** Whether one line is a comment worth counting. Not counted:
61
125
  * - a line that already carries a `@lat:` pointer (the compliant state)
126
+ * - a line carrying the explicit opt-out token
62
127
  * - machine directives: shebangs, magic comments, linter/type pragmas
63
128
  * - decoration with no alphanumeric character (`# ----`, `//////`, a bare
64
- * `*​/` closing a block) */
65
- export function candidateCommentLines(written, family) {
66
- return written
67
- .split('\n')
68
- .filter((line) => family.commentRe.test(line))
69
- .filter((line) => !line.includes('@lat:'))
70
- .filter((line) => !/^\s*#!/.test(line))
71
- .filter((line) => !family.pragmaRe.test(line))
72
- .filter((line) => /[a-zA-Z0-9]/.test(line));
129
+ * `*​/` closing a block)
130
+ *
131
+ * Both halves of the convention call this and nothing else, so the gate and
132
+ * the reminder cannot drift apart on what counts as prose. */
133
+ export function isCandidateCommentLine(line, family) {
134
+ return (family.commentRe.test(line) &&
135
+ !line.includes('@lat:') &&
136
+ !LAT_IGNORE_RE.test(line) &&
137
+ !/^\s*#!/.test(line) &&
138
+ !family.pragmaRe.test(line) &&
139
+ /[a-zA-Z0-9]/.test(line));
140
+ }
141
+ /** Candidate comment lines in one write before either half reacts. A single
142
+ * line ("// bytes, not chars") is the bare-fact case the convention allows;
143
+ * two is a block of prose. */
144
+ const PROSE_THRESHOLD = 2;
145
+ /**
146
+ * What one Edit/Write/MultiEdit call did to the comments in a file.
147
+ *
148
+ * Two rules, because neither alone survives contact with an agent:
149
+ *
150
+ * A run of `PROSE_THRESHOLD` or more *adjacent* comment lines is flagged as
151
+ * soon as any one of them is new. Counting only the new lines lets a block be
152
+ * grown one line per `Edit` and never reach the threshold — and the denial
153
+ * text itself recommends `Edit` over `Write`, so the technique is advertised.
154
+ * What matters is the block the edit leaves behind, not the increment.
155
+ *
156
+ * Isolated new comment lines are flagged once `PROSE_THRESHOLD` of them
157
+ * accumulate anywhere in the call. Two one-line comments in different places
158
+ * are still two lines of prose; without this a scattered write walks past the
159
+ * adjacency rule.
160
+ *
161
+ * Neither rule counts a line that was already there, so re-emitting a
162
+ * comment — the whole of a `Write`'s payload, or the lines bracketing an
163
+ * `Edit` — stays free.
164
+ */
165
+ export function judgeWrittenComments(toolInput, family, phase) {
166
+ const fragments = writtenFragments(toolInput, phase);
167
+ if (!fragments.some((fragment) => fragment.text !== ''))
168
+ return { count: 0, flagged: false };
169
+ let inFlaggedRuns = 0;
170
+ let isolatedNew = 0;
171
+ for (const fragment of fragments) {
172
+ const lines = fragment.text.split('\n');
173
+ const unclaimed = fragment.baseline ? new Map(fragment.baseline) : null;
174
+ const isNew = [];
175
+ const isComment = [];
176
+ for (const line of lines) {
177
+ let fresh = true;
178
+ if (unclaimed) {
179
+ const left = unclaimed.get(line) ?? 0;
180
+ if (left > 0) {
181
+ unclaimed.set(line, left - 1);
182
+ fresh = false;
183
+ }
184
+ }
185
+ isNew.push(fresh);
186
+ isComment.push(isCandidateCommentLine(line, family));
187
+ }
188
+ let i = 0;
189
+ while (i < lines.length) {
190
+ if (!isComment[i]) {
191
+ i += 1;
192
+ continue;
193
+ }
194
+ let end = i;
195
+ while (end < lines.length && isComment[end])
196
+ end += 1;
197
+ let fresh = 0;
198
+ for (let k = i; k < end; k += 1)
199
+ if (isNew[k])
200
+ fresh += 1;
201
+ if (end - i >= PROSE_THRESHOLD && fresh > 0)
202
+ inFlaggedRuns += end - i;
203
+ else
204
+ isolatedNew += fresh;
205
+ i = end;
206
+ }
207
+ }
208
+ return {
209
+ count: inFlaggedRuns + isolatedNew,
210
+ flagged: inFlaggedRuns > 0 || isolatedNew >= PROSE_THRESHOLD,
211
+ };
73
212
  }
74
213
  /** Resolvable project root, required so a scratch file in /tmp is not treated
75
214
  * as a project. `cwd` is preferred (matches CLAUDE_PROJECT_DIR / payload cwd
@@ -176,16 +315,13 @@ export function computeCommentReminder(input) {
176
315
  const family = matchFamily(filePath);
177
316
  if (!family)
178
317
  return null;
179
- const written = extractWrittenText(toolInput);
180
- if (!written)
181
- return null;
182
318
  // A single-line comment ("// increment the counter") almost always states
183
319
  // a bare fact, not a rationale — the acceptance case for this hook. Only a
184
- // multi-line block is treated as prose worth a reminder; the model still
185
- // makes the real judgment call, this just filters the overwhelmingly
186
- // common one-liner before it ever reaches that question.
187
- const candidates = candidateCommentLines(written, family);
188
- if (candidates.length < 2)
320
+ // block is treated as prose worth a reminder; the model still makes the
321
+ // real judgment call, this just filters the overwhelmingly common one-liner
322
+ // before it ever reaches that question.
323
+ const verdict = judgeWrittenComments(toolInput, family, 'after-write');
324
+ if (!verdict.flagged)
189
325
  return null;
190
326
  const sessionId = input.session_id;
191
327
  let dedup = null;
@@ -201,7 +337,7 @@ export function computeCommentReminder(input) {
201
337
  const base = basename(filePath);
202
338
  const message = buildMessage({
203
339
  base,
204
- count: candidates.length,
340
+ count: verdict.count,
205
341
  marker: family.marker,
206
342
  hasTree,
207
343
  });
@@ -0,0 +1,20 @@
1
+ export declare const SKILL_FRONTMATTER = "---\nname: lat-md-conventions\ndescription: >-\n Rules lat check enforces in this project: Di\u00E1taxis mode placement for every\n lat.md/ document, mandatory @lat: code refs starting at the application\n entrypoint, and test specs wired to their tests. Load alongside lat-md\n whenever creating or editing files under lat.md/.\n---\n\n";
2
+ export declare function readForkConventions(): string;
3
+ /**
4
+ * Replace the fork block in `content`, or append one when absent.
5
+ *
6
+ * Returns null when the block is already byte-identical, so the caller can
7
+ * stay quiet rather than announce a write that did not happen.
8
+ */
9
+ export declare function spliceForkBlock(content: string, body: string): string | null;
10
+ /** Read the body currently fenced by the fork markers, or null if absent. */
11
+ export declare function extractForkBlock(content: string): string | null;
12
+ /**
13
+ * Write the fork's conventions into every instruction file and skill directory
14
+ * the selected agents produced.
15
+ *
16
+ * `fileHashes` is upstream's record, threaded through so `writeInitMeta`
17
+ * persists these entries alongside its own; a stored hash is what lets a later
18
+ * run tell "the user edited our block" from "the template moved on".
19
+ */
20
+ export declare function writeForkInstructions(root: string, latDir: string, fileHashes: Record<string, string>, ask: (message: string) => Promise<boolean>): Promise<void>;