@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
@@ -0,0 +1,445 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { isAbsolute, resolve } from 'node:path';
4
+ // @lat: [[upstream-guard#The upstream guard]]
5
+ export const SYNC_POINT_FILE = 'fork-upstream-sync-point';
6
+ /**
7
+ * The commit this fork was originally cut from. It is not the guard's
8
+ * baseline — see [[upstream-guard#The upstream guard#The sync point]] for why a frozen
9
+ * one cannot tell the fork's changes from upstream's.
10
+ */
11
+ export const ORIGINAL_FORK_POINT = '65d2f5b220853b2b10dca3de18d511f575bc12c7';
12
+ // @lat: [[upstream-guard#The upstream guard#The allowlist]]
13
+ export const ALLOWLIST_FILE = 'fork-upstream-allowlist.tsv';
14
+ export const REASON_PLACEHOLDER = 'TODO';
15
+ const ALLOWLIST_HEADER = [
16
+ '# Upstream files this fork is permitted to modify.',
17
+ '#',
18
+ '# Generated by `node dist/src/fork/upstream-guard-cli.js --regenerate`.',
19
+ '# One entry per line: the repo-relative path, a TAB, then the reason the',
20
+ '# fork must diverge in that file. A placeholder reason fails the check —',
21
+ '# regenerate writes the marker, a person replaces it with the real reason.',
22
+ '#',
23
+ '# Paths are relative to the sync point in fork-upstream-sync-point, so an',
24
+ '# upstream merge moves this list. Regenerate in the same commit.',
25
+ '#',
26
+ '# Deleting an upstream file is never allowlistable. See lat.md/fork.md.',
27
+ '',
28
+ ];
29
+ export function parseAllowlist(text) {
30
+ const entries = [];
31
+ const errors = [];
32
+ const seen = new Map();
33
+ text.split('\n').forEach((raw, index) => {
34
+ const line = index + 1;
35
+ const stripped = raw.replace(/\r$/, '');
36
+ if (stripped.trim() === '' || stripped.trimStart().startsWith('#'))
37
+ return;
38
+ const tab = stripped.indexOf('\t');
39
+ const split = tab >= 0 ? tab : stripped.search(/\s/);
40
+ if (split < 0) {
41
+ errors.push(`${ALLOWLIST_FILE}:${line}: entry has no reason: ${stripped}`);
42
+ return;
43
+ }
44
+ const path = stripped.slice(0, split).trim();
45
+ const reason = stripped.slice(split + 1).trim();
46
+ if (path === '') {
47
+ errors.push(`${ALLOWLIST_FILE}:${line}: entry has no path`);
48
+ return;
49
+ }
50
+ if (reason === '') {
51
+ errors.push(`${ALLOWLIST_FILE}:${line}: entry has no reason: ${path}`);
52
+ return;
53
+ }
54
+ const first = seen.get(path);
55
+ if (first !== undefined) {
56
+ errors.push(`${ALLOWLIST_FILE}:${line}: duplicate entry for ${path} ` +
57
+ `(first at line ${first})`);
58
+ return;
59
+ }
60
+ seen.set(path, line);
61
+ entries.push({ path, reason, line });
62
+ });
63
+ return { entries, errors };
64
+ }
65
+ export function renderAllowlist(entries) {
66
+ const sorted = [...entries].sort((a, b) => (a.path < b.path ? -1 : 1));
67
+ const body = sorted.map((e) => `${e.path}\t${e.reason}`);
68
+ return [...ALLOWLIST_HEADER, ...body, ''].join('\n');
69
+ }
70
+ export function needsReason(reason) {
71
+ return reason.trimStart().toUpperCase().startsWith(REASON_PLACEHOLDER);
72
+ }
73
+ export function parseNameStatus(raw) {
74
+ const fields = raw.split('\0').filter((f) => f !== '');
75
+ const out = [];
76
+ for (let i = 0; i + 1 < fields.length; i += 2) {
77
+ out.push({ status: fields[i], path: fields[i + 1] });
78
+ }
79
+ return out;
80
+ }
81
+ export function classify(input) {
82
+ const findings = [];
83
+ const touched = [];
84
+ for (const entry of input.diff) {
85
+ if (!input.upstream.has(entry.path))
86
+ continue;
87
+ touched.push(entry.path);
88
+ if (entry.status.startsWith('D')) {
89
+ findings.push({
90
+ kind: 'deletion',
91
+ path: entry.path,
92
+ message: `${entry.path}: deletes an upstream file.\n` +
93
+ ' No allowlist entry can permit this. A file this fork deletes ' +
94
+ 'conflicts on\n' +
95
+ ' every future merge that touches it upstream; a file this fork ' +
96
+ 'modifies\n' +
97
+ ' conflicts once and stays resolved. Leave the file in place and ' +
98
+ 'change\n' +
99
+ ' what it does, or turn it off out of band.',
100
+ });
101
+ continue;
102
+ }
103
+ const reason = input.allowlist.get(entry.path);
104
+ if (reason === undefined) {
105
+ findings.push({
106
+ kind: 'unallowlisted',
107
+ path: entry.path,
108
+ message: `${entry.path}: modifies an upstream file that is not allowlisted.\n` +
109
+ ' Move the change into a new fork-owned file, or add the path to ' +
110
+ `${ALLOWLIST_FILE}\n` +
111
+ ' with the reason the fork must diverge here.',
112
+ });
113
+ continue;
114
+ }
115
+ if (needsReason(reason)) {
116
+ findings.push({
117
+ kind: 'missing-reason',
118
+ path: entry.path,
119
+ message: `${entry.path}: allowlisted with a placeholder reason.\n` +
120
+ ` Replace the ${REASON_PLACEHOLDER} in ${ALLOWLIST_FILE} with why ` +
121
+ 'this file must diverge.',
122
+ });
123
+ }
124
+ }
125
+ const touchedSet = new Set(touched);
126
+ const warnings = [];
127
+ for (const path of input.allowlist.keys()) {
128
+ if (!touchedSet.has(path)) {
129
+ warnings.push(`${path}: allowlisted but no longer modified — regenerate to drop it.`);
130
+ }
131
+ }
132
+ return { findings, warnings, touched };
133
+ }
134
+ function git(repo, args, stdin) {
135
+ const result = spawnSync('git', ['-C', repo, ...args], {
136
+ input: stdin,
137
+ encoding: 'utf-8',
138
+ maxBuffer: 64 * 1024 * 1024,
139
+ });
140
+ if (result.error)
141
+ throw result.error;
142
+ return result;
143
+ }
144
+ export function repoRoot(from) {
145
+ const result = git(from, ['rev-parse', '--show-toplevel']);
146
+ if (result.status !== 0) {
147
+ throw new Error(`not a git repository: ${from}`);
148
+ }
149
+ return result.stdout.trim();
150
+ }
151
+ export function hasCommit(repo, rev) {
152
+ return git(repo, ['cat-file', '-e', `${rev}^{commit}`]).status === 0;
153
+ }
154
+ export function diffAgainst(repo, rev) {
155
+ const result = git(repo, [
156
+ 'diff',
157
+ '--no-renames',
158
+ '--name-status',
159
+ '-z',
160
+ rev,
161
+ '--',
162
+ ]);
163
+ if (result.status !== 0) {
164
+ throw new Error(`git diff against ${rev} failed: ${result.stderr.trim()}`);
165
+ }
166
+ return parseNameStatus(result.stdout);
167
+ }
168
+ export function upstreamPaths(repo, rev, paths) {
169
+ const out = new Set();
170
+ if (paths.length === 0)
171
+ return out;
172
+ const batchable = paths.filter((p) => !p.includes('\n'));
173
+ const input = batchable.map((p) => `${rev}:${p}`).join('\n') + '\n';
174
+ const result = git(repo, ['cat-file', '--batch-check'], input);
175
+ const lines = result.stdout.split('\n').filter((l) => l !== '');
176
+ const aligned = lines.length === batchable.length;
177
+ if (aligned) {
178
+ lines.forEach((line, index) => {
179
+ if (/ blob \d+$/.test(line))
180
+ out.add(batchable[index]);
181
+ });
182
+ }
183
+ const leftover = aligned ? paths.filter((p) => p.includes('\n')) : paths;
184
+ for (const path of leftover) {
185
+ const probe = git(repo, ['cat-file', '-t', `${rev}:${path}`]);
186
+ if (probe.status === 0 && probe.stdout.trim() === 'blob')
187
+ out.add(path);
188
+ }
189
+ return out;
190
+ }
191
+ // @lat: [[upstream-guard#The upstream guard#The sync point]]
192
+ export function parseSyncPoint(text) {
193
+ for (const raw of text.split('\n')) {
194
+ const line = raw.replace(/\r$/, '').trim();
195
+ if (line === '' || line.startsWith('#'))
196
+ continue;
197
+ return line.split(/\s/)[0];
198
+ }
199
+ return undefined;
200
+ }
201
+ export function renderSyncPoint(previous, rev) {
202
+ const kept = previous
203
+ .split('\n')
204
+ .filter((raw) => {
205
+ const line = raw.replace(/\r$/, '').trim();
206
+ return line === '' || line.startsWith('#');
207
+ })
208
+ .join('\n')
209
+ .replace(/\n+$/, '');
210
+ return `${kept}\n\n${rev}\n`;
211
+ }
212
+ export function isAncestorOfHead(repo, rev) {
213
+ return git(repo, ['merge-base', '--is-ancestor', rev, 'HEAD']).status === 0;
214
+ }
215
+ export function isShallow(repo) {
216
+ const result = git(repo, ['rev-parse', '--is-shallow-repository']);
217
+ return result.stdout.trim() === 'true';
218
+ }
219
+ // @lat: [[upstream-guard#The upstream guard#Running it in CI]]
220
+ export function fetchSyncPoint(repo, rev) {
221
+ const result = git(repo, ['fetch', '--no-tags', '--depth=1', 'origin', rev]);
222
+ return result.status === 0 && hasCommit(repo, rev);
223
+ }
224
+ function pathFor(repo, file) {
225
+ return isAbsolute(file) ? file : resolve(repo, file);
226
+ }
227
+ function allowlistPathFor(options) {
228
+ return pathFor(options.repo, options.allowlistFile);
229
+ }
230
+ function syncPointPathFor(options) {
231
+ return pathFor(options.repo, options.syncPointFile ?? SYNC_POINT_FILE);
232
+ }
233
+ function loadAllowlist(file) {
234
+ if (!existsSync(file))
235
+ return { entries: [], errors: [] };
236
+ return parseAllowlist(readFileSync(file, 'utf-8'));
237
+ }
238
+ function fail(report) {
239
+ return { ok: false, report, findings: [], warnings: [] };
240
+ }
241
+ export function runGuard(options) {
242
+ const root = repoRoot(options.repo);
243
+ const opts = { ...options, repo: root };
244
+ const syncFile = syncPointPathFor(opts);
245
+ if (opts.setSyncPoint !== undefined) {
246
+ return recordSyncPoint(root, syncFile, opts.setSyncPoint);
247
+ }
248
+ let syncPoint = opts.syncPoint;
249
+ if (syncPoint === undefined) {
250
+ if (!existsSync(syncFile)) {
251
+ return fail(`upstream-guard: no sync point recorded at ${syncFile}\n` +
252
+ ' The guard measures everything from the last upstream commit ' +
253
+ 'merged\n' +
254
+ ' into this fork. Record it:\n' +
255
+ ' node dist/src/fork/upstream-guard-cli.js --set-sync-point ' +
256
+ '<rev>\n');
257
+ }
258
+ syncPoint = parseSyncPoint(readFileSync(syncFile, 'utf-8'));
259
+ if (syncPoint === undefined || syncPoint === '') {
260
+ return fail(`upstream-guard: ${syncFile} holds no revision.\n` +
261
+ ' It must contain one commit id on a line of its own.\n');
262
+ }
263
+ }
264
+ const present = hasCommit(root, syncPoint) ||
265
+ (opts.fetch && fetchSyncPoint(root, syncPoint));
266
+ if (!present) {
267
+ return fail(`upstream-guard: sync point ${syncPoint} is not in this repository.\n` +
268
+ ' A shallow clone does not carry it. Fetch it first:\n' +
269
+ ` git fetch --no-tags --depth=1 origin ${syncPoint}\n`);
270
+ }
271
+ // A sync point off the current history makes every number below
272
+ // meaningless — the diff would report upstream's own commits as the fork's.
273
+ // Truncated history cannot answer the question, so shallow clones skip it.
274
+ if (!isShallow(root) && !isAncestorOfHead(root, syncPoint)) {
275
+ return fail(`upstream-guard: sync point ${syncPoint.slice(0, 7)} is not an ancestor ` +
276
+ 'of HEAD.\n' +
277
+ ' Merge that upstream commit before recording it, or record the ' +
278
+ 'one\n' +
279
+ ' this branch actually descends from.\n');
280
+ }
281
+ const file = allowlistPathFor(opts);
282
+ const diff = diffAgainst(root, syncPoint);
283
+ const upstream = upstreamPaths(root, syncPoint, diff.map((d) => d.path));
284
+ if (opts.regenerate) {
285
+ return regenerateInto(file, diff, upstream);
286
+ }
287
+ const parsed = loadAllowlist(file);
288
+ if (!existsSync(file)) {
289
+ return fail(`upstream-guard: allowlist not found at ${file}\n`);
290
+ }
291
+ const map = new Map(parsed.entries.map((e) => [e.path, e.reason]));
292
+ const result = classify({ diff, upstream, allowlist: map });
293
+ const findings = [
294
+ ...parsed.errors.map((message) => ({ kind: 'allowlist-error', path: file, message })),
295
+ ...result.findings,
296
+ ];
297
+ const lines = [];
298
+ if (findings.length > 0) {
299
+ lines.push(`upstream-guard: ${findings.length} violation(s) against sync point ` +
300
+ `${syncPoint.slice(0, 7)}`, '');
301
+ for (const finding of findings)
302
+ lines.push(` ${finding.message}`, '');
303
+ lines.push('The fork keeps its diff small: new behaviour goes in new fork-owned ' +
304
+ 'files,', 'and an upstream file takes an import plus one call line. See ' +
305
+ 'lat.md/fork.md.');
306
+ }
307
+ else {
308
+ lines.push(`upstream-guard: ok — ${result.touched.length} upstream file(s) ` +
309
+ 'modified, all allowlisted.');
310
+ }
311
+ for (const warning of result.warnings)
312
+ lines.push(` warning: ${warning}`);
313
+ return {
314
+ ok: findings.length === 0,
315
+ report: lines.join('\n') + '\n',
316
+ findings,
317
+ warnings: result.warnings,
318
+ };
319
+ }
320
+ // @lat: [[upstream-guard#The upstream guard#The sync point]]
321
+ function recordSyncPoint(repo, file, rev) {
322
+ const resolved = git(repo, ['rev-parse', `${rev}^{commit}`]);
323
+ if (resolved.status !== 0) {
324
+ return fail(`upstream-guard: ${rev} is not a commit in this repository.\n`);
325
+ }
326
+ const sha = resolved.stdout.trim();
327
+ if (!isShallow(repo) && !isAncestorOfHead(repo, sha)) {
328
+ return fail(`upstream-guard: ${sha.slice(0, 7)} is not an ancestor of HEAD.\n` +
329
+ ' Record a sync point only once its merge has landed.\n');
330
+ }
331
+ const previous = existsSync(file) ? readFileSync(file, 'utf-8') : '';
332
+ writeFileSync(file, renderSyncPoint(previous, sha), 'utf-8');
333
+ return {
334
+ ok: true,
335
+ report: `upstream-guard: sync point is now ${sha}\n` +
336
+ ' regenerate the allowlist next — the set of files the fork ' +
337
+ 'diverges in has moved.\n',
338
+ findings: [],
339
+ warnings: [],
340
+ };
341
+ }
342
+ function regenerateInto(file, diff, upstream) {
343
+ const existing = new Map(loadAllowlist(file).entries.map((e) => [e.path, e.reason]));
344
+ const entries = [];
345
+ const deletions = [];
346
+ const fresh = [];
347
+ for (const entry of diff) {
348
+ if (!upstream.has(entry.path))
349
+ continue;
350
+ if (entry.status.startsWith('D')) {
351
+ deletions.push({
352
+ kind: 'deletion',
353
+ path: entry.path,
354
+ message: `${entry.path}: deletes an upstream file, which is never ` +
355
+ 'allowlistable — left out of the regenerated list.',
356
+ });
357
+ continue;
358
+ }
359
+ const reason = existing.get(entry.path) ??
360
+ `${REASON_PLACEHOLDER}: why must this upstream file diverge?`;
361
+ if (!existing.has(entry.path))
362
+ fresh.push(entry.path);
363
+ entries.push({ path: entry.path, reason, line: 0 });
364
+ }
365
+ writeFileSync(file, renderAllowlist(entries), 'utf-8');
366
+ const lines = [
367
+ `upstream-guard: wrote ${entries.length} entr(ies) to ${file}`,
368
+ ];
369
+ for (const path of fresh) {
370
+ lines.push(` new entry needs a reason: ${path}`);
371
+ }
372
+ for (const finding of deletions)
373
+ lines.push(` ${finding.message}`);
374
+ return {
375
+ ok: deletions.length === 0,
376
+ report: lines.join('\n') + '\n',
377
+ findings: deletions,
378
+ warnings: [],
379
+ };
380
+ }
381
+ const USAGE = `Usage: node dist/src/fork/upstream-guard-cli.js [options]
382
+
383
+ --regenerate rewrite the allowlist from the current diff
384
+ --fetch fetch the sync point from origin if it is absent
385
+ --set-sync-point <rev> record <rev> as the last merged upstream commit
386
+ --sync-point <rev> check against <rev> instead of the recorded one
387
+ --repo <dir> default the current working directory
388
+ --allowlist <file> default <repo>/${ALLOWLIST_FILE}
389
+ --sync-point-file <f> default <repo>/${SYNC_POINT_FILE}
390
+ -h, --help this text
391
+ `;
392
+ export function parseArgs(argv) {
393
+ const options = {
394
+ repo: process.cwd(),
395
+ allowlistFile: ALLOWLIST_FILE,
396
+ regenerate: false,
397
+ fetch: false,
398
+ };
399
+ for (let i = 0; i < argv.length; i += 1) {
400
+ const arg = argv[i];
401
+ if (arg === '-h' || arg === '--help')
402
+ return { help: true };
403
+ else if (arg === '--regenerate' || arg === '--write')
404
+ options.regenerate = true;
405
+ else if (arg === '--fetch')
406
+ options.fetch = true;
407
+ else if (arg === '--set-sync-point')
408
+ options.setSyncPoint = argv[++i] ?? '';
409
+ else if (arg === '--sync-point')
410
+ options.syncPoint = argv[++i] ?? '';
411
+ else if (arg === '--sync-point-file')
412
+ options.syncPointFile = argv[++i] ?? '';
413
+ else if (arg === '--repo')
414
+ options.repo = argv[++i] ?? '';
415
+ else if (arg === '--allowlist')
416
+ options.allowlistFile = argv[++i] ?? '';
417
+ else
418
+ throw new Error(`unknown argument: ${arg}`);
419
+ }
420
+ return options;
421
+ }
422
+ export function main(argv) {
423
+ let options;
424
+ try {
425
+ options = parseArgs(argv);
426
+ }
427
+ catch (error) {
428
+ process.stderr.write(`${error.message}\n\n${USAGE}`);
429
+ return 2;
430
+ }
431
+ if ('help' in options) {
432
+ process.stdout.write(USAGE);
433
+ return 0;
434
+ }
435
+ let result;
436
+ try {
437
+ result = runGuard(options);
438
+ }
439
+ catch (error) {
440
+ process.stderr.write(`upstream-guard: ${error.message}\n`);
441
+ return 2;
442
+ }
443
+ process.stdout.write(result.report);
444
+ return result.ok ? 0 : 1;
445
+ }
@@ -8,6 +8,8 @@ export type GraphNode = {
8
8
  mode?: string;
9
9
  status?: string;
10
10
  reviewedHash?: string;
11
+ /** sha256 of a section's own prose, excluding its heading and children. */
12
+ bodyHash?: string;
11
13
  };
