diffing 0.16.1 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/{cli-agent-Dk0yRtrA.mjs → cli-agent-du9TS34N.mjs} +763 -20
  2. package/dist/{cli-gh-BvoDabKK.mjs → cli-gh-2NEumKQB.mjs} +1 -1
  3. package/dist/cli.mjs +5 -4
  4. package/dist/client/assets/{angular-html-ByeJB0rh.js → angular-html-Des_xD-2.js} +1 -1
  5. package/dist/client/assets/{angular-ts-CiwZGajG.js → angular-ts-FXZjKR0Q.js} +1 -1
  6. package/dist/client/assets/{apl-C8k5a5fk.js → apl-CU0GH5rh.js} +1 -1
  7. package/dist/client/assets/{arc-B8VMoqVr.js → arc-DnvfnvVv.js} +1 -1
  8. package/dist/client/assets/{architectureDiagram-3BPJPVTR-C-ng2xNL.js → architectureDiagram-3BPJPVTR-Cb41y9QS.js} +1 -1
  9. package/dist/client/assets/{astro-BE-h-qpp.js → astro-CUWNJBWq.js} +1 -1
  10. package/dist/client/assets/{blade-D-0BV1wW.js → blade-2PPtLT0e.js} +1 -1
  11. package/dist/client/assets/{blockDiagram-GPEHLZMM-DyAQuONU.js → blockDiagram-GPEHLZMM-BYxsiST2.js} +1 -1
  12. package/dist/client/assets/{c-Dg_Gx29H.js → c-D3K42QQV.js} +1 -1
  13. package/dist/client/assets/{c4Diagram-AAUBKEIU-k5uKTHvm.js → c4Diagram-AAUBKEIU-BU7I7J1w.js} +1 -1
  14. package/dist/client/assets/channel-Cx6nXkw1.js +1 -0
  15. package/dist/client/assets/{chunk-2J33WTMH-Dod_VwYj.js → chunk-2J33WTMH-DQnKNewn.js} +1 -1
  16. package/dist/client/assets/{chunk-3OPIFGDE-B8HMjry-.js → chunk-3OPIFGDE-BE6bYzdw.js} +1 -1
  17. package/dist/client/assets/{chunk-4BX2VUAB-TvkJVIZw.js → chunk-4BX2VUAB-CTw-Pjas.js} +1 -1
  18. package/dist/client/assets/{chunk-55IACEB6-BsXuKAKc.js → chunk-55IACEB6-BBCsXXSw.js} +1 -1
  19. package/dist/client/assets/{chunk-5ZQYHXKU-BI1cj2cZ.js → chunk-5ZQYHXKU-BrM_4KH1.js} +1 -1
  20. package/dist/client/assets/{chunk-727SXJPM-yWQ27Wk0.js → chunk-727SXJPM-B_Vz3jyP.js} +1 -1
  21. package/dist/client/assets/{chunk-AQP2D5EJ-B2BBCtTr.js → chunk-AQP2D5EJ-CHSGbbjn.js} +1 -1
  22. package/dist/client/assets/{chunk-BSJP7CBP-CXXvPXhK.js → chunk-BSJP7CBP-D6jlDOzg.js} +1 -1
  23. package/dist/client/assets/{chunk-CSCIHK7Q-jYiloqyi.js → chunk-CSCIHK7Q-D_02XyF0.js} +1 -1
  24. package/dist/client/assets/{chunk-FMBD7UC4-DZzqIT32.js → chunk-FMBD7UC4-DxbqnfDH.js} +1 -1
  25. package/dist/client/assets/{chunk-KSCS5N6A-krdCf8iG.js → chunk-KSCS5N6A-_Pc1IHYq.js} +1 -1
  26. package/dist/client/assets/{chunk-L5ZTLDWV-0uuTmeZV.js → chunk-L5ZTLDWV-Dj8OTMjE.js} +1 -1
  27. package/dist/client/assets/{chunk-LZXEDZCA-BBfUgD06.js → chunk-LZXEDZCA-DQn_LdxZ.js} +2 -2
  28. package/dist/client/assets/{chunk-ND2GUHAM-jkir4ZKE.js → chunk-ND2GUHAM-BYbHwrXp.js} +1 -1
  29. package/dist/client/assets/{chunk-NZK2D7GU-DMWZNibJ.js → chunk-NZK2D7GU-Bvgyac_1.js} +1 -1
  30. package/dist/client/assets/{chunk-O5CBEL6O-B1e1HGaX.js → chunk-O5CBEL6O-DeRRUqxP.js} +1 -1
  31. package/dist/client/assets/chunk-QZHKN3VN-BkYYlBcO.js +1 -0
  32. package/dist/client/assets/chunk-WU5MYG2G-DyGKxcFl.js +1 -0
  33. package/dist/client/assets/{chunk-XPW4576I-AvbCAUav.js → chunk-XPW4576I-BrtDtomN.js} +1 -1
  34. package/dist/client/assets/classDiagram-4FO5ZUOK-DwPqBLeK.js +1 -0
  35. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Mxl80axQ.js +1 -0
  36. package/dist/client/assets/{cobol-DQnGB_GV.js → cobol-hL9_ibhX.js} +1 -1
  37. package/dist/client/assets/{coffee-CDV5a89x.js → coffee-DFRt5qt1.js} +1 -1
  38. package/dist/client/assets/{cose-bilkent-S5V4N54A-t-mDNRF2.js → cose-bilkent-S5V4N54A-DHCJqbho.js} +1 -1
  39. package/dist/client/assets/{cpp-8G6b-siI.js → cpp-3A0aT-dF.js} +1 -1
  40. package/dist/client/assets/{crystal-CGYCC6FM.js → crystal-BzBxs8w6.js} +1 -1
  41. package/dist/client/assets/{css-nez4R8_Z.js → css-C5lLpDbg.js} +1 -1
  42. package/dist/client/assets/{dagre-BM42HDAG--azesYIg.js → dagre-BM42HDAG-DOuf0bNE.js} +1 -1
  43. package/dist/client/assets/{diagram-2AECGRRQ-4WJ7nKhA.js → diagram-2AECGRRQ-Bd5Jiq1y.js} +1 -1
  44. package/dist/client/assets/{diagram-5GNKFQAL-C7ZGhLXn.js → diagram-5GNKFQAL-DvYLKuEY.js} +1 -1
  45. package/dist/client/assets/{diagram-KO2AKTUF-BobK_rbl.js → diagram-KO2AKTUF-BVvqGAtk.js} +1 -1
  46. package/dist/client/assets/{diagram-LMA3HP47-BOc-OkIS.js → diagram-LMA3HP47-QqHAHEnc.js} +1 -1
  47. package/dist/client/assets/{diagram-OG6HWLK6-BEtLgm86.js → diagram-OG6HWLK6-CkG5SOks.js} +1 -1
  48. package/dist/client/assets/{dist-DPuBwToc.js → dist-Dh8NC3tY.js} +1 -1
  49. package/dist/client/assets/{edge-Z5RMbJQv.js → edge-9n_aVc7U.js} +1 -1
  50. package/dist/client/assets/{edit-CFxbgVPV.js → edit-DlkvWCkd.js} +1 -1
  51. package/dist/client/assets/{elixir-d3C79FQc.js → elixir-C7ssdzaK.js} +1 -1
  52. package/dist/client/assets/{elm-CDAf6Uyn.js → elm-CWs6E90H.js} +1 -1
  53. package/dist/client/assets/{erDiagram-TEJ5UH35-D-pR2GY9.js → erDiagram-TEJ5UH35-CfW0k0ip.js} +1 -1
  54. package/dist/client/assets/{erb-CTJk9FKH.js → erb-CdXoY37D.js} +1 -1
  55. package/dist/client/assets/{flowDiagram-I6XJVG4X-BwMCy2QV.js → flowDiagram-I6XJVG4X-CBKpQpqx.js} +1 -1
  56. package/dist/client/assets/{ganttDiagram-6RSMTGT7-BI1WLJDi.js → ganttDiagram-6RSMTGT7-CS13EpgX.js} +1 -1
  57. package/dist/client/assets/{git-rebase-Dr0-MVoW.js → git-rebase-BSoXZvcc.js} +1 -1
  58. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CUfW0k1f.js → gitGraphDiagram-PVQCEYII-BN0i9pDL.js} +1 -1
  59. package/dist/client/assets/{glimmer-js-CXhLhyW-.js → glimmer-js-BZMH6HKs.js} +1 -1
  60. package/dist/client/assets/{glimmer-ts-BFAFT-VD.js → glimmer-ts-DXSo27g6.js} +1 -1
  61. package/dist/client/assets/{glsl-D7yanhgY.js → glsl-Cl8JUVz8.js} +1 -1
  62. package/dist/client/assets/{graphql-CpoDxrok.js → graphql-CNR_SNqJ.js} +1 -1
  63. package/dist/client/assets/{hack-BL5fmC4q.js → hack-iAxNUz76.js} +1 -1
  64. package/dist/client/assets/{haml-ClAE6RVM.js → haml-D11gKVlW.js} +1 -1
  65. package/dist/client/assets/{handlebars-nHw91uOv.js → handlebars-BAAs9kjt.js} +1 -1
  66. package/dist/client/assets/{html-BIiOKqgG.js → html-C-zWst2_.js} +1 -1
  67. package/dist/client/assets/{html-derivative-D1nPnuOK.js → html-derivative-Bu6k8r8J.js} +1 -1
  68. package/dist/client/assets/{http-B_QcRZjt.js → http-B_xQ7YQ1.js} +1 -1
  69. package/dist/client/assets/{hurl-CZE0DsD3.js → hurl-B7IYz3qL.js} +1 -1
  70. package/dist/client/assets/index-1GDpp-wO.css +1 -0
  71. package/dist/client/assets/{index-Erqhm7rW.js → index-D_eRtjau.js} +49 -49
  72. package/dist/client/assets/{infoDiagram-5YYISTIA-w2M_OHua.js → infoDiagram-5YYISTIA-Choq89cB.js} +1 -1
  73. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CW_l9PGx.js → ishikawaDiagram-YF4QCWOH-CZY99mk-.js} +1 -1
  74. package/dist/client/assets/{java-CXTqX09w.js → java-BPJVzio0.js} +1 -1
  75. package/dist/client/assets/{javascript-9z9ABBep.js → javascript-DiOMtf_A.js} +1 -1
  76. package/dist/client/assets/{jinja-Cluf_5Bk.js → jinja-l7JM-8PK.js} +1 -1
  77. package/dist/client/assets/{jison-CqCEUtc4.js → jison-CAc6lM-g.js} +1 -1
  78. package/dist/client/assets/{journeyDiagram-JHISSGLW-D-YwusBa.js → journeyDiagram-JHISSGLW-CDbADAMs.js} +1 -1
  79. package/dist/client/assets/{json-BMq_bPP8.js → json-DqZO-AEh.js} +1 -1
  80. package/dist/client/assets/{jsx-Djij59b9.js → jsx-UqnCpKTP.js} +1 -1
  81. package/dist/client/assets/{julia-DybzCMnh.js → julia-DlbfYJz3.js} +1 -1
  82. package/dist/client/assets/{just-4ChcAr9-.js → just-9ze8Wi6d.js} +1 -1
  83. package/dist/client/assets/{kanban-definition-UN3LZRKU-Brvoukmm.js → kanban-definition-UN3LZRKU-DgrkeisF.js} +1 -1
  84. package/dist/client/assets/{latex-CS2TtFXG.js → latex-_9Fzq-E3.js} +1 -1
  85. package/dist/client/assets/{line-CqxM-stA.js → line-b-Rcrqbg.js} +1 -1
  86. package/dist/client/assets/{linear-C7T82059.js → linear-C_RFbWEw.js} +1 -1
  87. package/dist/client/assets/{liquid-84AtYenm.js → liquid-CMiBoRv6.js} +1 -1
  88. package/dist/client/assets/{lua-DJzFx3Cf.js → lua-BzfULkCh.js} +1 -1
  89. package/dist/client/assets/{marko-BLw9BV9E.js → marko-BfcVqnF5.js} +1 -1
  90. package/dist/client/assets/{mdc-BjZejN3b.js → mdc-BKqkdcKC.js} +1 -1
  91. package/dist/client/assets/{mermaid-parser.core-BdiEBAsd.js → mermaid-parser.core-DiJRF2JX.js} +1 -1
  92. package/dist/client/assets/{mermaid.core-BsnKhnrM.js → mermaid.core-2WkwaCuC.js} +3 -3
  93. package/dist/client/assets/{mindmap-definition-RKZ34NQL-CBaIFnbc.js → mindmap-definition-RKZ34NQL-BYxdNWsh.js} +1 -1
  94. package/dist/client/assets/{nginx-DuamCwvX.js → nginx-DiNLSsbI.js} +1 -1
  95. package/dist/client/assets/{nim-Bt8c9-ge.js → nim-CohF0tPX.js} +1 -1
  96. package/dist/client/assets/{perl-D52AfmwC.js → perl-Q-a6BHYR.js} +1 -1
  97. package/dist/client/assets/{php-BBMlJg0_.js → php-DEj4odG7.js} +1 -1
  98. package/dist/client/assets/{pieDiagram-4H26LBE5-BlmPajcA.js → pieDiagram-4H26LBE5-DIMAUXZJ.js} +1 -1
  99. package/dist/client/assets/{pug-DFHmDqEE.js → pug-vqq1Jo-6.js} +1 -1
  100. package/dist/client/assets/{qml-C0333o-g.js → qml-DkOoIH_j.js} +1 -1
  101. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Bqlm_zZk.js → quadrantDiagram-W4KKPZXB-DKoe-k2E.js} +1 -1
  102. package/dist/client/assets/{r-DZFmvIgR.js → r-Cen6c1F0.js} +1 -1
  103. package/dist/client/assets/{razor-BIGLkWzR.js → razor-SY8AQA9t.js} +1 -1
  104. package/dist/client/assets/{regexp-BW6jpgf9.js → regexp-CjphwwlN.js} +1 -1
  105. package/dist/client/assets/{requirementDiagram-4Y6WPE33-B1PrtUeD.js → requirementDiagram-4Y6WPE33-Du9fS-Dw.js} +1 -1
  106. package/dist/client/assets/{review-export-BlgD3bdw.js → review-export-FIT7B1uP.js} +1 -1
  107. package/dist/client/assets/{rst-Cj51ZJaa.js → rst-B79v9bjn.js} +1 -1
  108. package/dist/client/assets/{ruby-lMhGjwpf.js → ruby-Dia4QaOO.js} +1 -1
  109. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-D2HtUNVF.js → sankeyDiagram-5OEKKPKP-CsezsM2Z.js} +1 -1
  110. package/dist/client/assets/{sas-BWSQku7l.js → sas-CYRTwHiQ.js} +1 -1
  111. package/dist/client/assets/{scss-CdRPuhLN.js → scss-DNWCy5-q.js} +1 -1
  112. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-D9SQy0XI.js → sequenceDiagram-3UESZ5HK-CNovbaFC.js} +1 -1
  113. package/dist/client/assets/{shellscript-DOfOBXcj.js → shellscript-BSQLDkAe.js} +1 -1
  114. package/dist/client/assets/{shellsession-7XAOCVni.js → shellsession-CfvHV3oR.js} +1 -1
  115. package/dist/client/assets/{soy-DDnzVRIw.js → soy-DLwu5RtN.js} +1 -1
  116. package/dist/client/assets/{sql-Ba34agKe.js → sql-DlyzTy0P.js} +1 -1
  117. package/dist/client/assets/{src-BO-qT_v_.js → src-DNJJnOd8.js} +1 -1
  118. package/dist/client/assets/{stata-BQlYGHQU.js → stata-Djh-WYVT.js} +1 -1
  119. package/dist/client/assets/{stateDiagram-AJRCARHV-7IEYjFRo.js → stateDiagram-AJRCARHV-cV6R_6CN.js} +1 -1
  120. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-jmBy204V.js +1 -0
  121. package/dist/client/assets/{surrealql-BET1l7SW.js → surrealql-Cu-G0NVH.js} +1 -1
  122. package/dist/client/assets/{svelte-Bd7ZOeQI.js → svelte-CDA2MakI.js} +1 -1
  123. package/dist/client/assets/{templ-BYvRQdOw.js → templ-H2xc1tDE.js} +1 -1
  124. package/dist/client/assets/{tex-CT8DpKxX.js → tex-DJ6mQsYC.js} +1 -1
  125. package/dist/client/assets/{timeline-definition-PNZ67QCA-CurQYWmi.js → timeline-definition-PNZ67QCA-BlCMASdx.js} +1 -1
  126. package/dist/client/assets/{ts-tags-D4iPPwau.js → ts-tags-bYEWgmJO.js} +1 -1
  127. package/dist/client/assets/{tsx-Dgk_Aw-B.js → tsx-CtTBiWFd.js} +1 -1
  128. package/dist/client/assets/{twig-abRNWeh3.js → twig-CgXr9wNF.js} +1 -1
  129. package/dist/client/assets/{typescript-BgR1JqdC.js → typescript-Na7Lj9Vl.js} +1 -1
  130. package/dist/client/assets/{vennDiagram-CIIHVFJN-DbrRBL5S.js → vennDiagram-CIIHVFJN-CvcrIycD.js} +1 -1
  131. package/dist/client/assets/{vue-DSkhKq4Q.js → vue-BtOd_AfK.js} +1 -1
  132. package/dist/client/assets/{vue-html-J0jWoD9X.js → vue-html-CVlcjzDD.js} +1 -1
  133. package/dist/client/assets/{vue-vine-BgX5MC8-.js → vue-vine-k7M6KtfE.js} +1 -1
  134. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DIFXQsoP.js → wardleyDiagram-YWT4CUSO-BUDJvh59.js} +1 -1
  135. package/dist/client/assets/{xml-Ca53xGLB.js → xml-Brz46P3k.js} +1 -1
  136. package/dist/client/assets/{xsl-CNnXclYV.js → xsl-Bh5vdfXi.js} +1 -1
  137. package/dist/client/assets/{xychartDiagram-2RQKCTM6-CmfInPqa.js → xychartDiagram-2RQKCTM6-T2Bb0lF_.js} +1 -1
  138. package/dist/client/assets/{yaml-CjjyUhfg.js → yaml-CRsmB7sL.js} +1 -1
  139. package/dist/client/index.html +2 -2
  140. package/dist/{completions-7YSX1neu.mjs → completions-2aHVTmiJ.mjs} +17 -0
  141. package/dist/completions-CYa5usLb.mjs +2 -0
  142. package/dist/{mcp-BgrRjzn3.mjs → mcp-H7ytYScV.mjs} +430 -23
  143. package/dist/mockup-format-CM3ivHHX.mjs +322 -0
  144. package/dist/native/tui-darwin-arm64/diffing-tui +0 -0
  145. package/dist/native/tui-darwin-x64/diffing-tui +0 -0
  146. package/dist/native/tui-linux-arm64-gnu/diffing-tui +0 -0
  147. package/dist/native/tui-linux-arm64-musl/diffing-tui +0 -0
  148. package/dist/native/tui-linux-x64-gnu/diffing-tui +0 -0
  149. package/dist/native/tui-linux-x64-musl/diffing-tui +0 -0
  150. package/dist/native/tui-win32-x64-msvc/diffing-tui.exe +0 -0
  151. package/dist/{review-export-GW4KNsGN.mjs → review-export-DoST2oPt.mjs} +1 -1
  152. package/dist/{server-Ct9QuV1l.mjs → server-CiE24vh-.mjs} +1842 -53
  153. package/dist/{setup-BnS_rSxK.mjs → setup-BwWZ9Fzq.mjs} +1 -1
  154. package/extensions/pi/index.ts +453 -5
  155. package/package.json +2 -1
  156. package/dist/client/assets/channel-DsuXeLi4.js +0 -1
  157. package/dist/client/assets/chunk-QZHKN3VN-D5b3mBJ2.js +0 -1
  158. package/dist/client/assets/chunk-WU5MYG2G-C1Pa4Y04.js +0 -1
  159. package/dist/client/assets/classDiagram-4FO5ZUOK-DCnVfYt-.js +0 -1
  160. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BPa1Jkt-.js +0 -1
  161. package/dist/client/assets/index-BR6EGJ-g.css +0 -1
  162. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-DP6swlL9.js +0 -1
  163. package/dist/completions-5rtfoxB9.mjs +0 -2
  164. package/dist/plan-format-BwJh5JSE.mjs +0 -144
  165. /package/dist/{comment-format-B85NHZnG.mjs → comment-format-D4mCqr7T.mjs} +0 -0
