local-review 0.0.1 → 0.3.0

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 (372) hide show
  1. package/AGENT-GUIDE.md +468 -0
  2. package/CHANGELOG.md +67 -0
  3. package/LICENSE +21 -0
  4. package/README.md +126 -2
  5. package/dist/demo/lib/gitworld.js +224 -0
  6. package/dist/demo/world/api.js +2436 -0
  7. package/dist/demo/world/infra.js +189 -0
  8. package/dist/demo/world/people.js +16 -0
  9. package/dist/demo/world/review.js +578 -0
  10. package/dist/demo/world/sdk.js +376 -0
  11. package/dist/demo/world/web.js +657 -0
  12. package/dist/server/anchors.js +207 -0
  13. package/dist/server/assets.js +147 -0
  14. package/dist/server/backups.js +212 -0
  15. package/dist/server/branches.js +113 -0
  16. package/dist/server/cli.js +283 -0
  17. package/dist/server/commits.js +393 -0
  18. package/dist/server/config.js +62 -0
  19. package/dist/server/context.js +68 -0
  20. package/dist/server/events.js +87 -0
  21. package/dist/server/examples.js +204 -0
  22. package/dist/server/git.js +103 -0
  23. package/dist/server/index.js +581 -0
  24. package/dist/server/landed.js +71 -0
  25. package/dist/server/plan.js +260 -0
  26. package/dist/server/port.js +3 -0
  27. package/dist/server/projectEdit.js +117 -0
  28. package/dist/server/projects.js +321 -0
  29. package/dist/server/prs.js +314 -0
  30. package/dist/server/reviewer.js +33 -0
  31. package/dist/server/reviews.js +586 -0
  32. package/dist/server/safefs.js +92 -0
  33. package/dist/server/security.js +38 -0
  34. package/dist/server/stackviz.js +446 -0
  35. package/dist/shared/assets.js +54 -0
  36. package/dist/shared/paths.js +13 -0
  37. package/dist/shared/prText.js +22 -0
  38. package/dist/shared/readiness.js +37 -0
  39. package/dist/shared/repoLabel.js +13 -0
  40. package/dist/shared/totals.js +75 -0
  41. package/dist/web/assets/abap-CLvhMVsD.js +1 -0
  42. package/dist/web/assets/actionscript-3-Cs6JJXT5.js +1 -0
  43. package/dist/web/assets/ada-X3Y2mlq2.js +1 -0
  44. package/dist/web/assets/ahk-DwADkxyd.js +1 -0
  45. package/dist/web/assets/ahk2-_6rzW9BS.js +1 -0
  46. package/dist/web/assets/andromeeda-vGVdxbeo.js +1 -0
  47. package/dist/web/assets/angular-html-qFVZP7wG.js +1 -0
  48. package/dist/web/assets/angular-ts-DzOxtaC9.js +1 -0
  49. package/dist/web/assets/apache-U0d_L8uA.js +1 -0
  50. package/dist/web/assets/apex-KFGFyKwG.js +1 -0
  51. package/dist/web/assets/apl-0AThSj8r.js +1 -0
  52. package/dist/web/assets/apple-touch-icon-BNrSfFwW.png +0 -0
  53. package/dist/web/assets/applescript-CCn79oCD.js +1 -0
  54. package/dist/web/assets/ara-4CJ0cIlV.js +1 -0
  55. package/dist/web/assets/asciidoc-SCjQUq34.js +1 -0
  56. package/dist/web/assets/asm-Cmm7eHzH.js +1 -0
  57. package/dist/web/assets/astro-By53bk_I.js +1 -0
  58. package/dist/web/assets/aurora-x-CDeNXAV0.js +1 -0
  59. package/dist/web/assets/awk-BWXHIvNe.js +1 -0
  60. package/dist/web/assets/ayu-dark-DluEY0Gj.js +1 -0
  61. package/dist/web/assets/ayu-light-C3h-C4tm.js +1 -0
  62. package/dist/web/assets/ayu-mirage-Bqwy1Gya.js +1 -0
  63. package/dist/web/assets/ballerina-B7ZEbQpA.js +1 -0
  64. package/dist/web/assets/bat-qdxAdBWQ.js +1 -0
  65. package/dist/web/assets/beancount-C2IN4Vk8.js +1 -0
  66. package/dist/web/assets/berry-DKpUyyne.js +1 -0
  67. package/dist/web/assets/bibtex-Ci_nEsc7.js +1 -0
  68. package/dist/web/assets/bicep-CUHmPFLl.js +1 -0
  69. package/dist/web/assets/bird2-CYIUgYqB.js +1 -0
  70. package/dist/web/assets/blade-Dw_Nglq9.js +1 -0
  71. package/dist/web/assets/bsl-_VjlB9mv.js +1 -0
  72. package/dist/web/assets/c-CQT-cQHo.js +1 -0
  73. package/dist/web/assets/c3-D0apG41V.js +1 -0
  74. package/dist/web/assets/cadence-CQ2zXKGN.js +1 -0
  75. package/dist/web/assets/cairo-DLTphjLi.js +1 -0
  76. package/dist/web/assets/catppuccin-frappe-C9Q5zVZY.js +1 -0
  77. package/dist/web/assets/catppuccin-latte-CtsyrtPq.js +1 -0
  78. package/dist/web/assets/catppuccin-macchiato-R8nAf1vR.js +1 -0
  79. package/dist/web/assets/catppuccin-mocha-DjuaAJKi.js +1 -0
  80. package/dist/web/assets/chapel-64o3Hib1.js +1 -0
  81. package/dist/web/assets/clarity-CtfBBspg.js +1 -0
  82. package/dist/web/assets/clojure-DqKBuwfJ.js +1 -0
  83. package/dist/web/assets/cmake-Bj61d0ZC.js +1 -0
  84. package/dist/web/assets/cobol-COyB9oJ9.js +1 -0
  85. package/dist/web/assets/codeowners-C8r90Shi.js +1 -0
  86. package/dist/web/assets/codeql-oeQT6MSM.js +1 -0
  87. package/dist/web/assets/coffee-BGIfDQTv.js +1 -0
  88. package/dist/web/assets/common-lisp-Cv5bFMCO.js +1 -0
  89. package/dist/web/assets/coq-CGaSfhwR.js +1 -0
  90. package/dist/web/assets/cpp-wi7ixCRa.js +1 -0
  91. package/dist/web/assets/crystal-ADRCp2kP.js +1 -0
  92. package/dist/web/assets/csharp-dmMxLVLW.js +1 -0
  93. package/dist/web/assets/css-BH9tz7qt.js +1 -0
  94. package/dist/web/assets/csv-Dx-8-gkx.js +1 -0
  95. package/dist/web/assets/cue-CE9AQfxI.js +1 -0
  96. package/dist/web/assets/cypher-ClKdZ_lG.js +1 -0
  97. package/dist/web/assets/d-qD-0Kul2.js +1 -0
  98. package/dist/web/assets/dark-plus-Cs2F2srj.js +1 -0
  99. package/dist/web/assets/dart-CHZY9Xhz.js +1 -0
  100. package/dist/web/assets/dax-BkyTk9wS.js +1 -0
  101. package/dist/web/assets/desktop-Dlh5hvp9.js +1 -0
  102. package/dist/web/assets/diff-woXpYk--.js +1 -0
  103. package/dist/web/assets/docker-IyjqRm3v.js +1 -0
  104. package/dist/web/assets/dotenv-CuH5my32.js +1 -0
  105. package/dist/web/assets/dracula-BHWKrbxM.js +1 -0
  106. package/dist/web/assets/dracula-soft-5eyTD99u.js +1 -0
  107. package/dist/web/assets/dream-maker-DW3nJb8Q.js +1 -0
  108. package/dist/web/assets/edge-DFzphiQm.js +1 -0
  109. package/dist/web/assets/elixir-BUj3AoF_.js +1 -0
  110. package/dist/web/assets/elm-Bqgz9ryu.js +1 -0
  111. package/dist/web/assets/emacs-lisp-DR2vtWaj.js +1 -0
  112. package/dist/web/assets/erb-DHHWfqsN.js +1 -0
  113. package/dist/web/assets/erlang-Cphh6RMH.js +1 -0
  114. package/dist/web/assets/everforest-dark-sB-x3p7T.js +1 -0
  115. package/dist/web/assets/everforest-light-Df2xbC6M.js +1 -0
  116. package/dist/web/assets/favicon-Bld6z4jR.svg +13 -0
  117. package/dist/web/assets/favicon-CDTyigy2.ico +0 -0
  118. package/dist/web/assets/fennel-DQxkIbk2.js +1 -0
  119. package/dist/web/assets/fish-BJitypiv.js +1 -0
  120. package/dist/web/assets/fluent-C03EYrpw.js +1 -0
  121. package/dist/web/assets/fortran-fixed-form-DEKoE2YW.js +1 -0
  122. package/dist/web/assets/fortran-free-form-CYNrtFtB.js +1 -0
  123. package/dist/web/assets/fsharp-D13ZGOAj.js +1 -0
  124. package/dist/web/assets/gdresource-xMC7jr7x.js +1 -0
  125. package/dist/web/assets/gdscript-DYvRD3hg.js +1 -0
  126. package/dist/web/assets/gdshader-CBce3t8t.js +1 -0
  127. package/dist/web/assets/genie-CV2tkWYe.js +1 -0
  128. package/dist/web/assets/gherkin-DExj1W_8.js +1 -0
  129. package/dist/web/assets/git-commit-BSykSTBG.js +1 -0
  130. package/dist/web/assets/git-rebase-C7iLD81B.js +1 -0
  131. package/dist/web/assets/github-dark-C-LZuMrd.js +1 -0
  132. package/dist/web/assets/github-dark-default-DXG-b-1a.js +1 -0
  133. package/dist/web/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
  134. package/dist/web/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
  135. package/dist/web/assets/github-light-EUqPIrTm.js +1 -0
  136. package/dist/web/assets/github-light-default-BXViO-2h.js +1 -0
  137. package/dist/web/assets/github-light-high-contrast-B68TUdTA.js +1 -0
  138. package/dist/web/assets/gleam-CSRkHgEL.js +1 -0
  139. package/dist/web/assets/glimmer-js-BT_UjuD4.js +1 -0
  140. package/dist/web/assets/glimmer-ts-BpeqXZCf.js +1 -0
  141. package/dist/web/assets/glsl-mPoRKPQX.js +1 -0
  142. package/dist/web/assets/gn-CAXYAgV-.js +1 -0
  143. package/dist/web/assets/gnuplot-7GGW24-e.js +1 -0
  144. package/dist/web/assets/go-rLFTqkRN.js +1 -0
  145. package/dist/web/assets/graphql-D5e-luGt.js +1 -0
  146. package/dist/web/assets/groovy-CacY0gHj.js +1 -0
  147. package/dist/web/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
  148. package/dist/web/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
  149. package/dist/web/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
  150. package/dist/web/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
  151. package/dist/web/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
  152. package/dist/web/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
  153. package/dist/web/assets/hack-Yj_0xAq9.js +1 -0
  154. package/dist/web/assets/haml-rCEvyqvs.js +1 -0
  155. package/dist/web/assets/handlebars-CZRXN7yP.js +1 -0
  156. package/dist/web/assets/haskell-D8IpX4py.js +1 -0
  157. package/dist/web/assets/haxe-C8oCnwJJ.js +1 -0
  158. package/dist/web/assets/hcl-Dh228itO.js +1 -0
  159. package/dist/web/assets/hjson-CxZEssPk.js +1 -0
  160. package/dist/web/assets/hlsl-Cvrh5tZx.js +1 -0
  161. package/dist/web/assets/horizon-CE9ld1lL.js +1 -0
  162. package/dist/web/assets/horizon-bright-DSNQnXHK.js +1 -0
  163. package/dist/web/assets/houston-CsvMBhTu.js +1 -0
  164. package/dist/web/assets/html-XqiqNW99.js +1 -0
  165. package/dist/web/assets/html-derivative-nWC0aMV7.js +1 -0
  166. package/dist/web/assets/http-C4fFBmNU.js +1 -0
  167. package/dist/web/assets/hurl-DjZ_NyWj.js +1 -0
  168. package/dist/web/assets/hxml-Dcm88srW.js +1 -0
  169. package/dist/web/assets/hy-CZbG8q4J.js +1 -0
  170. package/dist/web/assets/imba-DsUTQ-LC.js +1 -0
  171. package/dist/web/assets/index-BPfnOrNh.js +451 -0
  172. package/dist/web/assets/index-BplAAg-1.css +1 -0
  173. package/dist/web/assets/ini-B5eOa1yu.js +1 -0
  174. package/dist/web/assets/java-B0zZG6CJ.js +1 -0
  175. package/dist/web/assets/javascript-CFawPmAZ.js +1 -0
  176. package/dist/web/assets/jinja-qu2nA6S7.js +1 -0
  177. package/dist/web/assets/jison-DviWniCr.js +1 -0
  178. package/dist/web/assets/json-C0vF7y3r.js +1 -0
  179. package/dist/web/assets/json5-BR5RXkoi.js +1 -0
  180. package/dist/web/assets/jsonc-CYpm1nAK.js +1 -0
  181. package/dist/web/assets/jsonl-CmCQp5Yx.js +1 -0
  182. package/dist/web/assets/jsonnet-CJTPZ8u_.js +1 -0
  183. package/dist/web/assets/jssm-DXw9l8Rf.js +1 -0
  184. package/dist/web/assets/jsx-DQYIrdZV.js +1 -0
  185. package/dist/web/assets/julia-C_b0WNzR.js +1 -0
  186. package/dist/web/assets/just-NwM_1ruS.js +1 -0
  187. package/dist/web/assets/kanagawa-dragon-Bozd7klA.js +1 -0
  188. package/dist/web/assets/kanagawa-lotus-BmuOG9ZW.js +1 -0
  189. package/dist/web/assets/kanagawa-wave-BML5mF2J.js +1 -0
  190. package/dist/web/assets/kdl-CsD5j6eV.js +1 -0
  191. package/dist/web/assets/kotlin-DhhofPvG.js +1 -0
  192. package/dist/web/assets/kusto-C7mF5XQf.js +1 -0
  193. package/dist/web/assets/laserwave-C_8bwKvT.js +1 -0
  194. package/dist/web/assets/latex-As2zLFN8.js +1 -0
  195. package/dist/web/assets/lean-CewbzKMR.js +1 -0
  196. package/dist/web/assets/less-DVTAwKKz.js +1 -0
  197. package/dist/web/assets/light-plus-DVQuIRkW.js +1 -0
  198. package/dist/web/assets/liquid-By8qNhnm.js +1 -0
  199. package/dist/web/assets/llvm-DqHOMS-s.js +1 -0
  200. package/dist/web/assets/log-BNLmms1o.js +1 -0
  201. package/dist/web/assets/logo-Cluzi2Zq.js +1 -0
  202. package/dist/web/assets/lua-Dairqw_c.js +1 -0
  203. package/dist/web/assets/luau-VGWVLoqX.js +1 -0
  204. package/dist/web/assets/make-Dixweg8N.js +1 -0
  205. package/dist/web/assets/markdown-BYOwaDjH.js +1 -0
  206. package/dist/web/assets/marko-C2UPN-L_.js +1 -0
  207. package/dist/web/assets/material-theme-Bm3Qr25_.js +1 -0
  208. package/dist/web/assets/material-theme-darker-2IIEA8gg.js +1 -0
  209. package/dist/web/assets/material-theme-lighter-uhdI0v04.js +1 -0
  210. package/dist/web/assets/material-theme-ocean-CHQ94UKr.js +1 -0
  211. package/dist/web/assets/material-theme-palenight-B5W6OYN7.js +1 -0
  212. package/dist/web/assets/matlab-D7qyCx1q.js +1 -0
  213. package/dist/web/assets/mdc-Eijzzwck.js +1 -0
  214. package/dist/web/assets/mdx-DQZ5AkYe.js +1 -0
  215. package/dist/web/assets/mermaid-BxpfTIzt.js +1 -0
  216. package/dist/web/assets/min-dark-BSWPekZh.js +1 -0
  217. package/dist/web/assets/min-light-DDpmG2fV.js +1 -0
  218. package/dist/web/assets/mipsasm-BMqwQI7S.js +1 -0
  219. package/dist/web/assets/mojo-fg40_ntQ.js +1 -0
  220. package/dist/web/assets/monokai-BscAjiQZ.js +1 -0
  221. package/dist/web/assets/moonbit-D9W784E_.js +1 -0
  222. package/dist/web/assets/move-BYnGhZoM.js +1 -0
  223. package/dist/web/assets/narrat-_X_XdTYD.js +1 -0
  224. package/dist/web/assets/nextflow-CSgcYZcx.js +1 -0
  225. package/dist/web/assets/nextflow-groovy-D7IRt6Pp.js +1 -0
  226. package/dist/web/assets/nginx-BF8eLz9n.js +1 -0
  227. package/dist/web/assets/night-owl-DhmEMT88.js +1 -0
  228. package/dist/web/assets/night-owl-light-eJ-hLW7d.js +1 -0
  229. package/dist/web/assets/nim-BlPfC-Lu.js +1 -0
  230. package/dist/web/assets/nix-IvuFDN5E.js +1 -0
  231. package/dist/web/assets/nord-Cb4Vim4T.js +1 -0
  232. package/dist/web/assets/nsis-CjNLYCLg.js +1 -0
  233. package/dist/web/assets/nushell--0fT3PI1.js +1 -0
  234. package/dist/web/assets/objective-c-D1A_Heim.js +1 -0
  235. package/dist/web/assets/objective-cpp-BsSzOQcm.js +1 -0
  236. package/dist/web/assets/ocaml-O90oeIOV.js +1 -0
  237. package/dist/web/assets/odin-B1RWQWA5.js +1 -0
  238. package/dist/web/assets/one-dark-pro-C6YyR8OD.js +1 -0
  239. package/dist/web/assets/one-light-D7Lr4KcI.js +1 -0
  240. package/dist/web/assets/openscad-BUDT5pXO.js +1 -0
  241. package/dist/web/assets/org-jZgCqYxI.js +1 -0
  242. package/dist/web/assets/pascal-4ZHwLPI5.js +1 -0
  243. package/dist/web/assets/perl-BIZHUlMC.js +1 -0
  244. package/dist/web/assets/php-Bu0o-4mH.js +1 -0
  245. package/dist/web/assets/pierre-dark-CpLgRqie.js +1 -0
  246. package/dist/web/assets/pierre-dark-protanopia-deuteranopia-B35FxJx-.js +1 -0
  247. package/dist/web/assets/pierre-dark-soft-kZQmAZld.js +1 -0
  248. package/dist/web/assets/pierre-dark-tritanopia-CpjhbsIL.js +1 -0
  249. package/dist/web/assets/pierre-dark-vibrant-CpQYzh95.js +1 -0
  250. package/dist/web/assets/pierre-light-CoaEpmwp.js +1 -0
  251. package/dist/web/assets/pierre-light-protanopia-deuteranopia-0fSaH845.js +1 -0
  252. package/dist/web/assets/pierre-light-soft-lWLdNTOI.js +1 -0
  253. package/dist/web/assets/pierre-light-tritanopia-CEbqgOJL.js +1 -0
  254. package/dist/web/assets/pierre-light-vibrant-D80Fkn33.js +1 -0
  255. package/dist/web/assets/pkl-ot-7Btpt.js +1 -0
  256. package/dist/web/assets/plastic-DQwYfKfQ.js +1 -0
  257. package/dist/web/assets/plsql-DGHpHOYJ.js +1 -0
  258. package/dist/web/assets/po-BiJDBrnU.js +1 -0
  259. package/dist/web/assets/poimandres-DRFjx7u4.js +1 -0
  260. package/dist/web/assets/polar-C7UOKdEL.js +1 -0
  261. package/dist/web/assets/postcss-BXeXVLqQ.js +1 -0
  262. package/dist/web/assets/powerquery-DNMTfnFr.js +1 -0
  263. package/dist/web/assets/powershell-QtSnmXcu.js +1 -0
  264. package/dist/web/assets/prisma-DMgNhnYS.js +1 -0
  265. package/dist/web/assets/prolog-iXnhIJG7.js +1 -0
  266. package/dist/web/assets/proto-DB4EqR-F.js +1 -0
  267. package/dist/web/assets/pug-Benj2kck.js +1 -0
  268. package/dist/web/assets/puppet-CDv2pdJW.js +1 -0
  269. package/dist/web/assets/purescript-9MfHhQsQ.js +1 -0
  270. package/dist/web/assets/python-gzcpVVnB.js +1 -0
  271. package/dist/web/assets/qml-BHSeoMrW.js +1 -0
  272. package/dist/web/assets/qmldir-DCQb3MpD.js +1 -0
  273. package/dist/web/assets/qss-Fe1Jh2GI.js +1 -0
  274. package/dist/web/assets/r-CUvEVkuI.js +1 -0
  275. package/dist/web/assets/racket-DcIDlBhZ.js +1 -0
  276. package/dist/web/assets/raku-B3gFvitq.js +1 -0
  277. package/dist/web/assets/razor-BwPBMIxs.js +1 -0
  278. package/dist/web/assets/rbs-C0MpOrj4.js +1 -0
  279. package/dist/web/assets/red-CYlAq2b0.js +1 -0
  280. package/dist/web/assets/reg-CRGYupPL.js +1 -0
  281. package/dist/web/assets/regexp-_ifpjSOj.js +1 -0
  282. package/dist/web/assets/rel-BtDbiS_P.js +1 -0
  283. package/dist/web/assets/riscv-Ckw8ddFX.js +1 -0
  284. package/dist/web/assets/ron-VUp2lXgN.js +1 -0
  285. package/dist/web/assets/rose-pine-aMsaMD1D.js +1 -0
  286. package/dist/web/assets/rose-pine-dawn-8PwUyIlP.js +1 -0
  287. package/dist/web/assets/rose-pine-moon-DO-qXg-I.js +1 -0
  288. package/dist/web/assets/rosmsg-CAekHB0j.js +1 -0
  289. package/dist/web/assets/rst-Es4SE5IJ.js +1 -0
  290. package/dist/web/assets/ruby-76YJjt1j.js +1 -0
  291. package/dist/web/assets/rust-DpiIf5JO.js +1 -0
  292. package/dist/web/assets/sas-BEWDsPnW.js +1 -0
  293. package/dist/web/assets/sass-DXrisJhu.js +1 -0
  294. package/dist/web/assets/scala-BiFt_7mu.js +1 -0
  295. package/dist/web/assets/scheme-DQCgrYNe.js +1 -0
  296. package/dist/web/assets/scss-DgcF5VZK.js +1 -0
  297. package/dist/web/assets/sdbl-bTVj8UrX.js +1 -0
  298. package/dist/web/assets/shaderlab-TOUzSsQk.js +1 -0
  299. package/dist/web/assets/shellscript-Cty74sqM.js +1 -0
  300. package/dist/web/assets/shellsession-BCfSy2gp.js +1 -0
  301. package/dist/web/assets/slack-dark-DnToyrRv.js +1 -0
  302. package/dist/web/assets/slack-ochin-B2OO5cIa.js +1 -0
  303. package/dist/web/assets/smalltalk-BHpQb5nM.js +1 -0
  304. package/dist/web/assets/smithy-Dgaz7aur.js +1 -0
  305. package/dist/web/assets/snazzy-light-4G7pJPwS.js +1 -0
  306. package/dist/web/assets/solarized-dark-b97czJOk.js +1 -0
  307. package/dist/web/assets/solarized-light-DFPxpIYQ.js +1 -0
  308. package/dist/web/assets/solidity-Bj7E1mRn.js +1 -0
  309. package/dist/web/assets/soy-dM_k5IdG.js +1 -0
  310. package/dist/web/assets/sparql-D_iOobhT.js +1 -0
  311. package/dist/web/assets/splunk-BC2Px7Mm.js +1 -0
  312. package/dist/web/assets/sql-Dr4UDgPO.js +1 -0
  313. package/dist/web/assets/ssh-config-BgfXC-Er.js +1 -0
  314. package/dist/web/assets/stata-COCyw4rB.js +1 -0
  315. package/dist/web/assets/stylus-B6D30XZt.js +1 -0
  316. package/dist/web/assets/surrealql-Cc-YKbr5.js +1 -0
  317. package/dist/web/assets/svelte-BBNgvYF5.js +1 -0
  318. package/dist/web/assets/swift-CyEgAFGc.js +1 -0
  319. package/dist/web/assets/synthwave-84-nFMaYfgc.js +1 -0
  320. package/dist/web/assets/system-verilog-Dh5ihWbf.js +1 -0
  321. package/dist/web/assets/systemd-BxMlprV5.js +1 -0
  322. package/dist/web/assets/talonscript-CohzipZa.js +1 -0
  323. package/dist/web/assets/tasl-DMoTqEGO.js +1 -0
  324. package/dist/web/assets/tcl-CZd0xW_V.js +1 -0
  325. package/dist/web/assets/templ-D8WXh8mG.js +1 -0
  326. package/dist/web/assets/terraform-DswuEJGm.js +1 -0
  327. package/dist/web/assets/tex-BNsfM6-w.js +1 -0
  328. package/dist/web/assets/tokyo-night-oM2G3aXe.js +1 -0
  329. package/dist/web/assets/toml-CcmNWLt0.js +1 -0
  330. package/dist/web/assets/ts-tags-C5cp5d5e.js +1 -0
  331. package/dist/web/assets/tsv-sltzmVWM.js +1 -0
  332. package/dist/web/assets/tsx-D0nBgoOJ.js +1 -0
  333. package/dist/web/assets/turtle-ByJddavk.js +1 -0
  334. package/dist/web/assets/twig-Clef_vTO.js +1 -0
  335. package/dist/web/assets/typescript-84qFVDd8.js +1 -0
  336. package/dist/web/assets/typespec-BGhR2SqG.js +1 -0
  337. package/dist/web/assets/typst-kHrpsAej.js +1 -0
  338. package/dist/web/assets/v-DBj4K9d_.js +1 -0
  339. package/dist/web/assets/vala-zf12oZj6.js +1 -0
  340. package/dist/web/assets/vb-DOwzaHZ0.js +1 -0
  341. package/dist/web/assets/verilog-B2gwz-sJ.js +1 -0
  342. package/dist/web/assets/vesper-DdrHHSXu.js +1 -0
  343. package/dist/web/assets/vhdl-BroJfC0k.js +1 -0
  344. package/dist/web/assets/viml-DvXPmvsu.js +1 -0
  345. package/dist/web/assets/vitesse-black-fwtXNY1n.js +1 -0
  346. package/dist/web/assets/vitesse-dark-BZCL-v6S.js +1 -0
  347. package/dist/web/assets/vitesse-light-VbXTXTou.js +1 -0
  348. package/dist/web/assets/vue-D_k3LQNp.js +1 -0
  349. package/dist/web/assets/vue-html-Bclb5q-s.js +1 -0
  350. package/dist/web/assets/vue-vine-BvY0ORZK.js +1 -0
  351. package/dist/web/assets/vyper-CgoNMtux.js +1 -0
  352. package/dist/web/assets/wasm-BnjxR4X6.js +1 -0
  353. package/dist/web/assets/wasm-ByWQv1Qj.js +1 -0
  354. package/dist/web/assets/wenyan-C8pVoKbM.js +1 -0
  355. package/dist/web/assets/wgsl-BsKzXJz4.js +1 -0
  356. package/dist/web/assets/wikitext-ClFFjSW2.js +1 -0
  357. package/dist/web/assets/wit-DdvCle-K.js +1 -0
  358. package/dist/web/assets/wolfram-DLL8P-h_.js +1 -0
  359. package/dist/web/assets/xml-BgfQ1YbS.js +1 -0
  360. package/dist/web/assets/xsl-DEqH3rje.js +1 -0
  361. package/dist/web/assets/yaml-BiWwl5JE.js +1 -0
  362. package/dist/web/assets/zenscript-BnlCZFoB.js +1 -0
  363. package/dist/web/assets/zig-CMLA9XwU.js +1 -0
  364. package/dist/web/index.html +16 -0
  365. package/examples/project.example.yaml +40 -0
  366. package/package.json +90 -3
  367. package/server/fonts/Inter-OFL.txt +93 -0
  368. package/server/fonts/Inter_400Regular.ttf +0 -0
  369. package/server/fonts/Inter_600SemiBold.ttf +0 -0
  370. package/server/fonts/Inter_700Bold.ttf +0 -0
  371. package/server/fonts/JetBrainsMono-OFL.txt +93 -0
  372. package/server/fonts/JetBrainsMono_400Regular.ttf +0 -0
