diffing 0.3.0 → 0.4.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 (152) hide show
  1. package/README.md +11 -2
  2. package/dist/cli-agent-B4hU6Xay.mjs +499 -0
  3. package/dist/cli-agent-B_PHd635.mjs +499 -0
  4. package/dist/cli-gh-abs-IJrz.mjs +147 -0
  5. package/dist/cli.mjs +204 -58
  6. package/dist/client/assets/{angular-html-DbbrJavS.js → angular-html-DIKVmS-w.js} +1 -1
  7. package/dist/client/assets/{angular-ts-tCrxb1ht.js → angular-ts-DK13mfBQ.js} +1 -1
  8. package/dist/client/assets/{apl-DawII0w7.js → apl-NVZUxqwo.js} +1 -1
  9. package/dist/client/assets/{arc-DIRV7ZXr.js → arc-kijUuucY.js} +1 -1
  10. package/dist/client/assets/{architectureDiagram-3BPJPVTR-3IoH78OV.js → architectureDiagram-3BPJPVTR-Dup-CzBS.js} +1 -1
  11. package/dist/client/assets/{astro-DIyGTFXc.js → astro-RPdCumat.js} +1 -1
  12. package/dist/client/assets/{blade-CuF4Z1mP.js → blade-_ubz_KMD.js} +1 -1
  13. package/dist/client/assets/{blockDiagram-GPEHLZMM-CFxPndC0.js → blockDiagram-GPEHLZMM-Cl0-G1MY.js} +1 -1
  14. package/dist/client/assets/{c-DUrjo9T5.js → c-IEKcSTtW.js} +1 -1
  15. package/dist/client/assets/{c4Diagram-AAUBKEIU-CGMBnkiv.js → c4Diagram-AAUBKEIU-C3T6FV-K.js} +1 -1
  16. package/dist/client/assets/channel-CpWSvbvQ.js +1 -0
  17. package/dist/client/assets/{chunk-2J33WTMH-CJMBLaXy.js → chunk-2J33WTMH-B5MFxFjX.js} +1 -1
  18. package/dist/client/assets/{chunk-3OPIFGDE-C9HQWVLl.js → chunk-3OPIFGDE-DWxCOZgI.js} +1 -1
  19. package/dist/client/assets/{chunk-4BX2VUAB-Qqg72GQd.js → chunk-4BX2VUAB-DWdovlBC.js} +1 -1
  20. package/dist/client/assets/{chunk-55IACEB6-DZF8brTR.js → chunk-55IACEB6-CFSaZijP.js} +1 -1
  21. package/dist/client/assets/{chunk-5ZQYHXKU-BflVFV5z.js → chunk-5ZQYHXKU-BH_cLkIj.js} +1 -1
  22. package/dist/client/assets/{chunk-727SXJPM-COn6BRLY.js → chunk-727SXJPM-BbsJC2PT.js} +1 -1
  23. package/dist/client/assets/{chunk-AQP2D5EJ-CNOjODUR.js → chunk-AQP2D5EJ-tFAP6fC1.js} +1 -1
  24. package/dist/client/assets/{chunk-BSJP7CBP-PQX4ASCI.js → chunk-BSJP7CBP-BGuhsdwy.js} +1 -1
  25. package/dist/client/assets/{chunk-CSCIHK7Q-BfmUvL-L.js → chunk-CSCIHK7Q-DXSJWhsY.js} +1 -1
  26. package/dist/client/assets/{chunk-FMBD7UC4-DorfwLnQ.js → chunk-FMBD7UC4-6vBylg_I.js} +1 -1
  27. package/dist/client/assets/{chunk-KSCS5N6A-CXY93mId.js → chunk-KSCS5N6A-C7iBFsUg.js} +1 -1
  28. package/dist/client/assets/{chunk-L5ZTLDWV-BjMrSVmH.js → chunk-L5ZTLDWV-CRU3nvaK.js} +1 -1
  29. package/dist/client/assets/{chunk-LZXEDZCA-q8jj6Nyb.js → chunk-LZXEDZCA-6ppA-_4h.js} +2 -2
  30. package/dist/client/assets/{chunk-ND2GUHAM-BRRQbYHl.js → chunk-ND2GUHAM-D1cTmgHe.js} +1 -1
  31. package/dist/client/assets/{chunk-NZK2D7GU-Czt1qHrO.js → chunk-NZK2D7GU-CpUab6v7.js} +1 -1
  32. package/dist/client/assets/{chunk-O5CBEL6O-DEopURak.js → chunk-O5CBEL6O-cDxBycWe.js} +1 -1
  33. package/dist/client/assets/chunk-QZHKN3VN-HhMIifwl.js +1 -0
  34. package/dist/client/assets/chunk-WU5MYG2G-CvIzXEms.js +1 -0
  35. package/dist/client/assets/{chunk-XPW4576I-DocCoKUT.js → chunk-XPW4576I-CI9GEEdy.js} +1 -1
  36. package/dist/client/assets/classDiagram-4FO5ZUOK-DFu-dNVy.js +1 -0
  37. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-D7Xcgo6q.js +1 -0
  38. package/dist/client/assets/{cobol-BylylR2x.js → cobol-BVhbHJgp.js} +1 -1
  39. package/dist/client/assets/{coffee-DCKfk8xx.js → coffee-gY6asS_B.js} +1 -1
  40. package/dist/client/assets/{cose-bilkent-S5V4N54A-Cm0BD5CS.js → cose-bilkent-S5V4N54A-BOs7zxZa.js} +1 -1
  41. package/dist/client/assets/{cpp-CfKCIrGK.js → cpp-el3R6exS.js} +1 -1
  42. package/dist/client/assets/{crystal-v-9RIu3l.js → crystal-D2_0btIo.js} +1 -1
  43. package/dist/client/assets/{css-XjlXeGmZ.js → css-DtPe7dg4.js} +1 -1
  44. package/dist/client/assets/{dagre-BM42HDAG-kqgFUaF1.js → dagre-BM42HDAG-CR_0cBOz.js} +1 -1
  45. package/dist/client/assets/{diagram-2AECGRRQ-B3hBvGUc.js → diagram-2AECGRRQ-D9um0wp2.js} +1 -1
  46. package/dist/client/assets/{diagram-5GNKFQAL-r8zU8C-Y.js → diagram-5GNKFQAL-Dx0TEfBJ.js} +1 -1
  47. package/dist/client/assets/{diagram-KO2AKTUF-CDWD5zkZ.js → diagram-KO2AKTUF-DjBHOuUD.js} +1 -1
  48. package/dist/client/assets/{diagram-LMA3HP47-CRRfS6RD.js → diagram-LMA3HP47-CK2MO5Gx.js} +1 -1
  49. package/dist/client/assets/{diagram-OG6HWLK6-ChBS6E-K.js → diagram-OG6HWLK6-CANQancN.js} +1 -1
  50. package/dist/client/assets/{dist-DUTeKbN7.js → dist-DGlwcsQQ.js} +1 -1
  51. package/dist/client/assets/{edge-Cwh953i8.js → edge-Ch_PYBsB.js} +1 -1
  52. package/dist/client/assets/{elixir-C4MgLzTj.js → elixir-DEJrJATq.js} +1 -1
  53. package/dist/client/assets/{elm-CFBu8r7i.js → elm-Brlko4-d.js} +1 -1
  54. package/dist/client/assets/{erDiagram-TEJ5UH35-_03Fb9RH.js → erDiagram-TEJ5UH35-Dnfrs6yk.js} +1 -1
  55. package/dist/client/assets/{erb-CrCna7R1.js → erb-DQPphNua.js} +1 -1
  56. package/dist/client/assets/{flowDiagram-I6XJVG4X-D9pSaH_8.js → flowDiagram-I6XJVG4X-BNMdFtjG.js} +1 -1
  57. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DKBfXY9D.js → ganttDiagram-6RSMTGT7-X-fPXz_A.js} +1 -1
  58. package/dist/client/assets/{git-rebase-CExLk2dD.js → git-rebase-BpS915SI.js} +1 -1
  59. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-BPQ2kpJA.js → gitGraphDiagram-PVQCEYII-CRN_dRXM.js} +1 -1
  60. package/dist/client/assets/{glimmer-js-wjBSKaMu.js → glimmer-js-DZDngVuD.js} +1 -1
  61. package/dist/client/assets/{glimmer-ts-CyjMXDgZ.js → glimmer-ts-DKNjJvyp.js} +1 -1
  62. package/dist/client/assets/{glsl-CWOoN8or.js → glsl-C-Y1tHoU.js} +1 -1
  63. package/dist/client/assets/{graphql-DI5CB58U.js → graphql-DYVBNazO.js} +1 -1
  64. package/dist/client/assets/{hack-Ct_Cm7Pr.js → hack-DewKAtLe.js} +1 -1
  65. package/dist/client/assets/{haml-DzAjli8d.js → haml-CIA_AeXu.js} +1 -1
  66. package/dist/client/assets/{handlebars-BfJB0RYx.js → handlebars-Bade6JXX.js} +1 -1
  67. package/dist/client/assets/{html-CK8auhK8.js → html-C4hhbuGp.js} +1 -1
  68. package/dist/client/assets/{html-derivative-B7VntYnF.js → html-derivative-BXfztBtv.js} +1 -1
  69. package/dist/client/assets/{http-_Zy71Sv3.js → http-Cq90-CTl.js} +1 -1
  70. package/dist/client/assets/{hurl-VbL4I95z.js → hurl-DklQHYoa.js} +1 -1
  71. package/dist/client/assets/index-CMyunQt6.css +1 -0
  72. package/dist/client/assets/{index-Dv9jPmOc.js → index-j9gqGQit.js} +97 -96
  73. package/dist/client/assets/{infoDiagram-5YYISTIA-B5RN-4Ke.js → infoDiagram-5YYISTIA-Ht-AcvP4.js} +1 -1
  74. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-DpbSX3Zy.js → ishikawaDiagram-YF4QCWOH-k_l70iFb.js} +1 -1
  75. package/dist/client/assets/{java-0CC2DnK9.js → java-BMUwLm1h.js} +1 -1
  76. package/dist/client/assets/{javascript-DKSTP7rz.js → javascript-B8nPCUVp.js} +1 -1
  77. package/dist/client/assets/{jinja-DoGBHNJ6.js → jinja-CkxiRcB3.js} +1 -1
  78. package/dist/client/assets/{jison-Bnl3lPA2.js → jison-CFnN5uQs.js} +1 -1
  79. package/dist/client/assets/{journeyDiagram-JHISSGLW-B_X4_qPO.js → journeyDiagram-JHISSGLW-C98PUC9Y.js} +1 -1
  80. package/dist/client/assets/{json-BQ3Ddj6g.js → json-B7HNnjIx.js} +1 -1
  81. package/dist/client/assets/{jsx-C_aFDuFZ.js → jsx-BfVPuW3i.js} +1 -1
  82. package/dist/client/assets/{julia-nIjp5yMR.js → julia-Bj0ETGjT.js} +1 -1
  83. package/dist/client/assets/{just-zc4yJ46n.js → just-CqIe_c9k.js} +1 -1
  84. package/dist/client/assets/{kanban-definition-UN3LZRKU-BSs4C5DW.js → kanban-definition-UN3LZRKU-BE-uvH7w.js} +1 -1
  85. package/dist/client/assets/{latex-BszBC80j.js → latex-Cvy8E64e.js} +1 -1
  86. package/dist/client/assets/{line-C9HWVwPi.js → line-BpooNUgm.js} +1 -1
  87. package/dist/client/assets/{linear-DatJfpKY.js → linear-CM6ZoyCp.js} +1 -1
  88. package/dist/client/assets/{liquid-DglQpTge.js → liquid-x09l8QqE.js} +1 -1
  89. package/dist/client/assets/{lua-BcbliFeR.js → lua-BSlDLjt5.js} +1 -1
  90. package/dist/client/assets/{marko-BPm4M8j-.js → marko-CoNMQlTn.js} +1 -1
  91. package/dist/client/assets/{mdc-BNUj07UF.js → mdc-BSzVESPu.js} +1 -1
  92. package/dist/client/assets/{mermaid-parser.core-BVEKlqQr.js → mermaid-parser.core-BDXJMxHu.js} +1 -1
  93. package/dist/client/assets/{mermaid.core-DVis21V_.js → mermaid.core-Dw-_vXTc.js} +3 -3
  94. package/dist/client/assets/{mindmap-definition-RKZ34NQL-Cuo7KewF.js → mindmap-definition-RKZ34NQL-BTxGT8rM.js} +1 -1
  95. package/dist/client/assets/{nginx-CSSJwkd6.js → nginx-CBhJTj0i.js} +1 -1
  96. package/dist/client/assets/{nim-nQAskwTR.js → nim-CtHbOBlO.js} +1 -1
  97. package/dist/client/assets/{perl-CtH8xYH6.js → perl-DLMzG3tu.js} +1 -1
  98. package/dist/client/assets/{php-Dcc3T2PH.js → php-KomVR3Hz.js} +1 -1
  99. package/dist/client/assets/{pieDiagram-4H26LBE5-BfpvCRmo.js → pieDiagram-4H26LBE5-BBUObAJW.js} +1 -1
  100. package/dist/client/assets/{pug-BCLaymlV.js → pug-T4SRLx8P.js} +1 -1
  101. package/dist/client/assets/{qml-BpKydcOi.js → qml-DJdSRUmU.js} +1 -1
  102. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-CJY6un0-.js → quadrantDiagram-W4KKPZXB-B7vvauZv.js} +1 -1
  103. package/dist/client/assets/{r-BzCB9dpd.js → r-CwuTg9lK.js} +1 -1
  104. package/dist/client/assets/{razor-BWmujqiQ.js → razor-Ch4dgsGY.js} +1 -1
  105. package/dist/client/assets/{regexp-urCI3YiZ.js → regexp-D2GTwJE0.js} +1 -1
  106. package/dist/client/assets/{requirementDiagram-4Y6WPE33-DIHWguS0.js → requirementDiagram-4Y6WPE33-DpcNiR3Y.js} +1 -1
  107. package/dist/client/assets/{rst-YTDC9H6u.js → rst-D78f0DyR.js} +1 -1
  108. package/dist/client/assets/{ruby-v1IXQAFZ.js → ruby-BUcw6ITs.js} +1 -1
  109. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DJRPEO_F.js → sankeyDiagram-5OEKKPKP-ChzSQD2O.js} +1 -1
  110. package/dist/client/assets/{sas-KDUG3BSk.js → sas-DoT2JDsj.js} +1 -1
  111. package/dist/client/assets/{scss-pLIGFpjD.js → scss-B6G_d-6b.js} +1 -1
  112. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DX0jVCOU.js → sequenceDiagram-3UESZ5HK-CzkdL8s7.js} +1 -1
  113. package/dist/client/assets/{shellscript-B5ZTyEsK.js → shellscript-DI252Bga.js} +1 -1
  114. package/dist/client/assets/{shellsession-DVK3bw4p.js → shellsession-BuO_s8FQ.js} +1 -1
  115. package/dist/client/assets/{soy-BXgyj2-T.js → soy-DZTZJTEE.js} +1 -1
  116. package/dist/client/assets/{sql-G2vZCmw6.js → sql-C4isoJr_.js} +1 -1
  117. package/dist/client/assets/{src-D9zkqEEX.js → src-BMvz5HE8.js} +1 -1
  118. package/dist/client/assets/{stata-C4mEnygx.js → stata-Dhu6qITh.js} +1 -1
  119. package/dist/client/assets/{stateDiagram-AJRCARHV-AMtMxKgQ.js → stateDiagram-AJRCARHV-DvYcrdap.js} +1 -1
  120. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-D5igY81I.js +1 -0
  121. package/dist/client/assets/{surrealql-Bf8gsTcP.js → surrealql-CSDpq5j3.js} +1 -1
  122. package/dist/client/assets/{svelte-C1oIyl9t.js → svelte-C4ITAvxr.js} +1 -1
  123. package/dist/client/assets/{templ-PJcWUuTF.js → templ-BTgKA9-7.js} +1 -1
  124. package/dist/client/assets/{tex-B7Fnujsi.js → tex-Dg4N9ndP.js} +1 -1
  125. package/dist/client/assets/{timeline-definition-PNZ67QCA-BZJSqcAg.js → timeline-definition-PNZ67QCA-Djt5Lwk2.js} +1 -1
  126. package/dist/client/assets/{ts-tags-CAAsFnUt.js → ts-tags-BufYnYos.js} +1 -1
  127. package/dist/client/assets/{tsx-DAKyTsWW.js → tsx-DmkFP0sw.js} +1 -1
  128. package/dist/client/assets/{twig-Bd4dieZF.js → twig-BJv_IWYK.js} +1 -1
  129. package/dist/client/assets/{typescript-8myByDGd.js → typescript-BKp05Qme.js} +1 -1
  130. package/dist/client/assets/{vennDiagram-CIIHVFJN-CMjBZNs2.js → vennDiagram-CIIHVFJN-DDVADTIa.js} +1 -1
  131. package/dist/client/assets/{vue-fBA2B-QU.js → vue-BsHXCXzh.js} +1 -1
  132. package/dist/client/assets/{vue-html-DEZECpsR.js → vue-html-CCsH-lmo.js} +1 -1
  133. package/dist/client/assets/{vue-vine-B_gG939R.js → vue-vine-B5P1JXxo.js} +1 -1
  134. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-qeHmPLyL.js → wardleyDiagram-YWT4CUSO-yeVKWvtX.js} +1 -1
  135. package/dist/client/assets/{xml-DbXNss3s.js → xml-CFkhYff0.js} +1 -1
  136. package/dist/client/assets/{xsl-B7_dgqqc.js → xsl-Dc5XRzm6.js} +1 -1
  137. package/dist/client/assets/{xychartDiagram-2RQKCTM6-NN2Et7YL.js → xychartDiagram-2RQKCTM6--8Ik-9v0.js} +1 -1
  138. package/dist/client/assets/{yaml-DSyIApdJ.js → yaml-BTCCxCxi.js} +1 -1
  139. package/dist/client/index.html +2 -2
  140. package/dist/mcp-DNZQvKKF.mjs +298 -0
  141. package/dist/mcp-DO4zfNZX.mjs +298 -0
  142. package/dist/plan-format-DVnF8xrq.mjs +126 -0
  143. package/dist/plan-format-mSDtsPcC.mjs +132 -0
  144. package/dist/server-lock-D73NKKbn.mjs +842 -0
  145. package/package.json +1 -1
  146. package/dist/client/assets/channel-D5AdjEWX.js +0 -1
  147. package/dist/client/assets/chunk-QZHKN3VN-BE917fn9.js +0 -1
  148. package/dist/client/assets/chunk-WU5MYG2G-DdoWnPla.js +0 -1
  149. package/dist/client/assets/classDiagram-4FO5ZUOK-CxjhKO7s.js +0 -1
  150. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CSScAAQ4.js +0 -1
  151. package/dist/client/assets/index-C3p7VKOX.css +0 -1
  152. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-C89CM-_4.js +0 -1
