diffing 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/README.md +52 -18
  2. package/dist/{cli-agent-D71pq2ep.mjs → cli-agent-CLrK54k2.mjs} +2 -2
  3. package/dist/{cli-gh-D5OCnjNe.mjs → cli-gh-8YC1NzyM.mjs} +6 -1
  4. package/dist/cli.mjs +3 -3
  5. package/dist/client/assets/{angular-html-Dh86jUvr.js → angular-html-DdAR2u-k.js} +1 -1
  6. package/dist/client/assets/{angular-ts-Bh4wRJtT.js → angular-ts-Dz_vWKSu.js} +1 -1
  7. package/dist/client/assets/{apl-BSXBHduT.js → apl-BJ5QXrIk.js} +1 -1
  8. package/dist/client/assets/{arc-CRQlhBrV.js → arc-D9erSW6x.js} +1 -1
  9. package/dist/client/assets/{architectureDiagram-3BPJPVTR-BXq3UvEv.js → architectureDiagram-3BPJPVTR-CCQWZLXR.js} +1 -1
  10. package/dist/client/assets/{astro-Bh00klUw.js → astro-BQAej5a5.js} +1 -1
  11. package/dist/client/assets/{blade-CisA3zhm.js → blade-DkAmuwDx.js} +1 -1
  12. package/dist/client/assets/{blockDiagram-GPEHLZMM-Bw-PTAJr.js → blockDiagram-GPEHLZMM-vwjxNybI.js} +1 -1
  13. package/dist/client/assets/{c-D-pHVWrf.js → c-DG5p-ChJ.js} +1 -1
  14. package/dist/client/assets/{c4Diagram-AAUBKEIU-C0XyiqC_.js → c4Diagram-AAUBKEIU-DJ_hVlh4.js} +1 -1
  15. package/dist/client/assets/channel-DmZgR6r0.js +1 -0
  16. package/dist/client/assets/{chunk-2J33WTMH-DekjZXNj.js → chunk-2J33WTMH-CiLowxCo.js} +1 -1
  17. package/dist/client/assets/{chunk-3OPIFGDE-BtROpcL8.js → chunk-3OPIFGDE-H3jjMR5Y.js} +1 -1
  18. package/dist/client/assets/{chunk-4BX2VUAB-C92XLTlx.js → chunk-4BX2VUAB-Dk0dk4n5.js} +1 -1
  19. package/dist/client/assets/{chunk-55IACEB6-CceUgh8N.js → chunk-55IACEB6-nbLiUMJS.js} +1 -1
  20. package/dist/client/assets/{chunk-5ZQYHXKU-DSbYPAe7.js → chunk-5ZQYHXKU-DZ8RgHg8.js} +1 -1
  21. package/dist/client/assets/{chunk-727SXJPM-DVzUJa-9.js → chunk-727SXJPM-DDe5dqJV.js} +1 -1
  22. package/dist/client/assets/{chunk-AQP2D5EJ-6n3Z_IFa.js → chunk-AQP2D5EJ-DmpNclOr.js} +1 -1
  23. package/dist/client/assets/{chunk-BSJP7CBP-BgVLnkxr.js → chunk-BSJP7CBP-BKQKvtIj.js} +1 -1
  24. package/dist/client/assets/{chunk-CSCIHK7Q-CkGMXntp.js → chunk-CSCIHK7Q-D27t4cyv.js} +1 -1
  25. package/dist/client/assets/{chunk-FMBD7UC4-BM8lrLBo.js → chunk-FMBD7UC4-Bu3LA663.js} +1 -1
  26. package/dist/client/assets/{chunk-KSCS5N6A-Bm2pjMQg.js → chunk-KSCS5N6A-Ed5HNXRW.js} +1 -1
  27. package/dist/client/assets/{chunk-L5ZTLDWV-VL17ikXM.js → chunk-L5ZTLDWV-DLjJZgf8.js} +1 -1
  28. package/dist/client/assets/{chunk-LZXEDZCA-B629g67M.js → chunk-LZXEDZCA-B-cs89SM.js} +2 -2
  29. package/dist/client/assets/{chunk-ND2GUHAM-CjZGwhKA.js → chunk-ND2GUHAM-D_kPSuUA.js} +1 -1
  30. package/dist/client/assets/{chunk-NZK2D7GU-DGsLjESY.js → chunk-NZK2D7GU-D1u3pjq-.js} +1 -1
  31. package/dist/client/assets/{chunk-O5CBEL6O-EyUrGY3l.js → chunk-O5CBEL6O-Mt0cnPSf.js} +1 -1
  32. package/dist/client/assets/chunk-QZHKN3VN-BrYnBs7h.js +1 -0
  33. package/dist/client/assets/chunk-WU5MYG2G-CdBcsQs_.js +1 -0
  34. package/dist/client/assets/{chunk-XPW4576I-C2TeAQmQ.js → chunk-XPW4576I-Bt8a8x28.js} +1 -1
  35. package/dist/client/assets/classDiagram-4FO5ZUOK-CCpp3t5M.js +1 -0
  36. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-B1aV1sYb.js +1 -0
  37. package/dist/client/assets/{cobol-DsRZMP16.js → cobol-Daidj_5Y.js} +1 -1
  38. package/dist/client/assets/{coffee-DP0-MMsE.js → coffee-DUMdB2Cr.js} +1 -1
  39. package/dist/client/assets/{cose-bilkent-S5V4N54A-DMmK0b0W.js → cose-bilkent-S5V4N54A-DVUDLXie.js} +1 -1
  40. package/dist/client/assets/{cpp-B6XYbbze.js → cpp-C2FeX1Mc.js} +1 -1
  41. package/dist/client/assets/{crystal-dBLPFLMp.js → crystal-D4yb9blJ.js} +1 -1
  42. package/dist/client/assets/{css-tgwuSZL0.js → css-B5cr0U9F.js} +1 -1
  43. package/dist/client/assets/{dagre-BM42HDAG-CgNkf0TR.js → dagre-BM42HDAG-DdZWb-Ee.js} +1 -1
  44. package/dist/client/assets/{diagram-2AECGRRQ-ocAqn0LV.js → diagram-2AECGRRQ-C9DOZt92.js} +1 -1
  45. package/dist/client/assets/{diagram-5GNKFQAL-CB1Yo4Gc.js → diagram-5GNKFQAL-Bbc07UKr.js} +1 -1
  46. package/dist/client/assets/{diagram-KO2AKTUF-B2vpMBRT.js → diagram-KO2AKTUF-C-lUzY3m.js} +1 -1
  47. package/dist/client/assets/{diagram-LMA3HP47-DuxZOaQS.js → diagram-LMA3HP47-D-OjEFhp.js} +1 -1
  48. package/dist/client/assets/{diagram-OG6HWLK6-CkShDmcv.js → diagram-OG6HWLK6-BjtWGvrf.js} +1 -1
  49. package/dist/client/assets/{dist-BgZIvk5w.js → dist-BXZygPdX.js} +1 -1
  50. package/dist/client/assets/{edge-Bokubn8E.js → edge-RCQWcWNp.js} +1 -1
  51. package/dist/client/assets/{elixir-BEzcf3Xg.js → elixir-DwKyVg2X.js} +1 -1
  52. package/dist/client/assets/{elm-vJyLTAER.js → elm-DjR33IVs.js} +1 -1
  53. package/dist/client/assets/{erDiagram-TEJ5UH35-BFV1NQAH.js → erDiagram-TEJ5UH35-C5T4ywLG.js} +1 -1
  54. package/dist/client/assets/{erb-DDkAmjBS.js → erb-BnhUH1Hk.js} +1 -1
  55. package/dist/client/assets/{flowDiagram-I6XJVG4X-DU8xbFdO.js → flowDiagram-I6XJVG4X-BJmjm7Kx.js} +1 -1
  56. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DCb7Qh0c.js → ganttDiagram-6RSMTGT7-DREPnzu5.js} +1 -1
  57. package/dist/client/assets/{git-rebase-DWcifAdK.js → git-rebase-DzXoSDXA.js} +1 -1
  58. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-C_Tw-piE.js → gitGraphDiagram-PVQCEYII-DIR4G_-_.js} +1 -1
  59. package/dist/client/assets/{glimmer-js--HDru5ys.js → glimmer-js-HKnZpCn_.js} +1 -1
  60. package/dist/client/assets/{glimmer-ts-DAdXisHh.js → glimmer-ts-DedV7rWs.js} +1 -1
  61. package/dist/client/assets/{glsl-CmIRlJht.js → glsl-Bz-0Alp2.js} +1 -1
  62. package/dist/client/assets/{graphql-tx4-7I3U.js → graphql-DSKkDYCD.js} +1 -1
  63. package/dist/client/assets/{hack-yA0XjhF7.js → hack-CnaHZm8k.js} +1 -1
  64. package/dist/client/assets/{haml-BR5MUE1b.js → haml-huGFYKlj.js} +1 -1
  65. package/dist/client/assets/{handlebars-BPOF7iOf.js → handlebars-CKmgzfWY.js} +1 -1
  66. package/dist/client/assets/{html-BbteRk96.js → html-B9Ih_dUU.js} +1 -1
  67. package/dist/client/assets/{html-derivative-59NNrcMm.js → html-derivative-YabD-M5d.js} +1 -1
  68. package/dist/client/assets/{http-D3odGQI4.js → http-Bdu12JYX.js} +1 -1
  69. package/dist/client/assets/{hurl-BLTj8fPj.js → hurl-twZzDTNs.js} +1 -1
  70. package/dist/client/assets/{index-DHXpO7eS.js → index-BktdZRWB.js} +122 -118
  71. package/dist/client/assets/{index-BZcKS8Sb.css → index-YlwaPTSG.css} +1 -1
  72. package/dist/client/assets/{infoDiagram-5YYISTIA-CI6hJyRl.js → infoDiagram-5YYISTIA-D2ty2R-i.js} +1 -1
  73. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-gMS1P1LQ.js → ishikawaDiagram-YF4QCWOH-Ccnc9xlE.js} +1 -1
  74. package/dist/client/assets/{java-CsViuzs_.js → java-_5trO-p9.js} +1 -1
  75. package/dist/client/assets/{javascript-DQgsl2Dh.js → javascript-BOQn0ikE.js} +1 -1
  76. package/dist/client/assets/{jinja-DQNp2NUA.js → jinja-BgY-0IBz.js} +1 -1
  77. package/dist/client/assets/{jison-CDDOZQdc.js → jison-BLNTmT1_.js} +1 -1
  78. package/dist/client/assets/{journeyDiagram-JHISSGLW-BTQ_Ws_h.js → journeyDiagram-JHISSGLW-W7eWXlw0.js} +1 -1
  79. package/dist/client/assets/{json-DP4TqCuE.js → json-BLbH8rZf.js} +1 -1
  80. package/dist/client/assets/{jsx-Df5hn0Tf.js → jsx-CxteptsJ.js} +1 -1
  81. package/dist/client/assets/{julia--u33yGLi.js → julia-B__Ncp_z.js} +1 -1
  82. package/dist/client/assets/{just-BCpMmSOD.js → just-DBsHUGW1.js} +1 -1
  83. package/dist/client/assets/{kanban-definition-UN3LZRKU-CvqGbLR_.js → kanban-definition-UN3LZRKU-Z_AFL-rd.js} +1 -1
  84. package/dist/client/assets/{latex-D8D4jZGw.js → latex-DFF_0wxo.js} +1 -1
  85. package/dist/client/assets/{line-DBgaS_RW.js → line-aAv8TyOs.js} +1 -1
  86. package/dist/client/assets/{linear-RQVzKNVU.js → linear-BOvEZKH8.js} +1 -1
  87. package/dist/client/assets/{liquid-CkH85f12.js → liquid-kDWCO6d7.js} +1 -1
  88. package/dist/client/assets/{lua-iKoV7WE3.js → lua-CYTFayq-.js} +1 -1
  89. package/dist/client/assets/{marko-N8ScQSqT.js → marko-Dpmz3Pdx.js} +1 -1
  90. package/dist/client/assets/{mdc-BvHTOxsp.js → mdc-BsxHyM1i.js} +1 -1
  91. package/dist/client/assets/{mermaid-parser.core-CwDRwMR7.js → mermaid-parser.core-D8z_CWL1.js} +1 -1
  92. package/dist/client/assets/{mermaid.core-D2LDJgXO.js → mermaid.core-DNKchnly.js} +3 -3
  93. package/dist/client/assets/{mindmap-definition-RKZ34NQL-FRnPh642.js → mindmap-definition-RKZ34NQL-xQKMV4Kw.js} +1 -1
  94. package/dist/client/assets/{nginx-DGgvASuI.js → nginx-CcdTDme-.js} +1 -1
  95. package/dist/client/assets/{nim-D-v9hzvR.js → nim-B5wWDskL.js} +1 -1
  96. package/dist/client/assets/{perl-CG8leJ8K.js → perl-Ce2_iKLz.js} +1 -1
  97. package/dist/client/assets/{php-DvkeSP10.js → php-DuLCFERd.js} +1 -1
  98. package/dist/client/assets/{pieDiagram-4H26LBE5-CepOg38Y.js → pieDiagram-4H26LBE5-CBSCJnUr.js} +1 -1
  99. package/dist/client/assets/{pug-B4T7-xaL.js → pug-BfQJoQj1.js} +1 -1
  100. package/dist/client/assets/{qml-BE6D5Bxn.js → qml-DYeovlVl.js} +1 -1
  101. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DOFAsP60.js → quadrantDiagram-W4KKPZXB-uEcgb0Rm.js} +1 -1
  102. package/dist/client/assets/{r-BKZg9Mcq.js → r-DgHSP56L.js} +1 -1
  103. package/dist/client/assets/{razor-D-02oaxL.js → razor-B00x7W-i.js} +1 -1
  104. package/dist/client/assets/{regexp-BDFvAR1k.js → regexp-CmWVupmd.js} +1 -1
  105. package/dist/client/assets/{requirementDiagram-4Y6WPE33-BBVNH-2W.js → requirementDiagram-4Y6WPE33-Q4yY_ALe.js} +1 -1
  106. package/dist/client/assets/{review-export-D5iIl46_.js → review-export-pcqDKNqx.js} +1 -1
  107. package/dist/client/assets/{rst-BcT26OSj.js → rst-B3VARbTX.js} +1 -1
  108. package/dist/client/assets/{ruby-Cq_YQvFT.js → ruby-CaxKSSbT.js} +1 -1
  109. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BQfCTbBX.js → sankeyDiagram-5OEKKPKP-B-GLzFGa.js} +1 -1
  110. package/dist/client/assets/{sas-DN9oTxKY.js → sas-Ue0rHUIk.js} +1 -1
  111. package/dist/client/assets/{scss-BK_sp3z2.js → scss-BJJ7_L6c.js} +1 -1
  112. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-CKAW2eyx.js → sequenceDiagram-3UESZ5HK-DW0em0D_.js} +1 -1
  113. package/dist/client/assets/{shellscript-CvNpOu0J.js → shellscript-C6LkZs1u.js} +1 -1
  114. package/dist/client/assets/{shellsession-8-yj2cJj.js → shellsession-CkZu1e84.js} +1 -1
  115. package/dist/client/assets/{soy-C6Q9atPF.js → soy-CDaZV57J.js} +1 -1
  116. package/dist/client/assets/{sql-CBL3BuHb.js → sql-CLfMGH4x.js} +1 -1
  117. package/dist/client/assets/{src-D-VUWIK5.js → src-C27zxuNO.js} +1 -1
  118. package/dist/client/assets/{stata-B2Q7Vqzu.js → stata-B0kNMHc9.js} +1 -1
  119. package/dist/client/assets/{stateDiagram-AJRCARHV-BGJiv0qO.js → stateDiagram-AJRCARHV-CrO_D3m4.js} +1 -1
  120. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-C9KDGD7R.js +1 -0
  121. package/dist/client/assets/{surrealql-C-A2N8vk.js → surrealql-D9os_yZO.js} +1 -1
  122. package/dist/client/assets/{svelte-VAut_-uy.js → svelte-2SxVToeO.js} +1 -1
  123. package/dist/client/assets/{templ-mUetM--K.js → templ-DzmZB67T.js} +1 -1
  124. package/dist/client/assets/{tex-BT53mWGp.js → tex-BMPk3nHk.js} +1 -1
  125. package/dist/client/assets/{timeline-definition-PNZ67QCA-lVwMt1rk.js → timeline-definition-PNZ67QCA-C9FBevHv.js} +1 -1
  126. package/dist/client/assets/{ts-tags-VkIOkgLN.js → ts-tags-CG14Bplc.js} +1 -1
  127. package/dist/client/assets/{tsx-D_aaGHTf.js → tsx-D6NK461A.js} +1 -1
  128. package/dist/client/assets/{twig-CufwqKkk.js → twig-Agh0j5sD.js} +1 -1
  129. package/dist/client/assets/{typescript-osFD2CEV.js → typescript-_AkeziZ2.js} +1 -1
  130. package/dist/client/assets/{vennDiagram-CIIHVFJN-_K9NtpA5.js → vennDiagram-CIIHVFJN-2H9HfhhR.js} +1 -1
  131. package/dist/client/assets/{vue-Bub1rXmL.js → vue-C1z3QGG9.js} +1 -1
  132. package/dist/client/assets/{vue-html-CN3x2pYg.js → vue-html-C1RMzLZk.js} +1 -1
  133. package/dist/client/assets/{vue-vine-C0csYkGm.js → vue-vine-oOixA6ge.js} +1 -1
  134. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-B2jTMfra.js → wardleyDiagram-YWT4CUSO-BJxrQhTf.js} +1 -1
  135. package/dist/client/assets/{xml-my6OjeGb.js → xml-okuDZ7vq.js} +1 -1
  136. package/dist/client/assets/{xsl-Bx0YVn26.js → xsl-S_n3XrKb.js} +1 -1
  137. package/dist/client/assets/{xychartDiagram-2RQKCTM6-DfZ4YYXU.js → xychartDiagram-2RQKCTM6-BGdee7KG.js} +1 -1
  138. package/dist/client/assets/{yaml-DanwOypk.js → yaml-BoX-kqX1.js} +1 -1
  139. package/dist/client/index.html +2 -2
  140. package/dist/{doctor-q6oA64hm.mjs → doctor-CmP0u7-d.mjs} +1 -1
  141. package/dist/github-BjZZcSKV.mjs +2 -0
  142. package/dist/{github-HfIlIS-J.mjs → github-oCZx5aim.mjs} +270 -29
  143. package/dist/{mcp-eor39qmD.mjs → mcp-CqxXILYL.mjs} +1 -1
  144. package/dist/{server-C105rwQw.mjs → server-CczuXSWo.mjs} +164 -11
  145. package/package.json +3 -3
  146. package/dist/cli-agent-B4hU6Xay.mjs +0 -499
  147. package/dist/cli-agent-B_PHd635.mjs +0 -499
  148. package/dist/cli-agent-BatQgghr.mjs +0 -499
  149. package/dist/cli-agent-BkkRbKqq.mjs +0 -513
  150. package/dist/cli-agent-BlMtx1Tx.mjs +0 -736
  151. package/dist/cli-agent-BwZE05CQ.mjs +0 -499
  152. package/dist/cli-agent-C4Fb90GL.mjs +0 -728
  153. package/dist/cli-agent-CKE1Cvor.mjs +0 -513
  154. package/dist/cli-agent-CLvCYpmH.mjs +0 -499
  155. package/dist/cli-agent-CX8Fa1Ph.mjs +0 -499
  156. package/dist/cli-agent-ChALuNbw.mjs +0 -841
  157. package/dist/cli-agent-KxNAFIPf.mjs +0 -450
  158. package/dist/cli-agent-SILzesQP.mjs +0 -513
  159. package/dist/cli-agent-USzLoAit.mjs +0 -499
  160. package/dist/cli-agent-qBT8clNM.mjs +0 -841
  161. package/dist/cli-gh-6qiblgj7.mjs +0 -150
  162. package/dist/cli-gh-BV5az425.mjs +0 -147
  163. package/dist/cli-gh-Bo6fOHQR.mjs +0 -147
  164. package/dist/cli-gh-CDGSpndZ.mjs +0 -147
  165. package/dist/cli-gh-D1LoWJl-.mjs +0 -147
  166. package/dist/cli-gh-D1sIZ-jX.mjs +0 -147
  167. package/dist/cli-gh-DROAJlHi.mjs +0 -147
  168. package/dist/cli-gh-DgqscIkg.mjs +0 -147
  169. package/dist/cli-gh-DiT8BWwu.mjs +0 -147
  170. package/dist/cli-gh-KjPnyST6.mjs +0 -150
  171. package/dist/cli-gh-abs-IJrz.mjs +0 -147
  172. package/dist/client/assets/channel-CDxgOu-3.js +0 -1
  173. package/dist/client/assets/chunk-QZHKN3VN-DJCeMpP8.js +0 -1
  174. package/dist/client/assets/chunk-WU5MYG2G-BbuOkMaH.js +0 -1
  175. package/dist/client/assets/classDiagram-4FO5ZUOK-D5oJbiED.js +0 -1
  176. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-5MJt5ZxQ.js +0 -1
  177. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CdUgMghp.js +0 -1
  178. package/dist/comment-format-CKTt0fzi.mjs +0 -113
  179. package/dist/comment-format-DG_PAehC.mjs +0 -117
  180. package/dist/github-C9GLNak7.mjs +0 -2
  181. package/dist/mcp-7eXATtez.mjs +0 -1334
  182. package/dist/mcp-B6mCC7D3.mjs +0 -1047
  183. package/dist/mcp-BBk8AIO4.mjs +0 -1334
  184. package/dist/mcp-BJxrW9dS.mjs +0 -1334
  185. package/dist/mcp-BMTZLQuB.mjs +0 -758
  186. package/dist/mcp-BUF2nEL7.mjs +0 -1036
  187. package/dist/mcp-BWeWR4yd.mjs +0 -255
  188. package/dist/mcp-BgZO-iXI.mjs +0 -1047
  189. package/dist/mcp-BhT5GgYF.mjs +0 -1334
  190. package/dist/mcp-Bpx76OGU.mjs +0 -945
  191. package/dist/mcp-BsGdNA88.mjs +0 -1334
  192. package/dist/mcp-BxBm3d5l.mjs +0 -1047
  193. package/dist/mcp-C1T7LI0H.mjs +0 -1036
  194. package/dist/mcp-C4Bjmf9m.mjs +0 -1047
  195. package/dist/mcp-CFxcdEcJ.mjs +0 -733
  196. package/dist/mcp-Ca9QAlJ0.mjs +0 -1334
  197. package/dist/mcp-CphIUJUg.mjs +0 -1047
  198. package/dist/mcp-CtKRXQRC.mjs +0 -1047
  199. package/dist/mcp-CwjThGi2.mjs +0 -913
  200. package/dist/mcp-D4OsB9lt.mjs +0 -1047
  201. package/dist/mcp-DJ2batvt.mjs +0 -913
  202. package/dist/mcp-DNZQvKKF.mjs +0 -298
  203. package/dist/mcp-DO4zfNZX.mjs +0 -298
  204. package/dist/mcp-D_LGvBTw.mjs +0 -1047
  205. package/dist/mcp-Ds_lPX_o.mjs +0 -913
  206. package/dist/plan-format-B6nbAw_c.mjs +0 -132
  207. package/dist/plan-format-Bi46QS0m.mjs +0 -132
  208. package/dist/plan-format-DVnF8xrq.mjs +0 -126
  209. package/dist/plan-format-f62JWFI-.mjs +0 -117
  210. package/dist/plan-format-mSDtsPcC.mjs +0 -132
  211. package/dist/review-export-BoV0884M.mjs +0 -66
  212. package/dist/review-export-CKTI5Od_.mjs +0 -66
  213. package/dist/server-95UE5bgh.mjs +0 -3367
  214. package/dist/server-AisDKkpW.mjs +0 -3227
  215. package/dist/server-B0cS95Lf.mjs +0 -3488
  216. package/dist/server-BUsRhL1B.mjs +0 -3283
  217. package/dist/server-BZBFquG8.mjs +0 -3875
  218. package/dist/server-Bc2m9R5-.mjs +0 -3263
  219. package/dist/server-Bj-6KlK5.mjs +0 -3367
  220. package/dist/server-Bmt57wV5.mjs +0 -3875
  221. package/dist/server-Bpf84Il0.mjs +0 -3367
  222. package/dist/server-C15WRRnS.mjs +0 -3740
  223. package/dist/server-CYSpLbdA.mjs +0 -3360
  224. package/dist/server-ChT2DCaR.mjs +0 -3701
  225. package/dist/server-D0Ni8PKP.mjs +0 -3252
  226. package/dist/server-D2xEbakj.mjs +0 -3874
  227. package/dist/server-DJxysGxV.mjs +0 -3701
  228. package/dist/server-Dov7jbV3.mjs +0 -3367
  229. package/dist/server-TOElOuNP.mjs +0 -3258
  230. package/dist/server-eQ2E4Axo.mjs +0 -3367
  231. package/dist/server-lock-C_wPtEsH.mjs +0 -909
  232. package/dist/server-lock-Cbu3qHdu.mjs +0 -924
  233. package/dist/server-lock-D32inEn9.mjs +0 -924
  234. package/dist/server-lock-D73NKKbn.mjs +0 -842
  235. package/dist/server-lock-DaSx7DjW.mjs +0 -1049
  236. package/dist/server-lock-Ddcat0HF.mjs +0 -851
  237. package/dist/server-lock-Dj9fG9md.mjs +0 -838
  238. package/dist/server-lock-Dq_1CJaF.mjs +0 -1047
  239. package/dist/server-lock-Ujbcgq28.mjs +0 -954
  240. package/dist/update-check-CXeSZANo.mjs +0 -129
  241. package/dist/update-check-DAPFCqpF.mjs +0 -129