@@ -1,13 +1,13 @@
1
- import { f as getProjectStorageDir } from "./git-BGj31GX-.mjs";
1
+ import { f as getProjectStorageDir, h as getRepoRoot } from "./git-BGj31GX-.mjs";
2
2
  import { i as loadSettings, n as formatModeLabel, s as saveSettings } from "./settings-DkPDB_hS.mjs";
3
- import { t as formatComments } from "./comment-format-B85NHZnG.mjs";
4
- import { n as formatPlanReview } from "./plan-format-BwJh5JSE.mjs";
3
+ import { f as slugifyScreenId, r as formatPlanReview, t as formatMockupReview } from "./mockup-format-CM3ivHHX.mjs";
4
+ import { t as formatComments } from "./comment-format-D4mCqr7T.mjs";
5
5
  import { u as resolveActiveServerLock, x as SESSION_TOKEN_HEADER } from "./server-lock-CdaUNZyX.mjs";
6
6
  import { i as reviewSessionUrl, t as appendSessionToken } from "./session-url-B9FOnF3F.mjs";
7
7
  import { a as CLI_AWAIT_REVIEW_TIMEOUT_HINT, i as CLI_AWAIT_PLAN_TIMEOUT_HINT, o as CLI_PLAN_SUBMIT_PARK_HINT } from "./handoff-DcShCxWJ.mjs";