package/README.md CHANGED
@@ -1,3 +1,127 @@
1
- # local-review
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/edspencer/local-review/main/brand/lockup-dark.svg">
4
+ <img alt="Local Review" src="https://raw.githubusercontent.com/edspencer/local-review/main/brand/lockup-light.svg" width="380">
5
+ </picture>
6
+ </p>
2
7
 
3
- Placeholder. See https://github.com/edspencer/local-review
8
+ <p align="center"><strong>Review your PR stacks before you create them.</strong></p>
9
+
10
+ <p align="center">
11
+ <a href="https://github.com/edspencer/local-review/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/edspencer/local-review/actions/workflows/ci.yml/badge.svg?branch=main"></a>
12
+ <a href="https://www.npmjs.com/package/local-review"><img alt="npm" src="https://img.shields.io/npm/v/local-review?color=4a1942"></a>
13
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue"></a>
14
+ <a href="https://local-review.edspencer.net"><img alt="Docs" src="https://img.shields.io/badge/docs-local--review.edspencer.net-4a1942"></a>
15
+ <img alt="Node.js 22 or later" src="https://img.shields.io/badge/node-%E2%89%A522-339933">
16
+ </p>
17
+
18
+ <p align="center">
19
+ <img alt="Local Review showing a PR: the plum &quot;not on GitHub&quot; header, a sidebar of colour-coded PR stacks with readiness badges, and the PR's title, readiness toggle and Conversation tab" src="https://raw.githubusercontent.com/edspencer/local-review/main/docs/media/readme-hero.png" width="800">
20
+ </p>
21
+
22
+ Local Review is a GitHub-style pull-request UI for the **stack of commits on your machine**, before any of it is
23
+ pushed. Every commit is shown as its own PR, grouped into the stacks you mean to open. You review them like PRs, your
24
+ coding agent reads your comments and replies in the same place, and when a stack is right, the agent publishes it as
25
+ GitHub stacked PRs.
26
+
27
+ - **Every commit is a PR**, with a Conversation tab, a Files changed tab and a readiness badge, grouped into
28
+ colour-coded [PR stacks](https://local-review.edspencer.net/concepts/pr-stacks/), across one repo or several.
29
+ - **Your agent answers in the review.** Comments are plain YAML files that any coding agent can read and reply to; the
30
+ replies show up in your browser live, and threads follow their code through amends and rebases.
31
+ - **Publish when it's ready.** `local-review plan` prints each PR's branch, base, title and body, and your agent opens
32
+ the stack as [GitHub native stacked PRs](https://local-review.edspencer.net/agents/publishing-stacked-prs/). It
33
+ works in front of any [stacking tool](https://local-review.edspencer.net/concepts/stacking-tools/).
34
+ - **Safe to point at your work.** It never modifies your repos, never talks to GitHub, never deletes your review, and
35
+ only listens on localhost.
36
+
37
+ <p align="center">
38
+ <img alt="A review round trip: the reviewer comments on a range of lines, the agent's reply appears in the thread live, the reviewer resolves the thread and marks the PR ready, and the sidebar's stack tally goes from 1/3 to 2/3 ready" src="https://raw.githubusercontent.com/edspencer/local-review/main/docs/media/demo.gif" width="800">
39
+ <br><sub>One review round trip, on the fictional demo project the docs use.</sub>
40
+ </p>
41
+
42
+ ## Quickstart
43
+
44
+ Needs Node.js 22 or later and git, on macOS or Linux.
45
+
46
+ ```sh
47
+ npx local-review init my-feature # create ~/.local-review/projects/my-feature/project.yaml
48
+ npx local-review # serve the UI on http://localhost:5622
49
+ ```
50
+
51
+ Edit `project.yaml` to point at your repo, branch and stacks:
52
+
53
+ ```yaml
54
+ title: My feature
55
+ repos:
56
+ - path: ~/code/my-service
57
+ branch: me/my-feature # the range ends at this branch's head
58
+ base: origin/main # and starts at its merge-base with this
59
+ stacks:
60
+ - title: Groundwork
61
+ - title: The feature itself
62
+ starts_at: "Add the feature flag" # the subject of the commit the stack starts at
63
+ ```
64
+
65
+ Then open **http://localhost:5622**. Edits are picked up live. The full walk-through is the
66
+ [Quickstart](https://local-review.edspencer.net/getting-started/quickstart/).
67
+
68
+ > **Upgrading from before 0.2.0?** The default port was 5000; it's now 5622 (macOS uses 5000 for AirPlay). Update
69
+ > bookmarks and agent prompts, or run `npx local-review --port 5000`.
70
+
71
+ ## For agents
72
+
73
+ ```sh
74
+ npx local-review guide
75
+ ```
76
+
77
+ prints the guide for coding agents driving Local Review from the repo they work on: the data model, how to answer
78
+ review threads, fix a commit mid-stack, tag branches, publish stacked PRs and record merges. It's
79
+ [AGENT-GUIDE.md](AGENT-GUIDE.md) in this repo, and [on the docs site](https://local-review.edspencer.net/agents/agent-guide/).
80
+ To hand a reviewed project to your agent, paste the
81
+ [hand-off prompt](https://local-review.edspencer.net/agents/hand-off-prompt/).
82
+
83
+ ## Documentation
84
+
85
+ **https://local-review.edspencer.net**
86
+
87
+ - **Getting started:** [Quickstart](https://local-review.edspencer.net/getting-started/quickstart/) ·
88
+ [Installation](https://local-review.edspencer.net/getting-started/installation/)
89
+ - **Concepts:** [Projects & repos](https://local-review.edspencer.net/concepts/projects-and-repos/) ·
90
+ [PR stacks](https://local-review.edspencer.net/concepts/pr-stacks/) ·
91
+ [Readiness](https://local-review.edspencer.net/concepts/readiness/) ·
92
+ [Landed PRs](https://local-review.edspencer.net/concepts/landed-prs/) ·
93
+ [Following rebases](https://local-review.edspencer.net/concepts/following-rebases/)
94
+ - **Using it:** [A tour of the UI](https://local-review.edspencer.net/using/ui-tour/) ·
95
+ [Reviewing & comments](https://local-review.edspencer.net/using/reviewing-and-comments/) ·
96
+ [Stack pictures](https://local-review.edspencer.net/using/stack-pictures/)
97
+ - **Reference:** [CLI](https://local-review.edspencer.net/reference/cli/) ·
98
+ [project.yaml](https://local-review.edspencer.net/reference/project-yaml/) ·
99
+ [Comment files](https://local-review.edspencer.net/reference/comment-files/) ·
100
+ [HTTP API](https://local-review.edspencer.net/reference/http-api/) ·
101
+ [Where your data lives](https://local-review.edspencer.net/reference/data-directory/)
102
+
103
+ ## Guarantees
104
+
105
+ - **Read-only:** it never fetches, checks out, commits or rebases; all git access goes through one read-only
106
+ allow-list. [Read-only guarantee](https://local-review.edspencer.net/guarantees/read-only/)
107
+ - **Never deletes:** your review lives in `~/.local-review`, never in your repos; writes are atomic, nothing is
108
+ unlinked, and it's snapshotted hourly. [Never-delete guarantee](https://local-review.edspencer.net/guarantees/never-delete/)
109
+ - **Localhost only:** it binds 127.0.0.1 and rejects non-localhost `Host` and `Origin` headers.
110
+ [Localhost security](https://local-review.edspencer.net/guarantees/localhost-security/)
111
+
112
+ And so you can't mistake it for github.com, its header is deep plum with an amber underline and a "not on GitHub" pill.
113
+
114
+ ## Contributing
115
+
116
+ Contributions are welcome: see [CONTRIBUTING.md](CONTRIBUTING.md), and [AGENTS.md](AGENTS.md) for coding agents
117
+ working on this repo. Report vulnerabilities privately as described in [SECURITY.md](SECURITY.md).
118
+ How AI was used to build it: [AI-DECLARATION.md](AI-DECLARATION.md).
119
+ Releases: [RELEASING.md](RELEASING.md).
120
+
121
+ ## License
122
+
123
+ MIT, see [LICENSE](LICENSE).
124
+
125
+ The fonts bundled in `server/fonts/` for the stack picture are [Inter](https://github.com/rsms/inter) and
126
+ [JetBrains Mono](https://github.com/JetBrains/JetBrainsMono), both under the SIL Open Font License 1.1
127
+ (`server/fonts/*-OFL.txt`). npm dependencies keep their own licenses.
@@ -0,0 +1,224 @@
1
+ // A tiny deterministic git history builder for the demo test bed. Commits are built in memory (each one a full
2
+ // snapshot of the tree) and written with one `git fast-import` per repo, with fixed authors and dates and git's
3
+ // config isolated from the machine's, so every run on every machine produces the same shas.
4
+ import { execFile, execFileSync } from 'node:child_process';
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ /** The environment every git command here runs with: nothing from the machine's git config, no signing, no hooks. */
8
+ export function gitEnv() {
9
+ const env = {};
10
+ for (const [k, v] of Object.entries(process.env))
11
+ if (!k.startsWith('GIT_'))
12
+ env[k] = v;
13
+ return { ...env, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_NOSYSTEM: '1', GIT_TERMINAL_PROMPT: '0', LC_ALL: 'C', TZ: 'UTC' };
14
+ }
15
+ export function git(cwd, args, input) {
16
+ return execFileSync('git', args, { cwd, env: gitEnv(), input, encoding: 'utf8', maxBuffer: 64 << 20, stdio: ['pipe', 'pipe', 'pipe'] });
17
+ }
18
+ /** `git` without blocking: the app builds the example projects with this while it keeps serving. */
19
+ export function gitAsync(cwd, args, input) {
20
+ return new Promise((resolve, reject) => {
21
+ const child = execFile('git', args, { cwd, env: gitEnv(), encoding: 'utf8', maxBuffer: 64 << 20 }, (err, stdout, stderr) => err ? reject(new Error(`git ${args[0]} failed in ${cwd}: ${stderr || err.message}`)) : resolve(stdout));
22
+ // commands that don't read stdin may exit before it's written (EPIPE): their exit status is what counts
23
+ child.stdin?.on('error', () => { });
24
+ child.stdin?.end(input ?? '');
25
+ });
26
+ }
27
+ /** Replace exactly one occurrence of `from` (fails loudly if it isn't there, so content edits can't silently drift). */
28
+ export const replace = (from, to) => (old) => {
29
+ const i = old.indexOf(from);
30
+ if (i < 0)
31
+ throw new Error(`replace: text not found:\n${from}`);
32
+ if (old.indexOf(from, i + 1) >= 0)
33
+ throw new Error(`replace: text found more than once:\n${from}`);
34
+ return old.slice(0, i) + to + old.slice(i + from.length);
35
+ };
36
+ /** Several `replace`s in order. */
37
+ export const edits = (...pairs) => (old) => pairs.reduce((s, [a, b]) => replace(a, b)(s), old);
38
+ /** Insert `text` right after the (unique) `anchor`. */
39
+ export const after = (anchor, text) => replace(anchor, anchor + text);
40
+ /** Strip the common indentation of a template literal and its leading newline, keep one trailing newline. */
41
+ export function code(strings, ...vals) {
42
+ let s = strings.reduce((a, str, i) => a + str + (i < vals.length ? String(vals[i]) : ''), '');
43
+ s = s.replace(/^\n/, '').replace(/[ \t]+$/, '');
44
+ const lines = s.split('\n');
45
+ const indent = Math.min(...lines.filter((l) => l.trim()).map((l) => /^ */.exec(l)[0].length));
46
+ return lines.map((l) => l.slice(indent)).join('\n').replace(/\n*$/, '\n');
47
+ }
48
+ /** Go files and Makefiles are indented with tabs on disk; the content scripts write them with 4 spaces. */
49
+ export function onDisk(file, content) {
50
+ if (!/\.go$|(^|\/)Makefile$/.test(file))
51
+ return content;
52
+ return content.replace(/^((?: {4})+)/gm, (m) => '\t'.repeat(m.length / 4));
53
+ }
54
+ const unix = (iso) => Math.floor(new Date(iso).getTime() / 1000);
55
+ export class RepoBuilder {
56
+ name;
57
+ remoteUrl;
58
+ nextMark = 1;
59
+ commits = [];
60
+ byKey = new Map();
61
+ refs = new Map();
62
+ constructor(name, remoteUrl) {
63
+ this.name = name;
64
+ this.remoteUrl = remoteUrl;
65
+ }
66
+ /** A commit on `parent` (null: a root commit). `key` defaults to the subject. */
67
+ commit(parent, message, changes, o) {
68
+ const files = new Map(parent?.files ?? []);
69
+ if (o.merge)
70
+ for (const [p, c] of o.merge.files)
71
+ if (!files.has(p))
72
+ files.set(p, c);
73
+ for (const [p, ch] of Object.entries(changes)) {
74
+ if (ch === null) {
75
+ if (!files.delete(p))
76
+ throw new Error(`${this.name}: delete of missing file ${p}`);
77
+ }
78
+ else if (typeof ch === 'function') {
79
+ const old = files.get(p);
80
+ if (old === undefined)
81
+ throw new Error(`${this.name}: edit of missing file ${p} in "${message.split('\n')[0]}"`);
82
+ files.set(p, ch(old));
83
+ }
84
+ else
85
+ files.set(p, ch);
86
+ }
87
+ const subject = message.split('\n')[0];
88
+ const key = o.key ?? subject;
89
+ if (this.byKey.has(key))
90
+ throw new Error(`${this.name}: duplicate commit key ${key}`);
91
+ const c = {
92
+ mark: this.nextMark++,
93
+ key,
94
+ message: message.replace(/\n*$/, '\n'),
95
+ subject,
96
+ author: o.author,
97
+ authorAt: o.at,
98
+ committer: o.committer ?? o.author,
99
+ committedAt: o.committedAt ?? o.at,
100
+ parent,
101
+ merge: o.merge ?? null,
102
+ changes,
103
+ files,
104
+ };
105
+ this.commits.push(c);
106
+ this.byKey.set(key, c);
107
+ return c;
108
+ }
109
+ /**
110
+ * Replay commits onto `onto`, as `git rebase` would: same message, author and author date, a new committer
111
+ * date. Each copy takes over its original's key; the original stays reachable as `<key>@<tag>`.
112
+ */
113
+ replay(list, onto, o) {
114
+ let base = onto;
115
+ return list.map((c) => {
116
+ const key = c.key;
117
+ this.rekey(c, `${key}@${o.tag}`);
118
+ // `override`: the copy's changes, when the rebase had to adapt the commit (a fixup squashed into it)
119
+ base = this.commit(base, c.message, o.override?.[key] ?? c.changes, { author: c.author, at: c.authorAt, committer: o.committer ?? c.committer, committedAt: o.at, key });
120
+ return base;
121
+ });
122
+ }
123
+ rekey(c, key) {
124
+ if (this.byKey.has(key))
125
+ throw new Error(`${this.name}: duplicate commit key ${key}`);
126
+ this.byKey.delete(c.key);
127
+ c.key = key;
128
+ this.byKey.set(key, c);
129
+ }
130
+ get(key) {
131
+ const c = this.byKey.get(key);
132
+ if (!c)
133
+ throw new Error(`${this.name}: no commit ${key}`);
134
+ return c;
135
+ }
136
+ sha(key) {
137
+ const s = this.get(key).sha;
138
+ if (!s)
139
+ throw new Error(`${this.name}: not built yet`);
140
+ return s;
141
+ }
142
+ /** A file at a commit, as it is on disk. */
143
+ file(key, file) {
144
+ const s = this.get(key).files.get(file);
145
+ if (s === undefined)
146
+ throw new Error(`${this.name}: no ${file} at ${key}`);
147
+ return onDisk(file, s);
148
+ }
149
+ ref(name, c) {
150
+ this.refs.set(name.startsWith('refs/') ? name : `refs/heads/${name}`, c);
151
+ }
152
+ /** `git init`, one fast-import, then check out `checkout` (the work tree a developer would have). */
153
+ build(dir, checkout) {
154
+ const { steps, marks } = this.plan(dir, checkout);
155
+ for (const [args, input] of steps)
156
+ git(dir, args, input);
157
+ this.readMarks(marks);
158
+ }
159
+ /** The same as `build`, without blocking the event loop. */
160
+ async buildAsync(dir, checkout) {
161
+ const { steps, marks } = this.plan(dir, checkout);
162
+ for (const [args, input] of steps)
163
+ await gitAsync(dir, args, input);
164
+ this.readMarks(marks);
165
+ }
166
+ /** Create `dir` (it mustn't exist: never builds over anything) and list the git commands that fill it. */
167
+ plan(dir, checkout) {
168
+ if (fs.existsSync(dir))
169
+ throw new Error(`${dir} already exists`);
170
+ fs.mkdirSync(dir, { recursive: true });
171
+ const chunks = [];
172
+ const w = (s) => chunks.push(Buffer.from(s, 'utf8'));
173
+ const data = (s) => {
174
+ w(`data ${Buffer.byteLength(s, 'utf8')}\n`);
175
+ w(s);
176
+ w('\n');
177
+ };
178
+ for (const c of this.commits) {
179
+ // every commit goes through one scratch ref, reset first so its parent is exactly what it says (none for a root)
180
+ w(`reset refs/lr-demo/build\ncommit refs/lr-demo/build\nmark :${c.mark}\n`);
181
+ w(`author ${c.author.name} <${c.author.email}> ${unix(c.authorAt)} +0000\n`);
182
+ w(`committer ${c.committer.name} <${c.committer.email}> ${unix(c.committedAt)} +0000\n`);
183
+ data(c.message);
184
+ if (c.parent)
185
+ w(`from :${c.parent.mark}\n`);
186
+ if (c.merge)
187
+ w(`merge :${c.merge.mark}\n`);
188
+ w('deleteall\n');
189
+ for (const p of [...c.files.keys()].sort()) {
190
+ w(`M 100644 inline ${p}\n`);
191
+ data(onDisk(p, c.files.get(p)));
192
+ }
193
+ w('\n');
194
+ }
195
+ for (const [name, c] of [...this.refs].sort(([a], [b]) => a.localeCompare(b)))
196
+ w(`reset ${name}\nfrom :${c.mark}\n\n`);
197
+ const marks = path.join(dir, '.git', 'lr-demo-marks');
198
+ return {
199
+ marks,
200
+ steps: [
201
+ [['init', '-q', '-b', 'main']],
202
+ [['config', 'core.hooksPath', '/dev/null']],
203
+ [['config', 'commit.gpgSign', 'false']],
204
+ [['config', 'gc.auto', '0']], // keep rewritten-away commits (anchors and moved branches point at them)
205
+ [['config', 'rebase.updateRefs', 'true']], // AGENTS.md "Branch tagging"
206
+ [['remote', 'add', 'origin', this.remoteUrl]],
207
+ [['fast-import', '--quiet', '--date-format=raw', `--export-marks=${marks}`], Buffer.concat(chunks).toString('utf8')],
208
+ [['update-ref', '-d', 'refs/lr-demo/build']],
209
+ [['checkout', '-q', '-f', checkout]],
210
+ ],
211
+ };
212
+ }
213
+ /** Every commit's sha, from fast-import's marks. The file stays in .git: nothing reads it, and this code never
214
+ * deletes a file (the app runs it to create the example projects, so test/fsguard.test.ts scans it like the server). */
215
+ readMarks(marks) {
216
+ const markShas = new Map();
217
+ for (const line of fs.readFileSync(marks, 'utf8').split('\n').filter(Boolean)) {
218
+ const [m, sha] = line.split(' ');
219
+ markShas.set(Number(m.slice(1)), sha);
220
+ }
221
+ for (const c of this.commits)
222
+ c.sha = markShas.get(c.mark);
223
+ }
224
+ }