diffing 0.16.0 → 0.17.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 (156) hide show
  1. package/dist/{cli-agent-Dk0yRtrA.mjs → cli-agent-qNwkSjxK.mjs} +22 -7
  2. package/dist/cli.mjs +3 -3
  3. package/dist/client/assets/{angular-html-JZN5kc0L.js → angular-html-ByeJB0rh.js} +1 -1
  4. package/dist/client/assets/{angular-ts-CJtfwtPm.js → angular-ts-CiwZGajG.js} +1 -1
  5. package/dist/client/assets/{apl-BW-LdBEV.js → apl-C8k5a5fk.js} +1 -1
  6. package/dist/client/assets/{arc-CxBp7uj_.js → arc-B8VMoqVr.js} +1 -1
  7. package/dist/client/assets/{architectureDiagram-3BPJPVTR-DWRcM6V_.js → architectureDiagram-3BPJPVTR-C-ng2xNL.js} +1 -1
  8. package/dist/client/assets/{astro-gYWZxO_0.js → astro-BE-h-qpp.js} +1 -1
  9. package/dist/client/assets/{blade-Dhwn6-nM.js → blade-D-0BV1wW.js} +1 -1
  10. package/dist/client/assets/{blockDiagram-GPEHLZMM-C5RclE5o.js → blockDiagram-GPEHLZMM-DyAQuONU.js} +1 -1
  11. package/dist/client/assets/{c-DStVJ8gV.js → c-Dg_Gx29H.js} +1 -1
  12. package/dist/client/assets/{c4Diagram-AAUBKEIU-BGUA_e0K.js → c4Diagram-AAUBKEIU-k5uKTHvm.js} +1 -1
  13. package/dist/client/assets/channel-DsuXeLi4.js +1 -0
  14. package/dist/client/assets/{chunk-2J33WTMH-B7n4bw-R.js → chunk-2J33WTMH-Dod_VwYj.js} +1 -1
  15. package/dist/client/assets/{chunk-3OPIFGDE-w01g5zoV.js → chunk-3OPIFGDE-B8HMjry-.js} +1 -1
  16. package/dist/client/assets/{chunk-4BX2VUAB-DxqZfswb.js → chunk-4BX2VUAB-TvkJVIZw.js} +1 -1
  17. package/dist/client/assets/{chunk-55IACEB6-CbDClk6A.js → chunk-55IACEB6-BsXuKAKc.js} +1 -1
  18. package/dist/client/assets/{chunk-5ZQYHXKU-CnsO6rSw.js → chunk-5ZQYHXKU-BI1cj2cZ.js} +1 -1
  19. package/dist/client/assets/{chunk-727SXJPM-BtcRYmEV.js → chunk-727SXJPM-yWQ27Wk0.js} +1 -1
  20. package/dist/client/assets/{chunk-AQP2D5EJ-CGhb3Sth.js → chunk-AQP2D5EJ-B2BBCtTr.js} +1 -1
  21. package/dist/client/assets/{chunk-BSJP7CBP-DeuRDOb1.js → chunk-BSJP7CBP-CXXvPXhK.js} +1 -1
  22. package/dist/client/assets/{chunk-CSCIHK7Q-B9E4vQNj.js → chunk-CSCIHK7Q-jYiloqyi.js} +1 -1
  23. package/dist/client/assets/{chunk-FMBD7UC4-Bq9LG2aQ.js → chunk-FMBD7UC4-DZzqIT32.js} +1 -1
  24. package/dist/client/assets/{chunk-KSCS5N6A-BVlK-NC4.js → chunk-KSCS5N6A-krdCf8iG.js} +1 -1
  25. package/dist/client/assets/{chunk-L5ZTLDWV-DcdQquQQ.js → chunk-L5ZTLDWV-0uuTmeZV.js} +1 -1
  26. package/dist/client/assets/{chunk-LZXEDZCA-D3hLMWf5.js → chunk-LZXEDZCA-BBfUgD06.js} +2 -2
  27. package/dist/client/assets/{chunk-ND2GUHAM-0nxCj3q_.js → chunk-ND2GUHAM-jkir4ZKE.js} +1 -1
  28. package/dist/client/assets/{chunk-NZK2D7GU-C_Wkgas6.js → chunk-NZK2D7GU-DMWZNibJ.js} +1 -1
  29. package/dist/client/assets/{chunk-O5CBEL6O-BjWUWCBx.js → chunk-O5CBEL6O-B1e1HGaX.js} +1 -1
  30. package/dist/client/assets/chunk-QZHKN3VN-D5b3mBJ2.js +1 -0
  31. package/dist/client/assets/chunk-WU5MYG2G-C1Pa4Y04.js +1 -0
  32. package/dist/client/assets/{chunk-XPW4576I-BoIL-UAf.js → chunk-XPW4576I-AvbCAUav.js} +1 -1
  33. package/dist/client/assets/classDiagram-4FO5ZUOK-DCnVfYt-.js +1 -0
  34. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BPa1Jkt-.js +1 -0
  35. package/dist/client/assets/{cobol-BhFBbthm.js → cobol-DQnGB_GV.js} +1 -1
  36. package/dist/client/assets/{coffee-CIJ9uq8T.js → coffee-CDV5a89x.js} +1 -1
  37. package/dist/client/assets/{cose-bilkent-S5V4N54A-CbiOjhru.js → cose-bilkent-S5V4N54A-t-mDNRF2.js} +1 -1
  38. package/dist/client/assets/{cpp-DtSSrq00.js → cpp-8G6b-siI.js} +1 -1
  39. package/dist/client/assets/{crystal-CukkuPMH.js → crystal-CGYCC6FM.js} +1 -1
  40. package/dist/client/assets/{css-B1khiVXh.js → css-nez4R8_Z.js} +1 -1
  41. package/dist/client/assets/{dagre-BM42HDAG-DV-L1RXR.js → dagre-BM42HDAG--azesYIg.js} +1 -1
  42. package/dist/client/assets/{diagram-2AECGRRQ-BIS3Tcy4.js → diagram-2AECGRRQ-4WJ7nKhA.js} +1 -1
  43. package/dist/client/assets/{diagram-5GNKFQAL-BT7ScWHB.js → diagram-5GNKFQAL-C7ZGhLXn.js} +1 -1
  44. package/dist/client/assets/{diagram-KO2AKTUF-DhEvzqhQ.js → diagram-KO2AKTUF-BobK_rbl.js} +1 -1
  45. package/dist/client/assets/{diagram-LMA3HP47-DIh9QR2k.js → diagram-LMA3HP47-BOc-OkIS.js} +1 -1
  46. package/dist/client/assets/{diagram-OG6HWLK6-V6xtTYuU.js → diagram-OG6HWLK6-BEtLgm86.js} +1 -1
  47. package/dist/client/assets/{dist--CAI-jgr.js → dist-DPuBwToc.js} +1 -1
  48. package/dist/client/assets/{edge-CuCj0fvA.js → edge-Z5RMbJQv.js} +1 -1
  49. package/dist/client/assets/{edit-BMeAI0Sj.js → edit-CFxbgVPV.js} +1 -1
  50. package/dist/client/assets/{elixir-CakheKmu.js → elixir-d3C79FQc.js} +1 -1
  51. package/dist/client/assets/{elm-C1NdUKtL.js → elm-CDAf6Uyn.js} +1 -1
  52. package/dist/client/assets/{erDiagram-TEJ5UH35-DLJ0CCH3.js → erDiagram-TEJ5UH35-D-pR2GY9.js} +1 -1
  53. package/dist/client/assets/{erb-CetH6rlA.js → erb-CTJk9FKH.js} +1 -1
  54. package/dist/client/assets/{flowDiagram-I6XJVG4X-aIYTauNq.js → flowDiagram-I6XJVG4X-BwMCy2QV.js} +1 -1
  55. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DRQE_4dV.js → ganttDiagram-6RSMTGT7-BI1WLJDi.js} +1 -1
  56. package/dist/client/assets/{git-rebase-sKnthHnf.js → git-rebase-Dr0-MVoW.js} +1 -1
  57. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DbSrYZsG.js → gitGraphDiagram-PVQCEYII-CUfW0k1f.js} +1 -1
  58. package/dist/client/assets/{glimmer-js-Bt7nvyGa.js → glimmer-js-CXhLhyW-.js} +1 -1
  59. package/dist/client/assets/{glimmer-ts-YO9zmz82.js → glimmer-ts-BFAFT-VD.js} +1 -1
  60. package/dist/client/assets/{glsl-DsvRozYb.js → glsl-D7yanhgY.js} +1 -1
  61. package/dist/client/assets/{graphql-sXzHf_gb.js → graphql-CpoDxrok.js} +1 -1
  62. package/dist/client/assets/{hack-C3UDBYU5.js → hack-BL5fmC4q.js} +1 -1
  63. package/dist/client/assets/{haml-Bfx2v9ts.js → haml-ClAE6RVM.js} +1 -1
  64. package/dist/client/assets/{handlebars-D7y9m08n.js → handlebars-nHw91uOv.js} +1 -1
  65. package/dist/client/assets/{html-LJt8Nrzd.js → html-BIiOKqgG.js} +1 -1
  66. package/dist/client/assets/{html-derivative-GRYtRNx6.js → html-derivative-D1nPnuOK.js} +1 -1
  67. package/dist/client/assets/{http-CFcGSNWA.js → http-B_QcRZjt.js} +1 -1
  68. package/dist/client/assets/{hurl-EaOExxlv.js → hurl-CZE0DsD3.js} +1 -1
  69. package/dist/client/assets/{index-DIOUI027.js → index-Erqhm7rW.js} +7 -7
  70. package/dist/client/assets/{infoDiagram-5YYISTIA-CT2k1ZKR.js → infoDiagram-5YYISTIA-w2M_OHua.js} +1 -1
  71. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-D9ZWerXw.js → ishikawaDiagram-YF4QCWOH-CW_l9PGx.js} +1 -1
  72. package/dist/client/assets/{java-QdNEgqNn.js → java-CXTqX09w.js} +1 -1
  73. package/dist/client/assets/{javascript-C0TUIalv.js → javascript-9z9ABBep.js} +1 -1
  74. package/dist/client/assets/{jinja-CHXCs943.js → jinja-Cluf_5Bk.js} +1 -1
  75. package/dist/client/assets/{jison-Cu1JVrPL.js → jison-CqCEUtc4.js} +1 -1
  76. package/dist/client/assets/{journeyDiagram-JHISSGLW-CS0prGm3.js → journeyDiagram-JHISSGLW-D-YwusBa.js} +1 -1
  77. package/dist/client/assets/{json-DbuC832p.js → json-BMq_bPP8.js} +1 -1
  78. package/dist/client/assets/{jsx-D2ojEfVP.js → jsx-Djij59b9.js} +1 -1
  79. package/dist/client/assets/{julia-DBG4gf9M.js → julia-DybzCMnh.js} +1 -1
  80. package/dist/client/assets/{just-cnG6iJYR.js → just-4ChcAr9-.js} +1 -1
  81. package/dist/client/assets/{kanban-definition-UN3LZRKU-B5_nyCCN.js → kanban-definition-UN3LZRKU-Brvoukmm.js} +1 -1
  82. package/dist/client/assets/{latex-C-t8YHXx.js → latex-CS2TtFXG.js} +1 -1
  83. package/dist/client/assets/{line-PhY2p9yO.js → line-CqxM-stA.js} +1 -1
  84. package/dist/client/assets/{linear-DZfj4bbh.js → linear-C7T82059.js} +1 -1
  85. package/dist/client/assets/{liquid-BXKFGxIR.js → liquid-84AtYenm.js} +1 -1
  86. package/dist/client/assets/{lua-BNuqYfRm.js → lua-DJzFx3Cf.js} +1 -1
  87. package/dist/client/assets/{marko-DSC8kwzM.js → marko-BLw9BV9E.js} +1 -1
  88. package/dist/client/assets/{mdc-BwB9Dj6D.js → mdc-BjZejN3b.js} +1 -1
  89. package/dist/client/assets/{mermaid-parser.core-BrVg46EN.js → mermaid-parser.core-BdiEBAsd.js} +1 -1
  90. package/dist/client/assets/{mermaid.core-Bnf5T6PT.js → mermaid.core-BsnKhnrM.js} +3 -3
  91. package/dist/client/assets/{mindmap-definition-RKZ34NQL-CAomoDK8.js → mindmap-definition-RKZ34NQL-CBaIFnbc.js} +1 -1
  92. package/dist/client/assets/{nginx-BrQvZxpD.js → nginx-DuamCwvX.js} +1 -1
  93. package/dist/client/assets/{nim-tVYcrqi1.js → nim-Bt8c9-ge.js} +1 -1
  94. package/dist/client/assets/{perl-B-60oGLc.js → perl-D52AfmwC.js} +1 -1
  95. package/dist/client/assets/{php-DJJABwVz.js → php-BBMlJg0_.js} +1 -1
  96. package/dist/client/assets/{pieDiagram-4H26LBE5-BOtZhQH4.js → pieDiagram-4H26LBE5-BlmPajcA.js} +1 -1
  97. package/dist/client/assets/{pug-KJGvmNqp.js → pug-DFHmDqEE.js} +1 -1
  98. package/dist/client/assets/{qml-6M7QCkVi.js → qml-C0333o-g.js} +1 -1
  99. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Pcpz6-BU.js → quadrantDiagram-W4KKPZXB-Bqlm_zZk.js} +1 -1
  100. package/dist/client/assets/{r-CJX_7SfZ.js → r-DZFmvIgR.js} +1 -1
  101. package/dist/client/assets/{razor-DYZH4-tY.js → razor-BIGLkWzR.js} +1 -1
  102. package/dist/client/assets/{regexp-DWtQcg88.js → regexp-BW6jpgf9.js} +1 -1
  103. package/dist/client/assets/{requirementDiagram-4Y6WPE33-Cruba0r2.js → requirementDiagram-4Y6WPE33-B1PrtUeD.js} +1 -1
  104. package/dist/client/assets/{review-export-DUMpiyN2.js → review-export-BlgD3bdw.js} +1 -1
  105. package/dist/client/assets/{rst-idvXY1TW.js → rst-Cj51ZJaa.js} +1 -1
  106. package/dist/client/assets/{ruby-CBUdU1Jx.js → ruby-lMhGjwpf.js} +1 -1
  107. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DpLjNHX_.js → sankeyDiagram-5OEKKPKP-D2HtUNVF.js} +1 -1
  108. package/dist/client/assets/{sas-CD7-yPwf.js → sas-BWSQku7l.js} +1 -1
  109. package/dist/client/assets/{scss-CcmN3LNf.js → scss-CdRPuhLN.js} +1 -1
  110. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-Dxvk3OQI.js → sequenceDiagram-3UESZ5HK-D9SQy0XI.js} +1 -1
  111. package/dist/client/assets/{shellscript-BxkDFxdN.js → shellscript-DOfOBXcj.js} +1 -1
  112. package/dist/client/assets/{shellsession-Bz5EKpQO.js → shellsession-7XAOCVni.js} +1 -1
  113. package/dist/client/assets/{soy-DMVQQ2W2.js → soy-DDnzVRIw.js} +1 -1
  114. package/dist/client/assets/{sql-B-oK6O9W.js → sql-Ba34agKe.js} +1 -1
  115. package/dist/client/assets/{src-BNoZKBH3.js → src-BO-qT_v_.js} +1 -1
  116. package/dist/client/assets/{stata-CUZrRD4_.js → stata-BQlYGHQU.js} +1 -1
  117. package/dist/client/assets/{stateDiagram-AJRCARHV-DcGkjwzF.js → stateDiagram-AJRCARHV-7IEYjFRo.js} +1 -1
  118. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-DP6swlL9.js +1 -0
  119. package/dist/client/assets/{surrealql-TyxgDynf.js → surrealql-BET1l7SW.js} +1 -1
  120. package/dist/client/assets/{svelte-nYNHcaCO.js → svelte-Bd7ZOeQI.js} +1 -1
  121. package/dist/client/assets/{templ-DfKxw_Rl.js → templ-BYvRQdOw.js} +1 -1
  122. package/dist/client/assets/{tex-CNwID2CI.js → tex-CT8DpKxX.js} +1 -1
  123. package/dist/client/assets/{timeline-definition-PNZ67QCA-CluiloeV.js → timeline-definition-PNZ67QCA-CurQYWmi.js} +1 -1
  124. package/dist/client/assets/{ts-tags-DgUARkAi.js → ts-tags-D4iPPwau.js} +1 -1
  125. package/dist/client/assets/{tsx-2yb3iicd.js → tsx-Dgk_Aw-B.js} +1 -1
  126. package/dist/client/assets/{twig-l1ft8ReI.js → twig-abRNWeh3.js} +1 -1
  127. package/dist/client/assets/{typescript-BZqXFlL1.js → typescript-BgR1JqdC.js} +1 -1
  128. package/dist/client/assets/{vennDiagram-CIIHVFJN-DHRoQg07.js → vennDiagram-CIIHVFJN-DbrRBL5S.js} +1 -1
  129. package/dist/client/assets/{vue-BgXkMZZj.js → vue-DSkhKq4Q.js} +1 -1
  130. package/dist/client/assets/{vue-html-Byxl7Gyw.js → vue-html-J0jWoD9X.js} +1 -1
  131. package/dist/client/assets/{vue-vine-Bzpp44S9.js → vue-vine-BgX5MC8-.js} +1 -1
  132. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DQ_ujF1q.js → wardleyDiagram-YWT4CUSO-DIFXQsoP.js} +1 -1
  133. package/dist/client/assets/{xml-CpwLtzxK.js → xml-Ca53xGLB.js} +1 -1
  134. package/dist/client/assets/{xsl-VlCQyM4_.js → xsl-CNnXclYV.js} +1 -1
  135. package/dist/client/assets/{xychartDiagram-2RQKCTM6-DjG2u0Gz.js → xychartDiagram-2RQKCTM6-CmfInPqa.js} +1 -1
  136. package/dist/client/assets/{yaml-BypQObHK.js → yaml-CjjyUhfg.js} +1 -1
  137. package/dist/client/index.html +1 -1
  138. package/dist/{mcp-C9HpAFs0.mjs → mcp-DhynPewa.mjs} +35 -19
  139. package/dist/native/tui-darwin-arm64/diffing-tui +0 -0
  140. package/dist/native/tui-darwin-x64/diffing-tui +0 -0
  141. package/dist/native/tui-linux-arm64-gnu/diffing-tui +0 -0
  142. package/dist/native/tui-linux-arm64-musl/diffing-tui +0 -0
  143. package/dist/native/tui-linux-x64-gnu/diffing-tui +0 -0
  144. package/dist/native/tui-linux-x64-musl/diffing-tui +0 -0
  145. package/dist/native/tui-win32-x64-msvc/diffing-tui.exe +0 -0
  146. package/dist/{server-BmQqrWy4.mjs → server-CEbp7K-k.mjs} +289 -29
  147. package/extensions/pi/index.ts +283 -86
  148. package/extensions/pi/pi-modules.d.ts +70 -0
  149. package/extensions/pi/tsconfig.json +14 -0
  150. package/package.json +2 -1
  151. package/dist/client/assets/channel-BqKSU-HY.js +0 -1
  152. package/dist/client/assets/chunk-QZHKN3VN-BE1tL2Nx.js +0 -1
  153. package/dist/client/assets/chunk-WU5MYG2G-DP73Gify.js +0 -1
  154. package/dist/client/assets/classDiagram-4FO5ZUOK--5lgckcu.js +0 -1
  155. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CCQu7EQ7.js +0 -1
  156. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-DVv3yrP9.js +0 -1