@@ -1,913 +0,0 @@
1
- import { o as parseDiffOptions, t as startServer } from "./server-Dov7jbV3.mjs";
2
- import { a as removeServerLock, i as readServerLock, n as diffScopeKey, o as writeServerLock, r as isLockAlive, s as formatComments, t as acquireServerStartupLease } from "./server-lock-Cbu3qHdu.mjs";
3
- import { n as formatPlanReview } from "./plan-format-B6nbAw_c.mjs";
4
- import { execFileSync } from "node:child_process";
5
- import { fileURLToPath } from "node:url";
6
- import { dirname, isAbsolute, resolve } from "node:path";
7
- import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
8
- import { randomUUID } from "node:crypto";
9
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
- import { z } from "zod";
12
- //#region src/mcp.ts
13
- const moduleDirectory = dirname(fileURLToPath(import.meta.url));
14
- function readPackageVersion() {
15
- const packagePath = resolve(moduleDirectory, "..", "package.json");
16
- const pkg = JSON.parse(readFileSync(packagePath, "utf-8"));
17
- if (typeof pkg.version !== "string" || !pkg.version) throw new Error(`Invalid package version in ${packagePath}`);
18
- return pkg.version;
19
- }
20
- const MCP_VERSION = readPackageVersion();
21
- const READ_ONLY = {
22
- readOnlyHint: true,
23
- destructiveHint: false,
24
- idempotentHint: true,
25
- openWorldHint: false
26
- };
27
- const MUTATING = {
28
- readOnlyHint: false,
29
- destructiveHint: false,
30
- idempotentHint: false,
31
- openWorldHint: false
32
- };
33
- const IDEMPOTENT_MUTATION = {
34
- ...MUTATING,
35
- idempotentHint: true
36
- };
37
- const AWAIT = {
38
- readOnlyHint: true,
39
- destructiveHint: false,
40
- idempotentHint: false,
41
- openWorldHint: false
42
- };
43
- const commentSchema = z.object({
44
- id: z.string(),
45
- filePath: z.string(),
46
- side: z.enum(["deletions", "additions"]),
47
- lineNumber: z.number(),
48
- startLineNumber: z.number().optional(),
49
- lineContent: z.string(),
50
- body: z.string(),
51
- status: z.enum(["open", "resolved"]),
52
- createdAt: z.number(),
53
- replies: z.array(z.object({
54
- id: z.string(),
55
- body: z.string(),
56
- createdAt: z.number(),
57
- role: z.enum(["user", "agent"]).optional(),
58
- model: z.string().optional(),
59
- createdAtPlanVersion: z.number().optional()
60
- }))
61
- });
62
- function textResult(text, structuredContent) {
63
- return {
64
- content: [{
65
- type: "text",
66
- text
67
- }],
68
- structuredContent
69
- };
70
- }
71
- /** Resolve and validate an immutable repository binding without invoking a shell. */
72
- function resolveMcpRepository(repoPath = process.cwd(), explicit = false) {
73
- if (explicit && !isAbsolute(repoPath)) throw new Error("diffing mcp: --repo must be an absolute path");
74
- let candidate;
75
- try {
76
- candidate = realpathSync(repoPath);
77
- if (!statSync(candidate).isDirectory()) throw new Error("not a directory");
78
- } catch {
79
- throw new Error(`diffing mcp: repository path is not an accessible directory: ${repoPath}`);
80
- }
81
- try {
82
- return execFileSync("git", [
83
- "-C",
84
- candidate,
85
- "rev-parse",
86
- "--show-toplevel"
87
- ], {
88
- encoding: "utf-8",
89
- stdio: [
90
- "ignore",
91
- "pipe",
92
- "pipe"
93
- ]
94
- }).trim();
95
- } catch {
96
- throw new Error(`diffing mcp: path is not inside a Git repository: ${candidate}`);
97
- }
98
- }
99
- function isLoopbackHost(host) {
100
- const normalized = host.toLowerCase();
101
- if (normalized === "localhost" || normalized === "::1" || normalized === "[::1]") return true;
102
- const octets = normalized.split(".");
103
- return octets.length === 4 && octets[0] === "127" && octets.every((part) => {
104
- if (!/^\d{1,3}$/.test(part)) return false;
105
- const value = Number(part);
106
- return value >= 0 && value <= 255;
107
- });
108
- }
109
- function lockUrl(lock) {
110
- if ((lock.mode ?? "web") === "tui" || lock.port <= 0) return null;
111
- if (!isLoopbackHost(lock.host)) return null;
112
- return `http://${lock.host === "::1" ? "[::1]" : lock.host}:${lock.port}`;
113
- }
114
- async function requestJson(base, path, init) {
115
- let response;
116
- try {
117
- response = await fetch(`${base}${path}`, init);
118
- } catch (error) {
119
- if (error instanceof Error && error.name === "AbortError") throw new Error(`Request to ${path} was cancelled. Retry the MCP tool when ready.`);
120
- const detail = error instanceof Error ? error.message : String(error);
121
- throw new Error(`Cannot reach the diffing review server at ${base}: ${detail}. Call review_session_status, then start_review_session if needed.`);
122
- }
123
- const raw = await response.text();
124
- if (!response.ok) {
125
- let detail = raw.trim();
126
- try {
127
- const parsed = JSON.parse(raw);
128
- if (typeof parsed.error === "string") detail = parsed.error;
129
- } catch {}
130
- throw new Error(`diffing server rejected ${init?.method ?? "GET"} ${path} with HTTP ${response.status}` + (detail ? `: ${detail}` : "") + ".");
131
- }
132
- try {
133
- return JSON.parse(raw);
134
- } catch {
135
- throw new Error(`diffing server returned malformed JSON for ${path}. Check that client and server versions match.`);
136
- }
137
- }
138
- function sameScope(lock, requestedScope, requestedArgs) {
139
- if (lock.scope) return lock.scope === requestedScope;
140
- return requestedArgs.length === 0;
141
- }
142
- const SAFE_MCP_BOOLEAN_DIFF_ARGS = new Set([
143
- "--staged",
144
- "--cached",
145
- "--merge",
146
- "--indent-heuristic",
147
- "--no-indent-heuristic",
148
- "--ignore-space-change",
149
- "--ignore-all-space",
150
- "--ignore-blank-lines",
151
- "--ignore-cr-at-eol",
152
- "--function-context",
153
- "--find-copies-harder",
154
- "--pickaxe-all",
155
- "--no-textconv",
156
- "--no-ext-diff",
157
- "--patch",
158
- "-p",
159
- "-b",
160
- "-w",
161
- "-W"
162
- ]);
163
- const SAFE_MCP_VALUE_DIFF_ARGS = new Set([
164
- "--diff-algorithm",
165
- "--anchored",
166
- "--ws-error-highlight",
167
- "--unified",
168
- "--inter-hunk-context",
169
- "--find-copies",
170
- "--find-renames",
171
- "--break-rewrites",
172
- "--diff-filter",
173
- "--ignore-submodules"
174
- ]);
175
- const SAFE_MCP_ATTACHED_SHORT_ARG = /^-(?:U|C|M|B|S|G).+$/;
176
- /** Validate MCP-controlled git arguments before the general CLI parser sees them. */
177
- function validateMcpDiffArgs(diffArgs) {
178
- let pathsOnly = false;
179
- for (let index = 0; index < diffArgs.length; index += 1) {
180
- const arg = diffArgs[index];
181
- if (arg.includes("\0")) throw new Error("diffArgs must not contain NUL bytes.");
182
- if (pathsOnly) continue;
183
- if (arg === "--") {
184
- pathsOnly = true;
185
- continue;
186
- }
187
- if (!arg.startsWith("-")) continue;
188
- if (SAFE_MCP_BOOLEAN_DIFF_ARGS.has(arg) || SAFE_MCP_ATTACHED_SHORT_ARG.test(arg)) continue;
189
- const equals = arg.indexOf("=");
190
- const name = equals >= 0 ? arg.slice(0, equals) : arg;
191
- if (SAFE_MCP_VALUE_DIFF_ARGS.has(name)) {
192
- if (equals < 0) {
193
- const value = diffArgs[index + 1];
194
- if (value === void 0 || value === "--" || value.startsWith("-")) throw new Error(`Safe diff option ${name} requires a value.`);
195
- index += 1;
196
- } else if (arg.slice(equals + 1).length === 0) throw new Error(`Safe diff option ${name} requires a value.`);
197
- continue;
198
- }
199
- throw new Error(`Unsafe or unsupported diff argument ${JSON.stringify(arg)}. start_review_session accepts only revision/path scope, filtering, whitespace, context, and rename-detection options; output, external-driver, and diffing runtime flags are forbidden.`);
200
- }
201
- }
202
- function createMcpServer(options) {
203
- const repoRoot = resolveMcpRepository(options.repoRoot, true);
204
- const startServerFn = options.startServerFn ?? startServer;
205
- const now = options.now ?? Date.now;
206
- const readLock = options.readLock ?? readServerLock;
207
- const writeLock = options.writeLock ?? writeServerLock;
208
- const removeLock = options.removeLock ?? removeServerLock;
209
- const lockIsAlive = options.lockIsAlive ?? isLockAlive;
210
- const acquireStartupLease = options.acquireStartupLease ?? acquireServerStartupLease;
211
- const ownerId = randomUUID();
212
- const defaultClientDir = existsSync(resolve(moduleDirectory, "client")) ? resolve(moduleDirectory, "client") : resolve(repoRoot, "dist/client");
213
- const clientDir = options.clientDir ?? defaultClientDir;
214
- 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.`;
215
- const server = new McpServer({
216
- name: "diffing",
217
- version: MCP_VERSION
218
- }, { instructions });
219
- let reviewCursor = null;
220
- let planCursor = null;
221
- function liveLock() {
222
- const lock = readLock(repoRoot);
223
- return lock && lockIsAlive(lock, repoRoot) ? lock : null;
224
- }
225
- function ensureReusableLock(lock) {
226
- if (lock.owner === "mcp" && lock.pid !== process.pid) throw new Error(`A different diffing MCP process (${lock.pid}) owns this repository session. Its web server lifecycle is tied to that MCP connection; stop it or wait for it to exit, then retry.`);
227
- 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.`);
228
- }
229
- function requireWebSession() {
230
- const lock = liveLock();
231
- if (!lock) throw new Error(`No diffing web review session is running for ${repoRoot}. Call start_review_session, then retry this tool.`);
232
- ensureReusableLock(lock);
233
- const url = lockUrl(lock);
234
- if (!url) throw new Error("The active diffing session is TUI-only. Start a web review session before using MCP review tools.");
235
- return {
236
- lock,
237
- url,
238
- identity: `${lock.pid}:${lock.startedAt}:${lock.port}:${lock.ownerId ?? ""}`
239
- };
240
- }
241
- function requireBaseUrl() {
242
- return requireWebSession().url;
243
- }
244
- async function seedReviewCursor(session, signal) {
245
- if (reviewCursor?.identity === session.identity) return reviewCursor.round;
246
- const status = await requestJson(session.url, "/api/review/status", { signal });
247
- reviewCursor = {
248
- identity: session.identity,
249
- round: status.round ?? 0
250
- };
251
- return reviewCursor.round;
252
- }
253
- async function seedPlanCursor(session, signal, force = false) {
254
- if (!force && planCursor?.identity === session.identity) return planCursor.round;
255
- const status = await requestJson(session.url, "/api/plan-review/status", { signal });
256
- planCursor = {
257
- identity: session.identity,
258
- round: status.round ?? 0
259
- };
260
- return planCursor.round;
261
- }
262
- function sessionStatus() {
263
- const lock = liveLock();
264
- if (!lock) return {
265
- repository: repoRoot,
266
- serverState: "not-running",
267
- mode: "none",
268
- url: null,
269
- managedBy: null,
270
- diffArgs: [],
271
- nextAction: "Call start_review_session, then get_diff."
272
- };
273
- const mode = lock.mode ?? "web";
274
- const url = lockUrl(lock);
275
- const inaccessible = mode !== "tui" && url === null;
276
- return {
277
- repository: repoRoot,
278
- serverState: "running",
279
- mode,
280
- url,
281
- managedBy: lock.owner === "mcp" ? "mcp" : "user",
282
- diffArgs: lock.diffArgs ?? [],
283
- 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" ? "A TUI session cannot serve MCP tools; stop it manually and call start_review_session." : 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."
284
- };
285
- }
286
- server.registerTool("review_session_status", {
287
- title: "Get review session status",
288
- description: "Inspect the repository binding and active diffing session. Works when no web server is running; use its nextAction before other tools.",
289
- inputSchema: {},
290
- outputSchema: {
291
- repository: z.string(),
292
- serverState: z.enum(["running", "not-running"]),
293
- mode: z.enum([
294
- "none",
295
- "web",
296
- "gh-pr",
297
- "tui"
298
- ]),
299
- url: z.string().nullable(),
300
- managedBy: z.enum(["mcp", "user"]).nullable(),
301
- diffArgs: z.array(z.string()),
302
- nextAction: z.string()
303
- },
304
- annotations: READ_ONLY
305
- }, async () => {
306
- const status = sessionStatus();
307
- return textResult(status.serverState === "running" ? `diffing ${status.mode} session is running for ${repoRoot}${status.url ? ` at ${status.url}` : ""}. ${status.nextAction}` : `No diffing web session is running for ${repoRoot}. ${status.nextAction}`, { ...status });
308
- });
309
- let startQueue = Promise.resolve();
310
- server.registerTool("start_review_session", {
311
- title: "Start or reuse a local review session",
312
- description: "Idempotently reuse a matching web review session or start a headless loopback-only session on an OS-selected port. diffArgs are passed directly to diff parsing, never to a shell. A live incompatible user session is reported, never stopped.",
313
- inputSchema: { diffArgs: z.array(z.string()).optional().describe("Optional git-diff arguments, for example [\"--staged\"] or [\"main...HEAD\", \"--\", \"src/\"].") },
314
- outputSchema: {
315
- status: z.enum(["started", "reused"]),
316
- repository: z.string(),
317
- url: z.string(),
318
- mode: z.literal("web"),
319
- managedBy: z.enum(["mcp", "user"]),
320
- diffArgs: z.array(z.string()),
321
- nextAction: z.string()
322
- },
323
- annotations: IDEMPOTENT_MUTATION
324
- }, async ({ diffArgs = [] }) => {
325
- validateMcpDiffArgs(diffArgs);
326
- const parsed = parseDiffOptions(diffArgs);
327
- parsed.outputMode = "web";
328
- parsed.host = "127.0.0.1";
329
- parsed.port = void 0;
330
- parsed.noOpen = true;
331
- parsed.noExtDiff = true;
332
- parsed.textconv = false;
333
- parsed.extDiff = void 0;
334
- parsed.outputFormat = void 0;
335
- parsed.outputFile = void 0;
336
- parsed.exitCode = false;
337
- parsed.quiet = false;
338
- parsed.check = false;
339
- parsed.binary = false;
340
- const requestedScope = diffScopeKey(parsed);
341
- const operation = startQueue.then(async () => {
342
- const reuse = (existing) => {
343
- ensureReusableLock(existing);
344
- const mode = existing.mode ?? "web";
345
- if (mode !== "web") throw new Error(`A live ${mode} diffing session already owns this repository. diffing will not replace or stop it; end that session manually before starting a local web review.`);
346
- if (!sameScope(existing, requestedScope, diffArgs)) throw new Error(`A live diffing web session already shows a different diff scope. Requested arguments: ${JSON.stringify(diffArgs)}. Use the existing session or end it manually; diffing will not replace it.`);
347
- const url = lockUrl(existing);
348
- if (!url) throw new Error("The active diffing web session has no safe loopback URL.");
349
- return {
350
- status: "reused",
351
- repository: repoRoot,
352
- url,
353
- mode: "web",
354
- managedBy: existing.owner === "mcp" ? "mcp" : "user",
355
- diffArgs: existing.diffArgs ?? diffArgs,
356
- nextAction: "Call get_diff to inspect the active diff."
357
- };
358
- };
359
- const beforeLease = liveLock();
360
- if (beforeLease) return reuse(beforeLease);
361
- const lease = acquireStartupLease(repoRoot, ownerId);
362
- if (!lease) throw new Error("Another diffing process is starting a review session for this repository. Retry start_review_session after that startup completes.");
363
- try {
364
- const afterLease = liveLock();
365
- if (afterLease) return reuse(afterLease);
366
- const lock = {
367
- port: (await startServerFn({
368
- port: 0,
369
- host: "127.0.0.1",
370
- clientDir,
371
- diffOpts: parsed
372
- })).port,
373
- host: "127.0.0.1",
374
- pid: process.pid,
375
- repoRoot,
376
- startedAt: now(),
377
- version: MCP_VERSION,
378
- mode: "web",
379
- scope: requestedScope,
380
- diffArgs: [...diffArgs],
381
- owner: "mcp",
382
- ownerId
383
- };
384
- try {
385
- writeLock(lock);
386
- } catch (error) {
387
- const persisted = readLock(repoRoot);
388
- if (persisted?.owner === "mcp" && persisted.ownerId === ownerId) removeLock(repoRoot);
389
- const detail = error instanceof Error ? error.message : String(error);
390
- throw new Error(`The review server bound locally but its discovery lock could not be written: ${detail}. No MCP session was claimed; retry after fixing lock storage. The unadvertised loopback listener will close when this MCP process exits.`);
391
- }
392
- return {
393
- status: "started",
394
- repository: repoRoot,
395
- url: lockUrl(lock),
396
- mode: "web",
397
- managedBy: "mcp",
398
- diffArgs: [...diffArgs],
399
- nextAction: "Call get_diff to inspect the active diff."
400
- };
401
- } finally {
402
- lease.release();
403
- }
404
- });
405
- startQueue = operation.then(() => void 0, () => void 0);
406
- const result = await operation;
407
- return textResult(`${result.status === "started" ? "Started" : "Reused"} review session at ${result.url}.`, result);
408
- });
409
- server.registerTool("get_diff", {
410
- title: "Get the active local diff",
411
- description: "Fetch the complete patch and basic repository metadata from the active local web review. Start or locate the session first.",
412
- inputSchema: {},
413
- outputSchema: {
414
- patch: z.string(),
415
- repoName: z.string(),
416
- branch: z.string(),
417
- customMode: z.boolean(),
418
- binaryFiles: z.array(z.object({
419
- path: z.string(),
420
- type: z.enum([
421
- "added",
422
- "deleted",
423
- "changed",
424
- "untracked"
425
- ])
426
- })),
427
- tabSizeMap: z.record(z.string(), z.number()),
428
- untrackedFiles: z.array(z.string()),
429
- showMode: z.boolean().optional(),
430
- commits: z.array(z.unknown()).optional(),
431
- truncated: z.number().optional()
432
- },
433
- annotations: READ_ONLY
434
- }, async () => {
435
- const diff = await requestJson(requireBaseUrl(), "/api/diff");
436
- const structured = {
437
- patch: diff.patch,
438
- repoName: diff.repoName,
439
- branch: diff.branch,
440
- customMode: diff.customMode,
441
- binaryFiles: diff.binaryFiles,
442
- tabSizeMap: diff.tabSizeMap,
443
- untrackedFiles: diff.untrackedFiles,
444
- ...typeof diff.showMode === "boolean" ? { showMode: diff.showMode } : {},
445
- ...Array.isArray(diff.commits) ? { commits: diff.commits } : {},
446
- ...typeof diff.truncated === "number" ? { truncated: diff.truncated } : {}
447
- };
448
- return textResult(diff.patch || "(The active diff is empty.)", structured);
449
- });
450
- server.registerTool("create_comment", {
451
- title: "Create an inline review comment",
452
- description: "Create a local inline comment on an exact line from get_diff. side is additions for +/context in the new file and deletions for a removed line.",
453
- inputSchema: {
454
- filePath: z.string().min(1).describe("Repository-relative file path exactly as shown in the patch."),
455
- side: z.enum(["deletions", "additions"]).describe("Which side of the patch contains the target line."),
456
- lineNumber: z.number().int().positive().describe("Target line number on the selected side."),
457
- startLineNumber: z.number().int().positive().optional().describe("Optional first line for a multiline comment."),
458
- lineContent: z.string().describe("Target line text, used to preserve context in the review UI."),
459
- body: z.string().min(1).describe("Actionable review comment in Markdown.")
460
- },
461
- outputSchema: {
462
- status: z.literal("created"),
463
- comment: commentSchema
464
- },
465
- annotations: MUTATING
466
- }, async (input) => {
467
- const comment = await requestJson(requireBaseUrl(), "/api/comments", {
468
- method: "POST",
469
- headers: { "Content-Type": "application/json" },
470
- body: JSON.stringify(input)
471
- });
472
- return textResult(`Created inline comment ${comment.id} on ${comment.filePath}:${comment.lineNumber}.`, {
473
- status: "created",
474
- comment
475
- });
476
- });
477
- server.registerTool("await_review", {
478
- title: "Wait for code review handoff",
479
- description: "Wait for the human to send a code-review round. Returns status=released or status=timeout and always includes mode. Timeout is expected and safe to retry; progress notifications are sent between long polls.",
480
- inputSchema: { timeoutSeconds: z.number().positive().max(3600).optional().describe("Total wait budget in seconds; defaults to 570.") },
481
- outputSchema: {
482
- status: z.enum(["released", "timeout"]),
483
- mode: z.enum(["standard", "comment-only"]),
484
- round: z.number(),
485
- openCount: z.number().optional(),
486
- decision: z.enum([
487
- "approved",
488
- "changes-requested",
489
- "rejected",
490
- "comment-only"
491
- ]).optional(),
492
- comments: z.array(commentSchema).optional(),
493
- nextAction: z.string()
494
- },
495
- annotations: AWAIT
496
- }, async ({ timeoutSeconds }, extra) => {
497
- const session = requireWebSession();
498
- const base = session.url;
499
- const budgetMs = (timeoutSeconds ?? 570) * 1e3;
500
- const progressToken = extra?._meta?.progressToken;
501
- let sinceRound = await seedReviewCursor(session, extra?.signal);
502
- const deadline = Date.now() + budgetMs;
503
- let cycle = 0;
504
- while (Date.now() < deadline) {
505
- const remaining = Math.max(1, deadline - Date.now());
506
- const result = await requestJson(base, `/api/review/await?timeoutMs=${Math.min(25e3, remaining)}&sinceRound=${sinceRound}`, { signal: extra?.signal });
507
- if (result.status === "released") {
508
- const payload = result.payload;
509
- sinceRound = payload.round;
510
- reviewCursor = {
511
- identity: session.identity,
512
- round: sinceRound
513
- };
514
- const structured = {
515
- status: "released",
516
- mode: payload.mode ?? "standard",
517
- round: payload.round,
518
- openCount: payload.openCount,
519
- ...payload.decision ? { decision: payload.decision } : {},
520
- comments: payload.comments,
521
- nextAction: payload.mode === "comment-only" ? "Reply to comments without editing files; resolve only comments the human considers addressed." : "Address open comments, reply with evidence, and resolve completed threads."
522
- };
523
- return textResult(payload.commentXml, structured);
524
- }
525
- sinceRound = result.round ?? sinceRound;
526
- reviewCursor = {
527
- identity: session.identity,
528
- round: sinceRound
529
- };
530
- cycle += 1;
531
- if (progressToken !== void 0) await extra.sendNotification({
532
- method: "notifications/progress",
533
- params: {
534
- progressToken,
535
- progress: cycle,
536
- total: Math.max(cycle, Math.ceil(budgetMs / 25e3)),
537
- message: "Still waiting for a code-review handoff; long poll completed and will retry."
538
- }
539
- }).catch(() => {});
540
- }
541
- const structured = {
542
- status: "timeout",
543
- mode: "standard",
544
- round: sinceRound,
545
- nextAction: "No review was sent within the wait budget. Call await_review again to keep waiting."
546
- };
547
- return textResult(structured.nextAction, structured);
548
- });
549
- server.registerTool("list_comments", {
550
- title: "List code review comments",
551
- description: "Fetch code-review comments as XML plus structured data. Use openOnly=true when addressing the current review round.",
552
- inputSchema: { openOnly: z.boolean().optional().describe("Return only unresolved comments when true.") },
553
- outputSchema: { comments: z.array(commentSchema) },
554
- annotations: READ_ONLY
555
- }, async ({ openOnly }) => {
556
- const all = await requestJson(requireBaseUrl(), "/api/comments");
557
- const comments = openOnly ? all.filter((comment) => comment.status === "open") : all;
558
- return textResult(formatComments(comments), { comments });
559
- });
560
- server.registerTool("reply_to_comment", {
561
- title: "Reply to a code review comment",
562
- description: "Post an agent reply to an existing code-review thread. Include concise evidence of the answer or applied change.",
563
- inputSchema: {
564
- commentId: z.string().min(1).describe("Comment id from list_comments or await_review."),
565
- body: z.string().min(1).describe("Reply body in Markdown."),
566
- model: z.string().optional().describe("Optional agent/model identifier shown in the UI.")
567
- },
568
- outputSchema: {
569
- status: z.literal("replied"),
570
- commentId: z.string()
571
- },
572
- annotations: MUTATING
573
- }, async ({ commentId, body, model }) => {
574
- await requestJson(requireBaseUrl(), `/api/comments/${encodeURIComponent(commentId)}/replies`, {
575
- method: "POST",
576
- headers: { "Content-Type": "application/json" },
577
- body: JSON.stringify({
578
- body,
579
- role: "agent",
580
- model
581
- })
582
- });
583
- return textResult(`Replied to ${commentId}.`, {
584
- status: "replied",
585
- commentId
586
- });
587
- });
588
- server.registerTool("resolve_comment", {
589
- title: "Resolve a code review comment",
590
- description: "Mark a code-review thread resolved after its request is fully addressed. Safe to retry.",
591
- inputSchema: { commentId: z.string().min(1).describe("Comment id to resolve.") },
592
- outputSchema: {
593
- status: z.literal("resolved"),
594
- commentId: z.string()
595
- },
596
- annotations: IDEMPOTENT_MUTATION
597
- }, async ({ commentId }) => {
598
- await requestJson(requireBaseUrl(), `/api/comments/${encodeURIComponent(commentId)}`, {
599
- method: "PUT",
600
- headers: { "Content-Type": "application/json" },
601
- body: JSON.stringify({ status: "resolved" })
602
- });
603
- return textResult(`Resolved ${commentId}.`, {
604
- status: "resolved",
605
- commentId
606
- });
607
- });
608
- server.registerTool("submit_plan", {
609
- title: "Submit or resubmit a plan",
610
- description: "Submit Markdown for human plan review. To revise after changes-requested, pass the same planId so a new version is created.",
611
- inputSchema: {
612
- title: z.string().optional().describe("Human-readable plan title."),
613
- body: z.string().min(1).describe("Complete Markdown plan body."),
614
- source: z.string().optional().describe("Optional source filename or workflow label."),
615
- model: z.string().optional().describe("Optional agent/model identifier."),
616
- planId: z.string().optional().describe("Existing plan id when resubmitting a revised version.")
617
- },
618
- outputSchema: {
619
- status: z.literal("submitted"),
620
- planId: z.string(),
621
- version: z.number(),
622
- url: z.string()
623
- },
624
- annotations: MUTATING
625
- }, async ({ title, body, source, model, planId }) => {
626
- const session = requireWebSession();
627
- const base = session.url;
628
- await seedPlanCursor(session, void 0, true);
629
- const plan = await requestJson(base, "/api/plans", {
630
- method: "POST",
631
- headers: { "Content-Type": "application/json" },
632
- body: JSON.stringify({
633
- id: planId,
634
- title,
635
- body,
636
- source,
637
- model
638
- })
639
- });
640
- const url = `${base}/plan/${plan.id}`;
641
- return textResult(`Submitted plan ${plan.id} (v${plan.version}) at ${url}. Call await_plan_review.`, {
642
- status: "submitted",
643
- planId: plan.id,
644
- version: plan.version,
645
- url
646
- });
647
- });
648
- server.registerTool("await_plan_review", {
649
- title: "Wait for plan review verdict",
650
- description: "Wait for a plan verdict. Returns status=released or status=timeout and always includes mode. Timeout is expected and safe to retry; progress notifications are sent between long polls.",
651
- inputSchema: { timeoutSeconds: z.number().positive().max(3600).optional().describe("Total wait budget in seconds; defaults to 570.") },
652
- outputSchema: {
653
- status: z.enum(["released", "timeout"]),
654
- mode: z.enum(["standard", "comment-only"]),
655
- round: z.number(),
656
- planId: z.string().optional(),
657
- decision: z.enum([
658
- "pending",
659
- "approved",
660
- "changes-requested",
661
- "rejected",
662
- "comment-only"
663
- ]).optional(),
664
- decisionComment: z.string().optional(),
665
- openCommentCount: z.number().optional(),
666
- plan: z.unknown().optional(),
667
- nextAction: z.string()
668
- },
669
- annotations: AWAIT
670
- }, async ({ timeoutSeconds }, extra) => {
671
- const session = requireWebSession();
672
- const base = session.url;
673
- const budgetMs = (timeoutSeconds ?? 570) * 1e3;
674
- const progressToken = extra?._meta?.progressToken;
675
- let sinceRound = await seedPlanCursor(session, extra?.signal);
676
- const deadline = Date.now() + budgetMs;
677
- let cycle = 0;
678
- while (Date.now() < deadline) {
679
- const remaining = Math.max(1, deadline - Date.now());
680
- const result = await requestJson(base, `/api/plan-review/await?timeoutMs=${Math.min(25e3, remaining)}&sinceRound=${sinceRound}`, { signal: extra?.signal });
681
- if (result.status === "released") {
682
- const payload = result.payload;
683
- sinceRound = payload.round;
684
- planCursor = {
685
- identity: session.identity,
686
- round: sinceRound
687
- };
688
- const nextAction = payload.mode === "comment-only" ? "Reply to plan comments without changing implementation files." : payload.decision === "approved" ? "Proceed with the approved plan." : payload.decision === "changes-requested" ? "Revise the plan and call submit_plan with the same planId." : "Stop; the plan was rejected.";
689
- const structured = {
690
- status: "released",
691
- mode: payload.mode ?? "standard",
692
- round: payload.round,
693
- planId: payload.planId,
694
- decision: payload.decision,
695
- ...payload.decisionComment ? { decisionComment: payload.decisionComment } : {},
696
- openCommentCount: payload.openCommentCount,
697
- plan: payload.plan,
698
- nextAction
699
- };
700
- return textResult(payload.reviewXml, structured);
701
- }
702
- sinceRound = result.round ?? sinceRound;
703
- planCursor = {
704
- identity: session.identity,
705
- round: sinceRound
706
- };
707
- cycle += 1;
708
- if (progressToken !== void 0) await extra.sendNotification({
709
- method: "notifications/progress",
710
- params: {
711
- progressToken,
712
- progress: cycle,
713
- total: Math.max(cycle, Math.ceil(budgetMs / 25e3)),
714
- message: "Still waiting for a plan verdict; long poll completed and will retry."
715
- }
716
- }).catch(() => {});
717
- }
718
- const structured = {
719
- status: "timeout",
720
- mode: "standard",
721
- round: sinceRound,
722
- nextAction: "No plan verdict arrived within the wait budget. Call await_plan_review again to keep waiting."
723
- };
724
- return textResult(structured.nextAction, structured);
725
- });
726
- server.registerTool("list_plans", {
727
- title: "List submitted plans",
728
- description: "List every submitted plan with its current verdict, version, and inline comments.",
729
- inputSchema: {},
730
- outputSchema: { plans: z.array(z.unknown()) },
731
- annotations: READ_ONLY
732
- }, async () => {
733
- const plans = await requestJson(requireBaseUrl(), "/api/plans");
734
- return textResult(plans.map((plan) => {
735
- const open = (plan.comments ?? []).filter((comment) => comment.status === "open").length;
736
- return `${plan.id} [${plan.decision}] v${plan.version} — ${open} open comment(s) — ${plan.title}`;
737
- }).join("\n") || "No plans submitted yet.", { plans });
738
- });
739
- server.registerTool("get_plan", {
740
- title: "Get a plan",
741
- description: "Fetch one current plan as plan-review XML and structured data, including verdict and inline comments.",
742
- inputSchema: { planId: z.string().min(1).describe("Plan id from submit_plan or list_plans.") },
743
- outputSchema: { plan: z.unknown() },
744
- annotations: READ_ONLY
745
- }, async ({ planId }) => {
746
- const plan = await requestJson(requireBaseUrl(), `/api/plans/${encodeURIComponent(planId)}`);
747
- return textResult(formatPlanReview(plan), { plan });
748
- });
749
- server.registerTool("get_plan_versions", {
750
- title: "List plan versions",
751
- description: "List all submitted versions of a plan, oldest first.",
752
- inputSchema: { planId: z.string().min(1).describe("Plan id to inspect.") },
753
- outputSchema: { versions: z.array(z.unknown()) },
754
- annotations: READ_ONLY
755
- }, async ({ planId }) => {
756
- const versions = await requestJson(requireBaseUrl(), `/api/plans/${encodeURIComponent(planId)}/versions`);
757
- return textResult(versions.map((version) => {
758
- const date = new Date(version.createdAt).toISOString().slice(0, 16).replace("T", " ");
759
- return `v${version.version} — ${date} — ${version.title}`;
760
- }).join("\n") || "No versions recorded.", { versions });
761
- });
762
- server.registerTool("get_plan_version", {
763
- title: "Get a plan version",
764
- description: "Fetch the current or a historical plan version as plan-review XML with version-anchored comments.",
765
- inputSchema: {
766
- planId: z.string().min(1).describe("Plan id to inspect."),
767
- version: z.number().int().positive().optional().describe("Historical version number; omit for current.")
768
- },
769
- outputSchema: {
770
- plan: z.unknown(),
771
- version: z.unknown().optional(),
772
- currentVersion: z.number().optional()
773
- },
774
- annotations: READ_ONLY
775
- }, async ({ planId, version }) => {
776
- const base = requireBaseUrl();
777
- const encodedId = encodeURIComponent(planId);
778
- const plan = await requestJson(base, `/api/plans/${encodedId}`);
779
- if (version === void 0) return textResult(formatPlanReview(plan), { plan });
780
- const data = await requestJson(base, `/api/plans/${encodedId}/versions/${version}`);
781
- return textResult(formatPlanReview(plan, { viewingVersion: data.version.version }), {
782
- plan,
783
- version: data.version,
784
- currentVersion: data.plan.currentVersion
785
- });
786
- });
787
- async function findPlanForComment(base, commentId) {
788
- return (await requestJson(base, "/api/plans")).find((plan) => (plan.comments ?? []).some((comment) => comment.id === commentId)) ?? null;
789
- }
790
- server.registerTool("reply_to_plan_comment", {
791
- title: "Reply to a plan comment",
792
- description: "Post an agent reply to an existing inline plan-review thread.",
793
- inputSchema: {
794
- commentId: z.string().min(1).describe("Plan comment id from get_plan or await_plan_review."),
795
- body: z.string().min(1).describe("Reply body in Markdown."),
796
- model: z.string().optional().describe("Optional agent/model identifier shown in the UI.")
797
- },
798
- outputSchema: {
799
- status: z.literal("replied"),
800
- commentId: z.string(),
801
- planId: z.string()
802
- },
803
- annotations: MUTATING
804
- }, async ({ commentId, body, model }) => {
805
- const base = requireBaseUrl();
806
- const plan = await findPlanForComment(base, commentId);
807
- if (!plan) throw new Error(`Plan comment ${commentId} was not found. Refresh the plan before retrying.`);
808
- await requestJson(base, `/api/plans/${encodeURIComponent(plan.id)}/comments/${encodeURIComponent(commentId)}/replies`, {
809
- method: "POST",
810
- headers: { "Content-Type": "application/json" },
811
- body: JSON.stringify({
812
- body,
813
- role: "agent",
814
- model
815
- })
816
- });
817
- return textResult(`Replied to plan comment ${commentId}.`, {
818
- status: "replied",
819
- commentId,
820
- planId: plan.id
821
- });
822
- });
823
- server.registerTool("resolve_plan_comment", {
824
- title: "Resolve a plan comment",
825
- description: "Mark an inline plan-review thread resolved after it is fully addressed. Safe to retry.",
826
- inputSchema: { commentId: z.string().min(1).describe("Plan comment id to resolve.") },
827
- outputSchema: {
828
- status: z.literal("resolved"),
829
- commentId: z.string(),
830
- planId: z.string()
831
- },
832
- annotations: IDEMPOTENT_MUTATION
833
- }, async ({ commentId }) => {
834
- const base = requireBaseUrl();
835
- const plan = await findPlanForComment(base, commentId);
836
- if (!plan) throw new Error(`Plan comment ${commentId} was not found. Refresh the plan before retrying.`);
837
- await requestJson(base, `/api/plans/${encodeURIComponent(plan.id)}/comments/${encodeURIComponent(commentId)}`, {
838
- method: "PUT",
839
- headers: { "Content-Type": "application/json" },
840
- body: JSON.stringify({ status: "resolved" })
841
- });
842
- return textResult(`Resolved plan comment ${commentId}.`, {
843
- status: "resolved",
844
- commentId,
845
- planId: plan.id
846
- });
847
- });
848
- server.registerResource("agent-guide", "diffing://agent-guide", {
849
- title: "diffing agent guide",
850
- description: "Portable quick reference for the native code-review and plan-review workflows.",
851
- mimeType: "text/markdown"
852
- }, async () => ({ contents: [{
853
- uri: "diffing://agent-guide",
854
- mimeType: "text/markdown",
855
- text: `# diffing agent guide
856
-
857
- This MCP connection is immutably bound to \`${repoRoot}\`.
858
-
859
- ## Code review
860
- 1. Call \`review_session_status\`; call \`start_review_session\` if needed.
861
- 2. Call \`get_diff\` and inspect every changed file.
862
- 3. Use \`create_comment\` for actionable inline findings.
863
- 4. Use \`await_review\` for a human handoff; a timeout is safe to retry.
864
- 5. In \`comment-only\` mode, reply without editing files.
865
-
866
- ## Plan review
867
- 1. Start or reuse a review session.
868
- 2. Call \`submit_plan\`, then \`await_plan_review\`.
869
- 3. On \`changes-requested\`, revise and resubmit with the same \`planId\`.
870
- 4. On \`approved\`, proceed; on \`rejected\`, stop.
871
-
872
- All HTTP activity is loopback-only. MCP never terminates a user-owned session.`
873
- }] }));
874
- server.registerPrompt("review_local_changes", {
875
- title: "Review local changes with diffing",
876
- description: "Workflow prompt for inspecting a local diff and leaving inline review feedback.",
877
- argsSchema: { focus: z.string().optional().describe("Optional review focus such as security, correctness, or tests.") }
878
- }, async ({ focus }) => ({ messages: [{
879
- role: "user",
880
- content: {
881
- type: "text",
882
- 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}` : ""}.`
883
- }
884
- }] }));
885
- server.registerPrompt("submit_plan_for_review", {
886
- title: "Submit an implementation plan for review",
887
- description: "Workflow prompt for submitting a plan and acting on the human verdict.",
888
- argsSchema: { plan: z.string().describe("Complete Markdown implementation plan to submit.") }
889
- }, async ({ plan }) => ({ messages: [{
890
- role: "user",
891
- content: {
892
- type: "text",
893
- text: `Use diffing for plan review in ${repoRoot}. Start or reuse a review session, submit this plan, and await the verdict. On changes-requested revise and resubmit the same planId; on rejected stop; on approved proceed.
894
-
895
- ` + plan
896
- }
897
- }] }));
898
- return server;
899
- }
900
- async function startMcpServer(options = {}) {
901
- const repoRoot = resolveMcpRepository(options.repoPath ?? process.cwd(), options.repoPath !== void 0);
902
- process.chdir(repoRoot);
903
- const server = createMcpServer({ repoRoot });
904
- const cleanupOwnedSession = () => {
905
- const lock = readServerLock(repoRoot);
906
- if (lock?.owner === "mcp" && lock.pid === process.pid) removeServerLock(repoRoot);
907
- };
908
- process.once("exit", cleanupOwnedSession);
909
- process.stdin.once("end", () => process.exit(0));
910
- await server.connect(new StdioServerTransport());
911
- }
912
- //#endregion
913
- export { startMcpServer };