@plebeiantech/lat.md 0.12.3-fork.12 → 0.12.3-fork.14

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 (275) hide show
  1. package/README.md +24 -0
  2. package/dist/src/cli/check-context.d.ts +56 -0
  3. package/dist/src/cli/check-context.js +215 -0
  4. package/dist/src/cli/check-coverage.js +9 -5
  5. package/dist/src/cli/check-frontmatter.js +1 -1
  6. package/dist/src/cli/check-mode.js +33 -3
  7. package/dist/src/cli/check-status.js +1 -1
  8. package/dist/src/cli/check.d.ts +19 -12
  9. package/dist/src/cli/check.js +259 -206
  10. package/dist/src/cli/context.js +1 -1
  11. package/dist/src/cli/expand.d.ts +1 -2
  12. package/dist/src/cli/expand.js +52 -5
  13. package/dist/src/cli/external.d.ts +11 -0
  14. package/dist/src/cli/external.js +185 -0
  15. package/dist/src/cli/fork-instructions.d.ts +1 -0
  16. package/dist/src/cli/fork-instructions.js +3 -2
  17. package/dist/src/cli/hook.d.ts +26 -0
  18. package/dist/src/cli/hook.js +112 -40
  19. package/dist/src/cli/index.js +55 -3
  20. package/dist/src/cli/init.d.ts +1 -0
  21. package/dist/src/cli/init.js +40 -2
  22. package/dist/src/cli/locate.js +4 -3
  23. package/dist/src/cli/refs.d.ts +2 -1
  24. package/dist/src/cli/refs.js +88 -43
  25. package/dist/src/cli/reindex.js +2 -1
  26. package/dist/src/cli/search.d.ts +12 -3
  27. package/dist/src/cli/search.js +45 -49
  28. package/dist/src/cli/section.d.ts +3 -1
  29. package/dist/src/cli/section.js +114 -38
  30. package/dist/src/code-refs.d.ts +17 -5
  31. package/dist/src/code-refs.js +246 -65
  32. package/dist/src/context.d.ts +3 -0
  33. package/dist/src/document-formats.d.ts +7 -0
  34. package/dist/src/document-formats.js +39 -0
  35. package/dist/src/extensions/alert-marker.d.ts +25 -0
  36. package/dist/src/extensions/alert-marker.js +55 -0
  37. package/dist/src/external-documents.d.ts +56 -0
  38. package/dist/src/external-documents.js +404 -0
  39. package/dist/src/external-sources.d.ts +136 -0
  40. package/dist/src/external-sources.js +1098 -0
  41. package/dist/src/fork/antigravity-hook.d.ts +47 -0
  42. package/dist/src/fork/antigravity-hook.js +424 -0
  43. package/dist/src/fork/antigravity-init.d.ts +17 -0
  44. package/dist/src/fork/antigravity-init.js +124 -0
  45. package/dist/src/fork/frontmatter-fields.d.ts +36 -0
  46. package/dist/src/fork/frontmatter-fields.js +23 -0
  47. package/dist/src/format.d.ts +5 -2
  48. package/dist/src/format.js +8 -3
  49. package/dist/src/graph-export.js +1 -1
  50. package/dist/src/lattice-model.d.ts +77 -0
  51. package/dist/src/lattice-model.js +464 -0
  52. package/dist/src/lattice.d.ts +9 -104
  53. package/dist/src/lattice.js +69 -516
  54. package/dist/src/markdown-analysis-cache.d.ts +20 -0
  55. package/dist/src/markdown-analysis-cache.js +139 -0
  56. package/dist/src/markdown-analysis-worker.d.ts +16 -0
  57. package/dist/src/markdown-analysis-worker.js +33 -0
  58. package/dist/src/markdown-analysis.d.ts +50 -0
  59. package/dist/src/markdown-analysis.js +139 -0
  60. package/dist/src/markdown-analyzer-loader.d.ts +4 -0
  61. package/dist/src/markdown-analyzer-loader.js +13 -0
  62. package/dist/src/markdown-validation.d.ts +25 -0
  63. package/dist/src/markdown-validation.js +104 -0
  64. package/dist/src/mcp/server.js +27 -11
  65. package/dist/src/parser-cache.d.ts +17 -0
  66. package/dist/src/parser-cache.js +65 -0
  67. package/dist/src/parser-import.d.ts +8 -0
  68. package/dist/src/parser-import.js +1 -0
  69. package/dist/src/parser.js +80 -5
  70. package/dist/src/path.d.ts +2 -0
  71. package/dist/src/path.js +4 -0
  72. package/dist/src/profiler.d.ts +16 -0
  73. package/dist/src/profiler.js +81 -0
  74. package/dist/src/project-analysis.d.ts +59 -0
  75. package/dist/src/project-analysis.js +233 -0
  76. package/dist/src/project-discovery.d.ts +6 -0
  77. package/dist/src/project-discovery.js +30 -0
  78. package/dist/src/repository-path.d.ts +11 -0
  79. package/dist/src/repository-path.js +52 -0
  80. package/dist/src/search/db.d.ts +1 -1
  81. package/dist/src/search/db.js +2 -2
  82. package/dist/src/search/embedder.d.ts +2 -1
  83. package/dist/src/search/embedder.js +6 -5
  84. package/dist/src/search/index.d.ts +2 -1
  85. package/dist/src/search/index.js +10 -8
  86. package/dist/src/search/query.d.ts +20 -0
  87. package/dist/src/search/query.js +67 -0
  88. package/dist/src/search/search.d.ts +3 -1
  89. package/dist/src/search/search.js +5 -2
  90. package/dist/src/source-formats.d.ts +7 -0
  91. package/dist/src/source-formats.js +26 -0
  92. package/dist/src/source-parser.d.ts +44 -4
  93. package/dist/src/source-parser.js +494 -57
  94. package/dist/src/view/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  95. package/dist/src/view/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  96. package/dist/src/view/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  97. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  98. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  99. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  100. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  101. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  102. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  103. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  104. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  105. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  106. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  107. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  108. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  109. package/dist/src/view/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  110. package/dist/src/view/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  111. package/dist/src/view/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  112. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  113. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  114. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  115. package/dist/src/view/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  116. package/dist/src/view/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  117. package/dist/src/view/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  118. package/dist/src/view/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  119. package/dist/src/view/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  120. package/dist/src/view/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  121. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  122. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  123. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  124. package/dist/src/view/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  125. package/dist/src/view/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  126. package/dist/src/view/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  127. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  128. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  129. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  130. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  131. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  132. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  133. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  134. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  135. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  136. package/dist/src/view/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  137. package/dist/src/view/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  138. package/dist/src/view/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  139. package/dist/src/view/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  140. package/dist/src/view/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  141. package/dist/src/view/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  142. package/dist/src/view/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  143. package/dist/src/view/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  144. package/dist/src/view/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  145. package/dist/src/view/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  146. package/dist/src/view/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  147. package/dist/src/view/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  148. package/dist/src/view/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  149. package/dist/src/view/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  150. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  151. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  152. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  153. package/dist/src/view/client/assets/MarkdownEditor-B7quqPb8.js +28 -0
  154. package/dist/src/view/client/assets/OrbitControls-DwcpQNja.js +1 -0
  155. package/dist/src/view/client/assets/STLLoader-CLWTKt5i.js +1 -0
  156. package/dist/src/view/client/assets/abnfDiagram-VCTEODGH-V7-T6qsl.js +1 -0
  157. package/dist/src/view/client/assets/arc-BnwR8Bu7.js +1 -0
  158. package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-u97vDna9.js +36 -0
  159. package/dist/src/view/client/assets/blockDiagram-I7D4REHJ-DETl8e_H.js +129 -0
  160. package/dist/src/view/client/assets/c4Diagram-7LVT6UL2-DJRAT1Ve.js +38 -0
  161. package/dist/src/view/client/assets/channel-BwQbnWCN.js +1 -0
  162. package/dist/src/view/client/assets/chunk-2Q5K7J3B-C0TmOC1Q.js +1 -0
  163. package/dist/src/view/client/assets/chunk-5VM5RSS4-dfBOrYE6.js +15 -0
  164. package/dist/src/view/client/assets/chunk-F27PBJKO-DAnks99K.js +1 -0
  165. package/dist/src/view/client/assets/chunk-IMKFNOWR-CPjB07if.js +231 -0
  166. package/dist/src/view/client/assets/chunk-JWPE2WC7-VvU_gZI6.js +1 -0
  167. package/dist/src/view/client/assets/chunk-POPQ4Y6H-DDUbVF55.js +1 -0
  168. package/dist/src/view/client/assets/chunk-SVP7TREG-BHROkr6D.js +88 -0
  169. package/dist/src/view/client/assets/chunk-TICWLB2K-95PzGIOg.js +206 -0
  170. package/dist/src/view/client/assets/chunk-XXDRQBXY-MdmuLhIs.js +1 -0
  171. package/dist/src/view/client/assets/classDiagram-ZZMXUADV-HvQKkIS-.js +1 -0
  172. package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-HvQKkIS-.js +1 -0
  173. package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-CauUf_rN.js +1 -0
  174. package/dist/src/view/client/assets/cynefin-OW5HDTMX-CeA8IKWo.js +166 -0
  175. package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-L1rtcT8l.js +62 -0
  176. package/dist/src/view/client/assets/cytoscape.esm-DzSz-X2X.js +321 -0
  177. package/dist/src/view/client/assets/dagre-GXQ25YYZ-DLQwTa3H.js +4 -0
  178. package/dist/src/view/client/assets/defaultLocale-DX6XiGOO.js +1 -0
  179. package/dist/src/view/client/assets/diagram-S7CK7UJ4-kbKdIyXG.js +30 -0
  180. package/dist/src/view/client/assets/diagram-UQ7AKVKN-kmzp92RC.js +41 -0
  181. package/dist/src/view/client/assets/diagram-VSXAHHWV-EXJTU8qq.js +3 -0
  182. package/dist/src/view/client/assets/diagram-VX7I27RA-DAiIyMCG.js +24 -0
  183. package/dist/src/view/client/assets/diagram-Z3DM3KII-G9wz50ZV.js +24 -0
  184. package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-D2tVRCNA.js +1 -0
  185. package/dist/src/view/client/assets/erDiagram-RLTQ6QDP-C925J5A3.js +99 -0
  186. package/dist/src/view/client/assets/flowDiagram-HODETNUW-DzoCa-Nq.js +168 -0
  187. package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-D2ebbg4p.js +292 -0
  188. package/dist/src/view/client/assets/gitGraphDiagram-WWUBYQGX-B4QratYy.js +106 -0
  189. package/dist/src/view/client/assets/index-BnENo9bV.js +1 -0
  190. package/dist/src/view/client/assets/index-D0ajr_Ja.css +1 -0
  191. package/dist/src/view/client/assets/index-KZcnpXJH.js +321 -0
  192. package/dist/src/view/client/assets/infoDiagram-27XIBGKW-DL4mtHkt.js +2 -0
  193. package/dist/src/view/client/assets/init-Gi6I4Gst.js +1 -0
  194. package/dist/src/view/client/assets/ishikawaDiagram-5VMMS53U-CW4mto2d.js +70 -0
  195. package/dist/src/view/client/assets/journeyDiagram-3NMN7TZE-8R4eacFW.js +139 -0
  196. package/dist/src/view/client/assets/kanban-definition-UXKFOSKX-Cy9ioXEJ.js +89 -0
  197. package/dist/src/view/client/assets/katex-HP8lGamR.js +257 -0
  198. package/dist/src/view/client/assets/layout-C0MFuhth.js +1 -0
  199. package/dist/src/view/client/assets/linear-Dv3N4dQR.js +1 -0
  200. package/dist/src/view/client/assets/logo-small-RR_KBOAT.svg +149 -0
  201. package/dist/src/view/client/assets/maplibre-gl-DnqZgfUS.js +800 -0
  202. package/dist/src/view/client/assets/mermaid.core-bds9A7e2.js +310 -0
  203. package/dist/src/view/client/assets/mindmap-definition-YA3MSWOX-DjmGW355.js +96 -0
  204. package/dist/src/view/client/assets/ordinal-Cboi1Yqb.js +1 -0
  205. package/dist/src/view/client/assets/pegDiagram-XKGWAZYB-BWsZp0ab.js +1 -0
  206. package/dist/src/view/client/assets/pieDiagram-E7YTZNPT-CCH2gGR-.js +39 -0
  207. package/dist/src/view/client/assets/quadrantDiagram-AXDQQJYC-CjocPJFz.js +7 -0
  208. package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-BjLeb2KF.js +1 -0
  209. package/dist/src/view/client/assets/requirementDiagram-BXWQKSXE-Ct6AoXRr.js +84 -0
  210. package/dist/src/view/client/assets/sankeyDiagram-P5KCCOFB-CIpgGN_w.js +40 -0
  211. package/dist/src/view/client/assets/sequenceDiagram-WJ2MYXX4-DrmLFj6w.js +162 -0
  212. package/dist/src/view/client/assets/sizeCapture-INFHLROL-BdFMe2Lr.js +1 -0
  213. package/dist/src/view/client/assets/stateDiagram-D77RDMKH-Zd9F1goW.js +1 -0
  214. package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-ByIKCftl.js +1 -0
  215. package/dist/src/view/client/assets/swimlanes-42K2YHIH-a7VFwi00.js +1 -0
  216. package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-CzxaUHB-.js +8 -0
  217. package/dist/src/view/client/assets/three.module-PDSP0dbZ.js +4108 -0
  218. package/dist/src/view/client/assets/timeline-definition-24CTP7MA-BExPu6X5.js +120 -0
  219. package/dist/src/view/client/assets/vennDiagram-4TSXK5OY-PC1MU3si.js +34 -0
  220. package/dist/src/view/client/assets/wardleyDiagram-VM6X3IG4-DNWqU3fa.js +78 -0
  221. package/dist/src/view/client/assets/xychartDiagram-S5SC5T6Z-E1uA28_D.js +7 -0
  222. package/dist/src/view/client/index.html +2 -2
  223. package/dist/src/view/client-shell.d.ts +2 -0
  224. package/dist/src/view/client-shell.js +5 -0
  225. package/dist/src/view/diagnostics.d.ts +3 -2
  226. package/dist/src/view/diagnostics.js +49 -58
  227. package/dist/src/view/document-edit.d.ts +8 -0
  228. package/dist/src/view/document-edit.js +25 -0
  229. package/dist/src/view/document-route.d.ts +12 -0
  230. package/dist/src/view/document-route.js +93 -0
  231. package/dist/src/view/document-tree.d.ts +18 -0
  232. package/dist/src/view/document-tree.js +139 -0
  233. package/dist/src/view/external-document-tree.d.ts +3 -0
  234. package/dist/src/view/external-document-tree.js +674 -0
  235. package/dist/src/view/git-diff.js +187 -0
  236. package/dist/src/view/git.js +1 -1
  237. package/dist/src/view/graph.d.ts +3 -2
  238. package/dist/src/view/graph.js +94 -7
  239. package/dist/src/view/highlight.d.ts +10 -2
  240. package/dist/src/view/highlight.js +4540 -1360
  241. package/dist/src/view/markdown.d.ts +3 -2
  242. package/dist/src/view/markdown.js +214 -53
  243. package/dist/src/view/protocol.d.ts +59 -6
  244. package/dist/src/view/references.d.ts +10 -25
  245. package/dist/src/view/references.js +117 -107
  246. package/dist/src/view/repository.d.ts +13 -3
  247. package/dist/src/view/repository.js +234 -15
  248. package/dist/src/view/search.d.ts +1 -1
  249. package/dist/src/view/search.js +3 -7
  250. package/dist/src/view/server.d.ts +11 -2
  251. package/dist/src/view/server.js +212 -70
  252. package/dist/src/view/source-target.js +5 -8
  253. package/dist/src/view/static-build.d.ts +1 -0
  254. package/dist/src/view/static-build.js +245 -42
  255. package/dist/src/view/static-protocol.d.ts +19 -2
  256. package/dist/src/view/store.d.ts +16 -3
  257. package/dist/src/view/store.js +228 -36
  258. package/dist/src/view/table-of-contents.d.ts +2 -2
  259. package/dist/src/view/table-of-contents.js +2 -22
  260. package/dist/src/walk.d.ts +5 -3
  261. package/dist/src/walk.js +102 -8
  262. package/package.json +46 -8
  263. package/plugins/lat-md/README.md +61 -0
  264. package/plugins/lat-md/hooks.json +38 -0
  265. package/plugins/lat-md/plugin.json +9 -0
  266. package/plugins/lat-md/rules/AGENTS.md +118 -0
  267. package/plugins/lat-md/skills/lat-md/SKILL.md +178 -0
  268. package/plugins/lat-md/skills/lat-md-conventions/SKILL.md +182 -0
  269. package/templates/AGENTS.md +15 -3
  270. package/templates/cursor-rules.md +12 -3
  271. package/templates/fork/conventions.md +30 -4
  272. package/templates/skill/SKILL.md +22 -1
  273. package/dist/src/view/client/assets/index-C3iaZyYT.css +0 -1
  274. package/dist/src/view/client/assets/index-KWi67IMz.js +0 -334
  275. package/dist/src/view/client/assets/logo-CNF8zs47.svg +0 -182
