diffing 0.10.0 → 0.10.2

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 (152) hide show
  1. package/dist/{cli-agent-CLrK54k2.mjs → cli-agent-CV0k1pXp.mjs} +10 -8
  2. package/dist/cli-gh-C2LAp12S.mjs +313 -0
  3. package/dist/cli.mjs +3 -3
  4. package/dist/client/assets/{angular-html-DdAR2u-k.js → angular-html-CV0Ak818.js} +1 -1
  5. package/dist/client/assets/{angular-ts-Dz_vWKSu.js → angular-ts-2e8zQ7to.js} +1 -1
  6. package/dist/client/assets/{apl-BJ5QXrIk.js → apl-ByVy2dfo.js} +1 -1
  7. package/dist/client/assets/{arc-D9erSW6x.js → arc--R8kldOB.js} +1 -1
  8. package/dist/client/assets/{architectureDiagram-3BPJPVTR-CCQWZLXR.js → architectureDiagram-3BPJPVTR-BWbm1lrV.js} +1 -1
  9. package/dist/client/assets/{astro-BQAej5a5.js → astro-CE7ySGnq.js} +1 -1
  10. package/dist/client/assets/{blade-DkAmuwDx.js → blade-PJJaH6RK.js} +1 -1
  11. package/dist/client/assets/{blockDiagram-GPEHLZMM-vwjxNybI.js → blockDiagram-GPEHLZMM-BdfBhnvZ.js} +1 -1
  12. package/dist/client/assets/{c-DG5p-ChJ.js → c-BhDzEkUq.js} +1 -1
  13. package/dist/client/assets/{c4Diagram-AAUBKEIU-DJ_hVlh4.js → c4Diagram-AAUBKEIU-Bu_tfvPY.js} +1 -1
  14. package/dist/client/assets/channel-DSEnsd3l.js +1 -0
  15. package/dist/client/assets/{chunk-2J33WTMH-CiLowxCo.js → chunk-2J33WTMH-CTGsTtve.js} +1 -1
  16. package/dist/client/assets/{chunk-3OPIFGDE-H3jjMR5Y.js → chunk-3OPIFGDE-BODO_5hx.js} +1 -1
  17. package/dist/client/assets/{chunk-4BX2VUAB-Dk0dk4n5.js → chunk-4BX2VUAB-BTsyFV0Y.js} +1 -1
  18. package/dist/client/assets/{chunk-55IACEB6-nbLiUMJS.js → chunk-55IACEB6-DnrCm6LC.js} +1 -1
  19. package/dist/client/assets/{chunk-5ZQYHXKU-DZ8RgHg8.js → chunk-5ZQYHXKU-DBjF0sdg.js} +1 -1
  20. package/dist/client/assets/{chunk-727SXJPM-DDe5dqJV.js → chunk-727SXJPM-Do27Z5ji.js} +1 -1
  21. package/dist/client/assets/{chunk-AQP2D5EJ-DmpNclOr.js → chunk-AQP2D5EJ-CTnpneGA.js} +1 -1
  22. package/dist/client/assets/{chunk-BSJP7CBP-BKQKvtIj.js → chunk-BSJP7CBP-Kr0_rIcG.js} +1 -1
  23. package/dist/client/assets/{chunk-CSCIHK7Q-D27t4cyv.js → chunk-CSCIHK7Q-vYCYu98G.js} +1 -1
  24. package/dist/client/assets/{chunk-FMBD7UC4-Bu3LA663.js → chunk-FMBD7UC4-D325zkwE.js} +1 -1
  25. package/dist/client/assets/{chunk-KSCS5N6A-Ed5HNXRW.js → chunk-KSCS5N6A-Byf7HmWv.js} +1 -1
  26. package/dist/client/assets/{chunk-L5ZTLDWV-DLjJZgf8.js → chunk-L5ZTLDWV-D6bS5hm3.js} +1 -1
  27. package/dist/client/assets/{chunk-LZXEDZCA-B-cs89SM.js → chunk-LZXEDZCA-BMGYcU-n.js} +2 -2
  28. package/dist/client/assets/{chunk-ND2GUHAM-D_kPSuUA.js → chunk-ND2GUHAM-xfJDzv5I.js} +1 -1
  29. package/dist/client/assets/{chunk-NZK2D7GU-D1u3pjq-.js → chunk-NZK2D7GU-DY1HNoUO.js} +1 -1
  30. package/dist/client/assets/{chunk-O5CBEL6O-Mt0cnPSf.js → chunk-O5CBEL6O-BFgywBQV.js} +1 -1
  31. package/dist/client/assets/chunk-QZHKN3VN-wL51hu-L.js +1 -0
  32. package/dist/client/assets/chunk-WU5MYG2G-DcSuLObv.js +1 -0
  33. package/dist/client/assets/{chunk-XPW4576I-Bt8a8x28.js → chunk-XPW4576I-CCOlY0K5.js} +1 -1
  34. package/dist/client/assets/classDiagram-4FO5ZUOK-Cd9P7jo-.js +1 -0
  35. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-_8XVmWNY.js +1 -0
  36. package/dist/client/assets/{cobol-Daidj_5Y.js → cobol-D5uvQocP.js} +1 -1
  37. package/dist/client/assets/{coffee-DUMdB2Cr.js → coffee-DoE1_DMC.js} +1 -1
  38. package/dist/client/assets/{cose-bilkent-S5V4N54A-DVUDLXie.js → cose-bilkent-S5V4N54A-BffMlb0t.js} +1 -1
  39. package/dist/client/assets/{cpp-C2FeX1Mc.js → cpp-CxMs3g8q.js} +1 -1
  40. package/dist/client/assets/{crystal-D4yb9blJ.js → crystal-Ck_Sab8H.js} +1 -1
  41. package/dist/client/assets/{css-B5cr0U9F.js → css-Bx3zqeyw.js} +1 -1
  42. package/dist/client/assets/{dagre-BM42HDAG-DdZWb-Ee.js → dagre-BM42HDAG-DuK7JkP6.js} +1 -1
  43. package/dist/client/assets/{diagram-2AECGRRQ-C9DOZt92.js → diagram-2AECGRRQ-DW9W2wY6.js} +1 -1
  44. package/dist/client/assets/{diagram-5GNKFQAL-Bbc07UKr.js → diagram-5GNKFQAL-CfmdY-KT.js} +1 -1
  45. package/dist/client/assets/{diagram-KO2AKTUF-C-lUzY3m.js → diagram-KO2AKTUF-C3W0AVns.js} +1 -1
  46. package/dist/client/assets/{diagram-LMA3HP47-D-OjEFhp.js → diagram-LMA3HP47-Jxoj1EBH.js} +1 -1
  47. package/dist/client/assets/{diagram-OG6HWLK6-BjtWGvrf.js → diagram-OG6HWLK6-DKZXYFSu.js} +1 -1
  48. package/dist/client/assets/{dist-BXZygPdX.js → dist-Cxgi_w93.js} +1 -1
  49. package/dist/client/assets/{edge-RCQWcWNp.js → edge-D7XVJcl7.js} +1 -1
  50. package/dist/client/assets/{elixir-DwKyVg2X.js → elixir-DLQjwJ9Q.js} +1 -1
  51. package/dist/client/assets/{elm-DjR33IVs.js → elm-CP5Ox5XO.js} +1 -1
  52. package/dist/client/assets/{erDiagram-TEJ5UH35-C5T4ywLG.js → erDiagram-TEJ5UH35-BGd46oGi.js} +1 -1
  53. package/dist/client/assets/{erb-BnhUH1Hk.js → erb-C97aW-va.js} +1 -1
  54. package/dist/client/assets/{flowDiagram-I6XJVG4X-BJmjm7Kx.js → flowDiagram-I6XJVG4X-D6MFCwKy.js} +1 -1
  55. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DREPnzu5.js → ganttDiagram-6RSMTGT7-i1RH9KFv.js} +1 -1
  56. package/dist/client/assets/{git-rebase-DzXoSDXA.js → git-rebase-B4iN-h71.js} +1 -1
  57. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DIR4G_-_.js → gitGraphDiagram-PVQCEYII-DiLnrGe6.js} +1 -1
  58. package/dist/client/assets/{glimmer-js-HKnZpCn_.js → glimmer-js-DVkEnHRb.js} +1 -1
  59. package/dist/client/assets/{glimmer-ts-DedV7rWs.js → glimmer-ts-CyUzWW2D.js} +1 -1
  60. package/dist/client/assets/{glsl-Bz-0Alp2.js → glsl-5XFOCOCu.js} +1 -1
  61. package/dist/client/assets/{graphql-DSKkDYCD.js → graphql-IoOZHuWD.js} +1 -1
  62. package/dist/client/assets/{hack-CnaHZm8k.js → hack-BUQ8LqBu.js} +1 -1
  63. package/dist/client/assets/{haml-huGFYKlj.js → haml-BIbv0YDp.js} +1 -1
  64. package/dist/client/assets/{handlebars-CKmgzfWY.js → handlebars-9QihiA8n.js} +1 -1
  65. package/dist/client/assets/{html-B9Ih_dUU.js → html-5bjcBKb2.js} +1 -1
  66. package/dist/client/assets/{html-derivative-YabD-M5d.js → html-derivative-DFAt3hvS.js} +1 -1
  67. package/dist/client/assets/{http-Bdu12JYX.js → http-4_xHs8PR.js} +1 -1
  68. package/dist/client/assets/{hurl-twZzDTNs.js → hurl-B-wi-Gx2.js} +1 -1
  69. package/dist/client/assets/{index-BktdZRWB.js → index-CHvT3DR9.js} +10 -10
  70. package/dist/client/assets/{infoDiagram-5YYISTIA-D2ty2R-i.js → infoDiagram-5YYISTIA-Dy1M2f6k.js} +1 -1
  71. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-Ccnc9xlE.js → ishikawaDiagram-YF4QCWOH-J9qG2PlR.js} +1 -1
  72. package/dist/client/assets/{java-_5trO-p9.js → java-D9isH882.js} +1 -1
  73. package/dist/client/assets/{javascript-BOQn0ikE.js → javascript-Dj8e4yT5.js} +1 -1
  74. package/dist/client/assets/{jinja-BgY-0IBz.js → jinja-Bkare_Hv.js} +1 -1
  75. package/dist/client/assets/{jison-BLNTmT1_.js → jison-DDIUNnOW.js} +1 -1
  76. package/dist/client/assets/{journeyDiagram-JHISSGLW-W7eWXlw0.js → journeyDiagram-JHISSGLW-Do4kGeyR.js} +1 -1
  77. package/dist/client/assets/{json-BLbH8rZf.js → json-Bi84FPdJ.js} +1 -1
  78. package/dist/client/assets/{jsx-CxteptsJ.js → jsx-DI-NKxp6.js} +1 -1
  79. package/dist/client/assets/{julia-B__Ncp_z.js → julia-Yhlqb4JO.js} +1 -1
  80. package/dist/client/assets/{just-DBsHUGW1.js → just-B8KISqsZ.js} +1 -1
  81. package/dist/client/assets/{kanban-definition-UN3LZRKU-Z_AFL-rd.js → kanban-definition-UN3LZRKU-BSeQxfa9.js} +1 -1
  82. package/dist/client/assets/{latex-DFF_0wxo.js → latex-CkYf4HIK.js} +1 -1
  83. package/dist/client/assets/{line-aAv8TyOs.js → line-C2Za_tgK.js} +1 -1
  84. package/dist/client/assets/{linear-BOvEZKH8.js → linear-CRm3pgdR.js} +1 -1
  85. package/dist/client/assets/{liquid-kDWCO6d7.js → liquid-y_Utf1BC.js} +1 -1
  86. package/dist/client/assets/{lua-CYTFayq-.js → lua-C4gbVDKd.js} +1 -1
  87. package/dist/client/assets/{marko-Dpmz3Pdx.js → marko-BlcQZIyX.js} +1 -1
  88. package/dist/client/assets/{mdc-BsxHyM1i.js → mdc-C6KkSPM_.js} +1 -1
  89. package/dist/client/assets/{mermaid-parser.core-D8z_CWL1.js → mermaid-parser.core-B9mHNH7g.js} +1 -1
  90. package/dist/client/assets/{mermaid.core-DNKchnly.js → mermaid.core-pkJSRcFi.js} +3 -3
  91. package/dist/client/assets/{mindmap-definition-RKZ34NQL-xQKMV4Kw.js → mindmap-definition-RKZ34NQL-BAW_jZIg.js} +1 -1
  92. package/dist/client/assets/{nginx-CcdTDme-.js → nginx-BGbv5z-H.js} +1 -1
  93. package/dist/client/assets/{nim-B5wWDskL.js → nim-BtqaozVV.js} +1 -1
  94. package/dist/client/assets/{perl-Ce2_iKLz.js → perl-BaPVFzDG.js} +1 -1
  95. package/dist/client/assets/{php-DuLCFERd.js → php-CUSmmFXP.js} +1 -1
  96. package/dist/client/assets/{pieDiagram-4H26LBE5-CBSCJnUr.js → pieDiagram-4H26LBE5-DHTW5bo3.js} +1 -1
  97. package/dist/client/assets/{pug-BfQJoQj1.js → pug-ComryLhI.js} +1 -1
  98. package/dist/client/assets/{qml-DYeovlVl.js → qml-D4-pUDzN.js} +1 -1
  99. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-uEcgb0Rm.js → quadrantDiagram-W4KKPZXB-oE0LSIIu.js} +1 -1
  100. package/dist/client/assets/{r-DgHSP56L.js → r-IEiS2KmQ.js} +1 -1
  101. package/dist/client/assets/{razor-B00x7W-i.js → razor-A3NOQ-6o.js} +1 -1
  102. package/dist/client/assets/{regexp-CmWVupmd.js → regexp-Dnh3EraB.js} +1 -1
  103. package/dist/client/assets/{requirementDiagram-4Y6WPE33-Q4yY_ALe.js → requirementDiagram-4Y6WPE33-CAnOHDxz.js} +1 -1
  104. package/dist/client/assets/{review-export-pcqDKNqx.js → review-export-D-wScR31.js} +1 -1
  105. package/dist/client/assets/{rst-B3VARbTX.js → rst-DDcT8Cwj.js} +1 -1
  106. package/dist/client/assets/{ruby-CaxKSSbT.js → ruby-BqnC7im3.js} +1 -1
  107. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-B-GLzFGa.js → sankeyDiagram-5OEKKPKP-DARazeui.js} +1 -1
  108. package/dist/client/assets/{sas-Ue0rHUIk.js → sas-BsVPIslL.js} +1 -1
  109. package/dist/client/assets/{scss-BJJ7_L6c.js → scss-Cs-F21hy.js} +1 -1
  110. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DW0em0D_.js → sequenceDiagram-3UESZ5HK-BYAKokia.js} +1 -1
  111. package/dist/client/assets/{shellscript-C6LkZs1u.js → shellscript-CuqRpwp7.js} +1 -1
  112. package/dist/client/assets/{shellsession-CkZu1e84.js → shellsession-BT4CTJst.js} +1 -1
  113. package/dist/client/assets/{soy-CDaZV57J.js → soy-Njc_erP7.js} +1 -1
  114. package/dist/client/assets/{sql-CLfMGH4x.js → sql-C0M8bIzv.js} +1 -1
  115. package/dist/client/assets/{src-C27zxuNO.js → src-B8u-CQt1.js} +1 -1
  116. package/dist/client/assets/{stata-B0kNMHc9.js → stata-DQWLgfGE.js} +1 -1
  117. package/dist/client/assets/{stateDiagram-AJRCARHV-CrO_D3m4.js → stateDiagram-AJRCARHV-CL6IO6Yx.js} +1 -1
  118. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CXkQIsOM.js +1 -0
  119. package/dist/client/assets/{surrealql-D9os_yZO.js → surrealql-CxnsjSMN.js} +1 -1
  120. package/dist/client/assets/{svelte-2SxVToeO.js → svelte-YiRJWdT-.js} +1 -1
  121. package/dist/client/assets/{templ-DzmZB67T.js → templ-DVHepm0I.js} +1 -1
  122. package/dist/client/assets/{tex-BMPk3nHk.js → tex-CbaeWbjN.js} +1 -1
  123. package/dist/client/assets/{timeline-definition-PNZ67QCA-C9FBevHv.js → timeline-definition-PNZ67QCA-BAKLMRXW.js} +1 -1
  124. package/dist/client/assets/{ts-tags-CG14Bplc.js → ts-tags-KadJLbnQ.js} +1 -1
  125. package/dist/client/assets/{tsx-D6NK461A.js → tsx-DgmkmSe3.js} +1 -1
  126. package/dist/client/assets/{twig-Agh0j5sD.js → twig-BG7aoIMU.js} +1 -1
  127. package/dist/client/assets/{typescript-_AkeziZ2.js → typescript-C_w_yccd.js} +1 -1
  128. package/dist/client/assets/{vennDiagram-CIIHVFJN-2H9HfhhR.js → vennDiagram-CIIHVFJN-CCRx_JXh.js} +1 -1
  129. package/dist/client/assets/{vue-C1z3QGG9.js → vue-DXnUyZR0.js} +1 -1
  130. package/dist/client/assets/{vue-html-C1RMzLZk.js → vue-html-CetUfade.js} +1 -1
  131. package/dist/client/assets/{vue-vine-oOixA6ge.js → vue-vine-CSW5RjCH.js} +1 -1
  132. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-BJxrQhTf.js → wardleyDiagram-YWT4CUSO-Buoffz1N.js} +1 -1
  133. package/dist/client/assets/{xml-okuDZ7vq.js → xml-CJgpX9jH.js} +1 -1
  134. package/dist/client/assets/{xsl-S_n3XrKb.js → xsl-BZk-xhGC.js} +1 -1
  135. package/dist/client/assets/{xychartDiagram-2RQKCTM6-BGdee7KG.js → xychartDiagram-2RQKCTM6-CEKRZKfv.js} +1 -1
  136. package/dist/client/assets/{yaml-BoX-kqX1.js → yaml-DVxl0VLy.js} +1 -1
  137. package/dist/client/index.html +1 -1
  138. package/dist/{completions-ZSKYvW06.mjs → completions-Csvfqctx.mjs} +16 -1
  139. package/dist/{doctor-CmP0u7-d.mjs → doctor-DhuZFVwO.mjs} +1 -1
  140. package/dist/{github-oCZx5aim.mjs → github-CzxbZnhh.mjs} +152 -28
  141. package/dist/github-DL7-BqhD.mjs +2 -0
  142. package/dist/{mcp-CqxXILYL.mjs → mcp-BBDdXGOZ.mjs} +212 -28
  143. package/dist/{server-CczuXSWo.mjs → server-D-gJ7owz.mjs} +947 -54
  144. package/package.json +1 -1
  145. package/dist/cli-gh-8YC1NzyM.mjs +0 -155
  146. package/dist/client/assets/channel-DmZgR6r0.js +0 -1
  147. package/dist/client/assets/chunk-QZHKN3VN-BrYnBs7h.js +0 -1
  148. package/dist/client/assets/chunk-WU5MYG2G-CdBcsQs_.js +0 -1
  149. package/dist/client/assets/classDiagram-4FO5ZUOK-CCpp3t5M.js +0 -1
  150. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-B1aV1sYb.js +0 -1
  151. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-C9KDGD7R.js +0 -1
  152. package/dist/github-BjZZcSKV.mjs +0 -2
