diffing 0.10.5 → 0.12.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 (182) hide show
  1. package/README.md +76 -41
  2. package/dist/{cli-agent-BJiATn11.mjs → cli-agent-CWsOHN-j.mjs} +198 -57
  3. package/dist/{cli-gh-_bkDCxhh.mjs → cli-gh-DI69gJGo.mjs} +27 -17
  4. package/dist/cli.mjs +189 -229
  5. package/dist/client/assets/{angular-html-CBcwKYDC.js → angular-html-PLPuQo0k.js} +1 -1
  6. package/dist/client/assets/{angular-ts-DRBYUaJA.js → angular-ts-CE02Ij4b.js} +1 -1
  7. package/dist/client/assets/{apl-BM0X8jeL.js → apl-CsuUKZoi.js} +1 -1
  8. package/dist/client/assets/{arc-DTkmmKl3.js → arc-CgL5kFe-.js} +1 -1
  9. package/dist/client/assets/{architectureDiagram-3BPJPVTR-Cw75ay7q.js → architectureDiagram-3BPJPVTR-DxZQWGnx.js} +1 -1
  10. package/dist/client/assets/{astro-DKTNkoRZ.js → astro-DkFc7sbz.js} +1 -1
  11. package/dist/client/assets/{blade-DaM7a9Zp.js → blade-BkY07kcC.js} +1 -1
  12. package/dist/client/assets/{blockDiagram-GPEHLZMM-DAzINMr_.js → blockDiagram-GPEHLZMM-D5a2KZV2.js} +1 -1
  13. package/dist/client/assets/{c-B3C0Or4o.js → c-T1BrPfJ6.js} +1 -1
  14. package/dist/client/assets/{c4Diagram-AAUBKEIU-C7SsS28g.js → c4Diagram-AAUBKEIU-mpMH4kFC.js} +1 -1
  15. package/dist/client/assets/channel-D3niKhl0.js +1 -0
  16. package/dist/client/assets/{chunk-2J33WTMH-DHOUGWx8.js → chunk-2J33WTMH-vMtgRLFH.js} +1 -1
  17. package/dist/client/assets/{chunk-3OPIFGDE-D8d2scLX.js → chunk-3OPIFGDE-Dx-FqyO2.js} +1 -1
  18. package/dist/client/assets/{chunk-4BX2VUAB-BSC-E5yg.js → chunk-4BX2VUAB-CUrPd8Kv.js} +1 -1
  19. package/dist/client/assets/{chunk-55IACEB6-27pC5pKn.js → chunk-55IACEB6-DHg3Nr8A.js} +1 -1
  20. package/dist/client/assets/{chunk-5ZQYHXKU-BM60x82y.js → chunk-5ZQYHXKU-CpDPvbGs.js} +1 -1
  21. package/dist/client/assets/{chunk-727SXJPM-CHCF96vE.js → chunk-727SXJPM-3VOGMxwk.js} +1 -1
  22. package/dist/client/assets/{chunk-AQP2D5EJ-BoVsvmTa.js → chunk-AQP2D5EJ-4dbfbIS6.js} +1 -1
  23. package/dist/client/assets/{chunk-BSJP7CBP-Iip4uSio.js → chunk-BSJP7CBP-Dfyn0Uwp.js} +1 -1
  24. package/dist/client/assets/{chunk-CSCIHK7Q-DuIWPyay.js → chunk-CSCIHK7Q-VTpSKo0F.js} +1 -1
  25. package/dist/client/assets/{chunk-FMBD7UC4-Du6rtd2k.js → chunk-FMBD7UC4-kXD1MyOL.js} +1 -1
  26. package/dist/client/assets/{chunk-KSCS5N6A-BBq105vI.js → chunk-KSCS5N6A-BrVoWOAb.js} +1 -1
  27. package/dist/client/assets/{chunk-L5ZTLDWV-0DmB5Qp8.js → chunk-L5ZTLDWV-wZEQDPsa.js} +1 -1
  28. package/dist/client/assets/{chunk-LZXEDZCA-CoRsjUfr.js → chunk-LZXEDZCA-D3i0B0eg.js} +2 -2
  29. package/dist/client/assets/{chunk-ND2GUHAM-tX8cKEls.js → chunk-ND2GUHAM-hDVHT0bN.js} +1 -1
  30. package/dist/client/assets/{chunk-NZK2D7GU-BoJGgPic.js → chunk-NZK2D7GU-jQUfhp9h.js} +1 -1
  31. package/dist/client/assets/{chunk-O5CBEL6O-mlwCWs97.js → chunk-O5CBEL6O-CX_vPWhk.js} +1 -1
  32. package/dist/client/assets/chunk-QZHKN3VN-CZLFuGpt.js +1 -0
  33. package/dist/client/assets/chunk-WU5MYG2G-BhcpjkaB.js +1 -0
  34. package/dist/client/assets/{chunk-XPW4576I-B5efww8M.js → chunk-XPW4576I-Co_-vDB4.js} +1 -1
  35. package/dist/client/assets/classDiagram-4FO5ZUOK-DK5KJNIt.js +1 -0
  36. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-y5K9t6Se.js +1 -0
  37. package/dist/client/assets/{cobol-De9QR_Td.js → cobol-8-qBLs-a.js} +1 -1
  38. package/dist/client/assets/{coffee-Ct_GvuHP.js → coffee-Bjz5lkyW.js} +1 -1
  39. package/dist/client/assets/{cose-bilkent-S5V4N54A-ATaJZGF7.js → cose-bilkent-S5V4N54A-BljJ0FSA.js} +1 -1
  40. package/dist/client/assets/{cpp-Dw52w9cj.js → cpp-5BVQCBXT.js} +1 -1
  41. package/dist/client/assets/{crystal-Dx8ADdsd.js → crystal-CCkY93GS.js} +1 -1
  42. package/dist/client/assets/{css-CZ13XvDU.js → css-BTM7Tp2a.js} +1 -1
  43. package/dist/client/assets/{dagre-BM42HDAG-aDltuYut.js → dagre-BM42HDAG-BSynd_2D.js} +1 -1
  44. package/dist/client/assets/{diagram-2AECGRRQ-DGWnHVMJ.js → diagram-2AECGRRQ-K3E2CVdy.js} +1 -1
  45. package/dist/client/assets/{diagram-5GNKFQAL-Dd7eZBKe.js → diagram-5GNKFQAL-BQHIrfKc.js} +1 -1
  46. package/dist/client/assets/{diagram-KO2AKTUF-DWThXREf.js → diagram-KO2AKTUF-Bqs8sNN4.js} +1 -1
  47. package/dist/client/assets/{diagram-LMA3HP47-CXR4Bjtc.js → diagram-LMA3HP47-C9K4bpQe.js} +1 -1
  48. package/dist/client/assets/{diagram-OG6HWLK6-D4Cbf0nW.js → diagram-OG6HWLK6-DK6wIXY2.js} +1 -1
  49. package/dist/client/assets/{dist-CUDhrHNw.js → dist-1nkZYMRy.js} +1 -1
  50. package/dist/client/assets/{edge-CQCjdJKj.js → edge-DqN2FJce.js} +1 -1
  51. package/dist/client/assets/{elixir-bkjwPazA.js → elixir-BiAgRUFF.js} +1 -1
  52. package/dist/client/assets/{elm-CCweX5Cq.js → elm-N1IbY8sz.js} +1 -1
  53. package/dist/client/assets/{erDiagram-TEJ5UH35-BmAyt7SU.js → erDiagram-TEJ5UH35-jsHCiP02.js} +1 -1
  54. package/dist/client/assets/{erb-D-Z-KBLp.js → erb-B-yYlWAZ.js} +1 -1
  55. package/dist/client/assets/{flowDiagram-I6XJVG4X-C6nIFUVc.js → flowDiagram-I6XJVG4X-DOB1dAWG.js} +1 -1
  56. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DMlxThBf.js → ganttDiagram-6RSMTGT7-BYIewcY5.js} +1 -1
  57. package/dist/client/assets/{git-rebase-BdoFdenB.js → git-rebase-CVkPxOAF.js} +1 -1
  58. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-JaIFkAIT.js → gitGraphDiagram-PVQCEYII-VNQzbpv2.js} +1 -1
  59. package/dist/client/assets/{glimmer-js-CZQrBJKq.js → glimmer-js-BmG1xm72.js} +1 -1
  60. package/dist/client/assets/{glimmer-ts-CBYW46D4.js → glimmer-ts-CvTgwZsg.js} +1 -1
  61. package/dist/client/assets/{glsl-wxixKkY_.js → glsl-DwjNNCbW.js} +1 -1
  62. package/dist/client/assets/{graphql-CqZ0Lo5a.js → graphql-BvgkyF8E.js} +1 -1
  63. package/dist/client/assets/{hack-rqWymQRV.js → hack-DH3188x2.js} +1 -1
  64. package/dist/client/assets/{haml-DFw_IoGM.js → haml-B0GTfMhX.js} +1 -1
  65. package/dist/client/assets/{handlebars-3VmTVMYt.js → handlebars-he7hLDd9.js} +1 -1
  66. package/dist/client/assets/{html-C0Li76jB.js → html-DIYgdTBq.js} +1 -1
  67. package/dist/client/assets/{html-derivative-CO-RQW8y.js → html-derivative-2s6MXlci.js} +1 -1
  68. package/dist/client/assets/{http-sPrZOeBY.js → http-COS23XJP.js} +1 -1
  69. package/dist/client/assets/{hurl-DUoPe4Lc.js → hurl-BlZPjeJt.js} +1 -1
  70. package/dist/client/assets/index-BrEVVQOB.js +4021 -0
  71. package/dist/client/assets/index-BsuX6HVI.css +1 -0
  72. package/dist/client/assets/{infoDiagram-5YYISTIA-B6AZ5SCV.js → infoDiagram-5YYISTIA-DQRqk24I.js} +1 -1
  73. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CytMrpo8.js → ishikawaDiagram-YF4QCWOH-DU1rqrLx.js} +1 -1
  74. package/dist/client/assets/{java-Cj0G6QRg.js → java-BAa5w-Qd.js} +1 -1
  75. package/dist/client/assets/{javascript-d5SUuyWD.js → javascript-C9e02ssI.js} +1 -1
  76. package/dist/client/assets/{jinja-BTw9k0vO.js → jinja-Bx37xXwq.js} +1 -1
  77. package/dist/client/assets/{jison-9xSVvR1q.js → jison-m_DEf8Jo.js} +1 -1
  78. package/dist/client/assets/{journeyDiagram-JHISSGLW-Bd4jZUYI.js → journeyDiagram-JHISSGLW-CoHlfevf.js} +1 -1
  79. package/dist/client/assets/{json-DjAt53ka.js → json-DtN5AWRW.js} +1 -1
  80. package/dist/client/assets/{jsx-BX5KmQ7d.js → jsx-BlPpu8bR.js} +1 -1
  81. package/dist/client/assets/{julia-CLLx1dHC.js → julia-BAOmCbHl.js} +1 -1
  82. package/dist/client/assets/{just-C0v80bKe.js → just-C_bvVjwE.js} +1 -1
  83. package/dist/client/assets/{kanban-definition-UN3LZRKU-Ds4pXHJa.js → kanban-definition-UN3LZRKU-BiqYI7_m.js} +1 -1
  84. package/dist/client/assets/{latex-DY-oQfwO.js → latex-81pMfNMt.js} +1 -1
  85. package/dist/client/assets/{line-BbSLfOl6.js → line-DCovs9My.js} +1 -1
  86. package/dist/client/assets/{linear-BMIZJAAR.js → linear-cJF5XHE7.js} +1 -1
  87. package/dist/client/assets/{liquid-DDHDgbZx.js → liquid-sKRTk6dT.js} +1 -1
  88. package/dist/client/assets/{lua-CXymerKS.js → lua-C1uZL4IF.js} +1 -1
  89. package/dist/client/assets/{marko-DoPFsptl.js → marko-BKEzP1CL.js} +1 -1
  90. package/dist/client/assets/{mdc-BxSmTphd.js → mdc-CLkfm94H.js} +1 -1
  91. package/dist/client/assets/{mermaid-parser.core-CcYSvN4q.js → mermaid-parser.core-CTsTbvb2.js} +1 -1
  92. package/dist/client/assets/{mermaid.core-COTNtqgQ.js → mermaid.core-D86HJEwL.js} +3 -3
  93. package/dist/client/assets/{mindmap-definition-RKZ34NQL-BTxraRzn.js → mindmap-definition-RKZ34NQL-CL3heUmL.js} +1 -1
  94. package/dist/client/assets/{nginx-Bma0jNOT.js → nginx-eDGm66pc.js} +1 -1
  95. package/dist/client/assets/{nim-DMBpjygU.js → nim-CoWZPv8-.js} +1 -1
  96. package/dist/client/assets/{perl-CrnrV-AJ.js → perl-MoMqtMVr.js} +1 -1
  97. package/dist/client/assets/{php-CDYxzX6t.js → php-DXXVMAtN.js} +1 -1
  98. package/dist/client/assets/{pieDiagram-4H26LBE5-DglRmzer.js → pieDiagram-4H26LBE5-CWV3TMu4.js} +1 -1
  99. package/dist/client/assets/{pug-DNewy3GU.js → pug-CPWaB94J.js} +1 -1
  100. package/dist/client/assets/{qml-COjcnZ-W.js → qml-lhvpx8vk.js} +1 -1
  101. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BjSmyES7.js → quadrantDiagram-W4KKPZXB-QV25BYos.js} +1 -1
  102. package/dist/client/assets/{r-CrCaMxLS.js → r-DYbUnmmK.js} +1 -1
  103. package/dist/client/assets/{razor-jeuOZIar.js → razor-DcyOLv96.js} +1 -1
  104. package/dist/client/assets/{regexp-DSHVUX08.js → regexp-BdY10jm0.js} +1 -1
  105. package/dist/client/assets/{requirementDiagram-4Y6WPE33-B4f430jS.js → requirementDiagram-4Y6WPE33-BPMsmU6J.js} +1 -1
  106. package/dist/client/assets/{review-export-Cen1WEcX.js → review-export-CMwjJURs.js} +1 -1
  107. package/dist/client/assets/{rst-CIDgytzc.js → rst-6ShFOzgH.js} +1 -1
  108. package/dist/client/assets/{ruby-DtuseoNq.js → ruby-B04DeVbi.js} +1 -1
  109. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-TQPPlxc6.js → sankeyDiagram-5OEKKPKP-DQJeroPp.js} +1 -1
  110. package/dist/client/assets/{sas-C72Theia.js → sas-Dj26ftNa.js} +1 -1
  111. package/dist/client/assets/{scss-ClECtwRk.js → scss-BOHpBkgl.js} +1 -1
  112. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-D1tx07Wg.js → sequenceDiagram-3UESZ5HK-DdV0Y6Sz.js} +1 -1
  113. package/dist/client/assets/{shellscript-BCwBsi2k.js → shellscript-CpHwjRcU.js} +1 -1
  114. package/dist/client/assets/{shellsession-BrOddEl5.js → shellsession-BJAvf4GN.js} +1 -1
  115. package/dist/client/assets/{soy-Dcs0GWFW.js → soy-B67bZJnL.js} +1 -1
  116. package/dist/client/assets/{sql-BAjA7Gv7.js → sql-8MzOqSsK.js} +1 -1
  117. package/dist/client/assets/{src-CYYhkqTK.js → src-AaoRSmFl.js} +1 -1
  118. package/dist/client/assets/{stata-19ngQtzR.js → stata-aCME945h.js} +1 -1
  119. package/dist/client/assets/{stateDiagram-AJRCARHV-BZRCEzb5.js → stateDiagram-AJRCARHV-CMD_Xj2M.js} +1 -1
  120. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-vLl7D4ue.js +1 -0
  121. package/dist/client/assets/{surrealql-Ce2TBRjE.js → surrealql-B_V_HxAi.js} +1 -1
  122. package/dist/client/assets/{svelte-DQ_1eGZz.js → svelte-BcQvTlqz.js} +1 -1
  123. package/dist/client/assets/{templ-Cw2DDxWF.js → templ-DZCQ2blz.js} +1 -1
  124. package/dist/client/assets/{tex-myDSO2eP.js → tex-BfeQ5Wuu.js} +1 -1
  125. package/dist/client/assets/{timeline-definition-PNZ67QCA-CP0OZhBT.js → timeline-definition-PNZ67QCA-CyIS7Zoe.js} +1 -1
  126. package/dist/client/assets/{ts-tags-rh39ZwVs.js → ts-tags-DVWE0E87.js} +1 -1
  127. package/dist/client/assets/{tsx-B0-26jrU.js → tsx-DdJ7Sfqf.js} +1 -1
  128. package/dist/client/assets/{twig-CgasHzPb.js → twig-_gVgaha5.js} +1 -1
  129. package/dist/client/assets/{typescript-Dq3T0c-m.js → typescript-Bj0_LzZV.js} +1 -1
  130. package/dist/client/assets/{vennDiagram-CIIHVFJN-DHaKHj0j.js → vennDiagram-CIIHVFJN-C-J2kWyk.js} +1 -1
  131. package/dist/client/assets/{vue-CzFVh92-.js → vue-Dnt_GBRb.js} +1 -1
  132. package/dist/client/assets/{vue-html-Bv2uFkTH.js → vue-html-ClVA5__5.js} +1 -1
  133. package/dist/client/assets/{vue-vine-DzieBk9Y.js → vue-vine-BCcz4SVP.js} +1 -1
  134. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Dky7jxlZ.js → wardleyDiagram-YWT4CUSO-Dzt4EPh8.js} +1 -1
  135. package/dist/client/assets/{xml-wcdHRHN8.js → xml-BYeUHEti.js} +1 -1
  136. package/dist/client/assets/{xsl-CXrwsLG9.js → xsl-Vsx0SZEV.js} +1 -1
  137. package/dist/client/assets/{xychartDiagram-2RQKCTM6-BQ2hG60h.js → xychartDiagram-2RQKCTM6-BAWZKvwP.js} +1 -1
  138. package/dist/client/assets/{yaml-lO6lK6Xe.js → yaml-C6L1khMK.js} +1 -1
  139. package/dist/client/index.html +2 -2
  140. package/dist/{completions-3UF8uzhx.mjs → completions-DvyV49HY.mjs} +22 -1
  141. package/dist/{doctor-BGYZNIYP.mjs → doctor-Bz9VHuBI.mjs} +7 -6
  142. package/dist/find-tui-binary-DVQADOSF.mjs +101 -0
  143. package/dist/{git-Czo96ymM.mjs → git-BGj31GX-.mjs} +71 -7
  144. package/dist/git-CfDSkUcE.mjs +2 -0
  145. package/dist/{github-DKT0lJXv.mjs → github-DXiEeHzk.mjs} +1 -1
  146. package/dist/{github-CEjtGd41.mjs → github-DpzqNrUh.mjs} +3 -3
  147. package/dist/{github-attachments-Cf17YWp4.mjs → github-attachments-DgOf7BhP.mjs} +2 -2
  148. package/dist/{mcp-Dq7J5QOP.mjs → mcp-zDVzU1vl.mjs} +42 -33
  149. package/dist/native/tui-darwin-arm64/diffing-tui +0 -0
  150. package/dist/native/tui-darwin-x64/diffing-tui +0 -0
  151. package/dist/native/tui-linux-arm64-gnu/diffing-tui +0 -0
  152. package/dist/native/tui-linux-arm64-musl/diffing-tui +0 -0
  153. package/dist/native/tui-linux-x64-gnu/diffing-tui +0 -0
  154. package/dist/native/tui-linux-x64-musl/diffing-tui +0 -0
  155. package/dist/native/tui-win32-x64-msvc/diffing-tui.exe +0 -0
  156. package/dist/{review-export-BBDWvftT.mjs → review-export-B63eRAVa.mjs} +1 -1
  157. package/dist/{settings-bwe7yQ8U.mjs → search-3R2E5xxr.mjs} +133 -98
  158. package/dist/search-CQ4nsjBX.mjs +2 -0
  159. package/dist/{server-C9i4V36o.mjs → server-Dw3lkQ5q.mjs} +296 -110
  160. package/dist/server-lock-BNI8FKV1.mjs +427 -0
  161. package/dist/session-conflict-Bqt1M4Mp.mjs +96 -0
  162. package/dist/session-manager-BKSZQqDJ.mjs +159 -0
  163. package/dist/session-url-sr7lXXVc.mjs +100 -0
  164. package/dist/tui-search-bridge-BGgLCwMF.mjs +133 -0
  165. package/dist/{update-check-DUuHmPeZ.mjs → update-check-BUfh4Y6c.mjs} +16 -15
  166. package/package.json +9 -4
  167. package/dist/client/assets/channel-lsVHTuYR.js +0 -1
  168. package/dist/client/assets/chunk-QZHKN3VN-AHUiTM0z.js +0 -1
  169. package/dist/client/assets/chunk-WU5MYG2G-D41j4yxb.js +0 -1
  170. package/dist/client/assets/classDiagram-4FO5ZUOK-D1Eo3kly.js +0 -1
  171. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-bAMsbsfB.js +0 -1
  172. package/dist/client/assets/index-B42c0TyV.css +0 -1
  173. package/dist/client/assets/index-BN_-di_r.js +0 -3959
  174. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Da-mGMA2.js +0 -1
  175. package/dist/find-tui-binary-CRpbYXLC.mjs +0 -52
  176. package/dist/git-DaP_1rVy.mjs +0 -2
  177. package/dist/server-lock-Dd_ss_Il.mjs +0 -153
  178. /package/dist/{apply-suggestion-DNNsC5gx.mjs → apply-suggestion-CHYnw68I.mjs} +0 -0
  179. /package/dist/{comment-format-B5AcGcR-.mjs → comment-format-B85NHZnG.mjs} +0 -0
  180. /package/dist/{diff-fingerprint-C8YO3TTE.mjs → diff-fingerprint-BgR0tu1o.mjs} +0 -0
  181. /package/dist/{handoff-DPoIyeWn.mjs → handoff-LAFtmnHf.mjs} +0 -0
  182. /package/dist/{plan-format-BQyx--mp.mjs → plan-format-BwJh5JSE.mjs} +0 -0
