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.
- package/dist/{cli-agent-qNwkSjxK.mjs → cli-agent-Cf6SAnH4.mjs} +744 -15
- package/dist/{cli-gh-BvoDabKK.mjs → cli-gh-2NEumKQB.mjs} +1 -1
- package/dist/cli.mjs +5 -4
- package/dist/client/assets/{angular-html-ByeJB0rh.js → angular-html-BV3gPi7L.js} +1 -1
- package/dist/client/assets/{angular-ts-CiwZGajG.js → angular-ts-DuuSfKJK.js} +1 -1
- package/dist/client/assets/{apl-C8k5a5fk.js → apl-BMc69egk.js} +1 -1
- package/dist/client/assets/{arc-B8VMoqVr.js → arc-CEC1cz1S.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-C-ng2xNL.js → architectureDiagram-3BPJPVTR-BmJ5OHgd.js} +1 -1
- package/dist/client/assets/{astro-BE-h-qpp.js → astro-Bg_CYwmB.js} +1 -1
- package/dist/client/assets/{blade-D-0BV1wW.js → blade-Mca0XoRC.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-DyAQuONU.js → blockDiagram-GPEHLZMM--XrThRLW.js} +1 -1
- package/dist/client/assets/{c-Dg_Gx29H.js → c-B2Uqi6oI.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-k5uKTHvm.js → c4Diagram-AAUBKEIU-W8w5oiRi.js} +1 -1
- package/dist/client/assets/channel-C9URWRe4.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-Dod_VwYj.js → chunk-2J33WTMH-Bpr3MqKf.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-B8HMjry-.js → chunk-3OPIFGDE-DdMV_3DM.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-TvkJVIZw.js → chunk-4BX2VUAB-BkfeLLsz.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-BsXuKAKc.js → chunk-55IACEB6-pCwXbxR0.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-BI1cj2cZ.js → chunk-5ZQYHXKU-BGFzCgvs.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-yWQ27Wk0.js → chunk-727SXJPM-DU7oVWfJ.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-B2BBCtTr.js → chunk-AQP2D5EJ-DaVYG1qm.js} +1 -1
- package/dist/client/assets/{chunk-BSJP7CBP-CXXvPXhK.js → chunk-BSJP7CBP-DAvnzgjY.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-jYiloqyi.js → chunk-CSCIHK7Q-D6ZDvMsi.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-DZzqIT32.js → chunk-FMBD7UC4-BFDCYCqF.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-krdCf8iG.js → chunk-KSCS5N6A-BmD6XY52.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-0uuTmeZV.js → chunk-L5ZTLDWV-Dl9iUIBf.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-BBfUgD06.js → chunk-LZXEDZCA-rOOJDxdB.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-jkir4ZKE.js → chunk-ND2GUHAM-DBDu-JeD.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-DMWZNibJ.js → chunk-NZK2D7GU-CRyk4F3R.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-B1e1HGaX.js → chunk-O5CBEL6O-DcKB07iP.js} +1 -1
- package/dist/client/assets/chunk-QZHKN3VN-BBNO2-yv.js +1 -0
- package/dist/client/assets/chunk-WU5MYG2G-HP2Om5f9.js +1 -0
- package/dist/client/assets/{chunk-XPW4576I-AvbCAUav.js → chunk-XPW4576I-WuzWMSrp.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-BW_ZPHe7.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DOd628kk.js +1 -0
- package/dist/client/assets/{cobol-DQnGB_GV.js → cobol-oQ1i-YtO.js} +1 -1
- package/dist/client/assets/{coffee-CDV5a89x.js → coffee-s9XlmHbB.js} +1 -1
- package/dist/client/assets/{cose-bilkent-S5V4N54A-t-mDNRF2.js → cose-bilkent-S5V4N54A-CymUm7fn.js} +1 -1
- package/dist/client/assets/{cpp-8G6b-siI.js → cpp-D60LxTox.js} +1 -1
- package/dist/client/assets/{crystal-CGYCC6FM.js → crystal-CHrCczDb.js} +1 -1
- package/dist/client/assets/{css-nez4R8_Z.js → css-1PQO32Hw.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG--azesYIg.js → dagre-BM42HDAG-DOHZc0tO.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-4WJ7nKhA.js → diagram-2AECGRRQ-D8O2cleS.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-C7ZGhLXn.js → diagram-5GNKFQAL-CO-Ej4UD.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-BobK_rbl.js → diagram-KO2AKTUF-DCUr4wzS.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-BOc-OkIS.js → diagram-LMA3HP47-D9HnA0Qg.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-BEtLgm86.js → diagram-OG6HWLK6-BxAitO2n.js} +1 -1
- package/dist/client/assets/{dist-DPuBwToc.js → dist-N-ROBnkL.js} +1 -1
- package/dist/client/assets/{edge-Z5RMbJQv.js → edge-Ck54Fn7y.js} +1 -1
- package/dist/client/assets/{edit-CFxbgVPV.js → edit-B8RwtrDs.js} +1 -1
- package/dist/client/assets/{elixir-d3C79FQc.js → elixir-xXxdfsfU.js} +1 -1
- package/dist/client/assets/{elm-CDAf6Uyn.js → elm-g0vLRWbF.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-D-pR2GY9.js → erDiagram-TEJ5UH35-1ZMg7g98.js} +1 -1
- package/dist/client/assets/{erb-CTJk9FKH.js → erb-CGFnxOST.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-BwMCy2QV.js → flowDiagram-I6XJVG4X-D5u9lhJ-.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-BI1WLJDi.js → ganttDiagram-6RSMTGT7-BbVKEzs8.js} +1 -1
- package/dist/client/assets/{git-rebase-Dr0-MVoW.js → git-rebase-B3s6bE5_.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CUfW0k1f.js → gitGraphDiagram-PVQCEYII-sAottiiz.js} +1 -1
- package/dist/client/assets/{glimmer-js-CXhLhyW-.js → glimmer-js-C2Xff18_.js} +1 -1
- package/dist/client/assets/{glimmer-ts-BFAFT-VD.js → glimmer-ts-CRBS8ftd.js} +1 -1
- package/dist/client/assets/{glsl-D7yanhgY.js → glsl-aOY_t6dG.js} +1 -1
- package/dist/client/assets/{graphql-CpoDxrok.js → graphql-Cucpdpc6.js} +1 -1
- package/dist/client/assets/{hack-BL5fmC4q.js → hack-Bw0KkVza.js} +1 -1
- package/dist/client/assets/{haml-ClAE6RVM.js → haml-DnyaPoa2.js} +1 -1
- package/dist/client/assets/{handlebars-nHw91uOv.js → handlebars-AedEof-m.js} +1 -1
- package/dist/client/assets/{html-BIiOKqgG.js → html-Bu5h_Kgf.js} +1 -1
- package/dist/client/assets/{html-derivative-D1nPnuOK.js → html-derivative-CNiVLld1.js} +1 -1
- package/dist/client/assets/{http-B_QcRZjt.js → http-B9-oOYPw.js} +1 -1
- package/dist/client/assets/{hurl-CZE0DsD3.js → hurl-Bo6NUnnd.js} +1 -1
- package/dist/client/assets/{index-Erqhm7rW.js → index-BkwV-g6J.js} +91 -91
- package/dist/client/assets/index-C73yr1tm.css +1 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-w2M_OHua.js → infoDiagram-5YYISTIA-CfVELF4R.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CW_l9PGx.js → ishikawaDiagram-YF4QCWOH-DztjIGfb.js} +1 -1
- package/dist/client/assets/{java-CXTqX09w.js → java-JJ3dl_jz.js} +1 -1
- package/dist/client/assets/{javascript-9z9ABBep.js → javascript-QgUuQNTw.js} +1 -1
- package/dist/client/assets/{jinja-Cluf_5Bk.js → jinja-GbeEm2-u.js} +1 -1
- package/dist/client/assets/{jison-CqCEUtc4.js → jison-BXZbHsO3.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-D-YwusBa.js → journeyDiagram-JHISSGLW-Lg8CkxeI.js} +1 -1
- package/dist/client/assets/{json-BMq_bPP8.js → json-D02YvQSa.js} +1 -1
- package/dist/client/assets/{jsx-Djij59b9.js → jsx-eZde6NZS.js} +1 -1
- package/dist/client/assets/{julia-DybzCMnh.js → julia-CfcuUD-A.js} +1 -1
- package/dist/client/assets/{just-4ChcAr9-.js → just-hQr8WcQp.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-Brvoukmm.js → kanban-definition-UN3LZRKU-BWIeZIf6.js} +1 -1
- package/dist/client/assets/{latex-CS2TtFXG.js → latex-BxffZwv_.js} +1 -1
- package/dist/client/assets/{line-CqxM-stA.js → line-Cy1AFxf0.js} +1 -1
- package/dist/client/assets/{linear-C7T82059.js → linear-Kk5IbGHr.js} +1 -1
- package/dist/client/assets/{liquid-84AtYenm.js → liquid-BvKnpUyR.js} +1 -1
- package/dist/client/assets/{lua-DJzFx3Cf.js → lua-Bcds2vY9.js} +1 -1
- package/dist/client/assets/{marko-BLw9BV9E.js → marko-CZJQLjYc.js} +1 -1
- package/dist/client/assets/{mdc-BjZejN3b.js → mdc-BRigtixg.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-BdiEBAsd.js → mermaid-parser.core-Dh2uTVxt.js} +1 -1
- package/dist/client/assets/{mermaid.core-BsnKhnrM.js → mermaid.core-CXIGVQ74.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-CBaIFnbc.js → mindmap-definition-RKZ34NQL-DQNQJuAB.js} +1 -1
- package/dist/client/assets/{nginx-DuamCwvX.js → nginx-BI3-LI9U.js} +1 -1
- package/dist/client/assets/{nim-Bt8c9-ge.js → nim-PEd8ZOME.js} +1 -1
- package/dist/client/assets/{perl-D52AfmwC.js → perl-BPTGdP4p.js} +1 -1
- package/dist/client/assets/{php-BBMlJg0_.js → php-C8BKL0yL.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-BlmPajcA.js → pieDiagram-4H26LBE5-Dz7gE0t5.js} +1 -1
- package/dist/client/assets/{pug-DFHmDqEE.js → pug-H5z9rtbz.js} +1 -1
- package/dist/client/assets/{qml-C0333o-g.js → qml-EzSpo2aM.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Bqlm_zZk.js → quadrantDiagram-W4KKPZXB-pTnmYdEJ.js} +1 -1
- package/dist/client/assets/{r-DZFmvIgR.js → r-VMeUwMIt.js} +1 -1
- package/dist/client/assets/{razor-BIGLkWzR.js → razor-Ne05zcaj.js} +1 -1
- package/dist/client/assets/{regexp-BW6jpgf9.js → regexp-i5_jUZAj.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-B1PrtUeD.js → requirementDiagram-4Y6WPE33-GVw3NOxE.js} +1 -1
- package/dist/client/assets/{review-export-BlgD3bdw.js → review-export-B4TOUDUE.js} +1 -1
- package/dist/client/assets/{rst-Cj51ZJaa.js → rst-BZWuei_6.js} +1 -1
- package/dist/client/assets/{ruby-lMhGjwpf.js → ruby-CApICFTD.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-D2HtUNVF.js → sankeyDiagram-5OEKKPKP-CwWzwmqj.js} +1 -1
- package/dist/client/assets/{sas-BWSQku7l.js → sas-BzEPX6GL.js} +1 -1
- package/dist/client/assets/{scss-CdRPuhLN.js → scss-B6Ld2NRm.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-D9SQy0XI.js → sequenceDiagram-3UESZ5HK-Bz_Q76Kt.js} +1 -1
- package/dist/client/assets/{shellscript-DOfOBXcj.js → shellscript-KmoqGVVu.js} +1 -1
- package/dist/client/assets/{shellsession-7XAOCVni.js → shellsession-CsSh_KTo.js} +1 -1
- package/dist/client/assets/{soy-DDnzVRIw.js → soy-CLgOu1If.js} +1 -1
- package/dist/client/assets/{sql-Ba34agKe.js → sql-BdCh56kU.js} +1 -1
- package/dist/client/assets/{src-BO-qT_v_.js → src-D7QQkGEc.js} +1 -1
- package/dist/client/assets/{stata-BQlYGHQU.js → stata-C3fo8EYS.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-7IEYjFRo.js → stateDiagram-AJRCARHV-D0pZtzLu.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-P0vTqsyH.js +1 -0
- package/dist/client/assets/{surrealql-BET1l7SW.js → surrealql-qzihsJw_.js} +1 -1
- package/dist/client/assets/{svelte-Bd7ZOeQI.js → svelte-B4tbn_KI.js} +1 -1
- package/dist/client/assets/{templ-BYvRQdOw.js → templ-Ddev3BvY.js} +1 -1
- package/dist/client/assets/{tex-CT8DpKxX.js → tex-DJLtgNQ6.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-CurQYWmi.js → timeline-definition-PNZ67QCA-CWTWgYi0.js} +1 -1
- package/dist/client/assets/{ts-tags-D4iPPwau.js → ts-tags-qqxigRtr.js} +1 -1
- package/dist/client/assets/{tsx-Dgk_Aw-B.js → tsx-Pkf9BaPv.js} +1 -1
- package/dist/client/assets/{twig-abRNWeh3.js → twig-BiScb10p.js} +1 -1
- package/dist/client/assets/{typescript-BgR1JqdC.js → typescript-Bhy3UXig.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-DbrRBL5S.js → vennDiagram-CIIHVFJN-DwB89v9I.js} +1 -1
- package/dist/client/assets/{vue-DSkhKq4Q.js → vue-CANMWBRv.js} +1 -1
- package/dist/client/assets/{vue-html-J0jWoD9X.js → vue-html-DtS6f62r.js} +1 -1
- package/dist/client/assets/{vue-vine-BgX5MC8-.js → vue-vine-BpQ9kwSB.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DIFXQsoP.js → wardleyDiagram-YWT4CUSO-DNwwPk4s.js} +1 -1
- package/dist/client/assets/{xml-Ca53xGLB.js → xml-CD5eIXsm.js} +1 -1
- package/dist/client/assets/{xsl-CNnXclYV.js → xsl-DGNZQAUu.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-CmfInPqa.js → xychartDiagram-2RQKCTM6-L7M4YSvw.js} +1 -1
- package/dist/client/assets/{yaml-CjjyUhfg.js → yaml-Cm8mwIB7.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/{completions-7YSX1neu.mjs → completions-2aHVTmiJ.mjs} +17 -0
- package/dist/completions-CYa5usLb.mjs +2 -0
- package/dist/{mcp-DhynPewa.mjs → mcp-DoNBucix.mjs} +403 -6
- package/dist/mockup-format-D4Ln0WxJ.mjs +327 -0
- package/dist/native/tui-darwin-arm64/diffing-tui +0 -0
- package/dist/native/tui-darwin-x64/diffing-tui +0 -0
- package/dist/native/tui-linux-arm64-gnu/diffing-tui +0 -0
- package/dist/native/tui-linux-arm64-musl/diffing-tui +0 -0
- package/dist/native/tui-linux-x64-gnu/diffing-tui +0 -0
- package/dist/native/tui-linux-x64-musl/diffing-tui +0 -0
- package/dist/native/tui-win32-x64-msvc/diffing-tui.exe +0 -0
- package/dist/{review-export-GW4KNsGN.mjs → review-export-DoST2oPt.mjs} +1 -1
- package/dist/{server-CEbp7K-k.mjs → server-BRQdb64n.mjs} +1727 -49
- package/dist/{setup-BnS_rSxK.mjs → setup-BwWZ9Fzq.mjs} +1 -1
- package/extensions/pi/index.ts +452 -5
- package/package.json +1 -1
- package/dist/client/assets/channel-DsuXeLi4.js +0 -1
- package/dist/client/assets/chunk-QZHKN3VN-D5b3mBJ2.js +0 -1
- package/dist/client/assets/chunk-WU5MYG2G-C1Pa4Y04.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DCnVfYt-.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BPa1Jkt-.js +0 -1
- package/dist/client/assets/index-BR6EGJ-g.css +0 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-DP6swlL9.js +0 -1
- package/dist/completions-5rtfoxB9.mjs +0 -2
- package/dist/plan-format-BwJh5JSE.mjs +0 -144
- /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-
|
|
2
|
-
import { t as
|
|
3
|
-
import {
|
|
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
|
-
|
|
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\`.
|