comprehende 0.5.5 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/README.md +1 -1
  2. package/dist/api/agent-md.js +9 -9
  3. package/dist/api/live.js +16 -2
  4. package/dist/api/shiki-langs.js +138 -0
  5. package/dist/api/snapshot.js +2 -0
  6. package/dist/cli/args.js +3 -2
  7. package/dist/cli/commands.js +5 -2
  8. package/dist/cli/main.js +5 -6
  9. package/dist/review/pins.js +30 -0
  10. package/dist/schema/parse.js +203 -19
  11. package/dist/schema/source.js +89 -0
  12. package/dist/schema/types.js +8 -0
  13. package/dist/ui/assets/index-C1EoRByu.css +1 -0
  14. package/dist/ui/assets/index-DczIQS6v.js +1713 -0
  15. package/dist/ui/assets/shiki-lang-angular-expression-18AzvmH7.js +1 -0
  16. package/dist/ui/assets/shiki-lang-angular-html-QylgGsSV.js +1 -0
  17. package/dist/ui/assets/shiki-lang-angular-inline-style-CLIjy_D1.js +1 -0
  18. package/dist/ui/assets/shiki-lang-angular-inline-template-DivDh-Md.js +1 -0
  19. package/dist/ui/assets/shiki-lang-angular-let-declaration-D-jsw5PQ.js +1 -0
  20. package/dist/ui/assets/shiki-lang-angular-template-D8JUSKlm.js +1 -0
  21. package/dist/ui/assets/shiki-lang-angular-template-blocks-CGYc0CHO.js +1 -0
  22. package/dist/ui/assets/shiki-lang-angular-ts-DYfXTSRq.js +1 -0
  23. package/dist/ui/assets/shiki-lang-apl-D33AMsfu.js +1 -0
  24. package/dist/ui/assets/shiki-lang-astro-C5iwtRWy.js +1 -0
  25. package/dist/ui/assets/shiki-lang-blade-BjANHr7J.js +1 -0
  26. package/dist/ui/assets/shiki-lang-bsl-yjnClznE.js +1 -0
  27. package/dist/ui/assets/shiki-lang-cairo-D0VTnW3Z.js +1 -0
  28. package/dist/ui/assets/shiki-lang-chapel-DEeFbMvd.js +1 -0
  29. package/dist/ui/assets/shiki-lang-cobol-CEHSLdUX.js +1 -0
  30. package/dist/ui/assets/shiki-lang-coffee-C2SFSLa5.js +1 -0
  31. package/dist/ui/assets/shiki-lang-cpp-Czz1CvUk.js +1 -0
  32. package/dist/ui/assets/shiki-lang-cpp-macro-BytbzYqp.js +1 -0
  33. package/dist/ui/assets/shiki-lang-crystal-CBpenpwC.js +1 -0
  34. package/dist/ui/assets/shiki-lang-edge-B6n-4du_.js +1 -0
  35. package/dist/ui/assets/shiki-lang-elixir-B_21P7TP.js +1 -0
  36. package/dist/ui/assets/shiki-lang-elm-BhwBe0Iz.js +1 -0
  37. package/dist/ui/assets/shiki-lang-erb-g6AWwbWh.js +1 -0
  38. package/dist/ui/assets/shiki-lang-erlang-C1-mE0_u.js +1 -0
  39. package/dist/ui/assets/shiki-lang-es-tag-css-BEhnidaP.js +1 -0
  40. package/dist/ui/assets/shiki-lang-es-tag-glsl-BPGpRRbJ.js +1 -0
  41. package/dist/ui/assets/shiki-lang-es-tag-html-BAAjMQoR.js +1 -0
  42. package/dist/ui/assets/shiki-lang-es-tag-sql--3_Ne07y.js +1 -0
  43. package/dist/ui/assets/shiki-lang-es-tag-xml-CbEfPPAn.js +1 -0
  44. package/dist/ui/assets/shiki-lang-fortran-fixed-form-BtBWY50U.js +1 -0
  45. package/dist/ui/assets/shiki-lang-fsharp-6SDwCc8t.js +1 -0
  46. package/dist/ui/assets/shiki-lang-gdresource-CPMZsTRs.js +1 -0
  47. package/dist/ui/assets/shiki-lang-git-commit-DavtHCRV.js +1 -0
  48. package/dist/ui/assets/shiki-lang-git-rebase-Db7FSZFE.js +1 -0
  49. package/dist/ui/assets/shiki-lang-glimmer-js-D_dejnIT.js +1 -0
  50. package/dist/ui/assets/shiki-lang-glimmer-ts-CK9oT1LX.js +1 -0
  51. package/dist/ui/assets/shiki-lang-glsl-DgYrmVye.js +1 -0
  52. package/dist/ui/assets/shiki-lang-graphql-B2fodP1i.js +1 -0
  53. package/dist/ui/assets/shiki-lang-hack-3fAmn-Yd.js +1 -0
  54. package/dist/ui/assets/shiki-lang-haml-B5ggipOx.js +1 -0
  55. package/dist/ui/assets/shiki-lang-handlebars-CwuYkNpJ.js +1 -0
  56. package/dist/ui/assets/shiki-lang-html-D3aYWL0H.js +1 -0
  57. package/dist/ui/assets/shiki-lang-html-derivative-ByDQxhtI.js +1 -0
  58. package/dist/ui/assets/shiki-lang-http-wB1irth9.js +1 -0
  59. package/dist/ui/assets/shiki-lang-hurl-C98mAZkp.js +1 -0
  60. package/dist/ui/assets/shiki-lang-hxml-O4x38oJr.js +1 -0
  61. package/dist/ui/assets/shiki-lang-jinja-COi1gYwR.js +1 -0
  62. package/dist/ui/assets/shiki-lang-jinja-html-DzJAgftF.js +1 -0
  63. package/dist/ui/assets/shiki-lang-jison-CA27ooGo.js +1 -0
  64. package/dist/ui/assets/shiki-lang-julia-DWcJqbPK.js +1 -0
  65. package/dist/ui/assets/shiki-lang-just-DgUC6AYi.js +1 -0
  66. package/dist/ui/assets/shiki-lang-latex-CmUXxkfy.js +1 -0
  67. package/dist/ui/assets/shiki-lang-liquid-BIWOSLXI.js +1 -0
  68. package/dist/ui/assets/shiki-lang-lua-xHKCfFex.js +1 -0
  69. package/dist/ui/assets/shiki-lang-markdown-nix-BdjWkH0i.js +1 -0
  70. package/dist/ui/assets/shiki-lang-markdown-vue-B2VTuFQ6.js +1 -0
  71. package/dist/ui/assets/shiki-lang-marko-CCwpwraH.js +1 -0
  72. package/dist/ui/assets/shiki-lang-mdc-RXLzxXpy.js +1 -0
  73. package/dist/ui/assets/shiki-lang-nextflow-D1Hwl2jV.js +1 -0
  74. package/dist/ui/assets/shiki-lang-nginx-CdnGOWaL.js +1 -0
  75. package/dist/ui/assets/shiki-lang-nim-OOUtdPk6.js +1 -0
  76. package/dist/ui/assets/shiki-lang-nix-CB_6xJbh.js +1 -0
  77. package/dist/ui/assets/shiki-lang-org-B2im9gxM.js +1 -0
  78. package/dist/ui/assets/shiki-lang-perl-BOHVV1HZ.js +1 -0
  79. package/dist/ui/assets/shiki-lang-php-DjAfRze_.js +1 -0
  80. package/dist/ui/assets/shiki-lang-pug-a5xe6YCG.js +1 -0
  81. package/dist/ui/assets/shiki-lang-qml-4gxL1odC.js +1 -0
  82. package/dist/ui/assets/shiki-lang-razor-BBzT8ybq.js +1 -0
  83. package/dist/ui/assets/shiki-lang-rst-Bp6ncCmx.js +1 -0
  84. package/dist/ui/assets/shiki-lang-ruby-BZIZ_1-x.js +1 -0
  85. package/dist/ui/assets/shiki-lang-sas-yO3CuBDk.js +1 -0
  86. package/dist/ui/assets/shiki-lang-scss-m-4-QPZd.js +1 -0
  87. package/dist/ui/assets/shiki-lang-shaderlab-BoRclLx6.js +1 -0
  88. package/dist/ui/assets/shiki-lang-shellsession-BhTOBsOq.js +1 -0
  89. package/dist/ui/assets/shiki-lang-soy-D1j1eck3.js +1 -0
  90. package/dist/ui/assets/shiki-lang-sparql-D_PtPRod.js +1 -0
  91. package/dist/ui/assets/shiki-lang-stata-CqWsybMy.js +1 -0
  92. package/dist/ui/assets/shiki-lang-surrealql-B6Ue5sS6.js +1 -0
  93. package/dist/ui/assets/shiki-lang-svelte-rGUXZTmH.js +1 -0
  94. package/dist/ui/assets/shiki-lang-templ-B4iNmN4w.js +1 -0
  95. package/dist/ui/assets/shiki-lang-tex-DWfsXeKl.js +1 -0
  96. package/dist/ui/assets/shiki-lang-ts-tags-CLbH6hBz.js +1 -0
  97. package/dist/ui/assets/shiki-lang-twig-WIwJS9fZ.js +1 -0
  98. package/dist/ui/assets/shiki-lang-typst-CZjGHRRw.js +1 -0
  99. package/dist/ui/assets/shiki-lang-vue-VBLKUFTw.js +1 -0
  100. package/dist/ui/assets/shiki-lang-vue-directives-DB6IPGVS.js +1 -0
  101. package/dist/ui/assets/shiki-lang-vue-html-BZSTfcAd.js +1 -0
  102. package/dist/ui/assets/shiki-lang-vue-interpolations-B333VrTX.js +1 -0
  103. package/dist/ui/assets/shiki-lang-vue-sfc-style-variable-injection-COC9CTDq.js +1 -0
  104. package/dist/ui/assets/shiki-lang-vue-vine-Dwbf9xGA.js +1 -0
  105. package/dist/ui/assets/shiki-lang-xml-CahHRakx.js +1 -0
  106. package/dist/ui/assets/shiki-lang-xsl-C0UDC1a3.js +1 -0
  107. package/dist/ui/index.html +2 -2
  108. package/dist/ui/shiki-langs.json +1 -0
  109. package/package.json +1 -1
  110. package/skills/comprehende/SKILL.md +34 -14
  111. package/skills/comprehende/references/example.md +9 -5
  112. package/skills/comprehende/references/review.schema.json +88 -26
  113. package/dist/ui/assets/angular-html-DA-rfuFy.js +0 -1
  114. package/dist/ui/assets/angular-ts-BrjP3tb8.js +0 -1
  115. package/dist/ui/assets/apl-CORt7UWP.js +0 -1
  116. package/dist/ui/assets/astro-HNnZUWAn.js +0 -1
  117. package/dist/ui/assets/blade-2xfisSek.js +0 -1
  118. package/dist/ui/assets/bsl-DlhNcFeZ.js +0 -1
  119. package/dist/ui/assets/cairo-KRGpt6FW.js +0 -1
  120. package/dist/ui/assets/chapel-DTp_pixX.js +0 -1
  121. package/dist/ui/assets/cobol-nBiQ_Alo.js +0 -1
  122. package/dist/ui/assets/coffee-Ch7k5sss.js +0 -1
  123. package/dist/ui/assets/cpp-BMRokrvK.js +0 -1
  124. package/dist/ui/assets/crystal-DGywbUpC.js +0 -1
  125. package/dist/ui/assets/edge-FbVlp4U3.js +0 -1
  126. package/dist/ui/assets/elixir-CkH2-t6x.js +0 -1
  127. package/dist/ui/assets/elm-DbKCFpqz.js +0 -1
  128. package/dist/ui/assets/erb-DXfck5VN.js +0 -1
  129. package/dist/ui/assets/erlang-DsQrWhSR.js +0 -1
  130. package/dist/ui/assets/fortran-fixed-form-CkoXwp7k.js +0 -1
  131. package/dist/ui/assets/fsharp-CXgrBDvD.js +0 -1
  132. package/dist/ui/assets/gdresource-TyuKm33G.js +0 -1
  133. package/dist/ui/assets/git-commit-F4YmCXRG.js +0 -1
  134. package/dist/ui/assets/git-rebase-r7XF79zn.js +0 -1
  135. package/dist/ui/assets/glimmer-js-ByusRIyA.js +0 -1
  136. package/dist/ui/assets/glimmer-ts-BfAWNZQY.js +0 -1
  137. package/dist/ui/assets/glsl-DplSGwfg.js +0 -1
  138. package/dist/ui/assets/graphql-ChdNCCLP.js +0 -1
  139. package/dist/ui/assets/hack-BWmVpMyf.js +0 -1
  140. package/dist/ui/assets/haml-D5jkg6IW.js +0 -1
  141. package/dist/ui/assets/handlebars-BpdQsYii.js +0 -1
  142. package/dist/ui/assets/html-derivative-DlHx6ybY.js +0 -1
  143. package/dist/ui/assets/html-pp8916En.js +0 -1
  144. package/dist/ui/assets/http-jrhK8wxY.js +0 -1
  145. package/dist/ui/assets/hurl-irOxFIW8.js +0 -1
  146. package/dist/ui/assets/hxml-2-FPmUDs.js +0 -1
  147. package/dist/ui/assets/index-CYJX5eTF.css +0 -1
  148. package/dist/ui/assets/index-L_tbMz50.js +0 -1713
  149. package/dist/ui/assets/jinja-f2NsQr07.js +0 -1
  150. package/dist/ui/assets/jison-wvAkD_A8.js +0 -1
  151. package/dist/ui/assets/julia-5Bft2YPA.js +0 -1
  152. package/dist/ui/assets/just-Cwhn7H3k.js +0 -1
  153. package/dist/ui/assets/latex-D5pSuvFb.js +0 -1
  154. package/dist/ui/assets/liquid-C0sCDyMI.js +0 -1
  155. package/dist/ui/assets/lua-BaeVxFsk.js +0 -1
  156. package/dist/ui/assets/marko-DjSrsDqO.js +0 -1
  157. package/dist/ui/assets/mdc-D1_yUvq7.js +0 -1
  158. package/dist/ui/assets/nextflow-C-mBbutL.js +0 -1
  159. package/dist/ui/assets/nginx-BpAMiNFr.js +0 -1
  160. package/dist/ui/assets/nim-BIad80T-.js +0 -1
  161. package/dist/ui/assets/nix-CwoSXNpI.js +0 -1
  162. package/dist/ui/assets/org-DM6o9KBp.js +0 -1
  163. package/dist/ui/assets/perl-B9cMNwum.js +0 -1
  164. package/dist/ui/assets/php-Csjmro_R.js +0 -1
  165. package/dist/ui/assets/pug-DKIMFp6K.js +0 -1
  166. package/dist/ui/assets/qml-3beO22l8.js +0 -1
  167. package/dist/ui/assets/razor-BjBPvh-w.js +0 -1
  168. package/dist/ui/assets/rst-bs7f0vWN.js +0 -1
  169. package/dist/ui/assets/ruby-C0TQ7zu5.js +0 -1
  170. package/dist/ui/assets/sas-DEy46yEz.js +0 -1
  171. package/dist/ui/assets/scss-D5BDwBP9.js +0 -1
  172. package/dist/ui/assets/shaderlab-Dg9Lc6iA.js +0 -1
  173. package/dist/ui/assets/shellsession-BADoaaVG.js +0 -1
  174. package/dist/ui/assets/soy-8wufbnw4.js +0 -1
  175. package/dist/ui/assets/sparql-rVzFXLq3.js +0 -1
  176. package/dist/ui/assets/stata-DI20mbqo.js +0 -1
  177. package/dist/ui/assets/surrealql-Cjom0U5J.js +0 -1
  178. package/dist/ui/assets/svelte-Cy7k_4gC.js +0 -1
  179. package/dist/ui/assets/templ-DhtptRzy.js +0 -1
  180. package/dist/ui/assets/tex-D96PA37w.js +0 -1
  181. package/dist/ui/assets/ts-tags-D351s5mN.js +0 -1
  182. package/dist/ui/assets/twig-27uCiNez.js +0 -1
  183. package/dist/ui/assets/typst-BUadGCkm.js +0 -1
  184. package/dist/ui/assets/vue-BqiEGhQt.js +0 -1
  185. package/dist/ui/assets/vue-html-AaS7Mt5G.js +0 -1
  186. package/dist/ui/assets/vue-vine-BoDAl6tE.js +0 -1
  187. package/dist/ui/assets/xml-sdJ4AIDG.js +0 -1
  188. package/dist/ui/assets/xsl-CtQFsRM5.js +0 -1
  189. /package/dist/ui/assets/{abap-BdImnpbu.js → shiki-lang-abap-BdImnpbu.js} +0 -0
  190. /package/dist/ui/assets/{actionscript-3-B3316cI-.js → shiki-lang-actionscript-3-B3316cI-.js} +0 -0
  191. /package/dist/ui/assets/{ada-bCR0ucgS.js → shiki-lang-ada-bCR0ucgS.js} +0 -0
  192. /package/dist/ui/assets/{ahk-CsyLZFj1.js → shiki-lang-ahk-CsyLZFj1.js} +0 -0
  193. /package/dist/ui/assets/{ahk2-8Zs4aa1G.js → shiki-lang-ahk2-8Zs4aa1G.js} +0 -0
  194. /package/dist/ui/assets/{apache-Pmp26Uib.js → shiki-lang-apache-Pmp26Uib.js} +0 -0
  195. /package/dist/ui/assets/{apex-DhZFqWV2.js → shiki-lang-apex-DhZFqWV2.js} +0 -0
  196. /package/dist/ui/assets/{applescript-Co6uUVPk.js → shiki-lang-applescript-Co6uUVPk.js} +0 -0
  197. /package/dist/ui/assets/{ara-BRHolxvo.js → shiki-lang-ara-BRHolxvo.js} +0 -0
  198. /package/dist/ui/assets/{asciidoc-CSVQ5wI8.js → shiki-lang-asciidoc-CSVQ5wI8.js} +0 -0
  199. /package/dist/ui/assets/{asm-D_Q5rh1f.js → shiki-lang-asm-D_Q5rh1f.js} +0 -0
  200. /package/dist/ui/assets/{awk-DMzUqQB5.js → shiki-lang-awk-DMzUqQB5.js} +0 -0
  201. /package/dist/ui/assets/{ballerina-BFfxhgS-.js → shiki-lang-ballerina-BFfxhgS-.js} +0 -0
  202. /package/dist/ui/assets/{bat-CickPsom.js → shiki-lang-bat-CickPsom.js} +0 -0
  203. /package/dist/ui/assets/{beancount-k_qm7-4y.js → shiki-lang-beancount-k_qm7-4y.js} +0 -0
  204. /package/dist/ui/assets/{berry-uYugtg8r.js → shiki-lang-berry-uYugtg8r.js} +0 -0
  205. /package/dist/ui/assets/{bibtex-CHM0blh-.js → shiki-lang-bibtex-CHM0blh-.js} +0 -0
  206. /package/dist/ui/assets/{bicep-Bmn6On1c.js → shiki-lang-bicep-Bmn6On1c.js} +0 -0
  207. /package/dist/ui/assets/{bird2-Bx8U0n9b.js → shiki-lang-bird2-Bx8U0n9b.js} +0 -0
  208. /package/dist/ui/assets/{c-BIGW1oBm.js → shiki-lang-c-BIGW1oBm.js} +0 -0
  209. /package/dist/ui/assets/{c3-Dp5svz6Z.js → shiki-lang-c3-Dp5svz6Z.js} +0 -0
  210. /package/dist/ui/assets/{cadence-Bv_4Rxtq.js → shiki-lang-cadence-Bv_4Rxtq.js} +0 -0
  211. /package/dist/ui/assets/{clarity-Dn5IMItf.js → shiki-lang-clarity-Dn5IMItf.js} +0 -0
  212. /package/dist/ui/assets/{clojure-P80f7IUj.js → shiki-lang-clojure-P80f7IUj.js} +0 -0
  213. /package/dist/ui/assets/{cmake-D1j8_8rp.js → shiki-lang-cmake-D1j8_8rp.js} +0 -0
  214. /package/dist/ui/assets/{codeowners-Bp6g37R7.js → shiki-lang-codeowners-Bp6g37R7.js} +0 -0
  215. /package/dist/ui/assets/{codeql-DsOJ9woJ.js → shiki-lang-codeql-DsOJ9woJ.js} +0 -0
  216. /package/dist/ui/assets/{common-lisp-Cg-RD9OK.js → shiki-lang-common-lisp-Cg-RD9OK.js} +0 -0
  217. /package/dist/ui/assets/{coq-C7JzOVbR.js → shiki-lang-coq-C7JzOVbR.js} +0 -0
  218. /package/dist/ui/assets/{csharp-DSvCPggb.js → shiki-lang-csharp-DSvCPggb.js} +0 -0
  219. /package/dist/ui/assets/{css-CLj8gQPS.js → shiki-lang-css-CLj8gQPS.js} +0 -0
  220. /package/dist/ui/assets/{csv-fuZLfV_i.js → shiki-lang-csv-fuZLfV_i.js} +0 -0
  221. /package/dist/ui/assets/{cue-D82EKSYY.js → shiki-lang-cue-D82EKSYY.js} +0 -0
  222. /package/dist/ui/assets/{cypher-COkxafJQ.js → shiki-lang-cypher-COkxafJQ.js} +0 -0
  223. /package/dist/ui/assets/{d-85-TOEBH.js → shiki-lang-d-85-TOEBH.js} +0 -0
  224. /package/dist/ui/assets/{dart-bE4Kk8sk.js → shiki-lang-dart-bE4Kk8sk.js} +0 -0
  225. /package/dist/ui/assets/{dax-CEL-wOlO.js → shiki-lang-dax-CEL-wOlO.js} +0 -0
  226. /package/dist/ui/assets/{desktop-BmXAJ9_W.js → shiki-lang-desktop-BmXAJ9_W.js} +0 -0
  227. /package/dist/ui/assets/{diff-D97Zzqfu.js → shiki-lang-diff-D97Zzqfu.js} +0 -0
  228. /package/dist/ui/assets/{docker-BcOcwvcX.js → shiki-lang-docker-BcOcwvcX.js} +0 -0
  229. /package/dist/ui/assets/{dotenv-Da5cRb03.js → shiki-lang-dotenv-Da5cRb03.js} +0 -0
  230. /package/dist/ui/assets/{dream-maker-BtqSS_iP.js → shiki-lang-dream-maker-BtqSS_iP.js} +0 -0
  231. /package/dist/ui/assets/{emacs-lisp-C_m_b--Z.js → shiki-lang-emacs-lisp-C_m_b--Z.js} +0 -0
  232. /package/dist/ui/assets/{fennel-BYunw83y.js → shiki-lang-fennel-BYunw83y.js} +0 -0
  233. /package/dist/ui/assets/{fish-BvzEVeQv.js → shiki-lang-fish-BvzEVeQv.js} +0 -0
  234. /package/dist/ui/assets/{fluent-C4IJs8-o.js → shiki-lang-fluent-C4IJs8-o.js} +0 -0
  235. /package/dist/ui/assets/{fortran-free-form-BxgE0vQu.js → shiki-lang-fortran-free-form-BxgE0vQu.js} +0 -0
  236. /package/dist/ui/assets/{gdscript-DqcFQ5yU.js → shiki-lang-gdscript-DqcFQ5yU.js} +0 -0
  237. /package/dist/ui/assets/{gdshader-DkwncUOv.js → shiki-lang-gdshader-DkwncUOv.js} +0 -0
  238. /package/dist/ui/assets/{genie-D0YGMca9.js → shiki-lang-genie-D0YGMca9.js} +0 -0
  239. /package/dist/ui/assets/{gherkin-DyxjwDmM.js → shiki-lang-gherkin-DyxjwDmM.js} +0 -0
  240. /package/dist/ui/assets/{gleam-BspZqrRM.js → shiki-lang-gleam-BspZqrRM.js} +0 -0
  241. /package/dist/ui/assets/{gn-n2N0HUVH.js → shiki-lang-gn-n2N0HUVH.js} +0 -0
  242. /package/dist/ui/assets/{gnuplot-DdkO51Og.js → shiki-lang-gnuplot-DdkO51Og.js} +0 -0
  243. /package/dist/ui/assets/{go-C27-OAKa.js → shiki-lang-go-C27-OAKa.js} +0 -0
  244. /package/dist/ui/assets/{groovy-gcz8RCvz.js → shiki-lang-groovy-gcz8RCvz.js} +0 -0
  245. /package/dist/ui/assets/{haskell-Df6bDoY_.js → shiki-lang-haskell-Df6bDoY_.js} +0 -0
  246. /package/dist/ui/assets/{haxe-CfZj7gIn.js → shiki-lang-haxe-CfZj7gIn.js} +0 -0
  247. /package/dist/ui/assets/{hcl-BWvSN4gD.js → shiki-lang-hcl-BWvSN4gD.js} +0 -0
  248. /package/dist/ui/assets/{hjson-D5-asLiD.js → shiki-lang-hjson-D5-asLiD.js} +0 -0
  249. /package/dist/ui/assets/{hlsl-D3lLCCz7.js → shiki-lang-hlsl-D3lLCCz7.js} +0 -0
  250. /package/dist/ui/assets/{hy-DFXneXwc.js → shiki-lang-hy-DFXneXwc.js} +0 -0
  251. /package/dist/ui/assets/{imba-DGztddWO.js → shiki-lang-imba-DGztddWO.js} +0 -0
  252. /package/dist/ui/assets/{ini-BEwlwnbL.js → shiki-lang-ini-BEwlwnbL.js} +0 -0
  253. /package/dist/ui/assets/{java-CylS5w8V.js → shiki-lang-java-CylS5w8V.js} +0 -0
  254. /package/dist/ui/assets/{javascript-wDzz0qaB.js → shiki-lang-javascript-wDzz0qaB.js} +0 -0
  255. /package/dist/ui/assets/{json-Cp-IABpG.js → shiki-lang-json-Cp-IABpG.js} +0 -0
  256. /package/dist/ui/assets/{json5-C9tS-k6U.js → shiki-lang-json5-C9tS-k6U.js} +0 -0
  257. /package/dist/ui/assets/{jsonc-Des-eS-w.js → shiki-lang-jsonc-Des-eS-w.js} +0 -0
  258. /package/dist/ui/assets/{jsonl-DcaNXYhu.js → shiki-lang-jsonl-DcaNXYhu.js} +0 -0
  259. /package/dist/ui/assets/{jsonnet-DFQXde-d.js → shiki-lang-jsonnet-DFQXde-d.js} +0 -0
  260. /package/dist/ui/assets/{jssm-C2t-YnRu.js → shiki-lang-jssm-C2t-YnRu.js} +0 -0
  261. /package/dist/ui/assets/{jsx-g9-lgVsj.js → shiki-lang-jsx-g9-lgVsj.js} +0 -0
  262. /package/dist/ui/assets/{kdl-DV7GczEv.js → shiki-lang-kdl-DV7GczEv.js} +0 -0
  263. /package/dist/ui/assets/{kotlin-BdnUsdx6.js → shiki-lang-kotlin-BdnUsdx6.js} +0 -0
  264. /package/dist/ui/assets/{kusto-wEQ09or8.js → shiki-lang-kusto-wEQ09or8.js} +0 -0
  265. /package/dist/ui/assets/{lean-BZvkOJ9d.js → shiki-lang-lean-BZvkOJ9d.js} +0 -0
  266. /package/dist/ui/assets/{less-B1dDrJ26.js → shiki-lang-less-B1dDrJ26.js} +0 -0
  267. /package/dist/ui/assets/{llvm-BZoOZj88.js → shiki-lang-llvm-BZoOZj88.js} +0 -0
  268. /package/dist/ui/assets/{log-2UxHyX5q.js → shiki-lang-log-2UxHyX5q.js} +0 -0
  269. /package/dist/ui/assets/{logo-BtOb2qkB.js → shiki-lang-logo-BtOb2qkB.js} +0 -0
  270. /package/dist/ui/assets/{luau-BnpPk5vE.js → shiki-lang-luau-BnpPk5vE.js} +0 -0
  271. /package/dist/ui/assets/{make-CHLpvVh8.js → shiki-lang-make-CHLpvVh8.js} +0 -0
  272. /package/dist/ui/assets/{markdown-Cvjx9yec.js → shiki-lang-markdown-Cvjx9yec.js} +0 -0
  273. /package/dist/ui/assets/{matlab-D7o27uSR.js → shiki-lang-matlab-D7o27uSR.js} +0 -0
  274. /package/dist/ui/assets/{mdx-Cmh6b_Ma.js → shiki-lang-mdx-Cmh6b_Ma.js} +0 -0
  275. /package/dist/ui/assets/{mermaid-CQcHuHx7.js → shiki-lang-mermaid-CQcHuHx7.js} +0 -0
  276. /package/dist/ui/assets/{mipsasm-CKIfxQSi.js → shiki-lang-mipsasm-CKIfxQSi.js} +0 -0
  277. /package/dist/ui/assets/{mojo-DJz3ZmWd.js → shiki-lang-mojo-DJz3ZmWd.js} +0 -0
  278. /package/dist/ui/assets/{moonbit-CHtswR0a.js → shiki-lang-moonbit-CHtswR0a.js} +0 -0
  279. /package/dist/ui/assets/{move-el3G9tDJ.js → shiki-lang-move-el3G9tDJ.js} +0 -0
  280. /package/dist/ui/assets/{narrat-DRg8JJMk.js → shiki-lang-narrat-DRg8JJMk.js} +0 -0
  281. /package/dist/ui/assets/{nextflow-groovy-vE_lwT2v.js → shiki-lang-nextflow-groovy-vE_lwT2v.js} +0 -0
  282. /package/dist/ui/assets/{nsis-BlV79W_Q.js → shiki-lang-nsis-BlV79W_Q.js} +0 -0
  283. /package/dist/ui/assets/{nushell-D3jzshHO.js → shiki-lang-nushell-D3jzshHO.js} +0 -0
  284. /package/dist/ui/assets/{objective-c-DXmwc3jG.js → shiki-lang-objective-c-DXmwc3jG.js} +0 -0
  285. /package/dist/ui/assets/{objective-cpp-CLxacb5B.js → shiki-lang-objective-cpp-CLxacb5B.js} +0 -0
  286. /package/dist/ui/assets/{ocaml-C0hk2d4L.js → shiki-lang-ocaml-C0hk2d4L.js} +0 -0
  287. /package/dist/ui/assets/{odin-BBf5iR-q.js → shiki-lang-odin-BBf5iR-q.js} +0 -0
  288. /package/dist/ui/assets/{openscad-C4EeE6gA.js → shiki-lang-openscad-C4EeE6gA.js} +0 -0
  289. /package/dist/ui/assets/{pascal-D93ZcfNL.js → shiki-lang-pascal-D93ZcfNL.js} +0 -0
  290. /package/dist/ui/assets/{pkl-u5AG7uiY.js → shiki-lang-pkl-u5AG7uiY.js} +0 -0
  291. /package/dist/ui/assets/{plsql-ChMvpjG-.js → shiki-lang-plsql-ChMvpjG-.js} +0 -0
  292. /package/dist/ui/assets/{po-BTJTHyun.js → shiki-lang-po-BTJTHyun.js} +0 -0
  293. /package/dist/ui/assets/{polar-C0HS_06l.js → shiki-lang-polar-C0HS_06l.js} +0 -0
  294. /package/dist/ui/assets/{postcss-CXtECtnM.js → shiki-lang-postcss-CXtECtnM.js} +0 -0
  295. /package/dist/ui/assets/{powerquery-CEu0bR-o.js → shiki-lang-powerquery-CEu0bR-o.js} +0 -0
  296. /package/dist/ui/assets/{powershell-BmBUJMz7.js → shiki-lang-powershell-BmBUJMz7.js} +0 -0
  297. /package/dist/ui/assets/{prisma-Vru482bI.js → shiki-lang-prisma-Vru482bI.js} +0 -0
  298. /package/dist/ui/assets/{prolog-CbFg5uaA.js → shiki-lang-prolog-CbFg5uaA.js} +0 -0
  299. /package/dist/ui/assets/{proto-C7zT0LnQ.js → shiki-lang-proto-C7zT0LnQ.js} +0 -0
  300. /package/dist/ui/assets/{puppet-BMWR74SV.js → shiki-lang-puppet-BMWR74SV.js} +0 -0
  301. /package/dist/ui/assets/{purescript-CklMAg4u.js → shiki-lang-purescript-CklMAg4u.js} +0 -0
  302. /package/dist/ui/assets/{python-B6aJPvgy.js → shiki-lang-python-B6aJPvgy.js} +0 -0
  303. /package/dist/ui/assets/{qmldir-C8lEn-DE.js → shiki-lang-qmldir-C8lEn-DE.js} +0 -0
  304. /package/dist/ui/assets/{qss-IeuSbFQv.js → shiki-lang-qss-IeuSbFQv.js} +0 -0
  305. /package/dist/ui/assets/{r-Cf5RLm7j.js → shiki-lang-r-Cf5RLm7j.js} +0 -0
  306. /package/dist/ui/assets/{racket-BqYA7rlc.js → shiki-lang-racket-BqYA7rlc.js} +0 -0
  307. /package/dist/ui/assets/{raku-DXvB9xmW.js → shiki-lang-raku-DXvB9xmW.js} +0 -0
  308. /package/dist/ui/assets/{rbs-CpoqiR4B.js → shiki-lang-rbs-CpoqiR4B.js} +0 -0
  309. /package/dist/ui/assets/{reg-C-SQnVFl.js → shiki-lang-reg-C-SQnVFl.js} +0 -0
  310. /package/dist/ui/assets/{regexp-CDVJQ6XC.js → shiki-lang-regexp-CDVJQ6XC.js} +0 -0
  311. /package/dist/ui/assets/{rel-C3B-1QV4.js → shiki-lang-rel-C3B-1QV4.js} +0 -0
  312. /package/dist/ui/assets/{riscv-BM1_JUlF.js → shiki-lang-riscv-BM1_JUlF.js} +0 -0
  313. /package/dist/ui/assets/{ron-D8l8udqQ.js → shiki-lang-ron-D8l8udqQ.js} +0 -0
  314. /package/dist/ui/assets/{rosmsg-BJDFO7_C.js → shiki-lang-rosmsg-BJDFO7_C.js} +0 -0
  315. /package/dist/ui/assets/{rust-B1yitclQ.js → shiki-lang-rust-B1yitclQ.js} +0 -0
  316. /package/dist/ui/assets/{sass-Cj5Yp3dK.js → shiki-lang-sass-Cj5Yp3dK.js} +0 -0
  317. /package/dist/ui/assets/{scala-CqE71os6.js → shiki-lang-scala-CqE71os6.js} +0 -0
  318. /package/dist/ui/assets/{scheme-C98Dy4si.js → shiki-lang-scheme-C98Dy4si.js} +0 -0
  319. /package/dist/ui/assets/{sdbl-DVxCFoDh.js → shiki-lang-sdbl-DVxCFoDh.js} +0 -0
  320. /package/dist/ui/assets/{shellscript-Yzrsuije.js → shiki-lang-shellscript-Yzrsuije.js} +0 -0
  321. /package/dist/ui/assets/{smalltalk-BOQMe2GC.js → shiki-lang-smalltalk-BOQMe2GC.js} +0 -0
  322. /package/dist/ui/assets/{smithy-cds9vsN8.js → shiki-lang-smithy-cds9vsN8.js} +0 -0
  323. /package/dist/ui/assets/{solidity-DijEV5ha.js → shiki-lang-solidity-DijEV5ha.js} +0 -0
  324. /package/dist/ui/assets/{splunk-BtCnVYZw.js → shiki-lang-splunk-BtCnVYZw.js} +0 -0
  325. /package/dist/ui/assets/{sql-CRqJ_cUM.js → shiki-lang-sql-CRqJ_cUM.js} +0 -0
  326. /package/dist/ui/assets/{ssh-config-_ykCGR6B.js → shiki-lang-ssh-config-_ykCGR6B.js} +0 -0
  327. /package/dist/ui/assets/{stylus-BEDo0Tqx.js → shiki-lang-stylus-BEDo0Tqx.js} +0 -0
  328. /package/dist/ui/assets/{swift-C2oV4EkX.js → shiki-lang-swift-C2oV4EkX.js} +0 -0
  329. /package/dist/ui/assets/{system-verilog-0hqHdDBg.js → shiki-lang-system-verilog-0hqHdDBg.js} +0 -0
  330. /package/dist/ui/assets/{systemd-4A_iFExJ.js → shiki-lang-systemd-4A_iFExJ.js} +0 -0
  331. /package/dist/ui/assets/{talonscript-CkByrt1z.js → shiki-lang-talonscript-CkByrt1z.js} +0 -0
  332. /package/dist/ui/assets/{tasl-QIJgUcNo.js → shiki-lang-tasl-QIJgUcNo.js} +0 -0
  333. /package/dist/ui/assets/{tcl-dwOrl1Do.js → shiki-lang-tcl-dwOrl1Do.js} +0 -0
  334. /package/dist/ui/assets/{terraform-BETggiCN.js → shiki-lang-terraform-BETggiCN.js} +0 -0
  335. /package/dist/ui/assets/{toml-vGWfd6FD.js → shiki-lang-toml-vGWfd6FD.js} +0 -0
  336. /package/dist/ui/assets/{tsv-B_m7g4N7.js → shiki-lang-tsv-B_m7g4N7.js} +0 -0
  337. /package/dist/ui/assets/{tsx-COt5Ahok.js → shiki-lang-tsx-COt5Ahok.js} +0 -0
  338. /package/dist/ui/assets/{turtle-BsS91CYL.js → shiki-lang-turtle-BsS91CYL.js} +0 -0
  339. /package/dist/ui/assets/{typescript-BPQ3VLAy.js → shiki-lang-typescript-BPQ3VLAy.js} +0 -0
  340. /package/dist/ui/assets/{typespec-CAFt9gP4.js → shiki-lang-typespec-CAFt9gP4.js} +0 -0
  341. /package/dist/ui/assets/{v-BGw2Nkan.js → shiki-lang-v-BGw2Nkan.js} +0 -0
  342. /package/dist/ui/assets/{vala-CsfeWuGM.js → shiki-lang-vala-CsfeWuGM.js} +0 -0
  343. /package/dist/ui/assets/{vb-Cu-pLBUe.js → shiki-lang-vb-Cu-pLBUe.js} +0 -0
  344. /package/dist/ui/assets/{verilog-nZwndyjY.js → shiki-lang-verilog-nZwndyjY.js} +0 -0
  345. /package/dist/ui/assets/{vhdl-CeAyd5Ju.js → shiki-lang-vhdl-CeAyd5Ju.js} +0 -0
  346. /package/dist/ui/assets/{viml-CJc9bBzg.js → shiki-lang-viml-CJc9bBzg.js} +0 -0
  347. /package/dist/ui/assets/{vyper-CDx5xZoG.js → shiki-lang-vyper-CDx5xZoG.js} +0 -0
  348. /package/dist/ui/assets/{wasm-MzD3tlZU.js → shiki-lang-wasm-MzD3tlZU.js} +0 -0
  349. /package/dist/ui/assets/{wenyan-BV7otONQ.js → shiki-lang-wenyan-BV7otONQ.js} +0 -0
  350. /package/dist/ui/assets/{wgsl-Dx-B1_4e.js → shiki-lang-wgsl-Dx-B1_4e.js} +0 -0
  351. /package/dist/ui/assets/{wikitext-BhOHFoWU.js → shiki-lang-wikitext-BhOHFoWU.js} +0 -0
  352. /package/dist/ui/assets/{wit-5i3qLPDT.js → shiki-lang-wit-5i3qLPDT.js} +0 -0
  353. /package/dist/ui/assets/{wolfram-lXgVvXCa.js → shiki-lang-wolfram-lXgVvXCa.js} +0 -0
  354. /package/dist/ui/assets/{yaml-Buea-lGh.js → shiki-lang-yaml-Buea-lGh.js} +0 -0
  355. /package/dist/ui/assets/{zenscript-DVFEvuxE.js → shiki-lang-zenscript-DVFEvuxE.js} +0 -0
  356. /package/dist/ui/assets/{zig-VOosw3JB.js → shiki-lang-zig-VOosw3JB.js} +0 -0