@@ -1,8 +1,10 @@
1
- import { C as isSafePath, S as revertHunk, _ as getUntrackedFilePathsAsync, a as getCustomGitDiffAsync, b as listRepoFiles, c as getGitDiffAsync, d as getProjectStorageDir, f as getRepoName, g as getTabSizeForFiles, h as getShowDiff, i as getCommitSeriesSummary, l as getHunkHistory, o as getFileContent, p as getRepoRoot, r as getBranchName, u as getMergeStatus, v as gitAddFile, w as toSafeRelativePath, y as isGitRepo } from "./git-Czo96ymM.mjs";
2
- import { a as searchContent, c as trackSelection, i as searchAll, n as saveSettings, o as searchFiles, r as getSearchStatus, s as searchSymbols, t as loadSettings } from "./settings-bwe7yQ8U.mjs";
3
- import { t as formatComments } from "./comment-format-B5AcGcR-.mjs";
4
- import { n as formatPlanReview, r as sectionTitleForLine, t as extractPlanLines } from "./plan-format-BQyx--mp.mjs";
5
- import { D as updatePrReviewComment, E as submitReview, S as refreshPrSession, T as setPrReviewThreadResolved, a as detectCwdRepo, b as parsePrRef, f as fetchPrFileContentViaGh, i as deletePrReviewComment, l as fetchExistingCommentsViaGh, t as buildPrSession, u as fetchExistingReviewsViaGh, w as resolvedFromSession } from "./github-CEjtGd41.mjs";
1
+ import { C as listRepoFiles, D as toSafeRelativePath, E as isSafePath, S as isGitRepo, T as revertHunk, _ as getShowDiff, a as getCommitSeriesSummary, b as getUntrackedFilePathsAsync, d as getMergeStatus, f as getProjectStorageDir, h as getRepoRoot, l as getGitDiffAsync, m as getRepoMetadataAsync, o as getCustomGitDiffAsync, s as getFileContent, u as getHunkHistory, x as gitAddFile, y as getTabSizeForFilesAsync } from "./git-BGj31GX-.mjs";
2
+ import { a as searchFiles, i as searchContent, o as searchSymbols, r as searchAll, s as trackSelection, t as getSearchStatus } from "./search-3R2E5xxr.mjs";
3
+ import { a as loadSettings, o as saveSettings } from "./session-url-sr7lXXVc.mjs";
4
+ import { t as formatComments } from "./comment-format-B85NHZnG.mjs";
5
+ import { n as formatPlanReview, r as sectionTitleForLine, t as extractPlanLines } from "./plan-format-BwJh5JSE.mjs";
6
+ import { _ as buildSessionTokenSetCookieValue, b as injectSessionTokenIntoHtml, v as createServerAuthMiddleware } from "./server-lock-BNI8FKV1.mjs";
7
+ import { D as updatePrReviewComment, E as submitReview, S as refreshPrSession, T as setPrReviewThreadResolved, a as detectCwdRepo, b as parsePrRef, f as fetchPrFileContentViaGh, i as deletePrReviewComment, l as fetchExistingCommentsViaGh, t as buildPrSession, u as fetchExistingReviewsViaGh, w as resolvedFromSession } from "./github-DpzqNrUh.mjs";
6
8
  import { execFileSync } from "node:child_process";
