diffing 0.17.0 → 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-qNwkSjxK.mjs → cli-agent-du9TS34N.mjs} +741 -13
  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-DhynPewa.mjs → mcp-H7ytYScV.mjs} +397 -6
  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-CEbp7K-k.mjs → server-CiE24vh-.mjs} +1567 -15
  153. package/dist/{setup-BnS_rSxK.mjs → setup-BwWZ9Fzq.mjs} +1 -1
  154. package/extensions/pi/index.ts +452 -5
  155. package/package.json +1 -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
@@ -48,6 +48,17 @@ function baseUrl() {
48
48
  }
49
49
  /** Attach session credentials while preserving ordinary web calls. */
50
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}`);
51
62
  const headers = new Headers(init.headers);
52
63
  if (activeCapability) headers.set("X-Diffing-Capability", activeCapability);
53
64
  if (activeAuthToken) headers.set(SESSION_TOKEN_HEADER, activeAuthToken);
@@ -379,7 +390,7 @@ async function comments(args) {
379
390
  }
380
391
  if (values.json || format === "json") process.stdout.write(JSON.stringify(selected, null, 2) + "\n");
381
392
  else if (format === "markdown" || format === "md") {
382
- const { formatCommentsMarkdown } = await import("./review-export-GW4KNsGN.mjs");
393
+ const { formatCommentsMarkdown } = await import("./review-export-DoST2oPt.mjs");
383
394
  process.stdout.write(formatCommentsMarkdown(selected) + "\n");
384
395
  } else process.stdout.write(formatComments(selected) + "\n");
385
396
  return EXIT_OK;
@@ -645,7 +656,7 @@ async function planVersions(args) {
645
656
  allowPositionals: true
646
657
  });
647
658
  const base = baseUrl();
648
- let planId = positionals[0];
659
+ const planId = positionals[0];
649
660
  if (!planId) {
650
661
  console.error("Usage: diffing plan versions <id> [--json]");
651
662
  return EXIT_USAGE;
@@ -833,7 +844,7 @@ See docs/getting-started.md for the full walkthrough.`);
833
844
  console.error(`Unknown setup action: ${action}`);
834
845
  return EXIT_USAGE;
835
846
  }
836
- const { runSetup } = await import("./setup-BnS_rSxK.mjs");
847
+ const { runSetup } = await import("./setup-BwWZ9Fzq.mjs");
837
848
  return runSetup({
838
849
  yes,
839
850
  check,
@@ -869,7 +880,7 @@ async function completion(args) {
869
880
  console.error("Usage: diffing completion <bash|zsh|fish>");
870
881
  return EXIT_USAGE;
871
882
  }
872
- const { completionFor } = await import("./completions-5rtfoxB9.mjs");
883
+ const { completionFor } = await import("./completions-CYa5usLb.mjs");
873
884
  const script = completionFor(shell);
874
885
  if (!script) {
875
886
  console.error(`Unknown shell: ${shell}. Use bash, zsh, or fish.`);
@@ -1032,6 +1043,719 @@ async function progress(args) {
1032
1043
  }
1033
1044
  return EXIT_OK;
1034
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
+ }
1035
1759
  async function runSubcommand(name, args) {
1036
1760
  switch (name) {
1037
1761
  case "await-review": return awaitReview(args);
@@ -1042,21 +1766,25 @@ async function runSubcommand(name, args) {
1042
1766
  case "comments": return comments(args);
1043
1767
  case "url": return url();
1044
1768
  case "plan": return plan(args);
1045
- case "update":
1769
+ case "mockup": return mockupCommand(args);
1770
+ case "update": {
1046
1771
  const { runUpdateCommand } = await import("./update-check-ISaw61qS.mjs");
1047
1772
  return runUpdateCommand();
1048
- case "gh":
1049
- const { runGhSubcommand } = await import("./cli-gh-BvoDabKK.mjs");
1773
+ }
1774
+ case "gh": {
1775
+ const { runGhSubcommand } = await import("./cli-gh-2NEumKQB.mjs");
1050
1776
  return runGhSubcommand(args);
1777
+ }
1051
1778
  case "doctor": return doctor();
1052
1779
  case "setup": return setup(args);
1053
1780
  case "completion": return completion(args);
1054
1781
  case "progress": return progress(args);
1055
1782
  case "inspect": return inspect(args);
1056
1783
  case "mode": return mode(args);
1057
- case "sessions":
1784
+ case "sessions": {
1058
1785
  const { runSessionsCommand } = await import("./session-manager-CUqF8hkP.mjs");
1059
1786
  return runSessionsCommand(args);
1787
+ }
1060
1788
  default:
1061
1789
  console.error(`Unknown subcommand: ${name}`);
1062
1790
  return EXIT_USAGE;