12
14
  export type GraphEdge = {
13
15
  from: string;
@@ -8,15 +8,18 @@
8
8
  // `quoteUntrusted`) is correct here: node ids and labels must not be
9
9
  // truncated, and quoting them would corrupt them as identifiers.
10
10
  import { execFile } from 'node:child_process';
11
+ import { createHash } from 'node:crypto';
11
12
  import { join, relative } from 'node:path';
12
13
  import { promisify } from 'node:util';
13
- import { extractRefs, buildFileIndex, buildSectionSlugIndex, parseSections, parseFrontmatter, resolveRef, } from './lattice.js';
14
- import { scanCodeRefs } from './code-refs.js';
15
- import { DIATAXIS_MODES, MODE_DIRS } from './cli/check-mode.js';
16
- import { readProvenance } from './cli/check-status.js';
17
- import { cleanUntrustedId } from './untrusted.js';
18
- import { toPosix } from './walk.js';
14
+ import { extractRefs, buildFileIndex, buildSectionSlugIndex, parseSections, parseFrontmatter, resolveRef, } from '@plebeiantech/lat.md-core/lattice';
15
+ import { scanCodeRefs } from '@plebeiantech/lat.md-core/code-refs';
16
+ import { DIATAXIS_MODES, MODE_DIRS, } from '@plebeiantech/lat.md-core/cli/check-mode';
17
+ import { readProvenance } from '@plebeiantech/lat.md-core/cli/check-status';
18
+ import { cleanUntrustedId } from '@plebeiantech/lat.md-core/untrusted';
19
+ import { toPosix } from '@plebeiantech/lat.md-core/path';
19
20
  const execFileAsync = promisify(execFile);
21
+ /** Bytes. Node's `execFile` default is 1 MB, which a large document exceeds. */
22
+ const GIT_MAX_BUFFER = 256 * 1024 * 1024;
20
23
  function docId(relPath) {
21
24
  return `doc:${relPath}`;
22
25
  }
@@ -41,6 +44,15 @@ function modeForFile(relPath, fm) {
41
44
  }
42
45
  return undefined;
43
46
  }