@@ -1784,6 +1784,7 @@ function resolveEditorCommand(editor, absolutePath, platform = process.platform,
1784
1784
  case "zed": return guiEditorCommand("zed", absolutePath, platform);
1785
1785
  case "vim": return terminalEditorCommand("vim", absolutePath, platform, env);
1786
1786
  case "neovim": return terminalEditorCommand("nvim", absolutePath, platform, env);
1787
+ case "ghostty": return ghosttyEditorCommand(absolutePath, platform);
1787
1788
  default: return null;
1788
1789
  }
1789
1790
  }
@@ -1797,6 +1798,28 @@ function guiEditorCommand(bin, absolutePath, platform) {
1797
1798
  args: [absolutePath]
1798
1799
  };
1799
1800
  }
1801
+ function ghosttyEditorCommand(absolutePath, platform) {
1802
+ if (platform === "darwin") return {
1803
+ cmd: "open",
1804
+ args: [
1805
+ "-na",
1806
+ "Ghostty",
1807
+ "--args",
1808
+ "-e",
1809
+ "nvim",
1810
+ absolutePath
1811
+ ]
1812
+ };
1813
+ if (platform === "linux") return {
1814
+ cmd: "ghostty",
1815
+ args: [
1816
+ "-e",
1817
+ "nvim",
1818
+ absolutePath
1819
+ ]
1820
+ };
1821
+ return null;
1822
+ }
1800
1823
  function terminalEditorCommand(bin, absolutePath, platform, env) {
1801
1824
  if (platform === "darwin") return {
1802
1825
  cmd: "osascript",
@@ -2178,6 +2201,113 @@ var FilePrSessionStore = class {
2178
2201
  } catch {}
2179
2202
  }
2180
2203
  };
