diffing 0.17.0 → 0.18.1

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-Cf6SAnH4.mjs} +744 -15
  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-BV3gPi7L.js} +1 -1
  5. package/dist/client/assets/{angular-ts-CiwZGajG.js → angular-ts-DuuSfKJK.js} +1 -1
  6. package/dist/client/assets/{apl-C8k5a5fk.js → apl-BMc69egk.js} +1 -1
  7. package/dist/client/assets/{arc-B8VMoqVr.js → arc-CEC1cz1S.js} +1 -1
  8. package/dist/client/assets/{architectureDiagram-3BPJPVTR-C-ng2xNL.js → architectureDiagram-3BPJPVTR-BmJ5OHgd.js} +1 -1
  9. package/dist/client/assets/{astro-BE-h-qpp.js → astro-Bg_CYwmB.js} +1 -1
  10. package/dist/client/assets/{blade-D-0BV1wW.js → blade-Mca0XoRC.js} +1 -1
  11. package/dist/client/assets/{blockDiagram-GPEHLZMM-DyAQuONU.js → blockDiagram-GPEHLZMM--XrThRLW.js} +1 -1
  12. package/dist/client/assets/{c-Dg_Gx29H.js → c-B2Uqi6oI.js} +1 -1
  13. package/dist/client/assets/{c4Diagram-AAUBKEIU-k5uKTHvm.js → c4Diagram-AAUBKEIU-W8w5oiRi.js} +1 -1
  14. package/dist/client/assets/channel-C9URWRe4.js +1 -0
  15. package/dist/client/assets/{chunk-2J33WTMH-Dod_VwYj.js → chunk-2J33WTMH-Bpr3MqKf.js} +1 -1
  16. package/dist/client/assets/{chunk-3OPIFGDE-B8HMjry-.js → chunk-3OPIFGDE-DdMV_3DM.js} +1 -1
  17. package/dist/client/assets/{chunk-4BX2VUAB-TvkJVIZw.js → chunk-4BX2VUAB-BkfeLLsz.js} +1 -1
  18. package/dist/client/assets/{chunk-55IACEB6-BsXuKAKc.js → chunk-55IACEB6-pCwXbxR0.js} +1 -1
  19. package/dist/client/assets/{chunk-5ZQYHXKU-BI1cj2cZ.js → chunk-5ZQYHXKU-BGFzCgvs.js} +1 -1
  20. package/dist/client/assets/{chunk-727SXJPM-yWQ27Wk0.js → chunk-727SXJPM-DU7oVWfJ.js} +1 -1
  21. package/dist/client/assets/{chunk-AQP2D5EJ-B2BBCtTr.js → chunk-AQP2D5EJ-DaVYG1qm.js} +1 -1
  22. package/dist/client/assets/{chunk-BSJP7CBP-CXXvPXhK.js → chunk-BSJP7CBP-DAvnzgjY.js} +1 -1
  23. package/dist/client/assets/{chunk-CSCIHK7Q-jYiloqyi.js → chunk-CSCIHK7Q-D6ZDvMsi.js} +1 -1
  24. package/dist/client/assets/{chunk-FMBD7UC4-DZzqIT32.js → chunk-FMBD7UC4-BFDCYCqF.js} +1 -1
  25. package/dist/client/assets/{chunk-KSCS5N6A-krdCf8iG.js → chunk-KSCS5N6A-BmD6XY52.js} +1 -1
  26. package/dist/client/assets/{chunk-L5ZTLDWV-0uuTmeZV.js → chunk-L5ZTLDWV-Dl9iUIBf.js} +1 -1
  27. package/dist/client/assets/{chunk-LZXEDZCA-BBfUgD06.js → chunk-LZXEDZCA-rOOJDxdB.js} +2 -2
  28. package/dist/client/assets/{chunk-ND2GUHAM-jkir4ZKE.js → chunk-ND2GUHAM-DBDu-JeD.js} +1 -1
  29. package/dist/client/assets/{chunk-NZK2D7GU-DMWZNibJ.js → chunk-NZK2D7GU-CRyk4F3R.js} +1 -1
  30. package/dist/client/assets/{chunk-O5CBEL6O-B1e1HGaX.js → chunk-O5CBEL6O-DcKB07iP.js} +1 -1
  31. package/dist/client/assets/chunk-QZHKN3VN-BBNO2-yv.js +1 -0
  32. package/dist/client/assets/chunk-WU5MYG2G-HP2Om5f9.js +1 -0
  33. package/dist/client/assets/{chunk-XPW4576I-AvbCAUav.js → chunk-XPW4576I-WuzWMSrp.js} +1 -1
  34. package/dist/client/assets/classDiagram-4FO5ZUOK-BW_ZPHe7.js +1 -0
  35. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DOd628kk.js +1 -0
  36. package/dist/client/assets/{cobol-DQnGB_GV.js → cobol-oQ1i-YtO.js} +1 -1
  37. package/dist/client/assets/{coffee-CDV5a89x.js → coffee-s9XlmHbB.js} +1 -1
  38. package/dist/client/assets/{cose-bilkent-S5V4N54A-t-mDNRF2.js → cose-bilkent-S5V4N54A-CymUm7fn.js} +1 -1
  39. package/dist/client/assets/{cpp-8G6b-siI.js → cpp-D60LxTox.js} +1 -1
  40. package/dist/client/assets/{crystal-CGYCC6FM.js → crystal-CHrCczDb.js} +1 -1
  41. package/dist/client/assets/{css-nez4R8_Z.js → css-1PQO32Hw.js} +1 -1
  42. package/dist/client/assets/{dagre-BM42HDAG--azesYIg.js → dagre-BM42HDAG-DOHZc0tO.js} +1 -1
  43. package/dist/client/assets/{diagram-2AECGRRQ-4WJ7nKhA.js → diagram-2AECGRRQ-D8O2cleS.js} +1 -1
  44. package/dist/client/assets/{diagram-5GNKFQAL-C7ZGhLXn.js → diagram-5GNKFQAL-CO-Ej4UD.js} +1 -1
  45. package/dist/client/assets/{diagram-KO2AKTUF-BobK_rbl.js → diagram-KO2AKTUF-DCUr4wzS.js} +1 -1
  46. package/dist/client/assets/{diagram-LMA3HP47-BOc-OkIS.js → diagram-LMA3HP47-D9HnA0Qg.js} +1 -1
  47. package/dist/client/assets/{diagram-OG6HWLK6-BEtLgm86.js → diagram-OG6HWLK6-BxAitO2n.js} +1 -1
  48. package/dist/client/assets/{dist-DPuBwToc.js → dist-N-ROBnkL.js} +1 -1
  49. package/dist/client/assets/{edge-Z5RMbJQv.js → edge-Ck54Fn7y.js} +1 -1
  50. package/dist/client/assets/{edit-CFxbgVPV.js → edit-B8RwtrDs.js} +1 -1
  51. package/dist/client/assets/{elixir-d3C79FQc.js → elixir-xXxdfsfU.js} +1 -1
  52. package/dist/client/assets/{elm-CDAf6Uyn.js → elm-g0vLRWbF.js} +1 -1
  53. package/dist/client/assets/{erDiagram-TEJ5UH35-D-pR2GY9.js → erDiagram-TEJ5UH35-1ZMg7g98.js} +1 -1
  54. package/dist/client/assets/{erb-CTJk9FKH.js → erb-CGFnxOST.js} +1 -1
  55. package/dist/client/assets/{flowDiagram-I6XJVG4X-BwMCy2QV.js → flowDiagram-I6XJVG4X-D5u9lhJ-.js} +1 -1
  56. package/dist/client/assets/{ganttDiagram-6RSMTGT7-BI1WLJDi.js → ganttDiagram-6RSMTGT7-BbVKEzs8.js} +1 -1
  57. package/dist/client/assets/{git-rebase-Dr0-MVoW.js → git-rebase-B3s6bE5_.js} +1 -1
  58. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CUfW0k1f.js → gitGraphDiagram-PVQCEYII-sAottiiz.js} +1 -1
  59. package/dist/client/assets/{glimmer-js-CXhLhyW-.js → glimmer-js-C2Xff18_.js} +1 -1
  60. package/dist/client/assets/{glimmer-ts-BFAFT-VD.js → glimmer-ts-CRBS8ftd.js} +1 -1
  61. package/dist/client/assets/{glsl-D7yanhgY.js → glsl-aOY_t6dG.js} +1 -1
  62. package/dist/client/assets/{graphql-CpoDxrok.js → graphql-Cucpdpc6.js} +1 -1
  63. package/dist/client/assets/{hack-BL5fmC4q.js → hack-Bw0KkVza.js} +1 -1
  64. package/dist/client/assets/{haml-ClAE6RVM.js → haml-DnyaPoa2.js} +1 -1
  65. package/dist/client/assets/{handlebars-nHw91uOv.js → handlebars-AedEof-m.js} +1 -1
  66. package/dist/client/assets/{html-BIiOKqgG.js → html-Bu5h_Kgf.js} +1 -1
  67. package/dist/client/assets/{html-derivative-D1nPnuOK.js → html-derivative-CNiVLld1.js} +1 -1
  68. package/dist/client/assets/{http-B_QcRZjt.js → http-B9-oOYPw.js} +1 -1
  69. package/dist/client/assets/{hurl-CZE0DsD3.js → hurl-Bo6NUnnd.js} +1 -1
  70. package/dist/client/assets/{index-Erqhm7rW.js → index-BkwV-g6J.js} +91 -91
  71. package/dist/client/assets/index-C73yr1tm.css +1 -0
  72. package/dist/client/assets/{infoDiagram-5YYISTIA-w2M_OHua.js → infoDiagram-5YYISTIA-CfVELF4R.js} +1 -1
  73. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CW_l9PGx.js → ishikawaDiagram-YF4QCWOH-DztjIGfb.js} +1 -1
  74. package/dist/client/assets/{java-CXTqX09w.js → java-JJ3dl_jz.js} +1 -1
  75. package/dist/client/assets/{javascript-9z9ABBep.js → javascript-QgUuQNTw.js} +1 -1
  76. package/dist/client/assets/{jinja-Cluf_5Bk.js → jinja-GbeEm2-u.js} +1 -1
  77. package/dist/client/assets/{jison-CqCEUtc4.js → jison-BXZbHsO3.js} +1 -1
  78. package/dist/client/assets/{journeyDiagram-JHISSGLW-D-YwusBa.js → journeyDiagram-JHISSGLW-Lg8CkxeI.js} +1 -1
  79. package/dist/client/assets/{json-BMq_bPP8.js → json-D02YvQSa.js} +1 -1
  80. package/dist/client/assets/{jsx-Djij59b9.js → jsx-eZde6NZS.js} +1 -1
  81. package/dist/client/assets/{julia-DybzCMnh.js → julia-CfcuUD-A.js} +1 -1
  82. package/dist/client/assets/{just-4ChcAr9-.js → just-hQr8WcQp.js} +1 -1
  83. package/dist/client/assets/{kanban-definition-UN3LZRKU-Brvoukmm.js → kanban-definition-UN3LZRKU-BWIeZIf6.js} +1 -1
  84. package/dist/client/assets/{latex-CS2TtFXG.js → latex-BxffZwv_.js} +1 -1
  85. package/dist/client/assets/{line-CqxM-stA.js → line-Cy1AFxf0.js} +1 -1
  86. package/dist/client/assets/{linear-C7T82059.js → linear-Kk5IbGHr.js} +1 -1
  87. package/dist/client/assets/{liquid-84AtYenm.js → liquid-BvKnpUyR.js} +1 -1
  88. package/dist/client/assets/{lua-DJzFx3Cf.js → lua-Bcds2vY9.js} +1 -1
  89. package/dist/client/assets/{marko-BLw9BV9E.js → marko-CZJQLjYc.js} +1 -1
  90. package/dist/client/assets/{mdc-BjZejN3b.js → mdc-BRigtixg.js} +1 -1
  91. package/dist/client/assets/{mermaid-parser.core-BdiEBAsd.js → mermaid-parser.core-Dh2uTVxt.js} +1 -1
  92. package/dist/client/assets/{mermaid.core-BsnKhnrM.js → mermaid.core-CXIGVQ74.js} +3 -3
  93. package/dist/client/assets/{mindmap-definition-RKZ34NQL-CBaIFnbc.js → mindmap-definition-RKZ34NQL-DQNQJuAB.js} +1 -1
  94. package/dist/client/assets/{nginx-DuamCwvX.js → nginx-BI3-LI9U.js} +1 -1
  95. package/dist/client/assets/{nim-Bt8c9-ge.js → nim-PEd8ZOME.js} +1 -1
  96. package/dist/client/assets/{perl-D52AfmwC.js → perl-BPTGdP4p.js} +1 -1
  97. package/dist/client/assets/{php-BBMlJg0_.js → php-C8BKL0yL.js} +1 -1
  98. package/dist/client/assets/{pieDiagram-4H26LBE5-BlmPajcA.js → pieDiagram-4H26LBE5-Dz7gE0t5.js} +1 -1
  99. package/dist/client/assets/{pug-DFHmDqEE.js → pug-H5z9rtbz.js} +1 -1
  100. package/dist/client/assets/{qml-C0333o-g.js → qml-EzSpo2aM.js} +1 -1
  101. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Bqlm_zZk.js → quadrantDiagram-W4KKPZXB-pTnmYdEJ.js} +1 -1
  102. package/dist/client/assets/{r-DZFmvIgR.js → r-VMeUwMIt.js} +1 -1
  103. package/dist/client/assets/{razor-BIGLkWzR.js → razor-Ne05zcaj.js} +1 -1
  104. package/dist/client/assets/{regexp-BW6jpgf9.js → regexp-i5_jUZAj.js} +1 -1
  105. package/dist/client/assets/{requirementDiagram-4Y6WPE33-B1PrtUeD.js → requirementDiagram-4Y6WPE33-GVw3NOxE.js} +1 -1
  106. package/dist/client/assets/{review-export-BlgD3bdw.js → review-export-B4TOUDUE.js} +1 -1
  107. package/dist/client/assets/{rst-Cj51ZJaa.js → rst-BZWuei_6.js} +1 -1
  108. package/dist/client/assets/{ruby-lMhGjwpf.js → ruby-CApICFTD.js} +1 -1
  109. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-D2HtUNVF.js → sankeyDiagram-5OEKKPKP-CwWzwmqj.js} +1 -1
  110. package/dist/client/assets/{sas-BWSQku7l.js → sas-BzEPX6GL.js} +1 -1
  111. package/dist/client/assets/{scss-CdRPuhLN.js → scss-B6Ld2NRm.js} +1 -1
  112. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-D9SQy0XI.js → sequenceDiagram-3UESZ5HK-Bz_Q76Kt.js} +1 -1
  113. package/dist/client/assets/{shellscript-DOfOBXcj.js → shellscript-KmoqGVVu.js} +1 -1
  114. package/dist/client/assets/{shellsession-7XAOCVni.js → shellsession-CsSh_KTo.js} +1 -1
  115. package/dist/client/assets/{soy-DDnzVRIw.js → soy-CLgOu1If.js} +1 -1
  116. package/dist/client/assets/{sql-Ba34agKe.js → sql-BdCh56kU.js} +1 -1
  117. package/dist/client/assets/{src-BO-qT_v_.js → src-D7QQkGEc.js} +1 -1
  118. package/dist/client/assets/{stata-BQlYGHQU.js → stata-C3fo8EYS.js} +1 -1
  119. package/dist/client/assets/{stateDiagram-AJRCARHV-7IEYjFRo.js → stateDiagram-AJRCARHV-D0pZtzLu.js} +1 -1
  120. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-P0vTqsyH.js +1 -0
  121. package/dist/client/assets/{surrealql-BET1l7SW.js → surrealql-qzihsJw_.js} +1 -1
  122. package/dist/client/assets/{svelte-Bd7ZOeQI.js → svelte-B4tbn_KI.js} +1 -1
  123. package/dist/client/assets/{templ-BYvRQdOw.js → templ-Ddev3BvY.js} +1 -1
  124. package/dist/client/assets/{tex-CT8DpKxX.js → tex-DJLtgNQ6.js} +1 -1
  125. package/dist/client/assets/{timeline-definition-PNZ67QCA-CurQYWmi.js → timeline-definition-PNZ67QCA-CWTWgYi0.js} +1 -1
  126. package/dist/client/assets/{ts-tags-D4iPPwau.js → ts-tags-qqxigRtr.js} +1 -1
  127. package/dist/client/assets/{tsx-Dgk_Aw-B.js → tsx-Pkf9BaPv.js} +1 -1
  128. package/dist/client/assets/{twig-abRNWeh3.js → twig-BiScb10p.js} +1 -1
  129. package/dist/client/assets/{typescript-BgR1JqdC.js → typescript-Bhy3UXig.js} +1 -1
  130. package/dist/client/assets/{vennDiagram-CIIHVFJN-DbrRBL5S.js → vennDiagram-CIIHVFJN-DwB89v9I.js} +1 -1
  131. package/dist/client/assets/{vue-DSkhKq4Q.js → vue-CANMWBRv.js} +1 -1
  132. package/dist/client/assets/{vue-html-J0jWoD9X.js → vue-html-DtS6f62r.js} +1 -1
  133. package/dist/client/assets/{vue-vine-BgX5MC8-.js → vue-vine-BpQ9kwSB.js} +1 -1
  134. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DIFXQsoP.js → wardleyDiagram-YWT4CUSO-DNwwPk4s.js} +1 -1
  135. package/dist/client/assets/{xml-Ca53xGLB.js → xml-CD5eIXsm.js} +1 -1
  136. package/dist/client/assets/{xsl-CNnXclYV.js → xsl-DGNZQAUu.js} +1 -1
  137. package/dist/client/assets/{xychartDiagram-2RQKCTM6-CmfInPqa.js → xychartDiagram-2RQKCTM6-L7M4YSvw.js} +1 -1
  138. package/dist/client/assets/{yaml-CjjyUhfg.js → yaml-Cm8mwIB7.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-DoNBucix.mjs} +403 -6
  143. package/dist/mockup-format-D4Ln0WxJ.mjs +327 -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-BRQdb64n.mjs} +1727 -49
  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,6 +1,6 @@
