diffing 0.12.0 → 0.13.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 +56 -791
- package/dist/{cli-agent-CWsOHN-j.mjs → cli-agent-Cx2k0Ki7.mjs} +77 -12
- package/dist/cli.mjs +25 -13
- package/dist/client/assets/{angular-html-PLPuQo0k.js → angular-html-gcUdctE_.js} +1 -1
- package/dist/client/assets/{angular-ts-CE02Ij4b.js → angular-ts-BkjLgJJH.js} +1 -1
- package/dist/client/assets/{apl-CsuUKZoi.js → apl-BtuEza96.js} +1 -1
- package/dist/client/assets/{arc-CgL5kFe-.js → arc-yk_3cjs2.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-DxZQWGnx.js → architectureDiagram-3BPJPVTR-DDVXPdzY.js} +1 -1
- package/dist/client/assets/{astro-DkFc7sbz.js → astro-DfUNgEjV.js} +1 -1
- package/dist/client/assets/{blade-BkY07kcC.js → blade-uv3PNTww.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-D5a2KZV2.js → blockDiagram-GPEHLZMM-DlMLqbfD.js} +1 -1
- package/dist/client/assets/{c-T1BrPfJ6.js → c-DcsMh0kd.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-mpMH4kFC.js → c4Diagram-AAUBKEIU-C4ZZJAMJ.js} +1 -1
- package/dist/client/assets/channel-CPOnEOhs.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-vMtgRLFH.js → chunk-2J33WTMH-CwkSvLT8.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-Dx-FqyO2.js → chunk-3OPIFGDE-s6UH12Df.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-CUrPd8Kv.js → chunk-4BX2VUAB-wGjYymx_.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-DHg3Nr8A.js → chunk-55IACEB6-DObbP11Q.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-CpDPvbGs.js → chunk-5ZQYHXKU-DwbkzQEJ.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-3VOGMxwk.js → chunk-727SXJPM-DzoJfly4.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-4dbfbIS6.js → chunk-AQP2D5EJ-f1kNxYS3.js} +1 -1
- package/dist/client/assets/{chunk-BSJP7CBP-Dfyn0Uwp.js → chunk-BSJP7CBP-CTBkVJEp.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-VTpSKo0F.js → chunk-CSCIHK7Q-C6dvEyPX.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-kXD1MyOL.js → chunk-FMBD7UC4-C166WZ7L.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-BrVoWOAb.js → chunk-KSCS5N6A-CaVyQHxA.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-wZEQDPsa.js → chunk-L5ZTLDWV-CdW7usF5.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-D3i0B0eg.js → chunk-LZXEDZCA-imRCFVGq.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-hDVHT0bN.js → chunk-ND2GUHAM-BJCVPOgO.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-jQUfhp9h.js → chunk-NZK2D7GU-DodLkks9.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-CX_vPWhk.js → chunk-O5CBEL6O-DYxYE_dt.js} +1 -1
- package/dist/client/assets/chunk-QZHKN3VN-7sNTyEOs.js +1 -0
- package/dist/client/assets/chunk-WU5MYG2G-DluN2oZF.js +1 -0
- package/dist/client/assets/{chunk-XPW4576I-Co_-vDB4.js → chunk-XPW4576I-DkPXlA-y.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CHofrzZI.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-B8fKe5FS.js +1 -0
- package/dist/client/assets/{cobol-8-qBLs-a.js → cobol-B5tSUxVN.js} +1 -1
- package/dist/client/assets/{coffee-Bjz5lkyW.js → coffee-B2Q8w4Zu.js} +1 -1
- package/dist/client/assets/{cose-bilkent-S5V4N54A-BljJ0FSA.js → cose-bilkent-S5V4N54A-Bg2tspSU.js} +1 -1
- package/dist/client/assets/{cpp-5BVQCBXT.js → cpp-CfzAEc1z.js} +1 -1
- package/dist/client/assets/{crystal-CCkY93GS.js → crystal-BDxAjW7-.js} +1 -1
- package/dist/client/assets/{css-BTM7Tp2a.js → css-nC3_RAjk.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-BSynd_2D.js → dagre-BM42HDAG-BZiNHe27.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-K3E2CVdy.js → diagram-2AECGRRQ-D3Vd2itQ.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-BQHIrfKc.js → diagram-5GNKFQAL-D44Rm6ep.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-Bqs8sNN4.js → diagram-KO2AKTUF-DwoQejb5.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-C9K4bpQe.js → diagram-LMA3HP47-C1eaDL4X.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-DK6wIXY2.js → diagram-OG6HWLK6-BtBJHHnj.js} +1 -1
- package/dist/client/assets/{dist-1nkZYMRy.js → dist-DUBmiUQf.js} +1 -1
- package/dist/client/assets/{edge-DqN2FJce.js → edge-WCfPDabs.js} +1 -1
- package/dist/client/assets/{elixir-BiAgRUFF.js → elixir-CazmTZo9.js} +1 -1
- package/dist/client/assets/{elm-N1IbY8sz.js → elm-CTqaOirp.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-jsHCiP02.js → erDiagram-TEJ5UH35-CqjpbNDt.js} +1 -1
- package/dist/client/assets/{erb-B-yYlWAZ.js → erb-CK9x36t2.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-DOB1dAWG.js → flowDiagram-I6XJVG4X-CSHJZmlA.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-BYIewcY5.js → ganttDiagram-6RSMTGT7-D6KxkDpI.js} +1 -1
- package/dist/client/assets/{git-rebase-CVkPxOAF.js → git-rebase-Bn8xQDxV.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-VNQzbpv2.js → gitGraphDiagram-PVQCEYII-ZGSlrHF5.js} +1 -1
- package/dist/client/assets/{glimmer-js-BmG1xm72.js → glimmer-js-h7wFfOLo.js} +1 -1
- package/dist/client/assets/{glimmer-ts-CvTgwZsg.js → glimmer-ts-vlZuiamP.js} +1 -1
- package/dist/client/assets/{glsl-DwjNNCbW.js → glsl-CNutENPV.js} +1 -1
- package/dist/client/assets/{graphql-BvgkyF8E.js → graphql-BfddKr9j.js} +1 -1
- package/dist/client/assets/{hack-DH3188x2.js → hack-BkYH75Gx.js} +1 -1
- package/dist/client/assets/{haml-B0GTfMhX.js → haml-CM-xW5RI.js} +1 -1
- package/dist/client/assets/{handlebars-he7hLDd9.js → handlebars-CklcCbzf.js} +1 -1
- package/dist/client/assets/{html-DIYgdTBq.js → html-bltTcC3c.js} +1 -1
- package/dist/client/assets/{html-derivative-2s6MXlci.js → html-derivative-C4R1a7Nf.js} +1 -1
- package/dist/client/assets/{http-COS23XJP.js → http-B6uXoDkp.js} +1 -1
- package/dist/client/assets/{hurl-BlZPjeJt.js → hurl-gquKVFPU.js} +1 -1
- package/dist/client/assets/{index-BrEVVQOB.js → index-yhKiqbkK.js} +150 -150
- package/dist/client/assets/{infoDiagram-5YYISTIA-DQRqk24I.js → infoDiagram-5YYISTIA-DCQv5BKv.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-DU1rqrLx.js → ishikawaDiagram-YF4QCWOH-BqXC35qz.js} +1 -1
- package/dist/client/assets/{java-BAa5w-Qd.js → java-DZLUJ7qo.js} +1 -1
- package/dist/client/assets/{javascript-C9e02ssI.js → javascript-CayXc7uP.js} +1 -1
- package/dist/client/assets/{jinja-Bx37xXwq.js → jinja-CQQUTC0y.js} +1 -1
- package/dist/client/assets/{jison-m_DEf8Jo.js → jison-CvzuCn2x.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-CoHlfevf.js → journeyDiagram-JHISSGLW-bRlSO5Sv.js} +1 -1
- package/dist/client/assets/{json-DtN5AWRW.js → json-BsMA0GaP.js} +1 -1
- package/dist/client/assets/{jsx-BlPpu8bR.js → jsx-Dbxr4aPH.js} +1 -1
- package/dist/client/assets/{julia-BAOmCbHl.js → julia-CZ38GzHV.js} +1 -1
- package/dist/client/assets/{just-C_bvVjwE.js → just-BQwFLV1D.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-BiqYI7_m.js → kanban-definition-UN3LZRKU-BVjoqA0y.js} +1 -1
- package/dist/client/assets/{latex-81pMfNMt.js → latex-BGyrMdgQ.js} +1 -1
- package/dist/client/assets/{line-DCovs9My.js → line-DacHFvZH.js} +1 -1
- package/dist/client/assets/{linear-cJF5XHE7.js → linear-BPGHNTSb.js} +1 -1
- package/dist/client/assets/{liquid-sKRTk6dT.js → liquid-CiA2vjOH.js} +1 -1
- package/dist/client/assets/{lua-C1uZL4IF.js → lua-D9k3fxwM.js} +1 -1
- package/dist/client/assets/{marko-BKEzP1CL.js → marko-CwBH4-2T.js} +1 -1
- package/dist/client/assets/{mdc-CLkfm94H.js → mdc-D4WuuVvw.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-CTsTbvb2.js → mermaid-parser.core-DuXqm2Gp.js} +1 -1
- package/dist/client/assets/{mermaid.core-D86HJEwL.js → mermaid.core-DRh_KVT1.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-CL3heUmL.js → mindmap-definition-RKZ34NQL-C5wytjbD.js} +1 -1
- package/dist/client/assets/{nginx-eDGm66pc.js → nginx-DHMa03ia.js} +1 -1
- package/dist/client/assets/{nim-CoWZPv8-.js → nim-8gK3mQXD.js} +1 -1
- package/dist/client/assets/{perl-MoMqtMVr.js → perl-DqXBmOB_.js} +1 -1
- package/dist/client/assets/{php-DXXVMAtN.js → php-CruAnpl2.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-CWV3TMu4.js → pieDiagram-4H26LBE5-Cv2Csp1M.js} +1 -1
- package/dist/client/assets/{pug-CPWaB94J.js → pug-C7a5y4Mi.js} +1 -1
- package/dist/client/assets/{qml-lhvpx8vk.js → qml-mgWY__NW.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-QV25BYos.js → quadrantDiagram-W4KKPZXB-DHQIHzFU.js} +1 -1
- package/dist/client/assets/{r-DYbUnmmK.js → r-Do5QoKdS.js} +1 -1
- package/dist/client/assets/{razor-DcyOLv96.js → razor-BqdDbm9d.js} +1 -1
- package/dist/client/assets/{regexp-BdY10jm0.js → regexp-CNMlPqVr.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-BPMsmU6J.js → requirementDiagram-4Y6WPE33-rUeJykKo.js} +1 -1
- package/dist/client/assets/{review-export-CMwjJURs.js → review-export-BgwyN8Ky.js} +1 -1
- package/dist/client/assets/{rst-6ShFOzgH.js → rst-PYCtADQ2.js} +1 -1
- package/dist/client/assets/{ruby-B04DeVbi.js → ruby-B8JdoeHE.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DQJeroPp.js → sankeyDiagram-5OEKKPKP-Bsga6dQU.js} +1 -1
- package/dist/client/assets/{sas-Dj26ftNa.js → sas-kIev4j3d.js} +1 -1
- package/dist/client/assets/{scss-BOHpBkgl.js → scss-cf-rl7q3.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DdV0Y6Sz.js → sequenceDiagram-3UESZ5HK-BVcwcOB2.js} +1 -1
- package/dist/client/assets/{shellscript-CpHwjRcU.js → shellscript-BHMFbkaV.js} +1 -1
- package/dist/client/assets/{shellsession-BJAvf4GN.js → shellsession-y_3kBnoK.js} +1 -1
- package/dist/client/assets/{soy-B67bZJnL.js → soy-Coc7dfRW.js} +1 -1
- package/dist/client/assets/{sql-8MzOqSsK.js → sql-CsZYO19m.js} +1 -1
- package/dist/client/assets/{src-AaoRSmFl.js → src-ujohT29p.js} +1 -1
- package/dist/client/assets/{stata-aCME945h.js → stata-BFa4m_Eh.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CMD_Xj2M.js → stateDiagram-AJRCARHV-DBElJIPe.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-BocZ498b.js +1 -0
- package/dist/client/assets/{surrealql-B_V_HxAi.js → surrealql-DlwfRFnC.js} +1 -1
- package/dist/client/assets/{svelte-BcQvTlqz.js → svelte-RrLklQOZ.js} +1 -1
- package/dist/client/assets/{templ-DZCQ2blz.js → templ-BDbBg7z2.js} +1 -1
- package/dist/client/assets/{tex-BfeQ5Wuu.js → tex-BeAp-Hqv.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-CyIS7Zoe.js → timeline-definition-PNZ67QCA-Det2OiYV.js} +1 -1
- package/dist/client/assets/{ts-tags-DVWE0E87.js → ts-tags-Dz-1CFmn.js} +1 -1
- package/dist/client/assets/{tsx-DdJ7Sfqf.js → tsx-Dfi08w8H.js} +1 -1
- package/dist/client/assets/{twig-_gVgaha5.js → twig-BfzYY6Hd.js} +1 -1
- package/dist/client/assets/{typescript-Bj0_LzZV.js → typescript-6FNAPG3Z.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-C-J2kWyk.js → vennDiagram-CIIHVFJN-DhH4H0vl.js} +1 -1
- package/dist/client/assets/{vue-Dnt_GBRb.js → vue-DTPedjqE.js} +1 -1
- package/dist/client/assets/{vue-html-ClVA5__5.js → vue-html-B4sC29Oj.js} +1 -1
- package/dist/client/assets/{vue-vine-BCcz4SVP.js → vue-vine-BWzpqJ4r.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Dzt4EPh8.js → wardleyDiagram-YWT4CUSO-BC8K10yP.js} +1 -1
- package/dist/client/assets/{xml-BYeUHEti.js → xml-BK8ACjht.js} +1 -1
- package/dist/client/assets/{xsl-Vsx0SZEV.js → xsl-rjlrbfou.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-BAWZKvwP.js → xychartDiagram-2RQKCTM6-DwcVZiB5.js} +1 -1
- package/dist/client/assets/{yaml-C6L1khMK.js → yaml-DM6lgMhS.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/completions-5rtfoxB9.mjs +2 -0
- package/dist/{completions-DvyV49HY.mjs → completions-7YSX1neu.mjs} +6 -1
- package/dist/{doctor-Bz9VHuBI.mjs → doctor-BMfx7_GY.mjs} +13 -11
- package/dist/doctor-BRr5IWHe.mjs +2 -0
- package/dist/{github-DXiEeHzk.mjs → github-BLrnsozP.mjs} +1 -1
- package/dist/{github-DpzqNrUh.mjs → github-DbAFoB92.mjs} +3 -3
- package/dist/{github-attachments-DgOf7BhP.mjs → github-attachments-fNCKYL-Z.mjs} +1 -1
- package/dist/{mcp-zDVzU1vl.mjs → mcp-Bi551clR.mjs} +3 -3
- package/dist/native/tui-darwin-arm64/diffing-tui +0 -0
- package/dist/native/tui-darwin-x64/diffing-tui +0 -0
- package/dist/native/tui-linux-arm64-gnu/diffing-tui +0 -0
- package/dist/native/tui-linux-arm64-musl/diffing-tui +0 -0
- package/dist/native/tui-linux-x64-gnu/diffing-tui +0 -0
- package/dist/native/tui-linux-x64-musl/diffing-tui +0 -0
- package/dist/native/tui-win32-x64-msvc/diffing-tui.exe +0 -0
- package/dist/{server-Dw3lkQ5q.mjs → server-B9NVZu7l.mjs} +23 -10
- package/dist/{session-conflict-Bqt1M4Mp.mjs → session-conflict-CB0IErdn.mjs} +2 -1
- package/dist/{session-manager-BKSZQqDJ.mjs → session-manager-656aJXFE.mjs} +1 -1
- package/dist/session-url-B8TrGGeE.mjs +33 -0
- package/dist/{session-url-sr7lXXVc.mjs → settings-DkPDB_hS.mjs} +18 -30
- package/dist/setup-BiwX7vdd.mjs +446 -0
- package/dist/setup-first-run-DyT0PlOZ.mjs +2 -0
- package/dist/setup-first-run-i1o7DBki.mjs +74 -0
- package/dist/terminal-BroplOnw.mjs +116 -0
- package/package.json +8 -3
- package/scripts/postinstall.mjs +52 -0
- package/dist/client/assets/channel-D3niKhl0.js +0 -1
- package/dist/client/assets/chunk-QZHKN3VN-CZLFuGpt.js +0 -1
- package/dist/client/assets/chunk-WU5MYG2G-BhcpjkaB.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DK5KJNIt.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-y5K9t6Se.js +0 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-vLl7D4ue.js +0 -1
- /package/dist/{apply-suggestion-CHYnw68I.mjs → apply-suggestion-BsR_8FXW.mjs} +0 -0
- /package/dist/{cli-gh-DI69gJGo.mjs → cli-gh-BQuQpV-s.mjs} +0 -0
- /package/dist/{diff-fingerprint-BgR0tu1o.mjs → diff-fingerprint-BL5jNHnP.mjs} +0 -0
- /package/dist/{find-tui-binary-DVQADOSF.mjs → find-tui-binary-DGspwyvd.mjs} +0 -0
- /package/dist/{handoff-LAFtmnHf.mjs → handoff-DcShCxWJ.mjs} +0 -0
- /package/dist/{review-export-B63eRAVa.mjs → review-export-GW4KNsGN.mjs} +0 -0
- /package/dist/{tui-search-bridge-BGgLCwMF.mjs → tui-search-bridge-fFKqX4Fi.mjs} +0 -0
- /package/dist/{update-check-BUfh4Y6c.mjs → update-check-ISaw61qS.mjs} +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { g as SESSION_TOKEN_QUERY, x as isLoopbackHost } from "./server-lock-BNI8FKV1.mjs";
|
|
2
|
+
//#region src/lib/session-url.ts
|
|
3
|
+
/** Loopback-safe review URL for web / gh-pr locks; null for TUI or invalid ports. */
|
|
4
|
+
function reviewSessionBaseUrl(lock) {
|
|
5
|
+
if ((lock.mode ?? "web") === "tui" || !(lock.port > 0)) return null;
|
|
6
|
+
if (!isLoopbackHost(lock.host) && lock.host !== "0.0.0.0" && lock.host !== "::") return null;
|
|
7
|
+
const host = lock.host === "0.0.0.0" || lock.host === "::" ? "127.0.0.1" : lock.host;
|
|
8
|
+
const path = lock.mode === "gh-pr" ? "/gh/pr" : "";
|
|
9
|
+
return `http://${host}:${lock.port}${path}`;
|
|
10
|
+
}
|
|
11
|
+
/** Browseable review URL without auth query params (auth uses cookie + header). */
|
|
12
|
+
function appendSessionToken(url, _token) {
|
|
13
|
+
try {
|
|
14
|
+
const parsed = new URL(url);
|
|
15
|
+
parsed.searchParams.delete(SESSION_TOKEN_QUERY);
|
|
16
|
+
const search = parsed.searchParams.toString();
|
|
17
|
+
return `${parsed.origin}${parsed.pathname}${search ? `?${search}` : ""}${parsed.hash}`;
|
|
18
|
+
} catch {
|
|
19
|
+
return url;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Public review URL for humans (no `?token=` — session cookie is set when HTML is served). */
|
|
23
|
+
function reviewSessionUrl(lock) {
|
|
24
|
+
const base = reviewSessionBaseUrl(lock);
|
|
25
|
+
if (!base) return null;
|
|
26
|
+
return base;
|
|
27
|
+
}
|
|
28
|
+
/** Join an API path to a session base URL (origin only; strips any legacy `?token=`). */
|
|
29
|
+
function joinSessionApiUrl(base, path) {
|
|
30
|
+
return `${new URL(base).origin}${path.startsWith("/") ? path : `/${path}`}`;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { reviewSessionUrl as i, joinSessionApiUrl as n, reviewSessionBaseUrl as r, appendSessionToken as t };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { g as SESSION_TOKEN_QUERY, x as isLoopbackHost } from "./server-lock-BNI8FKV1.mjs";
|
|
2
1
|
import { randomUUID } from "node:crypto";
|
|
3
2
|
import { join } from "node:path";
|
|
4
3
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
@@ -13,7 +12,14 @@ function writeJsonAtomically(path, value) {
|
|
|
13
12
|
}
|
|
14
13
|
//#endregion
|
|
15
14
|
//#region src/lib/settings.ts
|
|
16
|
-
const
|
|
15
|
+
const CONFIG_DIR = join(homedir(), ".config", "diffing");
|
|
16
|
+
const SETTINGS_FILE = join(CONFIG_DIR, "settings.json");
|
|
17
|
+
/** Human-facing label for stored mode values (`tui` → `TUI`, `web` → `Web`). */
|
|
18
|
+
function formatModeLabel(mode) {
|
|
19
|
+
if (mode === "tui") return "TUI";
|
|
20
|
+
if (mode === "web") return "Web";
|
|
21
|
+
return mode;
|
|
22
|
+
}
|
|
17
23
|
const DEFAULTS = {
|
|
18
24
|
defaultMode: "web",
|
|
19
25
|
staged: true,
|
|
@@ -65,36 +71,18 @@ function saveSettings(settings) {
|
|
|
65
71
|
writeJsonAtomically(SETTINGS_FILE, merged);
|
|
66
72
|
return merged;
|
|
67
73
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
/** Loopback-safe review URL for web / gh-pr locks; null for TUI or invalid ports. */
|
|
71
|
-
function reviewSessionBaseUrl(lock) {
|
|
72
|
-
if ((lock.mode ?? "web") === "tui" || !(lock.port > 0)) return null;
|
|
73
|
-
if (!isLoopbackHost(lock.host) && lock.host !== "0.0.0.0" && lock.host !== "::") return null;
|
|
74
|
-
const host = lock.host === "0.0.0.0" || lock.host === "::" ? "127.0.0.1" : lock.host;
|
|
75
|
-
const path = lock.mode === "gh-pr" ? "/gh/pr" : "";
|
|
76
|
-
return `http://${host}:${lock.port}${path}`;
|
|
74
|
+
function ensureConfigDir() {
|
|
75
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
77
76
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const parsed = new URL(url);
|
|
82
|
-
parsed.searchParams.delete(SESSION_TOKEN_QUERY);
|
|
83
|
-
const search = parsed.searchParams.toString();
|
|
84
|
-
return `${parsed.origin}${parsed.pathname}${search ? `?${search}` : ""}${parsed.hash}`;
|
|
85
|
-
} catch {
|
|
86
|
-
return url;
|
|
87
|
-
}
|
|
77
|
+
function isSetupCompleted() {
|
|
78
|
+
const at = loadSettings().setupCompletedAt;
|
|
79
|
+
return typeof at === "string" && at.length > 0;
|
|
88
80
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const base = reviewSessionBaseUrl(lock);
|
|
92
|
-
if (!base) return null;
|
|
93
|
-
return base;
|
|
81
|
+
function markSetupCompleted() {
|
|
82
|
+
return saveSettings({ setupCompletedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
94
83
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return `${new URL(base).origin}${path.startsWith("/") ? path : `/${path}`}`;
|
|
84
|
+
function resetSetupCompleted() {
|
|
85
|
+
return saveSettings({ setupCompletedAt: null });
|
|
98
86
|
}
|
|
99
87
|
//#endregion
|
|
100
|
-
export {
|
|
88
|
+
export { markSetupCompleted as a, loadSettings as i, formatModeLabel as n, resetSetupCompleted as o, isSetupCompleted as r, saveSettings as s, ensureConfigDir as t };
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import { S as isGitRepo, h as getRepoRoot } from "./git-BGj31GX-.mjs";
|
|
2
|
+
import { a as markSetupCompleted, i as loadSettings, n as formatModeLabel, o as resetSetupCompleted, r as isSetupCompleted, s as saveSettings, t as ensureConfigDir } from "./settings-DkPDB_hS.mjs";
|
|
3
|
+
import { n as completionFor } from "./completions-7YSX1neu.mjs";
|
|
4
|
+
import { a as hintLine, c as stepHeader, i as dim, l as tone, n as box, o as isColorEnabled, r as copyBlock, s as rule, t as bold } from "./terminal-BroplOnw.mjs";
|
|
5
|
+
import { n as runDoctor, t as formatDoctorReport } from "./doctor-BMfx7_GY.mjs";
|
|
6
|
+
import { t as DOCS_GETTING_STARTED_URL } from "./setup-first-run-i1o7DBki.mjs";
|
|
7
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { dirname, join, resolve } from "node:path";
|
|
10
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { homedir, platform } from "node:os";
|
|
12
|
+
import { stdin, stdout } from "node:process";
|
|
13
|
+
import { createInterface } from "node:readline/promises";
|
|
14
|
+
//#region src/lib/setup-mcp.ts
|
|
15
|
+
const DIFFING_SERVER_KEY = "diffing";
|
|
16
|
+
function buildDiffingMcpEntry(repoPath) {
|
|
17
|
+
if (repoPath) return {
|
|
18
|
+
command: "diffing",
|
|
19
|
+
args: [
|
|
20
|
+
"mcp",
|
|
21
|
+
"--repo",
|
|
22
|
+
repoPath
|
|
23
|
+
]
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
command: "diffing",
|
|
27
|
+
args: ["mcp"]
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function formatMcpSnippet(entry) {
|
|
31
|
+
return JSON.stringify({ mcpServers: { [DIFFING_SERVER_KEY]: entry } }, null, 2);
|
|
32
|
+
}
|
|
33
|
+
function detectGlobalMcpClients(home = homedir()) {
|
|
34
|
+
const clients = [];
|
|
35
|
+
const cursor = join(home, ".cursor", "mcp.json");
|
|
36
|
+
clients.push({
|
|
37
|
+
id: "cursor",
|
|
38
|
+
label: "Cursor",
|
|
39
|
+
path: cursor,
|
|
40
|
+
scope: "global"
|
|
41
|
+
});
|
|
42
|
+
const claude = claudeDesktopConfigPath(home);
|
|
43
|
+
if (claude) clients.push({
|
|
44
|
+
id: "claude-desktop",
|
|
45
|
+
label: "Claude Desktop",
|
|
46
|
+
path: claude,
|
|
47
|
+
scope: "global"
|
|
48
|
+
});
|
|
49
|
+
return clients;
|
|
50
|
+
}
|
|
51
|
+
function projectMcpPath(cwd) {
|
|
52
|
+
return join(cwd, ".cursor", "mcp.json");
|
|
53
|
+
}
|
|
54
|
+
function claudeDesktopConfigPath(home) {
|
|
55
|
+
const os = platform();
|
|
56
|
+
if (os === "darwin") return join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
57
|
+
if (os === "win32") {
|
|
58
|
+
const appData = process.env.APPDATA;
|
|
59
|
+
if (!appData) return null;
|
|
60
|
+
return join(appData, "Claude", "claude_desktop_config.json");
|
|
61
|
+
}
|
|
62
|
+
return join(home, ".config", "Claude", "claude_desktop_config.json");
|
|
63
|
+
}
|
|
64
|
+
function readMcpConfig(path) {
|
|
65
|
+
if (!existsSync(path)) return {};
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
68
|
+
} catch {
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Merge only `mcpServers.diffing`; preserve every other server and top-level key. */
|
|
73
|
+
function mergeDiffingMcpConfig(existing, entry) {
|
|
74
|
+
const servers = { ...existing.mcpServers ?? {} };
|
|
75
|
+
servers[DIFFING_SERVER_KEY] = entry;
|
|
76
|
+
return {
|
|
77
|
+
...existing,
|
|
78
|
+
mcpServers: servers
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function backupMcpConfig(path, backupsRoot = join(homedir(), ".diffing", "backups")) {
|
|
82
|
+
if (!existsSync(path)) return null;
|
|
83
|
+
mkdirSync(backupsRoot, { recursive: true });
|
|
84
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
85
|
+
const backupPath = join(backupsRoot, `${path.replaceAll(/[/\\:]/g, "_")}.${stamp}.json`);
|
|
86
|
+
copyFileSync(path, backupPath);
|
|
87
|
+
return backupPath;
|
|
88
|
+
}
|
|
89
|
+
function writeMcpConfig(target, entry, options) {
|
|
90
|
+
const merged = mergeDiffingMcpConfig(readMcpConfig(target.path), entry);
|
|
91
|
+
const created = !existsSync(target.path);
|
|
92
|
+
if (options?.dryRun) return {
|
|
93
|
+
path: target.path,
|
|
94
|
+
written: false,
|
|
95
|
+
backupPath: null,
|
|
96
|
+
created
|
|
97
|
+
};
|
|
98
|
+
mkdirSync(join(target.path, ".."), { recursive: true });
|
|
99
|
+
const backupPath = backupMcpConfig(target.path, options?.backupsRoot);
|
|
100
|
+
writeFileSync(target.path, `${JSON.stringify(merged, null, 2)}\n`, "utf-8");
|
|
101
|
+
return {
|
|
102
|
+
path: target.path,
|
|
103
|
+
written: true,
|
|
104
|
+
backupPath,
|
|
105
|
+
created
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function writeGlobalMcpConfigs(entry, options) {
|
|
109
|
+
return detectGlobalMcpClients(options?.home).map((target) => writeMcpConfig(target, entry, options));
|
|
110
|
+
}
|
|
111
|
+
function writeProjectMcpConfig(cwd, entry, options) {
|
|
112
|
+
return writeMcpConfig({
|
|
113
|
+
id: "cursor-project",
|
|
114
|
+
label: "Cursor (project)",
|
|
115
|
+
path: projectMcpPath(cwd),
|
|
116
|
+
scope: "project"
|
|
117
|
+
}, entry, options);
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/lib/setup-skills.ts
|
|
121
|
+
const SKILLS_PACKAGE = "ahmedragab20/diffing";
|
|
122
|
+
function buildSkillsInstallCommand() {
|
|
123
|
+
return {
|
|
124
|
+
command: "npx",
|
|
125
|
+
args: [
|
|
126
|
+
"skills",
|
|
127
|
+
"add",
|
|
128
|
+
SKILLS_PACKAGE
|
|
129
|
+
]
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function formatSkillsInstallCommand() {
|
|
133
|
+
const { command, args } = buildSkillsInstallCommand();
|
|
134
|
+
return [command, ...args].join(" ");
|
|
135
|
+
}
|
|
136
|
+
function runSkillsInstall(options = {}) {
|
|
137
|
+
if (options.dryRun) return Promise.resolve(0);
|
|
138
|
+
const { command, args } = buildSkillsInstallCommand();
|
|
139
|
+
return new Promise((resolve) => {
|
|
140
|
+
const child = spawn(command, args, {
|
|
141
|
+
cwd: options.cwd ?? process.cwd(),
|
|
142
|
+
env: options.env ?? process.env,
|
|
143
|
+
stdio: "inherit",
|
|
144
|
+
shell: process.platform === "win32"
|
|
145
|
+
});
|
|
146
|
+
child.on("error", () => resolve(1));
|
|
147
|
+
child.on("exit", (code) => resolve(code ?? 1));
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/lib/setup.ts
|
|
152
|
+
const SETUP_STEPS = 6;
|
|
153
|
+
function packageVersion(cliImportMetaUrl) {
|
|
154
|
+
const candidates = [];
|
|
155
|
+
if (cliImportMetaUrl) {
|
|
156
|
+
const base = dirname(fileURLToPath(cliImportMetaUrl));
|
|
157
|
+
candidates.push(resolve(base, "..", "package.json"), resolve(base, "..", "..", "package.json"));
|
|
158
|
+
}
|
|
159
|
+
candidates.push(resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "package.json"));
|
|
160
|
+
for (const path of candidates) try {
|
|
161
|
+
const pkg = JSON.parse(readFileSync(path, "utf-8"));
|
|
162
|
+
if (pkg.version) return pkg.version;
|
|
163
|
+
} catch {}
|
|
164
|
+
return "unknown";
|
|
165
|
+
}
|
|
166
|
+
function gitOnPath() {
|
|
167
|
+
try {
|
|
168
|
+
execFileSync("git", ["--version"], { stdio: [
|
|
169
|
+
"ignore",
|
|
170
|
+
"ignore",
|
|
171
|
+
"ignore"
|
|
172
|
+
] });
|
|
173
|
+
return true;
|
|
174
|
+
} catch {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function nodeVersionOk() {
|
|
179
|
+
const major = Number(process.versions.node.split(".")[0]);
|
|
180
|
+
return Number.isFinite(major) && major >= 20;
|
|
181
|
+
}
|
|
182
|
+
function printStep(step, title, color) {
|
|
183
|
+
console.log("");
|
|
184
|
+
console.log(stepHeader(step, SETUP_STEPS, title, { color }));
|
|
185
|
+
}
|
|
186
|
+
async function askYesNo(question, defaultYes, interactive, color) {
|
|
187
|
+
if (!interactive || !process.stdin.isTTY) return defaultYes;
|
|
188
|
+
const rl = createInterface({
|
|
189
|
+
input: stdin,
|
|
190
|
+
output: stdout
|
|
191
|
+
});
|
|
192
|
+
try {
|
|
193
|
+
const suffix = defaultYes ? `[${bold("Y", color)}/${bold("n", color)}]` : `[${bold("y", color)}/${bold("N", color)}]`;
|
|
194
|
+
console.log(hintLine([
|
|
195
|
+
{
|
|
196
|
+
key: "Y",
|
|
197
|
+
label: "yes"
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
key: "n",
|
|
201
|
+
label: "no"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
key: "?",
|
|
205
|
+
label: "help"
|
|
206
|
+
}
|
|
207
|
+
], { color }));
|
|
208
|
+
while (true) {
|
|
209
|
+
const answer = (await rl.question(`${question} ${suffix} `)).trim().toLowerCase();
|
|
210
|
+
if (!answer) return defaultYes;
|
|
211
|
+
if (answer === "?" || answer === "help") {
|
|
212
|
+
console.log(dim("Answer y/yes or n/no; Enter accepts the default in brackets.", color));
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (answer === "y" || answer === "yes") return true;
|
|
216
|
+
if (answer === "n" || answer === "no") return false;
|
|
217
|
+
console.log(dim("Please answer y or n (or ? for help).", color));
|
|
218
|
+
}
|
|
219
|
+
} finally {
|
|
220
|
+
rl.close();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function askDefaultMode(interactive, color) {
|
|
224
|
+
if (!interactive || !process.stdin.isTTY) return;
|
|
225
|
+
const current = loadSettings().defaultMode;
|
|
226
|
+
console.log("");
|
|
227
|
+
console.log(tone("info", "Default interactive mode:", { color }));
|
|
228
|
+
console.log(` ${bold("Web", color)} ${dim("— browser review UI (supported)", color)}`);
|
|
229
|
+
console.log(` ${bold("TUI", color)} ${dim("— native terminal UI (experimental)", color)}`);
|
|
230
|
+
console.log(dim(`Current: ${formatModeLabel(current)}`, color));
|
|
231
|
+
if (await askYesNo(`Switch to TUI?`, false, interactive, color)) {
|
|
232
|
+
saveSettings({ defaultMode: "tui" });
|
|
233
|
+
console.log(tone("ok", "Default mode set to TUI", { color }));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (!await askYesNo("Keep Web as the default interactive mode?", true, interactive, color)) {
|
|
237
|
+
saveSettings({ defaultMode: "tui" });
|
|
238
|
+
console.log(tone("ok", "Default mode set to TUI", { color }));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function runSetupCheck(options = {}) {
|
|
242
|
+
const cwd = options.cwd ?? process.cwd();
|
|
243
|
+
const messages = [];
|
|
244
|
+
const nodeOk = nodeVersionOk();
|
|
245
|
+
if (!nodeOk) messages.push(`Node.js >= 20 required (found ${process.versions.node})`);
|
|
246
|
+
const gitOk = gitOnPath();
|
|
247
|
+
if (!gitOk) messages.push("git not found on PATH");
|
|
248
|
+
const inGitRepo = isGitRepo();
|
|
249
|
+
if (!inGitRepo) messages.push(`${cwd} is not inside a Git repository (setup can continue)`);
|
|
250
|
+
ensureConfigDir();
|
|
251
|
+
const configDirReady = true;
|
|
252
|
+
const setupCompleted = isSetupCompleted();
|
|
253
|
+
return {
|
|
254
|
+
ok: nodeOk && gitOk && configDirReady,
|
|
255
|
+
nodeOk,
|
|
256
|
+
gitOk,
|
|
257
|
+
inGitRepo,
|
|
258
|
+
setupCompleted,
|
|
259
|
+
configDirReady,
|
|
260
|
+
messages
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
async function runSetup(options = {}) {
|
|
264
|
+
const cwd = options.cwd ?? process.cwd();
|
|
265
|
+
const interactive = options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
266
|
+
const color = interactive && isColorEnabled();
|
|
267
|
+
const version = packageVersion(options.cliImportMetaUrl);
|
|
268
|
+
if (options.reset) {
|
|
269
|
+
resetSetupCompleted();
|
|
270
|
+
console.log(tone("ok", "Setup marker cleared. Run `diffing setup` to configure again.", { color }));
|
|
271
|
+
return 0;
|
|
272
|
+
}
|
|
273
|
+
const preflight = runSetupCheck({ cwd });
|
|
274
|
+
if (options.check) {
|
|
275
|
+
printCheckReport(preflight, version, color);
|
|
276
|
+
return preflight.ok ? 0 : 1;
|
|
277
|
+
}
|
|
278
|
+
console.log(box("diffing setup", [dim(`v${version} — first-time configuration wizard`, color)], { color }));
|
|
279
|
+
printStep(1, "Environment", color);
|
|
280
|
+
if (!preflight.nodeOk) {
|
|
281
|
+
console.error(tone("error", `Node.js >= 20 required (found ${process.versions.node})`, { color }));
|
|
282
|
+
return 1;
|
|
283
|
+
}
|
|
284
|
+
if (!preflight.gitOk) {
|
|
285
|
+
console.error(tone("error", "git not found on PATH — install git and retry.", { color }));
|
|
286
|
+
return 1;
|
|
287
|
+
}
|
|
288
|
+
ensureConfigDir();
|
|
289
|
+
console.log(tone("ok", "Config directory ready (~/.config/diffing/)", { color }));
|
|
290
|
+
if (preflight.inGitRepo) try {
|
|
291
|
+
console.log(tone("ok", `Git repository: ${getRepoRoot()}`, { color }));
|
|
292
|
+
} catch {
|
|
293
|
+
console.log(tone("warn", "Not inside a Git repository — review commands need a repo later", { color }));
|
|
294
|
+
}
|
|
295
|
+
else console.log(tone("warn", "Not inside a Git repository — you can still finish setup", { color }));
|
|
296
|
+
if (options.skillsOnly) return runSkillsStep(options, interactive, color, 5);
|
|
297
|
+
if (options.mcpOnly) return runMcpStep(cwd, options, interactive, preflight.inGitRepo, color, 6);
|
|
298
|
+
printStep(2, "Environment checks", color);
|
|
299
|
+
console.log(dim("Running `diffing doctor`…", color));
|
|
300
|
+
const doctor = await runDoctor({
|
|
301
|
+
cwd,
|
|
302
|
+
cliImportMetaUrl: options.cliImportMetaUrl
|
|
303
|
+
});
|
|
304
|
+
console.log(formatDoctorReport(doctor, { color }));
|
|
305
|
+
printStep(3, "Default mode", color);
|
|
306
|
+
if (!options.yes) await askDefaultMode(interactive, color);
|
|
307
|
+
else console.log(tone("ok", `Default mode: ${formatModeLabel(loadSettings().defaultMode)} (unchanged)`, { color }));
|
|
308
|
+
printStep(4, "Shell completions", color);
|
|
309
|
+
await runCompletionsStep(options, interactive, color);
|
|
310
|
+
printStep(5, "Agent skills", color);
|
|
311
|
+
const skillsCode = await runSkillsStep(options, interactive, color, 5);
|
|
312
|
+
if (skillsCode !== 0) return skillsCode;
|
|
313
|
+
printStep(6, "MCP registration", color);
|
|
314
|
+
const mcpCode = await runMcpStep(cwd, options, interactive, preflight.inGitRepo, color, 6);
|
|
315
|
+
if (mcpCode !== 0) return mcpCode;
|
|
316
|
+
markSetupCompleted();
|
|
317
|
+
printDoneChecklist(preflight.inGitRepo, cwd, color);
|
|
318
|
+
return 0;
|
|
319
|
+
}
|
|
320
|
+
async function runSkillsStep(options, interactive, color, _step) {
|
|
321
|
+
const cmd = formatSkillsInstallCommand();
|
|
322
|
+
if (!(options.yes || await askYesNo(`Install agent skills via \`${cmd}\`?`, true, interactive, color))) {
|
|
323
|
+
console.log(dim("Skipped skills install.", color));
|
|
324
|
+
return 0;
|
|
325
|
+
}
|
|
326
|
+
if (options.yes && !process.stdin.isTTY) console.log(dim(`Running: ${cmd}`, color));
|
|
327
|
+
const code = await runSkillsInstall({ cwd: options.cwd });
|
|
328
|
+
if (code === 0) console.log(tone("ok", "Skills installed (or already present)", { color }));
|
|
329
|
+
else {
|
|
330
|
+
console.error(tone("error", `Skills install exited with code ${code}`, { color }));
|
|
331
|
+
console.error(dim(` You can retry manually: ${cmd}`, color));
|
|
332
|
+
return options.yes ? 0 : code;
|
|
333
|
+
}
|
|
334
|
+
return 0;
|
|
335
|
+
}
|
|
336
|
+
async function runMcpStep(cwd, options, interactive, inGitRepo, color, _step) {
|
|
337
|
+
let repoPath;
|
|
338
|
+
if (inGitRepo) try {
|
|
339
|
+
repoPath = getRepoRoot();
|
|
340
|
+
} catch {
|
|
341
|
+
repoPath = void 0;
|
|
342
|
+
}
|
|
343
|
+
const entry = buildDiffingMcpEntry(repoPath);
|
|
344
|
+
const snippet = formatMcpSnippet(entry);
|
|
345
|
+
if (options.writeMcp || options.writeProjectMcp) {
|
|
346
|
+
if (options.writeMcp) {
|
|
347
|
+
const results = writeGlobalMcpConfigs(entry);
|
|
348
|
+
for (const r of results) if (r.written) {
|
|
349
|
+
const backup = r.backupPath ? ` (backup: ${r.backupPath})` : "";
|
|
350
|
+
console.log(tone("ok", `Wrote MCP config: ${r.path}${backup}`, { color }));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (options.writeProjectMcp) {
|
|
354
|
+
const r = writeProjectMcpConfig(cwd, entry);
|
|
355
|
+
const backup = r.backupPath ? ` (backup: ${r.backupPath})` : "";
|
|
356
|
+
console.log(tone("ok", `Wrote project MCP config: ${r.path}${backup}`, { color }));
|
|
357
|
+
}
|
|
358
|
+
return 0;
|
|
359
|
+
}
|
|
360
|
+
console.log(tone("info", "MCP registration (paste into your agent client):", { color }));
|
|
361
|
+
console.log(copyBlock("Copy MCP JSON", snippet, { color }));
|
|
362
|
+
const detected = detectGlobalMcpClients().filter((c) => c.id === "cursor" || c.id === "claude-desktop");
|
|
363
|
+
if (detected.length > 0) {
|
|
364
|
+
console.log("");
|
|
365
|
+
console.log(tone("info", "Detected clients:", { color }));
|
|
366
|
+
for (const c of detected) console.log(dim(` • ${c.label}: ${c.path}`, color));
|
|
367
|
+
}
|
|
368
|
+
if (options.yes) {
|
|
369
|
+
console.log(dim("\n(--yes prints MCP JSON only; pass --write-mcp to merge into IDE configs)", color));
|
|
370
|
+
return 0;
|
|
371
|
+
}
|
|
372
|
+
if (await askYesNo("Write diffing MCP entry to global IDE configs?", false, interactive, color)) writeGlobalMcpConfigs(entry).forEach((r) => {
|
|
373
|
+
if (r.written) console.log(tone("ok", `Wrote ${r.path}`, { color }));
|
|
374
|
+
});
|
|
375
|
+
if (await askYesNo("Also write project-local .cursor/mcp.json in the current directory?", false, interactive, color)) {
|
|
376
|
+
const r = writeProjectMcpConfig(cwd, entry);
|
|
377
|
+
if (r.written) console.log(tone("ok", `Wrote ${r.path}`, { color }));
|
|
378
|
+
}
|
|
379
|
+
return 0;
|
|
380
|
+
}
|
|
381
|
+
async function runCompletionsStep(options, interactive, color) {
|
|
382
|
+
const shells = [
|
|
383
|
+
"bash",
|
|
384
|
+
"zsh",
|
|
385
|
+
"fish"
|
|
386
|
+
];
|
|
387
|
+
if (options.writeCompletions) {
|
|
388
|
+
for (const shell of shells) {
|
|
389
|
+
const script = completionFor(shell);
|
|
390
|
+
if (script) console.log(copyBlock(`${shell} completion`, script, { color }));
|
|
391
|
+
}
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (options.yes) {
|
|
395
|
+
console.log(dim("Shell completions: run `diffing completion <bash|zsh|fish>` and append to your shell rc.", color));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (!await askYesNo("Print shell completion scripts?", false, interactive, color)) return;
|
|
399
|
+
for (const shell of shells) {
|
|
400
|
+
const script = completionFor(shell);
|
|
401
|
+
if (script) console.log(copyBlock(`${shell} completion`, script, { color }));
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function printCheckReport(result, version, color) {
|
|
405
|
+
console.log(box("diffing setup --check", [dim(`v${version}`, color)], { color }));
|
|
406
|
+
console.log(rule({ color }));
|
|
407
|
+
console.log(result.nodeOk ? tone("ok", "Node.js >= 20", { color }) : tone("error", "Node.js >= 20", { color }));
|
|
408
|
+
console.log(result.gitOk ? tone("ok", "git on PATH", { color }) : tone("error", "git on PATH", { color }));
|
|
409
|
+
console.log(result.configDirReady ? tone("ok", "~/.config/diffing/", { color }) : tone("error", "config dir", { color }));
|
|
410
|
+
console.log(result.inGitRepo ? tone("ok", "inside Git repository", { color }) : tone("warn", "not in a Git repository", { color }));
|
|
411
|
+
console.log(result.setupCompleted ? tone("ok", "setup completed previously", { color }) : tone("warn", "setup not completed", { color }));
|
|
412
|
+
for (const msg of result.messages) console.log(dim(` ${msg}`, color));
|
|
413
|
+
}
|
|
414
|
+
function printDoneChecklist(inGitRepo, cwd, color) {
|
|
415
|
+
const nextSteps = inGitRepo ? ["diffing Open the review UI", "diffing doctor Re-run diagnostics anytime"] : ["cd <your-repo> && diffing", `# current directory: ${cwd}`];
|
|
416
|
+
console.log(box("Setup complete", [
|
|
417
|
+
tone("ok", "Settings saved", { color }),
|
|
418
|
+
tone("ok", "Environment checked (`diffing doctor`)", { color }),
|
|
419
|
+
"",
|
|
420
|
+
tone("info", "Next steps:", { color }),
|
|
421
|
+
...nextSteps.map((line) => ` ${line}`),
|
|
422
|
+
"",
|
|
423
|
+
`Docs: ${DOCS_GETTING_STARTED_URL}`,
|
|
424
|
+
dim("CLI reference: docs/cli.md", color)
|
|
425
|
+
], { color }));
|
|
426
|
+
}
|
|
427
|
+
async function handleFirstRunGate(options) {
|
|
428
|
+
const { shouldOfferFirstRunSetup, promptFirstRunSetup, DOCS_GETTING_STARTED_URL } = await import("./setup-first-run-DyT0PlOZ.mjs");
|
|
429
|
+
if (!shouldOfferFirstRunSetup({ skipSetup: options.skipSetup })) return;
|
|
430
|
+
while (true) {
|
|
431
|
+
const choice = await promptFirstRunSetup();
|
|
432
|
+
if (choice === "skip") return;
|
|
433
|
+
if (choice === "docs") {
|
|
434
|
+
console.log(`\nGetting started: ${DOCS_GETTING_STARTED_URL}\n`);
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
const code = await runSetup({
|
|
438
|
+
cliImportMetaUrl: options.cliImportMetaUrl,
|
|
439
|
+
interactive: true
|
|
440
|
+
});
|
|
441
|
+
if (code !== 0) process.exit(code);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
//#endregion
|
|
446
|
+
export { handleFirstRunGate, runSetup };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { r as isSetupCompleted } from "./settings-DkPDB_hS.mjs";
|
|
2
|
+
import { a as hintLine, i as dim, n as box, o as isColorEnabled } from "./terminal-BroplOnw.mjs";
|
|
3
|
+
import { stdin, stdout } from "node:process";
|
|
4
|
+
import { createInterface } from "node:readline/promises";
|
|
5
|
+
//#region src/lib/setup-first-run.ts
|
|
6
|
+
const DOCS_GETTING_STARTED_URL = "https://github.com/ahmedragab20/diffing/blob/main/docs/getting-started.md";
|
|
7
|
+
function isCiEnvironment(env = process.env) {
|
|
8
|
+
return Boolean(env.CI || env.CONTINUOUS_INTEGRATION || env.BUILD_NUMBER || env.GITHUB_ACTIONS || env.GITLAB_CI || env.CIRCLECI || env.JENKINS_URL);
|
|
9
|
+
}
|
|
10
|
+
function shouldOfferFirstRunSetup(options = {}) {
|
|
11
|
+
const env = options.env ?? process.env;
|
|
12
|
+
const tty = options.isTTY ?? Boolean(process.stdout.isTTY);
|
|
13
|
+
const stdinTty = options.isStdinTTY ?? Boolean(process.stdin.isTTY);
|
|
14
|
+
if (options.skipSetup) return false;
|
|
15
|
+
if (!tty || !stdinTty) return false;
|
|
16
|
+
if (isCiEnvironment(env)) return false;
|
|
17
|
+
return !(options.setupCompleted ?? isSetupCompleted());
|
|
18
|
+
}
|
|
19
|
+
function formatFirstRunWelcome(options = {}) {
|
|
20
|
+
const color = options.color ?? isColorEnabled();
|
|
21
|
+
return box("diffing", [
|
|
22
|
+
"Welcome — local-first code review for git diffs and AI handoff.",
|
|
23
|
+
"",
|
|
24
|
+
hintLine([
|
|
25
|
+
{
|
|
26
|
+
key: "Y",
|
|
27
|
+
label: "Run setup now"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
key: "n",
|
|
31
|
+
label: "Skip"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: "?",
|
|
35
|
+
label: "Docs"
|
|
36
|
+
}
|
|
37
|
+
], { color }),
|
|
38
|
+
dim(`Getting started: ${DOCS_GETTING_STARTED_URL}`, color)
|
|
39
|
+
], { color });
|
|
40
|
+
}
|
|
41
|
+
async function promptFirstRunSetup(write = (line) => stdout.write(line)) {
|
|
42
|
+
const color = isColorEnabled();
|
|
43
|
+
write(formatFirstRunWelcome({ color }));
|
|
44
|
+
const rl = createInterface({
|
|
45
|
+
input: stdin,
|
|
46
|
+
output: stdout
|
|
47
|
+
});
|
|
48
|
+
try {
|
|
49
|
+
while (true) {
|
|
50
|
+
const answer = (await rl.question("> ")).trim().toLowerCase();
|
|
51
|
+
if (!answer || answer === "y" || answer === "yes") return "run";
|
|
52
|
+
if (answer === "n" || answer === "no") return "skip";
|
|
53
|
+
if (answer === "?" || answer === "docs" || answer === "help") return "docs";
|
|
54
|
+
write(hintLine([
|
|
55
|
+
{
|
|
56
|
+
key: "Y",
|
|
57
|
+
label: "yes"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
key: "n",
|
|
61
|
+
label: "no"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: "?",
|
|
65
|
+
label: "help"
|
|
66
|
+
}
|
|
67
|
+
], { color }) + "\n");
|
|
68
|
+
}
|
|
69
|
+
} finally {
|
|
70
|
+
rl.close();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { shouldOfferFirstRunSetup as a, promptFirstRunSetup as i, formatFirstRunWelcome as n, isCiEnvironment as r, DOCS_GETTING_STARTED_URL as t };
|