@@ -1,8 +1,7 @@
1
- import { type Section } from '../lattice.js';
2
1
  import type { CmdContext, CmdResult } from '../context.js';
3
2
  /**
4
3
  * Resolve [[refs]] in text and return the expanded output.
5
4
  * Returns null if there are no wiki links, or if resolution fails.
6
5
  */
7
- export declare function expandPrompt(ctx: CmdContext, text: string, preloadedSections?: Section[]): Promise<string | null>;
6
+ export declare function expandPrompt(ctx: CmdContext, text: string): Promise<string | null>;
8
7
  export declare function expandCommand(ctx: CmdContext, text: string): Promise<CmdResult>;
@@ -1,6 +1,7 @@
1
1
  import { join, relative } from 'node:path';
2
- import { loadAllSections, findSections, } from '../lattice.js';
2
+ import { findSections, } from '../lattice-model.js';
3
3
  import { UNTRUSTED_NOTICE, quoteUntrusted, cleanUntrustedId, } from '../untrusted.js';
4
+ import { commandProjectAnalysis, commandProjectSession, } from '../project-analysis.js';
4
5
  const WIKI_LINK_RE = /\[\[([^\]]+)\]\]/g;
5
6
  function formatLocation(section, projectRoot) {
6
7
  const relPath = relative(process.cwd(), join(projectRoot, section.filePath));
@@ -10,17 +11,34 @@ function formatLocation(section, projectRoot) {
10
11
  * Resolve [[refs]] in text and return the expanded output.
11
12
  * Returns null if there are no wiki links, or if resolution fails.
12
13
  */
13
- export async function expandPrompt(ctx, text, preloadedSections) {
14
+ export async function expandPrompt(ctx, text) {
14
15
  const refs = [...text.matchAll(WIKI_LINK_RE)];
15
16
  if (refs.length === 0)
16
17
  return null;
17
- const allSections = preloadedSections ?? (await loadAllSections(ctx.latDir));
18
+ const { allSections } = await commandProjectAnalysis(ctx);
18
19
  const resolved = new Map();
20
+ const externalResolved = new Map();
21
+ const external = await commandProjectSession(ctx).external();
19
22
  const errors = [];
20
23
  for (const match of refs) {
21
24
  const target = match[1];
22
- if (resolved.has(target))
25
+ if (resolved.has(target) || externalResolved.has(target))
23
26
  continue;
27
+ try {
28
+ if (external.parse(target)) {
29
+ externalResolved.set(target, await external.resolve(target));
30
+ continue;
31
+ }
32
+ }
33
+ catch (error) {
34
+ errors.push(error.message);
35
+ continue;
36
+ }
37
+ const unknownExternal = external.unknownTargetMessage(target);
38
+ if (unknownExternal) {
39
+ errors.push(unknownExternal);
40
+ continue;
41
+ }
24
42
  const matches = findSections(allSections, target);
25
43
  if (matches.length >= 1) {
26
44
  resolved.set(target, {
@@ -37,12 +55,21 @@ export async function expandPrompt(ctx, text, preloadedSections) {
37
55
  return null;
38
56
  // Replace [[refs]] inline
39
57
  let output = text.replace(WIKI_LINK_RE, (_match, target) => {
58
+ if (externalResolved.has(target))
59
+ return `[[${target}]]`;
40
60
  const ref = resolved.get(target);
41
61
  return `[[${cleanUntrustedId(ref.best.section.id)}]]`;
42
62
  });
43
63
  // Append context block as nested outliner
44
64
  output += '\n\n<lat-context>\n';
45
65
  output += UNTRUSTED_NOTICE + '\n';
66
+ for (const [target, value] of externalResolved) {
67
+ output += `* \`[[${target}]]\` is referring to:\n`;
68
+ output += ` * ${value.source.repo} @ ${value.source.commit} via ${value.provider}\n`;
69
+ output += ` * ${value.target.repositoryPath}:${value.startLine}-${value.endLine}\n`;
70
+ for (const line of value.content.split('\n'))
71
+ output += ` | ${line}\n`;
72
+ }
46
73
  for (const ref of resolved.values()) {
47
74
  const isExact = ref.best.reason === 'exact match' ||
48
75
  ref.best.reason.startsWith('file stem expanded');
@@ -73,9 +100,29 @@ export async function expandCommand(ctx, text) {
73
100
  return { output: text };
74
101
  }
75
102
  // Resolution failed — find which ref is broken
76
- const allSections = await loadAllSections(ctx.latDir);
103
+ const { allSections } = await commandProjectAnalysis(ctx);
104
+ const external = await commandProjectSession(ctx).external();
77
105
  for (const match of refs) {
78
106
  const target = match[1];
107
+ try {
108
+ if (external.parse(target)) {
109
+ await external.resolve(target);
110
+ continue;
111
+ }
112
+ }
113
+ catch (error) {
114
+ return {
115
+ output: ctx.styler.red(error.message),
116
+ isError: true,
117
+ };
118
+ }
119
+ const unknownExternal = external.unknownTargetMessage(target);
120
+ if (unknownExternal) {
121
+ return {
122
+ output: ctx.styler.red(unknownExternal),
123
+ isError: true,
124
+ };
125
+ }
79
126
  const matches = findSections(allSections, target);
80
127
  if (matches.length === 0) {
81
128
  const s = ctx.styler;
@@ -0,0 +1,11 @@
1
+ import type { CmdContext, CmdResult } from '../context.js';
2
+ export type ExternalAddOptions = {
3
+ commit?: string;
4
+ prefix?: string;
5
+ defaultFileExtension?: string;
6
+ strategy?: string;
7
+ fetchUrl?: string;
8
+ };
9
+ export declare function externalListCommand(ctx: CmdContext, json?: boolean): Promise<CmdResult>;
10
+ export declare function externalShowCommand(ctx: CmdContext, query: string, json?: boolean): Promise<CmdResult>;
11
+ export declare function externalAddCommand(ctx: CmdContext, handleValue: string | undefined, repoValue: string | undefined, options: ExternalAddOptions): Promise<CmdResult>;
@@ -0,0 +1,185 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ import { addCanonicalExternalSource, describeExternalSources, inferExternalFetchUrl, loadExternalSources, normalizeExternalDefaultFileExtension, normalizeExternalRepoUrl, parseExternalTarget, resolveExternalCommit, } from '../external-sources.js';
3
+ import { selectMenu } from './select-menu.js';
4
+ function shellArg(value) {
5
+ return /^[a-zA-Z0-9_./:@=-]+$/.test(value)
6
+ ? value
7
+ : `'${value.replaceAll("'", "'\\''")}'`;
8
+ }
9
+ function formatDescription(value) {
10
+ const lines = [
11
+ `${value.handle}`,
12
+ ` Repository: ${value.repo}`,
13
+ ` Commit: ${value.effectiveCommit}${value.effectiveCommit !== value.canonicalCommit ? ` (canonical: ${value.canonicalCommit})` : ''}`,
14
+ ` Strategy: ${value.effectiveStrategy}${value.effectiveStrategy !== value.strategy ? ` (canonical: ${value.strategy})` : ''}`,
15
+ ` Prefix: ${value.prefix || '(none)'}`,
16
+ ` Default file extension: ${value.defaultFileExtension || '(none)'}`,
17
+ ` Cache: ${value.cache ? `${value.cache.strategy} @ ${value.cache.commit}` : '(empty)'}`,
18
+ ];
19
+ if (value.fetchUrl)
20
+ lines.push(` Fetch URL: ${value.fetchUrl}`);
21
+ if (value.localPath)
22
+ lines.push(` Local path: ${value.localPath}`);
23
+ if (value.localError)
24
+ lines.push(` Local error: ${value.localError}`);
25
+ lines.push(' Suggested checkout:');
26
+ for (const command of value.checkout) {
27
+ lines.push(` ${[command.command, ...command.args].map(shellArg).join(' ')}`);
28
+ }
29
+ return lines.join('\n');
30
+ }
31
+ export async function externalListCommand(ctx, json = false) {
32
+ const snapshot = await loadExternalSources(ctx.latDir, ctx.projectRoot);
33
+ if (!snapshot.validCanonical) {
34
+ return {
35
+ output: snapshot.errors
36
+ .map((error) => `${error.file}: ${error.message}`)
37
+ .join('\n'),
38
+ isError: true,
39
+ };
40
+ }
41
+ const descriptions = describeExternalSources(ctx.latDir, snapshot);
42
+ return {
43
+ output: json
44
+ ? JSON.stringify(descriptions, null, 2)
45
+ : descriptions.length
46
+ ? descriptions.map(formatDescription).join('\n\n')
47
+ : 'No external sources configured',
48
+ };
49
+ }
50
+ export async function externalShowCommand(ctx, query, json = false) {
51
+ const snapshot = await loadExternalSources(ctx.latDir, ctx.projectRoot);
52
+ const colon = query.indexOf(':');
53
+ const handle = colon === -1 ? query : query.slice(0, colon);
54
+ const source = describeExternalSources(ctx.latDir, snapshot).find((item) => item.handle === handle);
55
+ if (!source) {
56
+ return {
57
+ output: `External source "${handle}" is not configured`,
58
+ isError: true,
59
+ };
60
+ }
61
+ let target = null;
62
+ if (colon !== -1) {
63
+ try {
64
+ target = parseExternalTarget(query, snapshot);
65
+ }
66
+ catch (error) {
67
+ return { output: error.message, isError: true };
68
+ }
69
+ }
70
+ const value = target
71
+ ? {
72
+ ...source,
73
+ target: target.identity,
74
+ repositoryPath: target.repositoryPath,
75
+ }
76
+ : source;
77
+ return {
78
+ output: json
79
+ ? JSON.stringify(value, null, 2)
80
+ : formatDescription(source) +
81
+ (target
82
+ ? `\n Target: ${target.identity}\n Repository path: ${target.repositoryPath}`
83
+ : ''),
84
+ };
85
+ }
86
+ async function question(rl, label, current) {
87
+ if (current)
88
+ return current;
89
+ const answer = (await rl.question(`${label}: `)).trim();
90
+ if (!answer)
91
+ throw new Error(`${label} is required`);
92
+ return answer;
93
+ }
94
+ export async function externalAddCommand(ctx, handleValue, repoValue, options) {
95
+ const interactive = !!process.stdin.isTTY;
96
+ if (!interactive &&
97
+ (!handleValue || !repoValue || !options.commit || !options.strategy)) {
98
+ return {
99
+ output: 'Non-interactive use requires handle, repo, --commit, and --strategy',
100
+ isError: true,
101
+ };
102
+ }
103
+ let rl = null;
104
+ const getReadline = () => {
105
+ rl ??= createInterface({ input: process.stdin, output: process.stdout });
106
+ return rl;
107
+ };
108
+ const closeReadline = () => {
109
+ rl?.close();
110
+ rl = null;
111
+ };
112
+ try {
113
+ const handle = await question(getReadline(), 'Handle', handleValue);
114
+ const repo = normalizeExternalRepoUrl(await question(getReadline(), 'Repository', repoValue));
115
+ const ref = await question(getReadline(), 'Commit or ref', options.commit);
116
+ const prefix = options.prefix ??
117
+ (interactive
118
+ ? (await getReadline().question('Prefix (optional): ')).trim()
119
+ : '');
120
+ const defaultFileExtensionValue = options.defaultFileExtension ??
121
+ (interactive
122
+ ? (await getReadline().question('Default file extension (optional): ')).trim()
123
+ : '');
124
+ const defaultFileExtension = defaultFileExtensionValue
125
+ ? normalizeExternalDefaultFileExtension(defaultFileExtensionValue)
126
+ : '';
127
+ let strategy = options.strategy;
128
+ if (!strategy && interactive) {
129
+ const inferred = inferExternalFetchUrl(repo);
130
+ // selectMenu reads raw keypresses directly. Detach readline first so it
131
+ // cannot consume the selection's Enter key or remain paused afterward.
132
+ closeReadline();
133
+ strategy =
134
+ (await selectMenu(inferred
135
+ ? [
136
+ {
137
+ label: 'Fetch individual files (recommended)',
138
+ value: 'fetch',
139
+ },
140
+ { label: 'Use a Lat-managed checkout', value: 'checkout' },
141
+ ]
142
+ : [
143
+ {
144
+ label: 'Use a Lat-managed checkout (recommended)',
145
+ value: 'checkout',
146
+ },
147
+ { label: 'Fetch individual files', value: 'fetch' },
148
+ ], 'How should Lat read this source?')) ?? undefined;
149
+ }
150
+ if (strategy !== 'fetch' && strategy !== 'checkout') {
151
+ return { output: 'strategy must be fetch or checkout', isError: true };
152
+ }
153
+ let fetchUrl = options.fetchUrl;
154
+ if (strategy === 'fetch' &&
155
+ !fetchUrl &&
156
+ !inferExternalFetchUrl(repo) &&
157
+ interactive) {
158
+ fetchUrl = await question(getReadline(), 'Fetch URL template');
159
+ }
160
+ const commit = await resolveExternalCommit(repo, ref);
161
+ if (interactive) {
162
+ const confirmation = (await getReadline().question(`Add external source "${handle}" at commit "${commit}" using "${strategy}"? [Y/n] `))
163
+ .trim()
164
+ .toLowerCase();
165
+ if (confirmation === 'n' || confirmation === 'no')
166
+ return { output: 'Aborted' };
167
+ }
168
+ await addCanonicalExternalSource(ctx.latDir, {
169
+ handle,
170
+ repo,
171
+ commit,
172
+ ...(prefix ? { prefix } : {}),
173
+ ...(defaultFileExtension ? { defaultFileExtension } : {}),
174
+ strategy: strategy,
175
+ ...(fetchUrl ? { fetchUrl } : {}),
176
+ });
177
+ return { output: `Added external source ${handle} at ${commit}` };
178
+ }
179
+ catch (error) {
180
+ return { output: error.message, isError: true };
181
+ }
182
+ finally {
183
+ closeReadline();
184
+ }
185
+ }
@@ -1,3 +1,4 @@
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";
1
2
  export declare function readForkConventions(): string;
2
3
  /**
3
4
  * Replace the fork block in `content`, or append one when absent.
@@ -17,7 +17,7 @@ import { findTemplatesDir } from './templates.js';
17
17
  *
18
18
  * Two shapes, because the destinations behave differently:
19
19
  *
20
- * - Always-loaded instruction files (`CLAUDE.md`, `AGENTS.md`,
20
+ * - Always-loaded instruction files (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`,
21
21
  * `.github/copilot-instructions.md`) already carry upstream's
22
22
  * `%% lat:begin %%` block, so this appends a *second* block under its own
23
23
  * `%% lat-fork:begin %%` markers. Upstream's `appendTemplateSection` slices
@@ -42,6 +42,7 @@ function forkHashKey(relPath) {
42
42
  const BLOCK_TARGETS = [
43
43
  'CLAUDE.md',
44
44
  'AGENTS.md',
45
+ 'GEMINI.md',
45
46
  '.github/copilot-instructions.md',
46
47
  ];
47
48
  /**
@@ -56,7 +57,7 @@ const SKILL_ROOTS = [
56
57
  '.codex/skills',
57
58
  ];
58
59
  const SKILL_NAME = 'lat-md-conventions';
59
- const SKILL_FRONTMATTER = `---
60
+ export const SKILL_FRONTMATTER = `---
60
61
  name: ${SKILL_NAME}
61
62
  description: >-
62
63
  Rules lat check enforces in this project: Diátaxis mode placement for every
@@ -1 +1,27 @@
1
+ /**
2
+ * Measure code vs `lat.md/` churn since HEAD, in lines. Combines tracked
3
+ * changes (`git diff HEAD --numstat`) with untracked files
4
+ * (`git ls-files --others --exclude-standard -z`). Counting untracked files is
5
+ * what makes a freshly scaffolded, never-committed `lat.md/` register as
6
+ * updated — otherwise its edits are invisible to `git diff HEAD` and the sync
7
+ * reminder fires on every turn until `lat.md/` is committed (issue #61).
8
+ * Both scans are scoped and made relative to `projectRoot`, so a Lat project
9
+ * nested in a larger worktree neither misses its own `lat.md/` paths nor counts
10
+ * changes from sibling projects.
11
+ * Outside a Git worktree both scans contribute zero churn by design: Git is
12
+ * optional, so the hook still validates the project but skips the sync ratio.
13
+ */
14
+ export declare function analyzeDiff(projectRoot: string): {
15
+ codeLines: number;
16
+ latMdLines: number;
17
+ };
18
+ export type StopStatus = {
19
+ checkFailed: boolean;
20
+ totalErrors: number;
21
+ needsSync: boolean;
22
+ codeLines: number;
23
+ latMdLines: number;
24
+ };
25
+ export declare function getStopStatus(latDir: string): Promise<StopStatus>;
26
+ export declare function formatStopReason({ checkFailed, totalErrors, needsSync, codeLines, latMdLines, }: StopStatus): string | null;
1
27
  export declare function hookCmd(agent: string, event: string): Promise<void>;
@@ -1,17 +1,23 @@
1
1
  import { execSync } from 'node:child_process';
2
- import { dirname, extname } from 'node:path';
3
- import { findLatticeDir, loadAllSections, findSections, } from '../lattice.js';
2
+ import { lstatSync, readFileSync } from 'node:fs';
3
+ import { dirname, extname, join } from 'node:path';
4
+ import { findLatticeDir } from '../project-discovery.js';
5
+ import { findSections } from '../lattice-model.js';
4
6
  import { plainStyler } from '../context.js';
5
7
  import { expandPrompt } from './expand.js';
6
8
  import { runSearch } from './search.js';
9
+ import { DEFAULT_SEARCH_LIMIT } from '../search/search.js';
7
10
  import { getSection, formatSectionOutput } from './section.js';
8
11
  import { checkMd, checkCodeRefs, checkIndex, checkSections } from './check.js';
12
+ import { CheckRunContext } from './check-context.js';
13
+ import { isSourceFileExtension } from '../source-formats.js';
14
+ import { commandProjectAnalysis } from '../project-analysis.js';
9
15
  import { checkMode } from './check-mode.js';
10
- import { SOURCE_EXTENSIONS } from '../source-parser.js';
11
16
  import { federateTags, taggedDocsForFiles } from '../knowledge/index.js';
12
17
  import { loadSessionMarkers, saveSessionMarkers, } from '../knowledge/session.js';
13
18
  import { computeCommentReminder, } from './comment-reminder.js';
14
19
  import { handlePreToolUse } from './comment-guard.js';
20
+ import { handleAntigravityHook } from '../fork/antigravity-hook.js';
15
21
  function outputPromptSubmit(context) {
16
22
  process.stdout.write(JSON.stringify({
17
23
  hookSpecificOutput: {
@@ -64,15 +70,15 @@ function makeHookCtx(latDir) {
64
70
  mode: 'cli',
65
71
  };
66
72
  }
67
- async function searchAndExpand(ctx, userPrompt, preloadedSections) {
73
+ async function searchAndExpand(ctx, userPrompt) {
68
74
  let result;
69
75
  try {
70
76
  // Read-only: search an existing index but never build/update it here. A fresh
71
77
  // repo's first prompt must not trigger a full local embed pass — that's what
72
78
  // `lat search` / `lat reindex` are for. Returns no matches until then.
73
- result = await runSearch(ctx.latDir, userPrompt, 5, undefined, {
79
+ result = await runSearch(ctx.latDir, userPrompt, DEFAULT_SEARCH_LIMIT, undefined, {
74
80
  buildIndex: false,
75
- preloadedSections,
81
+ project: await commandProjectAnalysis(ctx),
76
82
  });
77
83
  }
78
84
  catch {
@@ -113,13 +119,14 @@ async function handleUserPromptSubmit() {
113
119
  const latDir = findLatticeDir();
114
120
  if (latDir && userPrompt) {
115
121
  const ctx = makeHookCtx(latDir);
116
- // Loaded once and threaded through expansion, search, and federation
117
- // below — each of those otherwise walks and parses the whole tree itself.
118
- const allSections = await loadAllSections(latDir);
122
+ // Parsed once for expansion, search, and federation below. The analysis is
123
+ // memoised on `ctx`, so passing that one object everywhere is what keeps
124
+ // the three of them from walking and parsing the tree three times.
125
+ const { allSections } = await commandProjectAnalysis(ctx);
119
126
  // If the user prompt contains [[refs]], resolve them inline
120
127
  if (hasWikiLinks(userPrompt)) {
121
128
  try {
122
- const expanded = await expandPrompt(ctx, userPrompt, allSections);
129
+ const expanded = await expandPrompt(ctx, userPrompt);
123
130
  if (expanded) {
124
131
  parts.push('', 'Expanded user prompt with resolved [[refs]]:', expanded);
125
132
  }
@@ -134,7 +141,7 @@ async function handleUserPromptSubmit() {
134
141
  // Search for relevant sections and include their full content
135
142
  let searchFilePaths = [];
136
143
  try {
137
- const searchContext = await searchAndExpand(ctx, userPrompt, allSections);
144
+ const searchContext = await searchAndExpand(ctx, userPrompt);
138
145
  searchFilePaths = searchContext.filePaths;
139
146
  if (searchContext.text) {
140
147
  parts.push('', searchContext.text);
@@ -252,55 +259,116 @@ const DIFF_THRESHOLD = 5;
252
259
  const LATMD_RATIO = 0.05;
253
260
  /** If lat.md/ changes exceed this many lines, skip the ratio check entirely. */
254
261
  const LATMD_UPPER_THRESHOLD = 50;
255
- /** Run `git diff --numstat` and return { codeLines, latMdLines }. */
256
- function analyzeDiff(projectRoot) {
257
- let output;
262
+ function diffFileKind(file) {
263
+ if (file.startsWith('lat.md/'))
264
+ return 'latMd';
265
+ if (isSourceFileExtension(extname(file)))
266
+ return 'code';
267
+ return null;
268
+ }
269
+ /** Count a regular text file's lines as additions, matching Git numstat. */
270
+ function countUntrackedFileLines(projectRoot, file) {
258
271
  try {
259
- output = execSync('git diff HEAD --numstat', {
260
- cwd: projectRoot,
261
- encoding: 'utf-8',
262
- timeout: 2000,
263
- });
272
+ const path = join(projectRoot, file);
273
+ if (!lstatSync(path).isFile())
274
+ return 0;
275
+ const text = readFileSync(path, 'utf-8');
276
+ if (text.length === 0)
277
+ return 0;
278
+ return text.split('\n').length - (text.endsWith('\n') ? 1 : 0);
264
279
  }
265
280
  catch {
266
- return { codeLines: 0, latMdLines: 0 };
281
+ return 0;
267
282
  }
283
+ }
284
+ /**
285
+ * Measure code vs `lat.md/` churn since HEAD, in lines. Combines tracked
286
+ * changes (`git diff HEAD --numstat`) with untracked files
287
+ * (`git ls-files --others --exclude-standard -z`). Counting untracked files is
288
+ * what makes a freshly scaffolded, never-committed `lat.md/` register as
289
+ * updated — otherwise its edits are invisible to `git diff HEAD` and the sync
290
+ * reminder fires on every turn until `lat.md/` is committed (issue #61).
291
+ * Both scans are scoped and made relative to `projectRoot`, so a Lat project
292
+ * nested in a larger worktree neither misses its own `lat.md/` paths nor counts
293
+ * changes from sibling projects.
294
+ * Outside a Git worktree both scans contribute zero churn by design: Git is
295
+ * optional, so the hook still validates the project but skips the sync ratio.
296
+ */
297
+ export function analyzeDiff(projectRoot) {
268
298
  let codeLines = 0;
269
299
  let latMdLines = 0;
270
- // Each line: "added\tremoved\tfile" (e.g. "42\t11\tsrc/cli/hook.ts")
271
- for (const line of output.split('\n')) {
272
- const parts = line.split('\t');
273
- if (parts.length < 3)
274
- continue;
275
- const added = parseInt(parts[0], 10) || 0;
276
- const removed = parseInt(parts[1], 10) || 0;
277
- const file = parts[2];
278
- const changed = added + removed;
279
- if (file.startsWith('lat.md/')) {
300
+ const tally = (kind, changed) => {
301
+ if (kind === 'latMd') {
280
302
  latMdLines += changed;
281
303
  }
282
- else if (SOURCE_EXTENSIONS.has(extname(file))) {
304
+ else {
283
305
  codeLines += changed;
284
306
  }
307
+ };
308
+ // Tracked changes vs HEAD. Throws when there is no HEAD yet (a repo with no
309
+ // commits) or no repo at all; the untracked scan below still runs.
310
+ try {
311
+ const output = execSync('git diff HEAD --numstat --relative -- .', {
312
+ cwd: projectRoot,
313
+ encoding: 'utf-8',
314
+ stdio: ['ignore', 'pipe', 'ignore'],
315
+ });
316
+ // Each line: "added\tremoved\tfile" (e.g. "42\t11\tsrc/cli/hook.ts")
317
+ for (const line of output.split('\n')) {
318
+ const parts = line.split('\t');
319
+ if (parts.length < 3)
320
+ continue;
321
+ const added = parseInt(parts[0], 10) || 0;
322
+ const removed = parseInt(parts[1], 10) || 0;
323
+ const kind = diffFileKind(parts[2]);
324
+ if (kind)
325
+ tally(kind, added + removed);
326
+ }
327
+ }
328
+ catch {
329
+ // Not a git repo, or no HEAD — fall through to the untracked scan.
330
+ }
331
+ // NUL-delimited output preserves spaces, non-ASCII names, and newlines.
332
+ // Classify paths before reading so unrelated untracked files are never read.
333
+ try {
334
+ const output = execSync('git ls-files --others --exclude-standard -z -- .', {
335
+ cwd: projectRoot,
336
+ encoding: 'utf-8',
337
+ stdio: ['ignore', 'pipe', 'ignore'],
338
+ });
339
+ for (const file of output.split('\0')) {
340
+ if (!file)
341
+ continue;
342
+ const kind = diffFileKind(file);
343
+ if (!kind)
344
+ continue;
345
+ tally(kind, countUntrackedFileLines(projectRoot, file));
346
+ }
347
+ }
348
+ catch {
349
+ // Not a Git repo — diff-based sync analysis is intentionally disabled.
285
350
  }
286
351
  return { codeLines, latMdLines };
287
352
  }
288
- async function getStopStatus(latDir) {
289
- const md = await checkMd(latDir);
290
- const code = await checkCodeRefs(latDir);
291
- const indexErrors = await checkIndex(latDir);
292
- const sectionErrors = await checkSections(latDir);
353
+ export async function getStopStatus(latDir) {
354
+ const projectRoot = dirname(latDir);
355
+ const run = new CheckRunContext(latDir, projectRoot);
293
356
  // Must stay in step with `checkAllCommand` in check.ts. A check counted
294
357
  // there but not here lets the Stop hook call a failing tree clean, which
295
358
  // silently disables the very enforcement the check was added for.
296
- const modeErrors = await checkMode(latDir);
359
+ const [md, code, indexErrors, sectionErrors, modeErrors] = await Promise.all([
360
+ checkMd(latDir, projectRoot, run),
361
+ checkCodeRefs(latDir, projectRoot, run),
362
+ checkIndex(latDir, run),
363
+ checkSections(latDir, projectRoot, run),
364
+ checkMode(latDir, projectRoot),
365
+ ]);
297
366
  const totalErrors = md.errors.length +
298
367
  code.errors.length +
299
368
  indexErrors.length +
300
369
  sectionErrors.length +
301
370
  modeErrors.length;
302
371
  const checkFailed = totalErrors > 0;
303
- const projectRoot = dirname(latDir);
304
372
  const { codeLines, latMdLines } = analyzeDiff(projectRoot);
305
373
  let needsSync = false;
306
374
  if (codeLines >= DIFF_THRESHOLD && latMdLines < LATMD_UPPER_THRESHOLD) {
@@ -315,7 +383,7 @@ async function getStopStatus(latDir) {
315
383
  latMdLines,
316
384
  };
317
385
  }
318
- function formatStopReason({ checkFailed, totalErrors, needsSync, codeLines, latMdLines, }) {
386
+ export function formatStopReason({ checkFailed, totalErrors, needsSync, codeLines, latMdLines, }) {
319
387
  if (!checkFailed && !needsSync)
320
388
  return null;
321
389
  const parts = [];
@@ -425,8 +493,12 @@ export async function hookCmd(agent, event) {
425
493
  console.error(`Unknown hook event for cursor: ${event}. Supported: stop, postToolUse`);
426
494
  process.exit(1);
427
495
  }
496
+ case 'antigravity':
497
+ case 'gemini':
498
+ await handleAntigravityHook(event);
499
+ return;
428
500
  default:
429
- console.error(`Unknown agent: ${agent}. Supported: claude, codex, cursor`);
501
+ console.error(`Unknown agent: ${agent}. Supported: claude, codex, cursor, antigravity, gemini`);
430
502
  process.exit(1);
431
503
  }
432
504
  }