diffing 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +41 -7
  2. package/dist/cli-agent-BatQgghr.mjs +499 -0
  3. package/dist/cli-agent-BwZE05CQ.mjs +499 -0
  4. package/dist/cli-agent-CKE1Cvor.mjs +513 -0
  5. package/dist/cli-agent-CLvCYpmH.mjs +499 -0
  6. package/dist/cli-agent-CX8Fa1Ph.mjs +499 -0
  7. package/dist/cli-agent-SILzesQP.mjs +513 -0
  8. package/dist/cli-agent-USzLoAit.mjs +499 -0
  9. package/dist/cli-gh-BV5az425.mjs +147 -0
  10. package/dist/cli-gh-Bo6fOHQR.mjs +147 -0
  11. package/dist/cli-gh-D1LoWJl-.mjs +147 -0
  12. package/dist/cli-gh-D1sIZ-jX.mjs +147 -0
  13. package/dist/cli-gh-DROAJlHi.mjs +147 -0
  14. package/dist/cli-gh-DiT8BWwu.mjs +147 -0
  15. package/dist/cli.mjs +97 -3381
  16. package/dist/client/assets/{angular-html-DIKVmS-w.js → angular-html-CDZ9SrQX.js} +1 -1
  17. package/dist/client/assets/{angular-ts-DK13mfBQ.js → angular-ts-CNMbVia5.js} +1 -1
  18. package/dist/client/assets/{apl-NVZUxqwo.js → apl-BqNxQgsm.js} +1 -1
  19. package/dist/client/assets/{arc-kijUuucY.js → arc-BrMp-_55.js} +1 -1
  20. package/dist/client/assets/{architectureDiagram-3BPJPVTR-Dup-CzBS.js → architectureDiagram-3BPJPVTR-BIZJkdXp.js} +1 -1
  21. package/dist/client/assets/{astro-RPdCumat.js → astro-r55-Uy0e.js} +1 -1
  22. package/dist/client/assets/{blade-_ubz_KMD.js → blade-F8SZw05t.js} +1 -1
  23. package/dist/client/assets/{blockDiagram-GPEHLZMM-Cl0-G1MY.js → blockDiagram-GPEHLZMM-DlzFca7z.js} +1 -1
  24. package/dist/client/assets/{c-IEKcSTtW.js → c-DMmuyoXv.js} +1 -1
  25. package/dist/client/assets/{c4Diagram-AAUBKEIU-C3T6FV-K.js → c4Diagram-AAUBKEIU-BqJFYdcn.js} +1 -1
  26. package/dist/client/assets/channel-DVxVbqCb.js +1 -0
  27. package/dist/client/assets/{chunk-2J33WTMH-B5MFxFjX.js → chunk-2J33WTMH-CRN3t_Zt.js} +1 -1
  28. package/dist/client/assets/{chunk-3OPIFGDE-DWxCOZgI.js → chunk-3OPIFGDE-Bb8pGWXE.js} +1 -1
  29. package/dist/client/assets/{chunk-4BX2VUAB-DWdovlBC.js → chunk-4BX2VUAB--s6olL4E.js} +1 -1
  30. package/dist/client/assets/{chunk-55IACEB6-CFSaZijP.js → chunk-55IACEB6-C-iaNYfB.js} +1 -1
  31. package/dist/client/assets/{chunk-5ZQYHXKU-BH_cLkIj.js → chunk-5ZQYHXKU-Bvzi1CUr.js} +1 -1
  32. package/dist/client/assets/{chunk-727SXJPM-BbsJC2PT.js → chunk-727SXJPM-CjoyjZeg.js} +1 -1
  33. package/dist/client/assets/{chunk-AQP2D5EJ-tFAP6fC1.js → chunk-AQP2D5EJ-BJJOqjwm.js} +1 -1
  34. package/dist/client/assets/{chunk-BSJP7CBP-BGuhsdwy.js → chunk-BSJP7CBP-A0UL_0xV.js} +1 -1
  35. package/dist/client/assets/{chunk-CSCIHK7Q-DXSJWhsY.js → chunk-CSCIHK7Q-DW570vKT.js} +1 -1
  36. package/dist/client/assets/{chunk-FMBD7UC4-6vBylg_I.js → chunk-FMBD7UC4-CzNJ-Iy-.js} +1 -1
  37. package/dist/client/assets/{chunk-KSCS5N6A-C7iBFsUg.js → chunk-KSCS5N6A-DCbYTAZg.js} +1 -1
  38. package/dist/client/assets/{chunk-L5ZTLDWV-CRU3nvaK.js → chunk-L5ZTLDWV-BMdyyjjc.js} +1 -1
  39. package/dist/client/assets/{chunk-LZXEDZCA-6ppA-_4h.js → chunk-LZXEDZCA-BENQV-I4.js} +2 -2
  40. package/dist/client/assets/{chunk-ND2GUHAM-D1cTmgHe.js → chunk-ND2GUHAM-C1y3WzOx.js} +1 -1
  41. package/dist/client/assets/{chunk-NZK2D7GU-CpUab6v7.js → chunk-NZK2D7GU-BkBT6O_M.js} +1 -1
  42. package/dist/client/assets/{chunk-O5CBEL6O-cDxBycWe.js → chunk-O5CBEL6O-jdz4J9aA.js} +1 -1
  43. package/dist/client/assets/chunk-QZHKN3VN-mumapnEV.js +1 -0
  44. package/dist/client/assets/chunk-WU5MYG2G-Bsqs8ZKl.js +1 -0
  45. package/dist/client/assets/{chunk-XPW4576I-CI9GEEdy.js → chunk-XPW4576I-Bf8OI8wY.js} +1 -1
  46. package/dist/client/assets/classDiagram-4FO5ZUOK-CKaaw3uU.js +1 -0
  47. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BxwzeqDR.js +1 -0
  48. package/dist/client/assets/{cobol-BVhbHJgp.js → cobol-BanHX5pe.js} +1 -1
  49. package/dist/client/assets/{coffee-gY6asS_B.js → coffee-CACo0LEP.js} +1 -1
  50. package/dist/client/assets/{cose-bilkent-S5V4N54A-BOs7zxZa.js → cose-bilkent-S5V4N54A-7mNCsL0j.js} +1 -1
  51. package/dist/client/assets/{cpp-el3R6exS.js → cpp-4Kwtmkwn.js} +1 -1
  52. package/dist/client/assets/{crystal-D2_0btIo.js → crystal-D1kAaPsE.js} +1 -1
  53. package/dist/client/assets/{css-DtPe7dg4.js → css-CCMEBYEn.js} +1 -1
  54. package/dist/client/assets/{dagre-BM42HDAG-CR_0cBOz.js → dagre-BM42HDAG-BTEYZegB.js} +1 -1
  55. package/dist/client/assets/{diagram-2AECGRRQ-D9um0wp2.js → diagram-2AECGRRQ-Cj_XZNTN.js} +1 -1
  56. package/dist/client/assets/{diagram-5GNKFQAL-Dx0TEfBJ.js → diagram-5GNKFQAL-DVTFfYPd.js} +1 -1
  57. package/dist/client/assets/{diagram-KO2AKTUF-DjBHOuUD.js → diagram-KO2AKTUF-C7BU5Ncp.js} +1 -1
  58. package/dist/client/assets/{diagram-LMA3HP47-CK2MO5Gx.js → diagram-LMA3HP47-06rwT7mb.js} +1 -1
  59. package/dist/client/assets/{diagram-OG6HWLK6-CANQancN.js → diagram-OG6HWLK6-COEuKA9B.js} +1 -1
  60. package/dist/client/assets/{dist-DGlwcsQQ.js → dist-C-G1vI6B.js} +1 -1
  61. package/dist/client/assets/{edge-Ch_PYBsB.js → edge-BV-WbkC9.js} +1 -1
  62. package/dist/client/assets/{elixir-DEJrJATq.js → elixir-C6pMvu6H.js} +1 -1
  63. package/dist/client/assets/{elm-Brlko4-d.js → elm-D8LnUrCG.js} +1 -1
  64. package/dist/client/assets/{erDiagram-TEJ5UH35-Dnfrs6yk.js → erDiagram-TEJ5UH35-SPxPVg85.js} +1 -1
  65. package/dist/client/assets/{erb-DQPphNua.js → erb-C6ADBE0Q.js} +1 -1
  66. package/dist/client/assets/{flowDiagram-I6XJVG4X-BNMdFtjG.js → flowDiagram-I6XJVG4X-DyEhCtUv.js} +1 -1
  67. package/dist/client/assets/{ganttDiagram-6RSMTGT7-X-fPXz_A.js → ganttDiagram-6RSMTGT7-fj-zRHIk.js} +1 -1
  68. package/dist/client/assets/{git-rebase-BpS915SI.js → git-rebase-BhYZ4R3x.js} +1 -1
  69. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CRN_dRXM.js → gitGraphDiagram-PVQCEYII-CpUJh4CR.js} +1 -1
  70. package/dist/client/assets/{glimmer-js-DZDngVuD.js → glimmer-js-D2CVO-NF.js} +1 -1
  71. package/dist/client/assets/{glimmer-ts-DKNjJvyp.js → glimmer-ts-BjYisPeM.js} +1 -1
  72. package/dist/client/assets/{glsl-C-Y1tHoU.js → glsl-qP0lyfIY.js} +1 -1
  73. package/dist/client/assets/{graphql-DYVBNazO.js → graphql-BZE_lBur.js} +1 -1
  74. package/dist/client/assets/{hack-DewKAtLe.js → hack-C6KM9SLG.js} +1 -1
  75. package/dist/client/assets/{haml-CIA_AeXu.js → haml-BT3JKqL8.js} +1 -1
  76. package/dist/client/assets/{handlebars-Bade6JXX.js → handlebars-SHKYyRQ6.js} +1 -1
  77. package/dist/client/assets/{html-C4hhbuGp.js → html-CAc9HUGa.js} +1 -1
  78. package/dist/client/assets/{html-derivative-BXfztBtv.js → html-derivative-Cu6n_YRN.js} +1 -1
  79. package/dist/client/assets/{http-Cq90-CTl.js → http-o21PuOy8.js} +1 -1
  80. package/dist/client/assets/{hurl-DklQHYoa.js → hurl-ClrxMWQi.js} +1 -1
  81. package/dist/client/assets/{index-j9gqGQit.js → index-BPI8nJUS.js} +98 -98
  82. package/dist/client/assets/index-F_YfUisJ.css +1 -0
  83. package/dist/client/assets/{infoDiagram-5YYISTIA-Ht-AcvP4.js → infoDiagram-5YYISTIA-DDYGwUUs.js} +1 -1
  84. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-k_l70iFb.js → ishikawaDiagram-YF4QCWOH-Benmqbm6.js} +1 -1
  85. package/dist/client/assets/{java-BMUwLm1h.js → java-Bmlmy6B3.js} +1 -1
  86. package/dist/client/assets/{javascript-B8nPCUVp.js → javascript-Bdrc0lyI.js} +1 -1
  87. package/dist/client/assets/{jinja-CkxiRcB3.js → jinja-DBkct7nV.js} +1 -1
  88. package/dist/client/assets/{jison-CFnN5uQs.js → jison-Tb1xkShc.js} +1 -1
  89. package/dist/client/assets/{journeyDiagram-JHISSGLW-C98PUC9Y.js → journeyDiagram-JHISSGLW-CCkv1GnE.js} +1 -1
  90. package/dist/client/assets/{json-B7HNnjIx.js → json-DwLxGyui.js} +1 -1
  91. package/dist/client/assets/{jsx-BfVPuW3i.js → jsx-DWSSTqEB.js} +1 -1
  92. package/dist/client/assets/{julia-Bj0ETGjT.js → julia-LUN3XhcV.js} +1 -1
  93. package/dist/client/assets/{just-CqIe_c9k.js → just-C6LrD5tm.js} +1 -1
  94. package/dist/client/assets/{kanban-definition-UN3LZRKU-BE-uvH7w.js → kanban-definition-UN3LZRKU-CQSBf6Ys.js} +1 -1
  95. package/dist/client/assets/{latex-Cvy8E64e.js → latex-aaU-rirg.js} +1 -1
  96. package/dist/client/assets/{line-BpooNUgm.js → line-DG7hZYkR.js} +1 -1
  97. package/dist/client/assets/{linear-CM6ZoyCp.js → linear-A5s-ps1u.js} +1 -1
  98. package/dist/client/assets/{liquid-x09l8QqE.js → liquid-BhVOoUfG.js} +1 -1
  99. package/dist/client/assets/{lua-BSlDLjt5.js → lua-BTF9vmdD.js} +1 -1
  100. package/dist/client/assets/{marko-CoNMQlTn.js → marko-DlwBrpd_.js} +1 -1
  101. package/dist/client/assets/{mdc-BSzVESPu.js → mdc-H8mdhEag.js} +1 -1
  102. package/dist/client/assets/{mermaid-parser.core-BDXJMxHu.js → mermaid-parser.core-BKvEMhB3.js} +1 -1
  103. package/dist/client/assets/{mermaid.core-Dw-_vXTc.js → mermaid.core-BaDGp29W.js} +3 -3
  104. package/dist/client/assets/{mindmap-definition-RKZ34NQL-BTxGT8rM.js → mindmap-definition-RKZ34NQL-BCrf7cJv.js} +1 -1
  105. package/dist/client/assets/{nginx-CBhJTj0i.js → nginx-DoaqXoFJ.js} +1 -1
  106. package/dist/client/assets/{nim-CtHbOBlO.js → nim-DqoAP4Np.js} +1 -1
  107. package/dist/client/assets/{perl-DLMzG3tu.js → perl-D1ou0isN.js} +1 -1
  108. package/dist/client/assets/{php-KomVR3Hz.js → php-DL6kQj3i.js} +1 -1
  109. package/dist/client/assets/{pieDiagram-4H26LBE5-BBUObAJW.js → pieDiagram-4H26LBE5-CF0X4ECG.js} +1 -1
  110. package/dist/client/assets/{pug-T4SRLx8P.js → pug-Bn3xDPVT.js} +1 -1
  111. package/dist/client/assets/{qml-DJdSRUmU.js → qml-CaA5S6hT.js} +1 -1
  112. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-B7vvauZv.js → quadrantDiagram-W4KKPZXB-A76pK4Cy.js} +1 -1
  113. package/dist/client/assets/{r-CwuTg9lK.js → r-DuUvfJBZ.js} +1 -1
  114. package/dist/client/assets/{razor-Ch4dgsGY.js → razor-DOv4xAWl.js} +1 -1
  115. package/dist/client/assets/{regexp-D2GTwJE0.js → regexp-DXetztjB.js} +1 -1
  116. package/dist/client/assets/{requirementDiagram-4Y6WPE33-DpcNiR3Y.js → requirementDiagram-4Y6WPE33-Cju--fB8.js} +1 -1
  117. package/dist/client/assets/{rst-D78f0DyR.js → rst-CDn5EUee.js} +1 -1
  118. package/dist/client/assets/{ruby-BUcw6ITs.js → ruby-40qcnJX-.js} +1 -1
  119. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-ChzSQD2O.js → sankeyDiagram-5OEKKPKP-CvkQY1O-.js} +1 -1
  120. package/dist/client/assets/{sas-DoT2JDsj.js → sas-D3a9oRtu.js} +1 -1
  121. package/dist/client/assets/{scss-B6G_d-6b.js → scss-BJ8ZKq49.js} +1 -1
  122. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-CzkdL8s7.js → sequenceDiagram-3UESZ5HK-DKU8lZlC.js} +1 -1
  123. package/dist/client/assets/{shellscript-DI252Bga.js → shellscript-yTgK8aYT.js} +1 -1
  124. package/dist/client/assets/{shellsession-BuO_s8FQ.js → shellsession-DrVaQSNg.js} +1 -1
  125. package/dist/client/assets/{soy-DZTZJTEE.js → soy-BasCadaZ.js} +1 -1
  126. package/dist/client/assets/{sql-C4isoJr_.js → sql-COJH6ALP.js} +1 -1
  127. package/dist/client/assets/{src-BMvz5HE8.js → src-BokBJ4oL.js} +1 -1
  128. package/dist/client/assets/{stata-Dhu6qITh.js → stata-Ble9lhMp.js} +1 -1
  129. package/dist/client/assets/{stateDiagram-AJRCARHV-DvYcrdap.js → stateDiagram-AJRCARHV-9IU9PT8A.js} +1 -1
  130. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Ce46Lf8G.js +1 -0
  131. package/dist/client/assets/{surrealql-CSDpq5j3.js → surrealql-BZ5cQJLk.js} +1 -1
  132. package/dist/client/assets/{svelte-C4ITAvxr.js → svelte-CLa9l6VK.js} +1 -1
  133. package/dist/client/assets/{templ-BTgKA9-7.js → templ-DVKkE2Tb.js} +1 -1
  134. package/dist/client/assets/{tex-Dg4N9ndP.js → tex-BET3iISl.js} +1 -1
  135. package/dist/client/assets/{timeline-definition-PNZ67QCA-Djt5Lwk2.js → timeline-definition-PNZ67QCA-CkkYIhNz.js} +1 -1
  136. package/dist/client/assets/{ts-tags-BufYnYos.js → ts-tags-D5fPAs5W.js} +1 -1
  137. package/dist/client/assets/{tsx-DmkFP0sw.js → tsx-CfNzz9iB.js} +1 -1
  138. package/dist/client/assets/{twig-BJv_IWYK.js → twig-_Ml9CBGK.js} +1 -1
  139. package/dist/client/assets/{typescript-BKp05Qme.js → typescript-DMiV0dhX.js} +1 -1
  140. package/dist/client/assets/{vennDiagram-CIIHVFJN-DDVADTIa.js → vennDiagram-CIIHVFJN-HUv6L4Dt.js} +1 -1
  141. package/dist/client/assets/{vue-BsHXCXzh.js → vue-BcbmWIQt.js} +1 -1
  142. package/dist/client/assets/{vue-html-CCsH-lmo.js → vue-html-CcUgk6WA.js} +1 -1
  143. package/dist/client/assets/{vue-vine-B5P1JXxo.js → vue-vine-BbIBT3jy.js} +1 -1
  144. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-yeVKWvtX.js → wardleyDiagram-YWT4CUSO-avUfWkuC.js} +1 -1
  145. package/dist/client/assets/{xml-CFkhYff0.js → xml-9mtlBTUG.js} +1 -1
  146. package/dist/client/assets/{xsl-Dc5XRzm6.js → xsl-Dm8BN5j0.js} +1 -1
  147. package/dist/client/assets/{xychartDiagram-2RQKCTM6--8Ik-9v0.js → xychartDiagram-2RQKCTM6-BSZPOH7-.js} +1 -1
  148. package/dist/client/assets/{yaml-BTCCxCxi.js → yaml-Ccz1qKlL.js} +1 -1
  149. package/dist/client/index.html +2 -2
  150. package/dist/mcp-B6mCC7D3.mjs +1047 -0
  151. package/dist/mcp-BMTZLQuB.mjs +758 -0
  152. package/dist/mcp-BUF2nEL7.mjs +1036 -0
  153. package/dist/mcp-Bpx76OGU.mjs +945 -0
  154. package/dist/mcp-C1T7LI0H.mjs +1036 -0
  155. package/dist/mcp-C4Bjmf9m.mjs +1047 -0
  156. package/dist/mcp-CFxcdEcJ.mjs +733 -0
  157. package/dist/mcp-CwjThGi2.mjs +913 -0
  158. package/dist/mcp-D4OsB9lt.mjs +1047 -0
  159. package/dist/mcp-DJ2batvt.mjs +913 -0
  160. package/dist/mcp-D_LGvBTw.mjs +1047 -0
  161. package/dist/mcp-Ds_lPX_o.mjs +913 -0
  162. package/dist/plan-format-B6nbAw_c.mjs +132 -0
  163. package/dist/server-95UE5bgh.mjs +3367 -0
  164. package/dist/server-B0cS95Lf.mjs +3488 -0
  165. package/dist/server-Bj-6KlK5.mjs +3367 -0
  166. package/dist/server-Bpf84Il0.mjs +3367 -0
  167. package/dist/server-CYSpLbdA.mjs +3360 -0
  168. package/dist/server-ChT2DCaR.mjs +3701 -0
  169. package/dist/server-DJxysGxV.mjs +3701 -0
  170. package/dist/server-Dov7jbV3.mjs +3367 -0
  171. package/dist/server-eQ2E4Axo.mjs +3367 -0
  172. package/dist/server-lock-C_wPtEsH.mjs +909 -0
  173. package/dist/server-lock-Cbu3qHdu.mjs +924 -0
  174. package/dist/server-lock-D32inEn9.mjs +924 -0
  175. package/dist/server-lock-Ddcat0HF.mjs +851 -0
  176. package/dist/server-lock-Dq_1CJaF.mjs +1047 -0
  177. package/dist/server-lock-Ujbcgq28.mjs +954 -0
  178. package/dist/update-check-CXeSZANo.mjs +129 -0
  179. package/package.json +3 -2
  180. package/dist/client/assets/channel-CpWSvbvQ.js +0 -1
  181. package/dist/client/assets/chunk-QZHKN3VN-HhMIifwl.js +0 -1
  182. package/dist/client/assets/chunk-WU5MYG2G-CvIzXEms.js +0 -1
  183. package/dist/client/assets/classDiagram-4FO5ZUOK-DFu-dNVy.js +0 -1
  184. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-D7Xcgo6q.js +0 -1
  185. package/dist/client/assets/index-CMyunQt6.css +0 -1
  186. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-D5igY81I.js +0 -1
