diffing 0.6.2 → 0.7.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 (186) hide show
  1. package/README.md +134 -100
  2. package/dist/apply-suggestion-BAuyCYk6.mjs +54 -0
  3. package/dist/cli-agent-BlMtx1Tx.mjs +736 -0
  4. package/dist/cli-agent-C4Fb90GL.mjs +728 -0
  5. package/dist/cli-agent-ChALuNbw.mjs +841 -0
  6. package/dist/cli-agent-D71pq2ep.mjs +841 -0
  7. package/dist/cli-agent-qBT8clNM.mjs +841 -0
  8. package/dist/cli-gh-6qiblgj7.mjs +150 -0
  9. package/dist/cli-gh-D5OCnjNe.mjs +150 -0
  10. package/dist/cli-gh-KjPnyST6.mjs +150 -0
  11. package/dist/cli.mjs +38 -66
  12. package/dist/client/assets/{angular-html-Dg8frIKA.js → angular-html-Dh86jUvr.js} +1 -1
  13. package/dist/client/assets/{angular-ts-BgXWv16g.js → angular-ts-Bh4wRJtT.js} +1 -1
  14. package/dist/client/assets/{apl-uFl-_xI6.js → apl-BSXBHduT.js} +1 -1
  15. package/dist/client/assets/{arc-2DyFlUoQ.js → arc-CRQlhBrV.js} +1 -1
  16. package/dist/client/assets/{architectureDiagram-3BPJPVTR-Cpo5XnQh.js → architectureDiagram-3BPJPVTR-BXq3UvEv.js} +1 -1
  17. package/dist/client/assets/{astro-CsSgohj_.js → astro-Bh00klUw.js} +1 -1
  18. package/dist/client/assets/{blade-tmMbmea0.js → blade-CisA3zhm.js} +1 -1
  19. package/dist/client/assets/{blockDiagram-GPEHLZMM-C2j4VeON.js → blockDiagram-GPEHLZMM-Bw-PTAJr.js} +1 -1
  20. package/dist/client/assets/{c-CYFchk-Y.js → c-D-pHVWrf.js} +1 -1
  21. package/dist/client/assets/{c4Diagram-AAUBKEIU-B9Iu60rg.js → c4Diagram-AAUBKEIU-C0XyiqC_.js} +1 -1
  22. package/dist/client/assets/channel-CDxgOu-3.js +1 -0
  23. package/dist/client/assets/{chunk-2J33WTMH-CYF5wgmI.js → chunk-2J33WTMH-DekjZXNj.js} +1 -1
  24. package/dist/client/assets/{chunk-3OPIFGDE-BsCKEfSk.js → chunk-3OPIFGDE-BtROpcL8.js} +1 -1
  25. package/dist/client/assets/{chunk-4BX2VUAB-CKaohpWW.js → chunk-4BX2VUAB-C92XLTlx.js} +1 -1
  26. package/dist/client/assets/{chunk-55IACEB6-CPg0c8fY.js → chunk-55IACEB6-CceUgh8N.js} +1 -1
  27. package/dist/client/assets/{chunk-5ZQYHXKU-qstslfP1.js → chunk-5ZQYHXKU-DSbYPAe7.js} +1 -1
  28. package/dist/client/assets/{chunk-727SXJPM-C1WRwOi2.js → chunk-727SXJPM-DVzUJa-9.js} +1 -1
  29. package/dist/client/assets/{chunk-AQP2D5EJ-_jbfFh_H.js → chunk-AQP2D5EJ-6n3Z_IFa.js} +1 -1
  30. package/dist/client/assets/{chunk-BSJP7CBP-CjQkUxwj.js → chunk-BSJP7CBP-BgVLnkxr.js} +1 -1
  31. package/dist/client/assets/{chunk-CSCIHK7Q-D4khYgW5.js → chunk-CSCIHK7Q-CkGMXntp.js} +1 -1
  32. package/dist/client/assets/{chunk-FMBD7UC4-B1r9As4X.js → chunk-FMBD7UC4-BM8lrLBo.js} +1 -1
  33. package/dist/client/assets/{chunk-KSCS5N6A-XehlazPT.js → chunk-KSCS5N6A-Bm2pjMQg.js} +1 -1
  34. package/dist/client/assets/{chunk-L5ZTLDWV-5KMCRmgT.js → chunk-L5ZTLDWV-VL17ikXM.js} +1 -1
  35. package/dist/client/assets/{chunk-LZXEDZCA--x7aE-36.js → chunk-LZXEDZCA-B629g67M.js} +2 -2
  36. package/dist/client/assets/{chunk-ND2GUHAM-BcUeNQF6.js → chunk-ND2GUHAM-CjZGwhKA.js} +1 -1
  37. package/dist/client/assets/{chunk-NZK2D7GU-HdA4zA81.js → chunk-NZK2D7GU-DGsLjESY.js} +1 -1
  38. package/dist/client/assets/{chunk-O5CBEL6O-BbkpkgBW.js → chunk-O5CBEL6O-EyUrGY3l.js} +1 -1
  39. package/dist/client/assets/chunk-QZHKN3VN-DJCeMpP8.js +1 -0
  40. package/dist/client/assets/chunk-WU5MYG2G-BbuOkMaH.js +1 -0
  41. package/dist/client/assets/{chunk-XPW4576I-BrjJtb92.js → chunk-XPW4576I-C2TeAQmQ.js} +1 -1
  42. package/dist/client/assets/classDiagram-4FO5ZUOK-D5oJbiED.js +1 -0
  43. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-5MJt5ZxQ.js +1 -0
  44. package/dist/client/assets/{cobol-CbEdzJpe.js → cobol-DsRZMP16.js} +1 -1
  45. package/dist/client/assets/{coffee-BYBPPvKf.js → coffee-DP0-MMsE.js} +1 -1
  46. package/dist/client/assets/{cose-bilkent-S5V4N54A-C2UlmvWG.js → cose-bilkent-S5V4N54A-DMmK0b0W.js} +1 -1
  47. package/dist/client/assets/{cpp-Cy1Wnp8d.js → cpp-B6XYbbze.js} +1 -1
  48. package/dist/client/assets/{crystal-Bg1sUJya.js → crystal-dBLPFLMp.js} +1 -1
  49. package/dist/client/assets/{css-C6iyzRBi.js → css-tgwuSZL0.js} +1 -1
  50. package/dist/client/assets/{dagre-BM42HDAG-CLP5IYO3.js → dagre-BM42HDAG-CgNkf0TR.js} +1 -1
  51. package/dist/client/assets/{diagram-2AECGRRQ-Dg8GXMUD.js → diagram-2AECGRRQ-ocAqn0LV.js} +1 -1
  52. package/dist/client/assets/{diagram-5GNKFQAL-D5eskvC7.js → diagram-5GNKFQAL-CB1Yo4Gc.js} +1 -1
  53. package/dist/client/assets/{diagram-KO2AKTUF-SO-9Apcj.js → diagram-KO2AKTUF-B2vpMBRT.js} +1 -1
  54. package/dist/client/assets/{diagram-LMA3HP47-Dz15-yRy.js → diagram-LMA3HP47-DuxZOaQS.js} +1 -1
  55. package/dist/client/assets/{diagram-OG6HWLK6-BuQK0W1m.js → diagram-OG6HWLK6-CkShDmcv.js} +1 -1
  56. package/dist/client/assets/{dist-CZ555WMx.js → dist-BgZIvk5w.js} +1 -1
  57. package/dist/client/assets/{edge-CmOR4HCT.js → edge-Bokubn8E.js} +1 -1
  58. package/dist/client/assets/{elixir-CK9qhOZG.js → elixir-BEzcf3Xg.js} +1 -1
  59. package/dist/client/assets/{elm-DZWlTp8-.js → elm-vJyLTAER.js} +1 -1
  60. package/dist/client/assets/{erDiagram-TEJ5UH35-D7vADGZy.js → erDiagram-TEJ5UH35-BFV1NQAH.js} +1 -1
  61. package/dist/client/assets/{erb-CTTGdXJ0.js → erb-DDkAmjBS.js} +1 -1
  62. package/dist/client/assets/{flowDiagram-I6XJVG4X-BwcgAtP3.js → flowDiagram-I6XJVG4X-DU8xbFdO.js} +1 -1
  63. package/dist/client/assets/{ganttDiagram-6RSMTGT7-kInginE9.js → ganttDiagram-6RSMTGT7-DCb7Qh0c.js} +1 -1
  64. package/dist/client/assets/{git-rebase-B_sSbVaY.js → git-rebase-DWcifAdK.js} +1 -1
  65. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-C4pDDMLO.js → gitGraphDiagram-PVQCEYII-C_Tw-piE.js} +1 -1
  66. package/dist/client/assets/{glimmer-js-BaLXlo90.js → glimmer-js--HDru5ys.js} +1 -1
  67. package/dist/client/assets/{glimmer-ts-ugNWY62Y.js → glimmer-ts-DAdXisHh.js} +1 -1
  68. package/dist/client/assets/{glsl-CDofiyvf.js → glsl-CmIRlJht.js} +1 -1
  69. package/dist/client/assets/{graphql-lRTsx6gb.js → graphql-tx4-7I3U.js} +1 -1
  70. package/dist/client/assets/{hack-DG646xxf.js → hack-yA0XjhF7.js} +1 -1
  71. package/dist/client/assets/{haml-CjNyEqTY.js → haml-BR5MUE1b.js} +1 -1
  72. package/dist/client/assets/{handlebars-DmTlsycc.js → handlebars-BPOF7iOf.js} +1 -1
  73. package/dist/client/assets/{html-CYKa0iGt.js → html-BbteRk96.js} +1 -1
  74. package/dist/client/assets/{html-derivative-CJrmldEy.js → html-derivative-59NNrcMm.js} +1 -1
  75. package/dist/client/assets/{http-Dv_sB6KO.js → http-D3odGQI4.js} +1 -1
  76. package/dist/client/assets/{hurl-aIa6gC3s.js → hurl-BLTj8fPj.js} +1 -1
  77. package/dist/client/assets/index-BZcKS8Sb.css +1 -0
  78. package/dist/client/assets/{index-DLwFuHoH.js → index-DHXpO7eS.js} +136 -98
  79. package/dist/client/assets/{infoDiagram-5YYISTIA-DE1yaqeG.js → infoDiagram-5YYISTIA-CI6hJyRl.js} +1 -1
  80. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-D61KC690.js → ishikawaDiagram-YF4QCWOH-gMS1P1LQ.js} +1 -1
  81. package/dist/client/assets/{java--GCJC5Ht.js → java-CsViuzs_.js} +1 -1
  82. package/dist/client/assets/{javascript-CLU-vrsL.js → javascript-DQgsl2Dh.js} +1 -1
  83. package/dist/client/assets/{jinja-DpKxO4hN.js → jinja-DQNp2NUA.js} +1 -1
  84. package/dist/client/assets/{jison-DAzEKgwk.js → jison-CDDOZQdc.js} +1 -1
  85. package/dist/client/assets/{journeyDiagram-JHISSGLW-Bt37oidm.js → journeyDiagram-JHISSGLW-BTQ_Ws_h.js} +1 -1
  86. package/dist/client/assets/{json-CoxC1kMn.js → json-DP4TqCuE.js} +1 -1
  87. package/dist/client/assets/{jsx-Bjv8cqXv.js → jsx-Df5hn0Tf.js} +1 -1
  88. package/dist/client/assets/{julia-2HI5Piu6.js → julia--u33yGLi.js} +1 -1
  89. package/dist/client/assets/{just-AUrPjdB4.js → just-BCpMmSOD.js} +1 -1
  90. package/dist/client/assets/{kanban-definition-UN3LZRKU-D46QdRYJ.js → kanban-definition-UN3LZRKU-CvqGbLR_.js} +1 -1
  91. package/dist/client/assets/{latex-DjzDlfkY.js → latex-D8D4jZGw.js} +1 -1
  92. package/dist/client/assets/{line-D_rKDTVX.js → line-DBgaS_RW.js} +1 -1
  93. package/dist/client/assets/{linear-DCm3ZWOL.js → linear-RQVzKNVU.js} +1 -1
  94. package/dist/client/assets/{liquid-DZ8JEV93.js → liquid-CkH85f12.js} +1 -1
  95. package/dist/client/assets/{lua-iKYCK6sd.js → lua-iKoV7WE3.js} +1 -1
  96. package/dist/client/assets/{marko-CrhrBZpj.js → marko-N8ScQSqT.js} +1 -1
  97. package/dist/client/assets/{mdc-M6YkRqT_.js → mdc-BvHTOxsp.js} +1 -1
  98. package/dist/client/assets/{mermaid-parser.core-DOlZkAUl.js → mermaid-parser.core-CwDRwMR7.js} +1 -1
  99. package/dist/client/assets/{mermaid.core-B5kraaYr.js → mermaid.core-D2LDJgXO.js} +3 -3
  100. package/dist/client/assets/{mindmap-definition-RKZ34NQL-CG-S_0Tm.js → mindmap-definition-RKZ34NQL-FRnPh642.js} +1 -1
  101. package/dist/client/assets/{nginx-DGSv66H3.js → nginx-DGgvASuI.js} +1 -1
  102. package/dist/client/assets/{nim-wTb9iFPK.js → nim-D-v9hzvR.js} +1 -1
  103. package/dist/client/assets/{perl-BrLOtih-.js → perl-CG8leJ8K.js} +1 -1
  104. package/dist/client/assets/{php-BpG59zED.js → php-DvkeSP10.js} +1 -1
  105. package/dist/client/assets/{pieDiagram-4H26LBE5-BSsvT950.js → pieDiagram-4H26LBE5-CepOg38Y.js} +1 -1
  106. package/dist/client/assets/{pug-L7QegceF.js → pug-B4T7-xaL.js} +1 -1
  107. package/dist/client/assets/{qml-Bhe1hZ7B.js → qml-BE6D5Bxn.js} +1 -1
  108. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Bxs1ZIId.js → quadrantDiagram-W4KKPZXB-DOFAsP60.js} +1 -1
  109. package/dist/client/assets/{r-D1TBw_pw.js → r-BKZg9Mcq.js} +1 -1
  110. package/dist/client/assets/{razor-CQm1uFKr.js → razor-D-02oaxL.js} +1 -1
  111. package/dist/client/assets/{regexp--jRne08M.js → regexp-BDFvAR1k.js} +1 -1
  112. package/dist/client/assets/{requirementDiagram-4Y6WPE33-BvrwIHRn.js → requirementDiagram-4Y6WPE33-BBVNH-2W.js} +1 -1
  113. package/dist/client/assets/review-export-D5iIl46_.js +7 -0
  114. package/dist/client/assets/{rst-DojkdR69.js → rst-BcT26OSj.js} +1 -1
  115. package/dist/client/assets/{ruby-CihGsdWE.js → ruby-Cq_YQvFT.js} +1 -1
  116. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-ChVx7-uL.js → sankeyDiagram-5OEKKPKP-BQfCTbBX.js} +1 -1
  117. package/dist/client/assets/{sas-De9KOrSF.js → sas-DN9oTxKY.js} +1 -1
  118. package/dist/client/assets/{scss-s1VsrZAM.js → scss-BK_sp3z2.js} +1 -1
  119. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DfGXu7Db.js → sequenceDiagram-3UESZ5HK-CKAW2eyx.js} +1 -1
  120. package/dist/client/assets/{shellscript-BQarhH5B.js → shellscript-CvNpOu0J.js} +1 -1
  121. package/dist/client/assets/{shellsession-DudKTKQ_.js → shellsession-8-yj2cJj.js} +1 -1
  122. package/dist/client/assets/{soy-CQggW1_P.js → soy-C6Q9atPF.js} +1 -1
  123. package/dist/client/assets/{sql-CyxjmxEX.js → sql-CBL3BuHb.js} +1 -1
  124. package/dist/client/assets/{src-Be3tFbq6.js → src-D-VUWIK5.js} +1 -1
  125. package/dist/client/assets/{stata-Baq2oukf.js → stata-B2Q7Vqzu.js} +1 -1
  126. package/dist/client/assets/{stateDiagram-AJRCARHV-DUsSkWi-.js → stateDiagram-AJRCARHV-BGJiv0qO.js} +1 -1
  127. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CdUgMghp.js +1 -0
  128. package/dist/client/assets/{surrealql-DY_lPQU6.js → surrealql-C-A2N8vk.js} +1 -1
  129. package/dist/client/assets/{svelte-oBaORrzD.js → svelte-VAut_-uy.js} +1 -1
  130. package/dist/client/assets/{templ-WpkyVGPg.js → templ-mUetM--K.js} +1 -1
  131. package/dist/client/assets/{tex-t_gMyObp.js → tex-BT53mWGp.js} +1 -1
  132. package/dist/client/assets/{timeline-definition-PNZ67QCA-DO30eyIk.js → timeline-definition-PNZ67QCA-lVwMt1rk.js} +1 -1
  133. package/dist/client/assets/{ts-tags-Dn6NjJLA.js → ts-tags-VkIOkgLN.js} +1 -1
  134. package/dist/client/assets/{tsx-DDWp_p5y.js → tsx-D_aaGHTf.js} +1 -1
  135. package/dist/client/assets/{twig-FBY22xUa.js → twig-CufwqKkk.js} +1 -1
  136. package/dist/client/assets/{typescript-BHZBosYe.js → typescript-osFD2CEV.js} +1 -1
  137. package/dist/client/assets/{vennDiagram-CIIHVFJN-DbE8UKE1.js → vennDiagram-CIIHVFJN-_K9NtpA5.js} +1 -1
  138. package/dist/client/assets/{vue-CrOmWBlh.js → vue-Bub1rXmL.js} +1 -1
  139. package/dist/client/assets/{vue-html-jBuGpXgg.js → vue-html-CN3x2pYg.js} +1 -1
  140. package/dist/client/assets/{vue-vine-DEzqOkvG.js → vue-vine-C0csYkGm.js} +1 -1
  141. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-BqhSY5Qs.js → wardleyDiagram-YWT4CUSO-B2jTMfra.js} +1 -1
  142. package/dist/client/assets/{xml-DkWImCZV.js → xml-my6OjeGb.js} +1 -1
  143. package/dist/client/assets/{xsl-BotuNSYU.js → xsl-Bx0YVn26.js} +1 -1
  144. package/dist/client/assets/{xychartDiagram-2RQKCTM6-DA2pXGD9.js → xychartDiagram-2RQKCTM6-DfZ4YYXU.js} +1 -1
  145. package/dist/client/assets/{yaml-8lMTQUH5.js → yaml-DanwOypk.js} +1 -1
  146. package/dist/client/index.html +2 -2
  147. package/dist/comment-format-B5AcGcR-.mjs +118 -0
  148. package/dist/comment-format-CKTt0fzi.mjs +113 -0
  149. package/dist/comment-format-DG_PAehC.mjs +117 -0
  150. package/dist/completions-ZSKYvW06.mjs +126 -0
  151. package/dist/diff-fingerprint-CWa-soYD.mjs +78 -0
  152. package/dist/doctor-q6oA64hm.mjs +195 -0
  153. package/dist/find-tui-binary-CRpbYXLC.mjs +52 -0
  154. package/dist/git-Czo96ymM.mjs +789 -0
  155. package/dist/git-DaP_1rVy.mjs +2 -0
  156. package/dist/github-C9GLNak7.mjs +2 -0
  157. package/dist/github-HfIlIS-J.mjs +525 -0
  158. package/dist/mcp-7eXATtez.mjs +1334 -0
  159. package/dist/mcp-BBk8AIO4.mjs +1334 -0
  160. package/dist/mcp-BJxrW9dS.mjs +1334 -0
  161. package/dist/mcp-BhT5GgYF.mjs +1334 -0
  162. package/dist/mcp-BsGdNA88.mjs +1334 -0
  163. package/dist/mcp-Ca9QAlJ0.mjs +1334 -0
  164. package/dist/mcp-eor39qmD.mjs +1352 -0
  165. package/dist/plan-format-BQyx--mp.mjs +144 -0
  166. package/dist/plan-format-Bi46QS0m.mjs +132 -0
  167. package/dist/review-export-BoV0884M.mjs +66 -0
  168. package/dist/review-export-CKTI5Od_.mjs +66 -0
  169. package/dist/review-export-DjxKBeIZ.mjs +66 -0
  170. package/dist/server-AisDKkpW.mjs +3227 -0
  171. package/dist/server-BUsRhL1B.mjs +3283 -0
  172. package/dist/server-Bc2m9R5-.mjs +3263 -0
  173. package/dist/server-C105rwQw.mjs +3283 -0
  174. package/dist/server-D0Ni8PKP.mjs +3252 -0
  175. package/dist/server-TOElOuNP.mjs +3258 -0
  176. package/dist/server-lock-uqSdDYeR.mjs +153 -0
  177. package/dist/settings-BVnzGFL7.mjs +478 -0
  178. package/dist/update-check-fe0tR8tx.mjs +129 -0
  179. package/package.json +1 -1
  180. package/dist/client/assets/channel-Br3gV-0C.js +0 -1
  181. package/dist/client/assets/chunk-QZHKN3VN-yNyHmFLj.js +0 -1
  182. package/dist/client/assets/chunk-WU5MYG2G-DOSgjhZV.js +0 -1
  183. package/dist/client/assets/classDiagram-4FO5ZUOK-BV8x1oiD.js +0 -1
  184. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-AibFxRy2.js +0 -1
  185. package/dist/client/assets/index-CkjX2qPW.css +0 -1
  186. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-DczGQieS.js +0 -1