8
- import { join } from "node:path";
8
+ import { basename, join, resolve } from "node:path";
9
9
  import { parseArgs } from "node:util";
10
- import { mkdir, readFile, writeFile } from "node:fs/promises";
10
+ import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
11
11
  //#region src/cli-agent.ts
12
12
  /**
13
13
  * Agent-facing `diffing` subcommands. These make the user→agent handoff
@@ -25,6 +25,13 @@ const EXIT_AWAIT_TIMEOUT = 2;
25
25
  const EXIT_NO_SERVER = 3;
26
26
  const EXIT_NOT_FOUND = 4;
27
27
  const EXIT_USAGE = 5;
28
+ function validateInspectSelectors(resource, file, path) {
29
+ const hasFile = file != null && file !== "";
30
+ const hasPath = path != null && path !== "";
31
+ if ((resource === "hunks" || resource === "slice") && hasFile && hasPath) return `diffing inspect ${resource}: --path and --file are mutually exclusive`;
32
+ if ((resource === "hunks" || resource === "slice") && !hasFile && !hasPath) return `diffing inspect ${resource}: --file or --path is required`;
33
+ return null;
34
+ }
28
35
  let activeCapability;
29
36
  let activeAuthToken;
30
37
  /** Resolve the running server's base URL from the lockfile, or exit cleanly. */
@@ -41,6 +48,17 @@ function baseUrl() {
41
48
  }
42
49
  /** Attach session credentials while preserving ordinary web calls. */
