diffing 0.16.1 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{cli-agent-Dk0yRtrA.mjs → cli-agent-du9TS34N.mjs} +763 -20
- 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-Des_xD-2.js} +1 -1
- package/dist/client/assets/{angular-ts-CiwZGajG.js → angular-ts-FXZjKR0Q.js} +1 -1
- package/dist/client/assets/{apl-C8k5a5fk.js → apl-CU0GH5rh.js} +1 -1
- package/dist/client/assets/{arc-B8VMoqVr.js → arc-DnvfnvVv.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-C-ng2xNL.js → architectureDiagram-3BPJPVTR-Cb41y9QS.js} +1 -1
- package/dist/client/assets/{astro-BE-h-qpp.js → astro-CUWNJBWq.js} +1 -1
- package/dist/client/assets/{blade-D-0BV1wW.js → blade-2PPtLT0e.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-DyAQuONU.js → blockDiagram-GPEHLZMM-BYxsiST2.js} +1 -1
- package/dist/client/assets/{c-Dg_Gx29H.js → c-D3K42QQV.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-k5uKTHvm.js → c4Diagram-AAUBKEIU-BU7I7J1w.js} +1 -1
- package/dist/client/assets/channel-Cx6nXkw1.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-Dod_VwYj.js → chunk-2J33WTMH-DQnKNewn.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-B8HMjry-.js → chunk-3OPIFGDE-BE6bYzdw.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-TvkJVIZw.js → chunk-4BX2VUAB-CTw-Pjas.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-BsXuKAKc.js → chunk-55IACEB6-BBCsXXSw.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-BI1cj2cZ.js → chunk-5ZQYHXKU-BrM_4KH1.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-yWQ27Wk0.js → chunk-727SXJPM-B_Vz3jyP.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-B2BBCtTr.js → chunk-AQP2D5EJ-CHSGbbjn.js} +1 -1
- package/dist/client/assets/{chunk-BSJP7CBP-CXXvPXhK.js → chunk-BSJP7CBP-D6jlDOzg.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-jYiloqyi.js → chunk-CSCIHK7Q-D_02XyF0.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-DZzqIT32.js → chunk-FMBD7UC4-DxbqnfDH.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-krdCf8iG.js → chunk-KSCS5N6A-_Pc1IHYq.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-0uuTmeZV.js → chunk-L5ZTLDWV-Dj8OTMjE.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-BBfUgD06.js → chunk-LZXEDZCA-DQn_LdxZ.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-jkir4ZKE.js → chunk-ND2GUHAM-BYbHwrXp.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-DMWZNibJ.js → chunk-NZK2D7GU-Bvgyac_1.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-B1e1HGaX.js → chunk-O5CBEL6O-DeRRUqxP.js} +1 -1
- package/dist/client/assets/chunk-QZHKN3VN-BkYYlBcO.js +1 -0
- package/dist/client/assets/chunk-WU5MYG2G-DyGKxcFl.js +1 -0
- package/dist/client/assets/{chunk-XPW4576I-AvbCAUav.js → chunk-XPW4576I-BrtDtomN.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DwPqBLeK.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Mxl80axQ.js +1 -0
- package/dist/client/assets/{cobol-DQnGB_GV.js → cobol-hL9_ibhX.js} +1 -1
- package/dist/client/assets/{coffee-CDV5a89x.js → coffee-DFRt5qt1.js} +1 -1
- package/dist/client/assets/{cose-bilkent-S5V4N54A-t-mDNRF2.js → cose-bilkent-S5V4N54A-DHCJqbho.js} +1 -1
- package/dist/client/assets/{cpp-8G6b-siI.js → cpp-3A0aT-dF.js} +1 -1
- package/dist/client/assets/{crystal-CGYCC6FM.js → crystal-BzBxs8w6.js} +1 -1
- package/dist/client/assets/{css-nez4R8_Z.js → css-C5lLpDbg.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG--azesYIg.js → dagre-BM42HDAG-DOuf0bNE.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-4WJ7nKhA.js → diagram-2AECGRRQ-Bd5Jiq1y.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-C7ZGhLXn.js → diagram-5GNKFQAL-DvYLKuEY.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-BobK_rbl.js → diagram-KO2AKTUF-BVvqGAtk.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-BOc-OkIS.js → diagram-LMA3HP47-QqHAHEnc.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-BEtLgm86.js → diagram-OG6HWLK6-CkG5SOks.js} +1 -1
- package/dist/client/assets/{dist-DPuBwToc.js → dist-Dh8NC3tY.js} +1 -1
- package/dist/client/assets/{edge-Z5RMbJQv.js → edge-9n_aVc7U.js} +1 -1
- package/dist/client/assets/{edit-CFxbgVPV.js → edit-DlkvWCkd.js} +1 -1
- package/dist/client/assets/{elixir-d3C79FQc.js → elixir-C7ssdzaK.js} +1 -1
- package/dist/client/assets/{elm-CDAf6Uyn.js → elm-CWs6E90H.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-D-pR2GY9.js → erDiagram-TEJ5UH35-CfW0k0ip.js} +1 -1
- package/dist/client/assets/{erb-CTJk9FKH.js → erb-CdXoY37D.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-BwMCy2QV.js → flowDiagram-I6XJVG4X-CBKpQpqx.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-BI1WLJDi.js → ganttDiagram-6RSMTGT7-CS13EpgX.js} +1 -1
- package/dist/client/assets/{git-rebase-Dr0-MVoW.js → git-rebase-BSoXZvcc.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CUfW0k1f.js → gitGraphDiagram-PVQCEYII-BN0i9pDL.js} +1 -1
- package/dist/client/assets/{glimmer-js-CXhLhyW-.js → glimmer-js-BZMH6HKs.js} +1 -1
- package/dist/client/assets/{glimmer-ts-BFAFT-VD.js → glimmer-ts-DXSo27g6.js} +1 -1
- package/dist/client/assets/{glsl-D7yanhgY.js → glsl-Cl8JUVz8.js} +1 -1
- package/dist/client/assets/{graphql-CpoDxrok.js → graphql-CNR_SNqJ.js} +1 -1
- package/dist/client/assets/{hack-BL5fmC4q.js → hack-iAxNUz76.js} +1 -1
- package/dist/client/assets/{haml-ClAE6RVM.js → haml-D11gKVlW.js} +1 -1
- package/dist/client/assets/{handlebars-nHw91uOv.js → handlebars-BAAs9kjt.js} +1 -1
- package/dist/client/assets/{html-BIiOKqgG.js → html-C-zWst2_.js} +1 -1
- package/dist/client/assets/{html-derivative-D1nPnuOK.js → html-derivative-Bu6k8r8J.js} +1 -1
- package/dist/client/assets/{http-B_QcRZjt.js → http-B_xQ7YQ1.js} +1 -1
- package/dist/client/assets/{hurl-CZE0DsD3.js → hurl-B7IYz3qL.js} +1 -1
- package/dist/client/assets/index-1GDpp-wO.css +1 -0
- package/dist/client/assets/{index-Erqhm7rW.js → index-D_eRtjau.js} +49 -49
- package/dist/client/assets/{infoDiagram-5YYISTIA-w2M_OHua.js → infoDiagram-5YYISTIA-Choq89cB.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CW_l9PGx.js → ishikawaDiagram-YF4QCWOH-CZY99mk-.js} +1 -1
- package/dist/client/assets/{java-CXTqX09w.js → java-BPJVzio0.js} +1 -1
- package/dist/client/assets/{javascript-9z9ABBep.js → javascript-DiOMtf_A.js} +1 -1
- package/dist/client/assets/{jinja-Cluf_5Bk.js → jinja-l7JM-8PK.js} +1 -1
- package/dist/client/assets/{jison-CqCEUtc4.js → jison-CAc6lM-g.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-D-YwusBa.js → journeyDiagram-JHISSGLW-CDbADAMs.js} +1 -1
- package/dist/client/assets/{json-BMq_bPP8.js → json-DqZO-AEh.js} +1 -1
- package/dist/client/assets/{jsx-Djij59b9.js → jsx-UqnCpKTP.js} +1 -1
- package/dist/client/assets/{julia-DybzCMnh.js → julia-DlbfYJz3.js} +1 -1
- package/dist/client/assets/{just-4ChcAr9-.js → just-9ze8Wi6d.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-Brvoukmm.js → kanban-definition-UN3LZRKU-DgrkeisF.js} +1 -1
- package/dist/client/assets/{latex-CS2TtFXG.js → latex-_9Fzq-E3.js} +1 -1
- package/dist/client/assets/{line-CqxM-stA.js → line-b-Rcrqbg.js} +1 -1
- package/dist/client/assets/{linear-C7T82059.js → linear-C_RFbWEw.js} +1 -1
- package/dist/client/assets/{liquid-84AtYenm.js → liquid-CMiBoRv6.js} +1 -1
- package/dist/client/assets/{lua-DJzFx3Cf.js → lua-BzfULkCh.js} +1 -1
- package/dist/client/assets/{marko-BLw9BV9E.js → marko-BfcVqnF5.js} +1 -1
- package/dist/client/assets/{mdc-BjZejN3b.js → mdc-BKqkdcKC.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-BdiEBAsd.js → mermaid-parser.core-DiJRF2JX.js} +1 -1
- package/dist/client/assets/{mermaid.core-BsnKhnrM.js → mermaid.core-2WkwaCuC.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-CBaIFnbc.js → mindmap-definition-RKZ34NQL-BYxdNWsh.js} +1 -1
- package/dist/client/assets/{nginx-DuamCwvX.js → nginx-DiNLSsbI.js} +1 -1
- package/dist/client/assets/{nim-Bt8c9-ge.js → nim-CohF0tPX.js} +1 -1
- package/dist/client/assets/{perl-D52AfmwC.js → perl-Q-a6BHYR.js} +1 -1
- package/dist/client/assets/{php-BBMlJg0_.js → php-DEj4odG7.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-BlmPajcA.js → pieDiagram-4H26LBE5-DIMAUXZJ.js} +1 -1
- package/dist/client/assets/{pug-DFHmDqEE.js → pug-vqq1Jo-6.js} +1 -1
- package/dist/client/assets/{qml-C0333o-g.js → qml-DkOoIH_j.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Bqlm_zZk.js → quadrantDiagram-W4KKPZXB-DKoe-k2E.js} +1 -1
- package/dist/client/assets/{r-DZFmvIgR.js → r-Cen6c1F0.js} +1 -1
- package/dist/client/assets/{razor-BIGLkWzR.js → razor-SY8AQA9t.js} +1 -1
- package/dist/client/assets/{regexp-BW6jpgf9.js → regexp-CjphwwlN.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-B1PrtUeD.js → requirementDiagram-4Y6WPE33-Du9fS-Dw.js} +1 -1
- package/dist/client/assets/{review-export-BlgD3bdw.js → review-export-FIT7B1uP.js} +1 -1
- package/dist/client/assets/{rst-Cj51ZJaa.js → rst-B79v9bjn.js} +1 -1
- package/dist/client/assets/{ruby-lMhGjwpf.js → ruby-Dia4QaOO.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-D2HtUNVF.js → sankeyDiagram-5OEKKPKP-CsezsM2Z.js} +1 -1
- package/dist/client/assets/{sas-BWSQku7l.js → sas-CYRTwHiQ.js} +1 -1
- package/dist/client/assets/{scss-CdRPuhLN.js → scss-DNWCy5-q.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-D9SQy0XI.js → sequenceDiagram-3UESZ5HK-CNovbaFC.js} +1 -1
- package/dist/client/assets/{shellscript-DOfOBXcj.js → shellscript-BSQLDkAe.js} +1 -1
- package/dist/client/assets/{shellsession-7XAOCVni.js → shellsession-CfvHV3oR.js} +1 -1
- package/dist/client/assets/{soy-DDnzVRIw.js → soy-DLwu5RtN.js} +1 -1
- package/dist/client/assets/{sql-Ba34agKe.js → sql-DlyzTy0P.js} +1 -1
- package/dist/client/assets/{src-BO-qT_v_.js → src-DNJJnOd8.js} +1 -1
- package/dist/client/assets/{stata-BQlYGHQU.js → stata-Djh-WYVT.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-7IEYjFRo.js → stateDiagram-AJRCARHV-cV6R_6CN.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-jmBy204V.js +1 -0
- package/dist/client/assets/{surrealql-BET1l7SW.js → surrealql-Cu-G0NVH.js} +1 -1
- package/dist/client/assets/{svelte-Bd7ZOeQI.js → svelte-CDA2MakI.js} +1 -1
- package/dist/client/assets/{templ-BYvRQdOw.js → templ-H2xc1tDE.js} +1 -1
- package/dist/client/assets/{tex-CT8DpKxX.js → tex-DJ6mQsYC.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-CurQYWmi.js → timeline-definition-PNZ67QCA-BlCMASdx.js} +1 -1
- package/dist/client/assets/{ts-tags-D4iPPwau.js → ts-tags-bYEWgmJO.js} +1 -1
- package/dist/client/assets/{tsx-Dgk_Aw-B.js → tsx-CtTBiWFd.js} +1 -1
- package/dist/client/assets/{twig-abRNWeh3.js → twig-CgXr9wNF.js} +1 -1
- package/dist/client/assets/{typescript-BgR1JqdC.js → typescript-Na7Lj9Vl.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-DbrRBL5S.js → vennDiagram-CIIHVFJN-CvcrIycD.js} +1 -1
- package/dist/client/assets/{vue-DSkhKq4Q.js → vue-BtOd_AfK.js} +1 -1
- package/dist/client/assets/{vue-html-J0jWoD9X.js → vue-html-CVlcjzDD.js} +1 -1
- package/dist/client/assets/{vue-vine-BgX5MC8-.js → vue-vine-k7M6KtfE.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DIFXQsoP.js → wardleyDiagram-YWT4CUSO-BUDJvh59.js} +1 -1
- package/dist/client/assets/{xml-Ca53xGLB.js → xml-Brz46P3k.js} +1 -1
- package/dist/client/assets/{xsl-CNnXclYV.js → xsl-Bh5vdfXi.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-CmfInPqa.js → xychartDiagram-2RQKCTM6-T2Bb0lF_.js} +1 -1
- package/dist/client/assets/{yaml-CjjyUhfg.js → yaml-CRsmB7sL.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-BgrRjzn3.mjs → mcp-H7ytYScV.mjs} +430 -23
- package/dist/mockup-format-CM3ivHHX.mjs +322 -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-Ct9QuV1l.mjs → server-CiE24vh-.mjs} +1842 -53
- package/dist/{setup-BnS_rSxK.mjs → setup-BwWZ9Fzq.mjs} +1 -1
- package/extensions/pi/index.ts +453 -5
- package/package.json +2 -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-CiE24vh-.mjs";
|
|
2
|
+
import { r as formatPlanReview, t as formatMockupReview } from "./mockup-format-CM3ivHHX.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 {
|
|
@@ -618,54 +633,67 @@ function createMcpServer(options) {
|
|
|
618
633
|
});
|
|
619
634
|
server.registerTool("diff_summary", {
|
|
620
635
|
title: "Summarize the active diff (bounded)",
|
|
621
|
-
description: "Return bounded totals and change-kind counts without transferring the patch. Works for web, TUI, and GitHub PR sessions. Prefer this over get_diff.",
|
|
622
|
-
inputSchema: {},
|
|
636
|
+
description: "Return bounded totals, top-level directory buckets, and change-kind counts without transferring the patch. Optional exclude=[\"lockfiles\"] drops lock/generated basenames from counts only. Works for web, TUI, and GitHub PR sessions. Prefer this over get_diff.",
|
|
637
|
+
inputSchema: { exclude: z.array(z.enum(["lockfiles"])).optional() },
|
|
623
638
|
outputSchema: { result: z.unknown() },
|
|
624
639
|
annotations: READ_ONLY
|
|
625
|
-
}, async () => {
|
|
626
|
-
const
|
|
640
|
+
}, async ({ exclude } = { exclude: void 0 }) => {
|
|
641
|
+
const session = requireInspectSession();
|
|
642
|
+
const query = new URLSearchParams();
|
|
643
|
+
if (exclude?.length) query.set("exclude", exclude.join(","));
|
|
644
|
+
const result = await requestSessionJson(session, `/api/diff/summary${query.size ? `?${query}` : ""}`);
|
|
627
645
|
return textResult(JSON.stringify(result), { result });
|
|
628
646
|
});
|
|
629
647
|
server.registerTool("diff_files", {
|
|
630
648
|
title: "Page changed files (bounded)",
|
|
631
|
-
description: "Return a bounded page of changed-file metadata
|
|
649
|
+
description: "Return a bounded page of changed-file metadata. Optional path is a git pathspec-ish glob (src/lib/**, **/foo.ts). cursor/nextCursor index the filtered list; each row still has the global file index. Works for web, TUI, and GitHub PR sessions.",
|
|
632
650
|
inputSchema: {
|
|
633
651
|
cursor: z.number().int().nonnegative().optional(),
|
|
634
|
-
limit: z.number().int().positive().max(1e3).optional()
|
|
652
|
+
limit: z.number().int().positive().max(1e3).optional(),
|
|
653
|
+
path: z.string().min(1).optional()
|
|
635
654
|
},
|
|
636
655
|
outputSchema: { result: z.unknown() },
|
|
637
656
|
annotations: READ_ONLY
|
|
638
|
-
}, async ({ cursor = 0, limit = 100 }) => {
|
|
639
|
-
const
|
|
657
|
+
}, async ({ cursor = 0, limit = 100, path }) => {
|
|
658
|
+
const session = requireInspectSession();
|
|
659
|
+
const query = new URLSearchParams({
|
|
660
|
+
cursor: String(cursor),
|
|
661
|
+
limit: String(limit)
|
|
662
|
+
});
|
|
663
|
+
if (path) query.set("path", path);
|
|
664
|
+
const result = await requestSessionJson(session, `/api/diff/files?${query}`);
|
|
640
665
|
return textResult(JSON.stringify(result), { result });
|
|
641
666
|
});
|
|
642
667
|
server.registerTool("diff_hunks", {
|
|
643
668
|
title: "Page hunk metadata (bounded)",
|
|
644
|
-
description: "Return bounded hunk metadata for one file index. Pass generation from diff_summary to reject stale navigation. Works for web, TUI, and GitHub PR sessions.",
|
|
669
|
+
description: "Return bounded hunk metadata for one file. Pass path (glob resolving to exactly one file) or file (global index), not both. Pass generation from diff_summary to reject stale navigation. Works for web, TUI, and GitHub PR sessions.",
|
|
645
670
|
inputSchema: {
|
|
646
|
-
file: z.number().int().nonnegative(),
|
|
671
|
+
file: z.number().int().nonnegative().optional(),
|
|
672
|
+
path: z.string().min(1).optional(),
|
|
647
673
|
generation: z.number().int().nonnegative().optional(),
|
|
648
674
|
cursor: z.number().int().nonnegative().optional(),
|
|
649
675
|
limit: z.number().int().positive().max(1e3).optional()
|
|
650
676
|
},
|
|
651
677
|
outputSchema: { result: z.unknown() },
|
|
652
678
|
annotations: READ_ONLY
|
|
653
|
-
}, async ({ file, generation, cursor = 0, limit = 100 }) => {
|
|
679
|
+
}, async ({ file, path, generation, cursor = 0, limit = 100 }) => {
|
|
654
680
|
const session = requireInspectSession();
|
|
655
681
|
const query = new URLSearchParams({
|
|
656
|
-
file: String(file),
|
|
657
682
|
cursor: String(cursor),
|
|
658
683
|
limit: String(limit)
|
|
659
684
|
});
|
|
685
|
+
if (file !== void 0) query.set("file", String(file));
|
|
686
|
+
if (path) query.set("path", path);
|
|
660
687
|
if (generation !== void 0) query.set("generation", String(generation));
|
|
661
688
|
const result = await requestSessionJson(session, `/api/diff/hunks?${query}`);
|
|
662
689
|
return textResult(JSON.stringify(result), { result });
|
|
663
690
|
});
|
|
664
691
|
server.registerTool("diff_slice", {
|
|
665
692
|
title: "Read a bounded diff slice",
|
|
666
|
-
description: "Read exact logical rows for one file with strict line and byte budgets; use nextRow to continue. Works for web, TUI, and GitHub PR sessions. Prefer this over get_diff.",
|
|
693
|
+
description: "Read exact logical rows for one file with strict line and byte budgets; use nextRow to continue. Pass path (glob resolving to exactly one file) or file (global index), not both. Works for web, TUI, and GitHub PR sessions. Prefer this over get_diff.",
|
|
667
694
|
inputSchema: {
|
|
668
|
-
file: z.number().int().nonnegative(),
|
|
695
|
+
file: z.number().int().nonnegative().optional(),
|
|
696
|
+
path: z.string().min(1).optional(),
|
|
669
697
|
start: z.number().int().nonnegative().optional(),
|
|
670
698
|
generation: z.number().int().nonnegative().optional(),
|
|
671
699
|
maxLines: z.number().int().positive().max(1e3).optional(),
|
|
@@ -673,23 +701,25 @@ function createMcpServer(options) {
|
|
|
673
701
|
},
|
|
674
702
|
outputSchema: { result: z.unknown() },
|
|
675
703
|
annotations: READ_ONLY
|
|
676
|
-
}, async ({ file, start = 0, generation, maxLines = 120, maxBytes = 256 * 1024 }) => {
|
|
704
|
+
}, async ({ file, path, start = 0, generation, maxLines = 120, maxBytes = 256 * 1024 }) => {
|
|
677
705
|
const session = requireInspectSession();
|
|
678
706
|
const query = new URLSearchParams({
|
|
679
|
-
file: String(file),
|
|
680
707
|
start: String(start),
|
|
681
708
|
maxLines: String(maxLines),
|
|
682
709
|
maxBytes: String(maxBytes)
|
|
683
710
|
});
|
|
711
|
+
if (file !== void 0) query.set("file", String(file));
|
|
712
|
+
if (path) query.set("path", path);
|
|
684
713
|
if (generation !== void 0) query.set("generation", String(generation));
|
|
685
714
|
const result = await requestSessionJson(session, `/api/diff/slice?${query}`);
|
|
686
715
|
return textResult(JSON.stringify(result), { result });
|
|
687
716
|
});
|
|
688
717
|
server.registerTool("diff_search", {
|
|
689
718
|
title: "Search the active diff (bounded)",
|
|
690
|
-
description: "Search changed paths and content with bounded hits/bytes and generation-safe continuation coordinates. Works for web, TUI, and GitHub PR sessions.",
|
|
719
|
+
description: "Search changed paths and content with bounded hits/bytes and generation-safe continuation coordinates. Optional path glob limits hits to matching files (in addition to file+row continuation). Works for web, TUI, and GitHub PR sessions.",
|
|
691
720
|
inputSchema: {
|
|
692
721
|
query: z.string().min(1),
|
|
722
|
+
path: z.string().min(1).optional(),
|
|
693
723
|
generation: z.number().int().nonnegative().optional(),
|
|
694
724
|
file: z.number().int().nonnegative().optional(),
|
|
695
725
|
row: z.number().int().nonnegative().optional(),
|
|
@@ -698,7 +728,7 @@ function createMcpServer(options) {
|
|
|
698
728
|
},
|
|
699
729
|
outputSchema: { result: z.unknown() },
|
|
700
730
|
annotations: READ_ONLY
|
|
701
|
-
}, async ({ query, generation, file = 0, row = 0, limit = 100, maxBytes = 256 * 1024 }) => {
|
|
731
|
+
}, async ({ query, path, generation, file = 0, row = 0, limit = 100, maxBytes = 256 * 1024 }) => {
|
|
702
732
|
const session = requireInspectSession();
|
|
703
733
|
const params = new URLSearchParams({
|
|
704
734
|
q: query,
|
|
@@ -707,6 +737,7 @@ function createMcpServer(options) {
|
|
|
707
737
|
limit: String(limit),
|
|
708
738
|
maxBytes: String(maxBytes)
|
|
709
739
|
});
|
|
740
|
+
if (path) params.set("path", path);
|
|
710
741
|
if (generation !== void 0) params.set("generation", String(generation));
|
|
711
742
|
const result = await requestSessionJson(session, `/api/diff/search?${params}`);
|
|
712
743
|
return textResult(JSON.stringify(result), { result });
|
|
@@ -1476,6 +1507,375 @@ function createMcpServer(options) {
|
|
|
1476
1507
|
planId: plan.id
|
|
1477
1508
|
});
|
|
1478
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. 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
|
+
return textResult(`Submitted mockup ${mockup.id} (v${mockup.version}) at ${url}. ${MOCKUP_SUBMIT_NEXT_ACTION}`, {
|
|
1553
|
+
mockupId: mockup.id,
|
|
1554
|
+
version: mockup.version,
|
|
1555
|
+
url,
|
|
1556
|
+
nextAction: MOCKUP_SUBMIT_NEXT_ACTION
|
|
1557
|
+
});
|
|
1558
|
+
});
|
|
1559
|
+
server.registerTool("await_mockup_review", {
|
|
1560
|
+
title: "Wait for mockup review verdict",
|
|
1561
|
+
description: "Sync wait for a mockup verdict. Prefer async handoff after submit_mockup unless asked to wait.",
|
|
1562
|
+
inputSchema: { timeoutSeconds: z.number().optional() },
|
|
1563
|
+
outputSchema: {
|
|
1564
|
+
status: z.enum(["released", "timeout"]),
|
|
1565
|
+
disposition: z.enum(["continue", "park"]).optional(),
|
|
1566
|
+
decision: z.string().optional(),
|
|
1567
|
+
reviewXml: z.string().optional(),
|
|
1568
|
+
mockup: z.unknown().optional(),
|
|
1569
|
+
round: z.number().optional()
|
|
1570
|
+
},
|
|
1571
|
+
annotations: {
|
|
1572
|
+
readOnlyHint: true,
|
|
1573
|
+
idempotentHint: false
|
|
1574
|
+
}
|
|
1575
|
+
}, async ({ timeoutSeconds }, extra) => {
|
|
1576
|
+
const session = requireWebSession();
|
|
1577
|
+
const deadline = Date.now() + Math.max(1, timeoutSeconds ?? 25) * 1e3;
|
|
1578
|
+
let sinceRound = await seedMockupCursor(session, extra?.signal);
|
|
1579
|
+
while (Date.now() < deadline) {
|
|
1580
|
+
const remaining = Math.max(deadline - Date.now(), 0);
|
|
1581
|
+
const result = await requestSessionJson(session, `/api/mockup-review/await?timeoutMs=${Math.min(25e3, remaining)}&sinceRound=${sinceRound}`, { signal: extra?.signal });
|
|
1582
|
+
if (result.status === "released" && result.payload) {
|
|
1583
|
+
mockupCursor = {
|
|
1584
|
+
identity: session.identity,
|
|
1585
|
+
round: result.payload.round ?? sinceRound
|
|
1586
|
+
};
|
|
1587
|
+
return textResult(result.payload.reviewXml ?? `decision=${result.payload.decision}`, {
|
|
1588
|
+
status: "released",
|
|
1589
|
+
disposition: "continue",
|
|
1590
|
+
decision: result.payload.decision,
|
|
1591
|
+
reviewXml: result.payload.reviewXml,
|
|
1592
|
+
mockup: result.payload.mockup,
|
|
1593
|
+
round: result.payload.round
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
sinceRound = result.round ?? sinceRound;
|
|
1597
|
+
mockupCursor = {
|
|
1598
|
+
identity: session.identity,
|
|
1599
|
+
round: sinceRound
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
return textResult("Timeout waiting for mockup review. Park and retry await_mockup_review when the human is ready.", {
|
|
1603
|
+
status: "timeout",
|
|
1604
|
+
disposition: "park",
|
|
1605
|
+
round: sinceRound
|
|
1606
|
+
});
|
|
1607
|
+
});
|
|
1608
|
+
server.registerTool("list_mockups", {
|
|
1609
|
+
title: "List mockups",
|
|
1610
|
+
description: "List submitted HTML mockups for this repository.",
|
|
1611
|
+
inputSchema: {},
|
|
1612
|
+
outputSchema: { mockups: z.array(z.unknown()) },
|
|
1613
|
+
annotations: {
|
|
1614
|
+
readOnlyHint: true,
|
|
1615
|
+
idempotentHint: true
|
|
1616
|
+
}
|
|
1617
|
+
}, async () => {
|
|
1618
|
+
const all = await requestBaseJson("/api/mockups");
|
|
1619
|
+
return textResult(`${all.length} mockup(s)`, { mockups: all });
|
|
1620
|
+
});
|
|
1621
|
+
server.registerTool("get_mockup", {
|
|
1622
|
+
title: "Get a mockup",
|
|
1623
|
+
description: "Fetch one mockup by id, including comments and screens.",
|
|
1624
|
+
inputSchema: { mockupId: z.string().min(1) },
|
|
1625
|
+
outputSchema: { mockup: z.unknown() },
|
|
1626
|
+
annotations: {
|
|
1627
|
+
readOnlyHint: true,
|
|
1628
|
+
idempotentHint: true
|
|
1629
|
+
}
|
|
1630
|
+
}, async ({ mockupId }) => {
|
|
1631
|
+
const mockup = await requestBaseJson(`/api/mockups/${encodeURIComponent(mockupId)}`);
|
|
1632
|
+
return textResult(formatMockupReview(mockup), { mockup });
|
|
1633
|
+
});
|
|
1634
|
+
server.registerTool("get_mockup_versions", {
|
|
1635
|
+
title: "List mockup versions",
|
|
1636
|
+
description: "List version snapshots for a mockup.",
|
|
1637
|
+
inputSchema: { mockupId: z.string().min(1) },
|
|
1638
|
+
outputSchema: { versions: z.array(z.unknown()) },
|
|
1639
|
+
annotations: {
|
|
1640
|
+
readOnlyHint: true,
|
|
1641
|
+
idempotentHint: true
|
|
1642
|
+
}
|
|
1643
|
+
}, async ({ mockupId }) => {
|
|
1644
|
+
const versions = await requestBaseJson(`/api/mockups/${encodeURIComponent(mockupId)}/versions`);
|
|
1645
|
+
return textResult(`${versions.length} version(s)`, { versions });
|
|
1646
|
+
});
|
|
1647
|
+
server.registerTool("get_mockup_version", {
|
|
1648
|
+
title: "Get a mockup version",
|
|
1649
|
+
description: "Fetch one historical mockup version.",
|
|
1650
|
+
inputSchema: {
|
|
1651
|
+
mockupId: z.string().min(1),
|
|
1652
|
+
version: z.number().int().positive()
|
|
1653
|
+
},
|
|
1654
|
+
outputSchema: {
|
|
1655
|
+
version: z.unknown(),
|
|
1656
|
+
mockup: z.unknown()
|
|
1657
|
+
},
|
|
1658
|
+
annotations: {
|
|
1659
|
+
readOnlyHint: true,
|
|
1660
|
+
idempotentHint: true
|
|
1661
|
+
}
|
|
1662
|
+
}, async ({ mockupId, version }) => {
|
|
1663
|
+
const data = await requestBaseJson(`/api/mockups/${encodeURIComponent(mockupId)}/versions/${version}`);
|
|
1664
|
+
return textResult(`v${version}`, data);
|
|
1665
|
+
});
|
|
1666
|
+
async function findMockupForComment(commentId) {
|
|
1667
|
+
const mockup = (await requestBaseJson("/api/mockups?include=comments")).find((m) => (m.comments ?? []).some((c) => c.id === commentId));
|
|
1668
|
+
if (!mockup) throw new Error(`Mockup comment ${commentId} was not found.`);
|
|
1669
|
+
return mockup;
|
|
1670
|
+
}
|
|
1671
|
+
server.registerTool("reply_to_mockup_comment", {
|
|
1672
|
+
title: "Reply to a mockup comment",
|
|
1673
|
+
description: "Post an agent reply on a mockup comment thread.",
|
|
1674
|
+
inputSchema: {
|
|
1675
|
+
commentId: z.string().min(1),
|
|
1676
|
+
body: z.string().min(1),
|
|
1677
|
+
model: z.string().optional()
|
|
1678
|
+
},
|
|
1679
|
+
outputSchema: { ok: z.boolean() },
|
|
1680
|
+
annotations: {
|
|
1681
|
+
readOnlyHint: false,
|
|
1682
|
+
idempotentHint: false
|
|
1683
|
+
}
|
|
1684
|
+
}, async ({ commentId, body, model }) => {
|
|
1685
|
+
const mockup = await findMockupForComment(commentId);
|
|
1686
|
+
await requestBaseJson(`/api/mockups/${encodeURIComponent(mockup.id)}/comments/${encodeURIComponent(commentId)}/replies`, {
|
|
1687
|
+
method: "POST",
|
|
1688
|
+
headers: { "Content-Type": "application/json" },
|
|
1689
|
+
body: JSON.stringify({
|
|
1690
|
+
body,
|
|
1691
|
+
model,
|
|
1692
|
+
role: "agent"
|
|
1693
|
+
})
|
|
1694
|
+
});
|
|
1695
|
+
return textResult(`Replied to ${commentId}`, { ok: true });
|
|
1696
|
+
});
|
|
1697
|
+
server.registerTool("resolve_mockup_comment", {
|
|
1698
|
+
title: "Resolve a mockup comment",
|
|
1699
|
+
description: "Mark a mockup comment thread resolved.",
|
|
1700
|
+
inputSchema: { commentId: z.string().min(1) },
|
|
1701
|
+
outputSchema: { ok: z.boolean() },
|
|
1702
|
+
annotations: {
|
|
1703
|
+
readOnlyHint: false,
|
|
1704
|
+
idempotentHint: false
|
|
1705
|
+
}
|
|
1706
|
+
}, async ({ commentId }) => {
|
|
1707
|
+
const mockup = await findMockupForComment(commentId);
|
|
1708
|
+
await requestBaseJson(`/api/mockups/${encodeURIComponent(mockup.id)}/comments/${encodeURIComponent(commentId)}`, {
|
|
1709
|
+
method: "PUT",
|
|
1710
|
+
headers: { "Content-Type": "application/json" },
|
|
1711
|
+
body: JSON.stringify({ status: "resolved" })
|
|
1712
|
+
});
|
|
1713
|
+
return textResult(`Resolved ${commentId}`, { ok: true });
|
|
1714
|
+
});
|
|
1715
|
+
server.registerTool("inspect_mockup", {
|
|
1716
|
+
title: "Inspect a mockup (bounded)",
|
|
1717
|
+
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.",
|
|
1718
|
+
inputSchema: {
|
|
1719
|
+
mockupId: z.string().min(1),
|
|
1720
|
+
view: z.enum([
|
|
1721
|
+
"summary",
|
|
1722
|
+
"comments",
|
|
1723
|
+
"comment",
|
|
1724
|
+
"screen"
|
|
1725
|
+
]).default("summary"),
|
|
1726
|
+
status: z.enum(["open", "resolved"]).optional(),
|
|
1727
|
+
screenId: z.string().optional(),
|
|
1728
|
+
viewport: z.enum([
|
|
1729
|
+
"desktop",
|
|
1730
|
+
"tablet",
|
|
1731
|
+
"mobile"
|
|
1732
|
+
]).optional(),
|
|
1733
|
+
version: z.number().int().positive().optional(),
|
|
1734
|
+
commentId: z.string().optional(),
|
|
1735
|
+
cursor: z.number().int().nonnegative().optional(),
|
|
1736
|
+
limit: z.number().int().positive().max(200).optional(),
|
|
1737
|
+
context: z.enum([
|
|
1738
|
+
"none",
|
|
1739
|
+
"anchor",
|
|
1740
|
+
"source"
|
|
1741
|
+
]).default("anchor")
|
|
1742
|
+
},
|
|
1743
|
+
outputSchema: { data: z.unknown() },
|
|
1744
|
+
annotations: READ_ONLY
|
|
1745
|
+
}, async ({ mockupId, view, status, screenId, viewport, version, commentId, cursor, limit, context }) => {
|
|
1746
|
+
const params = new URLSearchParams();
|
|
1747
|
+
params.set("view", view);
|
|
1748
|
+
if (status) params.set("status", status);
|
|
1749
|
+
if (screenId) params.set("screen", screenId);
|
|
1750
|
+
if (viewport) params.set("viewport", viewport);
|
|
1751
|
+
if (version !== void 0) params.set("version", String(version));
|
|
1752
|
+
if (commentId) params.set("id", commentId);
|
|
1753
|
+
if (cursor !== void 0) params.set("cursor", String(cursor));
|
|
1754
|
+
if (limit !== void 0) params.set("limit", String(limit));
|
|
1755
|
+
params.set("context", context);
|
|
1756
|
+
const data = await requestBaseJson(`/api/mockups/${encodeURIComponent(mockupId)}/inspect?${params.toString()}`);
|
|
1757
|
+
return textResult(`inspect_mockup ${view} for ${mockupId}`, { data });
|
|
1758
|
+
});
|
|
1759
|
+
server.registerTool("revise_mockup", {
|
|
1760
|
+
title: "Revise a mockup screen",
|
|
1761
|
+
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.",
|
|
1762
|
+
inputSchema: {
|
|
1763
|
+
mockupId: z.string().min(1),
|
|
1764
|
+
op: z.enum([
|
|
1765
|
+
"upsert",
|
|
1766
|
+
"remove",
|
|
1767
|
+
"patch"
|
|
1768
|
+
]),
|
|
1769
|
+
screenId: z.string().min(1),
|
|
1770
|
+
html: z.string().optional(),
|
|
1771
|
+
label: z.string().optional(),
|
|
1772
|
+
expectedText: z.string().optional(),
|
|
1773
|
+
replacement: z.string().optional(),
|
|
1774
|
+
expectedVersion: z.number().int().positive().optional()
|
|
1775
|
+
},
|
|
1776
|
+
outputSchema: {
|
|
1777
|
+
mockupId: z.string(),
|
|
1778
|
+
version: z.number(),
|
|
1779
|
+
screenIds: z.array(z.string()),
|
|
1780
|
+
occurrences: z.number().optional()
|
|
1781
|
+
},
|
|
1782
|
+
annotations: MUTATING
|
|
1783
|
+
}, async ({ mockupId, op, screenId, html, label, expectedText, replacement, expectedVersion }) => {
|
|
1784
|
+
const headers = { "Content-Type": "application/json" };
|
|
1785
|
+
const base = `/api/mockups/${encodeURIComponent(mockupId)}/screens/${encodeURIComponent(screenId)}`;
|
|
1786
|
+
if (op === "upsert") {
|
|
1787
|
+
if (!html) throw new Error("revise_mockup op=upsert requires html");
|
|
1788
|
+
const mockup = await requestBaseJson(base, {
|
|
1789
|
+
method: "PUT",
|
|
1790
|
+
headers,
|
|
1791
|
+
body: JSON.stringify({
|
|
1792
|
+
html,
|
|
1793
|
+
label,
|
|
1794
|
+
expectedVersion
|
|
1795
|
+
})
|
|
1796
|
+
});
|
|
1797
|
+
return textResult(`Upserted screen ${screenId} on ${mockupId} → v${mockup.version}`, {
|
|
1798
|
+
mockupId,
|
|
1799
|
+
version: mockup.version,
|
|
1800
|
+
screenIds: mockup.screens.map((s) => s.id)
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1803
|
+
if (op === "remove") {
|
|
1804
|
+
const mockup = await requestBaseJson(`${base}${expectedVersion !== void 0 ? `?expectedVersion=${expectedVersion}` : ""}`, { method: "DELETE" });
|
|
1805
|
+
return textResult(`Removed screen ${screenId} from ${mockupId} → v${mockup.version}`, {
|
|
1806
|
+
mockupId,
|
|
1807
|
+
version: mockup.version,
|
|
1808
|
+
screenIds: mockup.screens.map((s) => s.id)
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
if (expectedText === void 0 || replacement === void 0) throw new Error("revise_mockup op=patch requires expectedText and replacement");
|
|
1812
|
+
const data = await requestBaseJson(base, {
|
|
1813
|
+
method: "PATCH",
|
|
1814
|
+
headers,
|
|
1815
|
+
body: JSON.stringify({
|
|
1816
|
+
expectedText,
|
|
1817
|
+
replacement,
|
|
1818
|
+
expectedVersion
|
|
1819
|
+
})
|
|
1820
|
+
});
|
|
1821
|
+
return textResult(`Patched ${screenId} on ${mockupId} (${data.occurrences} exact match(es)) → v${data.mockup.version}`, {
|
|
1822
|
+
mockupId,
|
|
1823
|
+
version: data.mockup.version,
|
|
1824
|
+
screenIds: data.mockup.screens.map((s) => s.id),
|
|
1825
|
+
occurrences: data.occurrences
|
|
1826
|
+
});
|
|
1827
|
+
});
|
|
1828
|
+
const threadOpSchema = z.discriminatedUnion("op", [
|
|
1829
|
+
z.object({
|
|
1830
|
+
op: z.literal("reply"),
|
|
1831
|
+
commentId: z.string().min(1),
|
|
1832
|
+
body: z.string().min(1),
|
|
1833
|
+
role: z.string().optional(),
|
|
1834
|
+
model: z.string().optional()
|
|
1835
|
+
}),
|
|
1836
|
+
z.object({
|
|
1837
|
+
op: z.literal("edit"),
|
|
1838
|
+
commentId: z.string().min(1),
|
|
1839
|
+
replyId: z.string().optional(),
|
|
1840
|
+
body: z.string().min(1)
|
|
1841
|
+
}),
|
|
1842
|
+
z.object({
|
|
1843
|
+
op: z.literal("delete"),
|
|
1844
|
+
commentId: z.string().min(1),
|
|
1845
|
+
replyId: z.string().optional()
|
|
1846
|
+
}),
|
|
1847
|
+
z.object({
|
|
1848
|
+
op: z.literal("resolve"),
|
|
1849
|
+
commentId: z.string().min(1)
|
|
1850
|
+
}),
|
|
1851
|
+
z.object({
|
|
1852
|
+
op: z.literal("unresolve"),
|
|
1853
|
+
commentId: z.string().min(1)
|
|
1854
|
+
})
|
|
1855
|
+
]);
|
|
1856
|
+
server.registerTool("update_mockup_threads", {
|
|
1857
|
+
title: "Update mockup threads (atomic batch)",
|
|
1858
|
+
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.",
|
|
1859
|
+
inputSchema: {
|
|
1860
|
+
mockupId: z.string().min(1),
|
|
1861
|
+
operations: z.array(threadOpSchema).min(1)
|
|
1862
|
+
},
|
|
1863
|
+
outputSchema: {
|
|
1864
|
+
applied: z.number(),
|
|
1865
|
+
results: z.array(z.unknown())
|
|
1866
|
+
},
|
|
1867
|
+
annotations: MUTATING
|
|
1868
|
+
}, async ({ mockupId, operations }) => {
|
|
1869
|
+
const data = await requestBaseJson(`/api/mockups/${encodeURIComponent(mockupId)}/threads/batch`, {
|
|
1870
|
+
method: "POST",
|
|
1871
|
+
headers: { "Content-Type": "application/json" },
|
|
1872
|
+
body: JSON.stringify({ operations })
|
|
1873
|
+
});
|
|
1874
|
+
return textResult(`Applied ${data.applied} thread op(s) to ${mockupId}`, {
|
|
1875
|
+
applied: data.applied,
|
|
1876
|
+
results: data.results
|
|
1877
|
+
});
|
|
1878
|
+
});
|
|
1479
1879
|
server.registerResource("agent-guide", "diffing://agent-guide", {
|
|
1480
1880
|
title: "diffing agent guide",
|
|
1481
1881
|
description: "Portable quick reference for the native code-review and plan-review workflows.",
|
|
@@ -1506,6 +1906,13 @@ This MCP connection is immutably bound to \`${repoRoot}\`.
|
|
|
1506
1906
|
4. On \`changes-requested\`, revise and resubmit with the same \`planId\`.
|
|
1507
1907
|
5. On \`approved\`, proceed; on \`rejected\`, stop.
|
|
1508
1908
|
|
|
1909
|
+
## Mockup review
|
|
1910
|
+
1. Call \`submit_mockup\`, share the URL, and park unless asked to wait; resubmit with the same \`mockupId\` to bump the version.
|
|
1911
|
+
2. On resume or sync wait, call \`await_mockup_review\` (or \`list_mockups\` / \`get_mockup\`).
|
|
1912
|
+
3. For bounded reads prefer \`inspect_mockup\` (view=summary/comments/comment/screen, filters by status/screenId/viewport/version).
|
|
1913
|
+
4. To revise one screen use \`revise_mockup\` (upsert/remove/patch with \`expectedVersion\` guard); thread ops go through atomic \`update_mockup_threads\`.
|
|
1914
|
+
5. On \`approved\`, implement; on \`changes-requested\`, revise and resubmit; on \`rejected\`, stop.
|
|
1915
|
+
|
|
1509
1916
|
## GitHub PR
|
|
1510
1917
|
1. In \`gh-pr\` mode call \`gh_overview\`, then the bounded diff tools.
|
|
1511
1918
|
2. Fetch published discussion with \`gh_list_threads\` (prefer unresolved) and \`gh_list_reviews\`.
|