package/dist/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { C as toSafeRelativePath, S as isSafePath, _ as getUntrackedFilePathsAsync, a as formatComments, b as listRepoFiles, c as getFileContent, d as getMergeStatus, f as getProjectStorageDir, g as getTabSizeForFiles, h as getShowDiff, i as writeServerLock, l as getGitDiffAsync, m as getRepoRoot, o as getBranchName, p as getRepoName, r as removeServerLock, s as getCustomGitDiffAsync, u as getHunkHistory, v as gitAddFile, x as revertHunk, y as isGitRepo } from "./server-lock-Dj9fG9md.mjs";
3
- import { n as formatPlanReview, r as sectionTitleForLine, t as extractPlanLines } from "./plan-format-f62JWFI-.mjs";
2
+ import { C as toSafeRelativePath, S as isSafePath, _ as getUntrackedFilePathsAsync, a as formatComments, b as listRepoFiles, c as getFileContent, d as getMergeStatus, f as getProjectStorageDir, g as getTabSizeForFiles, h as getShowDiff, i as writeServerLock, l as getGitDiffAsync, m as getRepoRoot, o as getBranchName, p as getRepoName, r as removeServerLock, s as getCustomGitDiffAsync, u as getHunkHistory, v as gitAddFile, x as revertHunk, y as isGitRepo } from "./server-lock-D73NKKbn.mjs";
3
+ import { n as formatPlanReview, r as sectionTitleForLine, t as extractPlanLines } from "./plan-format-mSDtsPcC.mjs";
4
4
  import { execFile, execFileSync, spawn } from "node:child_process";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { dirname, extname, isAbsolute, join, resolve } from "node:path";