47
+ /** sha256 over lines (startLine, endLine], with trailing blanks normalized. */
48
+ function hashSectionBody(lines, section) {
49
+ const body = lines
50
+ .slice(section.startLine, section.endLine)
51
+ .map((line) => line.replace(/[ \t]+$/, ''))
52
+ .join('\n')
53
+ .trim();
54
+ return createHash('sha256').update(body, 'utf-8').digest('hex');
55
+ }
44
56
  function tagsForFile(fm) {
45
57
  const raw = fm['tags'];
46
58
  if (Array.isArray(raw))
@@ -107,10 +119,12 @@ export async function buildGraph(files, projectRoot, includeCode) {
107
119
  addNode({ id: tId, type: 'tag', label: cleanUntrustedId(tag) });
108
120
  addEdge({ from: dId, to: tId, type: 'tag' });
109
121
  }
122
+ const bodyLines = f.content.replace(/\r\n/g, '\n').split('\n');
110
123
  const walkSection = (section, parentId) => {
111
124
  const sId = ensureSectionNode(section.id, section.heading);
112
125
  const node = nodes.get(sId);
113
126
  node.file = f.relPath;
127
+ node.bodyHash = hashSectionBody(bodyLines, section);
114
128
  addEdge({ from: parentId, to: sId, type: 'contains' });
115
129
  for (const child of section.children) {
116
130
  walkSection(child, sId);
@@ -129,6 +143,13 @@ export async function buildGraph(files, projectRoot, includeCode) {
129
143
  };
130
144
  flatten(allSections);
131
145
  const sectionIds = new Set(flat.map((s) => s.id.toLowerCase()));
146
+ const canonicalIds = new Map();
147
+ for (const s of flat) {
148
+ const key = s.id.toLowerCase();
149
+ if (!canonicalIds.has(key))
150
+ canonicalIds.set(key, s.id);
151
+ }
152
+ const canonicalize = (id) => canonicalIds.get(id.toLowerCase());
132
153
  const fileIndex = buildFileIndex(allSections);
133
154
  const slugIndex = buildSectionSlugIndex(allSections);
134
155
  // Wiki links: section -> section (or section -> code target).
@@ -137,9 +158,10 @@ export async function buildGraph(files, projectRoot, includeCode) {
137
158
  const refs = extractRefs(absPath, f.content, projectRoot);
138
159
  for (const ref of refs) {
139
160
  const { resolved, ambiguous } = resolveRef(ref.target, sectionIds, fileIndex, slugIndex);
140
- const fromId = ensureSectionNode(ref.fromSection);
141
- if (!ambiguous && sectionIds.has(resolved.toLowerCase())) {
142
- const toId = ensureSectionNode(resolved);
161
+ const fromId = ensureSectionNode(canonicalize(ref.fromSection) ?? ref.fromSection);
162
+ const canonicalTarget = ambiguous ? undefined : canonicalize(resolved);
163
+ if (canonicalTarget !== undefined) {
164
+ const toId = ensureSectionNode(canonicalTarget);
143
165
  addEdge({ from: fromId, to: toId, type: 'wikilink' });
144
166
  }
145
167
  else {
@@ -161,9 +183,8 @@ export async function buildGraph(files, projectRoot, includeCode) {
161
183
  const label = `${ref.file}:${ref.line}`;
162
184
  const cId = codeId(label);
163
185
  addNode({ id: cId, type: 'code', label: cleanUntrustedId(label) });
164
- const toId = !ambiguous && sectionIds.has(resolved.toLowerCase())
165
- ? ensureSectionNode(resolved)
166
- : ensureSectionNode(ref.target);
186
+ const canonicalTarget = ambiguous ? undefined : canonicalize(resolved);
187
+ const toId = ensureSectionNode(canonicalTarget ?? ref.target);
167
188
  addEdge({ from: cId, to: toId, type: 'code-ref' });
168
189
  }
169
190
  }
@@ -171,7 +192,7 @@ export async function buildGraph(files, projectRoot, includeCode) {
171
192
  }
172
193
  /** Read the working tree's lat.md files straight off disk. */
173
194
  export async function loadWorkingTreeFiles(latticeDir, projectRoot) {
174
- const { listLatticeFiles } = await import('./lattice.js');
195
+ const { listLatticeFiles } = await import('@plebeiantech/lat.md-core/lattice');
175
196
  const { readFile } = await import('node:fs/promises');
176
197
  const paths = await listLatticeFiles(latticeDir);
177
198
  const files = [];
@@ -190,20 +211,18 @@ export async function loadRevisionFiles(projectRoot, latticeDir, rev) {
190
211
  const latRelDir = toPosix(relative(projectRoot, latticeDir));
191
212
  let lsOutput;
192
213
  try {
193
- const { stdout } = await execFileAsync('git', ['ls-tree', '-r', '--name-only', rev, '--', latRelDir], { cwd: projectRoot });
214
+ const { stdout } = await execFileAsync('git', ['ls-tree', '-r', '-z', '--name-only', rev, '--', latRelDir], { cwd: projectRoot, maxBuffer: GIT_MAX_BUFFER });
194
215
  lsOutput = stdout;
195
216
  }
196
217
  catch (err) {
197
218
  throw new Error(`failed to list "${latRelDir}" at revision "${rev}": ${err instanceof Error ? err.message : String(err)}`);
198
219
  }
199
- const paths = lsOutput
200
- .split('\n')
201
- .map((l) => l.trim())
202
- .filter((l) => l.endsWith('.md'));
220
+ const paths = lsOutput.split('\0').filter((l) => l.endsWith('.md'));
203
221
  const files = [];
204
222
  for (const p of paths) {
205
223
  const { stdout } = await execFileAsync('git', ['show', `${rev}:${p}`], {
206
224
  cwd: projectRoot,
225
+ maxBuffer: GIT_MAX_BUFFER,
207
226
  });
208
227
  files.push({ relPath: p, content: stdout });
209
228
  }
@@ -221,7 +240,7 @@ export function diffGraphs(before, after) {
221
240
  if (!prior) {
222
241
  added.push(id);
223
242
  }
224
- else if (prior.label !== node.label) {
243
+ else if (prior.bodyHash !== node.bodyHash || prior.label !== node.label) {
225
244
  changed.push(id);
226
245
  }
227
246
  }
@@ -10,4 +10,5 @@ import type { Store } from './types.js';
10
10
  * containing a dot.
11
11
  */
12
12
  export declare function slugify(absPath: string): string;
13
+ export declare function readFileCapped(path: string): string | undefined;
13
14
  export declare const claudeMemoryStore: Store;
@@ -84,16 +84,45 @@ function unquote(value) {
84
84
  }
85
85
  return value;
86
86
  }
87
+ /** The body of a leading `---` / `---` block, or undefined if there is none. */
88
+ function frontmatterBlock(text) {
89
+ const match = /^---\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/.exec(text);
90
+ return match ? match[1] : undefined;
91
+ }
87
92
  /** Pull a `key: value` line out of YAML-ish frontmatter, quotes stripped. */
88
93
  function frontmatterField(text, key) {
89
- const re = new RegExp(`^${key}:\\s*(.*)$`, 'm');
90
- const match = re.exec(text);
94
+ const block = frontmatterBlock(text);
95
+ if (block === undefined)
96
+ return undefined;
97
+ const re = new RegExp(`^${escapeRegExp(key)}:[ \\t]*(.*)$`, 'm');
98
+ const match = re.exec(block);
91
99
  return match ? unquote(match[1].trim()) : undefined;
92
100
  }
93
- function readFileCapped(path) {
101
+ function severedTailBytes(buf) {
102
+ for (let back = 1; back <= 3 && back <= buf.length; back++) {
103
+ const byte = buf[buf.length - back];
104
+ if ((byte & 0xc0) === 0x80)
105
+ continue;
106
+ let width = 0;
107
+ if ((byte & 0xe0) === 0xc0)
108
+ width = 2;
109
+ else if ((byte & 0xf0) === 0xe0)
110
+ width = 3;
111
+ else if ((byte & 0xf8) === 0xf0)
112
+ width = 4;
113
+ return width > back ? back : 0;
114
+ }
115
+ return 0;
116
+ }
117
+ export function readFileCapped(path) {
94
118
  try {
95
119
  const buf = readFileSync(path);
96
- return buf.subarray(0, MAX_FILE_BYTES).toString('utf-8');
120
+ if (buf.length <= MAX_FILE_BYTES)
121
+ return buf.toString('utf-8');
122
+ const capped = buf.subarray(0, MAX_FILE_BYTES);
123
+ return capped
124
+ .subarray(0, capped.length - severedTailBytes(capped))
125
+ .toString('utf-8');
97
126
  }
98
127
  catch {
99
128
  return undefined;
@@ -1,7 +1,7 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
- import { parseFrontmatter } from '../lattice.js';
4
- import { UNTRUSTED_NOTICE, quoteUntrusted, cleanUntrustedId, } from '../untrusted.js';
3
+ import { parseFrontmatter } from '@plebeiantech/lat.md-core/lattice';
4
+ import { UNTRUSTED_NOTICE, quoteUntrusted, cleanUntrustedId, } from '@plebeiantech/lat.md-core/untrusted';
5
5
  import { tagsToTerms, capHits } from './ranking.js';
6
6
  const STORE_GROUP_LABELS = {
7
7
  cq: 'cq:',