diffing 0.6.0 → 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 (196) hide show
  1. package/README.md +138 -100
  2. package/dist/apply-suggestion-BAuyCYk6.mjs +54 -0
  3. package/dist/cli-agent-BkkRbKqq.mjs +513 -0
  4. package/dist/cli-agent-BlMtx1Tx.mjs +736 -0
  5. package/dist/cli-agent-C4Fb90GL.mjs +728 -0
  6. package/dist/cli-agent-ChALuNbw.mjs +841 -0
  7. package/dist/cli-agent-D71pq2ep.mjs +841 -0
  8. package/dist/cli-agent-qBT8clNM.mjs +841 -0
  9. package/dist/cli-gh-6qiblgj7.mjs +150 -0
  10. package/dist/cli-gh-D5OCnjNe.mjs +150 -0
  11. package/dist/cli-gh-DgqscIkg.mjs +147 -0
  12. package/dist/cli-gh-KjPnyST6.mjs +150 -0
  13. package/dist/cli.mjs +38 -66
  14. package/dist/client/assets/{angular-html-BotC7B4-.js → angular-html-Dh86jUvr.js} +1 -1
  15. package/dist/client/assets/{angular-ts-DQTHeTJm.js → angular-ts-Bh4wRJtT.js} +1 -1
  16. package/dist/client/assets/{apl-Bwz5Dpd_.js → apl-BSXBHduT.js} +1 -1
  17. package/dist/client/assets/{arc-D-TSgv-7.js → arc-CRQlhBrV.js} +1 -1
  18. package/dist/client/assets/{architectureDiagram-3BPJPVTR-DLe25t8t.js → architectureDiagram-3BPJPVTR-BXq3UvEv.js} +1 -1
  19. package/dist/client/assets/{astro-DjkoD2fR.js → astro-Bh00klUw.js} +1 -1
  20. package/dist/client/assets/{blade-Cap2uJIA.js → blade-CisA3zhm.js} +1 -1
  21. package/dist/client/assets/{blockDiagram-GPEHLZMM-B-bihLYO.js → blockDiagram-GPEHLZMM-Bw-PTAJr.js} +1 -1
  22. package/dist/client/assets/{c-C7JoAATL.js → c-D-pHVWrf.js} +1 -1
  23. package/dist/client/assets/{c4Diagram-AAUBKEIU-CoCcjXEC.js → c4Diagram-AAUBKEIU-C0XyiqC_.js} +1 -1
  24. package/dist/client/assets/channel-CDxgOu-3.js +1 -0
  25. package/dist/client/assets/{chunk-2J33WTMH-Sm_uQrGq.js → chunk-2J33WTMH-DekjZXNj.js} +1 -1
  26. package/dist/client/assets/{chunk-3OPIFGDE-Ct83Sp4P.js → chunk-3OPIFGDE-BtROpcL8.js} +1 -1
  27. package/dist/client/assets/{chunk-4BX2VUAB-DrqChk1a.js → chunk-4BX2VUAB-C92XLTlx.js} +1 -1
  28. package/dist/client/assets/{chunk-55IACEB6-CodeFb9E.js → chunk-55IACEB6-CceUgh8N.js} +1 -1
  29. package/dist/client/assets/{chunk-5ZQYHXKU-CpEx4dmS.js → chunk-5ZQYHXKU-DSbYPAe7.js} +1 -1
  30. package/dist/client/assets/{chunk-727SXJPM-D3U6TUmk.js → chunk-727SXJPM-DVzUJa-9.js} +1 -1
  31. package/dist/client/assets/{chunk-AQP2D5EJ-Dr-_DyBy.js → chunk-AQP2D5EJ-6n3Z_IFa.js} +1 -1
  32. package/dist/client/assets/{chunk-BSJP7CBP-Bnyml8mU.js → chunk-BSJP7CBP-BgVLnkxr.js} +1 -1
  33. package/dist/client/assets/{chunk-CSCIHK7Q-BJSFGTlk.js → chunk-CSCIHK7Q-CkGMXntp.js} +1 -1
  34. package/dist/client/assets/{chunk-FMBD7UC4-BqiwDIwx.js → chunk-FMBD7UC4-BM8lrLBo.js} +1 -1
  35. package/dist/client/assets/{chunk-KSCS5N6A-D-H-HBRn.js → chunk-KSCS5N6A-Bm2pjMQg.js} +1 -1
  36. package/dist/client/assets/{chunk-L5ZTLDWV-CVMSAF7h.js → chunk-L5ZTLDWV-VL17ikXM.js} +1 -1
  37. package/dist/client/assets/{chunk-LZXEDZCA-BQsF6HF3.js → chunk-LZXEDZCA-B629g67M.js} +2 -2
  38. package/dist/client/assets/{chunk-ND2GUHAM-eoM20ySO.js → chunk-ND2GUHAM-CjZGwhKA.js} +1 -1
  39. package/dist/client/assets/{chunk-NZK2D7GU-B1iU0i4a.js → chunk-NZK2D7GU-DGsLjESY.js} +1 -1
  40. package/dist/client/assets/{chunk-O5CBEL6O-CcIPuHUC.js → chunk-O5CBEL6O-EyUrGY3l.js} +1 -1
  41. package/dist/client/assets/chunk-QZHKN3VN-DJCeMpP8.js +1 -0
  42. package/dist/client/assets/chunk-WU5MYG2G-BbuOkMaH.js +1 -0
  43. package/dist/client/assets/{chunk-XPW4576I-jSj3E63-.js → chunk-XPW4576I-C2TeAQmQ.js} +1 -1
  44. package/dist/client/assets/classDiagram-4FO5ZUOK-D5oJbiED.js +1 -0
  45. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-5MJt5ZxQ.js +1 -0
  46. package/dist/client/assets/{cobol-DkiK_msx.js → cobol-DsRZMP16.js} +1 -1
  47. package/dist/client/assets/{coffee-f7z4wIqP.js → coffee-DP0-MMsE.js} +1 -1
  48. package/dist/client/assets/{cose-bilkent-S5V4N54A-Dikk-RXu.js → cose-bilkent-S5V4N54A-DMmK0b0W.js} +1 -1
  49. package/dist/client/assets/{cpp-DB_FV8ps.js → cpp-B6XYbbze.js} +1 -1
  50. package/dist/client/assets/{crystal-BRBnyZFz.js → crystal-dBLPFLMp.js} +1 -1
  51. package/dist/client/assets/{css-GpNBLqRF.js → css-tgwuSZL0.js} +1 -1
  52. package/dist/client/assets/{dagre-BM42HDAG-tErJNBcm.js → dagre-BM42HDAG-CgNkf0TR.js} +1 -1
  53. package/dist/client/assets/{diagram-2AECGRRQ-D0-DgitC.js → diagram-2AECGRRQ-ocAqn0LV.js} +1 -1
  54. package/dist/client/assets/{diagram-5GNKFQAL-DqMjQTtW.js → diagram-5GNKFQAL-CB1Yo4Gc.js} +1 -1
  55. package/dist/client/assets/{diagram-KO2AKTUF-CP7V9tqS.js → diagram-KO2AKTUF-B2vpMBRT.js} +1 -1
  56. package/dist/client/assets/{diagram-LMA3HP47-tCGdsz4p.js → diagram-LMA3HP47-DuxZOaQS.js} +1 -1
  57. package/dist/client/assets/{diagram-OG6HWLK6-DtDnaqso.js → diagram-OG6HWLK6-CkShDmcv.js} +1 -1
  58. package/dist/client/assets/{dist-B5Eb-OKZ.js → dist-BgZIvk5w.js} +1 -1
  59. package/dist/client/assets/{edge-DqnWvViE.js → edge-Bokubn8E.js} +1 -1
  60. package/dist/client/assets/{elixir-BSuxOIIa.js → elixir-BEzcf3Xg.js} +1 -1
  61. package/dist/client/assets/{elm-B6U1Is0L.js → elm-vJyLTAER.js} +1 -1
  62. package/dist/client/assets/{erDiagram-TEJ5UH35-C5S0U2x3.js → erDiagram-TEJ5UH35-BFV1NQAH.js} +1 -1
  63. package/dist/client/assets/{erb-kK3QM18H.js → erb-DDkAmjBS.js} +1 -1
  64. package/dist/client/assets/{flowDiagram-I6XJVG4X-NNqmBYk2.js → flowDiagram-I6XJVG4X-DU8xbFdO.js} +1 -1
  65. package/dist/client/assets/{ganttDiagram-6RSMTGT7-8ng09X8S.js → ganttDiagram-6RSMTGT7-DCb7Qh0c.js} +1 -1
  66. package/dist/client/assets/{git-rebase-BDKTWtxr.js → git-rebase-DWcifAdK.js} +1 -1
  67. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-D_HBvwVl.js → gitGraphDiagram-PVQCEYII-C_Tw-piE.js} +1 -1
  68. package/dist/client/assets/{glimmer-js-BthGlRra.js → glimmer-js--HDru5ys.js} +1 -1
  69. package/dist/client/assets/{glimmer-ts-CS8Z9z2h.js → glimmer-ts-DAdXisHh.js} +1 -1
  70. package/dist/client/assets/{glsl-C5ERhU2v.js → glsl-CmIRlJht.js} +1 -1
  71. package/dist/client/assets/{graphql-sfgTqbIp.js → graphql-tx4-7I3U.js} +1 -1
  72. package/dist/client/assets/{hack-EeOmOf4N.js → hack-yA0XjhF7.js} +1 -1
  73. package/dist/client/assets/{haml-DVf5-Ndq.js → haml-BR5MUE1b.js} +1 -1
  74. package/dist/client/assets/{handlebars-BSLgW49o.js → handlebars-BPOF7iOf.js} +1 -1
  75. package/dist/client/assets/{html-DoOSf-hz.js → html-BbteRk96.js} +1 -1
  76. package/dist/client/assets/{html-derivative-BM0DLd1Z.js → html-derivative-59NNrcMm.js} +1 -1
  77. package/dist/client/assets/{http-d2p18QI6.js → http-D3odGQI4.js} +1 -1
  78. package/dist/client/assets/{hurl-CH2I2f66.js → hurl-BLTj8fPj.js} +1 -1
  79. package/dist/client/assets/index-BZcKS8Sb.css +1 -0
  80. package/dist/client/assets/{index-CJI1E0bV.js → index-DHXpO7eS.js} +137 -104
  81. package/dist/client/assets/{infoDiagram-5YYISTIA-Cgg2FsCN.js → infoDiagram-5YYISTIA-CI6hJyRl.js} +1 -1
  82. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-pYRQFecr.js → ishikawaDiagram-YF4QCWOH-gMS1P1LQ.js} +1 -1
  83. package/dist/client/assets/{java-BFiqhlWm.js → java-CsViuzs_.js} +1 -1
  84. package/dist/client/assets/{javascript-YWa7zRPZ.js → javascript-DQgsl2Dh.js} +1 -1
  85. package/dist/client/assets/{jinja-h_UdmDAn.js → jinja-DQNp2NUA.js} +1 -1
  86. package/dist/client/assets/{jison-cOvcjPYz.js → jison-CDDOZQdc.js} +1 -1
  87. package/dist/client/assets/{journeyDiagram-JHISSGLW-D3EBlW7m.js → journeyDiagram-JHISSGLW-BTQ_Ws_h.js} +1 -1
  88. package/dist/client/assets/{json-DT0GjoRi.js → json-DP4TqCuE.js} +1 -1
  89. package/dist/client/assets/{jsx-BaYjgZVH.js → jsx-Df5hn0Tf.js} +1 -1
  90. package/dist/client/assets/{julia-Cg36ySxC.js → julia--u33yGLi.js} +1 -1
  91. package/dist/client/assets/{just-KYC4U1y4.js → just-BCpMmSOD.js} +1 -1
  92. package/dist/client/assets/{kanban-definition-UN3LZRKU-DA42mt4M.js → kanban-definition-UN3LZRKU-CvqGbLR_.js} +1 -1
  93. package/dist/client/assets/{latex-DpuXxlsH.js → latex-D8D4jZGw.js} +1 -1
  94. package/dist/client/assets/{line-D91vf_Yl.js → line-DBgaS_RW.js} +1 -1
  95. package/dist/client/assets/{linear-CL7WzrL5.js → linear-RQVzKNVU.js} +1 -1
  96. package/dist/client/assets/{liquid-Dua3sjBU.js → liquid-CkH85f12.js} +1 -1
  97. package/dist/client/assets/{lua-CpDMEbOh.js → lua-iKoV7WE3.js} +1 -1
  98. package/dist/client/assets/{marko-rM0rxrJ5.js → marko-N8ScQSqT.js} +1 -1
  99. package/dist/client/assets/{mdc-D-E4W6ne.js → mdc-BvHTOxsp.js} +1 -1
  100. package/dist/client/assets/{mermaid-parser.core-BLQDdrsf.js → mermaid-parser.core-CwDRwMR7.js} +1 -1
  101. package/dist/client/assets/{mermaid.core-BBR2Otc_.js → mermaid.core-D2LDJgXO.js} +3 -3
  102. package/dist/client/assets/{mindmap-definition-RKZ34NQL-DMrApv-_.js → mindmap-definition-RKZ34NQL-FRnPh642.js} +1 -1
  103. package/dist/client/assets/{nginx-wZWgcQLM.js → nginx-DGgvASuI.js} +1 -1
  104. package/dist/client/assets/{nim-Bh7OZsZh.js → nim-D-v9hzvR.js} +1 -1
  105. package/dist/client/assets/{perl-DtEqNC_V.js → perl-CG8leJ8K.js} +1 -1
  106. package/dist/client/assets/{php-DX7bHce8.js → php-DvkeSP10.js} +1 -1
  107. package/dist/client/assets/{pieDiagram-4H26LBE5-B2f9_PO6.js → pieDiagram-4H26LBE5-CepOg38Y.js} +1 -1
  108. package/dist/client/assets/{pug-r67inScB.js → pug-B4T7-xaL.js} +1 -1
  109. package/dist/client/assets/{qml-f1bOqUwX.js → qml-BE6D5Bxn.js} +1 -1
  110. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DgdKno5G.js → quadrantDiagram-W4KKPZXB-DOFAsP60.js} +1 -1
  111. package/dist/client/assets/{r-Cpm-_vkr.js → r-BKZg9Mcq.js} +1 -1
  112. package/dist/client/assets/{razor-CEymfLfo.js → razor-D-02oaxL.js} +1 -1
  113. package/dist/client/assets/{regexp-DCnAJwuE.js → regexp-BDFvAR1k.js} +1 -1
  114. package/dist/client/assets/{requirementDiagram-4Y6WPE33-BIRl56-J.js → requirementDiagram-4Y6WPE33-BBVNH-2W.js} +1 -1
  115. package/dist/client/assets/review-export-D5iIl46_.js +7 -0
  116. package/dist/client/assets/{rst-Cm1M2uc8.js → rst-BcT26OSj.js} +1 -1
  117. package/dist/client/assets/{ruby-B49kkGT0.js → ruby-Cq_YQvFT.js} +1 -1
  118. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-7XZZmV9N.js → sankeyDiagram-5OEKKPKP-BQfCTbBX.js} +1 -1
  119. package/dist/client/assets/{sas-BOXC9Iq4.js → sas-DN9oTxKY.js} +1 -1
  120. package/dist/client/assets/{scss-DU2HGWZP.js → scss-BK_sp3z2.js} +1 -1
  121. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-CtIjo3i9.js → sequenceDiagram-3UESZ5HK-CKAW2eyx.js} +1 -1
  122. package/dist/client/assets/{shellscript-YFjx1NBH.js → shellscript-CvNpOu0J.js} +1 -1
  123. package/dist/client/assets/{shellsession-4zfyiKm-.js → shellsession-8-yj2cJj.js} +1 -1
  124. package/dist/client/assets/{soy-CRExh-ql.js → soy-C6Q9atPF.js} +1 -1
  125. package/dist/client/assets/{sql-2Iw6Ta1F.js → sql-CBL3BuHb.js} +1 -1
  126. package/dist/client/assets/{src-Dn3_YOHM.js → src-D-VUWIK5.js} +1 -1
  127. package/dist/client/assets/{stata-DflAcpzB.js → stata-B2Q7Vqzu.js} +1 -1
  128. package/dist/client/assets/{stateDiagram-AJRCARHV-D_w9F0BN.js → stateDiagram-AJRCARHV-BGJiv0qO.js} +1 -1
  129. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CdUgMghp.js +1 -0
  130. package/dist/client/assets/{surrealql-CMprrxj1.js → surrealql-C-A2N8vk.js} +1 -1
  131. package/dist/client/assets/{svelte-Bb_Bxt4a.js → svelte-VAut_-uy.js} +1 -1
  132. package/dist/client/assets/{templ-MJ_5gYAf.js → templ-mUetM--K.js} +1 -1
  133. package/dist/client/assets/{tex-BQ33E56K.js → tex-BT53mWGp.js} +1 -1
  134. package/dist/client/assets/{timeline-definition-PNZ67QCA-BzgsTjf8.js → timeline-definition-PNZ67QCA-lVwMt1rk.js} +1 -1
  135. package/dist/client/assets/{ts-tags-Cddttr6f.js → ts-tags-VkIOkgLN.js} +1 -1
  136. package/dist/client/assets/{tsx-BFchi_eS.js → tsx-D_aaGHTf.js} +1 -1
  137. package/dist/client/assets/{twig-CA8ssIYI.js → twig-CufwqKkk.js} +1 -1
  138. package/dist/client/assets/{typescript-tdiTR9sY.js → typescript-osFD2CEV.js} +1 -1
  139. package/dist/client/assets/{vennDiagram-CIIHVFJN-CMjPo4Qf.js → vennDiagram-CIIHVFJN-_K9NtpA5.js} +1 -1
  140. package/dist/client/assets/{vue-BbZNP0Rd.js → vue-Bub1rXmL.js} +1 -1
  141. package/dist/client/assets/{vue-html-02g5ipYx.js → vue-html-CN3x2pYg.js} +1 -1
  142. package/dist/client/assets/{vue-vine-9lCbaeLR.js → vue-vine-C0csYkGm.js} +1 -1
  143. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Cy6qwCgU.js → wardleyDiagram-YWT4CUSO-B2jTMfra.js} +1 -1
  144. package/dist/client/assets/{xml-BPPCB2P8.js → xml-my6OjeGb.js} +1 -1
  145. package/dist/client/assets/{xsl-1-u1Beet.js → xsl-Bx0YVn26.js} +1 -1
  146. package/dist/client/assets/{xychartDiagram-2RQKCTM6-q9ysdRiS.js → xychartDiagram-2RQKCTM6-DfZ4YYXU.js} +1 -1
  147. package/dist/client/assets/{yaml-79W-bav7.js → yaml-DanwOypk.js} +1 -1
  148. package/dist/client/favicon.svg +40 -0
  149. package/dist/client/index.html +7 -3
  150. package/dist/comment-format-B5AcGcR-.mjs +118 -0
  151. package/dist/comment-format-CKTt0fzi.mjs +113 -0
  152. package/dist/comment-format-DG_PAehC.mjs +117 -0
  153. package/dist/completions-ZSKYvW06.mjs +126 -0
  154. package/dist/diff-fingerprint-CWa-soYD.mjs +78 -0
  155. package/dist/doctor-q6oA64hm.mjs +195 -0
  156. package/dist/find-tui-binary-CRpbYXLC.mjs +52 -0
  157. package/dist/git-Czo96ymM.mjs +789 -0
  158. package/dist/git-DaP_1rVy.mjs +2 -0
  159. package/dist/github-C9GLNak7.mjs +2 -0
  160. package/dist/github-HfIlIS-J.mjs +525 -0
  161. package/dist/mcp-7eXATtez.mjs +1334 -0
  162. package/dist/mcp-BBk8AIO4.mjs +1334 -0
  163. package/dist/mcp-BJxrW9dS.mjs +1334 -0
  164. package/dist/mcp-BgZO-iXI.mjs +1047 -0
  165. package/dist/mcp-BhT5GgYF.mjs +1334 -0
  166. package/dist/mcp-BsGdNA88.mjs +1334 -0
  167. package/dist/mcp-BxBm3d5l.mjs +1047 -0
  168. package/dist/mcp-Ca9QAlJ0.mjs +1334 -0
  169. package/dist/mcp-CphIUJUg.mjs +1047 -0
  170. package/dist/mcp-eor39qmD.mjs +1352 -0
  171. package/dist/plan-format-BQyx--mp.mjs +144 -0
  172. package/dist/plan-format-Bi46QS0m.mjs +132 -0
  173. package/dist/review-export-BoV0884M.mjs +66 -0
  174. package/dist/review-export-CKTI5Od_.mjs +66 -0
  175. package/dist/review-export-DjxKBeIZ.mjs +66 -0
  176. package/dist/server-AisDKkpW.mjs +3227 -0
  177. package/dist/server-BUsRhL1B.mjs +3283 -0
  178. package/dist/server-BZBFquG8.mjs +3875 -0
  179. package/dist/server-Bc2m9R5-.mjs +3263 -0
  180. package/dist/server-Bmt57wV5.mjs +3875 -0
  181. package/dist/server-C105rwQw.mjs +3283 -0
  182. package/dist/server-D0Ni8PKP.mjs +3252 -0
  183. package/dist/server-D2xEbakj.mjs +3874 -0
  184. package/dist/server-TOElOuNP.mjs +3258 -0
  185. package/dist/server-lock-DaSx7DjW.mjs +1049 -0
  186. package/dist/server-lock-uqSdDYeR.mjs +153 -0
  187. package/dist/settings-BVnzGFL7.mjs +478 -0
  188. package/dist/update-check-fe0tR8tx.mjs +129 -0
  189. package/package.json +1 -1
  190. package/dist/client/assets/channel-h7e2aSOW.js +0 -1
  191. package/dist/client/assets/chunk-QZHKN3VN-HzjMGBiA.js +0 -1
  192. package/dist/client/assets/chunk-WU5MYG2G-D_yZ0nqc.js +0 -1
  193. package/dist/client/assets/classDiagram-4FO5ZUOK-eToeXGJV.js +0 -1
  194. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-D_EfzX89.js +0 -1
  195. package/dist/client/assets/index-Ct9zZxJ5.css +0 -1
  196. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Brvg0TWH.js +0 -1