2204
+ const PATH_MATCH_CAP = 20;
2205
+ function compilePathspecGlob(pattern) {
2206
+ const source = pattern.trim();
2207
+ if (!source) return {
2208
+ error: "invalid path glob: empty pattern",
2209
+ status: 400,
2210
+ path: pattern
2211
+ };
2212
+ try {
2213
+ return {
2214
+ source,
2215
+ test: compileGlobTester(source)
2216
+ };
2217
+ } catch (error) {
2218
+ return {
2219
+ error: error instanceof Error ? error.message : "invalid path glob",
2220
+ status: 400,
2221
+ path: pattern
2222
+ };
2223
+ }
2224
+ }
2225
+ function fileMatchesPath(matcher, oldPath, newPath) {
2226
+ if (newPath && matcher.test(newPath)) return true;
2227
+ if (oldPath && matcher.test(oldPath)) return true;
2228
+ return false;
2229
+ }
2230
+ function parseExcludeList(raw) {
2231
+ if (raw == null) return [];
2232
+ const values = (Array.isArray(raw) ? raw : raw.split(",")).map((value) => value.trim()).filter(Boolean);
2233
+ for (const value of values) if (value !== "lockfiles") return {
2234
+ error: `unknown exclude: ${value}`,
2235
+ status: 400
2236
+ };
2237
+ return [...new Set(values)];
2238
+ }
2239
+ function isLockfileNoise(path) {
2240
+ const base = path.split("/").pop() ?? path;
2241
+ if (base === "package-lock.json" || base === "pnpm-lock.yaml") return true;
2242
+ if (base.endsWith(".lock")) return true;
2243
+ if (base.endsWith(".min.js")) return true;
2244
+ return false;
2245
+ }
2246
+ function displayPath(oldPath, newPath) {
2247
+ return newPath ?? oldPath ?? "";
2248
+ }
2249
+ function capPathMatches(matches) {
2250
+ return matches.slice(0, PATH_MATCH_CAP);
2251
+ }
2252
+ function compileGlobTester(pattern) {
2253
+ const parts = (pattern.includes("/") ? pattern : `**/${pattern}`).split("/");
2254
+ for (const part of parts) {
2255
+ if (part !== "**" && part.includes("**")) throw new Error(`invalid path glob: ${pattern}`);
2256
+ validateGlobSegment(part, pattern);
2257
+ }
2258
+ return (path) => matchParts(parts, path.split("/"));
2259
+ }
2260
+ function matchParts(pat, path) {
2261
+ if (pat.length === 0) return path.length === 0;
2262
+ if (pat[0] === "**") return matchParts(pat.slice(1), path) || path.length > 0 && matchParts(pat, path.slice(1));
2263
+ if (path.length === 0) return false;
2264
+ return globSegment(pat[0], path[0]) && matchParts(pat.slice(1), path.slice(1));
2265
+ }
2266
+ function validateGlobSegment(segment, pattern) {
2267
+ for (let i = 0; i < segment.length; i++) {
2268
+ if (segment[i] === "[") {
2269
+ const close = segment.indexOf("]", i + 1);
2270
+ if (close < 0 || close === i + 1) throw new Error(`invalid path glob: ${pattern}`);
2271
+ i = close;
2272
+ continue;
2273
+ }
2274
+ if (segment[i] === "\\") {
2275
+ if (segment[i + 1] == null) throw new Error(`invalid path glob: ${pattern}`);
2276
+ i++;
2277
+ }
2278
+ }
2279
+ }
2280
+ function globSegment(pattern, value) {
2281
+ return globChars([...pattern], [...value]);
2282
+ }
2283
+ function globChars(pattern, value) {
2284
+ if (pattern.length === 0) return value.length === 0;
2285
+ const [head, ...rest] = pattern;
2286
+ if (head === "*") return globChars(rest, value) || value.length > 0 && globChars(pattern, value.slice(1));
2287
+ if (head === "?") return value.length > 0 && globChars(rest, value.slice(1));
2288
+ if (head === "[") {
2289
+ const close = rest.indexOf("]");
2290
+ if (close < 0 || value.length === 0) return false;
2291
+ const classBody = rest.slice(0, close);
2292
+ const negated = classBody[0] === "!" || classBody[0] === "^";
2293
+ if (classMatches(negated ? classBody.slice(1) : classBody, value[0]) === negated) return false;
2294
+ return globChars(rest.slice(close + 1), value.slice(1));
2295
+ }
2296
+ if (head === "\\") return rest.length > 0 && value.length > 0 && rest[0] === value[0] && globChars(rest.slice(1), value.slice(1));
2297
+ return value.length > 0 && head === value[0] && globChars(rest, value.slice(1));
2298
+ }
2299
+ function classMatches(body, value) {
2300
+ for (let i = 0; i < body.length;) {
2301
+ if (i + 2 < body.length && body[i + 1] === "-") {
2302
+ if (body[i] <= value && value <= body[i + 2]) return true;
2303
+ i += 3;
2304
+ continue;
2305
+ }
2306
+ if (body[i] === value) return true;
2307
+ i++;
2308
+ }
2309
+ return false;
2310
+ }
2181
2311
  //#endregion