43
50
  function apiFetch(input, init = {}) {
51
+ let target;
52
+ try {
53
+ target = new URL(input instanceof Request ? input.url : input instanceof URL ? input.href : input);
54
+ } catch {
55
+ return Promise.reject(/* @__PURE__ */ new Error("Invalid diffing API URL"));
56
+ }
57
+ if (![
58
+ "127.0.0.1",
59
+ "localhost",
60
+ "::1"
61
+ ].includes(target.hostname)) throw new Error(`Refusing non-loopback diffing API URL: ${target.origin}`);
44
62
  const headers = new Headers(init.headers);
45
63
  if (activeCapability) headers.set("X-Diffing-Capability", activeCapability);
46
64
  if (activeAuthToken) headers.set(SESSION_TOKEN_HEADER, activeAuthToken);
@@ -372,7 +390,7 @@ async function comments(args) {
372
390
  }
373
391
  if (values.json || format === "json") process.stdout.write(JSON.stringify(selected, null, 2) + "\n");
374
392
  else if (format === "markdown" || format === "md") {
375
- const { formatCommentsMarkdown } = await import("./review-export-GW4KNsGN.mjs");
393
+ const { formatCommentsMarkdown } = await import("./review-export-DoST2oPt.mjs");
376
394
  process.stdout.write(formatCommentsMarkdown(selected) + "\n");
377
395
  } else process.stdout.write(formatComments(selected) + "\n");
378
396
  return EXIT_OK;
@@ -638,7 +656,7 @@ async function planVersions(args) {
638
656
  allowPositionals: true
639
657
  });
640
658
  const base = baseUrl();
641
- let planId = positionals[0];
659
+ const planId = positionals[0];
642
660
  if (!planId) {
643
661
  console.error("Usage: diffing plan versions <id> [--json]");
644
662
  return EXIT_USAGE;
@@ -826,7 +844,7 @@ See docs/getting-started.md for the full walkthrough.`);
826
844
  console.error(`Unknown setup action: ${action}`);
827
845
  return EXIT_USAGE;
828
846
  }
829
- const { runSetup } = await import("./setup-BnS_rSxK.mjs");
847
+ const { runSetup } = await import("./setup-BwWZ9Fzq.mjs");
830
848
  return runSetup({
831
849
  yes,
832
850
  check,
@@ -862,7 +880,7 @@ async function completion(args) {
862
880
  console.error("Usage: diffing completion <bash|zsh|fish>");
863
881
  return EXIT_USAGE;
864
882
  }
865
- const { completionFor } = await import("./completions-5rtfoxB9.mjs");
883
+ const { completionFor } = await import("./completions-CYa5usLb.mjs");
866
884
  const script = completionFor(shell);
867
885
  if (!script) {
868
886
  console.error(`Unknown shell: ${shell}. Use bash, zsh, or fish.`);
@@ -880,6 +898,8 @@ async function inspect(args) {
880
898
  cursor: { type: "string" },
881
899
  limit: { type: "string" },
882
900
  file: { type: "string" },
901
+ path: { type: "string" },
902
+ exclude: { type: "string" },
883
903
  generation: { type: "string" },
884
904
  start: { type: "string" },
885
905
  row: { type: "string" },
@@ -906,12 +926,13 @@ async function inspect(args) {
906
926
  console.error(`Usage: diffing inspect <summary|files|hunks|slice|search> [options]
907
927
 
908
928
  Read bounded data from a running session (web, TUI, or gh-pr) without transferring the full patch.
909
- summary
910
- files [--cursor N] [--limit N]
911
- hunks --file N [--cursor N] [--limit N] [--generation N]
912
- slice --file N [--start N] [--max-lines N] [--max-bytes N] [--generation N]
913
- search <text>|--query <text> [--file N] [--row N] [--limit N] [--max-bytes N] [--generation N]
929
+ summary [--exclude lockfiles]
930
+ files [--path GLOB] [--cursor N] [--limit N]
931
+ hunks (--file N | --path GLOB) [--cursor N] [--limit N] [--generation N]
932
+ slice (--file N | --path GLOB) [--start N] [--max-lines N] [--max-bytes N] [--generation N]
933
+ search <text>|--query <text> [--path GLOB] [--file N] [--row N] [--limit N] [--max-bytes N] [--generation N]
914
934
 
935
+ Filtered files nextCursor is an index into the filtered list, not a global file index.
915
936
  Add --pretty for indented JSON. Compact JSON is the token-efficient default.`);
916
937
  return parsed.values.help ? EXIT_OK : EXIT_USAGE;
917
938
  }
@@ -945,8 +966,13 @@ Add --pretty for indented JSON. Compact JSON is the token-efficient default.`);
945
966
  }
946
967
  params.set(parameter, value);
947
968
  }