package/README.md CHANGED
@@ -42,7 +42,7 @@ This tool is for preventing this cognitive surrender while trying to maintain mo
42
42
 
43
43
  `comprehende serve` and `comprehende export` share one UI and one git payload. Serve resolves refs to commit SHAs when it starts, then computes those payloads from the objects on each request. Export writes the same JSON (and image bytes) next to the UI so any static file server can host the review.
44
44
 
45
- `pnpm dev` and `pnpm exec` run with this package as cwd, so they only make sense when _this_ repo is the one under review. To review a different project from a checkout, `cd` into it and run `npx comprehende@0.5.5` (or `node /path/to/comprehende/dist/cli/main.js` after `pnpm build`).
45
+ `pnpm dev` and `pnpm exec` run with this package as cwd, so they only make sense when _this_ repo is the one under review. To review a different project from a checkout, `cd` into it and run `npx comprehende@0.6.1` (or `node /path/to/comprehende/dist/cli/main.js` after `pnpm build`).
46
46
 
47
47
  ## Release
48
48
 
@@ -23,7 +23,7 @@ function overviewAgentMd(review) {
23
23
  "Answer questions about this git change.",
24
24
  overviewSteps(review),
25
25
  pinBlock(review, { commits: true }),
26
- ticketsBlock(review),
26
+ sourcesBlock(review),
27
27
  coverageBlock(review),
28
28
  joinBlocks(["The title:", review.document.title]),
29
29
  review.document.why !== undefined ? joinBlocks(["The why:", review.document.why]) : null,
@@ -79,17 +79,17 @@ function pinBlock(review, options) {
79
79
  commits,
80
80
  ]);