1
- import { i as buildGitDiffArgs, s as parseDiffOptions, t as startServer } from "./server-CEbp7K-k.mjs";
2
- import { t as formatComments } from "./comment-format-B85NHZnG.mjs";
3
- import { n as formatPlanReview } from "./plan-format-BwJh5JSE.mjs";
1
+ import { i as buildGitDiffArgs, s as parseDiffOptions, t as startServer } from "./server-BRQdb64n.mjs";
2
+ import { r as formatPlanReview, t as formatMockupReview } from "./mockup-format-D4Ln0WxJ.mjs";
3
+ import { t as formatComments } from "./comment-format-D4mCqr7T.mjs";
4
4
  import { _ as generateSessionToken, c as removeServerLockIfOwned, i as isLockAlive, p as writeServerLock, r as diffScopeKey, s as removeServerLock, t as acquireServerStartupLease, u as resolveActiveServerLock, x as SESSION_TOKEN_HEADER, y as isLoopbackHost } from "./server-lock-CdaUNZyX.mjs";
5
5
  import { i as reviewSessionUrl, n as joinSessionApiUrl, r as reviewSessionBaseUrl, t as appendSessionToken } from "./session-url-B9FOnF3F.mjs";
6
6
  import { n as AWAIT_REVIEW_TIMEOUT_NEXT_ACTION, r as AWAIT_TOOL_DESCRIPTION_SUFFIX, s as PLAN_SUBMIT_NEXT_ACTION, t as AWAIT_PLAN_TIMEOUT_NEXT_ACTION } from "./handoff-DcShCxWJ.mjs";