package/README.md CHANGED
@@ -276,7 +276,9 @@ diffing plan resolve <commentId> # Mark a plan comment resolved
276
276
  ```
277
277
 
278
278
  ### B. Model Context Protocol (MCP) Server
279
- If your agent supports MCP (such as Cursor, Claude Desktop, or Gemini), configure `diffing` as a stdio-based MCP server. No ports need to be configured:
279
+ If your agent supports MCP, configure `diffing` as a stdio server. The MCP is
280
+ self-describing and can start or reuse the loopback review server, so an
281
+ MCP-only agent does not need to know a port or fall back to raw HTTP:
280
282
 
281
283
  ```json
282
284
  {
@@ -288,18 +290,50 @@ If your agent supports MCP (such as Cursor, Claude Desktop, or Gemini), configur
288
290
  }
289
291
  }
290
292
  ```
291
- Exposes tools directly to your agent for both review flows — diff review: `await_review`, `list_comments`, `reply_to_comment`, `resolve_comment`; and plan review: `submit_plan`, `await_plan_review`, `list_plans`, `get_plan`, `get_plan_versions`, `get_plan_version`, `reply_to_plan_comment`, `resolve_plan_comment`.
293
+
294
+ Global/desktop clients that do not launch MCP from the workspace should bind it
295
+ to one repository explicitly:
296
+
297
+ ```json
298
+ {
299
+ "mcpServers": {
300
+ "diffing": {
301
+ "command": "diffing",
302
+ "args": ["mcp", "--repo", "/absolute/path/to/repository"]
303
+ }
304
+ }
305
+ }
306
+ ```
307
+
308
+ Run `diffing mcp --help` for setup details. The server initialization
309
+ instructions tell an unfamiliar model what to do next, and every tool returns
310
+ both readable text and structured content.
311
+
312
+ - Session: `review_session_status`, `start_review_session`
313
+ - Local review: `get_diff`, `create_comment`, `await_review`, `list_comments`, `reply_to_comment`, `resolve_comment`
314
+ - Plan review: `submit_plan`, `await_plan_review`, `list_plans`, `get_plan`, `get_plan_versions`, `get_plan_version`, `reply_to_plan_comment`, `resolve_plan_comment`
315
+
316
+ Clients that expose MCP prompts can use `review_local_changes` and
317
+ `submit_plan_for_review`; clients that expose resources can read
318
+ `diffing://agent-guide`. Essential guidance is also present in initialization
319
+ instructions and tool descriptions because prompt/resource support varies.
292
320
 
