diffing 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -1
- package/dist/{cli-agent-ZAkixf4c.mjs → cli-agent-KxNAFIPf.mjs} +6 -2
- package/dist/cli-gh-CDGSpndZ.mjs +147 -0
- package/dist/cli.mjs +1064 -44
- package/dist/client/assets/{angular-html-DsssUIMZ.js → angular-html-DbbrJavS.js} +1 -1
- package/dist/client/assets/{angular-ts-dTg-sJ77.js → angular-ts-tCrxb1ht.js} +1 -1
- package/dist/client/assets/{apl-C1yUKbqz.js → apl-DawII0w7.js} +1 -1
- package/dist/client/assets/arc-DIRV7ZXr.js +1 -0
- package/dist/client/assets/architecture-7EHR7CIX-BM5HxebS.js +1 -0
- package/dist/client/assets/architectureDiagram-3BPJPVTR-3IoH78OV.js +36 -0
- package/dist/client/assets/array-CkF66D10.js +1 -0
- package/dist/client/assets/{astro-BI07ha2y.js → astro-DIyGTFXc.js} +1 -1
- package/dist/client/assets/{blade-DkzUGffS.js → blade-CuF4Z1mP.js} +1 -1
- package/dist/client/assets/blockDiagram-GPEHLZMM-CFxPndC0.js +132 -0
- package/dist/client/assets/{bsl-Dx5tFn3i.js → bsl-BrHd4RA2.js} +1 -1
- package/dist/client/assets/{c-KkuqqOvW.js → c-DUrjo9T5.js} +1 -1
- package/dist/client/assets/c4Diagram-AAUBKEIU-CGMBnkiv.js +10 -0
- package/dist/client/assets/{cairo-wCCQTdA9.js → cairo-BDZ6TRZS.js} +1 -1
- package/dist/client/assets/channel-D5AdjEWX.js +1 -0
- package/dist/client/assets/chunk-2J33WTMH-CJMBLaXy.js +1 -0
- package/dist/client/assets/chunk-3OPIFGDE-C9HQWVLl.js +62 -0
- package/dist/client/assets/chunk-4BX2VUAB-Qqg72GQd.js +1 -0
- package/dist/client/assets/chunk-4EGX6M5U-C70lfA2p.js +1 -0
- package/dist/client/assets/chunk-55IACEB6-DZF8brTR.js +1 -0
- package/dist/client/assets/chunk-5DO6E6H7-PprNUwlI.js +1 -0
- package/dist/client/assets/chunk-5ZQYHXKU-BflVFV5z.js +2 -0
- package/dist/client/assets/chunk-727SXJPM-COn6BRLY.js +206 -0
- package/dist/client/assets/chunk-AQP2D5EJ-CNOjODUR.js +231 -0
- package/dist/client/assets/chunk-BR22UD5L-D8XCYMy9.js +1 -0
- package/dist/client/assets/chunk-BSJP7CBP-PQX4ASCI.js +1 -0
- package/dist/client/assets/chunk-CSCIHK7Q-BfmUvL-L.js +124 -0
- package/dist/client/assets/chunk-FHYWG6QK-CcHPrHH1.js +1 -0
- package/dist/client/assets/chunk-FMBD7UC4-DorfwLnQ.js +15 -0
- package/dist/client/assets/chunk-KSCS5N6A-CXY93mId.js +10 -0
- package/dist/client/assets/chunk-L5ZTLDWV-BjMrSVmH.js +1 -0
- package/dist/client/assets/chunk-LZXEDZCA-q8jj6Nyb.js +2 -0
- package/dist/client/assets/chunk-MPE355IW-Bf9yHfDS.js +1 -0
- package/dist/client/assets/chunk-MZUSXYTE-CnUIUKI4.js +1 -0
- package/dist/client/assets/chunk-N66VUXT2-ahAf9GvV.js +1 -0
- package/dist/client/assets/chunk-ND2GUHAM-BRRQbYHl.js +1 -0
- package/dist/client/assets/chunk-NNHCCRGN-DHF3V75i.js +159 -0
- package/dist/client/assets/chunk-NZK2D7GU-Czt1qHrO.js +1 -0
- package/dist/client/assets/chunk-O5CBEL6O-DEopURak.js +70 -0
- package/dist/client/assets/chunk-PUPMXCY4-D0KKIhut.js +1 -0
- package/dist/client/assets/chunk-QZHKN3VN-BE917fn9.js +1 -0
- package/dist/client/assets/chunk-UIBZB4QT-TYsZ_c8u.js +1 -0
- package/dist/client/assets/chunk-WCWK7LTN-Dj8SfnMU.js +1 -0
- package/dist/client/assets/chunk-WU5MYG2G-DdoWnPla.js +1 -0
- package/dist/client/assets/chunk-XPW4576I-DocCoKUT.js +32 -0
- package/dist/client/assets/classDiagram-4FO5ZUOK-CxjhKO7s.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CSScAAQ4.js +1 -0
- package/dist/client/assets/{cobol-DRh7-jLi.js → cobol-BylylR2x.js} +1 -1
- package/dist/client/assets/{coffee-wR_ZAh9a.js → coffee-DCKfk8xx.js} +1 -1
- package/dist/client/assets/cose-bilkent-S5V4N54A-Cm0BD5CS.js +1 -0
- package/dist/client/assets/{cpp-OD_qyywn.js → cpp-CfKCIrGK.js} +1 -1
- package/dist/client/assets/{crystal-E4ahAddE.js → crystal-v-9RIu3l.js} +1 -1
- package/dist/client/assets/{css-2NgE_uxz.js → css-XjlXeGmZ.js} +1 -1
- package/dist/client/assets/cytoscape.esm-B7MQoI9k.js +321 -0
- package/dist/client/assets/dagre-BM42HDAG-kqgFUaF1.js +4 -0
- package/dist/client/assets/dagre-DAhKikc0.js +1 -0
- package/dist/client/assets/defaultLocale-B_TxGd0k.js +1 -0
- package/dist/client/assets/diagram-2AECGRRQ-B3hBvGUc.js +43 -0
- package/dist/client/assets/diagram-5GNKFQAL-r8zU8C-Y.js +10 -0
- package/dist/client/assets/diagram-KO2AKTUF-CDWD5zkZ.js +3 -0
- package/dist/client/assets/diagram-LMA3HP47-CRRfS6RD.js +24 -0
- package/dist/client/assets/diagram-OG6HWLK6-ChBS6E-K.js +24 -0
- package/dist/client/assets/dist-DUTeKbN7.js +1 -0
- package/dist/client/assets/{edge-8mbscxxD.js → edge-Cwh953i8.js} +1 -1
- package/dist/client/assets/{elixir-CJv1b3Q6.js → elixir-C4MgLzTj.js} +1 -1
- package/dist/client/assets/{elm-DUPzqZ9H.js → elm-CFBu8r7i.js} +1 -1
- package/dist/client/assets/erDiagram-TEJ5UH35-_03Fb9RH.js +85 -0
- package/dist/client/assets/{erb-ssvEF26z.js → erb-CrCna7R1.js} +1 -1
- package/dist/client/assets/{erlang-CtK0kQ6z.js → erlang-cuhwpmsu.js} +1 -1
- package/dist/client/assets/eventmodeling-FCH6USID-hlXE2E34.js +1 -0
- package/dist/client/assets/flowDiagram-I6XJVG4X-D9pSaH_8.js +162 -0
- package/dist/client/assets/{fortran-fixed-form-DBM4e0_k.js → fortran-fixed-form-DWr0EnJn.js} +1 -1
- package/dist/client/assets/{fsharp-CmzzGQLe.js → fsharp-CQ5iYSPA.js} +1 -1
- package/dist/client/assets/ganttDiagram-6RSMTGT7-DKBfXY9D.js +292 -0
- package/dist/client/assets/{gdresource-B_wbPqHd.js → gdresource-CN-lsNu5.js} +1 -1
- package/dist/client/assets/{git-commit-1z4D6NzL.js → git-commit-6e79eN7Q.js} +1 -1
- package/dist/client/assets/{git-rebase-DiWIK1Oa.js → git-rebase-CExLk2dD.js} +1 -1
- package/dist/client/assets/gitGraph-WXDBUCRP-D_3ZhTam.js +1 -0
- package/dist/client/assets/gitGraphDiagram-PVQCEYII-BPQ2kpJA.js +106 -0
- package/dist/client/assets/{glimmer-js-DrHg6AuA.js → glimmer-js-wjBSKaMu.js} +1 -1
- package/dist/client/assets/{glimmer-ts-wQSGOa8A.js → glimmer-ts-CyjMXDgZ.js} +1 -1
- package/dist/client/assets/{glsl-OY27QGY0.js → glsl-CWOoN8or.js} +1 -1
- package/dist/client/assets/graphlib-DBdw4opr.js +1 -0
- package/dist/client/assets/{graphql-CSWSqe6f.js → graphql-DI5CB58U.js} +1 -1
- package/dist/client/assets/{hack-Bi1v624Q.js → hack-Ct_Cm7Pr.js} +1 -1
- package/dist/client/assets/{haml-CX94zV9m.js → haml-DzAjli8d.js} +1 -1
- package/dist/client/assets/{handlebars-C1j6wnjG.js → handlebars-BfJB0RYx.js} +1 -1
- package/dist/client/assets/{html-BcAjMlB7.js → html-CK8auhK8.js} +1 -1
- package/dist/client/assets/{html-derivative-DcEpOzdx.js → html-derivative-B7VntYnF.js} +1 -1
- package/dist/client/assets/{http-BBxxSc6S.js → http-_Zy71Sv3.js} +1 -1
- package/dist/client/assets/{hurl-CpKRNTz4.js → hurl-VbL4I95z.js} +1 -1
- package/dist/client/assets/{hxml-DC4KwKt2.js → hxml-nwJci6H5.js} +1 -1
- package/dist/client/assets/{index-CoZyJYkw.css → index-C3p7VKOX.css} +1 -1
- package/dist/client/assets/index-Dv9jPmOc.js +3921 -0
- package/dist/client/assets/info-J43DQDTF-CAllC7mK.js +1 -0
- package/dist/client/assets/infoDiagram-5YYISTIA-B5RN-4Ke.js +2 -0
- package/dist/client/assets/init-Ce66tIvf.js +1 -0
- package/dist/client/assets/ishikawaDiagram-YF4QCWOH-DpbSX3Zy.js +70 -0
- package/dist/client/assets/{java-CFUlLvAA.js → java-0CC2DnK9.js} +1 -1
- package/dist/client/assets/{javascript-C-HnT3sE.js → javascript-DKSTP7rz.js} +1 -1
- package/dist/client/assets/{jinja-CPU8RwNP.js → jinja-DoGBHNJ6.js} +1 -1
- package/dist/client/assets/{jison-CW0TBQOs.js → jison-Bnl3lPA2.js} +1 -1
- package/dist/client/assets/journeyDiagram-JHISSGLW-B_X4_qPO.js +139 -0
- package/dist/client/assets/{json-qUjlNuEJ.js → json-BQ3Ddj6g.js} +1 -1
- package/dist/client/assets/{jsx-KWeo60kf.js → jsx-C_aFDuFZ.js} +1 -1
- package/dist/client/assets/{julia-CnJB8y2e.js → julia-nIjp5yMR.js} +1 -1
- package/dist/client/assets/{just-Do22d9PP.js → just-zc4yJ46n.js} +1 -1
- package/dist/client/assets/kanban-definition-UN3LZRKU-BSs4C5DW.js +89 -0
- package/dist/client/assets/katex-eRFNlMh0.js +257 -0
- package/dist/client/assets/{latex-CCPgzvKn.js → latex-BszBC80j.js} +1 -1
- package/dist/client/assets/line-C9HWVwPi.js +1 -0
- package/dist/client/assets/linear-DatJfpKY.js +1 -0
- package/dist/client/assets/{liquid-B9btdZoI.js → liquid-DglQpTge.js} +1 -1
- package/dist/client/assets/{lua-IFRifurW.js → lua-BcbliFeR.js} +1 -1
- package/dist/client/assets/{marko-CCaVBFq1.js → marko-BPm4M8j-.js} +1 -1
- package/dist/client/assets/{mdc-BtbnOLUc.js → mdc-BNUj07UF.js} +1 -1
- package/dist/client/assets/mermaid-parser.core-BVEKlqQr.js +4 -0
- package/dist/client/assets/mermaid.core-DVis21V_.js +9 -0
- package/dist/client/assets/mindmap-definition-RKZ34NQL-Cuo7KewF.js +96 -0
- package/dist/client/assets/{nextflow-CApkvBGW.js → nextflow-DSfydIxt.js} +1 -1
- package/dist/client/assets/{nginx-BkbFZHVt.js → nginx-CSSJwkd6.js} +1 -1
- package/dist/client/assets/{nim-2uCd5j65.js → nim-nQAskwTR.js} +1 -1
- package/dist/client/assets/ordinal-5ziJS8mo.js +1 -0
- package/dist/client/assets/packet-YPE3B663-Dy58IAiO.js +1 -0
- package/dist/client/assets/path-g3G46IZB.js +1 -0
- package/dist/client/assets/{perl-C4ixqSNT.js → perl-CtH8xYH6.js} +1 -1
- package/dist/client/assets/{php-DJ8WOtcI.js → php-Dcc3T2PH.js} +1 -1
- package/dist/client/assets/pie-LRSECV5Y-FXFFfdz2.js +1 -0
- package/dist/client/assets/pieDiagram-4H26LBE5-BfpvCRmo.js +30 -0
- package/dist/client/assets/{pug-BOaafhT5.js → pug-BCLaymlV.js} +1 -1
- package/dist/client/assets/{qml-CURE-1y4.js → qml-BpKydcOi.js} +1 -1
- package/dist/client/assets/quadrantDiagram-W4KKPZXB-CJY6un0-.js +7 -0
- package/dist/client/assets/{r-CPWmgzo3.js → r-BzCB9dpd.js} +1 -1
- package/dist/client/assets/radar-GUYGQ44K-BKTzSQ68.js +1 -0
- package/dist/client/assets/{razor-DH83VP_d.js → razor-BWmujqiQ.js} +1 -1
- package/dist/client/assets/{regexp-BXVWi-t_.js → regexp-urCI3YiZ.js} +1 -1
- package/dist/client/assets/requirementDiagram-4Y6WPE33-DIHWguS0.js +84 -0
- package/dist/client/assets/rough.esm-BBrzuQ0N.js +1 -0
- package/dist/client/assets/{rst-Bbxxs6E2.js → rst-YTDC9H6u.js} +1 -1
- package/dist/client/assets/{ruby-C-TuWES4.js → ruby-v1IXQAFZ.js} +1 -1
- package/dist/client/assets/sankeyDiagram-5OEKKPKP-DJRPEO_F.js +40 -0
- package/dist/client/assets/{sas-njzNYNOq.js → sas-KDUG3BSk.js} +1 -1
- package/dist/client/assets/{scss-D2i6rHB8.js → scss-pLIGFpjD.js} +1 -1
- package/dist/client/assets/sequenceDiagram-3UESZ5HK-DX0jVCOU.js +162 -0
- package/dist/client/assets/{shaderlab-DXAhMdPo.js → shaderlab-CbbQwRPF.js} +1 -1
- package/dist/client/assets/{shellscript-D6Bxi40C.js → shellscript-B5ZTyEsK.js} +1 -1
- package/dist/client/assets/{shellsession-Defxq-I6.js → shellsession-DVK3bw4p.js} +1 -1
- package/dist/client/assets/{soy-D0GKXrtT.js → soy-BXgyj2-T.js} +1 -1
- package/dist/client/assets/{sparql-BdcL8cbr.js → sparql-DEoxFJ5d.js} +1 -1
- package/dist/client/assets/{sql-BRW_ADkU.js → sql-G2vZCmw6.js} +1 -1
- package/dist/client/assets/src-D9zkqEEX.js +1 -0
- package/dist/client/assets/{stata-w7w59F48.js → stata-C4mEnygx.js} +1 -1
- package/dist/client/assets/stateDiagram-AJRCARHV-AMtMxKgQ.js +1 -0
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-C89CM-_4.js +1 -0
- package/dist/client/assets/{surrealql-O-lLuqz7.js → surrealql-Bf8gsTcP.js} +1 -1
- package/dist/client/assets/{svelte-DN3DR5-t.js → svelte-C1oIyl9t.js} +1 -1
- package/dist/client/assets/{templ-CJ2JFoia.js → templ-PJcWUuTF.js} +1 -1
- package/dist/client/assets/{tex-CpWrf5g9.js → tex-B7Fnujsi.js} +1 -1
- package/dist/client/assets/timeline-definition-PNZ67QCA-BZJSqcAg.js +120 -0
- package/dist/client/assets/treeView-BLDUP644-DtjHwb6x.js +1 -0
- package/dist/client/assets/treemap-LRROVOQU-GqTXzMFU.js +1 -0
- package/dist/client/assets/{ts-tags-CZ6Af6Pe.js → ts-tags-CAAsFnUt.js} +1 -1
- package/dist/client/assets/{tsx-gIUSpRih.js → tsx-DAKyTsWW.js} +1 -1
- package/dist/client/assets/{twig-DwZINhmw.js → twig-Bd4dieZF.js} +1 -1
- package/dist/client/assets/{typescript-Dj2aMECR.js → typescript-8myByDGd.js} +1 -1
- package/dist/client/assets/vennDiagram-CIIHVFJN-CMjBZNs2.js +34 -0
- package/dist/client/assets/{vue-B5K0aWrP.js → vue-fBA2B-QU.js} +1 -1
- package/dist/client/assets/{vue-html-DKGgrypd.js → vue-html-DEZECpsR.js} +1 -1
- package/dist/client/assets/{vue-vine-B5iDYTCh.js → vue-vine-B_gG939R.js} +1 -1
- package/dist/client/assets/wardley-L42UT6IY-pROVAAhW.js +1 -0
- package/dist/client/assets/wardleyDiagram-YWT4CUSO-qeHmPLyL.js +78 -0
- package/dist/client/assets/{xml-CbTzxfc-.js → xml-DbXNss3s.js} +1 -1
- package/dist/client/assets/{xsl-Dj-Be6nx.js → xsl-B7_dgqqc.js} +1 -1
- package/dist/client/assets/xychartDiagram-2RQKCTM6-NN2Et7YL.js +7 -0
- package/dist/client/assets/{yaml-rTK8uEjh.js → yaml-DSyIApdJ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/{mcp-CE77qfjx.mjs → mcp-BWeWR4yd.mjs} +2 -1
- package/dist/plan-format-f62JWFI-.mjs +117 -0
- package/dist/{server-lock-DpCrvL-Z.mjs → server-lock-Dj9fG9md.mjs} +246 -122
- package/package.json +26 -14
- package/dist/cli-agent-BLPX3oOP.mjs +0 -443
- package/dist/cli-agent-BNwY5lZE.mjs +0 -446
- package/dist/cli-agent-CbzXDSSF.mjs +0 -446
- package/dist/cli-agent-Cfuy9fLC.mjs +0 -179
- package/dist/cli-agent-CtNivjQW.mjs +0 -179
- package/dist/cli-agent-DZBvZoXZ.mjs +0 -179
- package/dist/cli-agent-DsqbS3n7.mjs +0 -443
- package/dist/cli-agent-Vbs-ePHv.mjs +0 -443
- package/dist/cli-agent-WzJPMH52.mjs +0 -174
- package/dist/cli-agent-uYUoCA4X.mjs +0 -179
- package/dist/client/assets/index-Bwv7BWUp.js +0 -3959
- package/dist/mcp-BQmH_By-.mjs +0 -119
- package/dist/mcp-CFeE13n-.mjs +0 -254
- package/dist/mcp-CLgzC8_a.mjs +0 -119
- package/dist/mcp-D9E2HQZ9.mjs +0 -119
- package/dist/mcp-DKlThflu.mjs +0 -253
- package/dist/mcp-LVbLV17O.mjs +0 -119
- package/dist/mcp-SGCrfYGo.mjs +0 -254
- package/dist/mcp-nMwTAut3.mjs +0 -119
- package/dist/server-lock-B4crz9-_.mjs +0 -708
- package/dist/server-lock-BbiF7u3F.mjs +0 -493
- package/dist/server-lock-Bx9ixiF6.mjs +0 -575
- package/dist/server-lock-CAdqFSfl.mjs +0 -691
- package/dist/server-lock-DIkImokU.mjs +0 -487
- package/dist/server-lock-DbxpfF3j.mjs +0 -575
- package/dist/update-check-DJuFE9w6.mjs +0 -128
- package/dist/update-check-DSC02MWQ.mjs +0 -129
- package/dist/client/assets/{abap-CkQ0tsz-.js → abap-cWYhO34H.js} +0 -0
- package/dist/client/assets/{actionscript-3-DHm0ZfF4.js → actionscript-3-B80ndJsD.js} +0 -0
- package/dist/client/assets/{ada-DK_ua92Z.js → ada-C0C0SoYB.js} +0 -0
- package/dist/client/assets/{andromeeda-D2ccw8v4.js → andromeeda-Ch2kWS7s.js} +0 -0
- package/dist/client/assets/{apache-DXtpFu7o.js → apache-DBwBm4bB.js} +0 -0
- package/dist/client/assets/{apex-DlnRRWpl.js → apex-FcV7UxQi.js} +0 -0
- package/dist/client/assets/{applescript-CShk62ad.js → applescript-hQs8VNpS.js} +0 -0
- package/dist/client/assets/{ara-CWN-FDkm.js → ara-BOadBOZg.js} +0 -0
- package/dist/client/assets/{asciidoc-Bg-PM9Vn.js → asciidoc-BsfqiBck.js} +0 -0
- package/dist/client/assets/{asm-DKOhR24S.js → asm-C2vUqzqz.js} +0 -0
- package/dist/client/assets/{aurora-x-DpaEirL8.js → aurora-x-BHLqzt7j.js} +0 -0
- package/dist/client/assets/{awk-3ibKHffv.js → awk-D4e-eeZT.js} +0 -0
- package/dist/client/assets/{ayu-dark-ursiSH5k.js → ayu-dark-DCotN_ZT.js} +0 -0
- package/dist/client/assets/{ayu-light-DMjlrKmx.js → ayu-light-CKB6o_im.js} +0 -0
- package/dist/client/assets/{ayu-mirage-DZEMb75h.js → ayu-mirage-B38lW-C5.js} +0 -0
- package/dist/client/assets/{ballerina-xx7VNGxs.js → ballerina-BzvFqbha.js} +0 -0
- package/dist/client/assets/{bat-DGYsZ5wR.js → bat-CFyw2ERn.js} +0 -0
- package/dist/client/assets/{beancount-D_ATR1UV.js → beancount-a6Gsu5WX.js} +0 -0
- package/dist/client/assets/{berry-BqYMORfH.js → berry-C2jyz4Ah.js} +0 -0
- package/dist/client/assets/{bibtex-CQTBDajW.js → bibtex-Dyet-B83.js} +0 -0
- package/dist/client/assets/{bicep-CfNtxPrK.js → bicep-CghvUIvy.js} +0 -0
- package/dist/client/assets/{bird2-C0dUPe3I.js → bird2-nHigvs-R.js} +0 -0
- package/dist/client/assets/{c3-BUwZ5e3k.js → c3-9ZrcmAmr.js} +0 -0
- package/dist/client/assets/{cadence-C3OKDYrr.js → cadence-BPDgYNWy.js} +0 -0
- package/dist/client/assets/{catppuccin-frappe-CMnRXVZP.js → catppuccin-frappe-BY_2ofjk.js} +0 -0
- package/dist/client/assets/{catppuccin-latte-CqhXSjCC.js → catppuccin-latte-D6GXPBmA.js} +0 -0
- package/dist/client/assets/{catppuccin-macchiato-B6kneE0C.js → catppuccin-macchiato-BuW8_cyo.js} +0 -0
- package/dist/client/assets/{catppuccin-mocha-CP8YZpvj.js → catppuccin-mocha-znex9DPe.js} +0 -0
- package/dist/client/assets/{clarity-Crxe8gSr.js → clarity-Cmao5KSK.js} +0 -0
- package/dist/client/assets/{clojure-BuzOUPMr.js → clojure-D2dPuTkk.js} +0 -0
- package/dist/client/assets/{cmake-CCpZLdLI.js → cmake-DaqQuj4A.js} +0 -0
- package/dist/client/assets/{codeowners-Bo1VYpHz.js → codeowners-B748jmvc.js} +0 -0
- package/dist/client/assets/{codeql-Db01hzHE.js → codeql-CXb_8gHR.js} +0 -0
- package/dist/client/assets/{common-lisp-CMlJIc6Y.js → common-lisp-UZn9nc_5.js} +0 -0
- package/dist/client/assets/{coq-BlzBYp3f.js → coq-DtO6yBIf.js} +0 -0
- package/dist/client/assets/{csharp-kfNshAa7.js → csharp-C3YlYcLV.js} +0 -0
- package/dist/client/assets/{csv-Dr8ri5Nd.js → csv-BBwiVr0X.js} +0 -0
- package/dist/client/assets/{cue-jXfhyOwY.js → cue-ScBFXY5f.js} +0 -0
- package/dist/client/assets/{cypher-CayLeiaR.js → cypher-CGStaC1X.js} +0 -0
- package/dist/client/assets/{d-BMZQ0mAy.js → d-CuTCIvnX.js} +0 -0
- package/dist/client/assets/{dark-plus-B__q47Nr.js → dark-plus-BvePH-r3.js} +0 -0
- package/dist/client/assets/{dart-gMOEMyuz.js → dart-CqEodbm2.js} +0 -0
- package/dist/client/assets/{dax-C3t5LadU.js → dax-UdE5S0Dh.js} +0 -0
- package/dist/client/assets/{desktop-DmhXLwLR.js → desktop-DfmsfHJA.js} +0 -0
- package/dist/client/assets/{diff-togOXQX6.js → diff-CW0RWGd9.js} +0 -0
- package/dist/client/assets/{docker--6N01olR.js → docker-B1qtW4ha.js} +0 -0
- package/dist/client/assets/{dotenv-Bq930Yx8.js → dotenv-BUzOh1k8.js} +0 -0
- package/dist/client/assets/{dracula-CBkSghcw.js → dracula-kJQjC_Gy.js} +0 -0
- package/dist/client/assets/{dracula-soft-CCSYeoxL.js → dracula-soft-B3XhEjCl.js} +0 -0
- package/dist/client/assets/{dream-maker-BiMf3DHl.js → dream-maker-CBvk18nW.js} +0 -0
- package/dist/client/assets/{emacs-lisp-Dhht7he2.js → emacs-lisp-DvSgLhDc.js} +0 -0
- package/dist/client/assets/{everforest-dark-NqSz4oHS.js → everforest-dark-u0zLn75q.js} +0 -0
- package/dist/client/assets/{everforest-light-a3Yzs_lT.js → everforest-light-D3syIoAR.js} +0 -0
- package/dist/client/assets/{fennel-BExc9TZf.js → fennel-C-1kWlGf.js} +0 -0
- package/dist/client/assets/{fish-C2p-AJmZ.js → fish-CCpdtwqS.js} +0 -0
- package/dist/client/assets/{fluent-CeYzs8Qy.js → fluent-BLg032oD.js} +0 -0
- package/dist/client/assets/{fortran-free-form-BIQEK2Jw.js → fortran-free-form-Br4jRtSc.js} +0 -0
- package/dist/client/assets/{gdscript-B5JQPh_-.js → gdscript-hSAZM1eh.js} +0 -0
- package/dist/client/assets/{gdshader-vVjJ_Qr8.js → gdshader-D4XNAEs9.js} +0 -0
- package/dist/client/assets/{genie-DGl5rqC8.js → genie-DmN-ICAN.js} +0 -0
- package/dist/client/assets/{gherkin-CY_VLUnD.js → gherkin-cuHHmRcX.js} +0 -0
- package/dist/client/assets/{github-dark-ClO4AStc.js → github-dark-CEBLh0--.js} +0 -0
- package/dist/client/assets/{github-dark-default-CM0dPco8.js → github-dark-default-Dq0Z4Xai.js} +0 -0
- package/dist/client/assets/{github-dark-dimmed-Cb6xxpXY.js → github-dark-dimmed-D90YmS4w.js} +0 -0
- package/dist/client/assets/{github-dark-high-contrast-BpYtx2-M.js → github-dark-high-contrast-B6BWv1iC.js} +0 -0
- package/dist/client/assets/{github-light-default-BmnCWL6x.js → github-light-default-DsPWmj4v.js} +0 -0
- package/dist/client/assets/{github-light-high-contrast-rpagNF5O.js → github-light-high-contrast-C7aLb3kp.js} +0 -0
- package/dist/client/assets/{github-light-CeOVFgf_.js → github-light-lXSVSq5T.js} +0 -0
- package/dist/client/assets/{gleam-BarIG9kQ.js → gleam-D8VhqPDN.js} +0 -0
- package/dist/client/assets/{gn-DhYsTPYc.js → gn-BjGRlX27.js} +0 -0
- package/dist/client/assets/{gnuplot-CucLlTBc.js → gnuplot-BZCXuE4q.js} +0 -0
- package/dist/client/assets/{go-toi-uWvg.js → go-BJpWXZEs.js} +0 -0
- package/dist/client/assets/{groovy-Ds4dis_k.js → groovy-D9w3Dn-z.js} +0 -0
- package/dist/client/assets/{gruvbox-dark-hard-aPiW8pQh.js → gruvbox-dark-hard-_deksD0U.js} +0 -0
- package/dist/client/assets/{gruvbox-dark-medium-2HJCvzsS.js → gruvbox-dark-medium-CT31284j.js} +0 -0
- package/dist/client/assets/{gruvbox-dark-soft-Da97ODX9.js → gruvbox-dark-soft-BZuEUCbj.js} +0 -0
- package/dist/client/assets/{gruvbox-light-hard-BXlEipFe.js → gruvbox-light-hard-dxv4n_ZS.js} +0 -0
- package/dist/client/assets/{gruvbox-light-medium-Cun-GjGk.js → gruvbox-light-medium-BsW_fVfR.js} +0 -0
- package/dist/client/assets/{gruvbox-light-soft-Bd3qiiSw.js → gruvbox-light-soft-BiGy99gD.js} +0 -0
- package/dist/client/assets/{haskell-B_jvz5Po.js → haskell-DqDdKEgk.js} +0 -0
- package/dist/client/assets/{haxe-BRbuo5UO.js → haxe-DMg0jS3a.js} +0 -0
- package/dist/client/assets/{hcl-BzNUCtsK.js → hcl-SZIZUK_X.js} +0 -0
- package/dist/client/assets/{hjson-BxVo3sS2.js → hjson-DF1VspiE.js} +0 -0
- package/dist/client/assets/{hlsl-C8Wes1nf.js → hlsl-DPe9L0Dt.js} +0 -0
- package/dist/client/assets/{horizon-D54PjLAY.js → horizon-Ngud75Kn.js} +0 -0
- package/dist/client/assets/{horizon-bright-D9RccdIr.js → horizon-bright-Cnljd0XD.js} +0 -0
- package/dist/client/assets/{houston-CMu1N_4K.js → houston-BenZxlMr.js} +0 -0
- package/dist/client/assets/{hy-dMrJKKU_.js → hy-B0f-B1mH.js} +0 -0
- package/dist/client/assets/{imba-DG_oO8tg.js → imba-Bq3j1cW5.js} +0 -0
- package/dist/client/assets/{ini-geHw79Aj.js → ini-BayloP2U.js} +0 -0
- package/dist/client/assets/{json5-CDUsNqz6.js → json5-CRSfdIrY.js} +0 -0
- package/dist/client/assets/{jsonc-D48xcwyD.js → jsonc-y4g9DQPx.js} +0 -0
- package/dist/client/assets/{jsonl-cpIYDXEe.js → jsonl-D2paVn9r.js} +0 -0
- package/dist/client/assets/{jsonnet-Da1lhszN.js → jsonnet-CLH27mtz.js} +0 -0
- package/dist/client/assets/{jssm-B8BLrEGL.js → jssm-CPPYaqX-.js} +0 -0
- package/dist/client/assets/{kanagawa-dragon-2Sk8Rx83.js → kanagawa-dragon-Bt5mhLu1.js} +0 -0
- package/dist/client/assets/{kanagawa-lotus-CQVSWfJp.js → kanagawa-lotus-FGA3pRJk.js} +0 -0
- package/dist/client/assets/{kanagawa-wave-BKWNioLX.js → kanagawa-wave-DnLVZPAD.js} +0 -0
- package/dist/client/assets/{kdl-B-2tSRmp.js → kdl-D1BQjjnw.js} +0 -0
- package/dist/client/assets/{kotlin-DepsEzh3.js → kotlin-ndCaDnf0.js} +0 -0
- package/dist/client/assets/{kusto-BcCtXIb8.js → kusto-D-4nbkD3.js} +0 -0
- package/dist/client/assets/{laserwave-BUqkO46F.js → laserwave-D2qAZkaG.js} +0 -0
- package/dist/client/assets/{lean-CNO9ffpX.js → lean-YmtAsB0p.js} +0 -0
- package/dist/client/assets/{less-Dj0T9pWC.js → less-DyHub3ow.js} +0 -0
- package/dist/client/assets/{light-plus-C9Hnuc1T.js → light-plus-CP13QUvm.js} +0 -0
- package/dist/client/assets/{llvm-DbV9b66l.js → llvm-DCcqrY0D.js} +0 -0
- package/dist/client/assets/{log-BMblmPyH.js → log-DWAAMNbe.js} +0 -0
- package/dist/client/assets/{logo-B78qMxws.js → logo-ClQMGkNL.js} +0 -0
- package/dist/client/assets/{luau-BUd8xsdE.js → luau-CmcXFvLt.js} +0 -0
- package/dist/client/assets/{make-Ev-6cJrd.js → make-DcfJyUzm.js} +0 -0
- package/dist/client/assets/{markdown-CS4LI6sN.js → markdown-DXxotAxn.js} +0 -0
- package/dist/client/assets/{material-theme-5rA1zn0D.js → material-theme-JFoPTBSr.js} +0 -0
- package/dist/client/assets/{material-theme-darker-DGOEiL65.js → material-theme-darker-BHaqZ19v.js} +0 -0
- package/dist/client/assets/{material-theme-lighter-CULfFRX0.js → material-theme-lighter-CGnnS2bs.js} +0 -0
- package/dist/client/assets/{material-theme-ocean-LQZ9CleP.js → material-theme-ocean-Drhewjqf.js} +0 -0
- package/dist/client/assets/{material-theme-palenight-Di_f5oks.js → material-theme-palenight-DbUypmpb.js} +0 -0
- package/dist/client/assets/{matlab-C0qEtAx9.js → matlab-BlfbTznR.js} +0 -0
- package/dist/client/assets/{mdx-CzvnbLXb.js → mdx-H8fyPQd8.js} +0 -0
- package/dist/client/assets/{mermaid-BPquZaKt.js → mermaid-CWAV2dmV.js} +0 -0
- package/dist/client/assets/{min-dark-rkuIbtJV.js → min-dark-BEelvXUB.js} +0 -0
- package/dist/client/assets/{min-light-Dj_HOGsr.js → min-light-CTPBOACR.js} +0 -0
- package/dist/client/assets/{mipsasm-k5DQoS6j.js → mipsasm-BWYFAm2s.js} +0 -0
- package/dist/client/assets/{mojo-sTTDuNnR.js → mojo-DZcPJEIN.js} +0 -0
- package/dist/client/assets/{monokai-D5p-u2ij.js → monokai-DQlZXbkg.js} +0 -0
- package/dist/client/assets/{moonbit-j5ChFtBC.js → moonbit-6eslNlJL.js} +0 -0
- package/dist/client/assets/{move-Dj0Gmn6R.js → move-DxFc9-QU.js} +0 -0
- package/dist/client/assets/{narrat-DJgMz2oW.js → narrat-gv38dJOT.js} +0 -0
- package/dist/client/assets/{nextflow-groovy-eDLuZ_oi.js → nextflow-groovy-DV4H3hok.js} +0 -0
- package/dist/client/assets/{night-owl-CEdtPl3A.js → night-owl-BLcrYhaC.js} +0 -0
- package/dist/client/assets/{night-owl-light-GdSldewm.js → night-owl-light-BG-tBJOz.js} +0 -0
- package/dist/client/assets/{nix-CxIPS3pb.js → nix-NlOz3HGM.js} +0 -0
- package/dist/client/assets/{nord-3WFAz5H9.js → nord-Dm2F5_QB.js} +0 -0
- package/dist/client/assets/{nushell-GDfBmoBU.js → nushell-D86Z8aFF.js} +0 -0
- package/dist/client/assets/{objective-c-MFQ_MGUa.js → objective-c-iuJvUjCv.js} +0 -0
- package/dist/client/assets/{objective-cpp-Y83LcBE7.js → objective-cpp-C62JvfMT.js} +0 -0
- package/dist/client/assets/{ocaml-FxYfaJxu.js → ocaml-Rbryo9lg.js} +0 -0
- package/dist/client/assets/{odin-CprihY3z.js → odin-CfaOVNP_.js} +0 -0
- package/dist/client/assets/{one-dark-pro-BB6mpmmA.js → one-dark-pro-DC9SHdX0.js} +0 -0
- package/dist/client/assets/{one-light-BYLoxNtg.js → one-light-Doo39p0H.js} +0 -0
- package/dist/client/assets/{openscad-Bc1FbpOi.js → openscad-DCRhgheK.js} +0 -0
- package/dist/client/assets/{pascal-Bb4d8cUL.js → pascal-CTesTHtV.js} +0 -0
- package/dist/client/assets/{pierre-dark-cF19s-RM.js → pierre-dark-Gtc9M_gS.js} +0 -0
- package/dist/client/assets/{pierre-light-BdpX8OpD.js → pierre-light-EDxMHmhf.js} +0 -0
- package/dist/client/assets/{pkl-tM25Sq0w.js → pkl-BjUMjQkt.js} +0 -0
- package/dist/client/assets/{plastic-D3k2Xp_5.js → plastic-B3CcSpon.js} +0 -0
- package/dist/client/assets/{plsql-CLEfFhSj.js → plsql-BgKEZSSo.js} +0 -0
- package/dist/client/assets/{po-CaAzdrrh.js → po-C5s_CVt8.js} +0 -0
- package/dist/client/assets/{poimandres-bPTvT4Da.js → poimandres-BsK70ma5.js} +0 -0
- package/dist/client/assets/{polar-TG7aTGdK.js → polar-y4hX8qM0.js} +0 -0
- package/dist/client/assets/{postcss-8kqfa9f0.js → postcss-BHCm7Qae.js} +0 -0
- package/dist/client/assets/{powerquery-CtSu1Nfd.js → powerquery-C2vMt8aZ.js} +0 -0
- package/dist/client/assets/{powershell-87lLBHMy.js → powershell-DEcjmg1c.js} +0 -0
- package/dist/client/assets/{prisma-Dnf694DZ.js → prisma-Ba-Tcxy5.js} +0 -0
- package/dist/client/assets/{prolog-C0XbHTWa.js → prolog-B4o261Jv.js} +0 -0
- package/dist/client/assets/{proto-BZP-HLIb.js → proto-o3IZKiMR.js} +0 -0
- package/dist/client/assets/{puppet-CJYWgYC5.js → puppet-BC0dqZ31.js} +0 -0
- package/dist/client/assets/{purescript-B95Q6BAd.js → purescript-CLDw1y5G.js} +0 -0
- package/dist/client/assets/{python-CqTCsi7P.js → python-D1NFioGW.js} +0 -0
- package/dist/client/assets/{qmldir-DlIL4hls.js → qmldir-B7i9e9Vn.js} +0 -0
- package/dist/client/assets/{qss-BW72E0q_.js → qss-DIe6oKX8.js} +0 -0
- package/dist/client/assets/{racket-CCAimEsC.js → racket-CAOC_yyk.js} +0 -0
- package/dist/client/assets/{raku-mSuqElkM.js → raku-Ccua3vW6.js} +0 -0
- package/dist/client/assets/{red-C-DA4yBI.js → red-DtDH206x.js} +0 -0
- package/dist/client/assets/{reg-CGwMBJ2c.js → reg-CAYZT9kE.js} +0 -0
- package/dist/client/assets/{rel-JwrbYgX4.js → rel-cvB6F5nO.js} +0 -0
- package/dist/client/assets/{riscv-CoYyE75b.js → riscv-SYGtE4EN.js} +0 -0
- package/dist/client/assets/{ron-C5wrhnmJ.js → ron-CRt869SB.js} +0 -0
- package/dist/client/assets/{rose-pine-DlrMPYYd.js → rose-pine-CjBfSJ5b.js} +0 -0
- package/dist/client/assets/{rose-pine-dawn-DBHFtZD2.js → rose-pine-dawn-C0xwy7iJ.js} +0 -0
- package/dist/client/assets/{rose-pine-moon-BBfrpe5q.js → rose-pine-moon-BSNy9Ty0.js} +0 -0
- package/dist/client/assets/{rosmsg-DlimTR9t.js → rosmsg-BwrfJ66Z.js} +0 -0
- package/dist/client/assets/{rust-Cxnkxx3h.js → rust-DCKTTPxN.js} +0 -0
- package/dist/client/assets/{sass-DEyqUIpU.js → sass-CorcwySt.js} +0 -0
- package/dist/client/assets/{scala-i3RN3yAN.js → scala-DlI-OmYj.js} +0 -0
- package/dist/client/assets/{scheme-BcKdjYkO.js → scheme-CcNEGkZH.js} +0 -0
- package/dist/client/assets/{sdbl-Be_XWc3n.js → sdbl-AL_m-RO_.js} +0 -0
- package/dist/client/assets/{slack-dark-3PDtm74m.js → slack-dark-rMhrfPm6.js} +0 -0
- package/dist/client/assets/{slack-ochin-CwiuFRJD.js → slack-ochin-9w8X74hU.js} +0 -0
- package/dist/client/assets/{smalltalk-BdBlL6ks.js → smalltalk-Y2ogrfbk.js} +0 -0
- package/dist/client/assets/{snazzy-light-Dyqc8Is2.js → snazzy-light-DHr9ygpP.js} +0 -0
- package/dist/client/assets/{solarized-dark-DZiTscIX.js → solarized-dark-UPcKev9Q.js} +0 -0
- package/dist/client/assets/{solarized-light-DBcfxrfG.js → solarized-light-DUsjROvl.js} +0 -0
- package/dist/client/assets/{solidity-DH-yUSWc.js → solidity-Bfbm9QZQ.js} +0 -0
- package/dist/client/assets/{splunk-CIjc4kxs.js → splunk-6DJA2XFa.js} +0 -0
- package/dist/client/assets/{ssh-config-BKVd4drG.js → ssh-config-CbQtdF-u.js} +0 -0
- package/dist/client/assets/{stylus-tqAsmugv.js → stylus-B_iq45Sj.js} +0 -0
- package/dist/client/assets/{swift-CdDBQ3_Z.js → swift-C7KIgN-p.js} +0 -0
- package/dist/client/assets/{synthwave-84-CbbRs73K.js → synthwave-84-lf_Wt0K_.js} +0 -0
- package/dist/client/assets/{system-verilog-CBqEY-9v.js → system-verilog-C3G9wERd.js} +0 -0
- package/dist/client/assets/{systemd-khRP5zwK.js → systemd-CwxGrojj.js} +0 -0
- package/dist/client/assets/{talonscript-D_lneH-6.js → talonscript-DqnSo1EO.js} +0 -0
- package/dist/client/assets/{tasl-CLymawGe.js → tasl-T8ethp86.js} +0 -0
- package/dist/client/assets/{tcl-BRY1S87U.js → tcl-Ccl366o0.js} +0 -0
- package/dist/client/assets/{terraform-BqohMo2f.js → terraform-BBf6ZI_0.js} +0 -0
- package/dist/client/assets/{tokyo-night-BWUONxad.js → tokyo-night-BF5ata25.js} +0 -0
- package/dist/client/assets/{toml-C4c85Lwg.js → toml-C5Z_BR4r.js} +0 -0
- package/dist/client/assets/{tsv-DsZq9HBQ.js → tsv-rX8sUJs7.js} +0 -0
- package/dist/client/assets/{turtle-Dpr8zLlw.js → turtle-BM-MG8WY.js} +0 -0
- package/dist/client/assets/{typespec-DcqgATB-.js → typespec-C5gKydvR.js} +0 -0
- package/dist/client/assets/{typst-CeVxmuPg.js → typst--_5VchrY.js} +0 -0
- package/dist/client/assets/{v-C2Qsh2-1.js → v-C6PwpmTC.js} +0 -0
- package/dist/client/assets/{vala-CvZ3yL7c.js → vala-4pTaceSj.js} +0 -0
- package/dist/client/assets/{vb-B_p4qDw4.js → vb-DhoR5A6S.js} +0 -0
- package/dist/client/assets/{verilog-D8MGjxD7.js → verilog-B9f_1lPn.js} +0 -0
- package/dist/client/assets/{vesper-DossXiE1.js → vesper-CmBi_iCT.js} +0 -0
- package/dist/client/assets/{vhdl-MLyYh_Gz.js → vhdl-Cy4RAmOe.js} +0 -0
- package/dist/client/assets/{viml-BuYIArcz.js → viml-CFQW-CcD.js} +0 -0
- package/dist/client/assets/{vitesse-black-C3kc1227.js → vitesse-black-CE8RoBJP.js} +0 -0
- package/dist/client/assets/{vitesse-dark-kWOu5V36.js → vitesse-dark-BpF142p_.js} +0 -0
- package/dist/client/assets/{vitesse-light-DMtiL8bK.js → vitesse-light-CMwxPBrg.js} +0 -0
- package/dist/client/assets/{vyper-CNqG09-_.js → vyper-BW5zj9bE.js} +0 -0
- package/dist/client/assets/{wasm-BsQDV7Y0.js → wasm-BigxSLnm.js} +0 -0
- package/dist/client/assets/{wasm-DFVlQlgd.js → wasm-CSccDuAq.js} +0 -0
- package/dist/client/assets/{wenyan-BmOQge13.js → wenyan-BZzM1SQT.js} +0 -0
- package/dist/client/assets/{wgsl-CgWrmvIg.js → wgsl-xBPHbLlq.js} +0 -0
- package/dist/client/assets/{wikitext-DkwckXCk.js → wikitext-BXJWUNJK.js} +0 -0
- package/dist/client/assets/{wit-CtyVFGe7.js → wit-2GnUs67P.js} +0 -0
- package/dist/client/assets/{wolfram-CXE4FrKz.js → wolfram-DIFDmw-g.js} +0 -0
- package/dist/client/assets/{zenscript-DH15wJt0.js → zenscript-DQaUUjbx.js} +0 -0
- package/dist/client/assets/{zig-CqXN9brD.js → zig-BDepRvBi.js} +0 -0
- package/dist/{update-check-KL-2sGxE.mjs → update-check-DAPFCqpF.mjs} +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as
|
|
2
|
+
import { C as toSafeRelativePath, S as isSafePath, _ as getUntrackedFilePathsAsync, a as formatComments, b as listRepoFiles, c as getFileContent, d as getMergeStatus, f as getProjectStorageDir, g as getTabSizeForFiles, h as getShowDiff, i as writeServerLock, l as getGitDiffAsync, m as getRepoRoot, o as getBranchName, p as getRepoName, r as removeServerLock, s as getCustomGitDiffAsync, u as getHunkHistory, v as gitAddFile, x as revertHunk, y as isGitRepo } from "./server-lock-Dj9fG9md.mjs";
|
|
3
|
+
import { n as formatPlanReview, r as sectionTitleForLine, t as extractPlanLines } from "./plan-format-f62JWFI-.mjs";
|
|
4
|
+
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
3
5
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { dirname, extname, join, resolve } from "node:path";
|
|
6
|
+
import { dirname, extname, isAbsolute, join, resolve } from "node:path";
|
|
5
7
|
import { existsSync, mkdirSync, readFileSync, watch, writeFileSync } from "node:fs";
|
|
6
8
|
import getPort from "get-port";
|
|
7
|
-
import { parseArgs } from "node:util";
|
|
8
|
-
import { execFileSync } from "node:child_process";
|
|
9
|
+
import { parseArgs, promisify } from "node:util";
|
|
9
10
|
import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
10
11
|
import { homedir } from "node:os";
|
|
11
12
|
import { Hono } from "hono";
|
|
@@ -48,7 +49,11 @@ const DEFAULTS$1 = {
|
|
|
48
49
|
help: false,
|
|
49
50
|
version: false,
|
|
50
51
|
includeUnstaged: true,
|
|
51
|
-
includeUntracked: true
|
|
52
|
+
includeUntracked: true,
|
|
53
|
+
tui: false,
|
|
54
|
+
gpu: false,
|
|
55
|
+
showMode: false,
|
|
56
|
+
showRevspecs: []
|
|
52
57
|
};
|
|
53
58
|
/**
|
|
54
59
|
* All git-diff option definitions for `node:util.parseArgs`.
|
|
@@ -173,6 +178,15 @@ const DIFFING_OPTIONS = {
|
|
|
173
178
|
type: "boolean",
|
|
174
179
|
default: false
|
|
175
180
|
},
|
|
181
|
+
tui: {
|
|
182
|
+
type: "boolean",
|
|
183
|
+
default: false
|
|
184
|
+
},
|
|
185
|
+
gpu: {
|
|
186
|
+
type: "boolean",
|
|
187
|
+
default: false
|
|
188
|
+
},
|
|
189
|
+
"gh-pr": { type: "string" },
|
|
176
190
|
help: {
|
|
177
191
|
type: "boolean",
|
|
178
192
|
short: "h"
|
|
@@ -209,18 +223,37 @@ Output Modes:
|
|
|
209
223
|
- Terminal mode: when output is a pipe, redirect, or non-TTY
|
|
210
224
|
- Web mode: when output is a TTY (interactive terminal)
|
|
211
225
|
|
|
212
|
-
Force a mode with --web or --
|
|
226
|
+
Force a mode with --web, --terminal, or --tui flags.
|
|
227
|
+
--tui is experimental and opt-in. It opens a native-Rust terminal
|
|
228
|
+
interface that mirrors the web dashboard. Falls back to git diff
|
|
229
|
+
output if the environment cannot support a TUI (piped stdin, CI,
|
|
230
|
+
missing binary).
|
|
231
|
+
|
|
232
|
+
Subcommands:
|
|
233
|
+
show <revspec>... Drop-in for 'git show'. Renders commit metadata
|
|
234
|
+
(subject, author, date, message body) above the
|
|
235
|
+
diff in the web UI. Accepts single commits,
|
|
236
|
+
ranges (a..b, a...b), tags, and multiple SHAs.
|
|
237
|
+
Use '--' to separate revspecs from pathspecs.
|
|
213
238
|
|
|
214
239
|
Examples:
|
|
215
240
|
diffing Review uncommitted changes (web UI)
|
|
216
241
|
diffing --staged Review staged changes
|
|
217
242
|
diffing HEAD~3 Review last 3 commits
|
|
218
243
|
diffing main..feature Compare branches
|
|
244
|
+
diffing show HEAD Review a single commit's metadata + diff
|
|
245
|
+
diffing show HEAD~3..HEAD Review the last 3 commits as a series
|
|
246
|
+
diffing show HEAD -- src/ Limit a commit review to a directory
|
|
219
247
|
diffing --diff-algorithm=patience src/ Diff with patience algorithm
|
|
220
248
|
diffing -U10 Diff with 10 context lines
|
|
221
249
|
diffing --word-diff=color Word-level diff in color
|
|
222
250
|
diffing -b -w Ignore whitespace changes
|
|
223
|
-
diffing --host 0.0.0.0 Allow other machines on the LAN to review
|
|
251
|
+
diffing --host 0.0.0.0 Allow other machines on the LAN to review
|
|
252
|
+
diffing --tui Open the experimental TUI (native Rust)
|
|
253
|
+
|
|
254
|
+
Note: 'diffing <sha>' runs 'git diff <sha>' (working tree vs <sha>); it
|
|
255
|
+
is not the same as 'diffing show <sha>'. Use 'show' when you want to
|
|
256
|
+
review a commit's changes.`);
|
|
224
257
|
}
|
|
225
258
|
function printGitDiffHelp() {
|
|
226
259
|
console.log(`
|
|
@@ -464,7 +497,8 @@ function parseDiffOptions(rawArgs) {
|
|
|
464
497
|
}
|
|
465
498
|
for (let i = dashDashIdx + 1; i < rawArgs.length; i++) opts.pathspecs.push(rawArgs[i]);
|
|
466
499
|
} else for (const p of positionals) opts.revisions.push(p);
|
|
467
|
-
if (values.
|
|
500
|
+
if (values.tui) opts.outputMode = "tui";
|
|
501
|
+
else if (values.web) opts.outputMode = "web";
|
|
468
502
|
else if (values.terminal) opts.outputMode = "terminal";
|
|
469
503
|
if (process.stdout.isTTY && opts.outputMode === "auto") opts.outputMode = "web";
|
|
470
504
|
else if (!process.stdout.isTTY && opts.outputMode === "auto") opts.outputMode = "terminal";
|
|
@@ -545,6 +579,21 @@ function buildGitDiffArgs(opts) {
|
|
|
545
579
|
}
|
|
546
580
|
return args;
|
|
547
581
|
}
|
|
582
|
+
/**
|
|
583
|
+
* Transform a `parseDiffOptions(...)` result into a "show mode" variant:
|
|
584
|
+
* moves any parsed revisions into `showRevspecs`, clears `revisions` (so the
|
|
585
|
+
* existing diff-engine path does not try to `git diff` them), and sets the
|
|
586
|
+
* `showMode` flag. Pathspecs and every other flag survive unchanged.
|
|
587
|
+
*
|
|
588
|
+
* Returns the same object for callsite ergonomics — the mutation is in-place
|
|
589
|
+
* because `parseDiffOptions` always returns a fresh object.
|
|
590
|
+
*/
|
|
591
|
+
function intoShowMode(opts) {
|
|
592
|
+
opts.showRevspecs = opts.revisions;
|
|
593
|
+
opts.revisions = [];
|
|
594
|
+
opts.showMode = true;
|
|
595
|
+
return opts;
|
|
596
|
+
}
|
|
548
597
|
//#endregion
|
|
549
598
|
//#region src/lib/diff-engine.ts
|
|
550
599
|
const MAX_BUFFER = 50 * 1024 * 1024;
|
|
@@ -553,10 +602,11 @@ const MAX_BUFFER = 50 * 1024 * 1024;
|
|
|
553
602
|
* or a "custom" mode (specific revisions / pathspecs).
|
|
554
603
|
*
|
|
555
604
|
* Custom mode is used when the user provides explicit revisions or pathspecs
|
|
556
|
-
* beyond just toggling staged/untracked.
|
|
605
|
+
* beyond just toggling staged/untracked. `showMode` is *also* a custom mode
|
|
606
|
+
* — the "Show staged / Show untracked" toggles don't apply to a commit view.
|
|
557
607
|
*/
|
|
558
608
|
function isCustomMode$1(opts) {
|
|
559
|
-
return opts.revisions.length > 0 || opts.pathspecs.length > 0;
|
|
609
|
+
return opts.revisions.length > 0 || opts.pathspecs.length > 0 || opts.showMode;
|
|
560
610
|
}
|
|
561
611
|
function parseFilePaths(patch) {
|
|
562
612
|
const paths = /* @__PURE__ */ new Set();
|
|
@@ -603,8 +653,24 @@ function parseBinaryFiles(patch, untrackedFiles) {
|
|
|
603
653
|
}
|
|
604
654
|
/**
|
|
605
655
|
* Execute a diff asynchronously — used by the server.
|
|
656
|
+
*
|
|
657
|
+
* Show mode bypasses the normal `git diff` path entirely: it resolves
|
|
658
|
+
* revspecs to a commit list and fetches per-commit metadata + patches in
|
|
659
|
+
* one `git log` call. The concatenated patch is returned in the same shape
|
|
660
|
+
* the existing UI pipeline expects (so file tree, binary detection, and
|
|
661
|
+
* tab-size detection all keep working unchanged); the per-commit data
|
|
662
|
+
* rides along on `commits` for the metadata banners.
|
|
606
663
|
*/
|
|
607
664
|
async function executeDiffAsync(opts) {
|
|
665
|
+
if (opts.showMode) {
|
|
666
|
+
const { commits, patch, truncated } = await getShowDiff(opts.showRevspecs, opts.pathspecs);
|
|
667
|
+
return {
|
|
668
|
+
patch,
|
|
669
|
+
args: [],
|
|
670
|
+
commits,
|
|
671
|
+
truncated
|
|
672
|
+
};
|
|
673
|
+
}
|
|
608
674
|
if (isCustomMode$1(opts)) {
|
|
609
675
|
const args = buildGitDiffArgs(opts);
|
|
610
676
|
return {
|
|
@@ -624,10 +690,10 @@ async function executeDiffAsync(opts) {
|
|
|
624
690
|
* Execute a diff and produce the full enriched result used by the web API.
|
|
625
691
|
*/
|
|
626
692
|
async function executeDiffWithMeta(opts) {
|
|
627
|
-
const { patch } = await executeDiffAsync(opts);
|
|
693
|
+
const { patch, commits, truncated } = await executeDiffAsync(opts);
|
|
628
694
|
const repoName = getRepoName();
|
|
629
695
|
const branch = getBranchName();
|
|
630
|
-
const untrackedFiles = opts.includeUntracked ? await getUntrackedFilePathsAsync() : [];
|
|
696
|
+
const untrackedFiles = opts.includeUntracked && !opts.showMode ? await getUntrackedFilePathsAsync() : [];
|
|
631
697
|
const binaryFiles = parseBinaryFiles(patch, new Set(untrackedFiles));
|
|
632
698
|
const filePaths = parseFilePaths(patch);
|
|
633
699
|
return {
|
|
@@ -637,14 +703,19 @@ async function executeDiffWithMeta(opts) {
|
|
|
637
703
|
tabSizeMap: getTabSizeForFiles(filePaths),
|
|
638
704
|
untrackedFiles,
|
|
639
705
|
repoName,
|
|
640
|
-
branch
|
|
706
|
+
branch,
|
|
707
|
+
...commits ? { commits } : {},
|
|
708
|
+
...truncated ? { truncated } : {}
|
|
641
709
|
};
|
|
642
710
|
}
|
|
643
711
|
/**
|
|
644
712
|
* Run the diff in terminal mode: forward to git diff with full flags,
|
|
645
|
-
* output to stdout. Behaves identically to `git diff`.
|
|
713
|
+
* output to stdout. Behaves identically to `git diff`. In show mode this
|
|
714
|
+
* delegates to `git show` instead so `diffing show <rev>... --terminal`
|
|
715
|
+
* is a drop-in for `git show <rev>...`.
|
|
646
716
|
*/
|
|
647
717
|
function runTerminalDiff(opts) {
|
|
718
|
+
if (opts.showMode) return runTerminalShow(opts);
|
|
648
719
|
const args = buildGitDiffArgs(opts);
|
|
649
720
|
const enforceDefaults = !opts.outputFormat && !opts.quiet;
|
|
650
721
|
const finalArgs = [];
|
|
@@ -665,6 +736,30 @@ function runTerminalDiff(opts) {
|
|
|
665
736
|
}
|
|
666
737
|
}
|
|
667
738
|
/**
|
|
739
|
+
* Terminal mode for `diffing show`: spawn `git show` with the user's revspecs
|
|
740
|
+
* and pathspecs, inherit stdio, propagate exit code. We pass `--no-ext-diff`
|
|
741
|
+
* so the output is a clean unified diff regardless of the user's git config
|
|
742
|
+
* (matches the web-mode pipeline), but otherwise hand everything to git.
|
|
743
|
+
*/
|
|
744
|
+
function runTerminalShow(opts) {
|
|
745
|
+
const args = [
|
|
746
|
+
"show",
|
|
747
|
+
"--no-ext-diff",
|
|
748
|
+
...opts.showRevspecs
|
|
749
|
+
];
|
|
750
|
+
if (opts.pathspecs.length > 0) args.push("--", ...opts.pathspecs);
|
|
751
|
+
try {
|
|
752
|
+
execFileSync("git", args, {
|
|
753
|
+
encoding: "utf-8",
|
|
754
|
+
maxBuffer: MAX_BUFFER,
|
|
755
|
+
stdio: "inherit"
|
|
756
|
+
});
|
|
757
|
+
return 0;
|
|
758
|
+
} catch (err) {
|
|
759
|
+
return err.status ?? 1;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
668
763
|
* Validate the environment: check that we're in a git repo.
|
|
669
764
|
* Returns an error message or null.
|
|
670
765
|
*/
|
|
@@ -1438,6 +1533,62 @@ var FileUiStateStore = class {
|
|
|
1438
1533
|
}
|
|
1439
1534
|
};
|
|
1440
1535
|
//#endregion
|
|
1536
|
+
//#region src/lib/editor-launcher.ts
|
|
1537
|
+
function resolveEditorCommand(editor, absolutePath, platform = process.platform, env = process.env) {
|
|
1538
|
+
switch (editor) {
|
|
1539
|
+
case "vscode": return guiEditorCommand("code", absolutePath, platform);
|
|
1540
|
+
case "zed": return guiEditorCommand("zed", absolutePath, platform);
|
|
1541
|
+
case "vim": return terminalEditorCommand("vim", absolutePath, platform, env);
|
|
1542
|
+
case "neovim": return terminalEditorCommand("nvim", absolutePath, platform, env);
|
|
1543
|
+
default: return null;
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
function guiEditorCommand(bin, absolutePath, platform) {
|
|
1547
|
+
if (platform === "win32") return {
|
|
1548
|
+
cmd: `${bin}.cmd`,
|
|
1549
|
+
args: [absolutePath]
|
|
1550
|
+
};
|
|
1551
|
+
return {
|
|
1552
|
+
cmd: bin,
|
|
1553
|
+
args: [absolutePath]
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
function terminalEditorCommand(bin, absolutePath, platform, env) {
|
|
1557
|
+
if (platform === "darwin") return {
|
|
1558
|
+
cmd: "osascript",
|
|
1559
|
+
args: ["-e", `tell application "Terminal" to do script "${bin} \\"${absolutePath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}\\""`]
|
|
1560
|
+
};
|
|
1561
|
+
if (platform === "win32") return {
|
|
1562
|
+
cmd: "cmd.exe",
|
|
1563
|
+
args: [
|
|
1564
|
+
"/c",
|
|
1565
|
+
"start",
|
|
1566
|
+
"\"\"",
|
|
1567
|
+
"cmd.exe",
|
|
1568
|
+
"/k",
|
|
1569
|
+
bin,
|
|
1570
|
+
absolutePath
|
|
1571
|
+
]
|
|
1572
|
+
};
|
|
1573
|
+
const term = env.TERMINAL?.trim();
|
|
1574
|
+
if (term) return {
|
|
1575
|
+
cmd: term,
|
|
1576
|
+
args: [
|
|
1577
|
+
"-e",
|
|
1578
|
+
bin,
|
|
1579
|
+
absolutePath
|
|
1580
|
+
]
|
|
1581
|
+
};
|
|
1582
|
+
return {
|
|
1583
|
+
cmd: "x-terminal-emulator",
|
|
1584
|
+
args: [
|
|
1585
|
+
"-e",
|
|
1586
|
+
bin,
|
|
1587
|
+
absolutePath
|
|
1588
|
+
]
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
//#endregion
|
|
1441
1592
|
//#region src/lib/review-session.ts
|
|
1442
1593
|
var ReviewSession = class {
|
|
1443
1594
|
round = 0;
|
|
@@ -1630,6 +1781,483 @@ var PlanReviewSession = class {
|
|
|
1630
1781
|
}
|
|
1631
1782
|
};
|
|
1632
1783
|
//#endregion
|
|
1784
|
+
//#region src/lib/pr-session.ts
|
|
1785
|
+
var FilePrSessionStore = class {
|
|
1786
|
+
dirPath;
|
|
1787
|
+
filePath;
|
|
1788
|
+
constructor(storageDir) {
|
|
1789
|
+
this.dirPath = storageDir ?? getProjectStorageDir();
|
|
1790
|
+
this.filePath = join(this.dirPath, "pr-session.json");
|
|
1791
|
+
}
|
|
1792
|
+
async get() {
|
|
1793
|
+
try {
|
|
1794
|
+
const data = await readFile(this.filePath, "utf-8");
|
|
1795
|
+
return JSON.parse(data);
|
|
1796
|
+
} catch {
|
|
1797
|
+
return null;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
async save(session) {
|
|
1801
|
+
try {
|
|
1802
|
+
await mkdir(this.dirPath, { recursive: true });
|
|
1803
|
+
await writeFile(this.filePath, JSON.stringify(session, null, 2), "utf-8");
|
|
1804
|
+
} catch (err) {
|
|
1805
|
+
console.error("Failed to save pr-session to file:", err);
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
async set(session) {
|
|
1809
|
+
await this.save(session);
|
|
1810
|
+
}
|
|
1811
|
+
async update(fields) {
|
|
1812
|
+
const current = await this.get();
|
|
1813
|
+
if (!current) return null;
|
|
1814
|
+
const next = {
|
|
1815
|
+
...current,
|
|
1816
|
+
...fields
|
|
1817
|
+
};
|
|
1818
|
+
await this.save(next);
|
|
1819
|
+
return next;
|
|
1820
|
+
}
|
|
1821
|
+
async clear() {
|
|
1822
|
+
try {
|
|
1823
|
+
const { unlink } = await import("node:fs/promises");
|
|
1824
|
+
await unlink(this.filePath);
|
|
1825
|
+
} catch {}
|
|
1826
|
+
}
|
|
1827
|
+
};
|
|
1828
|
+
//#endregion
|
|
1829
|
+
//#region src/lib/github.ts
|
|
1830
|
+
const execFileAsync = promisify(execFile);
|
|
1831
|
+
/**
|
|
1832
|
+
* Probe the `gh` CLI: is it on `$PATH`, and is the user authenticated?
|
|
1833
|
+
* Never throws — returns a permissive `available: false` on any failure.
|
|
1834
|
+
*/
|
|
1835
|
+
async function detectGhCli() {
|
|
1836
|
+
try {
|
|
1837
|
+
const { stdout } = await execFileAsync("gh", ["--version"], { encoding: "utf-8" });
|
|
1838
|
+
const version = stdout.split("\n")[0]?.trim() || void 0;
|
|
1839
|
+
try {
|
|
1840
|
+
const result = await execFileAsync("gh", ["auth", "status"], { encoding: "utf-8" });
|
|
1841
|
+
return {
|
|
1842
|
+
available: true,
|
|
1843
|
+
authenticated: true,
|
|
1844
|
+
version,
|
|
1845
|
+
user: parseGhAuthStatusUser(`${result.stdout ?? ""}\n${result.stderr ?? ""}`)
|
|
1846
|
+
};
|
|
1847
|
+
} catch {
|
|
1848
|
+
return {
|
|
1849
|
+
available: true,
|
|
1850
|
+
authenticated: false,
|
|
1851
|
+
version
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
} catch {
|
|
1855
|
+
return {
|
|
1856
|
+
available: false,
|
|
1857
|
+
authenticated: false
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
/**
|
|
1862
|
+
* Extract the GitHub username from `gh auth status` output. Exported for
|
|
1863
|
+
* unit tests — see `__tests__/github-auth.test.ts`.
|
|
1864
|
+
*
|
|
1865
|
+
* Real-world samples:
|
|
1866
|
+
* `✓ Logged in to github.com account octocat (keyring)` (gh 2.40+)
|
|
1867
|
+
* `Logged in to github.com as octocat (oauth_token)` (older gh)
|
|
1868
|
+
* ` ✓ Logged in to github.com as octocat (~/.config/gh/hosts.yml)`
|
|
1869
|
+
*/
|
|
1870
|
+
function parseGhAuthStatusUser(output) {
|
|
1871
|
+
const account = /Logged in to [^\s]+ account\s+([^\s(]+)/.exec(output);
|
|
1872
|
+
if (account) return account[1];
|
|
1873
|
+
return /Logged in to [^\s]+ as\s+([^\s(]+)/.exec(output)?.[1];
|
|
1874
|
+
}
|
|
1875
|
+
/** Read the most-preferred GitHub token from the env. */
|
|
1876
|
+
function readGithubToken() {
|
|
1877
|
+
const env = process.env;
|
|
1878
|
+
return env.GH_TOKEN || env.GITHUB_TOKEN || env.GITHUB_API_TOKEN || null;
|
|
1879
|
+
}
|
|
1880
|
+
function parsePrRef(input, cwdRepo) {
|
|
1881
|
+
const trimmed = input.trim();
|
|
1882
|
+
const urlMatch = /^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/i.exec(trimmed);
|
|
1883
|
+
if (urlMatch) return {
|
|
1884
|
+
owner: urlMatch[1],
|
|
1885
|
+
repo: urlMatch[2],
|
|
1886
|
+
pullNumber: Number(urlMatch[3]),
|
|
1887
|
+
ref: trimmed
|
|
1888
|
+
};
|
|
1889
|
+
const shorthand = /^([^/\s]+)\/([^#\s]+)#(\d+)$/.exec(trimmed);
|
|
1890
|
+
if (shorthand) return {
|
|
1891
|
+
owner: shorthand[1],
|
|
1892
|
+
repo: shorthand[2],
|
|
1893
|
+
pullNumber: Number(shorthand[3]),
|
|
1894
|
+
ref: trimmed
|
|
1895
|
+
};
|
|
1896
|
+
const bare = /^#?(\d+)$/.exec(trimmed);
|
|
1897
|
+
if (bare && cwdRepo) return {
|
|
1898
|
+
owner: cwdRepo.owner,
|
|
1899
|
+
repo: cwdRepo.repo,
|
|
1900
|
+
pullNumber: Number(bare[1]),
|
|
1901
|
+
ref: trimmed
|
|
1902
|
+
};
|
|
1903
|
+
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\`.`);
|
|
1904
|
+
throw new Error(`Unrecognised PR ref: ${trimmed}`);
|
|
1905
|
+
}
|
|
1906
|
+
/**
|
|
1907
|
+
* Best-effort: derive `{ owner, repo }` for the current working directory
|
|
1908
|
+
* from `git remote get-url origin`. Falls back to throwing.
|
|
1909
|
+
*/
|
|
1910
|
+
async function detectCwdRepo() {
|
|
1911
|
+
try {
|
|
1912
|
+
const { stdout } = await execFileAsync("git", [
|
|
1913
|
+
"remote",
|
|
1914
|
+
"get-url",
|
|
1915
|
+
"origin"
|
|
1916
|
+
], { encoding: "utf-8" });
|
|
1917
|
+
const url = stdout.trim();
|
|
1918
|
+
const m = /github\.com[:/]([^/\s]+)\/([^/\s]+?)(?:\.git)?\s*$/.exec(url);
|
|
1919
|
+
if (m) return {
|
|
1920
|
+
owner: m[1],
|
|
1921
|
+
repo: m[2]
|
|
1922
|
+
};
|
|
1923
|
+
} catch {}
|
|
1924
|
+
return null;
|
|
1925
|
+
}
|
|
1926
|
+
/**
|
|
1927
|
+
* Fetch PR metadata + diff + existing review comments using `gh`.
|
|
1928
|
+
* Throws with a user-readable message on failure.
|
|
1929
|
+
*/
|
|
1930
|
+
async function fetchPrMetadataViaGh(resolved) {
|
|
1931
|
+
const args = ["-R", `${resolved.owner}/${resolved.repo}`];
|
|
1932
|
+
let metaJson;
|
|
1933
|
+
try {
|
|
1934
|
+
const { stdout } = await execFileAsync("gh", [
|
|
1935
|
+
"pr",
|
|
1936
|
+
"view",
|
|
1937
|
+
String(resolved.pullNumber),
|
|
1938
|
+
...args,
|
|
1939
|
+
"--json",
|
|
1940
|
+
"number,title,url,author,baseRefOid,headRefOid,additions,deletions,changedFiles,headRepositoryOwner,headRepository"
|
|
1941
|
+
], {
|
|
1942
|
+
encoding: "utf-8",
|
|
1943
|
+
maxBuffer: 20 * 1024 * 1024
|
|
1944
|
+
});
|
|
1945
|
+
metaJson = JSON.parse(stdout);
|
|
1946
|
+
} catch (err) {
|
|
1947
|
+
const stderr = err?.stderr || err?.message || "unknown error";
|
|
1948
|
+
throw new Error(`gh pr view failed: ${stderr.trim()}`);
|
|
1949
|
+
}
|
|
1950
|
+
let diff = "";
|
|
1951
|
+
try {
|
|
1952
|
+
const { stdout } = await execFileAsync("gh", [
|
|
1953
|
+
"pr",
|
|
1954
|
+
"diff",
|
|
1955
|
+
String(resolved.pullNumber),
|
|
1956
|
+
...args
|
|
1957
|
+
], {
|
|
1958
|
+
encoding: "utf-8",
|
|
1959
|
+
maxBuffer: 50 * 1024 * 1024
|
|
1960
|
+
});
|
|
1961
|
+
diff = stdout;
|
|
1962
|
+
} catch (err) {
|
|
1963
|
+
const stderr = err?.stderr || err?.message || "unknown error";
|
|
1964
|
+
throw new Error(`gh pr diff failed: ${stderr.trim()}`);
|
|
1965
|
+
}
|
|
1966
|
+
const headOwner = metaJson.headRepositoryOwner?.login || metaJson.headRepository?.owner?.login || resolved.owner;
|
|
1967
|
+
const repoName = metaJson.headRepository?.name || resolved.repo;
|
|
1968
|
+
const author = metaJson.author ? {
|
|
1969
|
+
login: metaJson.author.login,
|
|
1970
|
+
avatarUrl: metaJson.author.avatarUrl
|
|
1971
|
+
} : null;
|
|
1972
|
+
const existingComments = await fetchExistingCommentsViaGh(resolved);
|
|
1973
|
+
return {
|
|
1974
|
+
number: metaJson.number,
|
|
1975
|
+
title: metaJson.title,
|
|
1976
|
+
url: metaJson.url,
|
|
1977
|
+
author,
|
|
1978
|
+
baseSha: metaJson.baseRefOid,
|
|
1979
|
+
headSha: metaJson.headRefOid,
|
|
1980
|
+
additions: metaJson.additions,
|
|
1981
|
+
deletions: metaJson.deletions,
|
|
1982
|
+
changedFiles: metaJson.changedFiles,
|
|
1983
|
+
owner: headOwner,
|
|
1984
|
+
repo: repoName,
|
|
1985
|
+
diff,
|
|
1986
|
+
existingComments
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
async function fetchExistingCommentsViaGh(resolved) {
|
|
1990
|
+
`${resolved.owner}${resolved.repo}`;
|
|
1991
|
+
const endpoint = `repos/${resolved.owner}/${resolved.repo}/pulls/${resolved.pullNumber}/comments`;
|
|
1992
|
+
const reviewsEndpoint = `repos/${resolved.owner}/${resolved.repo}/pulls/${resolved.pullNumber}/reviews`;
|
|
1993
|
+
let reviews = [];
|
|
1994
|
+
try {
|
|
1995
|
+
const { stdout } = await execFileAsync("gh", [
|
|
1996
|
+
"api",
|
|
1997
|
+
reviewsEndpoint,
|
|
1998
|
+
"--paginate"
|
|
1999
|
+
], {
|
|
2000
|
+
encoding: "utf-8",
|
|
2001
|
+
maxBuffer: 20 * 1024 * 1024
|
|
2002
|
+
});
|
|
2003
|
+
const all = JSON.parse(stdout);
|
|
2004
|
+
if (Array.isArray(all)) reviews = all;
|
|
2005
|
+
} catch {}
|
|
2006
|
+
reviews.sort((a, b) => a.submitted_at < b.submitted_at ? 1 : -1);
|
|
2007
|
+
const recentReviews = reviews.slice(0, 50);
|
|
2008
|
+
const reviewStateById = /* @__PURE__ */ new Map();
|
|
2009
|
+
for (const r of recentReviews) reviewStateById.set(r.id, r.state);
|
|
2010
|
+
let commentsRaw = [];
|
|
2011
|
+
try {
|
|
2012
|
+
const { stdout } = await execFileAsync("gh", [
|
|
2013
|
+
"api",
|
|
2014
|
+
`${endpoint}?per_page=100`,
|
|
2015
|
+
"--paginate"
|
|
2016
|
+
], {
|
|
2017
|
+
encoding: "utf-8",
|
|
2018
|
+
maxBuffer: 20 * 1024 * 1024
|
|
2019
|
+
});
|
|
2020
|
+
const parsed = JSON.parse(stdout);
|
|
2021
|
+
if (Array.isArray(parsed)) commentsRaw = parsed;
|
|
2022
|
+
} catch {
|
|
2023
|
+
return [];
|
|
2024
|
+
}
|
|
2025
|
+
const byId = /* @__PURE__ */ new Map();
|
|
2026
|
+
for (const c of commentsRaw) byId.set(c.id, c);
|
|
2027
|
+
const replyByParent = /* @__PURE__ */ new Map();
|
|
2028
|
+
for (const c of commentsRaw) if (c.in_reply_to_id && byId.has(c.in_reply_to_id)) {
|
|
2029
|
+
const list = replyByParent.get(c.in_reply_to_id) ?? [];
|
|
2030
|
+
list.push(c);
|
|
2031
|
+
replyByParent.set(c.in_reply_to_id, list);
|
|
2032
|
+
}
|
|
2033
|
+
const tops = [];
|
|
2034
|
+
for (const c of commentsRaw) {
|
|
2035
|
+
if (c.in_reply_to_id) continue;
|
|
2036
|
+
const state = c.pull_request_review_id ? reviewStateById.get(c.pull_request_review_id) ?? null : null;
|
|
2037
|
+
tops.push({
|
|
2038
|
+
id: c.id,
|
|
2039
|
+
author: c.user ? {
|
|
2040
|
+
login: c.user.login,
|
|
2041
|
+
avatarUrl: c.user.avatar_url
|
|
2042
|
+
} : null,
|
|
2043
|
+
body: c.body ?? "",
|
|
2044
|
+
path: c.path,
|
|
2045
|
+
line: typeof c.line === "number" ? c.line : null,
|
|
2046
|
+
side: c.side === "LEFT" || c.side === "RIGHT" ? c.side : null,
|
|
2047
|
+
createdAt: c.created_at,
|
|
2048
|
+
updatedAt: c.updated_at,
|
|
2049
|
+
state,
|
|
2050
|
+
replies: (replyByParent.get(c.id) ?? []).map((r) => ({
|
|
2051
|
+
id: r.id,
|
|
2052
|
+
author: r.user ? {
|
|
2053
|
+
login: r.user.login,
|
|
2054
|
+
avatarUrl: r.user.avatar_url
|
|
2055
|
+
} : null,
|
|
2056
|
+
body: r.body ?? "",
|
|
2057
|
+
createdAt: r.created_at,
|
|
2058
|
+
updatedAt: r.updated_at
|
|
2059
|
+
})),
|
|
2060
|
+
isOutdated: typeof c.position === "number" && c.position === null ? false : false
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
tops.sort((a, b) => {
|
|
2064
|
+
if (a.path !== b.path) return a.path.localeCompare(b.path);
|
|
2065
|
+
return (a.line ?? 0) - (b.line ?? 0);
|
|
2066
|
+
});
|
|
2067
|
+
return tops;
|
|
2068
|
+
}
|
|
2069
|
+
/**
|
|
2070
|
+
* POST the review to GitHub. Prefers `gh api` (uses the user's existing
|
|
2071
|
+
* `gh auth`); falls back to a direct HTTP call with `$GITHUB_TOKEN` when
|
|
2072
|
+
* `gh` is unavailable.
|
|
2073
|
+
*/
|
|
2074
|
+
async function submitReview(input) {
|
|
2075
|
+
const gh = await detectGhCli();
|
|
2076
|
+
if (gh.available && gh.authenticated) return submitViaGh(input);
|
|
2077
|
+
const token = readGithubToken();
|
|
2078
|
+
if (token) return submitViaToken(input, token);
|
|
2079
|
+
return {
|
|
2080
|
+
ok: false,
|
|
2081
|
+
authSource: "none",
|
|
2082
|
+
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."
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
/**
|
|
2086
|
+
* Map our `PrDecision` to the GitHub REST event string. Exported so the
|
|
2087
|
+
* test suite can pin the mapping (including the `rejected → REQUEST_CHANGES`
|
|
2088
|
+
* quirk, which exists because GitHub has no REJECT event).
|
|
2089
|
+
*/
|
|
2090
|
+
function decisionToEvent(decision) {
|
|
2091
|
+
if (decision === "approve") return "APPROVE";
|
|
2092
|
+
if (decision === "request-changes") return "REQUEST_CHANGES";
|
|
2093
|
+
return "COMMENT";
|
|
2094
|
+
}
|
|
2095
|
+
/**
|
|
2096
|
+
* Expand multi-line `ReviewComment`s into N single-line `gh`-shaped entries
|
|
2097
|
+
* with `[part N/M]` prefixes. Exported for the test suite — the body of the
|
|
2098
|
+
* caller (`buildReviewPayload`) is otherwise private.
|
|
2099
|
+
*
|
|
2100
|
+
* Skips: non-`open` comments, file-level comments (`lineNumber === 0`).
|
|
2101
|
+
* Strips the `a/` / `b/` prefix from the file path (GitHub expects PR-relative
|
|
2102
|
+
* paths in the `path` field).
|
|
2103
|
+
*/
|
|
2104
|
+
function expandMultiLineComments(comments) {
|
|
2105
|
+
const flat = [];
|
|
2106
|
+
for (const c of comments) {
|
|
2107
|
+
if (c.status !== "open") continue;
|
|
2108
|
+
if (c.lineNumber === 0) continue;
|
|
2109
|
+
const path = stripBPrefix(c.filePath);
|
|
2110
|
+
const start = c.startLineNumber && c.startLineNumber < c.lineNumber ? c.startLineNumber : c.lineNumber;
|
|
2111
|
+
const end = c.lineNumber;
|
|
2112
|
+
const span = end - start + 1;
|
|
2113
|
+
if (span > 1) for (let i = 0; i < span; i++) flat.push({
|
|
2114
|
+
path,
|
|
2115
|
+
line: start + i,
|
|
2116
|
+
side: "RIGHT",
|
|
2117
|
+
body: `[part ${i + 1}/${span}]\n${c.body}`
|
|
2118
|
+
});
|
|
2119
|
+
else flat.push({
|
|
2120
|
+
path,
|
|
2121
|
+
line: end,
|
|
2122
|
+
side: "RIGHT",
|
|
2123
|
+
body: c.body
|
|
2124
|
+
});
|
|
2125
|
+
}
|
|
2126
|
+
return flat;
|
|
2127
|
+
}
|
|
2128
|
+
function buildReviewPayload(input) {
|
|
2129
|
+
return {
|
|
2130
|
+
body: input.body,
|
|
2131
|
+
event: decisionToEvent(input.decision ?? "comment"),
|
|
2132
|
+
comments: expandMultiLineComments(input.comments ?? [])
|
|
2133
|
+
};
|
|
2134
|
+
}
|
|
2135
|
+
function stripBPrefix(path) {
|
|
2136
|
+
if (path.startsWith("b/")) return path.slice(2);
|
|
2137
|
+
if (path.startsWith("a/")) return path.slice(2);
|
|
2138
|
+
return path;
|
|
2139
|
+
}
|
|
2140
|
+
async function submitViaGh(input) {
|
|
2141
|
+
const payload = buildReviewPayload(input);
|
|
2142
|
+
`${input.resolved.owner}${input.resolved.repo}`;
|
|
2143
|
+
const args = [
|
|
2144
|
+
"api",
|
|
2145
|
+
"--method",
|
|
2146
|
+
"POST",
|
|
2147
|
+
`repos/${input.resolved.owner}/${input.resolved.repo}/pulls/${input.resolved.pullNumber}/reviews`,
|
|
2148
|
+
"-H",
|
|
2149
|
+
"Accept: application/vnd.github+json",
|
|
2150
|
+
"--input",
|
|
2151
|
+
"-"
|
|
2152
|
+
];
|
|
2153
|
+
try {
|
|
2154
|
+
const { stdout } = await execFileAsync("gh", args, {
|
|
2155
|
+
encoding: "utf-8",
|
|
2156
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
2157
|
+
input: JSON.stringify(payload)
|
|
2158
|
+
});
|
|
2159
|
+
const result = JSON.parse(stdout);
|
|
2160
|
+
return {
|
|
2161
|
+
ok: true,
|
|
2162
|
+
authSource: "gh",
|
|
2163
|
+
reviewId: result.id,
|
|
2164
|
+
reviewUrl: result.html_url
|
|
2165
|
+
};
|
|
2166
|
+
} catch (err) {
|
|
2167
|
+
const stdout = err?.stdout || "";
|
|
2168
|
+
const stderr = err?.stderr || err?.message || "unknown error";
|
|
2169
|
+
let parsed = null;
|
|
2170
|
+
try {
|
|
2171
|
+
parsed = JSON.parse(stdout);
|
|
2172
|
+
} catch {}
|
|
2173
|
+
return {
|
|
2174
|
+
ok: false,
|
|
2175
|
+
authSource: "gh",
|
|
2176
|
+
error: parsed?.message || stderr.trim()
|
|
2177
|
+
};
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
async function submitViaToken(input, token) {
|
|
2181
|
+
const payload = buildReviewPayload(input);
|
|
2182
|
+
const url = `https://api.github.com/repos/${input.resolved.owner}/${input.resolved.repo}/pulls/${input.resolved.pullNumber}/reviews`;
|
|
2183
|
+
try {
|
|
2184
|
+
const res = await fetch(url, {
|
|
2185
|
+
method: "POST",
|
|
2186
|
+
headers: {
|
|
2187
|
+
Authorization: `Bearer ${token}`,
|
|
2188
|
+
Accept: "application/vnd.github+json",
|
|
2189
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
2190
|
+
"Content-Type": "application/json",
|
|
2191
|
+
"User-Agent": "diffing-cli"
|
|
2192
|
+
},
|
|
2193
|
+
body: JSON.stringify(payload)
|
|
2194
|
+
});
|
|
2195
|
+
if (!res.ok) {
|
|
2196
|
+
const errBody = await res.text().catch(() => "");
|
|
2197
|
+
return {
|
|
2198
|
+
ok: false,
|
|
2199
|
+
authSource: "token",
|
|
2200
|
+
error: `HTTP ${res.status}: ${errBody.slice(0, 500)}`
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
const result = await res.json();
|
|
2204
|
+
return {
|
|
2205
|
+
ok: true,
|
|
2206
|
+
authSource: "token",
|
|
2207
|
+
reviewId: result.id,
|
|
2208
|
+
reviewUrl: result.html_url
|
|
2209
|
+
};
|
|
2210
|
+
} catch (err) {
|
|
2211
|
+
return {
|
|
2212
|
+
ok: false,
|
|
2213
|
+
authSource: "token",
|
|
2214
|
+
error: err?.message || "Network error"
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
async function buildPrSession(ref) {
|
|
2219
|
+
const meta = await fetchPrMetadataViaGh(parsePrRef(ref, await detectCwdRepo() ?? void 0));
|
|
2220
|
+
return {
|
|
2221
|
+
ref,
|
|
2222
|
+
owner: meta.owner,
|
|
2223
|
+
repo: meta.repo,
|
|
2224
|
+
pullNumber: meta.number,
|
|
2225
|
+
baseSha: meta.baseSha,
|
|
2226
|
+
headSha: meta.headSha,
|
|
2227
|
+
title: meta.title,
|
|
2228
|
+
url: meta.url,
|
|
2229
|
+
author: meta.author,
|
|
2230
|
+
additions: meta.additions,
|
|
2231
|
+
deletions: meta.deletions,
|
|
2232
|
+
changedFiles: meta.changedFiles,
|
|
2233
|
+
diff: meta.diff,
|
|
2234
|
+
comments: [],
|
|
2235
|
+
existingComments: meta.existingComments
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
/** Refresh `diff` + `existingComments` + head SHA in an existing session. */
|
|
2239
|
+
async function refreshPrSession(session) {
|
|
2240
|
+
const meta = await fetchPrMetadataViaGh({
|
|
2241
|
+
owner: session.owner,
|
|
2242
|
+
repo: session.repo,
|
|
2243
|
+
pullNumber: session.pullNumber,
|
|
2244
|
+
ref: session.ref
|
|
2245
|
+
});
|
|
2246
|
+
return {
|
|
2247
|
+
...session,
|
|
2248
|
+
baseSha: meta.baseSha,
|
|
2249
|
+
headSha: meta.headSha,
|
|
2250
|
+
title: meta.title,
|
|
2251
|
+
url: meta.url,
|
|
2252
|
+
author: meta.author,
|
|
2253
|
+
additions: meta.additions,
|
|
2254
|
+
deletions: meta.deletions,
|
|
2255
|
+
changedFiles: meta.changedFiles,
|
|
2256
|
+
diff: meta.diff,
|
|
2257
|
+
existingComments: meta.existingComments
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
//#endregion
|
|
1633
2261
|
//#region src/server.ts
|
|
1634
2262
|
const MIME_TYPES = {
|
|
1635
2263
|
".html": "text/html",
|
|
@@ -1647,13 +2275,14 @@ const MIME_TYPES = {
|
|
|
1647
2275
|
".avif": "image/avif"
|
|
1648
2276
|
};
|
|
1649
2277
|
function isCustomMode(opts) {
|
|
1650
|
-
return opts.revisions.length > 0 || opts.pathspecs.length > 0;
|
|
2278
|
+
return opts.revisions.length > 0 || opts.pathspecs.length > 0 || opts.showMode;
|
|
1651
2279
|
}
|
|
1652
|
-
function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore) {
|
|
2280
|
+
function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, prSessionStore) {
|
|
1653
2281
|
const app = new Hono();
|
|
1654
2282
|
const customMode = isCustomMode(diffOpts);
|
|
1655
2283
|
const store = commentStore ?? new FileCommentStore();
|
|
1656
2284
|
const plans = planStore ?? new FilePlanStore();
|
|
2285
|
+
const prStore = prSessionStore ?? new FilePrSessionStore();
|
|
1657
2286
|
const uiStateStore = new FileUiStateStore();
|
|
1658
2287
|
const viewedFiles = /* @__PURE__ */ new Set();
|
|
1659
2288
|
const activeClients = /* @__PURE__ */ new Set();
|
|
@@ -1675,6 +2304,7 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore) {
|
|
|
1675
2304
|
mkdirSync(storageDir, { recursive: true });
|
|
1676
2305
|
let commentsDebounce = null;
|
|
1677
2306
|
let plansDebounce = null;
|
|
2307
|
+
let prSessionDebounce = null;
|
|
1678
2308
|
watch(storageDir, (_eventType, filename) => {
|
|
1679
2309
|
if (!filename) return;
|
|
1680
2310
|
if (filename.startsWith("comments.json")) {
|
|
@@ -1683,6 +2313,9 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore) {
|
|
|
1683
2313
|
} else if (filename.startsWith("plans.json")) {
|
|
1684
2314
|
if (plansDebounce) clearTimeout(plansDebounce);
|
|
1685
2315
|
plansDebounce = setTimeout(() => broadcast("plans", Date.now().toString()), 120);
|
|
2316
|
+
} else if (filename.startsWith("pr-session.json")) {
|
|
2317
|
+
if (prSessionDebounce) clearTimeout(prSessionDebounce);
|
|
2318
|
+
prSessionDebounce = setTimeout(() => broadcast("pr-session", Date.now().toString()), 120);
|
|
1686
2319
|
}
|
|
1687
2320
|
}).unref();
|
|
1688
2321
|
} catch (err) {
|
|
@@ -1743,11 +2376,40 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore) {
|
|
|
1743
2376
|
app.get("/api/diff", async (c) => {
|
|
1744
2377
|
const staged = c.req.query("staged") === "true";
|
|
1745
2378
|
const untracked = c.req.query("untracked") === "true";
|
|
1746
|
-
const
|
|
2379
|
+
const prSession = await prStore.get();
|
|
2380
|
+
if (prSession) {
|
|
2381
|
+
const binaryFiles = [];
|
|
2382
|
+
const filePaths = [];
|
|
2383
|
+
for (const line of prSession.diff.split("\n")) {
|
|
2384
|
+
const m = /^diff --git a\/.+ b\/(.+)$/.exec(line);
|
|
2385
|
+
if (m) filePaths.push(m[1]);
|
|
2386
|
+
}
|
|
2387
|
+
return c.json({
|
|
2388
|
+
patch: prSession.diff,
|
|
2389
|
+
repoName: prSession.repo,
|
|
2390
|
+
branch: `#${prSession.pullNumber}`,
|
|
2391
|
+
customMode: true,
|
|
2392
|
+
binaryFiles,
|
|
2393
|
+
tabSizeMap: {},
|
|
2394
|
+
untrackedFiles: [],
|
|
2395
|
+
prMode: true,
|
|
2396
|
+
prRef: prSession.ref,
|
|
2397
|
+
prOwner: prSession.owner,
|
|
2398
|
+
prRepo: prSession.repo,
|
|
2399
|
+
prPullNumber: prSession.pullNumber,
|
|
2400
|
+
prUrl: prSession.url,
|
|
2401
|
+
prTitle: prSession.title,
|
|
2402
|
+
prAuthor: prSession.author,
|
|
2403
|
+
prHeadSha: prSession.headSha,
|
|
2404
|
+
prBaseSha: prSession.baseSha
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
const optsForDiff = customMode ? diffOpts : {
|
|
1747
2408
|
...diffOpts,
|
|
1748
2409
|
staged,
|
|
1749
2410
|
includeUntracked: untracked
|
|
1750
|
-
}
|
|
2411
|
+
};
|
|
2412
|
+
const result = await executeDiffWithMeta(optsForDiff);
|
|
1751
2413
|
return c.json({
|
|
1752
2414
|
patch: result.patch,
|
|
1753
2415
|
repoName: result.repoName,
|
|
@@ -1755,7 +2417,10 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore) {
|
|
|
1755
2417
|
customMode,
|
|
1756
2418
|
binaryFiles: result.binaryFiles,
|
|
1757
2419
|
tabSizeMap: result.tabSizeMap,
|
|
1758
|
-
untrackedFiles: result.untrackedFiles
|
|
2420
|
+
untrackedFiles: result.untrackedFiles,
|
|
2421
|
+
showMode: optsForDiff.showMode || void 0,
|
|
2422
|
+
commits: result.commits,
|
|
2423
|
+
truncated: result.truncated
|
|
1759
2424
|
});
|
|
1760
2425
|
});
|
|
1761
2426
|
app.get("/api/file-content", (c) => {
|
|
@@ -1791,12 +2456,17 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore) {
|
|
|
1791
2456
|
const relPath = toSafeRelativePath(filePath, root);
|
|
1792
2457
|
if (!relPath) return c.json({ error: "Forbidden file path" }, 403);
|
|
1793
2458
|
const absolutePath = resolve(root, relPath);
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
2459
|
+
if (editor && editor !== "default") {
|
|
2460
|
+
const command = resolveEditorCommand(editor, absolutePath);
|
|
2461
|
+
if (command) {
|
|
2462
|
+
const { execFile } = await import("node:child_process");
|
|
2463
|
+
execFile(command.cmd, command.args, (err) => {
|
|
2464
|
+
if (err) console.error(`Failed to launch ${editor} for ${absolutePath}: ${err.message}`);
|
|
2465
|
+
});
|
|
2466
|
+
return c.json({ ok: true });
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
await (await import("open")).default(absolutePath);
|
|
1800
2470
|
return c.json({ ok: true });
|
|
1801
2471
|
} catch (err) {
|
|
1802
2472
|
return c.json({ error: `Failed to open file: ${err.message}` }, 500);
|
|
@@ -1881,7 +2551,7 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore) {
|
|
|
1881
2551
|
try {
|
|
1882
2552
|
const relPath = toSafeRelativePath(filePath, getRepoRoot());
|
|
1883
2553
|
if (!relPath) return c.json({ error: "Forbidden file path" }, 403);
|
|
1884
|
-
const history = getHunkHistory(relPath, deletionStart, deletionCount, diffOpts.revisions[0] || "HEAD");
|
|
2554
|
+
const history = getHunkHistory(relPath, deletionStart, deletionCount, diffOpts.showMode ? diffOpts.showRevspecs[diffOpts.showRevspecs.length - 1] || "HEAD" : diffOpts.revisions[0] || "HEAD");
|
|
1885
2555
|
return c.json(history);
|
|
1886
2556
|
} catch (err) {
|
|
1887
2557
|
return c.json({ error: err.message || "Failed to fetch hunk history" }, 500);
|
|
@@ -2027,6 +2697,193 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore) {
|
|
|
2027
2697
|
if (!await store.remove(id)) return c.json({ error: "Comment not found" }, 404);
|
|
2028
2698
|
return c.json({ ok: true });
|
|
2029
2699
|
});
|
|
2700
|
+
/** Shared helper: 404 with a stable shape so the client knows "not in PR mode". */
|
|
2701
|
+
const notInPrMode = (c) => c.json({
|
|
2702
|
+
error: "Not in PR review mode",
|
|
2703
|
+
prMode: false
|
|
2704
|
+
}, 404);
|
|
2705
|
+
app.get("/api/gh/session", async (c) => {
|
|
2706
|
+
const session = await prStore.get();
|
|
2707
|
+
if (!session) return notInPrMode(c);
|
|
2708
|
+
return c.json({
|
|
2709
|
+
prMode: true,
|
|
2710
|
+
ref: session.ref,
|
|
2711
|
+
owner: session.owner,
|
|
2712
|
+
repo: session.repo,
|
|
2713
|
+
pullNumber: session.pullNumber,
|
|
2714
|
+
baseSha: session.baseSha,
|
|
2715
|
+
headSha: session.headSha,
|
|
2716
|
+
title: session.title,
|
|
2717
|
+
url: session.url,
|
|
2718
|
+
author: session.author,
|
|
2719
|
+
additions: session.additions,
|
|
2720
|
+
deletions: session.deletions,
|
|
2721
|
+
changedFiles: session.changedFiles,
|
|
2722
|
+
existingComments: session.existingComments,
|
|
2723
|
+
submittedAt: session.submittedAt,
|
|
2724
|
+
submittedReviewId: session.submittedReviewId,
|
|
2725
|
+
submittedReviewUrl: session.submittedReviewUrl,
|
|
2726
|
+
authSource: session.authSource
|
|
2727
|
+
});
|
|
2728
|
+
});
|
|
2729
|
+
/** Re-fetch PR metadata (head SHA, diff, existing comments) and persist. */
|
|
2730
|
+
app.post("/api/gh/pr/refresh", async (c) => {
|
|
2731
|
+
const session = await prStore.get();
|
|
2732
|
+
if (!session) return notInPrMode(c);
|
|
2733
|
+
try {
|
|
2734
|
+
const refreshed = await refreshPrSession(session);
|
|
2735
|
+
await prStore.set(refreshed);
|
|
2736
|
+
return c.json({
|
|
2737
|
+
ok: true,
|
|
2738
|
+
headSha: refreshed.headSha
|
|
2739
|
+
});
|
|
2740
|
+
} catch (err) {
|
|
2741
|
+
return c.json({ error: err?.message ?? "Refresh failed" }, 500);
|
|
2742
|
+
}
|
|
2743
|
+
});
|
|
2744
|
+
/** Initialize a PR session from a ref like `1234`, `o/r#42`, or a GitHub URL. */
|
|
2745
|
+
app.post("/api/gh/pr/init", async (c) => {
|
|
2746
|
+
const body = await c.req.json().catch(() => ({}));
|
|
2747
|
+
const ref = typeof body?.ref === "string" ? body.ref : "";
|
|
2748
|
+
if (!ref.trim()) return c.json({ error: "ref is required" }, 400);
|
|
2749
|
+
try {
|
|
2750
|
+
parsePrRef(ref, await detectCwdRepo() ?? void 0);
|
|
2751
|
+
const session = await buildPrSession(ref);
|
|
2752
|
+
await prStore.set(session);
|
|
2753
|
+
return c.json({
|
|
2754
|
+
ok: true,
|
|
2755
|
+
ref: session.ref,
|
|
2756
|
+
owner: session.owner,
|
|
2757
|
+
repo: session.repo,
|
|
2758
|
+
pullNumber: session.pullNumber,
|
|
2759
|
+
url: session.url
|
|
2760
|
+
});
|
|
2761
|
+
} catch (err) {
|
|
2762
|
+
return c.json({ error: err?.message ?? "Failed to initialise PR session" }, 500);
|
|
2763
|
+
}
|
|
2764
|
+
});
|
|
2765
|
+
app.get("/api/gh/pr-session/comments", async (c) => {
|
|
2766
|
+
const session = await prStore.get();
|
|
2767
|
+
if (!session) return notInPrMode(c);
|
|
2768
|
+
return c.json(session.comments ?? []);
|
|
2769
|
+
});
|
|
2770
|
+
app.post("/api/gh/pr-session/comments", async (c) => {
|
|
2771
|
+
const body = await c.req.json();
|
|
2772
|
+
const session = await prStore.get();
|
|
2773
|
+
if (!session) return notInPrMode(c);
|
|
2774
|
+
const comment = {
|
|
2775
|
+
id: crypto.randomUUID(),
|
|
2776
|
+
filePath: body.filePath,
|
|
2777
|
+
side: body.side,
|
|
2778
|
+
lineNumber: body.lineNumber,
|
|
2779
|
+
startLineNumber: body.startLineNumber,
|
|
2780
|
+
lineContent: body.lineContent,
|
|
2781
|
+
body: body.body,
|
|
2782
|
+
status: "open",
|
|
2783
|
+
createdAt: Date.now(),
|
|
2784
|
+
replies: []
|
|
2785
|
+
};
|
|
2786
|
+
const next = {
|
|
2787
|
+
...session,
|
|
2788
|
+
comments: [...session.comments ?? [], comment]
|
|
2789
|
+
};
|
|
2790
|
+
await prStore.set(next);
|
|
2791
|
+
return c.json(comment, 201);
|
|
2792
|
+
});
|
|
2793
|
+
app.put("/api/gh/pr-session/comments/:id", async (c) => {
|
|
2794
|
+
const id = c.req.param("id");
|
|
2795
|
+
const { body, status } = await c.req.json();
|
|
2796
|
+
const session = await prStore.get();
|
|
2797
|
+
if (!session) return notInPrMode(c);
|
|
2798
|
+
const comments = (session.comments ?? []).map((cm) => cm.id === id ? {
|
|
2799
|
+
...cm,
|
|
2800
|
+
body: body ?? cm.body,
|
|
2801
|
+
status: status ?? cm.status
|
|
2802
|
+
} : cm);
|
|
2803
|
+
await prStore.set({
|
|
2804
|
+
...session,
|
|
2805
|
+
comments
|
|
2806
|
+
});
|
|
2807
|
+
const updated = comments.find((cm) => cm.id === id) ?? null;
|
|
2808
|
+
if (!updated) return c.json({ error: "Comment not found" }, 404);
|
|
2809
|
+
return c.json(updated);
|
|
2810
|
+
});
|
|
2811
|
+
app.delete("/api/gh/pr-session/comments/:id", async (c) => {
|
|
2812
|
+
const id = c.req.param("id");
|
|
2813
|
+
const session = await prStore.get();
|
|
2814
|
+
if (!session) return notInPrMode(c);
|
|
2815
|
+
const comments = (session.comments ?? []).filter((cm) => cm.id !== id);
|
|
2816
|
+
await prStore.set({
|
|
2817
|
+
...session,
|
|
2818
|
+
comments
|
|
2819
|
+
});
|
|
2820
|
+
return c.json({ ok: true });
|
|
2821
|
+
});
|
|
2822
|
+
app.post("/api/gh/pr-session/comments/:id/replies", async (c) => {
|
|
2823
|
+
const id = c.req.param("id");
|
|
2824
|
+
const { body, role, model } = await c.req.json();
|
|
2825
|
+
const session = await prStore.get();
|
|
2826
|
+
if (!session) return notInPrMode(c);
|
|
2827
|
+
const comments = (session.comments ?? []).map((cm) => {
|
|
2828
|
+
if (cm.id !== id) return cm;
|
|
2829
|
+
const reply = {
|
|
2830
|
+
id: crypto.randomUUID(),
|
|
2831
|
+
body,
|
|
2832
|
+
createdAt: Date.now(),
|
|
2833
|
+
role: role || (model ? "agent" : "user"),
|
|
2834
|
+
model: model || void 0
|
|
2835
|
+
};
|
|
2836
|
+
return {
|
|
2837
|
+
...cm,
|
|
2838
|
+
replies: [...cm.replies ?? [], reply]
|
|
2839
|
+
};
|
|
2840
|
+
});
|
|
2841
|
+
await prStore.set({
|
|
2842
|
+
...session,
|
|
2843
|
+
comments
|
|
2844
|
+
});
|
|
2845
|
+
return c.json(comments.find((cm) => cm.id === id));
|
|
2846
|
+
});
|
|
2847
|
+
/** Submit the current PR session's review (new comments + verdict + body) to GitHub. */
|
|
2848
|
+
app.post("/api/gh/submit", async (c) => {
|
|
2849
|
+
const session = await prStore.get();
|
|
2850
|
+
if (!session) return notInPrMode(c);
|
|
2851
|
+
const body = await c.req.json().catch(() => ({}));
|
|
2852
|
+
const decision = body.decision;
|
|
2853
|
+
if (decision !== "approve" && decision !== "comment" && decision !== "request-changes") return c.json({ error: "decision must be one of: approve, comment, request-changes" }, 400);
|
|
2854
|
+
const generalBody = typeof body.body === "string" ? body.body : "";
|
|
2855
|
+
const dryRun = body.dryRun === true;
|
|
2856
|
+
const result = await submitReview({
|
|
2857
|
+
resolved: {
|
|
2858
|
+
owner: session.owner,
|
|
2859
|
+
repo: session.repo,
|
|
2860
|
+
pullNumber: session.pullNumber,
|
|
2861
|
+
ref: session.ref
|
|
2862
|
+
},
|
|
2863
|
+
decision,
|
|
2864
|
+
body: generalBody,
|
|
2865
|
+
comments: session.comments ?? []
|
|
2866
|
+
});
|
|
2867
|
+
if (result.ok && !dryRun) {
|
|
2868
|
+
const next = {
|
|
2869
|
+
...session,
|
|
2870
|
+
submittedAt: Date.now(),
|
|
2871
|
+
submittedReviewId: result.reviewId,
|
|
2872
|
+
submittedReviewUrl: result.reviewUrl,
|
|
2873
|
+
authSource: result.authSource
|
|
2874
|
+
};
|
|
2875
|
+
await prStore.set(next);
|
|
2876
|
+
}
|
|
2877
|
+
return c.json({
|
|
2878
|
+
ok: result.ok,
|
|
2879
|
+
reviewId: result.reviewId,
|
|
2880
|
+
reviewUrl: result.reviewUrl,
|
|
2881
|
+
failedComments: result.failedComments ?? 0,
|
|
2882
|
+
authSource: result.authSource,
|
|
2883
|
+
error: result.error,
|
|
2884
|
+
dryRun
|
|
2885
|
+
});
|
|
2886
|
+
});
|
|
2030
2887
|
app.post("/api/review/send", async (c) => {
|
|
2031
2888
|
const body = await c.req.json().catch(() => ({}));
|
|
2032
2889
|
const generalComment = typeof body?.generalComment === "string" ? body.generalComment : void 0;
|
|
@@ -2269,7 +3126,11 @@ async function newestActivityMs(projectDir) {
|
|
|
2269
3126
|
return null;
|
|
2270
3127
|
}
|
|
2271
3128
|
};
|
|
2272
|
-
for (const name of [
|
|
3129
|
+
for (const name of [
|
|
3130
|
+
"comments.json",
|
|
3131
|
+
"plans.json",
|
|
3132
|
+
"pr-session.json"
|
|
3133
|
+
]) {
|
|
2273
3134
|
const file = join(projectDir, name);
|
|
2274
3135
|
if (!existsSync(file)) continue;
|
|
2275
3136
|
const m = await safeStat(file);
|
|
@@ -2320,14 +3181,35 @@ function startServer(options) {
|
|
|
2320
3181
|
cleanupStaleProjects().catch((err) => {
|
|
2321
3182
|
console.error("Failed to clean up stale projects:", err);
|
|
2322
3183
|
});
|
|
3184
|
+
let prMode = false;
|
|
3185
|
+
const prReady = (async () => {
|
|
3186
|
+
if (!options.prRef) return;
|
|
3187
|
+
console.error(`Building PR session for ${options.prRef}...`);
|
|
3188
|
+
try {
|
|
3189
|
+
const store = new FilePrSessionStore();
|
|
3190
|
+
const existing = await store.get();
|
|
3191
|
+
if (!existing || existing.ref !== options.prRef) {
|
|
3192
|
+
const session = await buildPrSession(options.prRef);
|
|
3193
|
+
await store.set(session);
|
|
3194
|
+
}
|
|
3195
|
+
prMode = true;
|
|
3196
|
+
} catch (err) {
|
|
3197
|
+
console.error(`[pr-session] failed to build session for ${options.prRef}: ${err?.message ?? err}`);
|
|
3198
|
+
}
|
|
3199
|
+
})();
|
|
2323
3200
|
const app = createApp(options.clientDir, options.diffOpts);
|
|
2324
3201
|
return new Promise((resolve) => {
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
3202
|
+
prReady.then(() => {
|
|
3203
|
+
serve({
|
|
3204
|
+
fetch: app.fetch,
|
|
3205
|
+
port: options.port,
|
|
3206
|
+
hostname: options.host
|
|
3207
|
+
}, (info) => {
|
|
3208
|
+
resolve({
|
|
3209
|
+
port: info.port,
|
|
3210
|
+
prMode
|
|
3211
|
+
});
|
|
3212
|
+
});
|
|
2331
3213
|
});
|
|
2332
3214
|
});
|
|
2333
3215
|
}
|
|
@@ -2715,8 +3597,68 @@ function playStartupDisplay() {
|
|
|
2715
3597
|
})();
|
|
2716
3598
|
}
|
|
2717
3599
|
//#endregion
|
|
3600
|
+
//#region src/lib/find-tui-binary.ts
|
|
3601
|
+
/**
|
|
3602
|
+
* Locate the `diffing-tui` native binary. Looks, in order:
|
|
3603
|
+
* 1. Sibling of the calling module (`dist/diffing-tui[.exe]` after build).
|
|
3604
|
+
* 2. `bin/diffing-tui[.exe]` next to the package root.
|
|
3605
|
+
* 3. `target/release/diffing-tui[.exe]` next to the package root
|
|
3606
|
+
* (cargo release build).
|
|
3607
|
+
* 4. `target/debug/diffing-tui[.exe]` next to the package root
|
|
3608
|
+
* (cargo debug build — the common case during development, especially
|
|
3609
|
+
* on Windows where release builds are slow).
|
|
3610
|
+
* 5. `$PATH` lookup via `which` / `where`.
|
|
3611
|
+
*
|
|
3612
|
+
* Returns the absolute path of the first match, or `null` if none are found.
|
|
3613
|
+
*
|
|
3614
|
+
* `callerUrl` is the `import.meta.url` of the caller — pass `import.meta.url`
|
|
3615
|
+
* from `cli.ts`. Exposed as a parameter so unit tests can pin the search
|
|
3616
|
+
* root to a known location instead of depending on the test runner's CWD.
|
|
3617
|
+
*/
|
|
3618
|
+
function findTuiBinary$1(callerUrl) {
|
|
3619
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
3620
|
+
const here = dirname(fileURLToPath(callerUrl));
|
|
3621
|
+
const candidates = [
|
|
3622
|
+
resolve(here, `diffing-tui${ext}`),
|
|
3623
|
+
resolve(here, "..", "bin", `diffing-tui${ext}`),
|
|
3624
|
+
resolve(here, "..", "target", "release", `diffing-tui${ext}`),
|
|
3625
|
+
resolve(here, "..", "..", "target", "release", `diffing-tui${ext}`),
|
|
3626
|
+
resolve(here, "..", "target", "debug", `diffing-tui${ext}`),
|
|
3627
|
+
resolve(here, "..", "..", "target", "debug", `diffing-tui${ext}`)
|
|
3628
|
+
];
|
|
3629
|
+
for (const c of candidates) if (existsSync(c)) return c;
|
|
3630
|
+
try {
|
|
3631
|
+
const out = execFileSync(process.platform === "win32" ? "where" : "which", ["diffing-tui"], {
|
|
3632
|
+
encoding: "utf-8",
|
|
3633
|
+
stdio: [
|
|
3634
|
+
"ignore",
|
|
3635
|
+
"pipe",
|
|
3636
|
+
"ignore"
|
|
3637
|
+
]
|
|
3638
|
+
}).trim();
|
|
3639
|
+
if (out) {
|
|
3640
|
+
const first = out.split(/\r?\n/)[0]?.trim();
|
|
3641
|
+
if (first && isAbsolute(first)) return first;
|
|
3642
|
+
}
|
|
3643
|
+
} catch {}
|
|
3644
|
+
return null;
|
|
3645
|
+
}
|
|
3646
|
+
//#endregion
|
|
2718
3647
|
//#region src/cli.ts
|
|
2719
3648
|
const args = process.argv.slice(2);
|
|
3649
|
+
let prRef = null;
|
|
3650
|
+
let ghPrConsumed = 0;
|
|
3651
|
+
if (args[0]?.startsWith("gh pr ") === true) {
|
|
3652
|
+
const rest = args[0].slice(6).trim();
|
|
3653
|
+
if (rest) {
|
|
3654
|
+
prRef = rest;
|
|
3655
|
+
ghPrConsumed = 1;
|
|
3656
|
+
}
|
|
3657
|
+
} else if (args[0] === "gh" && args[1] === "pr" && args[2] !== void 0) {
|
|
3658
|
+
prRef = args[2];
|
|
3659
|
+
ghPrConsumed = 3;
|
|
3660
|
+
}
|
|
3661
|
+
if (ghPrConsumed > 0) args.splice(0, ghPrConsumed);
|
|
2720
3662
|
if (new Set([
|
|
2721
3663
|
"await-review",
|
|
2722
3664
|
"reply",
|
|
@@ -2725,17 +3667,30 @@ if (new Set([
|
|
|
2725
3667
|
"url",
|
|
2726
3668
|
"mcp",
|
|
2727
3669
|
"plan",
|
|
2728
|
-
"update"
|
|
3670
|
+
"update",
|
|
3671
|
+
"gh"
|
|
2729
3672
|
]).has(args[0])) {
|
|
2730
3673
|
if (args[0] === "mcp") {
|
|
2731
|
-
const { startMcpServer } = await import("./mcp-
|
|
3674
|
+
const { startMcpServer } = await import("./mcp-BWeWR4yd.mjs");
|
|
2732
3675
|
await startMcpServer();
|
|
2733
3676
|
await new Promise(() => {});
|
|
2734
3677
|
}
|
|
2735
|
-
const { runSubcommand } = await import("./cli-agent-
|
|
3678
|
+
const { runSubcommand } = await import("./cli-agent-KxNAFIPf.mjs");
|
|
2736
3679
|
process.exit(await runSubcommand(args[0], args.slice(1)));
|
|
2737
3680
|
}
|
|
3681
|
+
let showSubcommand = false;
|
|
3682
|
+
if (args[0] === "show") {
|
|
3683
|
+
showSubcommand = true;
|
|
3684
|
+
args.shift();
|
|
3685
|
+
}
|
|
2738
3686
|
const opts = parseDiffOptions(args);
|
|
3687
|
+
if (showSubcommand) {
|
|
3688
|
+
if (opts.revisions.length === 0 && !opts.help && !opts.version) {
|
|
3689
|
+
console.error("Usage: diffing show <revspec>... [-- <pathspec>...]");
|
|
3690
|
+
process.exit(5);
|
|
3691
|
+
}
|
|
3692
|
+
intoShowMode(opts);
|
|
3693
|
+
}
|
|
2739
3694
|
if (opts.help) {
|
|
2740
3695
|
printHelp();
|
|
2741
3696
|
process.exit(0);
|
|
@@ -2751,6 +3706,10 @@ if (envErr) {
|
|
|
2751
3706
|
console.error(envErr);
|
|
2752
3707
|
process.exit(1);
|
|
2753
3708
|
}
|
|
3709
|
+
if (opts.outputMode === "tui") {
|
|
3710
|
+
const tuiResult = await launchTui(args);
|
|
3711
|
+
process.exit(tuiResult);
|
|
3712
|
+
}
|
|
2754
3713
|
if (opts.outputMode === "terminal") {
|
|
2755
3714
|
const exitCode = runTerminalDiff(opts);
|
|
2756
3715
|
process.exit(exitCode);
|
|
@@ -2759,7 +3718,7 @@ const __pkgDir = dirname(fileURLToPath(import.meta.url));
|
|
|
2759
3718
|
const currentVersion = JSON.parse(readFileSync(resolve(__pkgDir, "..", "package.json"), "utf-8")).version;
|
|
2760
3719
|
const updateCheckPromise = (async () => {
|
|
2761
3720
|
try {
|
|
2762
|
-
const { checkForUpdates } = await import("./update-check-
|
|
3721
|
+
const { checkForUpdates } = await import("./update-check-DAPFCqpF.mjs");
|
|
2763
3722
|
return await checkForUpdates(currentVersion);
|
|
2764
3723
|
} catch {
|
|
2765
3724
|
return null;
|
|
@@ -2768,12 +3727,12 @@ const updateCheckPromise = (async () => {
|
|
|
2768
3727
|
const port = await getPort(opts.port ? { port: opts.port } : void 0);
|
|
2769
3728
|
const host = opts.host;
|
|
2770
3729
|
const clientDir = resolve(__pkgDir, "client");
|
|
2771
|
-
const {
|
|
2772
|
-
const { port: actualPort } = await startServer({
|
|
3730
|
+
const { port: actualPort, prMode } = await startServer({
|
|
2773
3731
|
port,
|
|
2774
3732
|
host,
|
|
2775
|
-
clientDir: existsSync
|
|
2776
|
-
diffOpts: opts
|
|
3733
|
+
clientDir: existsSync(clientDir) ? clientDir : resolve(process.cwd(), "dist/client"),
|
|
3734
|
+
diffOpts: opts,
|
|
3735
|
+
prRef: prRef ?? void 0
|
|
2777
3736
|
});
|
|
2778
3737
|
const localUrl = `http://${host}:${actualPort}`;
|
|
2779
3738
|
try {
|
|
@@ -2789,7 +3748,9 @@ try {
|
|
|
2789
3748
|
pid: process.pid,
|
|
2790
3749
|
repoRoot,
|
|
2791
3750
|
startedAt: Date.now(),
|
|
2792
|
-
version: currentVersion
|
|
3751
|
+
version: currentVersion,
|
|
3752
|
+
mode: prMode ? "gh-pr" : "web",
|
|
3753
|
+
prRef: prMode ? prRef ?? void 0 : void 0
|
|
2793
3754
|
});
|
|
2794
3755
|
} catch {}
|
|
2795
3756
|
console.log(`diffing server running at ${localUrl}`);
|
|
@@ -2797,7 +3758,7 @@ await playStartupDisplay();
|
|
|
2797
3758
|
try {
|
|
2798
3759
|
const updateInfo = await updateCheckPromise;
|
|
2799
3760
|
if (updateInfo?.hasUpdate) {
|
|
2800
|
-
const { printUpdateDisclaimer } = await import("./update-check-
|
|
3761
|
+
const { printUpdateDisclaimer } = await import("./update-check-DAPFCqpF.mjs");
|
|
2801
3762
|
printUpdateDisclaimer(currentVersion, updateInfo.latestVersion);
|
|
2802
3763
|
}
|
|
2803
3764
|
} catch {}
|
|
@@ -2817,5 +3778,64 @@ const shutdown = () => {
|
|
|
2817
3778
|
};
|
|
2818
3779
|
process.on("SIGINT", shutdown);
|
|
2819
3780
|
process.on("SIGTERM", shutdown);
|
|
3781
|
+
/**
|
|
3782
|
+
* Wrapper around `findTuiBinary` that passes this script's `import.meta.url`
|
|
3783
|
+
* so the search paths anchor to the bundled CLI's directory. The real
|
|
3784
|
+
* implementation lives in `lib/find-tui-binary.ts` and is unit-tested there.
|
|
3785
|
+
*/
|
|
3786
|
+
function findTuiBinary() {
|
|
3787
|
+
return findTuiBinary$1(import.meta.url);
|
|
3788
|
+
}
|
|
3789
|
+
/**
|
|
3790
|
+
* Fall back to default `git diff` output when the TUI cannot run.
|
|
3791
|
+
* Re-parses `args` so the terminal output exactly matches `diffing` (no flag)
|
|
3792
|
+
* in a non-TTY context.
|
|
3793
|
+
*/
|
|
3794
|
+
function runTerminalFallback(args) {
|
|
3795
|
+
const terminalOpts = parseDiffOptions(args.filter((a) => a !== "--tui"));
|
|
3796
|
+
terminalOpts.outputMode = "terminal";
|
|
3797
|
+
terminalOpts.tui = false;
|
|
3798
|
+
return runTerminalDiff(terminalOpts);
|
|
3799
|
+
}
|
|
3800
|
+
/**
|
|
3801
|
+
* Launch the native-Rust TUI binary as a child process. Returns the process
|
|
3802
|
+
* exit code. If the TUI cannot start (no TTY, missing binary), prints a
|
|
3803
|
+
* single stderr line and falls back to the default `git diff` output.
|
|
3804
|
+
*/
|
|
3805
|
+
async function launchTui(args) {
|
|
3806
|
+
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
3807
|
+
console.error("diffing --tui requires a TTY; falling back to git diff");
|
|
3808
|
+
return runTerminalFallback(args);
|
|
3809
|
+
}
|
|
3810
|
+
const bin = findTuiBinary();
|
|
3811
|
+
if (!bin) {
|
|
3812
|
+
console.error("diffing-tui binary not found; build it with `pnpm build:tui`; falling back to git diff");
|
|
3813
|
+
return runTerminalFallback(args);
|
|
3814
|
+
}
|
|
3815
|
+
const forwarded = args.filter((a) => a !== "--tui");
|
|
3816
|
+
let repoRoot;
|
|
3817
|
+
try {
|
|
3818
|
+
repoRoot = getRepoRoot();
|
|
3819
|
+
} catch {
|
|
3820
|
+
repoRoot = process.cwd();
|
|
3821
|
+
}
|
|
3822
|
+
console.log(`diffing TUI starting (binary: ${bin})`);
|
|
3823
|
+
await playStartupDisplay();
|
|
3824
|
+
return new Promise((resolveP) => {
|
|
3825
|
+
const child = spawn(bin, [
|
|
3826
|
+
"--repo",
|
|
3827
|
+
repoRoot,
|
|
3828
|
+
...forwarded
|
|
3829
|
+
], {
|
|
3830
|
+
stdio: "inherit",
|
|
3831
|
+
env: process.env
|
|
3832
|
+
});
|
|
3833
|
+
child.on("exit", (code) => resolveP(code ?? 0));
|
|
3834
|
+
child.on("error", (err) => {
|
|
3835
|
+
console.error(`diffing-tui failed to start: ${err.message}; falling back to git diff`);
|
|
3836
|
+
resolveP(runTerminalFallback(args));
|
|
3837
|
+
});
|
|
3838
|
+
});
|
|
3839
|
+
}
|
|
2820
3840
|
//#endregion
|
|
2821
|
-
export {};
|
|
3841
|
+
export { findTuiBinary };
|