@@ -0,0 +1,117 @@
1
+ //#region src/lib/comment-format.ts
2
+ /** Plain-language guidance the agent should act on, derived from the verdict. */
3
+ function reviewDecisionSummary(decision) {
4
+ switch (decision) {
5
+ case "approved": return "The reviewer APPROVED these changes. Address any open comments below, then proceed — the overall direction is good.";
6
+ case "changes-requested": return "The reviewer REQUESTED EDITS. Address every open comment below and apply the requested changes before considering this review done.";
7
+ case "rejected": return "The reviewer REJECTED these changes. Do NOT keep building on this approach; reconsider it in light of the comments below before continuing.";
8
+ case "comment-only": return "The reviewer chose COMMENT-ONLY mode. You MUST NOT edit any files. Only reply to the comments below — answer questions, provide clarification, or discuss. The general comment (if any) is your prompt for the chat.";
9
+ }
10
+ }
11
+ /**
12
+ * Render review comments as the `<code-review-comments>` XML envelope used to
13
+ * hand a review to an AI agent. Shared by the UI clipboard button
14
+ * (`useComments.formatAllComments`), the server's `/api/review/send` handoff,
15
+ * and the `diffing` CLI/MCP so every channel emits byte-identical output.
16
+ *
17
+ * A review may carry a headline `decision` (approve / request edits / reject / comment-only)
18
+ * and/or an overall comment, so the envelope is emitted even with zero inline
19
+ * comments — letting a reviewer submit a verdict without annotating any line.
20
+ */
21
+ function formatComments(comments, generalComment, decision, mode) {
22
+ const trimmedGeneral = generalComment?.trim();
23
+ if (comments.length === 0 && !decision && !trimmedGeneral) return "";
24
+ const grouped = /* @__PURE__ */ new Map();
25
+ for (const comment of comments) {
26
+ const list = grouped.get(comment.filePath) ?? [];
27
+ list.push(comment);
28
+ grouped.set(comment.filePath, list);
29
+ }
30
+ const sortedFilePaths = [...grouped.keys()].sort((a, b) => a.localeCompare(b));
31
+ const modeAttr = mode && mode !== "standard" ? ` mode="${mode}"` : "";
32
+ const lines = [];
33
+ lines.push(decision ? `<code-review-comments decision="${decision}"${modeAttr}>` : `<code-review-comments${modeAttr}>`);
34
+ lines.push(" <instructions>");
35
+ lines.push(" You are an AI coding assistant. You are receiving a structured list of code review comments to address in the repository.");
36
+ if (mode === "comment-only") lines.push(" ⚠️ COMMENT-ONLY MODE: You MUST NOT edit any files. Your only task is to reply to the comments below — answer questions, provide clarification, or discuss. The general comment (if present) is your prompt for this chat.");
37
+ else lines.push(" For each file, review the inline comments and apply the changes requested.");
38
+ if (decision) {
39
+ lines.push(" - The \"decision\" attribute on the root element is the reviewer's headline verdict: \"approved\", \"changes-requested\", \"rejected\", or \"comment-only\".");
40
+ lines.push(" - <decision-summary> tells you, in plain language, what to do next based on that verdict.");
41
+ }
42
+ if (mode) lines.push(` - The "mode" attribute on the root element controls your behavior: "standard" (apply edits) or "comment-only" (reply only, no edits).`);
43
+ lines.push(" - Target lines are specified by the \"line\" attribute (e.g. line=\"10\" or line=\"10-15\").");
44
+ lines.push(" - \"side\" indicates whether the comment is on \"additions\" (added/modified lines) or \"deletions\" (deleted/old lines).");
45
+ lines.push(" - \"status\" indicates whether the comment is \"open\" or \"resolved\". Only address comments with status=\"open\".");
46
+ lines.push(" - Optional severity=\"blocking|nit|question|praise\":");
47
+ lines.push(" blocking = must fix before considering the review done;");
48
+ lines.push(" nit = optional polish; question = needs an answer; praise = positive (no change required).");
49
+ lines.push(" Omit severity (or severity=\"none\") means untriaged — treat as a normal request.");
50
+ lines.push(" - The <code> block contains the specific code context at the reviewed lines, prefixed with \"+\" or \"-\".");
51
+ lines.push(" - The <body> tag contains the review feedback or request.");
52
+ lines.push(" - If developers have replied to the comment, their discussion is captured under the <replies> element.");
53
+ lines.push(" - The comment \"id\" attribute can be used to reference or update the comment via API if available.");
54
+ lines.push("");
55
+ lines.push(" HOW TO REPLY OR ASK FOR CLARIFICATION:");
56
+ lines.push(" If you need to ask for clarification, explain what you did, or reply to any comment:");
57
+ lines.push("");
58
+ lines.push(" Option A: Via the diffing CLI or MCP (Preferred — port-agnostic, no copy-paste)");
59
+ lines.push(" diffing reply <comment-id> --body \"Your response\" --model \"<your-model-name>\"");
60
+ lines.push(" diffing resolve <comment-id>");
61
+ lines.push(" (Or the equivalent MCP tools: reply_to_comment, resolve_comment.)");
62
+ lines.push("");
63
+ lines.push(" Option B: Via the local HTTP API (if you know the running port)");
64
+ lines.push(" POST http://localhost:<port>/api/comments/<comment-id>/replies");
65
+ lines.push(" Payload: { \"body\": \"Your response or clarification request here\", \"model\": \"<your-model-name>\" }");
66
+ lines.push(" PUT http://localhost:<port>/api/comments/<comment-id> Payload: { \"status\": \"resolved\" }");
67
+ lines.push("");
68
+ lines.push(" Option C: Via Text Response (Offline / Chat Copy-Paste)");
69
+ lines.push(" If you do not have local API access, output your comments/replies inside a structured XML block at the end of your response:");
70
+ lines.push(" <comment-replies>");
71
+ lines.push(" <reply to=\"<comment-id>\" model=\"<your-model-name>\"><![CDATA[Your reply or clarification request here]]></reply>");
72
+ lines.push(" </comment-replies>");
73
+ lines.push(" </instructions>");
74
+ if (decision) lines.push(` <decision-summary><![CDATA[${reviewDecisionSummary(decision)}]]></decision-summary>`);
75
+ if (trimmedGeneral) {
76
+ lines.push(" <general-comment>");
77
+ lines.push(` <![CDATA[${trimmedGeneral}]]>`);
78
+ lines.push(" </general-comment>");
79
+ }
80
+ for (const filePath of sortedFilePaths) {
81
+ const fileComments = grouped.get(filePath);
82
+ lines.push(` <file path="${filePath}">`);
83
+ for (const comment of fileComments) {
84
+ const lineAttr = comment.lineNumber === 0 ? "file" : comment.startLineNumber && comment.startLineNumber !== comment.lineNumber ? `${comment.startLineNumber}-${comment.lineNumber}` : `${comment.lineNumber}`;
85
+ const isoDate = new Date(comment.createdAt).toISOString();
86
+ const severityAttr = comment.severity && comment.severity !== "none" ? ` severity="${comment.severity}"` : "";
87
+ lines.push(` <comment id="${comment.id}" line="${lineAttr}" side="${comment.side}" status="${comment.status}"${severityAttr} created-at="${isoDate}">`);
88
+ if (comment.lineNumber !== 0) {
89
+ const prefix = comment.side === "additions" ? "+" : "-";
90
+ const isMultiLine = comment.lineContent && comment.lineContent.includes("\n");
91
+ let codeVal = "";
92
+ if (isMultiLine) codeVal = `\n${comment.lineContent.split("\n").map((l) => `${prefix} ${l}`).join("\n")}\n`;
93
+ else codeVal = `${prefix} ${comment.lineContent}`;
94
+ lines.push(` <code><![CDATA[${codeVal}]]></code>`);
95
+ }
96
+ lines.push(` <body><![CDATA[${comment.body}]]></body>`);
97
+ if (comment.replies && comment.replies.length > 0) {
98
+ lines.push(" <replies>");
99
+ for (const reply of comment.replies) {
100
+ const replyIsoDate = new Date(reply.createdAt).toISOString();
101
+ const roleAttr = reply.role ? ` role="${reply.role}"` : " role=\"agent\"";
102
+ const modelAttr = reply.model ? ` model="${reply.model}"` : "";
103
+ lines.push(` <reply id="${reply.id}" created-at="${replyIsoDate}"${roleAttr}${modelAttr}>`);
104
+ lines.push(` <![CDATA[${reply.body}]]>`);
105
+ lines.push(" </reply>");
106
+ }
107
+ lines.push(" </replies>");
108
+ }
109
+ lines.push(" </comment>");
110
+ }
111
+ lines.push(" </file>");
112
+ }
113
+ lines.push("</code-review-comments>");
114
+ return lines.join("\n");
115
+ }
116
+ //#endregion
117
+ export { reviewDecisionSummary as n, formatComments as t };
@@ -0,0 +1,126 @@
1
+ //#region src/lib/completions.ts
2
+ /**
3
+ * Shell completion scripts for bash / zsh / fish.
4
+ */
5
+ const SUBCOMMANDS = [
6
+ "await-review",
7
+ "reply",
8
+ "resolve",
9
+ "unresolve",
10
+ "comment",
11
+ "comments",
12
+ "url",
13
+ "mcp",
14
+ "plan",
15
+ "update",
16
+ "gh",
17
+ "doctor",
18
+ "show",
19
+ "completion",
20
+ "progress"
21
+ ];
22
+ const PLAN_ACTIONS = [
23
+ "submit",
24
+ "await",
25
+ "list",
26
+ "show",
27
+ "versions",
28
+ "reply",
29
+ "resolve"
30
+ ];
31
+ const GH_ACTIONS = [
32
+ "status",
33
+ "pr-fetch",
34
+ "pr-review",
35
+ "pr-list-comments"
36
+ ];
37
+ const GLOBAL_FLAGS = [
38
+ "--help",
39
+ "--version",
40
+ "--web",
41
+ "--terminal",
42
+ "--tui",
43
+ "--no-open",
44
+ "--port",
45
+ "--host",
46
+ "--staged",
47
+ "--cached",
48
+ "--gh-pr"
49
+ ];
50
+ function bashCompletion() {
51
+ return `# diffing bash completion
52
+ _diffing() {
53
+ local cur prev words cword
54
+ _init_completion || return
55
+ case "\${words[1]}" in
56
+ plan) COMPREPLY=( $(compgen -W "${PLAN_ACTIONS.join(" ")}" -- "$cur") ) ;;
57
+ gh) COMPREPLY=( $(compgen -W "${GH_ACTIONS.join(" ")}" -- "$cur") ) ;;
58
+ completion) COMPREPLY=( $(compgen -W "bash zsh fish" -- "$cur") ) ;;
59
+ comments) COMPREPLY=( $(compgen -W "--open --json --format" -- "$cur") ) ;;
60
+ *)
61
+ if [[ "$cur" == -* ]]; then
62
+ COMPREPLY=( $(compgen -W "${GLOBAL_FLAGS.join(" ")}" -- "$cur") )
63
+ else
64
+ COMPREPLY=( $(compgen -W "${SUBCOMMANDS.join(" ")}" -- "$cur") )
65
+ fi
66
+ ;;
67
+ esac
68
+ }
69
+ complete -F _diffing diffing
70
+ `;
71
+ }
72
+ function zshCompletion() {
73
+ return `#compdef diffing
74
+ _diffing() {
75
+ local -a commands
76
+ commands=(
77
+ 'await-review:Block until human sends review'
78
+ 'reply:Reply to a comment'
79
+ 'resolve:Resolve a comment'
80
+ 'comments:Dump comments'
81
+ 'url:Print server URL'
82
+ 'mcp:Run MCP server'
83
+ 'plan:Plan review commands'
84
+ 'update:Upgrade diffing'
85
+ 'gh:GitHub PR commands'
86
+ 'doctor:Diagnose setup'
87
+ 'show:Show commit(s) like git show'
88
+ 'completion:Print shell completions'
89
+ )
90
+ _arguments -C \\
91
+ '1: :->cmd' \\
92
+ '*::arg:->args'
93
+ case $state in
94
+ cmd) _describe 'command' commands ;;
95
+ args)
96
+ case $words[1] in
97
+ plan) _values 'plan action' ${PLAN_ACTIONS.map((a) => `'${a}'`).join(" ")} ;;
98
+ gh) _values 'gh action' ${GH_ACTIONS.map((a) => `'${a}'`).join(" ")} ;;
99
+ completion) _values 'shell' bash zsh fish ;;
100
+ esac
101
+ ;;
102
+ esac
103
+ }
104
+ compdef _diffing diffing
105
+ `;
106
+ }
107
+ function fishCompletion() {
108
+ return [
109
+ "complete -c diffing -f",
110
+ ...SUBCOMMANDS.map((s) => `complete -c diffing -n "__fish_use_subcommand" -a ${s}`),
111
+ ...GLOBAL_FLAGS.map((f) => `complete -c diffing -n "__fish_use_subcommand" -l ${f.replace(/^--/, "")}`),
112
+ ...PLAN_ACTIONS.map((a) => `complete -c diffing -n "__fish_seen_subcommand_from plan" -a ${a}`),
113
+ ...GH_ACTIONS.map((a) => `complete -c diffing -n "__fish_seen_subcommand_from gh" -a ${a}`),
114
+ "complete -c diffing -n \"__fish_seen_subcommand_from completion\" -a \"bash zsh fish\""
115
+ ].join("\n") + "\n";
116
+ }
117
+ function completionFor(shell) {
118
+ switch (shell.toLowerCase()) {
119
+ case "bash": return bashCompletion();
120
+ case "zsh": return zshCompletion();
121
+ case "fish": return fishCompletion();
122
+ default: return null;
123
+ }
124
+ }
125
+ //#endregion
126
+ export { completionFor };
@@ -0,0 +1,78 @@
1
+ //#region src/lib/diff-fingerprint.ts
2
+ /**
3
+ * Per-file fingerprints of a unified diff, used to detect "what changed
4
+ * since the last review handoff" without storing full patch text.
5
+ */
6
+ /** Stable FNV-1a 32-bit hex hash of a string. */
7
+ function hashString(input) {
8
+ let h = 2166136261;
9
+ for (let i = 0; i < input.length; i++) {
10
+ h ^= input.charCodeAt(i);
11
+ h = Math.imul(h, 16777619);
12
+ }
13
+ return (h >>> 0).toString(16).padStart(8, "0");
14
+ }
15
+ /**
16
+ * Split a multi-file unified diff into path → raw file-diff text.
17
+ * Paths are taken from the `b/` side of `diff --git a/... b/...` headers
18
+ * (or the first path for pure deletions).
19
+ */
20
+ function splitUnifiedDiffByFile(patch) {
21
+ const map = /* @__PURE__ */ new Map();
22
+ if (!patch.trim()) return map;
23
+ const lines = patch.split(/\r?\n/);
24
+ let currentPath = null;
25
+ let buf = [];
26
+ const flush = () => {
27
+ if (currentPath != null && buf.length > 0) map.set(currentPath, buf.join("\n"));
28
+ currentPath = null;
29
+ buf = [];
30
+ };
31
+ for (const line of lines) {
32
+ const m = /^diff --git a\/(.+?) b\/(.+)$/.exec(line);
33
+ if (m) {
34
+ flush();
35
+ currentPath = m[2] === "/dev/null" ? m[1] : m[2];
36
+ buf = [line];
37
+ continue;
38
+ }
39
+ if (currentPath != null) buf.push(line);
40
+ }
41
+ flush();
42
+ return map;
43
+ }
44
+ /** path → content fingerprint for every file in the patch. */
45
+ function fingerprintDiffFiles(patch) {
46
+ const parts = splitUnifiedDiffByFile(patch);
47
+ const out = {};
48
+ for (const [path, text] of parts) out[path] = hashString(text);
49
+ return out;
50
+ }
51
+ /**
52
+ * Compare two fingerprint maps. Returns sorted path lists.
53
+ */
54
+ function diffSinceLast(previous, current) {
55
+ const prev = previous ?? {};
56
+ const prevKeys = new Set(Object.keys(prev));
57
+ const currKeys = new Set(Object.keys(current));
58
+ const changed = [];
59
+ const added = [];
60
+ const removed = [];
61
+ for (const path of currKeys) if (!prevKeys.has(path)) added.push(path);
62
+ else if (prev[path] !== current[path]) changed.push(path);
63
+ for (const path of prevKeys) if (!currKeys.has(path)) removed.push(path);
64
+ changed.sort((a, b) => a.localeCompare(b));
65
+ added.sort((a, b) => a.localeCompare(b));
66
+ removed.sort((a, b) => a.localeCompare(b));
67
+ return {
68
+ changed,
69
+ added,
70
+ removed
71
+ };
72
+ }
73
+ /** Union of changed + added — the files a reviewer should re-check. */
74
+ function filesToReviewSinceLast(delta) {
75
+ return [...delta.changed, ...delta.added].sort((a, b) => a.localeCompare(b));
76
+ }
77
+ //#endregion
78
+ export { diffSinceLast, filesToReviewSinceLast, fingerprintDiffFiles };
@@ -0,0 +1,195 @@
1
+ import { r as getSearchStatus, t as loadSettings } from "./settings-BVnzGFL7.mjs";
2
+ import { a as detectGhCli, d as readGithubToken } from "./github-HfIlIS-J.mjs";
3
+ import { i as readServerLock, r as isLockAlive } from "./server-lock-uqSdDYeR.mjs";
4
+ import { t as findTuiBinary } from "./find-tui-binary-CRpbYXLC.mjs";
5
+ import { execFileSync } from "node:child_process";
6
+ import { join } from "node:path";
7
+ import { existsSync, readFileSync } from "node:fs";
8
+ import { homedir } from "node:os";
9
+ //#region src/lib/doctor.ts
10
+ /**
11
+ * `diffing doctor` — diagnostic checks for a healthy local review setup.
12
+ * Pure-ish helpers so the CLI can print a clean report without side effects
13
+ * beyond read-only probes.
14
+ */
15
+ function tryGitTopLevel(cwd) {
16
+ try {
17
+ return execFileSync("git", [
18
+ "-C",
19
+ cwd,
20
+ "rev-parse",
21
+ "--show-toplevel"
22
+ ], {
23
+ encoding: "utf-8",
24
+ stdio: [
25
+ "ignore",
26
+ "pipe",
27
+ "pipe"
28
+ ]
29
+ }).trim();
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+ async function runDoctor(options) {
35
+ const cwd = options.cwd ?? process.cwd();
36
+ const checks = [];
37
+ const repoRoot = tryGitTopLevel(cwd);
38
+ if (repoRoot) checks.push({
39
+ id: "git",
40
+ label: "Git repository",
41
+ level: "ok",
42
+ detail: repoRoot
43
+ });
44
+ else checks.push({
45
+ id: "git",
46
+ label: "Git repository",
47
+ level: "error",
48
+ detail: `${cwd} is not inside a Git repository`
49
+ });
50
+ if (repoRoot) {
51
+ const lock = readServerLock(repoRoot);
52
+ if (!lock) checks.push({
53
+ id: "server",
54
+ label: "Review server",
55
+ level: "warn",
56
+ detail: "No active server lock (run `diffing` to start one)"
57
+ });
58
+ else if (!isLockAlive(lock, repoRoot)) checks.push({
59
+ id: "server",
60
+ label: "Review server",
61
+ level: "warn",
62
+ detail: `Stale lock for pid ${lock.pid} — will be replaced on next start`
63
+ });
64
+ else {
65
+ const url = lock.port > 0 ? `http://${lock.host}:${lock.port}` : `mode=${lock.mode ?? "web"}`;
66
+ checks.push({
67
+ id: "server",
68
+ label: "Review server",
69
+ level: "ok",
70
+ detail: `${url} (pid ${lock.pid}, mode ${lock.mode ?? "web"})`
71
+ });
72
+ }
73
+ }
74
+ try {
75
+ const gh = await detectGhCli();
76
+ if (gh.available && gh.authenticated) checks.push({
77
+ id: "gh",
78
+ label: "GitHub CLI (gh)",
79
+ level: "ok",
80
+ detail: gh.user ? `authenticated as ${gh.user}` : "authenticated"
81
+ });
82
+ else if (gh.available) {
83
+ const token = readGithubToken();
84
+ checks.push({
85
+ id: "gh",
86
+ label: "GitHub CLI (gh)",
87
+ level: token ? "warn" : "warn",
88
+ detail: token ? "gh present but not authenticated; $GITHUB_TOKEN is set (token fallback for submit)" : "gh present but not authenticated — run `gh auth login` for PR mode"
89
+ });
90
+ } else {
91
+ const token = readGithubToken();
92
+ checks.push({
93
+ id: "gh",
94
+ label: "GitHub CLI (gh)",
95
+ level: token ? "warn" : "warn",
96
+ detail: token ? "gh not found; $GITHUB_TOKEN is set (submit-only token path)" : "gh not found and no $GITHUB_TOKEN — PR mode unavailable"
97
+ });
98
+ }
99
+ } catch (err) {
100
+ checks.push({
101
+ id: "gh",
102
+ label: "GitHub CLI (gh)",
103
+ level: "warn",
104
+ detail: err?.message ?? "failed to probe gh"
105
+ });
106
+ }
107
+ try {
108
+ const status = await getSearchStatus();
109
+ if (status.available) checks.push({
110
+ id: "search",
111
+ label: "Native search (fff)",
112
+ level: "ok",
113
+ detail: status.indexedFiles != null ? `available · ~${status.indexedFiles} indexed files` : "available"
114
+ });
115
+ else checks.push({
116
+ id: "search",
117
+ label: "Native search (fff)",
118
+ level: "warn",
119
+ detail: status.error || "unavailable on this platform (graceful degradation)"
120
+ });
121
+ } catch (err) {
122
+ checks.push({
123
+ id: "search",
124
+ label: "Native search (fff)",
125
+ level: "warn",
126
+ detail: err?.message ?? "probe failed"
127
+ });
128
+ }
129
+ const tuiPath = options.cliImportMetaUrl ? findTuiBinary(options.cliImportMetaUrl) : findTuiBinary(import.meta.url);
130
+ checks.push({
131
+ id: "tui",
132
+ label: "TUI binary",
133
+ level: tuiPath ? "ok" : "warn",
134
+ detail: tuiPath ?? "not found (optional — web UI is the supported path)"
135
+ });
136
+ try {
137
+ const settings = loadSettings();
138
+ checks.push({
139
+ id: "settings",
140
+ label: "User settings",
141
+ level: "ok",
142
+ detail: `theme=${settings.theme} density=${settings.density} · ${join(homedir(), ".config", "diffing", "settings.json")}`
143
+ });
144
+ } catch (err) {
145
+ checks.push({
146
+ id: "settings",
147
+ label: "User settings",
148
+ level: "error",
149
+ detail: err?.message ?? "failed to load settings.json"
150
+ });
151
+ }
152
+ if (repoRoot) try {
153
+ const { getProjectStorageDir } = await import("./git-DaP_1rVy.mjs");
154
+ const dir = getProjectStorageDir();
155
+ const marker = join(dir, "repo_path.txt");
156
+ if (existsSync(marker)) {
157
+ const recorded = readFileSync(marker, "utf-8").trim();
158
+ checks.push({
159
+ id: "storage",
160
+ label: "Project storage",
161
+ level: "ok",
162
+ detail: `${dir} (repo_path=${recorded || "n/a"})`
163
+ });
164
+ } else checks.push({
165
+ id: "storage",
166
+ label: "Project storage",
167
+ level: "ok",
168
+ detail: `${dir} (will be created on first use)`
169
+ });
170
+ } catch (err) {
171
+ checks.push({
172
+ id: "storage",
173
+ label: "Project storage",
174
+ level: "warn",
175
+ detail: err?.message ?? "could not resolve storage dir"
176
+ });
177
+ }
178
+ return {
179
+ checks,
180
+ ok: !checks.some((c) => c.level === "error")
181
+ };
182
+ }
183
+ function formatDoctorReport(report) {
184
+ const icon = (level) => level === "ok" ? "✓" : level === "warn" ? "!" : "✗";
185
+ const lines = ["diffing doctor", "─────────────"];
186
+ for (const c of report.checks) {
187
+ lines.push(`${icon(c.level)} ${c.label}`);
188
+ lines.push(` ${c.detail}`);
189
+ }
190
+ lines.push("");
191
+ lines.push(report.ok ? "Overall: OK" : "Overall: issues found (see errors above)");
192
+ return lines.join("\n");
193
+ }
194
+ //#endregion
195
+ export { formatDoctorReport, runDoctor };
@@ -0,0 +1,52 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { fileURLToPath } from "node:url";
3
+ import { dirname, isAbsolute, resolve } from "node:path";
4
+ import { existsSync } from "node:fs";
5
+ //#region src/lib/find-tui-binary.ts
6
+ /**
7
+ * Locate the `diffing-tui` native binary. Looks, in order:
8
+ * 1. Sibling of the calling module (`dist/diffing-tui[.exe]` after build).
9
+ * 2. `bin/diffing-tui[.exe]` next to the package root.
10
+ * 3. `target/release/diffing-tui[.exe]` next to the package root
11
+ * (cargo release build).
12
+ * 4. `target/debug/diffing-tui[.exe]` next to the package root
13
+ * (cargo debug build — the common case during development, especially
14
+ * on Windows where release builds are slow).
15
+ * 5. `$PATH` lookup via `which` / `where`.
16
+ *
17
+ * Returns the absolute path of the first match, or `null` if none are found.
18
+ *
19
+ * `callerUrl` is the `import.meta.url` of the caller — pass `import.meta.url`
20
+ * from `cli.ts`. Exposed as a parameter so unit tests can pin the search
21
+ * root to a known location instead of depending on the test runner's CWD.
22
+ */
23
+ function findTuiBinary(callerUrl) {
24
+ const ext = process.platform === "win32" ? ".exe" : "";
25
+ const here = dirname(fileURLToPath(callerUrl));
26
+ const candidates = [
27
+ resolve(here, `diffing-tui${ext}`),
28
+ resolve(here, "..", "bin", `diffing-tui${ext}`),
29
+ resolve(here, "..", "target", "release", `diffing-tui${ext}`),
30
+ resolve(here, "..", "..", "target", "release", `diffing-tui${ext}`),
31
+ resolve(here, "..", "target", "debug", `diffing-tui${ext}`),
32
+ resolve(here, "..", "..", "target", "debug", `diffing-tui${ext}`)
33
+ ];
34
+ for (const c of candidates) if (existsSync(c)) return c;
35
+ try {
36
+ const out = execFileSync(process.platform === "win32" ? "where" : "which", ["diffing-tui"], {
37
+ encoding: "utf-8",
38
+ stdio: [
39
+ "ignore",
40
+ "pipe",
41
+ "ignore"
42
+ ]
43
+ }).trim();
44
+ if (out) {
45
+ const first = out.split(/\r?\n/)[0]?.trim();
46
+ if (first && isAbsolute(first)) return first;
47
+ }
48
+ } catch {}
49
+ return null;
50
+ }
51
+ //#endregion
52
+ export { findTuiBinary as t };