@@ -16,7 +16,12 @@ import { z } from "zod";
16
16
  const moduleDirectory = dirname(fileURLToPath(import.meta.url));
17
17
  function readPackageVersion() {
18
18
  const packagePath = resolve(moduleDirectory, "..", "package.json");
19
- const pkg = JSON.parse(readFileSync(packagePath, "utf-8"));
19
+ let pkg;
20
+ try {
21
+ pkg = JSON.parse(readFileSync(packagePath, "utf-8"));
22
+ } catch {
23
+ throw new Error(`Invalid package.json at ${packagePath}`);
24
+ }
20
25
  if (typeof pkg.version !== "string" || !pkg.version) throw new Error(`Invalid package version in ${packagePath}`);
21
26
  return pkg.version;
22
27
  }
@@ -340,13 +345,14 @@ function createMcpServer(options) {
340
345
  const ownerId = options.ownerId ?? randomUUID();
341
346
  const defaultClientDir = existsSync(resolve(moduleDirectory, "client")) ? resolve(moduleDirectory, "client") : resolve(repoRoot, "dist/client");
342
347
  const clientDir = options.clientDir ?? defaultClientDir;
343
- const instructions = `diffing is bound to ${repoRoot} for this connection. First call review_session_status. If no web session is running, call start_review_session; it is safe to retry and never replaces a user session. For code review, prefer diff_summary then paged diff_files/diff_hunks/diff_slice over get_diff; create_comment as needed. Handoff: default is async — share the UI URL and end your turn; use await_review only when the human is reviewing now or asked you to wait. For a GitHub PR session, call gh_overview and bounded diff tools; use gh_list_threads/reviews for discussion, and mutate GitHub only with explicit user authorization. For plan review, call submit_plan, share the plan URL, and park unless asked to wait; then await_plan_review when sync waiting. A changes-requested verdict means revise and resubmit the same planId; rejected means stop; approved means proceed. Wait tools return released or timeout with disposition/nextAction; timeout means park (do not silent-loop). MCP connects only to the loopback diffing server; explicitly authorized GitHub tools may make the server call GitHub.`;
348
+ const instructions = `diffing is bound to ${repoRoot} for this connection. First call review_session_status. If no web session is running, call start_review_session; it is safe to retry and never replaces a user session. For code review, prefer diff_summary then paged diff_files/diff_hunks/diff_slice over get_diff; create_comment as needed. Handoff: default is async — share the UI URL and end your turn; use await_review only when the human is reviewing now or asked you to wait. For a GitHub PR session, call gh_overview and bounded diff tools; use gh_list_threads/reviews for discussion, and mutate GitHub only with explicit user authorization. For plan review, call submit_plan, share the plan URL, and park unless asked to wait; then await_plan_review when sync waiting. For HTML mockup review, call submit_mockup, share the mockup URL, and park unless asked to wait; then await_mockup_review when sync waiting. A changes-requested verdict means revise and resubmit the same planId; rejected means stop; approved means proceed. Wait tools return released or timeout with disposition/nextAction; timeout means park (do not silent-loop). MCP connects only to the loopback diffing server; explicitly authorized GitHub tools may make the server call GitHub.`;
344
349
  const server = new McpServer({
345
350
  name: "diffing",
346
351
  version: MCP_VERSION
347
352
  }, { instructions });
348
353
  let reviewCursor = null;
349
354
  let planCursor = null;
355
+ let mockupCursor = null;
350
356
  let selectedLock = null;
351
357
  function liveLock() {
352
358
  if (selectedLock && lockIsAlive(selectedLock, repoRoot)) return selectedLock;
@@ -416,6 +422,15 @@ function createMcpServer(options) {
416
422
  };
417
423
  return planCursor.round;
418
424
  }
425
+ async function seedMockupCursor(session, signal, force = false) {
426
+ if (!force && mockupCursor?.identity === session.identity) return mockupCursor.round;
427
+ const status = await requestSessionJson(session, "/api/mockup-review/status", { signal });
428
+ mockupCursor = {
429
+ identity: session.identity,
430
+ round: force ? status.round ?? 0 : Math.max(0, (status.round ?? 0) - 1)
431
+ };
432
+ return mockupCursor.round;
433
+ }
419
434
  function sessionStatus() {
420
435
  const lock = liveLock();
421
436
  if (!lock) return {
@@ -622,7 +637,7 @@ function createMcpServer(options) {
622
637
  inputSchema: { exclude: z.array(z.enum(["lockfiles"])).optional() },
623
638
  outputSchema: { result: z.unknown() },
624
639
  annotations: READ_ONLY
625
- }, async ({ exclude } = {}) => {
640
+ }, async ({ exclude } = { exclude: void 0 }) => {
626
641
  const session = requireInspectSession();
627
642
  const query = new URLSearchParams();
628
643
  if (exclude?.length) query.set("exclude", exclude.join(","));
@@ -1492,6 +1507,381 @@ function createMcpServer(options) {
1492
1507
  planId: plan.id
1493
1508
  });
1494
1509
  });
1510
+ const MOCKUP_SUBMIT_NEXT_ACTION = "Share the mockup URL and park unless the human is reviewing now or asked you to wait. Then call await_mockup_review.";
1511
+ server.registerTool("submit_mockup", {
1512
+ title: "Submit HTML mockup for review",
1513
+ description: "Submit HTML mockup screen(s) for visual review. HARD RULE — one state per screen: every distinct state, variant, or case MUST be its own screens[] entry (stable id + label). NEVER encode state as in-page tabs, accordions, toggle switches, modals, dropdowns, or any JS content-swapping — each state is a separate screen. Pass html or screens[].html inline — do NOT write mockup files into the consumer git tree. Share the URL and park unless asked to wait. Resubmit with mockupId to bump version.",
1514
+ inputSchema: {
1515
+ title: z.string().optional(),
1516
+ html: z.string().optional(),
1517
+ screens: z.array(z.object({
1518
+ id: z.string().optional(),
1519
+ label: z.string().optional(),
1520
+ html: z.string()
1521
+ })).optional(),
1522
+ mockupId: z.string().optional(),
1523
+ source: z.string().optional(),
1524
+ model: z.string().optional()
1525
+ },
1526
+ outputSchema: {
1527
+ mockupId: z.string(),
1528
+ version: z.number(),
1529
+ url: z.string(),
1530
+ nextAction: z.string()
1531
+ },
1532
+ annotations: {
1533
+ readOnlyHint: false,
1534
+ idempotentHint: false
1535
+ }
1536
+ }, async ({ title, html, screens, mockupId, source, model }) => {
1537
+ const session = requireWebSession();
1538
+ await seedMockupCursor(session, void 0, true);
1539
+ const mockup = await requestSessionJson(session, "/api/mockups", {
1540
+ method: "POST",
1541
+ headers: { "Content-Type": "application/json" },
1542
+ body: JSON.stringify({
1543
+ id: mockupId,
1544
+ title,
1545
+ html,
1546
+ screens,
1547
+ source,
1548
+ model
1549
+ })
1550
+ });
1551
+ const url = appendSessionToken(`${session.apiOrigin}/mockup/${mockup.id}`, session.lock.authToken);
1552
+ const hints = mockup.hints ?? [];
1553
+ const hintText = hints.length > 0 ? `\n⚠️ In-page state UI detected — split each state into its own screen:\n${hints.map((h) => ` • ${h.screenId}: ${h.patterns.join(", ")}`).join("\n")}` : "";
1554
+ return textResult(`Submitted mockup ${mockup.id} (v${mockup.version}) at ${url}.${hintText} ${MOCKUP_SUBMIT_NEXT_ACTION}`, {
1555
+ mockupId: mockup.id,
1556
+ version: mockup.version,
1557
+ url,
1558
+ nextAction: MOCKUP_SUBMIT_NEXT_ACTION,
1559
+ ...hints.length > 0 ? { hints } : {}
1560
+ });
1561
+ });
1562
+ server.registerTool("await_mockup_review", {
1563
+ title: "Wait for mockup review verdict",
1564
+ description: "Sync wait for a mockup verdict. Prefer async handoff after submit_mockup unless asked to wait.",
1565
+ inputSchema: { timeoutSeconds: z.number().optional() },
1566
+ outputSchema: {
1567
+ status: z.enum(["released", "timeout"]),
1568
+ disposition: z.enum(["continue", "park"]).optional(),
1569
+ decision: z.string().optional(),
1570
+ reviewXml: z.string().optional(),
1571
+ mockup: z.unknown().optional(),
1572
+ round: z.number().optional()
1573
+ },
1574
+ annotations: {
1575
+ readOnlyHint: true,
1576
+ idempotentHint: false
1577
+ }
1578
+ }, async ({ timeoutSeconds }, extra) => {
1579
+ const session = requireWebSession();
1580
+ const deadline = Date.now() + Math.max(1, timeoutSeconds ?? 25) * 1e3;
1581
+ let sinceRound = await seedMockupCursor(session, extra?.signal);
1582
+ while (Date.now() < deadline) {
1583
+ const remaining = Math.max(deadline - Date.now(), 0);
1584
+ const result = await requestSessionJson(session, `/api/mockup-review/await?timeoutMs=${Math.min(25e3, remaining)}&sinceRound=${sinceRound}`, { signal: extra?.signal });
1585
+ if (result.status === "released" && result.payload) {
1586
+ mockupCursor = {
1587
+ identity: session.identity,
1588
+ round: result.payload.round ?? sinceRound
1589
+ };
1590
+ return textResult(result.payload.reviewXml ?? `decision=${result.payload.decision}`, {
1591
+ status: "released",
1592
+ disposition: "continue",
1593
+ decision: result.payload.decision,
1594
+ reviewXml: result.payload.reviewXml,
1595
+ mockup: result.payload.mockup,
1596
+ round: result.payload.round
1597
+ });
1598
+ }
1599
+ sinceRound = result.round ?? sinceRound;
1600
+ mockupCursor = {
1601
+ identity: session.identity,
1602
+ round: sinceRound
1603
+ };
1604
+ }
1605
+ return textResult("Timeout waiting for mockup review. Park and retry await_mockup_review when the human is ready.", {
1606
+ status: "timeout",
1607
+ disposition: "park",
1608
+ round: sinceRound
1609
+ });
1610
+ });
1611
+ server.registerTool("list_mockups", {
1612
+ title: "List mockups",
1613
+ description: "List submitted HTML mockups for this repository.",
1614
+ inputSchema: {},
1615
+ outputSchema: { mockups: z.array(z.unknown()) },
1616
+ annotations: {
1617
+ readOnlyHint: true,
1618
+ idempotentHint: true
1619
+ }
1620
+ }, async () => {
1621
+ const all = await requestBaseJson("/api/mockups");
1622
+ return textResult(`${all.length} mockup(s)`, { mockups: all });
1623
+ });
1624
+ server.registerTool("get_mockup", {
1625
+ title: "Get a mockup",
1626
+ description: "Fetch one mockup by id, including comments and screens.",
1627
+ inputSchema: { mockupId: z.string().min(1) },
1628
+ outputSchema: { mockup: z.unknown() },
1629
+ annotations: {
1630
+ readOnlyHint: true,
1631
+ idempotentHint: true
1632
+ }
1633
+ }, async ({ mockupId }) => {
1634
+ const mockup = await requestBaseJson(`/api/mockups/${encodeURIComponent(mockupId)}`);
1635
+ return textResult(formatMockupReview(mockup), { mockup });
1636
+ });
1637
+ server.registerTool("get_mockup_versions", {
1638
+ title: "List mockup versions",
1639
+ description: "List version snapshots for a mockup.",
1640
+ inputSchema: { mockupId: z.string().min(1) },
1641
+ outputSchema: { versions: z.array(z.unknown()) },
1642
+ annotations: {
1643
+ readOnlyHint: true,
1644
+ idempotentHint: true
1645
+ }
1646
+ }, async ({ mockupId }) => {
1647
+ const versions = await requestBaseJson(`/api/mockups/${encodeURIComponent(mockupId)}/versions`);
1648
+ return textResult(`${versions.length} version(s)`, { versions });
1649
+ });
1650
+ server.registerTool("get_mockup_version", {
1651
+ title: "Get a mockup version",
1652
+ description: "Fetch one historical mockup version.",
1653
+ inputSchema: {
1654
+ mockupId: z.string().min(1),
1655
+ version: z.number().int().positive()
1656
+ },
1657
+ outputSchema: {
1658
+ version: z.unknown(),
1659
+ mockup: z.unknown()
1660
+ },
1661
+ annotations: {
1662
+ readOnlyHint: true,
1663
+ idempotentHint: true
1664
+ }
1665
+ }, async ({ mockupId, version }) => {
1666
+ const data = await requestBaseJson(`/api/mockups/${encodeURIComponent(mockupId)}/versions/${version}`);
1667
+ return textResult(`v${version}`, data);
1668
+ });
1669
+ async function findMockupForComment(commentId) {
1670
+ const mockup = (await requestBaseJson("/api/mockups?include=comments")).find((m) => (m.comments ?? []).some((c) => c.id === commentId));
1671
+ if (!mockup) throw new Error(`Mockup comment ${commentId} was not found.`);
1672
+ return mockup;
1673
+ }
1674
+ server.registerTool("reply_to_mockup_comment", {
1675
+ title: "Reply to a mockup comment",
1676
+ description: "Post an agent reply on a mockup comment thread.",
1677
+ inputSchema: {
1678
+ commentId: z.string().min(1),
1679
+ body: z.string().min(1),
1680
+ model: z.string().optional()
1681
+ },
1682
+ outputSchema: { ok: z.boolean() },
1683
+ annotations: {
1684
+ readOnlyHint: false,
1685
+ idempotentHint: false
1686
+ }
1687
+ }, async ({ commentId, body, model }) => {
1688
+ const mockup = await findMockupForComment(commentId);
1689
+ await requestBaseJson(`/api/mockups/${encodeURIComponent(mockup.id)}/comments/${encodeURIComponent(commentId)}/replies`, {
1690
+ method: "POST",
1691
+ headers: { "Content-Type": "application/json" },
1692
+ body: JSON.stringify({
1693
+ body,
1694
+ model,
1695
+ role: "agent"
1696
+ })
1697
+ });
1698
+ return textResult(`Replied to ${commentId}`, { ok: true });
1699
+ });
1700
+ server.registerTool("resolve_mockup_comment", {
1701
+ title: "Resolve a mockup comment",
1702
+ description: "Mark a mockup comment thread resolved.",
1703
+ inputSchema: { commentId: z.string().min(1) },
1704
+ outputSchema: { ok: z.boolean() },
1705
+ annotations: {
1706
+ readOnlyHint: false,
1707
+ idempotentHint: false
1708
+ }
1709
+ }, async ({ commentId }) => {
1710
+ const mockup = await findMockupForComment(commentId);
1711
+ await requestBaseJson(`/api/mockups/${encodeURIComponent(mockup.id)}/comments/${encodeURIComponent(commentId)}`, {
1712
+ method: "PUT",
1713
+ headers: { "Content-Type": "application/json" },
1714
+ body: JSON.stringify({ status: "resolved" })
1715
+ });
1716
+ return textResult(`Resolved ${commentId}`, { ok: true });
1717
+ });
1718
+ server.registerTool("inspect_mockup", {
1719
+ title: "Inspect a mockup (bounded)",
1720
+ description: "Read compact, bounded mockup data. view=summary (headline stats), comments (paged comment list), comment (one thread), screen (screen list). Filter by comment scope: status, screenId, viewport (desktop|tablet|mobile), version. Page with cursor/limit; context=none|anchor|source controls how much anchor data is included. Prefer this over get_mockup, which returns every screen's full HTML.",
1721
+ inputSchema: {
1722
+ mockupId: z.string().min(1),
1723
+ view: z.enum([
1724
+ "summary",
1725
+ "comments",
1726
+ "comment",
1727
+ "screen"
1728
+ ]).default("summary"),
1729
+ status: z.enum(["open", "resolved"]).optional(),
1730
+ screenId: z.string().optional(),
1731
+ viewport: z.enum([
1732
+ "desktop",
1733
+ "tablet",
1734
+ "mobile"
1735
+ ]).optional(),
1736
+ version: z.number().int().positive().optional(),
1737
+ commentId: z.string().optional(),
1738
+ cursor: z.number().int().nonnegative().optional(),
1739
+ limit: z.number().int().positive().max(200).optional(),
1740
+ context: z.enum([
1741
+ "none",
1742
+ "anchor",
1743
+ "source"
1744
+ ]).default("anchor")
1745
+ },
1746
+ outputSchema: { data: z.unknown() },
1747
+ annotations: READ_ONLY
1748
+ }, async ({ mockupId, view, status, screenId, viewport, version, commentId, cursor, limit, context }) => {
1749
+ const params = new URLSearchParams();
1750
+ params.set("view", view);
1751
+ if (status) params.set("status", status);
1752
+ if (screenId) params.set("screen", screenId);
1753
+ if (viewport) params.set("viewport", viewport);
1754
+ if (version !== void 0) params.set("version", String(version));
1755
+ if (commentId) params.set("id", commentId);
1756
+ if (cursor !== void 0) params.set("cursor", String(cursor));
1757
+ if (limit !== void 0) params.set("limit", String(limit));
1758
+ params.set("context", context);
1759
+ const data = await requestBaseJson(`/api/mockups/${encodeURIComponent(mockupId)}/inspect?${params.toString()}`);
1760
+ return textResult(`inspect_mockup ${view} for ${mockupId}`, { data });
1761
+ });
1762
+ server.registerTool("revise_mockup", {
1763
+ title: "Revise a mockup screen",
1764
+ description: "One-screen revision of an HTML mockup. op=upsert adds/replaces a screen (html inline, never files on disk), op=remove deletes a screen, op=patch replaces the first exact occurrence of expectedText with replacement. Every success bumps the mockup version; pass expectedVersion to guard against racing edits (409 version-mismatch on conflict, nothing applied). For revisions touching many screens, resubmit via submit_mockup with the same mockupId instead.",
1765
+ inputSchema: {
1766
+ mockupId: z.string().min(1),
1767
+ op: z.enum([
1768
+ "upsert",
1769
+ "remove",
1770
+ "patch"
1771
+ ]),
1772
+ screenId: z.string().min(1),
1773
+ html: z.string().optional(),
1774
+ label: z.string().optional(),
1775
+ expectedText: z.string().optional(),
1776
+ replacement: z.string().optional(),
1777
+ expectedVersion: z.number().int().positive().optional()
1778
+ },
1779
+ outputSchema: {
1780
+ mockupId: z.string(),
1781
+ version: z.number(),
1782
+ screenIds: z.array(z.string()),
1783
+ occurrences: z.number().optional()
1784
+ },
1785
+ annotations: MUTATING
1786
+ }, async ({ mockupId, op, screenId, html, label, expectedText, replacement, expectedVersion }) => {
1787
+ const headers = { "Content-Type": "application/json" };
1788
+ const base = `/api/mockups/${encodeURIComponent(mockupId)}/screens/${encodeURIComponent(screenId)}`;
1789
+ if (op === "upsert") {
1790
+ if (!html) throw new Error("revise_mockup op=upsert requires html");
1791
+ const mockup = await requestBaseJson(base, {
1792
+ method: "PUT",
1793
+ headers,
1794
+ body: JSON.stringify({
1795
+ html,
1796
+ label,
1797
+ expectedVersion
1798
+ })
1799
+ });
1800
+ const hints = mockup.hints ?? [];
1801
+ const hintText = hints.length > 0 ? `\n⚠️ In-page state UI detected — split each state into its own screen: ${hints.map((h) => h.patterns.join(", ")).join("; ")}` : "";
1802
+ return textResult(`Upserted screen ${screenId} on ${mockupId} → v${mockup.version}.${hintText}`, {
1803
+ mockupId,
1804
+ version: mockup.version,
1805
+ screenIds: mockup.screens.map((s) => s.id),
1806
+ ...hints.length > 0 ? { hints } : {}
1807
+ });
1808
+ }
1809
+ if (op === "remove") {
1810
+ const mockup = await requestBaseJson(`${base}${expectedVersion === void 0 ? "" : `?expectedVersion=${expectedVersion}`}`, { method: "DELETE" });
1811
+ return textResult(`Removed screen ${screenId} from ${mockupId} → v${mockup.version}`, {
1812
+ mockupId,
1813
+ version: mockup.version,
1814
+ screenIds: mockup.screens.map((s) => s.id)
1815
+ });
1816
+ }
1817
+ if (expectedText === void 0 || replacement === void 0) throw new Error("revise_mockup op=patch requires expectedText and replacement");
1818
+ const data = await requestBaseJson(base, {
1819
+ method: "PATCH",
1820
+ headers,
1821
+ body: JSON.stringify({
1822
+ expectedText,
1823
+ replacement,
1824
+ expectedVersion
1825
+ })
1826
+ });
1827
+ return textResult(`Patched ${screenId} on ${mockupId} (${data.occurrences} exact match(es)) → v${data.mockup.version}`, {
1828
+ mockupId,
1829
+ version: data.mockup.version,
1830
+ screenIds: data.mockup.screens.map((s) => s.id),
1831
+ occurrences: data.occurrences
1832
+ });
1833
+ });
1834
+ const threadOpSchema = z.discriminatedUnion("op", [
1835
+ z.object({
1836
+ op: z.literal("reply"),
1837
+ commentId: z.string().min(1),
1838
+ body: z.string().min(1),
1839
+ role: z.string().optional(),
1840
+ model: z.string().optional()
1841
+ }),
1842
+ z.object({
1843
+ op: z.literal("edit"),
1844
+ commentId: z.string().min(1),
1845
+ replyId: z.string().optional(),
1846
+ body: z.string().min(1)
1847
+ }),
1848
+ z.object({
1849
+ op: z.literal("delete"),
1850
+ commentId: z.string().min(1),
1851
+ replyId: z.string().optional()
1852
+ }),
1853
+ z.object({
1854
+ op: z.literal("resolve"),
1855
+ commentId: z.string().min(1)
1856
+ }),
1857
+ z.object({
1858
+ op: z.literal("unresolve"),
1859
+ commentId: z.string().min(1)
1860
+ })
1861
+ ]);
1862
+ server.registerTool("update_mockup_threads", {
1863
+ title: "Update mockup threads (atomic batch)",
1864
+ description: "Atomically apply mockup thread operations: reply (agent answer), edit/delete replies, delete comments, resolve/unresolve. Every operation is validated against the mockup before any is applied — one invalid op aborts the whole batch with no changes. Thread ops never bump the mockup version. Prefer this over single-op reply_to_mockup_comment / resolve_mockup_comment when doing several edits.",
1865
+ inputSchema: {
1866
+ mockupId: z.string().min(1),
1867
+ operations: z.array(threadOpSchema).min(1)
1868
+ },
1869
+ outputSchema: {
1870
+ applied: z.number(),
1871
+ results: z.array(z.unknown())
1872
+ },
1873
+ annotations: MUTATING
1874
+ }, async ({ mockupId, operations }) => {
1875
+ const data = await requestBaseJson(`/api/mockups/${encodeURIComponent(mockupId)}/threads/batch`, {
1876
+ method: "POST",
1877
+ headers: { "Content-Type": "application/json" },
1878
+ body: JSON.stringify({ operations })
1879
+ });
1880
+ return textResult(`Applied ${data.applied} thread op(s) to ${mockupId}`, {
1881
+ applied: data.applied,
1882
+ results: data.results
1883
+ });
1884
+ });
1495
1885
  server.registerResource("agent-guide", "diffing://agent-guide", {
1496
1886
  title: "diffing agent guide",
1497
1887
  description: "Portable quick reference for the native code-review and plan-review workflows.",
@@ -1522,6 +1912,13 @@ This MCP connection is immutably bound to \`${repoRoot}\`.
1522
1912
  4. On \`changes-requested\`, revise and resubmit with the same \`planId\`.
1523
1913
  5. On \`approved\`, proceed; on \`rejected\`, stop.
1524
1914
 
1915
+ ## Mockup review
1916
+ 1. Call \`submit_mockup\`, share the URL, and park unless asked to wait; resubmit with the same \`mockupId\` to bump the version.
1917
+ 2. On resume or sync wait, call \`await_mockup_review\` (or \`list_mockups\` / \`get_mockup\`).
1918
+ 3. For bounded reads prefer \`inspect_mockup\` (view=summary/comments/comment/screen, filters by status/screenId/viewport/version).
1919
+ 4. To revise one screen use \`revise_mockup\` (upsert/remove/patch with \`expectedVersion\` guard); thread ops go through atomic \`update_mockup_threads\`.
1920
+ 5. On \`approved\`, implement; on \`changes-requested\`, revise and resubmit; on \`rejected\`, stop.
1921
+
1525
1922
  ## GitHub PR
1526
1923
  1. In \`gh-pr\` mode call \`gh_overview\`, then the bounded diff tools.
1527
1924
  2. Fetch published discussion with \`gh_list_threads\` (prefer unresolved) and \`gh_list_reviews\`.