@@ -1,4 +1,4 @@
1
- import { i as buildGitDiffArgs, o as parseDiffOptions, t as startServer } from "./server-CczuXSWo.mjs";
1
+ import { i as buildGitDiffArgs, o as parseDiffOptions, t as startServer } from "./server-D-gJ7owz.mjs";
2
2
  import { t as formatComments } from "./comment-format-B5AcGcR-.mjs";
3
3
  import { n as formatPlanReview } from "./plan-format-BQyx--mp.mjs";
4
4
  import { a as removeServerLock, i as readServerLock, n as diffScopeKey, o as removeServerLockIfOwned, r as isLockAlive, s as writeServerLock, t as acquireServerStartupLease } from "./server-lock-uqSdDYeR.mjs";
@@ -345,7 +345,7 @@ function createMcpServer(options) {
345
345
  const ownerId = options.ownerId ?? randomUUID();
346
346
  const defaultClientDir = existsSync(resolve(moduleDirectory, "client")) ? resolve(moduleDirectory, "client") : resolve(repoRoot, "dist/client");
347
347
  const clientDir = options.clientDir ?? defaultClientDir;
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, call get_diff, create_comment as needed, then await_review for the human handoff. For plan review, call submit_plan then await_plan_review. A changes-requested verdict means revise and resubmit the same planId; rejected means stop; approved means proceed. Wait tools return released or timeout with a mode; timeout is not failure and the same wait tool may be called again. All networking remains local.`;
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, then await_review for the human handoff. 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 then await_plan_review. A changes-requested verdict means revise and resubmit the same planId; rejected means stop; approved means proceed. Wait tools return released or timeout with a mode; timeout is not failure and the same wait tool may be called again. MCP connects only to the loopback diffing server; explicitly authorized GitHub tools may make the server call GitHub.`;
349
349
  const server = new McpServer({
350
350
  name: "diffing",
351
351
  version: MCP_VERSION
@@ -360,11 +360,10 @@ function createMcpServer(options) {
360
360
  if (lock.owner === "mcp" && (lock.pid !== process.pid || lock.ownerId !== ownerId)) throw new Error(`A different diffing MCP connection (${lock.pid}/${lock.ownerId ?? "legacy"}) owns this repository session. Its web server lifecycle is tied to that MCP connection; stop it or wait for it to exit, then retry.`);
361
361
  if (!isLoopbackHost(lock.host)) throw new Error(`The active diffing session is bound to non-loopback host ${JSON.stringify(lock.host)}. MCP refuses LAN/remote review URLs; end it and call start_review_session for a loopback-only session.`);
362
362
  }
363
- function requireWebSession() {
363
+ function requireAnySession() {
364
364
  const lock = liveLock();
365
- if (!lock) throw new Error(`No diffing web review session is running for ${repoRoot}. Call start_review_session, then retry this tool.`);
365
+ if (!lock) throw new Error(`No diffing review session is running for ${repoRoot}. Start one with start_review_session, \`diffing --web\`, or \`diffing "gh pr <ref>"\`, then retry.`);
366
366
  ensureReusableLock(lock);
367
- if ((lock.mode ?? "web") === "gh-pr") throw new Error("The active diffing session is a GitHub PR review, not a local review.");
368
367
  const url = lockUrl(lock);
369
368
  if (!url) throw new Error("The active diffing session does not expose a reachable loopback API.");
370
369
  return {
@@ -373,6 +372,16 @@ function createMcpServer(options) {
373
372
  identity: `${lock.pid}:${lock.startedAt}:${lock.port}:${lock.ownerId ?? ""}`
374
373
  };
375
374
  }
375
+ function requireWebSession() {
376
+ const session = requireAnySession();
377
+ if ((session.lock.mode ?? "web") === "gh-pr") throw new Error("The active diffing session is a GitHub PR review, not a local review. Use gh_* tools and bounded diff_* inspect tools, or end the PR session for local review/plan tools.");
378
+ return session;
379
+ }
380
+ function requireGhPrSession() {
381
+ const session = requireAnySession();
382
+ if ((session.lock.mode ?? "web") !== "gh-pr") throw new Error("This tool requires an active GitHub PR session (`diffing \"gh pr <ref>\"` or --gh-pr).");
383
+ return session;
384
+ }
376
385
  function requestSessionJson(session, path, init = {}) {
377
386
  const headers = new Headers(init.headers);
378
387
  if (session.lock.mode === "tui") {
@@ -387,10 +396,9 @@ function createMcpServer(options) {
387
396
  function requestBaseJson(path, init) {
388
397
  return requestSessionJson(requireWebSession(), path, init);
389
398
  }
390
- function requireTuiSession() {
391
- const session = requireWebSession();
392
- if (session.lock.mode !== "tui") throw new Error("This bounded diff-inspection tool requires an active `diffing --tui` session.");
393
- return session;
399
+ /** Bounded inspect works for web, tui, and gh-pr sessions. */
400
+ function requireInspectSession() {
401
+ return requireAnySession();
394
402
  }
395
403
  async function seedReviewCursor(session, signal) {
396
404
  if (reviewCursor?.identity === session.identity) return reviewCursor.round;
@@ -431,7 +439,7 @@ function createMcpServer(options) {
431
439
  url,
432
440
  managedBy: lock.owner === "mcp" ? "mcp" : "user",
433
441
  diffArgs: lock.diffArgs ?? [],
434
- nextAction: inaccessible ? "This session is not loopback-only, so MCP will not connect to it. End it manually and call start_review_session." : mode === "tui" ? "The native TUI agent API is available; use diff_summary/diff_files/diff_hunks/diff_slice/diff_search or review tools." : mode === "gh-pr" ? "This is a GitHub PR session; use the GitHub review workflow or start a local session after it ends." : "Call get_diff to inspect changes, or use a plan-review tool."
442
+ nextAction: inaccessible ? "This session is not loopback-only, so MCP will not connect to it. End it manually and call start_review_session." : mode === "tui" ? "Use bounded diff_summary/diff_files/diff_hunks/diff_slice/diff_search, then local review tools." : mode === "gh-pr" ? "Use gh_overview, then diff_summary→diff_files→diff_hunks/diff_slice/diff_search. For discussion use gh_list_threads (prefer unresolvedOnly). Do not fetch the full patch via get_diff." : "Prefer diff_summary→diff_files→diff_slice over get_diff. Use plan-review tools when gating implementation."
435
443
  };
436
444
  }
437
445
  server.registerTool("review_session_status", {
@@ -602,18 +610,18 @@ function createMcpServer(options) {
602
610
  return textResult(diff.patch || "(The active diff is empty.)", structured);
603
611
  });
604
612
  server.registerTool("diff_summary", {
605
- title: "Summarize the native TUI diff",
606
- description: "Return bounded totals and change-kind counts from the TUI sparse index without transferring the patch.",
613
+ title: "Summarize the active diff (bounded)",
614
+ 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.",
607
615
  inputSchema: {},
608
616
  outputSchema: { result: z.unknown() },
609
617
  annotations: READ_ONLY
610
618
  }, async () => {
611
- const result = await requestSessionJson(requireTuiSession(), "/api/diff/summary");
619
+ const result = await requestSessionJson(requireInspectSession(), "/api/diff/summary");
612
620
  return textResult(JSON.stringify(result), { result });
613
621
  });
614
622
  server.registerTool("diff_files", {
615
- title: "Page changed files from the native TUI",
616
- description: "Return a bounded page of changed-file metadata with an opaque numeric continuation cursor.",
623
+ title: "Page changed files (bounded)",
624
+ description: "Return a bounded page of changed-file metadata with an opaque numeric continuation cursor. Works for web, TUI, and GitHub PR sessions.",
617
625
  inputSchema: {
618
626
  cursor: z.number().int().nonnegative().optional(),
619
627
  limit: z.number().int().positive().max(1e3).optional()
@@ -621,12 +629,12 @@ function createMcpServer(options) {
621
629
  outputSchema: { result: z.unknown() },
622
630
  annotations: READ_ONLY
623
631
  }, async ({ cursor = 0, limit = 100 }) => {
624
- const result = await requestSessionJson(requireTuiSession(), `/api/diff/files?cursor=${cursor}&limit=${limit}`);
632
+ const result = await requestSessionJson(requireInspectSession(), `/api/diff/files?cursor=${cursor}&limit=${limit}`);
625
633
  return textResult(JSON.stringify(result), { result });
626
634
  });
627
635
  server.registerTool("diff_hunks", {
628
- title: "Page hunk metadata from the native TUI",
629
- description: "Return bounded hunk metadata for one file index. Pass generation from diff_summary to reject stale navigation.",
636
+ title: "Page hunk metadata (bounded)",
637
+ 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.",
630
638
  inputSchema: {
631
639
  file: z.number().int().nonnegative(),
632
640
  generation: z.number().int().nonnegative().optional(),
@@ -636,7 +644,7 @@ function createMcpServer(options) {
636
644
  outputSchema: { result: z.unknown() },
637
645
  annotations: READ_ONLY
638
646
  }, async ({ file, generation, cursor = 0, limit = 100 }) => {
639
- const session = requireTuiSession();
647
+ const session = requireInspectSession();
640
648
  const query = new URLSearchParams({
641
649
  file: String(file),
642
650
  cursor: String(cursor),
@@ -647,8 +655,8 @@ function createMcpServer(options) {
647
655
  return textResult(JSON.stringify(result), { result });
648
656
  });
649
657
  server.registerTool("diff_slice", {
650
- title: "Read a bounded native TUI diff slice",
651
- description: "Read exact logical rows for one file with strict line and byte budgets; use nextRow to continue.",
658
+ title: "Read a bounded diff slice",
659
+ 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.",
652
660
  inputSchema: {
653
661
  file: z.number().int().nonnegative(),
654
662
  start: z.number().int().nonnegative().optional(),
@@ -659,7 +667,7 @@ function createMcpServer(options) {
659
667
  outputSchema: { result: z.unknown() },
660
668
  annotations: READ_ONLY
661
669
  }, async ({ file, start = 0, generation, maxLines = 120, maxBytes = 256 * 1024 }) => {
662
- const session = requireTuiSession();
670
+ const session = requireInspectSession();
663
671
  const query = new URLSearchParams({
664
672
  file: String(file),
665
673
  start: String(start),
@@ -671,8 +679,8 @@ function createMcpServer(options) {
671
679
  return textResult(JSON.stringify(result), { result });
672
680
  });
673
681
  server.registerTool("diff_search", {
674
- title: "Search the native TUI diff",
675
- description: "Search changed paths and content with bounded hits/bytes and generation-safe continuation coordinates.",
682
+ title: "Search the active diff (bounded)",
683
+ description: "Search changed paths and content with bounded hits/bytes and generation-safe continuation coordinates. Works for web, TUI, and GitHub PR sessions.",
676
684
  inputSchema: {
677
685
  query: z.string().min(1),
678
686
  generation: z.number().int().nonnegative().optional(),
@@ -684,7 +692,7 @@ function createMcpServer(options) {
684
692
  outputSchema: { result: z.unknown() },
685
693
  annotations: READ_ONLY
686
694
  }, async ({ query, generation, file = 0, row = 0, limit = 100, maxBytes = 256 * 1024 }) => {
687
- const session = requireTuiSession();
695
+ const session = requireInspectSession();
688
696
  const params = new URLSearchParams({
689
697
  q: query,
690
698
  file: String(file),
@@ -696,6 +704,177 @@ function createMcpServer(options) {
696
704
  const result = await requestSessionJson(session, `/api/diff/search?${params}`);
697
705
  return textResult(JSON.stringify(result), { result });
698
706
  });
707
+ server.registerTool("gh_overview", {
708
+ title: "GitHub PR overview (slim)",
709
+ description: "Return compact PR identity, SHAs, size stats, and thread/review/draft counts without the patch or full conversation bodies. Requires an active gh-pr session.",
710
+ inputSchema: {},
711
+ outputSchema: { result: z.unknown() },
712
+ annotations: READ_ONLY
713
+ }, async () => {
714
+ const result = await requestSessionJson(requireGhPrSession(), "/api/gh/overview");
715
+ return textResult(JSON.stringify(result), { result });
716
+ });
717
+ server.registerTool("gh_list_threads", {
718
+ title: "List published PR review threads",
719
+ description: "Page published GitHub review threads with filters. Prefer unresolvedOnly=true. Bodies are truncated by default; set fullBody for complete text. format=xml for agent XML.",
720
+ inputSchema: {
721
+ unresolvedOnly: z.boolean().optional(),
722
+ path: z.string().optional(),
723
+ author: z.string().optional(),
724
+ cursor: z.number().int().nonnegative().optional(),
725
+ limit: z.number().int().positive().max(200).optional(),
726
+ bodyMaxChars: z.number().int().positive().max(5e4).optional(),
727
+ fullBody: z.boolean().optional(),
728
+ format: z.enum(["json", "xml"]).optional()
729
+ },
730
+ outputSchema: { result: z.unknown() },
731
+ annotations: READ_ONLY
732
+ }, async ({ unresolvedOnly, path, author, cursor = 0, limit = 50, bodyMaxChars = 500, fullBody, format = "json" }) => {
733
+ const session = requireGhPrSession();
734
+ const params = new URLSearchParams({
735
+ cursor: String(cursor),
736
+ limit: String(limit),
737
+ bodyMaxChars: String(bodyMaxChars),
738
+ format
739
+ });
740
+ if (unresolvedOnly) params.set("unresolvedOnly", "true");
741
+ if (path) params.set("path", path);
742
+ if (author) params.set("author", author);
743
+ if (fullBody) params.set("fullBody", "true");
744
+ if (format === "xml") {
745
+ const res = await fetch(`${session.url}/api/gh/threads?${params}`);
746
+ const text = await res.text();
747
+ if (!res.ok) throw new Error(text || res.statusText);
748
+ return textResult(text, { result: {
749
+ format: "xml",
750
+ xml: text
751
+ } });
752
+ }
753
+ const result = await requestSessionJson(session, `/api/gh/threads?${params}`);
754
+ return textResult(JSON.stringify(result), { result });
755
+ });
756
+ server.registerTool("gh_list_reviews", {
757
+ title: "List published PR review events",
758
+ description: "Page submitted GitHub review verdicts and overall comments. Bodies truncated by default.",
759
+ inputSchema: {
760
+ cursor: z.number().int().nonnegative().optional(),
761
+ limit: z.number().int().positive().max(100).optional(),
762
+ bodyMaxChars: z.number().int().positive().max(5e4).optional(),
763
+ fullBody: z.boolean().optional(),
764
+ state: z.string().optional(),
765
+ format: z.enum(["json", "xml"]).optional()
766
+ },
767
+ outputSchema: { result: z.unknown() },
768
+ annotations: READ_ONLY
769
+ }, async ({ cursor = 0, limit = 50, bodyMaxChars = 500, fullBody, state, format = "json" }) => {
770
+ const session = requireGhPrSession();
771
+ const params = new URLSearchParams({
772
+ cursor: String(cursor),
773
+ limit: String(limit),
774
+ bodyMaxChars: String(bodyMaxChars),
775
+ format
776
+ });
777
+ if (fullBody) params.set("fullBody", "true");
778
+ if (state) params.set("state", state);
779
+ if (format === "xml") {
780
+ const res = await fetch(`${session.url}/api/gh/reviews?${params}`);
781
+ const text = await res.text();
782
+ if (!res.ok) throw new Error(text || res.statusText);
783
+ return textResult(text, { result: {
784
+ format: "xml",
785
+ xml: text
786
+ } });
787
+ }
788
+ const result = await requestSessionJson(session, `/api/gh/reviews?${params}`);
789
+ return textResult(JSON.stringify(result), { result });
790
+ });
791
+ server.registerTool("gh_list_draft_comments", {
792
+ title: "List local PR draft comments",
793
+ description: "Return in-progress local draft comments for the active PR session (not published on GitHub).",
794
+ inputSchema: {},
795
+ outputSchema: { comments: z.array(commentSchema) },
796
+ annotations: READ_ONLY
797
+ }, async () => {
798
+ const comments = await requestSessionJson(requireGhPrSession(), "/api/gh/pr-session/comments");
799
+ return textResult(formatComments(comments), { comments });
800
+ });
801
+ server.registerTool("gh_create_draft_comment", {
802
+ title: "Create a local PR draft comment",
803
+ description: "Create a local draft inline comment on the active PR session. Does not publish to GitHub. Use gh_submit_review only when the user explicitly authorized publishing.",
804
+ inputSchema: {
805
+ filePath: z.string().min(1),
806
+ side: z.enum(["deletions", "additions"]),
807
+ lineNumber: z.number().int().nonnegative(),
808
+ startLineNumber: z.number().int().positive().optional(),
809
+ lineContent: z.string().optional(),
810
+ body: z.string().min(1),
811
+ severity: z.enum([
812
+ "blocking",
813
+ "nit",
814
+ "question",
815
+ "praise",
816
+ "none"
817
+ ]).optional()
818
+ },
819
+ outputSchema: { comment: commentSchema },
820
+ annotations: MUTATING
821
+ }, async ({ filePath, side, lineNumber, startLineNumber, lineContent, body, severity }) => {
822
+ const comment = await requestSessionJson(requireGhPrSession(), "/api/gh/pr-session/comments", {
823
+ method: "POST",
824
+ headers: { "Content-Type": "application/json" },
825
+ body: JSON.stringify({
826
+ filePath,
827
+ side,
828
+ lineNumber,
829
+ startLineNumber,
830
+ lineContent: lineContent ?? "",
831
+ body,
832
+ severity: severity === "none" ? void 0 : severity
833
+ })
834
+ });
835
+ return textResult(`Created PR draft comment ${comment.id} on ${comment.filePath}:${comment.lineNumber}.`, { comment });
836
+ });
837
+ server.registerTool("gh_refresh", {
838
+ title: "Refresh GitHub PR session",
839
+ description: "Re-fetch PR metadata, patch, and published conversations into the local session (force-push / new review sync).",
840
+ inputSchema: {},
841
+ outputSchema: { result: z.unknown() },
842
+ annotations: MUTATING
843
+ }, async () => {
844
+ const result = await requestSessionJson(requireGhPrSession(), "/api/gh/pr/refresh", { method: "POST" });
845
+ return textResult(JSON.stringify(result), { result });
846
+ });
847
+ server.registerTool("gh_submit_review", {
848
+ title: "Submit PR review to GitHub",
849
+ description: "Publish local draft comments and a decision to GitHub. REQUIRES explicit user authorization — this mutates the remote pull request. Prefer dryRun first.",
850
+ inputSchema: {
851
+ decision: z.enum([
852
+ "approve",
853
+ "comment",
854
+ "request-changes",
855
+ "draft"
856
+ ]),
857
+ body: z.string().optional(),
858
+ dryRun: z.boolean().optional()
859
+ },
860
+ outputSchema: { result: z.unknown() },
861
+ annotations: {
862
+ ...MUTATING,
863
+ openWorldHint: true,
864
+ destructiveHint: false
865
+ }
866
+ }, async ({ decision, body, dryRun }) => {
867
+ const result = await requestSessionJson(requireGhPrSession(), "/api/gh/submit", {
868
+ method: "POST",
869
+ headers: { "Content-Type": "application/json" },
870
+ body: JSON.stringify({
871
+ decision,
872
+ body: body ?? "",
873
+ dryRun: dryRun === true
874
+ })
875
+ });
876
+ return textResult(JSON.stringify(result), { result });
877
+ });
699
878
  server.registerTool("create_comment", {
700
879
  title: "Create an inline review comment",
701
880
  description: "Create a local inline comment on an exact line (or inclusive range) from get_diff. side is additions for +/context in the new file and deletions for a removed line. Optional severity triages the finding for the human and is included in the agent handoff XML.",
@@ -1294,7 +1473,7 @@ This MCP connection is immutably bound to \`${repoRoot}\`.
1294
1473
 
1295
1474
  ## Code review
1296
1475
  1. Call \`review_session_status\`; call \`start_review_session\` if needed.
1297
- 2. Call \`get_diff\` and inspect every changed file.
1476
+ 2. Prefer \`diff_summary\` → paged \`diff_files\` → \`diff_hunks\` / bounded \`diff_slice\`; use \`get_diff\` only as an escape hatch.
1298
1477
  3. Use \`create_comment\` for actionable inline findings.
1299
1478
  4. Use \`await_review\` for a human handoff; a timeout is safe to retry.
1300
1479
  5. In \`comment-only\` mode, reply without editing files.
@@ -1305,7 +1484,12 @@ This MCP connection is immutably bound to \`${repoRoot}\`.
1305
1484
  3. On \`changes-requested\`, revise and resubmit with the same \`planId\`.
1306
1485
  4. On \`approved\`, proceed; on \`rejected\`, stop.
1307
1486
 
1308
- All HTTP activity is loopback-only. MCP never terminates a user-owned session.`
1487
+ ## GitHub PR
1488
+ 1. In \`gh-pr\` mode call \`gh_overview\`, then the bounded diff tools.
1489
+ 2. Fetch published discussion with \`gh_list_threads\` (prefer unresolved) and \`gh_list_reviews\`.
1490
+ 3. Local drafts do not publish. Call \`gh_submit_review\` or mutate published threads only with explicit user authorization.
1491
+
1492
+ MCP connects only to the loopback diffing server and never terminates a user-owned session. Explicitly authorized GitHub operations may make that server call GitHub.`
1309
1493
  }] }));
1310
1494
  server.registerPrompt("review_local_changes", {
1311
1495
  title: "Review local changes with diffing",
@@ -1315,7 +1499,7 @@ All HTTP activity is loopback-only. MCP never terminates a user-owned session.`
1315
1499
  role: "user",
1316
1500
  content: {
1317
1501
  type: "text",
1318
- text: `Use diffing to review the local changes in ${repoRoot}. Check review_session_status, start_review_session if needed, inspect get_diff, and create only actionable inline comments. Review every changed file${focus ? ` with special attention to ${focus}` : ""}.`
1502
+ text: `Use diffing to review the local changes in ${repoRoot}. Check review_session_status, start_review_session if needed, inspect with bounded diff_summary/diff_files/diff_hunks/diff_slice calls, and create only actionable inline comments. Review every changed file${focus ? ` with special attention to ${focus}` : ""}.`
1319
1503
  }
1320
1504
  }] }));
1321
1505
  server.registerPrompt("submit_plan_for_review", {