2182
2312
  //#region src/lib/agent-diff-index.ts
2183
2313
  /**
@@ -2406,33 +2536,140 @@ function buildAgentDiffIndex(patch, generation = nextGeneration++) {
2406
2536
  patchBytes
2407
2537
  };
2408
2538
  }
2409
- function indexSummary(index) {
2539
+ const DIRECTORY_CAP = 20;
2540
+ function indexSummary(index, excludeRaw) {
2541
+ const exclude = parseExcludeList(excludeRaw);
2542
+ if ("error" in exclude) return exclude;
2543
+ const counted = exclude.includes("lockfiles") ? index.files.filter((file) => !isLockfileNoise(displayPath(file.oldPath, file.newPath))) : index.files;
2410
2544
  const changes = {};
2411
- for (const file of index.files) changes[file.kind] = (changes[file.kind] ?? 0) + 1;
2412
- return {
2545
+ let hunks = 0;
2546
+ let rows = 0;
2547
+ let additions = 0;
2548
+ let deletions = 0;
2549
+ for (const file of counted) {
2550
+ changes[file.kind] = (changes[file.kind] ?? 0) + 1;
2551
+ hunks += file.hunks.length;
2552
+ rows += file.rowCount;
2553
+ additions += file.additions;
2554
+ deletions += file.deletions;
2555
+ }
2556
+ const summary = {
2413
2557
  generation: index.generation,
2414
2558
  complete: index.complete,
2415
- files: index.files.length,
2416
- hunks: index.totalHunks,
2417
- rows: index.totalRows,
2418
- additions: index.additions,
2419
- deletions: index.deletions,
2559
+ files: counted.length,
2560
+ hunks,
2561
+ rows,
2562
+ additions,
2563
+ deletions,
2420
2564
  patchBytes: index.patchBytes,
2421
2565
  changes,
2566
+ directories: summarizeDirectories(counted),
2422
2567
  next: [
2423
2568
  "diff_files",
2424
2569
  "diff_search",
2425
2570
  "diff_slice"
2426
2571
  ]
2427
2572
  };
2573
+ if (exclude.length > 0) summary.exclude = exclude;
2574
+ return summary;
2575
+ }
2576
+ function summarizeDirectories(files) {
2577
+ const buckets = /* @__PURE__ */ new Map();
2578
+ for (const file of files) {
2579
+ const path = displayPath(file.oldPath, file.newPath);
2580
+ const slash = path.indexOf("/");
2581
+ const dir = slash < 0 ? "." : path.slice(0, slash);
2582
+ const bucket = buckets.get(dir) ?? {
2583
+ path: dir,
2584
+ files: 0,
2585
+ hunks: 0,
2586
+ additions: 0,
2587
+ deletions: 0
2588
+ };
2589
+ bucket.files += 1;
2590
+ bucket.hunks += file.hunks.length;
2591
+ bucket.additions += file.additions;
2592
+ bucket.deletions += file.deletions;
2593
+ buckets.set(dir, bucket);
2594
+ }
2595
+ const ranked = [...buckets.values()].sort((a, b) => {
2596
+ if (b.files !== a.files) return b.files - a.files;
2597
+ return b.additions + b.deletions - (a.additions + a.deletions);
2598
+ });
2599
+ if (ranked.length <= DIRECTORY_CAP) return ranked;
2600
+ const head = ranked.slice(0, DIRECTORY_CAP);
2601
+ const rest = ranked.slice(DIRECTORY_CAP);
2602
+ const other = {
2603
+ path: "+other",
2604
+ files: 0,
2605
+ hunks: 0,
2606
+ additions: 0,
2607
+ deletions: 0
2608
+ };
2609
+ for (const bucket of rest) {
2610
+ other.files += bucket.files;
2611
+ other.hunks += bucket.hunks;
2612
+ other.additions += bucket.additions;
2613
+ other.deletions += bucket.deletions;
2614
+ }
2615
+ head.push(other);
2616
+ return head;
2617
+ }
2618
+ function scopedFiles(index, path) {
2619
+ if (path == null || path === "") return { entries: index.files.map((file, i) => ({
2620
+ index: i,
2621
+ file
2622
+ })) };
2623
+ const matcher = compilePathspecGlob(path);
2624
+ if ("error" in matcher) return matcher;
2625
+ return {
2626
+ matcher,
2627
+ entries: index.files.flatMap((file, i) => fileMatchesPath(matcher, file.oldPath, file.newPath) ? [{
2628
+ index: i,
2629
+ file
2630
+ }] : [])
2631
+ };
2428
2632
  }