81
81
  }
82
- function ticketsBlock(review) {
83
- const tickets = review.document.tickets ?? [];
84
- if (tickets.length === 0) {
82
+ function sourcesBlock(review) {
83
+ const sources = review.document.sources ?? [];
84
+ if (sources.length === 0) {
85
85
  return null;
86
86
  }
87
- const lines = tickets.map((ticket) => {
88
- const title = ticket.title !== undefined ? ` ${ticket.title}` : "";
89
- const url = ticket.url !== undefined ? `\n ${ticket.url}` : "";
90
- return `- ${ticket.id}${title}${url}`;
87
+ const lines = sources.map((source) => {
88
+ const gist = source.gist !== undefined ? ` ${source.gist}` : "";
89
+ const url = source.url !== undefined ? `\n ${source.url}` : "";
90
+ return `- ${source.kind} ${source.label}${gist}${url}`;
91
91
  });
92
- return ["Tickets:", ...lines].join("\n");
92
+ return ["Sources:", ...lines].join("\n");
93
93
  }
94
94
  function coverageBlock(review) {
95
95
  const lines = [];
package/dist/api/live.js CHANGED
@@ -6,8 +6,10 @@ import { GitError } from "../git/exec.js";
6
6
  import { listCommits } from "../git/log.js";
7
7
  import { pinRange, readRepoIdentity } from "../git/repo.js";
8
8
  import { showFile } from "../git/show.js";
9
- import { coverReview } from "../review/coverage.js";
9
+ import { coverReview, coverageErrors } from "../review/coverage.js";
10
+ import { commentPinErrors, staleCommentPins } from "../review/pins.js";
10
11
  import { isLockfilePath } from "../schema/lockfile.js";
12
+ import { sourceCitationErrors } from "../schema/source.js";
11
13
  import { AGENT_MD_MEDIA_TYPE, agentMd } from "./agent-md.js";
12
14
  import { ApiError } from "./error.js";
13
15
  export async function pinReviewSource(cwd, dataPath) {
@@ -18,6 +20,7 @@ export async function openReview(cwd, dataPath, pin) {
18
20
  const document = await loadDocument(dataPath);
19
21
  const range = pin ?? (await pinRange(cwd, document.source.baseRef, document.source.headRef));
20
22
  const { files, coverage } = await coverReview(cwd, document, range);
23
+ const pins = await staleCommentPins(cwd, document, range);
21
24
  const commits = await listCommits(cwd, range.baseSha, range.headSha);
22
25
  const repo = await readRepoIdentity(cwd);
23
26
  return {
@@ -33,12 +36,13 @@ export async function openReview(cwd, dataPath, pin) {
33
36
  },
34
37
  files,
35
38
  coverage,
39
+ staleCommentPins: pins,
36
40
  mergeBaseSha: range.mergeBaseSha,
37
41
  commits,
38
42
  };
39
43
  }
40
44
  export function reviewPayload(ctx) {
41
- const { document, repo, resolved, files, coverage, commits } = ctx;
45
+ const { document, repo, resolved, files, coverage, staleCommentPins, commits } = ctx;
42
46
  const lockfiles = lockfileFiles(files);
43
47
  return {
44
48
  document,
@@ -49,6 +53,7 @@ export function reviewPayload(ctx) {
49
53
  assignedHunks: coverage.assignedHunks,
50
54
  unassignedCount: coverage.unassigned.length,
51
55
  staleCount: coverage.stale.length,
56
+ staleSourceCount: staleCommentPins.length,
52
57
  },
53
58
  groups: coverage.groups
54
59
  .slice()
@@ -61,6 +66,7 @@ export function reviewPayload(ctx) {
61
66
  lookFor: group.group.lookFor ?? [],
62
67
  dependsOn: group.group.dependsOn ?? [],
63
68
  part: group.group.part,
69
+ sources: group.group.sources ?? [],
64
70
  suggestedOrder: group.group.suggestedOrder,
65
71
  hunkCount: group.hunks.length,
66
72
  staleCount: group.stale.length,
@@ -75,6 +81,7 @@ export function reviewPayload(ctx) {
75
81
  files: lockfiles.map((file) => file.path),
76
82
  },
77
83
  stale: coverage.stale,
84
+ staleSources: staleCommentPins,
78
85
  files: files.map((file) => {
79
86
  const entry = {
80
87
  path: file.path,
@@ -92,6 +99,13 @@ export function reviewPayload(ctx) {
92
99
  commits,
93
100
  };
94
101
  }
102
+ export function reviewProblems(ctx) {
103
+ return [
104
+ ...coverageErrors(ctx.coverage),
105
+ ...sourceCitationErrors(ctx.document),
106
+ ...commentPinErrors(ctx.staleCommentPins),
107
+ ];
108
+ }
95
109
  export function hunksPayload(ctx, groupId) {
96
110
  if (groupId === "") {
97
111
  throw new ApiError(400, "missing group");
@@ -0,0 +1,138 @@
1
+ import { readFile, rm } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ export const SHIKI_LANGS_FILE = "shiki-langs.json";
4
+ const LANG_MODULE = /[/\\]@shikijs[/\\]langs[/\\]dist[/\\]([^/\\]+)\.m?js(?:\?|$)/;
5
+ const SKIP_LANGS = new Set(["text", "ansi"]);
6
+ export function shikiLangId(id) {
7
+ return id.match(LANG_MODULE)?.[1];
8
+ }
9
+ export function parseShikiLangsManifest(input) {
10
+ if (!isRecord(input) || input.version !== 1) {
11
+ return undefined;
12
+ }
13
+ const chunks = stringList(input.chunks);
14
+ const files = stringListMap(input.files);
15
+ const lookup = stringMap(input.lookup);
16
+ if (chunks === undefined || files === undefined || lookup === undefined) {
17
+ return undefined;
18
+ }
19
+ return { version: 1, chunks, files, lookup };
20
+ }
21
+ export function highlightLangFromPath(path, lookup) {
22
+ const fileName = path.split(/[/\\]/).pop() ?? path;
23
+ if (lookup[fileName] !== undefined) {
24
+ return lookup[fileName];
25
+ }
26
+ const compound = fileName.match(/\.([^/\\]+\.[^/\\]+)$/)?.[1];
27
+ if (compound !== undefined && lookup[compound] !== undefined) {
28
+ return lookup[compound];
29
+ }
30
+ const simple = fileName.match(/\.([^.]+)$/)?.[1] ?? "";
31
+ return lookup[simple];
32
+ }
33
+ export function highlightLangsForPaths(paths, lookup) {
34
+ const langs = new Set();
35
+ for (const path of paths) {
36
+ const lang = highlightLangFromPath(path, lookup);
37
+ if (lang !== undefined && !SKIP_LANGS.has(lang)) {
38
+ langs.add(lang);
39
+ }
40
+ }
41
+ return [...langs];
42
+ }
43
+ export function highlightPathsFromFiles(files) {
44
+ const paths = [];
45
+ for (const file of files) {
46
+ paths.push(file.path);
47
+ if (file.oldPath !== undefined) {
48
+ paths.push(file.oldPath);
49
+ }
50
+ }
51
+ return paths;
52
+ }
53
+ export function chunksToKeep(manifest, langs) {
54
+ const keep = new Set();
55
+ for (const lang of langs) {
56
+ const files = manifest.files[lang];
57
+ if (files === undefined) {
58
+ return undefined;
59
+ }
60
+ for (const file of files) {
61
+ keep.add(file);
62
+ }
63
+ }
64
+ return [...keep];
65
+ }
66
+ export async function pruneUnusedHighlighterChunks(outDir, paths) {
67
+ const manifest = await loadManifest(join(outDir, SHIKI_LANGS_FILE));
68
+ if (manifest === undefined) {
69
+ return [];
70
+ }
71
+ const langs = highlightLangsForPaths(paths, manifest.lookup);
72
+ const keep = chunksToKeep(manifest, langs);
73
+ if (keep === undefined) {
74
+ return [];
75
+ }
76
+ const keepSet = new Set(keep);
77
+ const removed = [];
78
+ for (const file of manifest.chunks) {
79
+ if (keepSet.has(file)) {
80
+ continue;
81
+ }
82
+ await rm(join(outDir, file), { force: true });
83
+ await rm(join(outDir, `${file}.map`), { force: true });
84
+ removed.push(file);
85
+ }
86
+ return removed;
87
+ }
88
+ async function loadManifest(path) {
89
+ let text;
90
+ try {
91
+ text = await readFile(path, "utf8");
92
+ }
93
+ catch {
94
+ return undefined;
95
+ }
96
+ try {
97
+ return parseShikiLangsManifest(JSON.parse(text));
98
+ }
99
+ catch {
100
+ return undefined;
101
+ }
102
+ }
103
+ function isRecord(value) {
104
+ return typeof value === "object" && value !== null && !Array.isArray(value);
105
+ }
106
+ function stringList(value) {
107
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
108
+ return undefined;
109
+ }
110
+ return value;
111
+ }
112
+ function stringMap(value) {
113
+ if (!isRecord(value)) {
114
+ return undefined;
115
+ }
116
+ const out = {};
117
+ for (const [key, item] of Object.entries(value)) {
118
+ if (typeof item !== "string") {
119
+ return undefined;
120
+ }
121
+ out[key] = item;
122
+ }
123
+ return out;
124
+ }
125
+ function stringListMap(value) {
126
+ if (!isRecord(value)) {
127
+ return undefined;
128
+ }
129
+ const out = {};
130
+ for (const [key, item] of Object.entries(value)) {
131
+ const list = stringList(item);
132
+ if (list === undefined) {
133
+ return undefined;
134
+ }
135
+ out[key] = list;
136
+ }
137
+ return out;
138
+ }
@@ -4,6 +4,7 @@ import { dirname, join, resolve } from "node:path";
4
4
  import { findPackageRoot } from "../package-root.js";
5
5
  import { isUnavailableSnapshot, listResources, openReview, renderResource, snapshotJson, } from "./live.js";
6
6
  import { apiFsRel } from "./paths.js";
7
+ import { highlightPathsFromFiles, pruneUnusedHighlighterChunks } from "./shiki-langs.js";
7
8
  export async function exportStaticSite(opts) {
8
9
  const outDir = resolve(opts.outDir);
9
10
  const uiRoot = opts.uiRoot ?? join(findPackageRoot(), "dist/ui");
@@ -41,5 +42,6 @@ export async function exportStaticSite(opts) {
41
42
  }
42
43
  apiFiles.push(rel);
43
44
  }
45
+ await pruneUnusedHighlighterChunks(outDir, highlightPathsFromFiles(ctx.files));
44
46
  return { outDir, apiFiles };
45
47
  }
package/dist/cli/args.js CHANGED
@@ -8,13 +8,14 @@ Commands:
8
8
  List hunk refs from live git (no patch text)
9
9
 
10
10
  validate --data <review.json>
11
- Check schema, ref resolution, and hunk coverage
11
+ Check schema, ref resolution, hunk coverage, and source citations
12
12
 
13
13
  serve --data <review.json> [--port <n>] [--open]
14
14
  Serve the local UI on 127.0.0.1 (pins commit SHAs at start)
15
15
 
16
16
  export --data <review.json> --out <dir>
17
- Write a static site (same UI + frozen git payloads). No server after that.
17
+ Write a static site (same UI + frozen git payloads). Highlighters only
18
+ for languages in the review. No server after that.
18
19
 
19
20
  Options:
20
21
  --base <ref> Base ref (default: origin/HEAD or main/master)
@@ -3,7 +3,9 @@ import { isAbsolute, resolve } from "node:path";
3
3
  import { readHunkIndex, resolveSource } from "../git/diff.js";
4
4
  import { defaultBaseRef } from "../git/repo.js";
5
5
  import { coverReview, coverageErrors } from "../review/coverage.js";
6
+ import { commentPinErrors, staleCommentPins } from "../review/pins.js";
6
7
  import { parseReviewJson } from "../schema/parse.js";
8
+ import { sourceCitationErrors } from "../schema/source.js";
7
9
  export async function resolveRange(cwd, base, head) {
8
10
  const baseRef = base ?? (await defaultBaseRef(cwd));
9
11
  const headRef = head ?? "HEAD";
@@ -36,9 +38,10 @@ export function resolveOutPath(out, cwd) {
36
38
  }
37
39
  export async function cmdValidate(cwd, dataPath) {
38
40
  const document = await loadDocument(dataPath);
39
- await resolveSource(cwd, document.source.baseRef, document.source.headRef);
41
+ const resolved = await resolveSource(cwd, document.source.baseRef, document.source.headRef);
40
42
  const { coverage } = await coverReview(cwd, document);
41
- const errors = coverageErrors(coverage);
43
+ const pins = await staleCommentPins(cwd, document, resolved);
44
+ const errors = [...coverageErrors(coverage), ...sourceCitationErrors(document), ...commentPinErrors(pins)];
42
45
  if (errors.length > 0) {
43
46
  throw new Error(errors.join("\n\n"));
44
47
  }
package/dist/cli/main.js CHANGED
@@ -6,8 +6,7 @@ import { fileURLToPath } from "node:url";
6
6
  import { parseArgv, USAGE } from "./args.js";
7
7
  import { cmdIndex, cmdValidate, resolveDataPath, resolveOutPath } from "./commands.js";
8
8
  import { exportStaticSite } from "../api/snapshot.js";
9
- import { openReview, pinReviewSource } from "../api/live.js";
10
- import { coverageErrors } from "../review/coverage.js";
9
+ import { openReview, pinReviewSource, reviewProblems } from "../api/live.js";
11
10
  import { assertWorkTree } from "../git/repo.js";
12
11
  import { readPackageVersion } from "../package-root.js";
13
12
  import { startServer } from "../server/http.js";
@@ -44,7 +43,7 @@ export async function run(argv) {
44
43
  const dataPath = resolveDataPath(request.data, request.cwd);
45
44
  const pin = await pinReviewSource(request.cwd, dataPath);
46
45
  const ctx = await openReview(request.cwd, dataPath, pin);
47
- warnCoverage(ctx.coverage, "serve continues; git wins, unassigned/stale are visible");
46
+ warnReview(ctx, "serve continues; git wins, unassigned/stale are visible");
48
47
  const running = await startServer({ cwd: request.cwd, dataPath, port: request.port, pin });
49
48
  console.log(running.url);
50
49
  console.error(`serving ${dataPath} cwd=${request.cwd} localhost only`);
@@ -58,7 +57,7 @@ export async function run(argv) {
58
57
  const dataPath = resolveDataPath(request.data, request.cwd);
59
58
  const outDir = resolveOutPath(request.out, request.cwd);
60
59
  const ctx = await openReview(request.cwd, dataPath);
61
- warnCoverage(ctx.coverage, "export continues; git wins, unassigned/stale are visible");
60
+ warnReview(ctx, "export continues; git wins, unassigned/stale are visible");
62
61
  const result = await exportStaticSite({ cwd: request.cwd, dataPath, outDir, ctx });
63
62
  console.log(result.outDir);
64
63
  console.error(`exported ${dataPath} ${result.apiFiles.length} api files no git in the folder`);
@@ -71,8 +70,8 @@ export async function run(argv) {
71
70
  return 1;
72
71
  }
73
72
  }
74
- function warnCoverage(coverage, note) {
75
- const problems = coverageErrors(coverage);
73
+ function warnReview(ctx, note) {
74
+ const problems = reviewProblems(ctx);
76
75
  if (problems.length > 0) {
77
76
  console.error(`coverage issues (${note}):\n${problems.join("\n\n")}`);
78
77
  }
@@ -0,0 +1,30 @@
1
+ import { fileExistsAt, showFile } from "../git/show.js";
2
+ import { isLinePinned, textLineCount } from "../schema/source.js";
3
+ export async function staleCommentPins(cwd, document, range) {
4
+ const stale = [];
5
+ for (const source of document.sources ?? []) {
6
+ if (!isLinePinned(source)) {
7
+ continue;
8
+ }
9
+ if (!(await commentPinMatches(cwd, source, range))) {
10
+ stale.push({ id: source.id, path: source.path, side: source.side, line: source.line });
11
+ }
12
+ }
13
+ return stale;
14
+ }
15
+ export function commentPinErrors(pins) {
16
+ if (pins.length === 0) {
17
+ return [];
18
+ }
19
+ const lines = pins.map((pin) => ` ${pin.id} ${pin.path} ${pin.side}:${pin.line}`);
20
+ return [`stale: ${pins.length} comment pin(s) do not match live git:\n${lines.join("\n")}`];
21
+ }
22
+ async function commentPinMatches(cwd, source, range) {
23
+ const sha = source.side === "old" ? range.baseSha : range.headSha;
24
+ if (!(await fileExistsAt(cwd, sha, source.path))) {
25
+ return false;
26
+ }
27
+ const content = await showFile(cwd, sha, source.path);
28
+ const count = textLineCount(content);
29
+ return source.line >= 1 && source.line <= count;
30
+ }
@@ -1,8 +1,34 @@
1
- import { isReviewSize, REVIEW_SIZES } from "./types.js";
2
- const DOCUMENT_KEYS = new Set(["version", "source", "size", "title", "summary", "why", "tickets", "groups"]);
1
+ import { isReviewSize, isSourceKind, isSourceSide, REVIEW_SIZES, SOURCE_KINDS, } from "./types.js";
2
+ const DOCUMENT_KEYS = new Set(["version", "source", "size", "title", "summary", "why", "tickets", "sources", "groups"]);
3
3
  const SOURCE_KEYS = new Set(["baseRef", "headRef", "range"]);
4
4
  const TICKET_KEYS = new Set(["id", "url", "title", "part"]);
5
- const GROUP_KEYS = new Set(["id", "title", "why", "summary", "lookFor", "dependsOn", "part", "suggestedOrder", "hunkRefs"]);
5
+ const REVIEW_SOURCE_KEYS = new Set([
6
+ "id",
7
+ "kind",
8
+ "label",
9
+ "url",
10
+ "title",
11
+ "gist",
12
+ "part",
13
+ "author",
14
+ "body",
15
+ "path",
16
+ "side",
17
+ "line",
18
+ ]);
19
+ const COMMENT_ONLY_KEYS = ["author", "body", "path", "side", "line"];
20
+ const GROUP_KEYS = new Set([
21
+ "id",
22
+ "title",
23
+ "why",
24
+ "summary",
25
+ "lookFor",
26
+ "dependsOn",
27
+ "part",
28
+ "sources",
29
+ "suggestedOrder",
30
+ "hunkRefs",
31
+ ]);
6
32
  const HUNK_KEYS = new Set(["path", "oldPath", "oldStart", "oldLines", "newStart", "newLines"]);
7
33
  export function parseReviewDocument(input) {
8
34
  const errors = [];
@@ -17,8 +43,8 @@ export function parseReviewDocument(input) {
17
43
  const size = parseSize(input.size, errors);
18
44
  const title = requiredString(input.title, "title", errors);
19
45
  const summary = requiredString(input.summary, "summary", errors);
20
- const tickets = parseTickets(input.tickets, errors);
21
- const groups = parseGroups(input.groups, errors);
46
+ const sources = parseDocumentSources(input.sources, input.tickets, errors);
47
+ const groups = parseGroups(input.groups, sources, errors);
22
48
  const why = input.why === undefined ? undefined : requiredString(input.why, "why", errors);
23
49
  if (errors.length > 0 || source === undefined || size === undefined || title === undefined || summary === undefined) {
24
50
  return { ok: false, errors };
@@ -34,8 +60,8 @@ export function parseReviewDocument(input) {
34
60
  if (why !== undefined) {
35
61
  document.why = why;
36
62
  }
37
- if (tickets !== undefined) {
38
- document.tickets = tickets;
63
+ if (sources !== undefined) {
64
+ document.sources = sources;
39
65
  }
40
66
  return { ok: true, document };
41
67
  }
@@ -77,15 +103,154 @@ function parseSource(value, errors) {
77
103
  }
78
104
  return source;
79
105
  }
80
- function parseTickets(value, errors) {
81
- if (value === undefined) {
106
+ function parseDocumentSources(sourcesValue, ticketsValue, errors) {
107
+ if (sourcesValue !== undefined && ticketsValue !== undefined) {
108
+ errors.push("document has both sources and tickets; use sources");
109
+ }
110
+ if (sourcesValue !== undefined) {
111
+ return parseSources(sourcesValue, errors);
112
+ }
113
+ if (ticketsValue !== undefined) {
114
+ return parseLegacyTickets(ticketsValue, errors);
115
+ }
116
+ return undefined;
117
+ }
118
+ function parseSources(value, errors) {
119
+ if (!Array.isArray(value)) {
120
+ errors.push("sources must be an array");
121
+ return undefined;
122
+ }
123
+ const sources = [];
124
+ const ids = new Set();
125
+ value.forEach((item, i) => {
126
+ const source = parseSourceItem(item, `sources[${i}]`, errors);
127
+ if (source === undefined) {
128
+ return;
129
+ }
130
+ if (ids.has(source.id)) {
131
+ errors.push(`duplicate source id "${source.id}"`);
132
+ }
133
+ ids.add(source.id);
134
+ sources.push(source);
135
+ });
136
+ return sources;
137
+ }
138
+ function parseSourceItem(value, label, errors) {
139
+ if (!isRecord(value)) {
140
+ errors.push(`${label} must be an object`);
141
+ return undefined;
142
+ }
143
+ extraKeys(value, REVIEW_SOURCE_KEYS, label, errors);
144
+ const id = requiredString(value.id, `${label}.id`, errors);
145
+ const kind = parseKind(value.kind, `${label}.kind`, errors);
146
+ const sourceLabel = requiredString(value.label, `${label}.label`, errors);
147
+ if (id === undefined || kind === undefined || sourceLabel === undefined) {
82
148
  return undefined;
83
149
  }
150
+ const source = { id, kind, label: sourceLabel };
151
+ if (value.url !== undefined) {
152
+ const url = requiredString(value.url, `${label}.url`, errors);
153
+ if (url !== undefined) {
154
+ source.url = url;
155
+ }
156
+ }
157
+ if (value.title !== undefined) {
158
+ const title = requiredString(value.title, `${label}.title`, errors);
159
+ if (title !== undefined) {
160
+ source.title = title;
161
+ }
162
+ }
163
+ if (value.gist !== undefined) {
164
+ const gist = requiredString(value.gist, `${label}.gist`, errors);
165
+ if (gist !== undefined) {
166
+ source.gist = gist;
167
+ }
168
+ }
169
+ if (value.part !== undefined) {
170
+ const part = requiredString(value.part, `${label}.part`, errors);
171
+ if (part !== undefined) {
172
+ source.part = part;
173
+ }
174
+ }
175
+ if (kind === "transcript" && source.url !== undefined) {
176
+ errors.push(`${label}.url must be omitted for transcripts`);
177
+ }
178
+ assignCommentFields(value, source, kind, label, errors);
179
+ return source;
180
+ }
181
+ function assignCommentFields(value, source, kind, label, errors) {
182
+ const present = COMMENT_ONLY_KEYS.filter((key) => value[key] !== undefined);
183
+ if (kind !== "pr-comment") {
184
+ for (const key of present) {
185
+ errors.push(`${label}.${key} is only valid on pr-comment sources`);
186
+ }
187
+ return;
188
+ }
189
+ if (value.author !== undefined) {
190
+ const author = requiredString(value.author, `${label}.author`, errors);
191
+ if (author !== undefined) {
192
+ source.author = author;
193
+ }
194
+ }
195
+ if (value.body !== undefined) {
196
+ const body = requiredString(value.body, `${label}.body`, errors, { allowEmpty: true });
197
+ if (body !== undefined) {
198
+ source.body = body;
199
+ }
200
+ }
201
+ if (source.author === undefined) {
202
+ errors.push(`${label}.author is required on pr-comment sources`);
203
+ }
204
+ if (source.body === undefined) {
205
+ errors.push(`${label}.body is required on pr-comment sources`);
206
+ }
207
+ const pinCount = [value.path, value.side, value.line].filter((item) => item !== undefined).length;
208
+ if (pinCount === 0) {
209
+ return;
210
+ }
211
+ if (pinCount !== 3) {
212
+ errors.push(`${label} line pin needs path, side, and line together`);
213
+ }
214
+ if (value.path !== undefined) {
215
+ const path = requiredString(value.path, `${label}.path`, errors);
216
+ if (path !== undefined) {
217
+ source.path = path;
218
+ }
219
+ }
220
+ if (value.side !== undefined) {
221
+ if (!isSourceSide(value.side)) {
222
+ errors.push(`${label}.side must be old or new`);
223
+ }
224
+ else {
225
+ source.side = value.side;
226
+ }
227
+ }
228
+ if (value.line !== undefined) {
229
+ const line = requiredInt(value.line, `${label}.line`, errors);
230
+ if (line !== undefined) {
231
+ if (line < 1) {
232
+ errors.push(`${label}.line must be a positive integer`);
233
+ }
234
+ else {
235
+ source.line = line;
236
+ }
237
+ }
238
+ }
239
+ }
240
+ function parseKind(value, label, errors) {
241
+ if (isSourceKind(value)) {
242
+ return value;
243
+ }
244
+ errors.push(`${label} must be one of ${SOURCE_KINDS.join(", ")}`);
245
+ return undefined;
246
+ }
247
+ function parseLegacyTickets(value, errors) {
84
248
  if (!Array.isArray(value)) {
85
249
  errors.push("tickets must be an array");
86
250
  return undefined;
87
251
  }
88
- const tickets = [];
252
+ const sources = [];
253
+ const ids = new Set();
89
254
  value.forEach((item, i) => {
90
255
  if (!isRecord(item)) {
91
256
  errors.push(`tickets[${i}] must be an object`);
@@ -96,30 +261,34 @@ function parseTickets(value, errors) {
96
261
  if (id === undefined) {
97
262
  return;
98
263
  }
99
- const ticket = { id };
264
+ if (ids.has(id)) {
265
+ errors.push(`duplicate source id "${id}"`);
266
+ }
267
+ ids.add(id);
268
+ const source = { id, kind: "ticket", label: id };
100
269
  if (item.url !== undefined) {
101
270
  const url = requiredString(item.url, `tickets[${i}].url`, errors);
102
271
  if (url !== undefined) {
103
- ticket.url = url;
272
+ source.url = url;
104
273
  }
105
274
  }
106
275
  if (item.title !== undefined) {
107
276
  const title = requiredString(item.title, `tickets[${i}].title`, errors);
108
277
  if (title !== undefined) {
109
- ticket.title = title;
278
+ source.title = title;
110
279
  }
111
280
  }
112
281
  if (item.part !== undefined) {
113
282
  const part = requiredString(item.part, `tickets[${i}].part`, errors);
114
283
  if (part !== undefined) {
115
- ticket.part = part;
284
+ source.part = part;
116
285
  }
117
286
  }
118
- tickets.push(ticket);
287
+ sources.push(source);
119
288
  });
120
- return tickets;
289
+ return sources;
121
290
  }
122
- function parseGroups(value, errors) {
291
+ function parseGroups(value, sources, errors) {
123
292
  if (!Array.isArray(value)) {
124
293
  errors.push("groups must be an array");
125
294
  return [];
@@ -140,6 +309,7 @@ function parseGroups(value, errors) {
140
309
  const hunkRefs = parseHunkRefs(item.hunkRefs, `groups[${i}].hunkRefs`, errors);
141
310
  const lookFor = parseStringList(item.lookFor, `groups[${i}].lookFor`, errors);
142
311
  const dependsOn = parseStringList(item.dependsOn, `groups[${i}].dependsOn`, errors);
312
+ const groupSources = parseStringList(item.sources, `groups[${i}].sources`, errors);
143
313
  const part = item.part === undefined ? undefined : requiredString(item.part, `groups[${i}].part`, errors);
144
314
  if (id === undefined || title === undefined || why === undefined || summary === undefined || suggestedOrder === undefined) {
145
315
  return;
@@ -158,18 +328,32 @@ function parseGroups(value, errors) {
158
328
  if (part !== undefined) {
159
329
  group.part = part;
160
330
  }
331
+ if (groupSources !== undefined) {
332
+ group.sources = groupSources;
333
+ }
161
334
  groups.push(group);
162
335
  });
163
- const known = new Set(groups.map((group) => group.id));
336
+ const knownGroups = new Set(groups.map((group) => group.id));
337
+ const knownSources = new Set((sources ?? []).map((source) => source.id));
164
338
  for (const group of groups) {
165
339
  for (const dep of group.dependsOn ?? []) {
166
340
  if (dep === group.id) {
167
341
  errors.push(`groups id "${group.id}" depends on itself`);
168
342
  }
169
- else if (!known.has(dep)) {
343
+ else if (!knownGroups.has(dep)) {
170
344
  errors.push(`groups id "${group.id}" dependsOn unknown group "${dep}"`);
171
345
  }
172
346
  }
347
+ const seen = new Set();
348
+ for (const sourceId of group.sources ?? []) {
349
+ if (seen.has(sourceId)) {
350
+ errors.push(`groups id "${group.id}" lists source "${sourceId}" twice`);
351
+ }
352
+ seen.add(sourceId);
353
+ if (!knownSources.has(sourceId)) {
354
+ errors.push(`groups id "${group.id}" sources unknown id "${sourceId}"`);
355
+ }
356
+ }
173
357
  }
174
358
  return groups;
175
359
  }