293
321
  ### C. Agent Skills
294
322
  You can install diffing skills directly into your AI coding assistant:
295
323
  ```bash
296
324
  npx skills add ahmedragab20/diffing
297
325
  ```
298
- Provides commands to coordinate reviews:
299
- 1. **`/diffing-start-review`** — Launches the review server.
300
- 2. **`/diffing-finish-review`** — Blocks the agent using `await-review` until comments are sent, then applies requested edits.
301
- 3. **`/diffing-review`** — Combined launch-and-wait flow.
302
- 4. **`/diffing-plan-review`** — Submit a markdown plan, block until the human approves/rejects/requests changes, then proceed or revise.
326
+ Installs five portable, natural-language-triggered workflows:
327
+
328
+ 1. **`diffing`** — Detects MCP/CLI/offline capabilities and routes the request.
329
+ 2. **`diffing-start-review`** — Starts or reuses the review session and returns its URL.
330
+ 3. **`diffing-finish-review`** — Waits for human feedback, applies clear requests, and synchronizes replies/resolutions live.
331
+ 4. **`diffing-review`** — Inspects the full local diff and posts actionable inline findings.
332
+ 5. **`diffing-plan-review`** — Gates implementation on a reviewed plan and deterministic verdict handling.
333
+
334
+ The skills prefer native MCP tools, fall back to the port-agnostic CLI, and can
335
+ still interpret pasted self-describing XML. The installable `skills/` copies and
336
+ repo-local `.agents/skills/` copies are contract-tested to remain identical.
303
337
 