7
9
  import { createHash } from "node:crypto";
8
10
  import { fileURLToPath } from "node:url";
@@ -34,6 +36,7 @@ const DEFAULTS = {
34
36
  pickaxeAll: false,
35
37
  exitCode: false,
36
38
  quiet: false,
39
+ suppressPatch: false,
37
40
  noPrefix: false,
38
41
  binary: false,
39
42
  fullIndex: false,
@@ -51,6 +54,7 @@ const DEFAULTS = {
51
54
  outputMode: "auto",
52
55
  host: "127.0.0.1",
53
56
  noOpen: false,
57
+ insecureNoAuth: false,
54
58
  reuseSession: false,
55
59
  replaceSession: false,
56
60
  help: false,
@@ -58,6 +62,7 @@ const DEFAULTS = {
58
62
  includeUnstaged: true,
59
63
  includeUntracked: true,
60
64
  tui: false,
65
+ viewOnly: false,
61
66
  gpu: false,
62
67
  showMode: false,
63
68
  showRevspecs: []
@@ -181,6 +186,10 @@ const DIFFING_OPTIONS = {
181
186
  type: "boolean",
182
187
  default: false
183
188
  },
189
+ "insecure-no-auth": {
190
+ type: "boolean",
191
+ default: false
192
+ },
184
193
  "reuse-session": {
185
194
  type: "boolean",
186
195
  default: false
@@ -201,6 +210,10 @@ const DIFFING_OPTIONS = {
201
210
  type: "boolean",
202
211
  default: false
203
212
  },
213
+ view: {
214
+ type: "boolean",
215
+ default: false
216
+ },
204
217
  gpu: {
205
218
  type: "boolean",
206
219
  default: false
@@ -226,6 +239,7 @@ function printHelp() {
226
239
 
227
240
  Usage: diffing [<git diff options>] [<revision>...] [-- <path>...]
228
241
  diffing --web [<git diff options>] [<revision>...] [-- <path>...]
242
+ diffing view [<git diff options>] [<revision>...] [-- <path>...]
229
243
  diffing [server options]
230
244
 
231
245
  Git Diff Options (drop-in replacement for git diff):`);
@@ -233,26 +247,33 @@ Git Diff Options (drop-in replacement for git diff):`);
233
247
  console.log(`
234
248
  Diffing Server Options:
235
249
  --port <port> Port to run the server on (default: random available port)
236
- --host <host> Host address to bind to (default: 127.0.0.1). Pass
237
- 0.0.0.0 to expose the server to the local network.
250
+ --host <host> Host address to bind to (default: 127.0.0.1). Loopback
251
+ binds require a per-session API token. To expose the
252
+ review UI on your LAN, pass 0.0.0.0 together with
253
+ --insecure-no-auth (disables API authentication).
254
+ --insecure-no-auth Allow binding to 0.0.0.0 without API tokens (unsafe on
255
+ shared networks). Ignored on loopback binds.
238
256
  --no-open Don't open the browser automatically
239
- --reuse-session If a review is already running, open it instead of prompting
240
- --replace-session If a review is already running, stop it and start a new one
257
+ --reuse-session Open the active review instead of starting another
258
+ --replace-session Stop the active review and start a replacement
241
259
  --gh-pr <ref> Open a GitHub PR review session (number, owner/repo#N, or URL)
242
260
  Same as: diffing "gh pr <ref>"
243
261
 
244
262
  Output Modes:
245
263
  By default diffing auto-selects the best output mode:
246
264
  - Terminal mode: when output is a pipe, redirect, or non-TTY
247
- - Web mode: when output is a TTY (interactive terminal)
265
+ - Web or TUI: saved preference for an interactive terminal (web by default)
248
266
 
249
- Force a mode with --web, --terminal, or --tui flags.
267
+ Force a mode with --web, --terminal, --tui, or --view flags.
268
+ Change the interactive default with: diffing mode <web|tui>
269
+ --view opens a focused, read-only diff browser with fff-powered search.
250
270
  --tui is experimental and opt-in. It opens a native-Rust terminal
251
271
  interface that mirrors the web dashboard. Falls back to git diff
252
272
  output if the environment cannot support a TUI (piped stdin, CI,
253
273
  missing binary).
254
274
 
255
275
  Subcommands:
276
+ view [diff options] Browse diffs in the focused read-only native TUI
256
277
  show <revspec>... Drop-in for 'git show'. Renders commit metadata
257
278
  above the diff. Accepts commits, ranges, tags.
258
279
  await-review Block until human clicks Send to agent; print XML
@@ -262,11 +283,13 @@ Subcommands:
262
283
  comment edit|delete <id>
263
284
  progress --message … Live agent progress toast
264
285
  url Print the active review base URL
286
+ sessions List, select, open, and stop running review sessions
265
287
  plan <submit|await|list|show|versions|reply|resolve>
266
288
  gh <status|overview|threads|reviews|pr-fetch|pr-list-comments|pr-review>
267
289
  mcp [--repo PATH] Stdio MCP server for agents
268
290
  inspect <summary|files|hunks|slice|search>
269
291
  Bounded reads from a running web, TUI, or PR session
292
+ mode [web|tui] Get or set the default interactive review mode
270
293
  doctor Environment self-check
271
294
  completion <bash|zsh|fish>
272
295
  update Check for a newer npm release
@@ -275,7 +298,7 @@ Custom ranges (e.g. main..feature) and 'gh pr' sessions also display a
275
298
  "what is this diff" banner in the web UI summarising the diff source.
276
299
 
277
300
  Examples:
278
- diffing Review uncommitted changes (web UI)
301
+ diffing Review uncommitted changes (preferred UI)
279
302
  diffing --staged Review staged changes
280
303
  diffing HEAD~3 Review last 3 commits
281
304
  diffing main..feature Compare branches
@@ -287,8 +310,13 @@ Examples:
287
310
  diffing -U10 Diff with 10 context lines
288
311
  diffing --word-diff=color Word-level diff in color
289
312
  diffing -b -w Ignore whitespace changes
290
- diffing --host 0.0.0.0 Allow other machines on the LAN to review
313
+ diffing --host 0.0.0.0 --insecure-no-auth
314
+ Expose the review UI on the LAN without API tokens
315
+ diffing view Browse the working-tree diff in the TUI
316
+ diffing view --staged Browse staged changes
317
+ diffing --view main..feature Browse a branch comparison
291
318
  diffing --tui Open the experimental TUI (native Rust)
319
+ diffing mode tui Use the TUI by default in interactive terminals
292
320
  diffing await-review Agent: wait for human handoff
293
321
  diffing plan submit PLAN.md Agent: submit a plan for approval
294
322
  diffing mcp --repo /abs/path MCP server bound to one repository
@@ -437,6 +465,15 @@ function preprocessOptionalValueArgs(rawArgs) {
437
465
  "find-renames",
438
466
  "break-rewrites"
439
467
  ]);
468
+ /** Git accepts values only as `--flag=value` for these; never `--flag value`. */
469
+ const ATTACHED_ONLY_LONGS = new Set([
470
+ "stat",
471
+ "dirstat",
472
+ "relative",
473
+ "word-diff",
474
+ "color-moved",
475
+ "submodule"
476
+ ]);
440
477
  const startsWithDigit = (s) => typeof s === "string" && /^[0-9]/.test(s);
441
478
  const out = [];
442
479
  for (let i = 0; i < rawArgs.length; i++) {
@@ -465,6 +502,13 @@ function preprocessOptionalValueArgs(rawArgs) {
465
502
  }
466
503
  continue;
467
504
  }
505
+ const attachedOnlyMatch = /^--(stat|dirstat|relative|word-diff|color-moved|submodule)$/.exec(arg);
506
+ if (attachedOnlyMatch) {
507
+ const name = attachedOnlyMatch[1];
508
+ if (ATTACHED_ONLY_LONGS.has(name)) out.push(`--${name}=`);
509
+ else out.push(arg);
510
+ continue;
511
+ }
468
512
  out.push(arg);
469
513
  }
470
514
  return out;
@@ -479,7 +523,7 @@ function preprocessOptionalValueArgs(rawArgs) {
479
523
  * 3. Parse known flags via node:util.parseArgs
480
524
  * 4. Anything unknown / positional becomes a revision or pathspec
481
525
  */
482
- function parseDiffOptions(rawArgs) {
526
+ function parseDiffOptions(rawArgs, defaultInteractiveMode = "web") {
483
527
  const { values, positionals } = parseArgs({
484
528
  options: {
485
529
  ...GIT_DIFF_OPTIONS,
@@ -500,9 +544,11 @@ function parseDiffOptions(rawArgs) {
500
544
  if (values.port) opts.port = parseInt(values.port, 10);
501
545
  if (values.host) opts.host = values.host;
502
546
  if (values["no-open"]) opts.noOpen = true;
547
+ if (values["insecure-no-auth"]) opts.insecureNoAuth = true;
503
548
  if (values["reuse-session"]) opts.reuseSession = true;
504
549
  if (values["replace-session"]) opts.replaceSession = true;
505
550
  if (values["gh-pr"]) opts.ghPr = String(values["gh-pr"]);
551
+ if (values.view) opts.viewOnly = true;
506
552
  if (values.staged || values.cached) opts.staged = true;
507
553
  if (values.merge) opts.merge = true;
508
554
  const algo = values["diff-algorithm"];
@@ -576,7 +622,11 @@ function parseDiffOptions(rawArgs) {
576
622
  if (values.raw) opts.outputFormat = "raw";
577
623
  if (values.numstat) opts.outputFormat = "numstat";
578
624
  if (values.shortstat) opts.outputFormat = "shortstat";
579
- if (values.stat !== void 0) opts.outputFormat = "stat";
625
+ if (values.stat !== void 0) {
626
+ opts.outputFormat = "stat";
627
+ const statVal = values.stat;
628
+ if (statVal) opts.statParam = statVal;
629
+ }
580
630
  if (values.summary) opts.outputFormat = "summary";
581
631
  if (values["name-only"]) opts.outputFormat = "name-only";
582
632
  if (values["name-status"]) opts.outputFormat = "name-status";
@@ -586,8 +636,14 @@ function parseDiffOptions(rawArgs) {
586
636
  if (values.dirstat) opts.dirstat = values.dirstat;
587
637
  if (values["dirstat-by-file"]) opts.dirstatByFile = values["dirstat-by-file"];
588
638
  if (values.cumulative) opts.cumulative = true;
589
- if (values["no-patch"]) opts.outputFormat = void 0;
590
- if (values.check) opts.outputFormat = void 0;
639
+ if (values["no-patch"]) {
640
+ opts.outputFormat = void 0;
641
+ opts.suppressPatch = true;
642
+ }
643
+ if (values.check) {
644
+ opts.outputFormat = void 0;
645
+ opts.check = true;
646
+ }
591
647
  if (values["diff-filter"]) opts.diffFilter = values["diff-filter"];
592
648
  if (values.S) opts.pickaxeString = values.S;
593
649
  if (values.G) opts.pickaxeRegex = values.G;
@@ -630,18 +686,17 @@ function parseDiffOptions(rawArgs) {
630
686
  }
631
687
  const dashDashIdx = rawArgs.indexOf("--");
632
688
  if (dashDashIdx >= 0) {
633
- for (let i = 0; i < dashDashIdx; i++) {
634
- const arg = rawArgs[i];
635
- if (!arg.startsWith("-")) opts.revisions.push(arg);
636
- }
637
689
  for (let i = dashDashIdx + 1; i < rawArgs.length; i++) opts.pathspecs.push(rawArgs[i]);
690
+ const pathspecSet = new Set(opts.pathspecs);
691
+ for (const p of positionals) if (!pathspecSet.has(p)) opts.revisions.push(p);
638
692
  } else for (const p of positionals) opts.revisions.push(p);
639
- if (values.tui) opts.outputMode = "tui";
693
+ if (values.view) opts.outputMode = "tui";
694
+ else if (values.tui) opts.outputMode = "tui";
640
695
  else if (values.web) opts.outputMode = "web";
641
696
  else if (values.terminal) opts.outputMode = "terminal";
642
- if (process.stdout.isTTY && opts.outputMode === "auto") opts.outputMode = "web";
697
+ if (process.stdout.isTTY && opts.outputMode === "auto") opts.outputMode = values["gh-pr"] ? "web" : defaultInteractiveMode;
643
698
  else if (!process.stdout.isTTY && opts.outputMode === "auto") opts.outputMode = "terminal";
644
- if (opts.outputFormat || opts.patchWithRaw || opts.patchWithStat || opts.exitCode || opts.quiet || opts.outputFile) opts.outputMode = "terminal";
699
+ if (opts.outputFormat || opts.patchWithRaw || opts.patchWithStat || opts.compactSummary || opts.dirstat || opts.dirstatByFile || opts.cumulative || opts.suppressPatch || opts.check || opts.exitCode || opts.quiet || opts.outputFile || opts.colorMoved || opts.colorMovedWs || opts.srcPrefix || opts.dstPrefix || opts.noPrefix || opts.linePrefix || opts.submodule !== void 0 && opts.submodule !== "diff" || opts.extDiff) opts.outputMode = "terminal";
645
700
  return opts;
646
701
  }
647
702
  /**
@@ -682,7 +737,7 @@ function buildGitDiffArgs(opts) {
682
737
  if (opts.outputFormat === "raw") args.push("--raw");
683
738
  if (opts.outputFormat === "numstat") args.push("--numstat");
684
739
  if (opts.outputFormat === "shortstat") args.push("--shortstat");
685
- if (opts.outputFormat === "stat") args.push("--stat");
740
+ if (opts.outputFormat === "stat") args.push(opts.statParam ? `--stat=${opts.statParam}` : "--stat");
686
741
  if (opts.outputFormat === "summary") args.push("--summary");
687
742
  if (opts.outputFormat === "name-only") args.push("--name-only");
688
743
  if (opts.outputFormat === "name-status") args.push("--name-status");
@@ -693,6 +748,7 @@ function buildGitDiffArgs(opts) {
693
748
  if (opts.dirstatByFile) args.push(`--dirstat-by-file=${opts.dirstatByFile}`);
694
749
  if (opts.cumulative) args.push("--cumulative");
695
750
  if (opts.check) args.push("--check");
751
+ if (opts.suppressPatch) args.push("--no-patch");
696
752
  if (opts.diffFilter) args.push(`--diff-filter=${opts.diffFilter}`);
697
753
  if (opts.pickaxeString) args.push(`-S${opts.pickaxeString}`);
698
754
  if (opts.pickaxeRegex) args.push(`-G${opts.pickaxeRegex}`);
@@ -723,6 +779,23 @@ function buildGitDiffArgs(opts) {
723
779
  return args;
724
780
  }
725
781
  /**
782
+ * Build the machine-stable patch arguments consumed by the native index.
783
+ * Presentation-only word/color modes are implemented by the renderer and
784
+ * must not rewrite the unified patch stream that Rust parses.
785
+ */
786
+ function buildTuiGitDiffArgs(opts) {
787
+ return buildGitDiffArgs({
788
+ ...opts,
789
+ revisions: opts.showMode ? [...opts.showRevspecs] : [...opts.revisions],
790
+ pathspecs: [...opts.pathspecs],
791
+ wordDiff: void 0,
792
+ wordDiffRegex: void 0,
793
+ colorWords: void 0,
794
+ colorMoved: void 0,
795
+ colorMovedWs: void 0
796
+ });
797
+ }
798
+ /**
726
799
  * Transform a `parseDiffOptions(...)` result into a "show mode" variant:
727
800
  * moves any parsed revisions into `showRevspecs`, clears `revisions` (so the
728
801
  * existing diff-engine path does not try to `git diff` them), and sets the
@@ -738,6 +811,88 @@ function intoShowMode(opts) {
738
811
  return opts;
739
812
  }
740
813
  //#endregion
814
+ //#region src/lib/git-path.ts
815
+ /** Decode Git `core.quotePath` C-quoted pathnames from diff headers. */
816
+ function decodeGitPath$1(raw) {
817
+ const input = raw.trim();
818
+ if (!(input.startsWith("\"") && input.endsWith("\""))) return input;
819
+ const bytes = [];
820
+ const pushText = (value) => bytes.push(...Buffer.from(value, "utf8"));
821
+ for (let i = 1; i < input.length - 1; i++) {
822
+ if (input[i] !== "\\") {
823
+ const codePoint = input.codePointAt(i);
824
+ pushText(String.fromCodePoint(codePoint));
825
+ if (codePoint > 65535) i++;
826
+ continue;
827
+ }
828
+ const escaped = input[++i];
829
+ if (escaped == null) break;
830
+ const simple = {
831
+ a: 7,
832
+ b: 8,
833
+ t: 9,
834
+ n: 10,
835
+ v: 11,
836
+ f: 12,
837
+ r: 13,
838
+ "\"": 34,
839
+ "\\": 92
840
+ };
841
+ if (escaped in simple) {
842
+ bytes.push(simple[escaped]);
843
+ continue;
844
+ }
845
+ if (/[0-7]/.test(escaped)) {
846
+ let octal = escaped;
847
+ while (octal.length < 3 && i + 1 < input.length - 1 && /[0-7]/.test(input[i + 1])) octal += input[++i];
848
+ bytes.push(Number.parseInt(octal, 8));
849
+ continue;
850
+ }
851
+ pushText(escaped);
852
+ }
853
+ return Buffer.from(bytes).toString("utf8");
854
+ }
855
+ function stripSidePrefix$1(path, prefix) {
856
+ return path.startsWith(prefix) ? path.slice(prefix.length) : path;
857
+ }
858
+ function consumeGitToken$1(input, start) {
859
+ if (input[start] !== "\"") {
860
+ const end = input.indexOf(" ", start);
861
+ return {
862
+ token: input.slice(start, end < 0 ? input.length : end),
863
+ next: end < 0 ? input.length : end
864
+ };
865
+ }
866
+ let escaped = false;
867
+ for (let i = start + 1; i < input.length; i++) {
868
+ const char = input[i];
869
+ if (!escaped && char === "\"") return {
870
+ token: input.slice(start, i + 1),
871
+ next: i + 1
872
+ };
873
+ if (!escaped && char === "\\") escaped = true;
874
+ else escaped = false;
875
+ }
876
+ return null;
877
+ }
878
+ /** Parse `diff --git` header paths, including quoted paths and spaces in names. */
879
+ function parseGitDiffHeaderPaths(line) {
880
+ if (!line.startsWith("diff --git ")) return null;
881
+ const rest = line.slice(11);
882
+ if (rest.startsWith("\"")) {
883
+ const first = consumeGitToken$1(rest, 0);
884
+ if (!first) return null;
885
+ let cursor = first.next;
886
+ while (cursor < rest.length && /\s/.test(rest[cursor])) cursor++;
887
+ const second = consumeGitToken$1(rest, cursor);
888
+ if (!second) return null;
889
+ return [stripSidePrefix$1(decodeGitPath$1(first.token), "a/"), stripSidePrefix$1(decodeGitPath$1(second.token), "b/")];
890
+ }
891
+ const separator = rest.lastIndexOf(" b/");
892
+ if (separator < 0) return null;
893
+ return [stripSidePrefix$1(rest.slice(0, separator), "a/"), stripSidePrefix$1(rest.slice(separator + 1), "b/")];
894
+ }
895
+ //#endregion
741
896
  //#region src/lib/diff-overview.ts
742
897
  /**
743
898
  * Compose the working-tree banner. Used by the default `git diff` (unstaged +
@@ -930,7 +1085,6 @@ function summariseCommitSeries(series, rows) {
930
1085
  }
931
1086
  //#endregion
932
1087
  //#region src/lib/diff-engine.ts
933
- const MAX_BUFFER = 50 * 1024 * 1024;
934
1088
  /**
935
1089
  * Determine whether to use the "standard" web mode (unstaged+staged+untracked)
936
1090
  * or a "custom" mode (specific revisions / pathspecs).
@@ -945,8 +1099,8 @@ function isCustomMode$1(opts) {
945
1099
  function parseFilePaths(patch) {
946
1100
  const paths = /* @__PURE__ */ new Set();
947
1101
  for (const line of patch.split("\n")) {
948
- const match = line.match(/^diff --git a\/.+ b\/(.+)$/);
949
- if (match) paths.add(match[1]);
1102
+ const parsed = parseGitDiffHeaderPaths(line);
1103
+ if (parsed) paths.add(parsed[1]);
950
1104
  }
951
1105
  return [...paths];
952
1106
  }
@@ -958,9 +1112,9 @@ function parseBinaryFiles(patch, untrackedFiles) {
958
1112
  if (!line.startsWith("Binary files ") || !line.includes(" differ")) continue;
959
1113
  let filePath = "";
960
1114
  for (let j = i - 1; j >= 0; j--) {
961
- const match = lines[j].match(/^diff --git a\/.+ b\/(.+)$/);
962
- if (match) {
963
- filePath = match[1];
1115
+ const parsed = parseGitDiffHeaderPaths(lines[j]);
1116
+ if (parsed) {
1117
+ filePath = parsed[1];
964
1118
  break;
965
1119
  }
966
1120
  }
@@ -1025,12 +1179,10 @@ async function executeDiffAsync(opts) {
1025
1179
  */
1026
1180
  async function executeDiffWithMeta(opts) {
1027
1181
  const { patch, commits, truncated } = await executeDiffAsync(opts);
1028
- const repoName = getRepoName();
1029
- const branch = getBranchName();
1030
- const untrackedFiles = opts.includeUntracked && !opts.showMode ? await getUntrackedFilePathsAsync() : [];
1182
+ const [{ repoName, branch }, untrackedFiles] = await Promise.all([getRepoMetadataAsync(), opts.includeUntracked && !opts.showMode ? getUntrackedFilePathsAsync() : Promise.resolve([])]);
1031
1183
  const binaryFiles = parseBinaryFiles(patch, new Set(untrackedFiles));
1032
1184
  const filePaths = parseFilePaths(patch);
1033
- const tabSizeMap = getTabSizeForFiles(filePaths);
1185
+ const tabSizeMap = await getTabSizeForFilesAsync(filePaths);
1034
1186
  let overview;
1035
1187
  if (opts.showMode && commits && commits.length > 0) overview = buildCommitOverview({
1036
1188
  commits: commits.map((c) => ({
@@ -1107,11 +1259,7 @@ function runTerminalDiff(opts) {
1107
1259
  }
1108
1260
  finalArgs.push(...args);
1109
1261
  try {
1110
- execFileSync("git", ["diff", ...finalArgs], {
1111
- encoding: "utf-8",
1112
- maxBuffer: MAX_BUFFER,
1113
- stdio: "inherit"
1114
- });
1262
+ execFileSync("git", ["diff", ...finalArgs], { stdio: "inherit" });
1115
1263
  return 0;
1116
1264
  } catch (err) {
1117
1265
  if (err.status === 1 && opts.exitCode) return 1;
@@ -1132,11 +1280,7 @@ function runTerminalShow(opts) {
1132
1280
  ];
1133
1281
  if (opts.pathspecs.length > 0) args.push("--", ...opts.pathspecs);
1134
1282
  try {
1135
- execFileSync("git", args, {
1136
- encoding: "utf-8",
1137
- maxBuffer: MAX_BUFFER,
1138
- stdio: "inherit"
1139
- });
1283
+ execFileSync("git", args, { stdio: "inherit" });
1140
1284
  return 0;
1141
1285
  } catch (err) {
1142
1286
  return err.status ?? 1;
@@ -1155,6 +1299,7 @@ function validateEnvironment() {
1155
1299
  var FileCommentStore = class {
1156
1300
  dirPath;
1157
1301
  filePath;
1302
+ mutationQueue = Promise.resolve();
1158
1303
  /**
1159
1304
  * @param storageDir Absolute directory to persist `comments.json` in.
1160
1305
  * Defaults to the per-repo storage dir under `~/.diffing` — comments are
@@ -1186,74 +1331,100 @@ var FileCommentStore = class {
1186
1331
  console.error("Failed to save comments to file:", err);
1187
1332
  }
1188
1333
  }
1334
+ /** Serialize each read-modify-write cycle so concurrent API calls cannot overwrite one another. */
1335
+ enqueueMutation(operation) {
1336
+ const result = this.mutationQueue.then(operation, operation);
1337
+ this.mutationQueue = result.then(() => void 0, () => void 0);
1338
+ return result;
1339
+ }
1189
1340
  async add(comment) {
1190
- const comments = await this.getAll();
1191
- comments.push(comment);
1192
- await this.save(comments);
1193
- return comment;
1341
+ return this.enqueueMutation(async () => {
1342
+ const comments = await this.getAll();
1343
+ comments.push(comment);
1344
+ await this.save(comments);
1345
+ return comment;
1346
+ });
1194
1347
  }
1195
1348
  async update(id, fields) {
1196
- const comments = await this.getAll();
1197
- const index = comments.findIndex((c) => c.id === id);
1198
- if (index === -1) return null;
1199
- const comment = comments[index];
1200
- if (fields.body !== void 0) comment.body = fields.body;
1201
- if (fields.status !== void 0) comment.status = fields.status;
1202
- await this.save(comments);
1203
- return comment;
1349
+ return this.enqueueMutation(async () => {
1350
+ const comments = await this.getAll();
1351
+ const index = comments.findIndex((c) => c.id === id);
1352
+ if (index === -1) return null;
1353
+ const comment = comments[index];
1354
+ if (fields.body !== void 0) comment.body = fields.body;
1355
+ if (fields.status !== void 0) comment.status = fields.status;
1356
+ await this.save(comments);
1357
+ return comment;
1358
+ });
1204
1359
  }
1205
1360
  async resolveAllOpen() {
1206
- const comments = await this.getAll();
1207
- let n = 0;
1208
- for (const c of comments) if (c.status === "open") {
1209
- c.status = "resolved";
1210
- n++;
1211
- }
1212
- if (n > 0) await this.save(comments);
1213
- return n;
1361
+ return this.enqueueMutation(async () => {
1362
+ const comments = await this.getAll();
1363
+ let n = 0;
1364
+ for (const c of comments) if (c.status === "open") {
1365
+ c.status = "resolved";
1366
+ n++;
1367
+ }
1368
+ if (n > 0) await this.save(comments);
1369
+ return n;
1370
+ });
1214
1371
  }
1215
1372
  async remove(id) {
1216
- const comments = await this.getAll();
1217
- const index = comments.findIndex((c) => c.id === id);
1218
- if (index === -1) return false;
1219
- comments.splice(index, 1);
1220
- await this.save(comments);
1221
- return true;
1373
+ return this.enqueueMutation(async () => {
1374
+ const comments = await this.getAll();
1375
+ const index = comments.findIndex((c) => c.id === id);
1376
+ if (index === -1) return false;
1377
+ comments.splice(index, 1);
1378
+ await this.save(comments);
1379
+ return true;
1380
+ });
1222
1381
  }
1223
1382
  async addReply(commentId, reply) {
1224
- const comments = await this.getAll();
1225
- const index = comments.findIndex((c) => c.id === commentId);
1226
- if (index === -1) return null;
1227
- const comment = comments[index];
1228
- if (!comment.replies) comment.replies = [];
1229
- comment.replies.push(reply);
1230
- await this.save(comments);
1231
- return comment;
1383
+ return this.enqueueMutation(async () => {
1384
+ const comments = await this.getAll();
1385
+ const index = comments.findIndex((c) => c.id === commentId);
1386
+ if (index === -1) return null;
1387
+ const comment = comments[index];
1388
+ if (!comment.replies) comment.replies = [];
1389
+ comment.replies.push(reply);
1390
+ await this.save(comments);
1391
+ return comment;
1392
+ });
1232
1393
  }
1233
1394
  async removeReply(commentId, replyId) {
1234
- const comments = await this.getAll();
1235
- const index = comments.findIndex((c) => c.id === commentId);
1236
- if (index === -1) return null;
1237
- const comment = comments[index];
1238
- const replyIndex = comment.replies.findIndex((r) => r.id === replyId);
1239
- if (replyIndex === -1) return null;
1240
- comment.replies.splice(replyIndex, 1);
1241
- await this.save(comments);
1242
- return comment;
1395
+ return this.enqueueMutation(async () => {
1396
+ const comments = await this.getAll();
1397
+ const index = comments.findIndex((c) => c.id === commentId);
1398
+ if (index === -1) return null;
1399
+ const comment = comments[index];
1400
+ const replyIndex = comment.replies.findIndex((r) => r.id === replyId);
1401
+ if (replyIndex === -1) return null;
1402
+ comment.replies.splice(replyIndex, 1);
1403
+ await this.save(comments);
1404
+ return comment;
1405
+ });
1243
1406
  }
1244
1407
  async updateReply(commentId, replyId, body) {
1245
- const comments = await this.getAll();
1246
- const index = comments.findIndex((c) => c.id === commentId);
1247
- if (index === -1) return null;
1248
- const comment = comments[index];
1249
- const reply = comment.replies.find((r) => r.id === replyId);
1250
- if (!reply) return null;
1251
- reply.body = body;
1252
- await this.save(comments);
1253
- return comment;
1408
+ return this.enqueueMutation(async () => {
1409
+ const comments = await this.getAll();
1410
+ const index = comments.findIndex((c) => c.id === commentId);
1411
+ if (index === -1) return null;
1412
+ const comment = comments[index];
1413
+ const reply = comment.replies.find((r) => r.id === replyId);
1414
+ if (!reply) return null;
1415
+ reply.body = body;
1416
+ await this.save(comments);
1417
+ return comment;
1418
+ });
1254
1419
  }
1255
1420
  };
1256
1421
  //#endregion
1422
+ //#region src/lib/types.ts
1423
+ /** Runtime guard for comment data crossing HTTP or persisted-storage boundaries. */
1424
+ function isReviewCommentSide(value) {
1425
+ return value === "deletions" || value === "additions";
1426
+ }
1427
+ //#endregion
1257
1428
  //#region src/lib/plans.ts
1258
1429
  /** Absolute path of the persisted markdown mirror for a plan id. */
1259
1430
  function planSourceFilePath(storageDir, planId) {
@@ -1613,7 +1784,7 @@ function guiEditorCommand(bin, absolutePath, platform) {
1613
1784
  function terminalEditorCommand(bin, absolutePath, platform, env) {
1614
1785
  if (platform === "darwin") return {
1615
1786
  cmd: "osascript",
1616
- args: ["-e", `tell application "Terminal" to do script "${bin} \\"${absolutePath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}\\""`]
1787
+ args: ["-e", `tell application "Terminal" to do script ("${bin} " & quoted form of "${absolutePath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}")`]
1617
1788
  };
1618
1789
  if (platform === "win32") return {
1619
1790
  cmd: "cmd.exe",
@@ -2765,8 +2936,13 @@ function collectPrAvatarUrls(session) {
2765
2936
  function isCustomMode(opts) {
2766
2937
  return opts.revisions.length > 0 || opts.pathspecs.length > 0 || opts.showMode;
2767
2938
  }
2768
- function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore, prSessionStore, prMode = false) {
2939
+ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore, prSessionStore, prMode = false, security = {
2940
+ bindHost: "127.0.0.1",
2941
+ authToken: null,
2942
+ insecureNoAuth: true
2943
+ }) {
2769
2944
  const app = new Hono();
2945
+ app.use("*", createServerAuthMiddleware(security));
2770
2946
  let diffOpts = { ...diffOptsInput };
2771
2947
  const customMode = isCustomMode(diffOpts);
2772
2948
  const store = commentStore ?? new FileCommentStore();
@@ -3347,6 +3523,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
3347
3523
  });
3348
3524
  app.post("/api/comments", async (c) => {
3349
3525
  const body = await c.req.json();
3526
+ if (!isReviewCommentSide(body.side)) return c.json({ error: "side must be additions or deletions" }, 400);
3350
3527
  const severityRaw = body.severity;
3351
3528
  const severity = severityRaw === "blocking" || severityRaw === "nit" || severityRaw === "question" || severityRaw === "praise" || severityRaw === "none" ? severityRaw : void 0;
3352
3529
  const comment = {
@@ -3415,7 +3592,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
3415
3592
  if (!relPath) return c.json({ error: "Forbidden file path" }, 403);
3416
3593
  const absolutePath = resolve(root, relPath);
3417
3594
  const content = await readFile(absolutePath, "utf-8");
3418
- const { applySuggestionToContent } = await import("./apply-suggestion-DNNsC5gx.mjs");
3595
+ const { applySuggestionToContent } = await import("./apply-suggestion-CHYnw68I.mjs");
3419
3596
  const result = applySuggestionToContent({
3420
3597
  content,
3421
3598
  lineNumber: comment.lineNumber,
@@ -3662,7 +3839,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
3662
3839
  const session = await prStore.get();
3663
3840
  if (!session) return notInPrMode(c);
3664
3841
  try {
3665
- const { fetchPrChecks } = await import("./github-DKT0lJXv.mjs");
3842
+ const { fetchPrChecks } = await import("./github-DXiEeHzk.mjs");
3666
3843
  const checks = await fetchPrChecks(resolvedFromSession(session), session.headSha);
3667
3844
  const summary = {
3668
3845
  total: checks.length,
@@ -3694,7 +3871,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
3694
3871
  const text = typeof body.body === "string" ? body.body.trim() : "";
3695
3872
  if (!text) return c.json({ error: "body is required" }, 400);
3696
3873
  try {
3697
- const { replyToPrComment } = await import("./github-DKT0lJXv.mjs");
3874
+ const { replyToPrComment } = await import("./github-DXiEeHzk.mjs");
3698
3875
  const result = await replyToPrComment({
3699
3876
  resolved: resolvedFromSession(session),
3700
3877
  inReplyTo: id,
@@ -3908,8 +4085,8 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
3908
4085
  if (decision !== "approve" && decision !== "comment" && decision !== "request-changes" && decision !== "draft") return c.json({ error: "decision must be one of: approve, comment, request-changes, draft" }, 400);
3909
4086
  const generalBody = typeof body.body === "string" ? body.body : "";
3910
4087
  if (body.dryRun === true) {
3911
- const { buildReviewPayload, resolvedFromSession } = await import("./github-DKT0lJXv.mjs");
3912
- const { rewriteLocalAttachmentsInBodies } = await import("./github-attachments-Cf17YWp4.mjs");
4088
+ const { buildReviewPayload, resolvedFromSession } = await import("./github-DXiEeHzk.mjs");
4089
+ const { rewriteLocalAttachmentsInBodies } = await import("./github-attachments-DgOf7BhP.mjs");
3913
4090
  const resolved = resolvedFromSession(session);
3914
4091
  const comments = session.comments ?? [];
3915
4092
  const rewritten = await rewriteLocalAttachmentsInBodies(resolved, [generalBody, ...comments.map((c) => c.body ?? "")], { dryRun: true });
@@ -4006,7 +4183,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
4006
4183
  const openCount = all.filter((x) => x.status === "open").length;
4007
4184
  let diffFingerprints;
4008
4185
  try {
4009
- const { fingerprintDiffFiles } = await import("./diff-fingerprint-C8YO3TTE.mjs");
4186
+ const { fingerprintDiffFiles } = await import("./diff-fingerprint-BgR0tu1o.mjs");
4010
4187
  if (prMode) {
4011
4188
  const prSession = await prStore.get();
4012
4189
  if (prSession?.diff) diffFingerprints = fingerprintDiffFiles(prSession.diff);
@@ -4074,7 +4251,7 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
4074
4251
  } catch (err) {
4075
4252
  return c.json({ error: err?.message ?? "Failed to compute current diff" }, 500);
4076
4253
  }
4077
- const { fingerprintDiffFiles, diffSinceLast, filesToReviewSinceLast } = await import("./diff-fingerprint-C8YO3TTE.mjs");
4254
+ const { fingerprintDiffFiles, diffSinceLast, filesToReviewSinceLast } = await import("./diff-fingerprint-BgR0tu1o.mjs");
4078
4255
  const delta = diffSinceLast(previous, fingerprintDiffFiles(currentPatch));
4079
4256
  return c.json({
4080
4257
  hasBaseline: true,
@@ -4299,10 +4476,19 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
4299
4476
  try {
4300
4477
  const content = await readFile(fullPath);
4301
4478
  const contentType = MIME_TYPES[extname(fullPath)] || "application/octet-stream";
4479
+ if (contentType === "text/html" && security.authToken) {
4480
+ const html = injectSessionTokenIntoHtml(content.toString("utf-8"), security.authToken);
4481
+ return new Response(html, { headers: {
4482
+ "Content-Type": contentType,
4483
+ "Set-Cookie": buildSessionTokenSetCookieValue(security.authToken)
4484
+ } });
4485
+ }
4302
4486
  return new Response(content, { headers: { "Content-Type": contentType } });
4303
4487
  } catch {
4304
- const indexContent = await readFile(join(clientDir, "index.html"));
4305
- return new Response(indexContent, { headers: { "Content-Type": "text/html" } });
4488
+ const html = injectSessionTokenIntoHtml((await readFile(join(clientDir, "index.html"))).toString("utf-8"), security.authToken);
4489
+ const spaHeaders = { "Content-Type": "text/html" };
4490
+ if (security.authToken) spaHeaders["Set-Cookie"] = buildSessionTokenSetCookieValue(security.authToken);
4491
+ return new Response(html, { headers: spaHeaders });
4306
4492
  }
4307
4493
  });
4308
4494
  return app;
@@ -4393,7 +4579,7 @@ async function startServer(options) {
4393
4579
  throw new Error(`Failed to build PR session for ${options.prRef}: ${detail}`);
4394
4580
  }
4395
4581
  }
4396
- const app = createApp(options.clientDir, options.diffOpts, void 0, void 0, void 0, prMode);
4582
+ const app = createApp(options.clientDir, options.diffOpts, void 0, void 0, void 0, prMode, options.security);
4397
4583
  return new Promise((resolve) => {
4398
4584
  serve({
4399
4585
  fetch: app.fetch,
@@ -4408,4 +4594,4 @@ async function startServer(options) {
4408
4594
  });
4409
4595
  }
4410
4596
  //#endregion
4411
- export { intoShowMode as a, buildGitDiffArgs as i, runTerminalDiff as n, parseDiffOptions as o, validateEnvironment as r, printHelp as s, startServer as t };
4597
+ export { buildTuiGitDiffArgs as a, printHelp as c, buildGitDiffArgs as i, runTerminalDiff as n, intoShowMode as o, validateEnvironment as r, parseDiffOptions as s, startServer as t };