@@ -1331,6 +1331,42 @@ var FileCommentStore = class {
1331
1331
  function newId() {
1332
1332
  return crypto.randomUUID();
1333
1333
  }
1334
+ /**
1335
+ * Backfill / repair a plan loaded from disk so it matches the current schema.
1336
+ * Older persisted plans may be missing `versions` and `createdAtPlanVersion`;
1337
+ * we synthesize sensible values so callers can always rely on the new fields.
1338
+ */
1339
+ function backfillPlan(plan) {
1340
+ const currentVersion = plan.version ?? 1;
1341
+ const have = Array.isArray(plan.versions) ? plan.versions.length : 0;
1342
+ if (!plan.versions || have === 0 || have < currentVersion) {
1343
+ const createdAt = plan.updatedAt ?? plan.createdAt ?? Date.now();
1344
+ const haveEntries = Array.isArray(plan.versions) ? plan.versions : [];
1345
+ const present = new Set(haveEntries.map((v) => v.version));
1346
+ const next = [];
1347
+ for (let i = 1; i <= currentVersion; i++) {
1348
+ const existing = haveEntries.find((v) => v.version === i);
1349
+ if (existing) next.push(existing);
1350
+ else next.push({
1351
+ version: i,
1352
+ body: plan.body,
1353
+ title: plan.title,
1354
+ source: plan.source,
1355
+ model: plan.model,
1356
+ createdAt
1357
+ });
1358
+ }
1359
+ plan.versions = next.filter((v) => v.version >= 1 && v.version <= currentVersion);
1360
+ if (have < currentVersion || !present.has(currentVersion)) markBackfilled(plan);
1361
+ }
1362
+ if (plan.comments) {
1363
+ for (const c of plan.comments) if (typeof c.createdAtPlanVersion !== "number") c.createdAtPlanVersion = plan.version ?? 1;
1364
+ }
1365
+ if (!plan.comments) plan.comments = [];
1366
+ }
1367
+ function markBackfilled(plan) {
1368
+ plan.__backfilledVersions = true;
1369
+ }
1334
1370
  /** Shared mutation helpers so both stores behave identically. */
1335
1371
  function applyUpsert(plans, input, now) {
1336
1372
  if (input.id) {
@@ -1342,6 +1378,15 @@ function applyUpsert(plans, input, now) {
1342
1378
  if (input.model !== void 0) existing.model = input.model;
1343
1379
  existing.version += 1;
1344
1380
  existing.updatedAt = now;
1381
+ if (!existing.versions) existing.versions = [];
1382
+ existing.versions.push({
1383
+ version: existing.version,
1384
+ body: existing.body,
1385
+ title: existing.title,
1386
+ source: existing.source,
1387
+ model: existing.model,
1388
+ createdAt: now
1389
+ });
1345
1390
  existing.decision = "pending";
1346
1391
  existing.decisionComment = void 0;
1347
1392
  existing.decidedAt = void 0;
@@ -1358,11 +1403,43 @@ function applyUpsert(plans, input, now) {
1358
1403
  updatedAt: now,
1359
1404
  version: 1,
1360
1405
  decision: "pending",
1361
- comments: []
1406
+ comments: [],
1407
+ versions: [{
1408
+ version: 1,
1409
+ body: input.body,
1410
+ title: input.title,
1411
+ source: input.source,
1412
+ model: input.model,
1413
+ createdAt: now
1414
+ }]
1362
1415
  };
1363
1416
  plans.push(plan);
1364
1417
  return plan;
1365
1418
  }
1419
+ /**
1420
+ * Keep the most-recent `PlanVersion` snapshot in sync with the current `Plan`
1421
+ * fields after a non-resubmit edit (PUT). Doesn't add a new entry — that only
1422
+ * happens via `upsert` (the resubmit flow).
1423
+ */
1424
+ function syncCurrentVersion(plan) {
1425
+ if (!plan.versions || plan.versions.length === 0) plan.versions = [];
1426
+ if (plan.versions.length === 0) {
1427
+ plan.versions.push({
1428
+ version: plan.version,
1429
+ body: plan.body,
1430
+ title: plan.title,
1431
+ source: plan.source,
1432
+ model: plan.model,
1433
+ createdAt: plan.updatedAt
1434
+ });
1435
+ return;
1436
+ }
1437
+ const last = plan.versions[plan.versions.length - 1];
1438
+ last.body = plan.body;
1439
+ last.title = plan.title;
1440
+ last.source = plan.source;
1441
+ last.model = plan.model;
1442
+ }
1366
1443
  var FilePlanStore = class {
1367
1444
  dirPath;
1368
1445
  filePath;
@@ -1380,7 +1457,19 @@ var FilePlanStore = class {
1380
1457
  async getAll() {
1381
1458
  try {
1382
1459
  const data = await readFile(this.filePath, "utf-8");
1383
- return JSON.parse(data);
1460
+ const plans = JSON.parse(data);
1461
+ let anyBackfilled = false;
1462
+ for (const p of plans) {
1463
+ const before = Array.isArray(p.versions) ? p.versions.length : 0;
1464
+ const beforeValid = Array.isArray(p.versions) && p.versions.length > 0;
1465
+ backfillPlan(p);
1466
+ const after = Array.isArray(p.versions) ? p.versions.length : 0;
1467
+ if ((!beforeValid || before < (p.version ?? 1)) && after > before) anyBackfilled = true;
1468
+ }
1469
+ if (anyBackfilled) try {
1470
+ await this.save(plans);
1471
+ } catch {}
1472
+ return plans;
1384
1473
  } catch {
1385
1474
  return [];
1386
1475
  }
@@ -1388,6 +1477,11 @@ var FilePlanStore = class {
1388
1477
  async get(id) {
1389
1478
  return (await this.getAll()).find((p) => p.id === id) ?? null;
1390
1479
  }
1480
+ async getVersion(id, version) {
1481
+ const plan = await this.get(id);
1482
+ if (!plan) return null;
1483
+ return plan.versions.find((v) => v.version === version) ?? null;
1484
+ }
1391
1485
  async save(plans) {
1392
1486
  try {
1393
1487
  await mkdir(this.dirPath, { recursive: true });
@@ -1395,7 +1489,12 @@ var FilePlanStore = class {
1395
1489
  const repoRoot = getRepoRoot();
1396
1490
  await writeFile(join(this.dirPath, "repo_path.txt"), repoRoot, "utf-8");
1397
1491
  } catch {}
1398
- await writeFile(this.filePath, JSON.stringify(plans, null, 2), "utf-8");
1492
+ const clean = plans.map((p) => {
1493
+ const copy = { ...p };
1494
+ delete copy.__backfilledVersions;
1495
+ return copy;
1496
+ });
1497
+ await writeFile(this.filePath, JSON.stringify(clean, null, 2), "utf-8");
1399
1498
  } catch (err) {
1400
1499
  console.error("Failed to save plans to file:", err);
1401
1500
  }
@@ -1403,6 +1502,7 @@ var FilePlanStore = class {
1403
1502
  async upsert(input) {
1404
1503
  const plans = await this.getAll();
1405
1504
  const plan = applyUpsert(plans, input, Date.now());
1505
+ backfillPlan(plan);
1406
1506
  await this.save(plans);
1407
1507
  return plan;
1408
1508
  }
@@ -1415,6 +1515,8 @@ var FilePlanStore = class {
1415
1515
  if (fields.source !== void 0) plan.source = fields.source;
1416
1516
  if (fields.model !== void 0) plan.model = fields.model;
1417
1517
  plan.updatedAt = Date.now();
1518
+ syncCurrentVersion(plan);
1519
+ backfillPlan(plan);
1418
1520
  await this.save(plans);
1419
1521
  return plan;
1420
1522
  }
@@ -1443,11 +1545,13 @@ var FilePlanStore = class {
1443
1545
  if (!plan) return null;
1444
1546
  if (!plan.comments) plan.comments = [];
1445
1547
  if (!fn(plan)) return null;
1548
+ backfillPlan(plan);
1446
1549
  await this.save(plans);
1447
1550
  return plan;
1448
1551
  }
1449
1552
  async addComment(planId, comment) {
1450
1553
  return this.mutate(planId, (plan) => {
1554
+ if (typeof comment.createdAtPlanVersion !== "number") comment.createdAtPlanVersion = plan.version ?? 1;
1451
1555
  plan.comments.push(comment);
1452
1556
  return true;
1453
1557
  });
@@ -1473,6 +1577,7 @@ var FilePlanStore = class {
1473
1577
  return this.mutate(planId, (plan) => {
1474
1578
  const c = plan.comments.find((x) => x.id === commentId);
1475
1579
  if (!c) return false;
1580
+ if (typeof c.createdAtPlanVersion === "number" && typeof reply.createdAtPlanVersion !== "number") reply.createdAtPlanVersion = c.createdAtPlanVersion;
1476
1581
  if (!c.replies) c.replies = [];
1477
1582
  c.replies.push(reply);
1478
1583
  return true;
@@ -1653,7 +1758,8 @@ var ReviewSession = class {
1653
1758
  commentXml: input.commentXml,
1654
1759
  openCount: input.openCount,
1655
1760
  comments: input.comments,
1656
- decision: input.decision
1761
+ decision: input.decision,
1762
+ mode: input.mode ?? "standard"
1657
1763
  };
1658
1764
  this.lastPayload = payload;
1659
1765
  const waiters = [...this.waiters];
@@ -1747,7 +1853,8 @@ var PlanReviewSession = class {
1747
1853
  decisionComment: input.decisionComment,
1748
1854
  reviewXml: input.reviewXml,
1749
1855
  openCommentCount: input.openCommentCount,
1750
- plan: input.plan
1856
+ plan: input.plan,
1857
+ mode: input.mode ?? "standard"
1751
1858
  };
1752
1859
  this.lastPayload = payload;
1753
1860
  const waiters = [...this.waiters];
@@ -2277,7 +2384,7 @@ const MIME_TYPES = {
2277
2384
  function isCustomMode(opts) {
2278
2385
  return opts.revisions.length > 0 || opts.pathspecs.length > 0 || opts.showMode;
2279
2386
  }
2280
- function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, prSessionStore) {
2387
+ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, prSessionStore, prMode = false) {
2281
2388
  const app = new Hono();
2282
2389
  const customMode = isCustomMode(diffOpts);
2283
2390
  const store = commentStore ?? new FileCommentStore();
@@ -2376,33 +2483,35 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2376
2483
  app.get("/api/diff", async (c) => {
2377
2484
  const staged = c.req.query("staged") === "true";
2378
2485
  const untracked = c.req.query("untracked") === "true";
2379
- const prSession = await prStore.get();
2380
- if (prSession) {
2381
- const binaryFiles = [];
2382
- const filePaths = [];
2383
- for (const line of prSession.diff.split("\n")) {
2384
- const m = /^diff --git a\/.+ b\/(.+)$/.exec(line);
2385
- if (m) filePaths.push(m[1]);
2486
+ if (prMode) {
2487
+ const prSession = await prStore.get();
2488
+ if (prSession) {
2489
+ const binaryFiles = [];
2490
+ const filePaths = [];
2491
+ for (const line of prSession.diff.split("\n")) {
2492
+ const m = /^diff --git a\/.+ b\/(.+)$/.exec(line);
2493
+ if (m) filePaths.push(m[1]);
2494
+ }
2495
+ return c.json({
2496
+ patch: prSession.diff,
2497
+ repoName: prSession.repo,
2498
+ branch: `#${prSession.pullNumber}`,
2499
+ customMode: true,
2500
+ binaryFiles,
2501
+ tabSizeMap: {},
2502
+ untrackedFiles: [],
2503
+ prMode: true,
2504
+ prRef: prSession.ref,
2505
+ prOwner: prSession.owner,
2506
+ prRepo: prSession.repo,
2507
+ prPullNumber: prSession.pullNumber,
2508
+ prUrl: prSession.url,
2509
+ prTitle: prSession.title,
2510
+ prAuthor: prSession.author,
2511
+ prHeadSha: prSession.headSha,
2512
+ prBaseSha: prSession.baseSha
2513
+ });
2386
2514
  }
2387
- return c.json({
2388
- patch: prSession.diff,
2389
- repoName: prSession.repo,
2390
- branch: `#${prSession.pullNumber}`,
2391
- customMode: true,
2392
- binaryFiles,
2393
- tabSizeMap: {},
2394
- untrackedFiles: [],
2395
- prMode: true,
2396
- prRef: prSession.ref,
2397
- prOwner: prSession.owner,
2398
- prRepo: prSession.repo,
2399
- prPullNumber: prSession.pullNumber,
2400
- prUrl: prSession.url,
2401
- prTitle: prSession.title,
2402
- prAuthor: prSession.author,
2403
- prHeadSha: prSession.headSha,
2404
- prBaseSha: prSession.baseSha
2405
- });
2406
2515
  }
2407
2516
  const optsForDiff = customMode ? diffOpts : {
2408
2517
  ...diffOpts,
@@ -2703,6 +2812,7 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2703
2812
  prMode: false
2704
2813
  }, 404);
2705
2814
  app.get("/api/gh/session", async (c) => {
2815
+ if (!prMode) return notInPrMode(c);
2706
2816
  const session = await prStore.get();
2707
2817
  if (!session) return notInPrMode(c);
2708
2818
  return c.json({
@@ -2728,6 +2838,7 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2728
2838
  });
2729
2839
  /** Re-fetch PR metadata (head SHA, diff, existing comments) and persist. */
2730
2840
  app.post("/api/gh/pr/refresh", async (c) => {
2841
+ if (!prMode) return notInPrMode(c);
2731
2842
  const session = await prStore.get();
2732
2843
  if (!session) return notInPrMode(c);
2733
2844
  try {
@@ -2743,6 +2854,7 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2743
2854
  });
2744
2855
  /** Initialize a PR session from a ref like `1234`, `o/r#42`, or a GitHub URL. */
2745
2856
  app.post("/api/gh/pr/init", async (c) => {
2857
+ if (!prMode) return notInPrMode(c);
2746
2858
  const body = await c.req.json().catch(() => ({}));
2747
2859
  const ref = typeof body?.ref === "string" ? body.ref : "";
2748
2860
  if (!ref.trim()) return c.json({ error: "ref is required" }, 400);
@@ -2763,11 +2875,13 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2763
2875
  }
2764
2876
  });
2765
2877
  app.get("/api/gh/pr-session/comments", async (c) => {
2878
+ if (!prMode) return notInPrMode(c);
2766
2879
  const session = await prStore.get();
2767
2880
  if (!session) return notInPrMode(c);
2768
2881
  return c.json(session.comments ?? []);
2769
2882
  });
2770
2883
  app.post("/api/gh/pr-session/comments", async (c) => {
2884
+ if (!prMode) return notInPrMode(c);
2771
2885
  const body = await c.req.json();
2772
2886
  const session = await prStore.get();
2773
2887
  if (!session) return notInPrMode(c);
@@ -2791,6 +2905,7 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2791
2905
  return c.json(comment, 201);
2792
2906
  });
2793
2907
  app.put("/api/gh/pr-session/comments/:id", async (c) => {
2908
+ if (!prMode) return notInPrMode(c);
2794
2909
  const id = c.req.param("id");
2795
2910
  const { body, status } = await c.req.json();
2796
2911
  const session = await prStore.get();
@@ -2809,6 +2924,7 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2809
2924
  return c.json(updated);
2810
2925
  });
2811
2926
  app.delete("/api/gh/pr-session/comments/:id", async (c) => {
2927
+ if (!prMode) return notInPrMode(c);
2812
2928
  const id = c.req.param("id");
2813
2929
  const session = await prStore.get();
2814
2930
  if (!session) return notInPrMode(c);
@@ -2820,6 +2936,7 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2820
2936
  return c.json({ ok: true });
2821
2937
  });
2822
2938
  app.post("/api/gh/pr-session/comments/:id/replies", async (c) => {
2939
+ if (!prMode) return notInPrMode(c);
2823
2940
  const id = c.req.param("id");
2824
2941
  const { body, role, model } = await c.req.json();
2825
2942
  const session = await prStore.get();
@@ -2846,6 +2963,7 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2846
2963
  });
2847
2964
  /** Submit the current PR session's review (new comments + verdict + body) to GitHub. */
2848
2965
  app.post("/api/gh/submit", async (c) => {
2966
+ if (!prMode) return notInPrMode(c);
2849
2967
  const session = await prStore.get();
2850
2968
  if (!session) return notInPrMode(c);
2851
2969
  const body = await c.req.json().catch(() => ({}));
@@ -2887,21 +3005,24 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2887
3005
  app.post("/api/review/send", async (c) => {
2888
3006
  const body = await c.req.json().catch(() => ({}));
2889
3007
  const generalComment = typeof body?.generalComment === "string" ? body.generalComment : void 0;
2890
- const decision = body?.decision === "approved" || body?.decision === "changes-requested" || body?.decision === "rejected" ? body.decision : void 0;
3008
+ const decision = body?.decision === "approved" || body?.decision === "changes-requested" || body?.decision === "rejected" || body?.decision === "comment-only" ? body.decision : void 0;
3009
+ const mode = body?.mode === "comment-only" || body?.mode === "standard" ? body.mode : "standard";
2891
3010
  const all = await store.getAll();
2892
3011
  const openCount = all.filter((x) => x.status === "open").length;
2893
3012
  const payload = reviewSession.send({
2894
3013
  sentAt: Date.now(),
2895
- commentXml: formatComments(all, generalComment, decision),
3014
+ commentXml: formatComments(all, generalComment, decision, mode),
2896
3015
  openCount,
2897
3016
  comments: all,
2898
- decision
3017
+ decision,
3018
+ mode
2899
3019
  });
2900
3020
  return c.json({
2901
3021
  ok: true,
2902
3022
  round: payload.round,
2903
3023
  openCount: payload.openCount,
2904
3024
  decision: payload.decision,
3025
+ mode: payload.mode,
2905
3026
  waiters: reviewSession.snapshot().waiters
2906
3027
  });
2907
3028
  });
@@ -2928,6 +3049,29 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2928
3049
  if (!plan) return c.json({ error: "Plan not found" }, 404);
2929
3050
  return c.json(plan);
2930
3051
  });
3052
+ app.get("/api/plans/:id/versions", async (c) => {
3053
+ const plan = await plans.get(c.req.param("id"));
3054
+ if (!plan) return c.json({ error: "Plan not found" }, 404);
3055
+ return c.json(plan.versions ?? []);
3056
+ });
3057
+ app.get("/api/plans/:id/versions/:n", async (c) => {
3058
+ const id = c.req.param("id");
3059
+ const n = Number(c.req.param("n"));
3060
+ if (!Number.isFinite(n) || n < 1) return c.json({ error: "version must be a positive integer" }, 400);
3061
+ const plan = await plans.get(id);
3062
+ if (!plan) return c.json({ error: "Plan not found" }, 404);
3063
+ const version = (plan.versions ?? []).find((v) => v.version === n);
3064
+ if (!version) return c.json({ error: `Version ${n} not found` }, 404);
3065
+ return c.json({
3066
+ version,
3067
+ plan: {
3068
+ id: plan.id,
3069
+ title: plan.title,
3070
+ decision: plan.decision,
3071
+ currentVersion: plan.version
3072
+ }
3073
+ });
3074
+ });
2931
3075
  app.post("/api/plans", async (c) => {
2932
3076
  const body = await c.req.json().catch(() => ({}));
2933
3077
  if (typeof body.body !== "string" || !body.body.trim()) return c.json({ error: "A plan body (markdown) is required" }, 400);
@@ -2967,6 +3111,7 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2967
3111
  const anchorStart = startLineNumber ?? lineNumber;
2968
3112
  const lineContent = typeof body.lineContent === "string" && body.lineContent ? body.lineContent : lineNumber > 0 ? extractPlanLines(plan.body, anchorStart, lineNumber) : "";
2969
3113
  const sectionTitle = typeof body.sectionTitle === "string" && body.sectionTitle ? body.sectionTitle : lineNumber > 0 ? sectionTitleForLine(plan.body, anchorStart) : void 0;
3114
+ const createdAtPlanVersion = Number.isFinite(body.createdAtPlanVersion) ? Number(body.createdAtPlanVersion) : plan.version;
2970
3115
  const comment = {
2971
3116
  id: crypto.randomUUID(),
2972
3117
  lineNumber,
@@ -2976,6 +3121,7 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
2976
3121
  body: body.body,
2977
3122
  status: "open",
2978
3123
  createdAt: Date.now(),
3124
+ createdAtPlanVersion,
2979
3125
  replies: []
2980
3126
  };
2981
3127
  const updated = await plans.addComment(planId, comment);
@@ -3025,8 +3171,9 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
3025
3171
  const planId = c.req.param("id");
3026
3172
  const body = await c.req.json().catch(() => ({}));
3027
3173
  const decision = body.decision;
3028
- if (decision !== "approved" && decision !== "rejected" && decision !== "changes-requested") return c.json({ error: "decision must be one of: approved, rejected, changes-requested" }, 400);
3174
+ if (decision !== "approved" && decision !== "rejected" && decision !== "changes-requested" && decision !== "comment-only") return c.json({ error: "decision must be one of: approved, rejected, changes-requested, comment-only" }, 400);
3029
3175
  const decisionComment = typeof body.decisionComment === "string" ? body.decisionComment : void 0;
3176
+ const mode = body?.mode === "comment-only" || body?.mode === "standard" ? body.mode : "standard";
3030
3177
  const plan = await plans.setDecision(planId, decision, decisionComment);
3031
3178
  if (!plan) return c.json({ error: "Plan not found" }, 404);
3032
3179
  const openCommentCount = (plan.comments ?? []).filter((x) => x.status === "open").length;
@@ -3035,14 +3182,16 @@ function createApp(clientDir, diffOpts = DEFAULTS$1, commentStore, planStore, pr
3035
3182
  planId,
3036
3183
  decision,
3037
3184
  decisionComment: plan.decisionComment,
3038
- reviewXml: formatPlanReview(plan),
3185
+ reviewXml: formatPlanReview(plan, { mode }),
3039
3186
  openCommentCount,
3040
- plan
3187
+ plan,
3188
+ mode
3041
3189
  });
3042
3190
  return c.json({
3043
3191
  ok: true,
3044
3192
  round: payload.round,
3045
3193
  decision,
3194
+ mode: payload.mode,
3046
3195
  openCommentCount,
3047
3196
  waiters: planReviewSession.snapshot().waiters
3048
3197
  });
@@ -3177,13 +3326,12 @@ async function cleanupStaleProjects() {
3177
3326
  console.error("Failed to cleanup stale projects:", err);
3178
3327
  }
3179
3328
  }
3180
- function startServer(options) {
3329
+ async function startServer(options) {
3181
3330
  cleanupStaleProjects().catch((err) => {
3182
3331
  console.error("Failed to clean up stale projects:", err);
3183
3332
  });
3184
3333
  let prMode = false;
3185
- const prReady = (async () => {
3186
- if (!options.prRef) return;
3334
+ if (options.prRef) {
3187
3335
  console.error(`Building PR session for ${options.prRef}...`);
3188
3336
  try {
3189
3337
  const store = new FilePrSessionStore();
@@ -3196,19 +3344,17 @@ function startServer(options) {
3196
3344
  } catch (err) {
3197
3345
  console.error(`[pr-session] failed to build session for ${options.prRef}: ${err?.message ?? err}`);
3198
3346
  }
3199
- })();
3200
- const app = createApp(options.clientDir, options.diffOpts);
3347
+ }
3348
+ const app = createApp(options.clientDir, options.diffOpts, void 0, void 0, void 0, prMode);
3201
3349
  return new Promise((resolve) => {
3202
- prReady.then(() => {
3203
- serve({
3204
- fetch: app.fetch,
3205
- port: options.port,
3206
- hostname: options.host
3207
- }, (info) => {
3208
- resolve({
3209
- port: info.port,
3210
- prMode
3211
- });
3350
+ serve({
3351
+ fetch: app.fetch,
3352
+ port: options.port,
3353
+ hostname: options.host
3354
+ }, (info) => {
3355
+ resolve({
3356
+ port: info.port,
3357
+ prMode
3212
3358
  });
3213
3359
  });
3214
3360
  });
@@ -3346,7 +3492,7 @@ const QUOTES = [
3346
3492
  author: "Unknown"
3347
3493
  },
3348
3494
  {
3349
- text: "A QA engineer walks into a bar. Orders 0 beers. Orders 999999 beers. Orders NULL beers. Walks in through the window.",
3495
+ text: "A QA engineer walks into a coffie shop. Orders 0 cups. Orders 999999 cups. Orders NULL cups. Walks in through the window.",
3350
3496
  author: "Unknown"
3351
3497
  },
3352
3498
  {
@@ -3671,11 +3817,11 @@ if (new Set([
3671
3817
  "gh"
3672
3818
  ]).has(args[0])) {
3673
3819
  if (args[0] === "mcp") {
3674
- const { startMcpServer } = await import("./mcp-BWeWR4yd.mjs");
3820
+ const { startMcpServer } = await import("./mcp-DNZQvKKF.mjs");
3675
3821
  await startMcpServer();
3676
3822
  await new Promise(() => {});
3677
3823
  }
3678
- const { runSubcommand } = await import("./cli-agent-KxNAFIPf.mjs");
3824
+ const { runSubcommand } = await import("./cli-agent-B4hU6Xay.mjs");
3679
3825
  process.exit(await runSubcommand(args[0], args.slice(1)));
3680
3826
  }
3681
3827
  let showSubcommand = false;