948
- if ((resource === "hunks" || resource === "slice") && !params.has("file")) {
949
- console.error(`diffing inspect ${resource}: --file is required`);
969
+ const path = parsed.values.path;
970
+ if (typeof path === "string") params.set("path", path);
971
+ const exclude = parsed.values.exclude;
972
+ if (typeof exclude === "string") params.set("exclude", exclude);
973
+ const selectorError = validateInspectSelectors(resource, typeof parsed.values.file === "string" ? parsed.values.file : void 0, typeof path === "string" ? path : void 0);
974
+ if (selectorError) {
975
+ console.error(selectorError);
950
976
  return EXIT_USAGE;
951
977
  }
952
978
  if (resource === "search") {
@@ -1017,6 +1043,719 @@ async function progress(args) {
1017
1043
  }
1018
1044
  return EXIT_OK;
1019
1045
  }
1046
+ const CLI_MOCKUP_SUBMIT_PARK_HINT = "Async handoff: share the URL above. Use --wait / `diffing mockup await` only when the human is reviewing now or asked you to block.";
1047
+ async function pollMockupDecision(base, timeoutMs, sinceRound = 0) {
1048
+ const deadline = Date.now() + timeoutMs;
1049
+ while (Date.now() < deadline) {
1050
+ const remaining = Math.max(deadline - Date.now(), 0);
1051
+ const res = await tryApiFetch(`${base}/api/mockup-review/await?sinceRound=${sinceRound}&timeoutMs=${Math.min(remaining, 25e3)}`);
1052
+ if (!res) return EXIT_NO_SERVER;
1053
+ if (!res.ok) {
1054
+ console.error(`Failed to await mockup review: HTTP ${res.status}`);
1055
+ return 1;
1056
+ }
1057
+ const result = await res.json();
1058
+ if (result.status === "released" && result.payload) {
1059
+ if (result.payload.reviewXml) process.stdout.write(result.payload.reviewXml + "\n");
1060
+ console.error(`DIFFING_MOCKUP_DECISION=${result.payload.decision}`);
1061
+ console.error(`DIFFING_MOCKUP_ROUND=${result.payload.round}`);
1062
+ return EXIT_OK;
1063
+ }
1064
+ sinceRound = result.round ?? sinceRound;
1065
+ }
1066
+ console.error("DIFFING_MOCKUP_AWAIT_TIMEOUT");
1067
+ return EXIT_AWAIT_TIMEOUT;
1068
+ }
1069
+ async function screensFromCli(file, screenFlags) {
1070
+ if (screenFlags && screenFlags.length > 0) {
1071
+ const screens = [];
1072
+ for (const flag of screenFlags) {
1073
+ const eq = flag.indexOf("=");
1074
+ if (eq <= 0) return { error: `--screen must be id=path (got ${flag})` };
1075
+ const id = slugifyScreenId(flag.slice(0, eq));
1076
+ if (!id) return { error: `Invalid screen id in ${flag}` };
1077
+ const path = resolve(flag.slice(eq + 1));
1078
+ try {
1079
+ const html = (await readFile(path, "utf-8")).replace(/\r\n/g, "\n");
1080
+ screens.push({
1081
+ id,
1082
+ html
1083
+ });
1084
+ } catch (err) {
1085
+ return { error: `Failed to read ${path}: ${err?.message ?? err}` };
1086
+ }
1087
+ }
1088
+ return { screens };
1089
+ }
1090
+ if (!file || file === "-") {
1091
+ const html = (await readStdin()).replace(/\r\n/g, "\n");
1092
+ if (!html.trim()) return { error: "A mockup HTML body is required (file, dir, --screen, or stdin)." };
1093
+ return { html };
1094
+ }
1095
+ const abs = resolve(file);
1096
+ let st;
1097
+ try {
1098
+ st = await stat(abs);
1099
+ } catch (err) {
1100
+ return { error: `Failed to read ${file}: ${err?.message ?? err}` };
1101
+ }
1102
+ if (st.isDirectory()) {
1103
+ const names = (await readdir(abs)).filter((n) => /\.html?$/i.test(n));
1104
+ names.sort((a, b) => {
1105
+ if (a.toLowerCase() === "index.html") return -1;
1106
+ if (b.toLowerCase() === "index.html") return 1;
1107
+ return a.localeCompare(b);
1108
+ });
1109
+ if (names.length === 0) return { error: `No HTML files in ${abs}` };
1110
+ const screens = [];
1111
+ for (const name of names) {
1112
+ const id = slugifyScreenId(name);
1113
+ if (!id) continue;
1114
+ const html = (await readFile(join(abs, name), "utf-8")).replace(/\r\n/g, "\n");
1115
+ screens.push({
1116
+ id,
1117
+ html
1118
+ });
1119
+ }
1120
+ if (screens.length === 0) return { error: `No valid screen ids in ${abs}` };
1121
+ return {
1122
+ screens,
1123
+ source: abs
1124
+ };
1125
+ }
1126
+ const html = (await readFile(abs, "utf-8")).replace(/\r\n/g, "\n");
1127
+ if (!html.trim()) return { error: `Empty HTML file ${file}` };
1128
+ return {
1129
+ html,
1130
+ source: abs
1131
+ };
1132
+ }
1133
+ function warnMockupPathsInRepo(paths) {
1134
+ let root;
1135
+ try {
1136
+ root = getRepoRoot();
1137
+ } catch {
1138
+ return;
1139
+ }
1140
+ const storage = getProjectStorageDir();
1141
+ const rootPrefix = root.endsWith("/") ? root : `${root}/`;
1142
+ const storagePrefix = storage.endsWith("/") ? storage : `${storage}/`;
1143
+ for (const raw of paths) {
1144
+ const abs = resolve(raw);
1145
+ if (abs === storage || abs.startsWith(storagePrefix)) continue;
1146
+ if (abs === root || abs.startsWith(rootPrefix)) console.error(`Warning: ${abs} is inside the consumer repo. Never write diffing mockups into the working tree. Submit HTML via stdin or MCP submit_mockup({ html }), or stage files under ${storage}/mockup-sources/.`);
1147
+ }
1148
+ }
1149
+ async function mockupSubmit(args) {
1150
+ const { values, positionals } = parseArgs({
1151
+ args,
1152
+ options: {
1153
+ title: { type: "string" },
1154
+ source: {
1155
+ type: "string",
1156
+ short: "s"
1157
+ },
1158
+ model: {
1159
+ type: "string",
1160
+ short: "m"
1161
+ },
1162
+ id: { type: "string" },
1163
+ wait: {
1164
+ type: "boolean",
1165
+ short: "w"
1166
+ },
1167
+ timeout: {
1168
+ type: "string",
1169
+ short: "t"
1170
+ },
1171
+ screen: {
1172
+ type: "string",
1173
+ multiple: true
1174
+ }
1175
+ },
1176
+ allowPositionals: true
1177
+ });
1178
+ warnMockupPathsInRepo([...positionals[0] && positionals[0] !== "-" ? [positionals[0]] : [], ...(values.screen ?? []).map((flag) => {
1179
+ const eq = flag.indexOf("=");
1180
+ return eq > 0 ? flag.slice(eq + 1) : "";
1181
+ }).filter(Boolean)]);
1182
+ const parsed = await screensFromCli(positionals[0], values.screen);
1183
+ if (parsed.error) {
1184
+ console.error(parsed.error);
1185
+ return EXIT_USAGE;
1186
+ }
1187
+ const title = values.title || (positionals[0] && positionals[0] !== "-" ? basename(positionals[0]).replace(/\.html?$/i, "") : "Untitled mockup");
1188
+ const base = baseUrl();
1189
+ let sinceRound = 0;
1190
+ if (values.wait) {
1191
+ const statusRes = await tryApiFetch(`${base}/api/mockup-review/status`);
1192
+ if (statusRes?.ok) sinceRound = (await statusRes.json()).round ?? 0;
1193
+ }
1194
+ const submitRes = await tryApiFetch(`${base}/api/mockups`, {
1195
+ method: "POST",
1196
+ headers: { "Content-Type": "application/json" },
1197
+ body: JSON.stringify({
1198
+ id: values.id,
1199
+ title,
1200
+ html: parsed.html,
1201
+ screens: parsed.screens,
1202
+ source: values.source || parsed.source,
1203
+ model: values.model
1204
+ })
1205
+ });
1206
+ if (!submitRes) return EXIT_NO_SERVER;
1207
+ if (!submitRes.ok) {
1208
+ const err = await submitRes.json().catch(() => ({ error: `HTTP ${submitRes.status}` }));
1209
+ console.error(`Failed to submit mockup: ${err.error ?? `HTTP ${submitRes.status}`}`);
1210
+ return 1;
1211
+ }
1212
+ const mockup = await submitRes.json();
1213
+ console.error(`Submitted mockup ${mockup.id} (v${mockup.version}) — review at ${appendSessionToken(`${base}/mockup/${mockup.id}`, activeAuthToken)}`);
1214
+ if (mockup.sourcePath) console.error(`Source path: ${mockup.sourcePath}`);
1215
+ if (!values.wait) {
1216
+ console.error(CLI_MOCKUP_SUBMIT_PARK_HINT);
1217
+ process.stdout.write(mockup.id + "\n");
1218
+ return EXIT_OK;
1219
+ }
1220
+ const timeoutSeconds = parseTimeoutSeconds(values.timeout);
1221
+ if (timeoutSeconds === null) return EXIT_USAGE;
1222
+ return pollMockupDecision(base, timeoutSeconds * 1e3, sinceRound);
1223
+ }
1224
+ async function mockupAwait(args) {
1225
+ const { values } = parseArgs({
1226
+ args,
1227
+ options: { timeout: {
1228
+ type: "string",
1229
+ short: "t"
1230
+ } },
1231
+ allowPositionals: false
1232
+ });
1233
+ const timeoutSeconds = parseTimeoutSeconds(values.timeout);
1234
+ if (timeoutSeconds === null) return EXIT_USAGE;
1235
+ return pollMockupDecision(baseUrl(), timeoutSeconds * 1e3);
1236
+ }
1237
+ async function mockupList(args) {
1238
+ const { values } = parseArgs({
1239
+ args,
1240
+ options: { json: { type: "boolean" } },
1241
+ allowPositionals: false
1242
+ });
1243
+ const res = await tryApiFetch(`${baseUrl()}/api/mockups`);
1244
+ if (!res) return EXIT_NO_SERVER;
1245
+ if (!res.ok) {
1246
+ console.error(`Failed to list mockups: HTTP ${res.status}`);
1247
+ return 1;
1248
+ }
1249
+ const all = await res.json();
1250
+ if (values.json) {
1251
+ process.stdout.write(JSON.stringify(all, null, 2) + "\n");
1252
+ return EXIT_OK;
1253
+ }
1254
+ if (all.length === 0) {
1255
+ console.error("No mockups submitted yet.");
1256
+ return EXIT_OK;
1257
+ }
1258
+ for (const m of all) {
1259
+ const open = (m.comments ?? []).filter((c) => c.status === "open").length;
1260
+ process.stdout.write(`${m.id}\t[${m.decision}]\tv${m.version}\t${m.screens.length} screen(s)\t${open} open\t${m.title}\n`);
1261
+ }
1262
+ return EXIT_OK;
1263
+ }
1264
+ async function mockupShow(args) {
1265
+ const { values, positionals } = parseArgs({
1266
+ args,
1267
+ options: {
1268
+ json: { type: "boolean" },
1269
+ version: { type: "string" }
1270
+ },
1271
+ allowPositionals: true
1272
+ });
1273
+ const base = baseUrl();
1274
+ let mockupId = positionals[0];
1275
+ if (!mockupId) {
1276
+ const listRes = await tryApiFetch(`${base}/api/mockups`);
1277
+ if (!listRes) return EXIT_NO_SERVER;
1278
+ const all = listRes.ok ? await listRes.json() : [];
1279
+ if (all.length === 0) {
1280
+ console.error("No mockups submitted yet.");
1281
+ return EXIT_NOT_FOUND;
1282
+ }
1283
+ mockupId = all[all.length - 1].id;
1284
+ }
1285
+ const res = await tryApiFetch(`${base}/api/mockups/${mockupId}`);
1286
+ if (!res) return EXIT_NO_SERVER;
1287
+ if (res.status === 404) {
1288
+ console.error(`Mockup ${mockupId} not found.`);
1289
+ return EXIT_NOT_FOUND;
1290
+ }
1291
+ if (!res.ok) {
1292
+ console.error(`Failed to load mockup: HTTP ${res.status}`);
1293
+ return 1;
1294
+ }
1295
+ const mockup = await res.json();
1296
+ const requestedVersion = values.version !== void 0 ? Number(values.version) : void 0;
1297
+ if (requestedVersion !== void 0 && (!Number.isFinite(requestedVersion) || requestedVersion < 1)) {
1298
+ console.error(`--version must be a positive integer.`);
1299
+ return EXIT_USAGE;
1300
+ }
1301
+ if (values.json) process.stdout.write(JSON.stringify(mockup, null, 2) + "\n");
1302
+ else process.stdout.write(formatMockupReview(mockup, { viewingVersion: requestedVersion }) + "\n");
1303
+ return EXIT_OK;
1304
+ }
1305
+ async function mockupVersions(args) {
1306
+ const { values, positionals } = parseArgs({
1307
+ args,
1308
+ options: { json: { type: "boolean" } },
1309
+ allowPositionals: true
1310
+ });
1311
+ const id = positionals[0];
1312
+ if (!id) {
1313
+ console.error("Usage: diffing mockup versions <id>");
1314
+ return EXIT_USAGE;
1315
+ }
1316
+ const res = await tryApiFetch(`${baseUrl()}/api/mockups/${id}/versions`);
1317
+ if (!res) return EXIT_NO_SERVER;
1318
+ if (res.status === 404) {
1319
+ console.error(`Mockup ${id} not found.`);
1320
+ return EXIT_NOT_FOUND;
1321
+ }
1322
+ if (!res.ok) {
1323
+ console.error(`Failed to list versions: HTTP ${res.status}`);
1324
+ return 1;
1325
+ }
1326
+ const versions = await res.json();
1327
+ if (values.json) {
1328
+ process.stdout.write(JSON.stringify(versions, null, 2) + "\n");
1329
+ return EXIT_OK;
1330
+ }
1331
+ for (const v of versions) process.stdout.write(`v${v.version}\t${v.title}\n`);
1332
+ return EXIT_OK;
1333
+ }
1334
+ async function mockupReply(args) {
1335
+ const { values, positionals } = parseArgs({
1336
+ args,
1337
+ options: {
1338
+ body: {
1339
+ type: "string",
1340
+ short: "b"
1341
+ },
1342
+ model: {
1343
+ type: "string",
1344
+ short: "m"
1345
+ }
1346
+ },
1347
+ allowPositionals: true
1348
+ });
1349
+ const commentId = positionals[0];
1350
+ if (!commentId) {
1351
+ console.error("Usage: diffing mockup reply <comment-id> --body \"...\"");
1352
+ return EXIT_USAGE;
1353
+ }
1354
+ let body = values.body;
1355
+ if (!body) body = await readStdin();
1356
+ if (!body?.trim()) {
1357
+ console.error("A reply body is required (--body or stdin).");
1358
+ return EXIT_USAGE;
1359
+ }
1360
+ const listRes = await tryApiFetch(`${baseUrl()}/api/mockups?include=comments`);
1361
+ if (!listRes) return EXIT_NO_SERVER;
1362
+ const mockup = (listRes.ok ? await listRes.json() : []).find((m) => (m.comments ?? []).some((c) => c.id === commentId));
1363
+ if (!mockup) {
1364
+ console.error(`Comment ${commentId} not found.`);
1365
+ return EXIT_NOT_FOUND;
1366
+ }
1367
+ const res = await tryApiFetch(`${baseUrl()}/api/mockups/${mockup.id}/comments/${commentId}/replies`, {
1368
+ method: "POST",
1369
+ headers: { "Content-Type": "application/json" },
1370
+ body: JSON.stringify({
1371
+ body,
1372
+ model: values.model,
1373
+ role: "agent"
1374
+ })
1375
+ });
1376
+ if (!res) return EXIT_NO_SERVER;
1377
+ if (!res.ok) {
1378
+ console.error(`Failed to reply: HTTP ${res.status}`);
1379
+ return 1;
1380
+ }
1381
+ return EXIT_OK;
1382
+ }
1383
+ async function mockupResolve(args) {
1384
+ const { positionals } = parseArgs({
1385
+ args,
1386
+ allowPositionals: true
1387
+ });
1388
+ const commentId = positionals[0];
1389
+ if (!commentId) {
1390
+ console.error("Usage: diffing mockup resolve <comment-id>");
1391
+ return EXIT_USAGE;
1392
+ }
1393
+ const listRes = await tryApiFetch(`${baseUrl()}/api/mockups?include=comments`);
1394
+ if (!listRes) return EXIT_NO_SERVER;
1395
+ const mockup = (listRes.ok ? await listRes.json() : []).find((m) => (m.comments ?? []).some((c) => c.id === commentId));
1396
+ if (!mockup) {
1397
+ console.error(`Comment ${commentId} not found.`);
1398
+ return EXIT_NOT_FOUND;
1399
+ }
1400
+ const res = await tryApiFetch(`${baseUrl()}/api/mockups/${mockup.id}/comments/${commentId}`, {
1401
+ method: "PUT",
1402
+ headers: { "Content-Type": "application/json" },
1403
+ body: JSON.stringify({ status: "resolved" })
1404
+ });
1405
+ if (!res) return EXIT_NO_SERVER;
1406
+ if (!res.ok) {
1407
+ console.error(`Failed to resolve: HTTP ${res.status}`);
1408
+ return 1;
1409
+ }
1410
+ return EXIT_OK;
1411
+ }
1412
+ async function latestMockupId(base) {
1413
+ const listRes = await tryApiFetch(`${base}/api/mockups`);
1414
+ if (!listRes || !listRes.ok) return null;
1415
+ const all = await listRes.json();
1416
+ if (all.length === 0) return null;
1417
+ return all[all.length - 1].id;
1418
+ }
1419
+ async function mockupOpErrorMessage(res) {
1420
+ const body = await res.json().catch(() => ({}));
1421
+ if (body.code === "version-mismatch") return `Mockup version mismatch: current v${body.currentVersion}, expected v${body.expectedVersion}. Retry with --expected-version ${body.currentVersion}.`;
1422
+ return body.error ?? `HTTP ${res.status}`;
1423
+ }
1424
+ async function mockupInspect(args) {
1425
+ const { values, positionals } = parseArgs({
1426
+ args,
1427
+ options: {
1428
+ status: { type: "string" },
1429
+ screen: { type: "string" },
1430
+ viewport: { type: "string" },
1431
+ version: { type: "string" },
1432
+ id: { type: "string" },
1433
+ cursor: { type: "string" },
1434
+ limit: { type: "string" },
1435
+ context: { type: "string" },
1436
+ pretty: { type: "boolean" }
1437
+ },
1438
+ allowPositionals: true
1439
+ });
1440
+ const [view, mockupArg, ...extra] = positionals;
1441
+ if (!view || ![
1442
+ "summary",
1443
+ "comments",
1444
+ "comment",
1445
+ "screen"
1446
+ ].includes(view) || extra.length > 0) {
1447
+ console.error(`Usage: diffing mockup inspect <summary|comments|comment|screen> [mockup-id] [options]
1448
+
1449
+ Read bounded mockup data as compact JSON (add --pretty for indented output).
1450
+ summary [mockup-id] — headline stats + screen list
1451
+ comments [mockup-id] [--status open|resolved] [--screen S] [--viewport desktop|tablet|mobile] [--version N] [--cursor N] [--limit N] [--context none|anchor|source]
1452
+ comment [mockup-id] --id <comment-id> [--context none|anchor|source]
1453
+ screen [mockup-id] [--cursor N] [--limit N]
1454
+
1455
+ context: none = metadata only; anchor = + locator fields (default); source = + contextHtml.`);
1456
+ return EXIT_USAGE;
1457
+ }
1458
+ const base = baseUrl();
1459
+ let mockupId = mockupArg;
1460
+ if (!mockupId) {
1461
+ mockupId = await latestMockupId(base) ?? "";
1462
+ if (!mockupId) {
1463
+ console.error("No mockups submitted yet.");
1464
+ return EXIT_NOT_FOUND;
1465
+ }
1466
+ }
1467
+ const params = new URLSearchParams();
1468
+ params.set("view", view);
1469
+ const numeric = (flag, name) => {
1470
+ const value = values[flag];
1471
+ if (typeof value !== "string") return true;
1472
+ if (!/^\d+$/.test(value)) {
1473
+ console.error(`--${name} must be a non-negative integer`);
1474
+ return false;
1475
+ }
1476
+ params.set(name, value);
1477
+ return true;
1478
+ };
1479
+ if (!numeric("cursor", "cursor")) return EXIT_USAGE;
1480
+ if (!numeric("limit", "limit")) return EXIT_USAGE;
1481
+ if (!numeric("version", "version")) return EXIT_USAGE;
1482
+ const str = (flag, name) => {
1483
+ const value = values[flag];
1484
+ if (typeof value === "string") params.set(name, value);
1485
+ };
1486
+ str("status", "status");
1487
+ str("screen", "screen");
1488
+ str("viewport", "viewport");
1489
+ str("id", "id");
1490
+ str("context", "context");
1491
+ const res = await tryApiFetch(`${base}/api/mockups/${encodeURIComponent(mockupId)}/inspect?${params.toString()}`);
1492
+ if (!res) return EXIT_NO_SERVER;
1493
+ if (res.status === 404) {
1494
+ console.error(`Mockup ${mockupId} not found.`);
1495
+ return EXIT_NOT_FOUND;
1496
+ }
1497
+ if (!res.ok) {
1498
+ const err = await res.json().catch(() => ({}));
1499
+ console.error(err.error ?? `HTTP ${res.status}`);
1500
+ return 1;
1501
+ }
1502
+ const data = await res.json();
1503
+ process.stdout.write(JSON.stringify(data, null, values.pretty ? 2 : void 0) + "\n");
1504
+ return EXIT_OK;
1505
+ }
1506
+ async function mockupScreenCmd(args) {
1507
+ const { values, positionals } = parseArgs({
1508
+ args,
1509
+ options: {
1510
+ file: { type: "string" },
1511
+ text: { type: "string" },
1512
+ replacement: { type: "string" },
1513
+ label: { type: "string" },
1514
+ "expected-version": { type: "string" }
1515
+ },
1516
+ allowPositionals: true
1517
+ });
1518
+ const [action, mockupId, screenId, ...extra] = positionals;
1519
+ if (!action || ![
1520
+ "upsert",
1521
+ "remove",
1522
+ "patch"
1523
+ ].includes(action) || !mockupId || !screenId || extra.length > 0) {
1524
+ console.error(`Usage: diffing mockup screen <upsert|remove|patch> <mockup-id> <screen-id> [options]
1525
+
1526
+ One-screen revision. Version bumps on success; an --expected-version guard aborts with
1527
+ 409 (no change) when the mockup moved on.
1528
+ upsert <id> <screen-id> --file <path> [--label L] [--expected-version N] # --file - = stdin
1529
+ remove <id> <screen-id> [--expected-version N]
1530
+ patch <id> <screen-id> --text <exact-text> --replacement <new-text> [--expected-version N]`);
1531
+ return EXIT_USAGE;
1532
+ }
1533
+ let expectedVersion;
1534
+ if (values["expected-version"] !== void 0) {
1535
+ if (!/^\d+$/.test(values["expected-version"])) {
1536
+ console.error("--expected-version must be a positive integer");
1537
+ return EXIT_USAGE;
1538
+ }
1539
+ expectedVersion = Number(values["expected-version"]);
1540
+ }
1541
+ const base = baseUrl();
1542
+ if (action === "upsert") {
1543
+ let html;
1544
+ const file = values.file;
1545
+ if (!file || file === "-") html = (await readStdin()).replace(/\r\n/g, "\n");
1546
+ else try {
1547
+ html = (await readFile(resolve(file), "utf-8")).replace(/\r\n/g, "\n");
1548
+ } catch (err) {
1549
+ console.error(`Failed to read ${file}: ${err?.message ?? err}`);
1550
+ return EXIT_USAGE;
1551
+ }
1552
+ if (!html.trim()) {
1553
+ console.error("Screen html is required (--file <path> or stdin).");
1554
+ return EXIT_USAGE;
1555
+ }
1556
+ const res = await tryApiFetch(`${base}/api/mockups/${encodeURIComponent(mockupId)}/screens/${encodeURIComponent(screenId)}`, {
1557
+ method: "PUT",
1558
+ headers: { "Content-Type": "application/json" },
1559
+ body: JSON.stringify({
1560
+ html,
1561
+ label: values.label,
1562
+ expectedVersion
1563
+ })
1564
+ });
1565
+ if (!res) return EXIT_NO_SERVER;
1566
+ if (!res.ok) {
1567
+ console.error(await mockupOpErrorMessage(res));
1568
+ return res.status === 404 ? EXIT_NOT_FOUND : 1;
1569
+ }
1570
+ const mockup = await res.json();
1571
+ console.error(`Upserted screen ${screenId} on ${mockupId} → v${mockup.version} (${mockup.screens.length} screens).`);
1572
+ return EXIT_OK;
1573
+ }
1574
+ if (action === "remove") {
1575
+ const qs = expectedVersion !== void 0 ? `?expectedVersion=${expectedVersion}` : "";
1576
+ const res = await tryApiFetch(`${base}/api/mockups/${encodeURIComponent(mockupId)}/screens/${encodeURIComponent(screenId)}${qs}`, { method: "DELETE" });
1577
+ if (!res) return EXIT_NO_SERVER;
1578
+ if (!res.ok) {
1579
+ console.error(await mockupOpErrorMessage(res));
1580
+ return res.status === 404 ? EXIT_NOT_FOUND : 1;
1581
+ }
1582
+ const mockup = await res.json();
1583
+ console.error(`Removed screen ${screenId} from ${mockupId} → v${mockup.version}.`);
1584
+ return EXIT_OK;
1585
+ }
1586
+ const text = values.text;
1587
+ const replacement = values.replacement;
1588
+ if (!text || replacement === void 0) {
1589
+ console.error("patch requires --text <exact-text> and --replacement <new-text>");
1590
+ return EXIT_USAGE;
1591
+ }
1592
+ const res = await tryApiFetch(`${base}/api/mockups/${encodeURIComponent(mockupId)}/screens/${encodeURIComponent(screenId)}`, {
1593
+ method: "PATCH",
1594
+ headers: { "Content-Type": "application/json" },
1595
+ body: JSON.stringify({
1596
+ expectedText: text,
1597
+ replacement,
1598
+ expectedVersion
1599
+ })
1600
+ });
1601
+ if (!res) return EXIT_NO_SERVER;
1602
+ if (!res.ok) {
1603
+ console.error(await mockupOpErrorMessage(res));
1604
+ return res.status === 404 ? EXIT_NOT_FOUND : 1;
1605
+ }
1606
+ const data = await res.json();
1607
+ console.error(`Patched ${screenId} on ${mockupId} (${data.occurrences} exact match(es)) → v${data.mockup.version}.`);
1608
+ return EXIT_OK;
1609
+ }
1610
+ async function mockupThreadsCmd(args) {
1611
+ const { values, positionals } = parseArgs({
1612
+ args,
1613
+ options: {
1614
+ body: {
1615
+ type: "string",
1616
+ short: "b"
1617
+ },
1618
+ model: {
1619
+ type: "string",
1620
+ short: "m"
1621
+ },
1622
+ id: { type: "string" }
1623
+ },
1624
+ allowPositionals: true
1625
+ });
1626
+ const [action, commentId, replyId, ...extra] = positionals;
1627
+ if (!(typeof action === "string" && [
1628
+ "reply",
1629
+ "edit",
1630
+ "delete",
1631
+ "resolve",
1632
+ "unresolve"
1633
+ ].includes(action)) || !commentId || extra.length > 0) {
1634
+ console.error(`Usage: diffing mockup threads <reply|edit|delete|resolve|unresolve> ...
1635
+
1636
+ Atomic thread ops — each call is validated before apply through the batch endpoint.
1637
+ reply <comment-id> --body "..." [--model M] [--id mockup-id]
1638
+ edit <comment-id> [<reply-id>] --body "..." # with reply-id: edit the reply; without: edit the comment body
1639
+ delete <comment-id> [<reply-id>] # with reply-id: delete the reply; without: delete the comment
1640
+ resolve <comment-id>
1641
+ unresolve <comment-id>`);
1642
+ return EXIT_USAGE;
1643
+ }
1644
+ let body = values.body;
1645
+ if (action === "reply" || action === "edit") {
1646
+ if (body === void 0) {
1647
+ if (process.stdin.isTTY) {
1648
+ console.error(`threads ${action}: a body is required (--body <text> or stdin)`);
1649
+ return EXIT_USAGE;
1650
+ }
1651
+ body = (await readStdin()).trim();
1652
+ } else if (body === "-") body = (await readStdin()).trim();
1653
+ if (!body?.trim()) {
1654
+ console.error(`threads ${action}: a body is required (--body <text> or stdin)`);
1655
+ return EXIT_USAGE;
1656
+ }
1657
+ }
1658
+ const base = baseUrl();
1659
+ let mockupId = values.id;
1660
+ if (!mockupId) {
1661
+ const listRes = await tryApiFetch(`${base}/api/mockups?include=comments`);
1662
+ if (!listRes) return EXIT_NO_SERVER;
1663
+ const found = (listRes.ok ? await listRes.json() : []).find((m) => (m.comments ?? []).some((c) => c.id === commentId));
1664
+ if (!found) {
1665
+ console.error(`Comment ${commentId} not found.`);
1666
+ return EXIT_NOT_FOUND;
1667
+ }
1668
+ mockupId = found.id;
1669
+ }
1670
+ const operations = [];
1671
+ if (action === "reply") operations.push({
1672
+ op: action,
1673
+ commentId,
1674
+ body,
1675
+ role: "agent",
1676
+ model: values.model
1677
+ });
1678
+ else if (action === "edit") operations.push(replyId ? {
1679
+ op: action,
1680
+ commentId,
1681
+ replyId,
1682
+ body
1683
+ } : {
1684
+ op: action,
1685
+ commentId,
1686
+ body
1687
+ });
1688
+ else if (action === "delete") operations.push(replyId ? {
1689
+ op: action,
1690
+ commentId,
1691
+ replyId
1692
+ } : {
1693
+ op: action,
1694
+ commentId
1695
+ });
1696
+ else operations.push({
1697
+ op: action,
1698
+ commentId
1699
+ });
1700
+ const res = await tryApiFetch(`${base}/api/mockups/${encodeURIComponent(mockupId)}/threads/batch`, {
1701
+ method: "POST",
1702
+ headers: { "Content-Type": "application/json" },
1703
+ body: JSON.stringify({ operations })
1704
+ });
1705
+ if (!res) return EXIT_NO_SERVER;
1706
+ if (!res.ok) {
1707
+ const err = await res.json().catch(() => ({}));
1708
+ console.error(err.error ?? `HTTP ${res.status}`);
1709
+ return res.status === 404 ? EXIT_NOT_FOUND : 1;
1710
+ }
1711
+ const newReplyId = (await res.json()).results[0]?.replyId;
1712
+ if (action === "reply") console.error(`Replied to ${commentId}${newReplyId ? ` (${newReplyId})` : ""}.`);
1713
+ else if (action === "edit") console.error(replyId ? `Edited reply ${replyId}.` : `Edited comment ${commentId}.`);
1714
+ else if (action === "delete") console.error(replyId ? `Deleted reply ${replyId}.` : `Deleted comment ${commentId}.`);
1715
+ else if (action === "resolve") console.error(`Resolved ${commentId}.`);
1716
+ else console.error(`Re-opened ${commentId}.`);
1717
+ return EXIT_OK;
1718
+ }
1719
+ async function mockupCommand(args) {
1720
+ const action = args[0];
1721
+ const rest = args.slice(1);
1722
+ if (args.includes("--help") || args.includes("-h") || !action) {
1723
+ console.log(`Usage: diffing mockup <submit|await|list|show|versions|reply|resolve|inspect|screen|threads> [options]
1724
+
1725
+ Submit HTML mockups for visual review. Same loop as plan review.
1726
+ Never write mockup HTML into the consumer git tree. Prefer stdin or MCP inline html.
1727
+ Staging files, if needed, go under ~/.diffing/<repo>/mockup-sources/ only.
1728
+
1729
+ submit [-] [--title T] [--screen id=path]... [--id ID] [--model M] [--wait]
1730
+ # or a path already under ~/.diffing/.../mockup-sources/
1731
+ await [--timeout N]
1732
+ list [--json]
1733
+ show [<id>] [--json] [--version N]
1734
+ versions <id> [--json]
1735
+ reply <comment-id> --body "..." [--model M]
1736
+ resolve <comment-id>
1737
+ inspect <summary|comments|comment|screen> [<id>] [--status S] [--screen S] [--viewport V] [--version N] [--id C] [--cursor N] [--limit N] [--context none|anchor|source] [--pretty]
1738
+ screen <upsert|remove|patch> <id> <screen-id> [--file P] [--text T] [--replacement R] [--expected-version N]
1739
+ threads <reply|edit|delete|resolve|unresolve> <comment-id> [<reply-id>] [--body "..."] [--model M] [--id mockup-id]
1740
+ `);
1741
+ return EXIT_OK;
1742
+ }
1743
+ switch (action) {
1744
+ case "submit": return mockupSubmit(rest);
1745
+ case "await": return mockupAwait(rest);
1746
+ case "list": return mockupList(rest);
1747
+ case "show": return mockupShow(rest);
1748
+ case "versions": return mockupVersions(rest);
1749
+ case "reply": return mockupReply(rest);
1750
+ case "resolve": return mockupResolve(rest);
1751
+ case "inspect": return mockupInspect(rest);
1752
+ case "screen": return mockupScreenCmd(rest);
1753
+ case "threads": return mockupThreadsCmd(rest);
1754
+ default:
1755
+ console.error(`Unknown mockup action: ${action}`);
1756
+ return EXIT_USAGE;
1757
+ }
1758
+ }
1020
1759
  async function runSubcommand(name, args) {
1021
1760
  switch (name) {
1022
1761
  case "await-review": return awaitReview(args);
@@ -1027,21 +1766,25 @@ async function runSubcommand(name, args) {
1027
1766
  case "comments": return comments(args);
1028
1767
  case "url": return url();
1029
1768
  case "plan": return plan(args);
1030
- case "update":
1769
+ case "mockup": return mockupCommand(args);
1770
+ case "update": {
1031
1771
  const { runUpdateCommand } = await import("./update-check-ISaw61qS.mjs");
1032
1772
  return runUpdateCommand();
1033
- case "gh":
1034
- const { runGhSubcommand } = await import("./cli-gh-BvoDabKK.mjs");
1773
+ }
1774
+ case "gh": {
1775
+ const { runGhSubcommand } = await import("./cli-gh-2NEumKQB.mjs");
1035
1776
  return runGhSubcommand(args);
1777
+ }
1036
1778
  case "doctor": return doctor();
1037
1779
  case "setup": return setup(args);
1038
1780
  case "completion": return completion(args);
1039
1781
  case "progress": return progress(args);
1040
1782
  case "inspect": return inspect(args);
1041
1783
  case "mode": return mode(args);
1042
- case "sessions":
1784
+ case "sessions": {
1043
1785
  const { runSessionsCommand } = await import("./session-manager-CUqF8hkP.mjs");
1044
1786
  return runSessionsCommand(args);
1787
+ }
1045
1788
  default:
1046
1789
  console.error(`Unknown subcommand: ${name}`);
1047
1790
  return EXIT_USAGE;