304
338
  ### Send Review Popover
305
339
  A GitHub-style "finish your review" popover with inline editing of each comment, an optional general/overall comment, and a visual indicator when an agent is waiting. The **"Copy comments"** toolbar button serializes all comments to the XML spec and copies them to the clipboard.
@@ -0,0 +1,499 @@
1
+ import { i as readServerLock, r as isLockAlive, s as formatComments } from "./server-lock-C_wPtEsH.mjs";
2
+ import { n as formatPlanReview } from "./plan-format-B6nbAw_c.mjs";
3
+ import { parseArgs } from "node:util";
4
+ import { readFile } from "node:fs/promises";
5
+ //#region src/cli-agent.ts
6
+ /**
7
+ * Agent-facing `diffing` subcommands. These make the user→agent handoff
8
+ * port-agnostic: each resolves the running server via the per-repo lockfile
9
+ * (`server.json`) so any agent with a shell — or a human — can drive the loop
10
+ * without being told a port.
11
+ *
12
+ * diffing await-review block until the human clicks "Send to agent"
13
+ * diffing reply <id> post an agent reply to a comment
14
+ * diffing resolve <id> mark a comment resolved
15
+ * diffing comments dump the current comments (XML or JSON)
16
+ */
17
+ const EXIT_OK = 0;
18
+ const EXIT_AWAIT_TIMEOUT = 2;
19
+ const EXIT_NO_SERVER = 3;
20
+ const EXIT_NOT_FOUND = 4;
21
+ const EXIT_USAGE = 5;
22
+ /** Resolve the running server's base URL from the lockfile, or exit cleanly. */
23
+ function baseUrl() {
24
+ const lock = readServerLock();
25
+ if (!lock || !isLockAlive(lock)) {
26
+ console.error("No diffing server running for this repo. Start one with `diffing`.");
27
+ process.exit(EXIT_NO_SERVER);
28
+ }
29
+ return `http://${lock.host === "0.0.0.0" || lock.host === "::" ? "127.0.0.1" : lock.host}:${lock.port}`;
30
+ }
31
+ async function readStdin() {
32
+ const chunks = [];
33
+ for await (const chunk of process.stdin) chunks.push(chunk);
34
+ return Buffer.concat(chunks).toString("utf-8");
35
+ }
36
+ async function awaitReview(args) {
37
+ const { values } = parseArgs({
38
+ args,
39
+ options: {
40
+ timeout: {
41
+ type: "string",
42
+ short: "t"
43
+ },
44
+ since: { type: "string" }
45
+ },
46
+ allowPositionals: false
47
+ });
48
+ const totalBudgetMs = (values.timeout ? Number(values.timeout) : 570) * 1e3;
49
+ const base = baseUrl();
50
+ let sinceRound = 0;
51
+ try {
52
+ sinceRound = (await fetch(`${base}/api/review/status`).then((r) => r.json())).round ?? 0;
53
+ } catch {}
54
+ const deadline = Date.now() + totalBudgetMs;
55
+ while (Date.now() < deadline) {
56
+ let res;
57
+ try {
58
+ res = await fetch(`${base}/api/review/await?timeoutMs=25000&sinceRound=${sinceRound}`, { signal: AbortSignal.timeout(3e4) });
59
+ } catch (err) {
60
+ if (err?.name === "TimeoutError") continue;
61
+ console.error(`Failed to reach diffing server: ${err?.message ?? err}`);
62
+ return EXIT_NO_SERVER;
63
+ }
64
+ const result = await res.json();
65
+ if (result.status === "released") {
66
+ process.stdout.write(result.payload.commentXml + "\n");
67
+ console.error(`DIFFING_REVIEW_ROUND=${result.payload.round}`);
68
+ return EXIT_OK;
69
+ }
70
+ sinceRound = result.round ?? sinceRound;
71
+ }
72
+ console.error("DIFFING_AWAIT_TIMEOUT");
73
+ console.error("No review sent within the timeout. Run `diffing await-review` again to keep waiting.");
74
+ return EXIT_AWAIT_TIMEOUT;
75
+ }
76
+ async function reply(args) {
77
+ const { values, positionals } = parseArgs({
78
+ args,
79
+ options: {
80
+ body: {
81
+ type: "string",
82
+ short: "b"
83
+ },
84
+ model: {
85
+ type: "string",
86
+ short: "m"
87
+ }
88
+ },
89
+ allowPositionals: true
90
+ });
91
+ const commentId = positionals[0];
92
+ if (!commentId) {
93
+ console.error("Usage: diffing reply <commentId> --body <text> [--model <name>]");
94
+ return EXIT_USAGE;
95
+ }
96
+ let body = values.body;
97
+ if (body === "-" || body === void 0) body = (await readStdin()).trim();
98
+ if (!body) {
99
+ console.error("A reply body is required (--body <text> or pipe via stdin).");
100
+ return EXIT_USAGE;
101
+ }
102
+ const res = await fetch(`${baseUrl()}/api/comments/${commentId}/replies`, {
103
+ method: "POST",
104
+ headers: { "Content-Type": "application/json" },
105
+ body: JSON.stringify({
106
+ body,
107
+ role: "agent",
108
+ model: values.model
109
+ })
110
+ });
111
+ if (res.status === 404) {
112
+ console.error(`Comment ${commentId} not found.`);
113
+ return EXIT_NOT_FOUND;
114
+ }
115
+ if (!res.ok) {
116
+ console.error(`Failed to reply: HTTP ${res.status}`);
117
+ return 1;
118
+ }
119
+ console.error(`Replied to ${commentId}.`);
120
+ return EXIT_OK;
121
+ }
122
+ async function resolve(args) {
123
+ const { positionals } = parseArgs({
124
+ args,
125
+ allowPositionals: true,
126
+ options: {}
127
+ });
128
+ const commentId = positionals[0];
129
+ if (!commentId) {
130
+ console.error("Usage: diffing resolve <commentId>");
131
+ return EXIT_USAGE;
132
+ }
133
+ const res = await fetch(`${baseUrl()}/api/comments/${commentId}`, {
134
+ method: "PUT",
135
+ headers: { "Content-Type": "application/json" },
136
+ body: JSON.stringify({ status: "resolved" })
137
+ });
138
+ if (res.status === 404) {
139
+ console.error(`Comment ${commentId} not found.`);
140
+ return EXIT_NOT_FOUND;
141
+ }
142
+ if (!res.ok) {
143
+ console.error(`Failed to resolve: HTTP ${res.status}`);
144
+ return 1;
145
+ }
146
+ console.error(`Resolved ${commentId}.`);
147
+ return EXIT_OK;
148
+ }
149
+ async function url() {
150
+ process.stdout.write(baseUrl() + "\n");
151
+ return EXIT_OK;
152
+ }
153
+ async function comments(args) {
154
+ const { values } = parseArgs({
155
+ args,
156
+ options: {
157
+ open: { type: "boolean" },
158
+ json: { type: "boolean" }
159
+ },
160
+ allowPositionals: false
161
+ });
162
+ const all = await fetch(`${baseUrl()}/api/comments`).then((r) => r.json());
163
+ const selected = values.open ? all.filter((c) => c.status === "open") : all;
164
+ if (values.json) process.stdout.write(JSON.stringify(selected, null, 2) + "\n");
165
+ else process.stdout.write(formatComments(selected) + "\n");
166
+ return EXIT_OK;
167
+ }
168
+ /** Derive a human title from a plan's first heading or non-empty line. */
169
+ function deriveTitle(body) {
170
+ for (const raw of body.split("\n")) {
171
+ const line = raw.trim();
172
+ if (!line) continue;
173
+ const heading = /^#{1,6}\s+(.*)$/.exec(line);
174
+ return (heading ? heading[1] : line).slice(0, 120);
175
+ }
176
+ return "Untitled plan";
177
+ }
178
+ /** Long-poll the plan-review handoff until a decision arrives or time runs out. */
179
+ async function pollPlanDecision(base, totalBudgetMs, seedSince) {
180
+ let sinceRound = seedSince ?? 0;
181
+ if (seedSince === void 0) try {
182
+ sinceRound = (await fetch(`${base}/api/plan-review/status`).then((r) => r.json())).round ?? 0;
183
+ } catch {}
184
+ const deadline = Date.now() + totalBudgetMs;
185
+ while (Date.now() < deadline) {
186
+ let res;
187
+ try {
188
+ res = await fetch(`${base}/api/plan-review/await?timeoutMs=25000&sinceRound=${sinceRound}`, { signal: AbortSignal.timeout(3e4) });
189
+ } catch (err) {
190
+ if (err?.name === "TimeoutError") continue;
191
+ console.error(`Failed to reach diffing server: ${err?.message ?? err}`);
192
+ return EXIT_NO_SERVER;
193
+ }
194
+ const result = await res.json();
195
+ if (result.status === "released") {
196
+ process.stdout.write(result.payload.reviewXml + "\n");
197
+ console.error(`DIFFING_PLAN_DECISION=${result.payload.decision}`);
198
+ console.error(`DIFFING_PLAN_ROUND=${result.payload.round}`);
199
+ return EXIT_OK;
200
+ }
201
+ sinceRound = result.round ?? sinceRound;
202
+ }
203
+ console.error("DIFFING_PLAN_AWAIT_TIMEOUT");
204
+ console.error("No plan decision within the timeout. Run `diffing plan await` again to keep waiting.");
205
+ return EXIT_AWAIT_TIMEOUT;
206
+ }
207
+ async function planSubmit(args) {
208
+ const { values, positionals } = parseArgs({
209
+ args,
210
+ options: {
211
+ title: { type: "string" },
212
+ source: {
213
+ type: "string",
214
+ short: "s"
215
+ },
216
+ model: {
217
+ type: "string",
218
+ short: "m"
219
+ },
220
+ id: { type: "string" },
221
+ wait: {
222
+ type: "boolean",
223
+ short: "w"
224
+ },
225
+ timeout: {
226
+ type: "string",
227
+ short: "t"
228
+ }
229
+ },
230
+ allowPositionals: true
231
+ });
232
+ const file = positionals[0];
233
+ let body;
234
+ if (!file || file === "-") body = await readStdin();
235
+ else try {
236
+ body = await readFile(file, "utf-8");
237
+ } catch (err) {
238
+ console.error(`Failed to read plan file ${file}: ${err?.message ?? err}`);
239
+ return EXIT_USAGE;
240
+ }
241
+ body = body.replace(/\r\n/g, "\n");
242
+ if (!body.trim()) {
243
+ console.error("A plan body is required (pass a markdown file path or pipe via stdin).");
244
+ return EXIT_USAGE;
245
+ }
246
+ const title = values.title || deriveTitle(body);
247
+ const base = baseUrl();
248
+ let sinceRound = 0;
249
+ if (values.wait) try {
250
+ sinceRound = (await fetch(`${base}/api/plan-review/status`).then((r) => r.json())).round ?? 0;
251
+ } catch {}
252
+ const res = await fetch(`${base}/api/plans`, {
253
+ method: "POST",
254
+ headers: { "Content-Type": "application/json" },
255
+ body: JSON.stringify({
256
+ id: values.id,
257
+ title,
258
+ body,
259
+ source: values.source,
260
+ model: values.model
261
+ })
262
+ });
263
+ if (!res.ok) {
264
+ console.error(`Failed to submit plan: HTTP ${res.status}`);
265
+ return 1;
266
+ }
267
+ const plan = await res.json();
268
+ console.error(`Submitted plan ${plan.id} (v${plan.version}) — review at ${base}/plan/${plan.id}`);
269
+ if (!values.wait) {
270
+ process.stdout.write(plan.id + "\n");
271
+ return EXIT_OK;
272
+ }
273
+ return pollPlanDecision(base, (values.timeout ? Number(values.timeout) : 570) * 1e3, sinceRound);
274
+ }
275
+ async function planAwait(args) {
276
+ const { values } = parseArgs({
277
+ args,
278
+ options: { timeout: {
279
+ type: "string",
280
+ short: "t"
281
+ } },
282
+ allowPositionals: false
283
+ });
284
+ const totalBudgetMs = (values.timeout ? Number(values.timeout) : 570) * 1e3;
285
+ return pollPlanDecision(baseUrl(), totalBudgetMs);
286
+ }
287
+ async function planList(args) {
288
+ const { values } = parseArgs({
289
+ args,
290
+ options: { json: { type: "boolean" } },
291
+ allowPositionals: false
292
+ });
293
+ const all = await fetch(`${baseUrl()}/api/plans`).then((r) => r.json());
294
+ if (values.json) {
295
+ process.stdout.write(JSON.stringify(all, null, 2) + "\n");
296
+ return EXIT_OK;
297
+ }
298
+ if (all.length === 0) {
299
+ console.error("No plans submitted yet.");
300
+ return EXIT_OK;
301
+ }
302
+ for (const p of all) {
303
+ const open = (p.comments ?? []).filter((c) => c.status === "open").length;
304
+ process.stdout.write(`${p.id}\t[${p.decision}]\tv${p.version}\t${open} open comment(s)\t${p.title}\n`);
305
+ }
306
+ return EXIT_OK;
307
+ }
308
+ async function planShow(args) {
309
+ const { values, positionals } = parseArgs({
310
+ args,
311
+ options: {
312
+ json: { type: "boolean" },
313
+ version: { type: "string" }
314
+ },
315
+ allowPositionals: true
316
+ });
317
+ const base = baseUrl();
318
+ let planId = positionals[0];
319
+ if (!planId) {
320
+ const all = await fetch(`${base}/api/plans`).then((r) => r.json());
321
+ if (all.length === 0) {
322
+ console.error("No plans submitted yet.");
323
+ return EXIT_NOT_FOUND;
324
+ }
325
+ planId = all[all.length - 1].id;
326
+ }
327
+ const res = await fetch(`${base}/api/plans/${planId}`);
328
+ if (res.status === 404) {
329
+ console.error(`Plan ${planId} not found.`);
330
+ return EXIT_NOT_FOUND;
331
+ }
332
+ const plan = await res.json();
333
+ const requestedVersion = values.version !== void 0 ? Number(values.version) : void 0;
334
+ if (requestedVersion !== void 0 && (!Number.isFinite(requestedVersion) || requestedVersion < 1)) {
335
+ console.error(`--version must be a positive integer.`);
336
+ return EXIT_USAGE;
337
+ }
338
+ if (requestedVersion !== void 0 && requestedVersion !== plan.version) {
339
+ if (!(plan.versions ?? []).find((v) => v.version === requestedVersion)) {
340
+ console.error(`Version ${requestedVersion} not found for plan ${planId} (current: v${plan.version}).`);
341
+ return EXIT_NOT_FOUND;
342
+ }
343
+ }
344
+ if (values.json) process.stdout.write(JSON.stringify(plan, null, 2) + "\n");
345
+ else process.stdout.write(formatPlanReview(plan, { viewingVersion: requestedVersion }) + "\n");
346
+ return EXIT_OK;
347
+ }
348
+ async function planVersions(args) {
349
+ const { values, positionals } = parseArgs({
350
+ args,
351
+ options: { json: { type: "boolean" } },
352
+ allowPositionals: true
353
+ });
354
+ const base = baseUrl();
355
+ let planId = positionals[0];
356
+ if (!planId) {
357
+ console.error("Usage: diffing plan versions <id> [--json]");
358
+ return EXIT_USAGE;
359
+ }
360
+ const planRes = await fetch(`${base}/api/plans/${planId}`);
361
+ if (planRes.status === 404) {
362
+ console.error(`Plan ${planId} not found.`);
363
+ return EXIT_NOT_FOUND;
364
+ }
365
+ const plan = await planRes.json();
366
+ const versions = plan.versions ?? [];
367
+ if (values.json) {
368
+ process.stdout.write(JSON.stringify(versions, null, 2) + "\n");
369
+ return EXIT_OK;
370
+ }
371
+ if (versions.length === 0) {
372
+ console.error("This plan has no recorded versions.");
373
+ return EXIT_OK;
374
+ }
375
+ for (const v of versions) {
376
+ const marker = v.version === plan.version ? "*" : " ";
377
+ const date = new Date(v.createdAt).toISOString().slice(0, 16).replace("T", " ");
378
+ process.stdout.write(`${marker} v${v.version}\t${date}\t${v.title}\n`);
379
+ }
380
+ return EXIT_OK;
381
+ }
382
+ /** Locate which plan owns a given comment id (comment ids are globally unique). */
383
+ async function findCommentPlan(base, commentId) {
384
+ return (await fetch(`${base}/api/plans`).then((r) => r.json())).find((p) => (p.comments ?? []).some((c) => c.id === commentId)) ?? null;
385
+ }
386
+ async function planReply(args) {
387
+ const { values, positionals } = parseArgs({
388
+ args,
389
+ options: {
390
+ body: {
391
+ type: "string",
392
+ short: "b"
393
+ },
394
+ model: {
395
+ type: "string",
396
+ short: "m"
397
+ }
398
+ },
399
+ allowPositionals: true
400
+ });
401
+ const commentId = positionals[0];
402
+ if (!commentId) {
403
+ console.error("Usage: diffing plan reply <commentId> --body <text> [--model <name>]");
404
+ return EXIT_USAGE;
405
+ }
406
+ let body = values.body;
407
+ if (body === "-" || body === void 0) body = (await readStdin()).trim();
408
+ if (!body) {
409
+ console.error("A reply body is required (--body <text> or pipe via stdin).");
410
+ return EXIT_USAGE;
411
+ }
412
+ const base = baseUrl();
413
+ const plan = await findCommentPlan(base, commentId);
414
+ if (!plan) {
415
+ console.error(`Plan comment ${commentId} not found.`);
416
+ return EXIT_NOT_FOUND;
417
+ }
418
+ const res = await fetch(`${base}/api/plans/${plan.id}/comments/${commentId}/replies`, {
419
+ method: "POST",
420
+ headers: { "Content-Type": "application/json" },
421
+ body: JSON.stringify({
422
+ body,
423
+ role: "agent",
424
+ model: values.model
425
+ })
426
+ });
427
+ if (!res.ok) {
428
+ console.error(`Failed to reply: HTTP ${res.status}`);
429
+ return 1;
430
+ }
431
+ console.error(`Replied to plan comment ${commentId}.`);
432
+ return EXIT_OK;
433
+ }
434
+ async function planResolve(args) {
435
+ const { positionals } = parseArgs({
436
+ args,
437
+ allowPositionals: true,
438
+ options: {}
439
+ });
440
+ const commentId = positionals[0];
441
+ if (!commentId) {
442
+ console.error("Usage: diffing plan resolve <commentId>");
443
+ return EXIT_USAGE;
444
+ }
445
+ const base = baseUrl();
446
+ const plan = await findCommentPlan(base, commentId);
447
+ if (!plan) {
448
+ console.error(`Plan comment ${commentId} not found.`);
449
+ return EXIT_NOT_FOUND;
450
+ }
451
+ const res = await fetch(`${base}/api/plans/${plan.id}/comments/${commentId}`, {
452
+ method: "PUT",
453
+ headers: { "Content-Type": "application/json" },
454
+ body: JSON.stringify({ status: "resolved" })
455
+ });
456
+ if (!res.ok) {
457
+ console.error(`Failed to resolve: HTTP ${res.status}`);
458
+ return 1;
459
+ }
460
+ console.error(`Resolved plan comment ${commentId}.`);
461
+ return EXIT_OK;
462
+ }
463
+ async function plan(args) {
464
+ const action = args[0];
465
+ const rest = args.slice(1);
466
+ switch (action) {
467
+ case "submit": return planSubmit(rest);
468
+ case "await": return planAwait(rest);
469
+ case "list": return planList(rest);
470
+ case "show": return planShow(rest);
471
+ case "versions": return planVersions(rest);
472
+ case "reply": return planReply(rest);
473
+ case "resolve": return planResolve(rest);
474
+ default:
475
+ console.error("Usage: diffing plan <submit|await|list|show|versions|reply|resolve> [...]");
476
+ return EXIT_USAGE;
477
+ }
478
+ }
479
+ async function runSubcommand(name, args) {
480
+ switch (name) {
481
+ case "await-review": return awaitReview(args);
482
+ case "reply": return reply(args);
483
+ case "resolve": return resolve(args);
484
+ case "comments": return comments(args);
485
+ case "url": return url();
486
+ case "plan": return plan(args);
487
+ case "update":
488
+ const { runUpdateCommand } = await import("./update-check-CXeSZANo.mjs");
489
+ return runUpdateCommand();
490
+ case "gh":
491
+ const { runGhSubcommand } = await import("./cli-gh-DiT8BWwu.mjs");
492
+ return runGhSubcommand(args);
493
+ default:
494
+ console.error(`Unknown subcommand: ${name}`);
495
+ return EXIT_USAGE;
496
+ }
497
+ }
498
+ //#endregion
499
+ export { runSubcommand };