@@ -0,0 +1,2 @@
1
+ import { d as getProjectStorageDir } from "./git-Czo96ymM.mjs";
2
+ export { getProjectStorageDir };
@@ -0,0 +1,2 @@
1
+ import { n as buildReviewPayload, p as replyToPrComment, s as fetchPrChecks } from "./github-HfIlIS-J.mjs";
2
+ export { buildReviewPayload, fetchPrChecks, replyToPrComment };
@@ -0,0 +1,525 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ //#region src/lib/github.ts
4
+ const execFileAsync = promisify(execFile);
5
+ /**
6
+ * Probe the `gh` CLI: is it on `$PATH`, and is the user authenticated?
7
+ * Never throws — returns a permissive `available: false` on any failure.
8
+ */
9
+ async function detectGhCli() {
10
+ try {
11
+ const { stdout } = await execFileAsync("gh", ["--version"], { encoding: "utf-8" });
12
+ const version = stdout.split("\n")[0]?.trim() || void 0;
13
+ try {
14
+ const result = await execFileAsync("gh", ["auth", "status"], { encoding: "utf-8" });
15
+ return {
16
+ available: true,
17
+ authenticated: true,
18
+ version,
19
+ user: parseGhAuthStatusUser(`${result.stdout ?? ""}\n${result.stderr ?? ""}`)
20
+ };
21
+ } catch {
22
+ return {
23
+ available: true,
24
+ authenticated: false,
25
+ version
26
+ };
27
+ }
28
+ } catch {
29
+ return {
30
+ available: false,
31
+ authenticated: false
32
+ };
33
+ }
34
+ }
35
+ /**
36
+ * Extract the GitHub username from `gh auth status` output. Exported for
37
+ * unit tests — see `__tests__/github-auth.test.ts`.
38
+ *
39
+ * Real-world samples:
40
+ * `✓ Logged in to github.com account octocat (keyring)` (gh 2.40+)
41
+ * `Logged in to github.com as octocat (oauth_token)` (older gh)
42
+ * ` ✓ Logged in to github.com as octocat (~/.config/gh/hosts.yml)`
43
+ */
44
+ function parseGhAuthStatusUser(output) {
45
+ const account = /Logged in to [^\s]+ account\s+([^\s(]+)/.exec(output);
46
+ if (account) return account[1];
47
+ return /Logged in to [^\s]+ as\s+([^\s(]+)/.exec(output)?.[1];
48
+ }
49
+ /** Read the most-preferred GitHub token from the env. */
50
+ function readGithubToken() {
51
+ const env = process.env;
52
+ return env.GH_TOKEN || env.GITHUB_TOKEN || env.GITHUB_API_TOKEN || null;
53
+ }
54
+ function parsePrRef(input, cwdRepo) {
55
+ const trimmed = input.trim();
56
+ const urlMatch = /^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/i.exec(trimmed);
57
+ if (urlMatch) return {
58
+ owner: urlMatch[1],
59
+ repo: urlMatch[2],
60
+ pullNumber: Number(urlMatch[3]),
61
+ ref: trimmed
62
+ };
63
+ const shorthand = /^([^/\s]+)\/([^#\s]+)#(\d+)$/.exec(trimmed);
64
+ if (shorthand) return {
65
+ owner: shorthand[1],
66
+ repo: shorthand[2],
67
+ pullNumber: Number(shorthand[3]),
68
+ ref: trimmed
69
+ };
70
+ const bare = /^#?(\d+)$/.exec(trimmed);
71
+ if (bare && cwdRepo) return {
72
+ owner: cwdRepo.owner,
73
+ repo: cwdRepo.repo,
74
+ pullNumber: Number(bare[1]),
75
+ ref: trimmed
76
+ };
77
+ if (bare) throw new Error(`Cannot resolve bare PR number "${trimmed}" — run from inside the target repo, or pass a full URL or \`owner/repo#1234\`.`);
78
+ throw new Error(`Unrecognised PR ref: ${trimmed}`);
79
+ }
80
+ /**
81
+ * Best-effort: derive `{ owner, repo }` for the current working directory
82
+ * from `git remote get-url origin`. Falls back to throwing.
83
+ */
84
+ async function detectCwdRepo() {
85
+ try {
86
+ const { stdout } = await execFileAsync("git", [
87
+ "remote",
88
+ "get-url",
89
+ "origin"
90
+ ], { encoding: "utf-8" });
91
+ const url = stdout.trim();
92
+ const m = /github\.com[:/]([^/\s]+)\/([^/\s]+?)(?:\.git)?\s*$/.exec(url);
93
+ if (m) return {
94
+ owner: m[1],
95
+ repo: m[2]
96
+ };
97
+ } catch {}
98
+ return null;
99
+ }
100
+ /**
101
+ * Fetch PR metadata + diff + existing review comments using `gh`.
102
+ * Throws with a user-readable message on failure.
103
+ */
104
+ async function fetchPrMetadataViaGh(resolved) {
105
+ const args = ["-R", `${resolved.owner}/${resolved.repo}`];
106
+ let metaJson;
107
+ try {
108
+ const { stdout } = await execFileAsync("gh", [
109
+ "pr",
110
+ "view",
111
+ String(resolved.pullNumber),
112
+ ...args,
113
+ "--json",
114
+ "number,title,url,author,baseRefOid,headRefOid,additions,deletions,changedFiles,headRepositoryOwner,headRepository"
115
+ ], {
116
+ encoding: "utf-8",
117
+ maxBuffer: 20 * 1024 * 1024
118
+ });
119
+ metaJson = JSON.parse(stdout);
120
+ } catch (err) {
121
+ const stderr = err?.stderr || err?.message || "unknown error";
122
+ throw new Error(`gh pr view failed: ${stderr.trim()}`);
123
+ }
124
+ let diff = "";
125
+ try {
126
+ const { stdout } = await execFileAsync("gh", [
127
+ "pr",
128
+ "diff",
129
+ String(resolved.pullNumber),
130
+ ...args
131
+ ], {
132
+ encoding: "utf-8",
133
+ maxBuffer: 50 * 1024 * 1024
134
+ });
135
+ diff = stdout;
136
+ } catch (err) {
137
+ const stderr = err?.stderr || err?.message || "unknown error";
138
+ throw new Error(`gh pr diff failed: ${stderr.trim()}`);
139
+ }
140
+ const author = metaJson.author ? {
141
+ login: metaJson.author.login,
142
+ avatarUrl: metaJson.author.avatarUrl
143
+ } : null;
144
+ const existingComments = await fetchExistingCommentsViaGh(resolved);
145
+ return {
146
+ number: metaJson.number,
147
+ title: metaJson.title,
148
+ url: metaJson.url,
149
+ author,
150
+ baseSha: metaJson.baseRefOid,
151
+ headSha: metaJson.headRefOid,
152
+ additions: metaJson.additions,
153
+ deletions: metaJson.deletions,
154
+ changedFiles: metaJson.changedFiles,
155
+ owner: resolved.owner,
156
+ repo: resolved.repo,
157
+ diff,
158
+ existingComments
159
+ };
160
+ }
161
+ async function fetchExistingCommentsViaGh(resolved) {
162
+ `${resolved.owner}${resolved.repo}`;
163
+ const endpoint = `repos/${resolved.owner}/${resolved.repo}/pulls/${resolved.pullNumber}/comments`;
164
+ const reviewsEndpoint = `repos/${resolved.owner}/${resolved.repo}/pulls/${resolved.pullNumber}/reviews`;
165
+ let reviews = [];
166
+ try {
167
+ const { stdout } = await execFileAsync("gh", [
168
+ "api",
169
+ reviewsEndpoint,
170
+ "--paginate"
171
+ ], {
172
+ encoding: "utf-8",
173
+ maxBuffer: 20 * 1024 * 1024
174
+ });
175
+ const all = JSON.parse(stdout);
176
+ if (Array.isArray(all)) reviews = all;
177
+ } catch {}
178
+ reviews.sort((a, b) => a.submitted_at < b.submitted_at ? 1 : -1);
179
+ const recentReviews = reviews.slice(0, 50);
180
+ const reviewStateById = /* @__PURE__ */ new Map();
181
+ for (const r of recentReviews) reviewStateById.set(r.id, r.state);
182
+ let commentsRaw = [];
183
+ try {
184
+ const { stdout } = await execFileAsync("gh", [
185
+ "api",
186
+ `${endpoint}?per_page=100`,
187
+ "--paginate"
188
+ ], {
189
+ encoding: "utf-8",
190
+ maxBuffer: 20 * 1024 * 1024
191
+ });
192
+ const parsed = JSON.parse(stdout);
193
+ if (Array.isArray(parsed)) commentsRaw = parsed;
194
+ } catch {
195
+ return [];
196
+ }
197
+ const byId = /* @__PURE__ */ new Map();
198
+ for (const c of commentsRaw) byId.set(c.id, c);
199
+ const replyByParent = /* @__PURE__ */ new Map();
200
+ for (const c of commentsRaw) if (c.in_reply_to_id && byId.has(c.in_reply_to_id)) {
201
+ const list = replyByParent.get(c.in_reply_to_id) ?? [];
202
+ list.push(c);
203
+ replyByParent.set(c.in_reply_to_id, list);
204
+ }
205
+ const tops = [];
206
+ for (const c of commentsRaw) {
207
+ if (c.in_reply_to_id) continue;
208
+ const state = c.pull_request_review_id ? reviewStateById.get(c.pull_request_review_id) ?? null : null;
209
+ tops.push({
210
+ id: c.id,
211
+ author: c.user ? {
212
+ login: c.user.login,
213
+ avatarUrl: c.user.avatar_url
214
+ } : null,
215
+ body: c.body ?? "",
216
+ path: c.path,
217
+ line: typeof c.line === "number" ? c.line : null,
218
+ side: c.side === "LEFT" || c.side === "RIGHT" ? c.side : null,
219
+ createdAt: c.created_at,
220
+ updatedAt: c.updated_at,
221
+ state,
222
+ replies: (replyByParent.get(c.id) ?? []).map((r) => ({
223
+ id: r.id,
224
+ author: r.user ? {
225
+ login: r.user.login,
226
+ avatarUrl: r.user.avatar_url
227
+ } : null,
228
+ body: r.body ?? "",
229
+ createdAt: r.created_at,
230
+ updatedAt: r.updated_at
231
+ })),
232
+ isOutdated: Boolean(c.line == null && c.original_line != null || c.position === null && c.original_position != null)
233
+ });
234
+ }
235
+ tops.sort((a, b) => {
236
+ if (a.path !== b.path) return a.path.localeCompare(b.path);
237
+ return (a.line ?? 0) - (b.line ?? 0);
238
+ });
239
+ return tops;
240
+ }
241
+ /**
242
+ * POST the review to GitHub. Prefers `gh api` (uses the user's existing
243
+ * `gh auth`); falls back to a direct HTTP call with `$GITHUB_TOKEN` when
244
+ * `gh` is unavailable.
245
+ */
246
+ async function submitReview(input) {
247
+ const gh = await detectGhCli();
248
+ if (gh.available && gh.authenticated) return submitViaGh(input);
249
+ const token = readGithubToken();
250
+ if (token) return submitViaToken(input, token);
251
+ return {
252
+ ok: false,
253
+ authSource: "none",
254
+ error: "Cannot submit: no `gh` CLI on $PATH (or not authenticated) and no $GITHUB_TOKEN env var. Run `gh auth login` or set $GITHUB_TOKEN."
255
+ };
256
+ }
257
+ /**
258
+ * Map our `PrDecision` to the GitHub REST event string. Exported so the
259
+ * test suite can pin the mapping (including the `rejected → REQUEST_CHANGES`
260
+ * quirk, which exists because GitHub has no REJECT event).
261
+ *
262
+ * Returns `null` for `draft` — GitHub creates a PENDING review when `event`
263
+ * is omitted from the payload.
264
+ */
265
+ function decisionToEvent(decision) {
266
+ if (decision === "draft") return null;
267
+ if (decision === "approve") return "APPROVE";
268
+ if (decision === "request-changes") return "REQUEST_CHANGES";
269
+ return "COMMENT";
270
+ }
271
+ /**
272
+ * Map open line-anchored `ReviewComment`s to GitHub multi-line review comments.
273
+ * - `deletions` → LEFT, `additions` → RIGHT
274
+ * - Ranges use `start_line`/`line` (no N-part explosion)
275
+ * - File-level comments (`lineNumber === 0`) are excluded; callers fold them
276
+ * into the review body via {@link buildReviewPayload}.
277
+ */
278
+ function expandMultiLineComments(comments) {
279
+ const flat = [];
280
+ for (const c of comments) {
281
+ if (c.status !== "open") continue;
282
+ if (c.lineNumber === 0) continue;
283
+ const path = stripBPrefix(c.filePath);
284
+ const side = c.side === "deletions" ? "LEFT" : "RIGHT";
285
+ const start = c.startLineNumber && c.startLineNumber < c.lineNumber ? c.startLineNumber : void 0;
286
+ const entry = {
287
+ path,
288
+ line: c.lineNumber,
289
+ side,
290
+ body: c.body
291
+ };
292
+ if (start !== void 0) {
293
+ entry.start_line = start;
294
+ entry.start_side = side;
295
+ }
296
+ flat.push(entry);
297
+ }
298
+ return flat;
299
+ }
300
+ function buildReviewPayload(input) {
301
+ const comments = input.comments ?? [];
302
+ const inline = expandMultiLineComments(comments);
303
+ const fileLevel = comments.filter((c) => c.status === "open" && c.lineNumber === 0);
304
+ let body = typeof input.body === "string" ? input.body.trim() : "";
305
+ if (fileLevel.length > 0) {
306
+ const block = ["### File comments", ...fileLevel.map((c) => {
307
+ return `**\`${stripBPrefix(c.filePath)}\`:** ${c.body}`;
308
+ })].join("\n\n");
309
+ body = body ? `${body}\n\n${block}` : block;
310
+ }
311
+ const event = decisionToEvent(input.decision ?? "comment");
312
+ if (event === null) return {
313
+ body: body || void 0,
314
+ comments: inline
315
+ };
316
+ return {
317
+ body: body || void 0,
318
+ event,
319
+ comments: inline
320
+ };
321
+ }
322
+ function stripBPrefix(path) {
323
+ if (path.startsWith("b/")) return path.slice(2);
324
+ if (path.startsWith("a/")) return path.slice(2);
325
+ return path;
326
+ }
327
+ async function submitViaGh(input) {
328
+ const payload = buildReviewPayload(input);
329
+ `${input.resolved.owner}${input.resolved.repo}`;
330
+ const args = [
331
+ "api",
332
+ "--method",
333
+ "POST",
334
+ `repos/${input.resolved.owner}/${input.resolved.repo}/pulls/${input.resolved.pullNumber}/reviews`,
335
+ "-H",
336
+ "Accept: application/vnd.github+json",
337
+ "--input",
338
+ "-"
339
+ ];
340
+ try {
341
+ const { stdout } = await execFileAsync("gh", args, {
342
+ encoding: "utf-8",
343
+ maxBuffer: 20 * 1024 * 1024,
344
+ input: JSON.stringify(payload)
345
+ });
346
+ const result = JSON.parse(stdout);
347
+ return {
348
+ ok: true,
349
+ authSource: "gh",
350
+ reviewId: result.id,
351
+ reviewUrl: result.html_url
352
+ };
353
+ } catch (err) {
354
+ const stdout = err?.stdout || "";
355
+ const stderr = err?.stderr || err?.message || "unknown error";
356
+ let parsed = null;
357
+ try {
358
+ parsed = JSON.parse(stdout);
359
+ } catch {}
360
+ return {
361
+ ok: false,
362
+ authSource: "gh",
363
+ error: parsed?.message || stderr.trim()
364
+ };
365
+ }
366
+ }
367
+ async function submitViaToken(input, token) {
368
+ const payload = buildReviewPayload(input);
369
+ const url = `https://api.github.com/repos/${input.resolved.owner}/${input.resolved.repo}/pulls/${input.resolved.pullNumber}/reviews`;
370
+ try {
371
+ const res = await fetch(url, {
372
+ method: "POST",
373
+ headers: {
374
+ Authorization: `Bearer ${token}`,
375
+ Accept: "application/vnd.github+json",
376
+ "X-GitHub-Api-Version": "2022-11-28",
377
+ "Content-Type": "application/json",
378
+ "User-Agent": "diffing-cli"
379
+ },
380
+ body: JSON.stringify(payload)
381
+ });
382
+ if (!res.ok) {
383
+ const errBody = await res.text().catch(() => "");
384
+ return {
385
+ ok: false,
386
+ authSource: "token",
387
+ error: `HTTP ${res.status}: ${errBody.slice(0, 500)}`
388
+ };
389
+ }
390
+ const result = await res.json();
391
+ return {
392
+ ok: true,
393
+ authSource: "token",
394
+ reviewId: result.id,
395
+ reviewUrl: result.html_url
396
+ };
397
+ } catch (err) {
398
+ return {
399
+ ok: false,
400
+ authSource: "token",
401
+ error: err?.message || "Network error"
402
+ };
403
+ }
404
+ }
405
+ /**
406
+ * Fetch check runs + combined status for a PR head via `gh api`.
407
+ * Best-effort: returns [] on any failure so the UI can degrade gracefully.
408
+ */
409
+ async function fetchPrChecks(resolved, headSha) {
410
+ const checks = [];
411
+ try {
412
+ const { stdout } = await execFileAsync("gh", [
413
+ "api",
414
+ `repos/${resolved.owner}/${resolved.repo}/commits/${headSha}/check-runs?per_page=50`,
415
+ "-H",
416
+ "Accept: application/vnd.github+json"
417
+ ], {
418
+ encoding: "utf-8",
419
+ maxBuffer: 10 * 1024 * 1024
420
+ });
421
+ const parsed = JSON.parse(stdout);
422
+ for (const run of parsed.check_runs ?? []) {
423
+ let state = "unknown";
424
+ if (run.status === "completed") {
425
+ const c = (run.conclusion ?? "").toLowerCase();
426
+ if (c === "success") state = "success";
427
+ else if (c === "failure" || c === "startup_failure") state = "failure";
428
+ else if (c === "neutral") state = "neutral";
429
+ else if (c === "cancelled") state = "cancelled";
430
+ else if (c === "timed_out") state = "timed_out";
431
+ else if (c === "skipped") state = "skipped";
432
+ else if (c === "action_required") state = "action_required";
433
+ else state = "error";
434
+ } else state = "pending";
435
+ checks.push({
436
+ name: run.name,
437
+ state,
438
+ conclusion: run.conclusion,
439
+ detailsUrl: run.html_url ?? null
440
+ });
441
+ }
442
+ } catch {}
443
+ return checks;
444
+ }
445
+ /**
446
+ * Reply to an existing GitHub review comment (thread).
447
+ * POST /repos/{owner}/{repo}/pulls/{pull_number}/comments with in_reply_to.
448
+ */
449
+ async function replyToPrComment(input) {
450
+ const endpoint = `repos/${input.resolved.owner}/${input.resolved.repo}/pulls/${input.resolved.pullNumber}/comments`;
451
+ const payload = JSON.stringify({
452
+ body: input.body,
453
+ in_reply_to: input.inReplyTo
454
+ });
455
+ try {
456
+ const { stdout } = await execFileAsync("gh", [
457
+ "api",
458
+ "--method",
459
+ "POST",
460
+ endpoint,
461
+ "-H",
462
+ "Accept: application/vnd.github+json",
463
+ "--input",
464
+ "-"
465
+ ], {
466
+ encoding: "utf-8",
467
+ input: payload,
468
+ maxBuffer: 5 * 1024 * 1024
469
+ });
470
+ return {
471
+ ok: true,
472
+ id: JSON.parse(stdout).id
473
+ };
474
+ } catch (err) {
475
+ const msg = err?.stderr || err?.stdout || err?.message || "reply failed";
476
+ return {
477
+ ok: false,
478
+ error: String(msg).slice(0, 500)
479
+ };
480
+ }
481
+ }
482
+ async function buildPrSession(ref) {
483
+ const meta = await fetchPrMetadataViaGh(parsePrRef(ref, await detectCwdRepo() ?? void 0));
484
+ return {
485
+ ref,
486
+ owner: meta.owner,
487
+ repo: meta.repo,
488
+ pullNumber: meta.number,
489
+ baseSha: meta.baseSha,
490
+ headSha: meta.headSha,
491
+ title: meta.title,
492
+ url: meta.url,
493
+ author: meta.author,
494
+ additions: meta.additions,
495
+ deletions: meta.deletions,
496
+ changedFiles: meta.changedFiles,
497
+ diff: meta.diff,
498
+ comments: [],
499
+ existingComments: meta.existingComments
500
+ };
501
+ }
502
+ /** Refresh `diff` + `existingComments` + head SHA in an existing session. */
503
+ async function refreshPrSession(session) {
504
+ const meta = await fetchPrMetadataViaGh({
505
+ owner: session.owner,
506
+ repo: session.repo,
507
+ pullNumber: session.pullNumber,
508
+ ref: session.ref
509
+ });
510
+ return {
511
+ ...session,
512
+ baseSha: meta.baseSha,
513
+ headSha: meta.headSha,
514
+ title: meta.title,
515
+ url: meta.url,
516
+ author: meta.author,
517
+ additions: meta.additions,
518
+ deletions: meta.deletions,
519
+ changedFiles: meta.changedFiles,
520
+ diff: meta.diff,
521
+ existingComments: meta.existingComments
522
+ };
523
+ }
524
+ //#endregion
525
+ export { detectGhCli as a, fetchPrMetadataViaGh as c, readGithubToken as d, refreshPrSession as f, detectCwdRepo as i, parseGhAuthStatusUser as l, submitReview as m, buildReviewPayload as n, expandMultiLineComments as o, replyToPrComment as p, decisionToEvent as r, fetchPrChecks as s, buildPrSession as t, parsePrRef as u };