2429
- function indexFiles(index, cursor = 0, limit = 100) {
2633
+ function resolveInspectFile(index, file, path) {
2634
+ const hasFile = file != null;
2635
+ const hasPath = path != null && path !== "";
2636
+ if (hasFile && hasPath) return {
2637
+ error: "path and file are mutually exclusive",
2638
+ status: 400,
2639
+ path
2640
+ };
2641
+ if (!hasFile && !hasPath) return {
2642
+ error: "file or path is required",
2643
+ status: 400
2644
+ };
2645
+ if (hasFile) return { fileIndex: file };
2646
+ const scoped = scopedFiles(index, path);
2647
+ if ("error" in scoped) return scoped;
2648
+ if (scoped.entries.length === 0) return {
2649
+ error: "path matched no files",
2650
+ status: 404,
2651
+ path
2652
+ };
2653
+ if (scoped.entries.length > 1) return {
2654
+ error: "path matched multiple files; narrow the glob or pass file",
2655
+ status: 409,
2656
+ path,
2657
+ matches: capPathMatches(scoped.entries.map(({ index, file }) => ({
2658
+ index,
2659
+ path: displayPath(file.oldPath, file.newPath)
2660
+ })))
2661
+ };
2662
+ return { fileIndex: scoped.entries[0].index };
2663
+ }
2664
+ function indexFiles(index, cursor = 0, limit = 100, path) {
2665
+ const scoped = scopedFiles(index, path);
2666
+ if ("error" in scoped) return scoped;
2430
2667
  const safeLimit = clamp(limit, 1, MAX_PAGE_LINES);
2431
2668
  const start = Math.max(0, cursor);
2432
- const end = Math.min(index.files.length, start + safeLimit);
2433
- const files = index.files.slice(start, end).map((file, offset) => ({
2434
- index: start + offset,
2435
- path: file.newPath ?? file.oldPath ?? "",
2669
+ const end = Math.min(scoped.entries.length, start + safeLimit);
2670
+ const files = scoped.entries.slice(start, end).map(({ index, file }) => ({
2671
+ index,
2672
+ path: displayPath(file.oldPath, file.newPath),
2436
2673
  oldPath: file.oldPath,
2437
2674
  newPath: file.newPath,
2438
2675
  kind: file.kind,
@@ -2442,13 +2679,16 @@ function indexFiles(index, cursor = 0, limit = 100) {
2442
2679
  additions: file.additions,
2443
2680
  deletions: file.deletions
2444
2681
  }));
2445
- return {
2682
+ const page = {
2446
2683
  generation: index.generation,
2447
2684
  returned: files.length,
2448
2685
  total: index.files.length,
2449
- nextCursor: end < index.files.length ? end : null,
2686
+ matched: scoped.entries.length,
2687
+ nextCursor: end < scoped.entries.length ? end : null,
2450
2688
  files
2451
2689
  };
2690
+ if (path) page.path = path;
2691
+ return page;
2452
2692
  }
2453
2693
  function indexHunks(index, fileIndex, cursor = 0, limit = 100, generation) {
2454
2694
  if (generation !== void 0 && generation !== index.generation) return {
@@ -2519,11 +2759,17 @@ function indexSlice(index, fileIndex, startRow = 0, maxLines = DEFAULT_SLICE_LIN
2519
2759
  rows
2520
2760
  };
2521
2761
  }
2522
- function indexSearch(index, query, fileStart = 0, rowStart = 0, limit = 100, maxBytes = DEFAULT_MAX_BYTES, generation) {
2762
+ function indexSearch(index, query, fileStart = 0, rowStart = 0, limit = 100, maxBytes = DEFAULT_MAX_BYTES, generation, path) {
2523
2763
  if (generation !== void 0 && generation !== index.generation) return {
2524
2764
  error: `stale generation ${generation}; current generation is ${index.generation}`,
2525
2765
  status: 409
2526
2766
  };
2767
+ let matcher;
2768
+ if (path) {
2769
+ const compiled = compilePathspecGlob(path);
2770
+ if ("error" in compiled) return compiled;
2771
+ matcher = compiled;
2772
+ }
2527
2773
  const q = query.toLowerCase();
2528
2774
  if (!q) return {
2529
2775
  generation: index.generation,
@@ -2542,6 +2788,7 @@ function indexSearch(index, query, fileStart = 0, rowStart = 0, limit = 100, max
2542
2788
  let nextRow = null;
2543
2789
  outer: for (let fi = Math.max(0, fileStart); fi < index.files.length; fi++) {
2544
2790
  const file = index.files[fi];
2791
+ if (matcher && !fileMatchesPath(matcher, file.oldPath, file.newPath)) continue;
2545
2792
  const path = file.newPath ?? file.oldPath ?? "";
2546
2793
  const pathMatch = path.toLowerCase().includes(q);
2547
2794
  const rowBegin = fi === fileStart ? Math.max(0, rowStart) : 0;
@@ -3146,9 +3393,21 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
3146
3393
  const n = Number(value);
3147
3394
  return Number.isFinite(n) && n >= 0 ? Math.trunc(n) : fallback;
3148
3395
  }
3396
+ function optionalUInt(value) {
3397
+ if (value == null || value === "") return void 0;
3398
+ const n = Number(value);
3399
+ return Number.isFinite(n) && n >= 0 ? Math.trunc(n) : void 0;
3400
+ }
3401
+ function inspectError(c, result) {
3402
+ const body = { error: result.error };
3403
+ if (result.path != null) body.path = result.path;
3404
+ if (result.matches != null) body.matches = result.matches;
3405
+ return c.json(body, result.status);
3406
+ }
3149
3407
  app.get("/api/diff/summary", async (c) => {
3150
3408
  const patch = await resolveAgentPatch();
3151
- const summary = indexSummary(agentDiffCache.getOrBuild(patch));
3409
+ const summary = indexSummary(agentDiffCache.getOrBuild(patch), c.req.query("exclude"));
3410
+ if ("status" in summary) return inspectError(c, summary);
3152
3411
  if (!prMode) return c.json(summary);
3153
3412
  const session = await prStore.get();
3154
3413
  if (!session) return c.json(summary);
@@ -3166,32 +3425,33 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
3166
3425
  });
3167
3426
  app.get("/api/diff/files", async (c) => {
3168
3427
  const patch = await resolveAgentPatch();
3169
- const index = agentDiffCache.getOrBuild(patch);
3170
- const cursor = parseUInt(c.req.query("cursor"), 0);
3171
- const limit = parseUInt(c.req.query("limit"), 100);
3172
- return c.json(indexFiles(index, cursor, limit));
3428
+ const result = indexFiles(agentDiffCache.getOrBuild(patch), parseUInt(c.req.query("cursor"), 0), parseUInt(c.req.query("limit"), 100), c.req.query("path"));
3429
+ if ("status" in result) return inspectError(c, result);
3430
+ return c.json(result);
3173
3431
  });
3174
3432
  app.get("/api/diff/hunks", async (c) => {
3175
3433
  const patch = await resolveAgentPatch();
3176
3434
  const index = agentDiffCache.getOrBuild(patch);
3177
- const file = parseUInt(c.req.query("file"), 0);
3435
+ const resolved = resolveInspectFile(index, optionalUInt(c.req.query("file")), c.req.query("path"));
3436
+ if ("status" in resolved) return inspectError(c, resolved);
3178
3437
  const cursor = parseUInt(c.req.query("cursor"), 0);
3179
3438
  const limit = parseUInt(c.req.query("limit"), 100);
3180
3439
  const generationRaw = c.req.query("generation");
3181
- const result = indexHunks(index, file, cursor, limit, generationRaw != null && generationRaw !== "" ? parseUInt(generationRaw, 0) : void 0);
3182
- if ("status" in result) return c.json({ error: result.error }, result.status);
3440
+ const result = indexHunks(index, resolved.fileIndex, cursor, limit, generationRaw != null && generationRaw !== "" ? parseUInt(generationRaw, 0) : void 0);
3441
+ if ("status" in result) return inspectError(c, result);
3183
3442
  return c.json(result);
3184
3443
  });
3185
3444
  app.get("/api/diff/slice", async (c) => {
3186
3445
  const patch = await resolveAgentPatch();
3187
3446
  const index = agentDiffCache.getOrBuild(patch);
3188
- const file = parseUInt(c.req.query("file"), 0);
3447
+ const resolved = resolveInspectFile(index, optionalUInt(c.req.query("file")), c.req.query("path"));
3448
+ if ("status" in resolved) return inspectError(c, resolved);
3189
3449
  const start = parseUInt(c.req.query("start"), 0);
3190
3450
  const maxLines = parseUInt(c.req.query("maxLines"), 120);
3191
3451
  const maxBytes = parseUInt(c.req.query("maxBytes"), 256 * 1024);
3192
3452
  const generationRaw = c.req.query("generation");
3193
- const result = indexSlice(index, file, start, maxLines, maxBytes, generationRaw != null && generationRaw !== "" ? parseUInt(generationRaw, 0) : void 0);
3194
- if ("status" in result) return c.json({ error: result.error }, result.status);
3453
+ const result = indexSlice(index, resolved.fileIndex, start, maxLines, maxBytes, generationRaw != null && generationRaw !== "" ? parseUInt(generationRaw, 0) : void 0);
3454
+ if ("status" in result) return inspectError(c, result);
3195
3455
  return c.json(result);
3196
3456
  });
3197
3457
  app.get("/api/diff/search", async (c) => {
@@ -3203,8 +3463,8 @@ function createApp(clientDir, diffOptsInput = DEFAULTS, commentStore, planStore,
3203
3463
  const limit = parseUInt(c.req.query("limit"), 100);
3204
3464
  const maxBytes = parseUInt(c.req.query("maxBytes"), 256 * 1024);
3205
3465
  const generationRaw = c.req.query("generation");
3206
- const result = indexSearch(index, q, file, row, limit, maxBytes, generationRaw != null && generationRaw !== "" ? parseUInt(generationRaw, 0) : void 0);
3207
- if ("status" in result) return c.json({ error: result.error }, result.status);
3466
+ const result = indexSearch(index, q, file, row, limit, maxBytes, generationRaw != null && generationRaw !== "" ? parseUInt(generationRaw, 0) : void 0, c.req.query("path"));
3467
+ if ("status" in result) return inspectError(c, result);
3208
3468
  return c.json(result);
3209
3469
  });
3210
3470
  const resolveFileVersion = async (path, version) => {