diffing 0.16.0 → 0.16.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 (155) hide show
  1. package/dist/cli.mjs +2 -2
  2. package/dist/client/assets/{angular-html-JZN5kc0L.js → angular-html-ByeJB0rh.js} +1 -1
  3. package/dist/client/assets/{angular-ts-CJtfwtPm.js → angular-ts-CiwZGajG.js} +1 -1
  4. package/dist/client/assets/{apl-BW-LdBEV.js → apl-C8k5a5fk.js} +1 -1
  5. package/dist/client/assets/{arc-CxBp7uj_.js → arc-B8VMoqVr.js} +1 -1
  6. package/dist/client/assets/{architectureDiagram-3BPJPVTR-DWRcM6V_.js → architectureDiagram-3BPJPVTR-C-ng2xNL.js} +1 -1
  7. package/dist/client/assets/{astro-gYWZxO_0.js → astro-BE-h-qpp.js} +1 -1
  8. package/dist/client/assets/{blade-Dhwn6-nM.js → blade-D-0BV1wW.js} +1 -1
  9. package/dist/client/assets/{blockDiagram-GPEHLZMM-C5RclE5o.js → blockDiagram-GPEHLZMM-DyAQuONU.js} +1 -1
  10. package/dist/client/assets/{c-DStVJ8gV.js → c-Dg_Gx29H.js} +1 -1
  11. package/dist/client/assets/{c4Diagram-AAUBKEIU-BGUA_e0K.js → c4Diagram-AAUBKEIU-k5uKTHvm.js} +1 -1
  12. package/dist/client/assets/channel-DsuXeLi4.js +1 -0
  13. package/dist/client/assets/{chunk-2J33WTMH-B7n4bw-R.js → chunk-2J33WTMH-Dod_VwYj.js} +1 -1
  14. package/dist/client/assets/{chunk-3OPIFGDE-w01g5zoV.js → chunk-3OPIFGDE-B8HMjry-.js} +1 -1
  15. package/dist/client/assets/{chunk-4BX2VUAB-DxqZfswb.js → chunk-4BX2VUAB-TvkJVIZw.js} +1 -1
  16. package/dist/client/assets/{chunk-55IACEB6-CbDClk6A.js → chunk-55IACEB6-BsXuKAKc.js} +1 -1
  17. package/dist/client/assets/{chunk-5ZQYHXKU-CnsO6rSw.js → chunk-5ZQYHXKU-BI1cj2cZ.js} +1 -1
  18. package/dist/client/assets/{chunk-727SXJPM-BtcRYmEV.js → chunk-727SXJPM-yWQ27Wk0.js} +1 -1
  19. package/dist/client/assets/{chunk-AQP2D5EJ-CGhb3Sth.js → chunk-AQP2D5EJ-B2BBCtTr.js} +1 -1
  20. package/dist/client/assets/{chunk-BSJP7CBP-DeuRDOb1.js → chunk-BSJP7CBP-CXXvPXhK.js} +1 -1
  21. package/dist/client/assets/{chunk-CSCIHK7Q-B9E4vQNj.js → chunk-CSCIHK7Q-jYiloqyi.js} +1 -1
  22. package/dist/client/assets/{chunk-FMBD7UC4-Bq9LG2aQ.js → chunk-FMBD7UC4-DZzqIT32.js} +1 -1
  23. package/dist/client/assets/{chunk-KSCS5N6A-BVlK-NC4.js → chunk-KSCS5N6A-krdCf8iG.js} +1 -1
  24. package/dist/client/assets/{chunk-L5ZTLDWV-DcdQquQQ.js → chunk-L5ZTLDWV-0uuTmeZV.js} +1 -1
  25. package/dist/client/assets/{chunk-LZXEDZCA-D3hLMWf5.js → chunk-LZXEDZCA-BBfUgD06.js} +2 -2
  26. package/dist/client/assets/{chunk-ND2GUHAM-0nxCj3q_.js → chunk-ND2GUHAM-jkir4ZKE.js} +1 -1
  27. package/dist/client/assets/{chunk-NZK2D7GU-C_Wkgas6.js → chunk-NZK2D7GU-DMWZNibJ.js} +1 -1
  28. package/dist/client/assets/{chunk-O5CBEL6O-BjWUWCBx.js → chunk-O5CBEL6O-B1e1HGaX.js} +1 -1
  29. package/dist/client/assets/chunk-QZHKN3VN-D5b3mBJ2.js +1 -0
  30. package/dist/client/assets/chunk-WU5MYG2G-C1Pa4Y04.js +1 -0
  31. package/dist/client/assets/{chunk-XPW4576I-BoIL-UAf.js → chunk-XPW4576I-AvbCAUav.js} +1 -1
  32. package/dist/client/assets/classDiagram-4FO5ZUOK-DCnVfYt-.js +1 -0
  33. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BPa1Jkt-.js +1 -0
  34. package/dist/client/assets/{cobol-BhFBbthm.js → cobol-DQnGB_GV.js} +1 -1
  35. package/dist/client/assets/{coffee-CIJ9uq8T.js → coffee-CDV5a89x.js} +1 -1
  36. package/dist/client/assets/{cose-bilkent-S5V4N54A-CbiOjhru.js → cose-bilkent-S5V4N54A-t-mDNRF2.js} +1 -1
  37. package/dist/client/assets/{cpp-DtSSrq00.js → cpp-8G6b-siI.js} +1 -1
  38. package/dist/client/assets/{crystal-CukkuPMH.js → crystal-CGYCC6FM.js} +1 -1
  39. package/dist/client/assets/{css-B1khiVXh.js → css-nez4R8_Z.js} +1 -1
  40. package/dist/client/assets/{dagre-BM42HDAG-DV-L1RXR.js → dagre-BM42HDAG--azesYIg.js} +1 -1
  41. package/dist/client/assets/{diagram-2AECGRRQ-BIS3Tcy4.js → diagram-2AECGRRQ-4WJ7nKhA.js} +1 -1
  42. package/dist/client/assets/{diagram-5GNKFQAL-BT7ScWHB.js → diagram-5GNKFQAL-C7ZGhLXn.js} +1 -1
  43. package/dist/client/assets/{diagram-KO2AKTUF-DhEvzqhQ.js → diagram-KO2AKTUF-BobK_rbl.js} +1 -1
  44. package/dist/client/assets/{diagram-LMA3HP47-DIh9QR2k.js → diagram-LMA3HP47-BOc-OkIS.js} +1 -1
  45. package/dist/client/assets/{diagram-OG6HWLK6-V6xtTYuU.js → diagram-OG6HWLK6-BEtLgm86.js} +1 -1
  46. package/dist/client/assets/{dist--CAI-jgr.js → dist-DPuBwToc.js} +1 -1
  47. package/dist/client/assets/{edge-CuCj0fvA.js → edge-Z5RMbJQv.js} +1 -1
  48. package/dist/client/assets/{edit-BMeAI0Sj.js → edit-CFxbgVPV.js} +1 -1
  49. package/dist/client/assets/{elixir-CakheKmu.js → elixir-d3C79FQc.js} +1 -1
  50. package/dist/client/assets/{elm-C1NdUKtL.js → elm-CDAf6Uyn.js} +1 -1
  51. package/dist/client/assets/{erDiagram-TEJ5UH35-DLJ0CCH3.js → erDiagram-TEJ5UH35-D-pR2GY9.js} +1 -1
  52. package/dist/client/assets/{erb-CetH6rlA.js → erb-CTJk9FKH.js} +1 -1
  53. package/dist/client/assets/{flowDiagram-I6XJVG4X-aIYTauNq.js → flowDiagram-I6XJVG4X-BwMCy2QV.js} +1 -1
  54. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DRQE_4dV.js → ganttDiagram-6RSMTGT7-BI1WLJDi.js} +1 -1
  55. package/dist/client/assets/{git-rebase-sKnthHnf.js → git-rebase-Dr0-MVoW.js} +1 -1
  56. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DbSrYZsG.js → gitGraphDiagram-PVQCEYII-CUfW0k1f.js} +1 -1
  57. package/dist/client/assets/{glimmer-js-Bt7nvyGa.js → glimmer-js-CXhLhyW-.js} +1 -1
  58. package/dist/client/assets/{glimmer-ts-YO9zmz82.js → glimmer-ts-BFAFT-VD.js} +1 -1
  59. package/dist/client/assets/{glsl-DsvRozYb.js → glsl-D7yanhgY.js} +1 -1
  60. package/dist/client/assets/{graphql-sXzHf_gb.js → graphql-CpoDxrok.js} +1 -1
  61. package/dist/client/assets/{hack-C3UDBYU5.js → hack-BL5fmC4q.js} +1 -1
  62. package/dist/client/assets/{haml-Bfx2v9ts.js → haml-ClAE6RVM.js} +1 -1
  63. package/dist/client/assets/{handlebars-D7y9m08n.js → handlebars-nHw91uOv.js} +1 -1
  64. package/dist/client/assets/{html-LJt8Nrzd.js → html-BIiOKqgG.js} +1 -1
  65. package/dist/client/assets/{html-derivative-GRYtRNx6.js → html-derivative-D1nPnuOK.js} +1 -1
  66. package/dist/client/assets/{http-CFcGSNWA.js → http-B_QcRZjt.js} +1 -1
  67. package/dist/client/assets/{hurl-EaOExxlv.js → hurl-CZE0DsD3.js} +1 -1
  68. package/dist/client/assets/{index-DIOUI027.js → index-Erqhm7rW.js} +7 -7
  69. package/dist/client/assets/{infoDiagram-5YYISTIA-CT2k1ZKR.js → infoDiagram-5YYISTIA-w2M_OHua.js} +1 -1
  70. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-D9ZWerXw.js → ishikawaDiagram-YF4QCWOH-CW_l9PGx.js} +1 -1
  71. package/dist/client/assets/{java-QdNEgqNn.js → java-CXTqX09w.js} +1 -1
  72. package/dist/client/assets/{javascript-C0TUIalv.js → javascript-9z9ABBep.js} +1 -1
  73. package/dist/client/assets/{jinja-CHXCs943.js → jinja-Cluf_5Bk.js} +1 -1
  74. package/dist/client/assets/{jison-Cu1JVrPL.js → jison-CqCEUtc4.js} +1 -1
  75. package/dist/client/assets/{journeyDiagram-JHISSGLW-CS0prGm3.js → journeyDiagram-JHISSGLW-D-YwusBa.js} +1 -1
  76. package/dist/client/assets/{json-DbuC832p.js → json-BMq_bPP8.js} +1 -1
  77. package/dist/client/assets/{jsx-D2ojEfVP.js → jsx-Djij59b9.js} +1 -1
  78. package/dist/client/assets/{julia-DBG4gf9M.js → julia-DybzCMnh.js} +1 -1
  79. package/dist/client/assets/{just-cnG6iJYR.js → just-4ChcAr9-.js} +1 -1
  80. package/dist/client/assets/{kanban-definition-UN3LZRKU-B5_nyCCN.js → kanban-definition-UN3LZRKU-Brvoukmm.js} +1 -1
  81. package/dist/client/assets/{latex-C-t8YHXx.js → latex-CS2TtFXG.js} +1 -1
  82. package/dist/client/assets/{line-PhY2p9yO.js → line-CqxM-stA.js} +1 -1
  83. package/dist/client/assets/{linear-DZfj4bbh.js → linear-C7T82059.js} +1 -1
  84. package/dist/client/assets/{liquid-BXKFGxIR.js → liquid-84AtYenm.js} +1 -1
  85. package/dist/client/assets/{lua-BNuqYfRm.js → lua-DJzFx3Cf.js} +1 -1
  86. package/dist/client/assets/{marko-DSC8kwzM.js → marko-BLw9BV9E.js} +1 -1
  87. package/dist/client/assets/{mdc-BwB9Dj6D.js → mdc-BjZejN3b.js} +1 -1
  88. package/dist/client/assets/{mermaid-parser.core-BrVg46EN.js → mermaid-parser.core-BdiEBAsd.js} +1 -1
  89. package/dist/client/assets/{mermaid.core-Bnf5T6PT.js → mermaid.core-BsnKhnrM.js} +3 -3
  90. package/dist/client/assets/{mindmap-definition-RKZ34NQL-CAomoDK8.js → mindmap-definition-RKZ34NQL-CBaIFnbc.js} +1 -1
  91. package/dist/client/assets/{nginx-BrQvZxpD.js → nginx-DuamCwvX.js} +1 -1
  92. package/dist/client/assets/{nim-tVYcrqi1.js → nim-Bt8c9-ge.js} +1 -1
  93. package/dist/client/assets/{perl-B-60oGLc.js → perl-D52AfmwC.js} +1 -1
  94. package/dist/client/assets/{php-DJJABwVz.js → php-BBMlJg0_.js} +1 -1
  95. package/dist/client/assets/{pieDiagram-4H26LBE5-BOtZhQH4.js → pieDiagram-4H26LBE5-BlmPajcA.js} +1 -1
  96. package/dist/client/assets/{pug-KJGvmNqp.js → pug-DFHmDqEE.js} +1 -1
  97. package/dist/client/assets/{qml-6M7QCkVi.js → qml-C0333o-g.js} +1 -1
  98. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Pcpz6-BU.js → quadrantDiagram-W4KKPZXB-Bqlm_zZk.js} +1 -1
  99. package/dist/client/assets/{r-CJX_7SfZ.js → r-DZFmvIgR.js} +1 -1
  100. package/dist/client/assets/{razor-DYZH4-tY.js → razor-BIGLkWzR.js} +1 -1
  101. package/dist/client/assets/{regexp-DWtQcg88.js → regexp-BW6jpgf9.js} +1 -1
  102. package/dist/client/assets/{requirementDiagram-4Y6WPE33-Cruba0r2.js → requirementDiagram-4Y6WPE33-B1PrtUeD.js} +1 -1
  103. package/dist/client/assets/{review-export-DUMpiyN2.js → review-export-BlgD3bdw.js} +1 -1
  104. package/dist/client/assets/{rst-idvXY1TW.js → rst-Cj51ZJaa.js} +1 -1
  105. package/dist/client/assets/{ruby-CBUdU1Jx.js → ruby-lMhGjwpf.js} +1 -1
  106. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DpLjNHX_.js → sankeyDiagram-5OEKKPKP-D2HtUNVF.js} +1 -1
  107. package/dist/client/assets/{sas-CD7-yPwf.js → sas-BWSQku7l.js} +1 -1
  108. package/dist/client/assets/{scss-CcmN3LNf.js → scss-CdRPuhLN.js} +1 -1
  109. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-Dxvk3OQI.js → sequenceDiagram-3UESZ5HK-D9SQy0XI.js} +1 -1
  110. package/dist/client/assets/{shellscript-BxkDFxdN.js → shellscript-DOfOBXcj.js} +1 -1
  111. package/dist/client/assets/{shellsession-Bz5EKpQO.js → shellsession-7XAOCVni.js} +1 -1
  112. package/dist/client/assets/{soy-DMVQQ2W2.js → soy-DDnzVRIw.js} +1 -1
  113. package/dist/client/assets/{sql-B-oK6O9W.js → sql-Ba34agKe.js} +1 -1
  114. package/dist/client/assets/{src-BNoZKBH3.js → src-BO-qT_v_.js} +1 -1
  115. package/dist/client/assets/{stata-CUZrRD4_.js → stata-BQlYGHQU.js} +1 -1
  116. package/dist/client/assets/{stateDiagram-AJRCARHV-DcGkjwzF.js → stateDiagram-AJRCARHV-7IEYjFRo.js} +1 -1
  117. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-DP6swlL9.js +1 -0
  118. package/dist/client/assets/{surrealql-TyxgDynf.js → surrealql-BET1l7SW.js} +1 -1
  119. package/dist/client/assets/{svelte-nYNHcaCO.js → svelte-Bd7ZOeQI.js} +1 -1
  120. package/dist/client/assets/{templ-DfKxw_Rl.js → templ-BYvRQdOw.js} +1 -1
  121. package/dist/client/assets/{tex-CNwID2CI.js → tex-CT8DpKxX.js} +1 -1
  122. package/dist/client/assets/{timeline-definition-PNZ67QCA-CluiloeV.js → timeline-definition-PNZ67QCA-CurQYWmi.js} +1 -1
  123. package/dist/client/assets/{ts-tags-DgUARkAi.js → ts-tags-D4iPPwau.js} +1 -1
  124. package/dist/client/assets/{tsx-2yb3iicd.js → tsx-Dgk_Aw-B.js} +1 -1
  125. package/dist/client/assets/{twig-l1ft8ReI.js → twig-abRNWeh3.js} +1 -1
  126. package/dist/client/assets/{typescript-BZqXFlL1.js → typescript-BgR1JqdC.js} +1 -1
  127. package/dist/client/assets/{vennDiagram-CIIHVFJN-DHRoQg07.js → vennDiagram-CIIHVFJN-DbrRBL5S.js} +1 -1
  128. package/dist/client/assets/{vue-BgXkMZZj.js → vue-DSkhKq4Q.js} +1 -1
  129. package/dist/client/assets/{vue-html-Byxl7Gyw.js → vue-html-J0jWoD9X.js} +1 -1
  130. package/dist/client/assets/{vue-vine-Bzpp44S9.js → vue-vine-BgX5MC8-.js} +1 -1
  131. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DQ_ujF1q.js → wardleyDiagram-YWT4CUSO-DIFXQsoP.js} +1 -1
  132. package/dist/client/assets/{xml-CpwLtzxK.js → xml-Ca53xGLB.js} +1 -1
  133. package/dist/client/assets/{xsl-VlCQyM4_.js → xsl-CNnXclYV.js} +1 -1
  134. package/dist/client/assets/{xychartDiagram-2RQKCTM6-DjG2u0Gz.js → xychartDiagram-2RQKCTM6-CmfInPqa.js} +1 -1
  135. package/dist/client/assets/{yaml-BypQObHK.js → yaml-CjjyUhfg.js} +1 -1
  136. package/dist/client/index.html +1 -1
  137. package/dist/{mcp-C9HpAFs0.mjs → mcp-BgrRjzn3.mjs} +1 -1
  138. package/dist/native/tui-darwin-arm64/diffing-tui +0 -0
  139. package/dist/native/tui-darwin-x64/diffing-tui +0 -0
  140. package/dist/native/tui-linux-arm64-gnu/diffing-tui +0 -0
  141. package/dist/native/tui-linux-arm64-musl/diffing-tui +0 -0
  142. package/dist/native/tui-linux-x64-gnu/diffing-tui +0 -0
  143. package/dist/native/tui-linux-x64-musl/diffing-tui +0 -0
  144. package/dist/native/tui-win32-x64-msvc/diffing-tui.exe +0 -0
  145. package/dist/{server-BmQqrWy4.mjs → server-Ct9QuV1l.mjs} +23 -0
  146. package/extensions/pi/index.ts +282 -86
  147. package/extensions/pi/pi-modules.d.ts +70 -0
  148. package/extensions/pi/tsconfig.json +14 -0
  149. package/package.json +1 -1
  150. package/dist/client/assets/channel-BqKSU-HY.js +0 -1
  151. package/dist/client/assets/chunk-QZHKN3VN-BE1tL2Nx.js +0 -1
  152. package/dist/client/assets/chunk-WU5MYG2G-DP73Gify.js +0 -1
  153. package/dist/client/assets/classDiagram-4FO5ZUOK--5lgckcu.js +0 -1
  154. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CCQu7EQ7.js +0 -1
  155. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-DVv3yrP9.js +0 -1
@@ -11,17 +11,28 @@
11
11
  * `[Skill conflicts]` banner appears when pi runs inside the diffing repo.
12
12
  *
13
13
  * All tool executions spawn the `diffing` CLI in the consumer repo (ctx.cwd).
14
- * Install: `pi install git:github.com/ahmedragab20/diffing`, or symlink this
14
+ * Install: `pi install npm:diffing` (published on npm),
15
+ * `pi install git:github.com/ahmedragab20/diffing`, or symlink this
15
16
  * directory into `~/.pi/agent/extensions/diffing`.
16
17
  */
17
18
 
18
19
  import { spawn } from "node:child_process";
19
- import { existsSync, lstatSync, readFileSync, realpathSync, rmSync, symlinkSync } from "node:fs";
20
+ import {
21
+ existsSync,
22
+ lstatSync,
23
+ readFileSync,
24
+ realpathSync,
25
+ rmSync,
26
+ symlinkSync,
27
+ } from "node:fs";
20
28
  import { homedir } from "node:os";
21
29
  import { dirname, join, resolve } from "node:path";
22
30
  import { fileURLToPath } from "node:url";
23
31
  import { StringEnum } from "@earendil-works/pi-ai";
24
- import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
32
+ import type {
33
+ ExtensionAPI,
34
+ ExtensionContext,
35
+ } from "@earendil-works/pi-coding-agent";
25
36
  import { Type } from "typebox";
26
37
 
27
38
  // ────────────────────────────────────────────────────────────────────────────
@@ -134,12 +145,12 @@ function describe(result: RunResult, command: string): string {
134
145
  return out || "(no output)";
135
146
  }
136
147
  const err = result.stderr.trim();
137
- const hint =
138
- result.exitCode === 127
139
- ? "\n\n`diffing` was not found on PATH. Install it with `npm i -g diffing` or run `diffing setup`."
140
- : result.exitCode === 3
141
- ? "\n\nNo diffing server is running for this repo. Start one with `diffing` (or the diffing_start_review tool)."
142
- : "";
148
+ let hint = "";
149
+ if (result.exitCode === 127) {
150
+ hint = `\n\n\`diffing\` was not found on PATH (ran: diffing ${command}). Install it with \`npm i -g diffing\` or run \`diffing setup\`.`;
151
+ } else if (result.exitCode === 3) {
152
+ hint = `\n\nNo diffing server is running for this repo (ran: diffing ${command}). Start one with \`diffing\` (or the diffing_start_review tool).`;
153
+ }
143
154
  return err ? `${err}${hint}` : `exit code ${result.exitCode}${hint}`;
144
155
  }
145
156
 
@@ -179,7 +190,10 @@ async function refreshStatus(ctx: ExtensionContext): Promise<void> {
179
190
  const sessions = await activeSessions(ctx.cwd);
180
191
  const active = sessions.find((s) => s.active) ?? sessions[0];
181
192
  if (active) {
182
- ctx.ui.setStatus("diffing", `diffing: ${active.mode ?? "session"} — ${active.url ?? ""}`);
193
+ ctx.ui.setStatus(
194
+ "diffing",
195
+ `diffing: ${active.mode ?? "session"} — ${active.url ?? ""}`,
196
+ );
183
197
  } else {
184
198
  ctx.ui.setStatus("diffing", "diffing: no server");
185
199
  }
@@ -209,7 +223,9 @@ function findGitRepoRoot(startDir: string): string | null {
209
223
 
210
224
  function isDiffingRoot(dir: string): boolean {
211
225
  try {
212
- const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8")) as { name?: string };
226
+ const pkg = JSON.parse(
227
+ readFileSync(join(dir, "package.json"), "utf-8"),
228
+ ) as { name?: string };
213
229
  if (pkg.name !== "diffing") return false;
214
230
  return existsSync(join(dir, SKILLS_REL));
215
231
  } catch {
@@ -262,7 +278,11 @@ function selfHealSkillLinks(canonical: string): void {
262
278
 
263
279
  function spawnDetachedReview(cwd: string, args: string[]): void {
264
280
  try {
265
- const child = spawn("diffing", args, { cwd, stdio: "ignore", detached: true });
281
+ const child = spawn("diffing", args, {
282
+ cwd,
283
+ stdio: "ignore",
284
+ detached: true,
285
+ });
266
286
  child.unref();
267
287
  } catch {
268
288
  // surfaced by the URL poll below
@@ -279,7 +299,12 @@ async function ensureReviewUrl(
279
299
  const existing = await diffingUrl(cwd);
280
300
  if (existing && reuse) return { url: existing, reused: true };
281
301
  if (existing && !reuse) return { url: existing, reused: true };
282
- spawnDetachedReview(cwd, ["--web", "--no-open", "--skip-setup", ...extraArgs]);
302
+ spawnDetachedReview(cwd, [
303
+ "--web",
304
+ "--no-open",
305
+ "--skip-setup",
306
+ ...extraArgs,
307
+ ]);
283
308
  const deadline = Date.now() + REVIEW_START_TIMEOUT_MS;
284
309
  for (;;) {
285
310
  if (signal?.aborted) break;
@@ -366,20 +391,33 @@ export default function (pi: ExtensionAPI) {
366
391
  }),
367
392
  ),
368
393
  reuse: Type.Optional(
369
- Type.Boolean({ description: "Reuse the active session if one is running. Default: true.", default: true }),
394
+ Type.Boolean({
395
+ description:
396
+ "Reuse the active session if one is running. Default: true.",
397
+ default: true,
398
+ }),
370
399
  ),
371
400
  }),
372
401
  async execute(_id, params, signal, _onUpdate, ctx) {
373
402
  try {
374
403
  const extra = params.prRef ? ["--gh-pr", params.prRef] : [];
375
- const { url, reused } = await ensureReviewUrl(ctx.cwd, extra, params.reuse ?? true, signal);
404
+ const { url, reused } = await ensureReviewUrl(
405
+ ctx.cwd,
406
+ extra,
407
+ params.reuse ?? true,
408
+ signal,
409
+ );
376
410
  await refreshStatus(ctx);
411
+ return textResult(`Review ${reused ? "reused" : "started"}: ${url}`, {
412
+ url,
413
+ reused,
414
+ mode: params.prRef ? "gh-pr" : "web",
415
+ });
416
+ } catch (error) {
377
417
  return textResult(
378
- `Review ${reused ? "reused" : "started"}: ${url}`,
379
- { url, reused, mode: params.prRef ? "gh-pr" : "web" },
418
+ error instanceof Error ? error.message : String(error),
419
+ { started: false },
380
420
  );
381
- } catch (error) {
382
- return textResult(error instanceof Error ? error.message : String(error), { started: false });
383
421
  }
384
422
  },
385
423
  });
@@ -391,9 +429,17 @@ export default function (pi: ExtensionAPI) {
391
429
  "Snapshot the current review comments as XML (or JSON). Use --open to see only unresolved threads. Parse the returned XML to act on feedback.",
392
430
  parameters: Type.Object({
393
431
  open: Type.Optional(
394
- Type.Boolean({ description: "Only include open (unresolved) comment threads. Default: true.", default: true }),
432
+ Type.Boolean({
433
+ description:
434
+ "Only include open (unresolved) comment threads. Default: true.",
435
+ default: true,
436
+ }),
437
+ ),
438
+ format: Type.Optional(
439
+ StringEnum(["xml", "json", "md"] as const, {
440
+ description: "Output format. Default: xml.",
441
+ }),
395
442
  ),
396
- format: Type.Optional(StringEnum(["xml", "json", "md"] as const, { description: "Output format. Default: xml." })),
397
443
  }),
398
444
  async execute(_id, params, _signal, _onUpdate, ctx) {
399
445
  const args = ["comments"];
@@ -411,16 +457,33 @@ export default function (pi: ExtensionAPI) {
411
457
  pi.registerTool({
412
458
  name: "diffing_reply",
413
459
  label: "Diffing Reply",
414
- description: "Append a reply to an existing comment thread by id. Answers questions; does not resolve.",
460
+ description:
461
+ "Append a reply to an existing comment thread by id. Answers questions; does not resolve.",
415
462
  parameters: Type.Object({
416
- commentId: Type.String({ description: "UUID of the comment thread to reply to." }),
463
+ commentId: Type.String({
464
+ description: "UUID of the comment thread to reply to.",
465
+ }),
417
466
  body: Type.String({ description: "Markdown reply body." }),
418
- model: Type.Optional(Type.String({ description: "Model name to attribute. Defaults to the active model." })),
467
+ model: Type.Optional(
468
+ Type.String({
469
+ description: "Model name to attribute. Defaults to the active model.",
470
+ }),
471
+ ),
419
472
  }),
420
473
  async execute(_id, params, _signal, _onUpdate, ctx) {
421
- const result = await runDiffing(["reply", params.commentId, "-", "--model", modelName(ctx, params.model)], ctx.cwd, {
422
- stdin: params.body,
423
- });
474
+ const result = await runDiffing(
475
+ [
476
+ "reply",
477
+ params.commentId,
478
+ "-",
479
+ "--model",
480
+ modelName(ctx, params.model),
481
+ ],
482
+ ctx.cwd,
483
+ {
484
+ stdin: params.body,
485
+ },
486
+ );
424
487
  return textResult(describe(result, `diffing reply ${params.commentId}`), {
425
488
  exitCode: result.exitCode,
426
489
  stderr: result.stderr.trim() || undefined,
@@ -431,14 +494,20 @@ export default function (pi: ExtensionAPI) {
431
494
  pi.registerTool({
432
495
  name: "diffing_resolve",
433
496
  label: "Diffing Resolve",
434
- description: "Mark a review comment thread resolved. Use after addressing a change request.",
435
- parameters: Type.Object({ commentId: Type.String({ description: "UUID of the comment thread." }) }),
497
+ description:
498
+ "Mark a review comment thread resolved. Use after addressing a change request.",
499
+ parameters: Type.Object({
500
+ commentId: Type.String({ description: "UUID of the comment thread." }),
501
+ }),
436
502
  async execute(_id, params, _signal, _onUpdate, ctx) {
437
503
  const result = await runDiffing(["resolve", params.commentId], ctx.cwd);
438
- return textResult(describe(result, `diffing resolve ${params.commentId}`), {
439
- exitCode: result.exitCode,
440
- stderr: result.stderr.trim() || undefined,
441
- });
504
+ return textResult(
505
+ describe(result, `diffing resolve ${params.commentId}`),
506
+ {
507
+ exitCode: result.exitCode,
508
+ stderr: result.stderr.trim() || undefined,
509
+ },
510
+ );
442
511
  },
443
512
  });
444
513
 
@@ -446,13 +515,18 @@ export default function (pi: ExtensionAPI) {
446
515
  name: "diffing_unresolve",
447
516
  label: "Diffing Unresolve",
448
517
  description: "Re-open a previously resolved comment thread.",
449
- parameters: Type.Object({ commentId: Type.String({ description: "UUID of the comment thread." }) }),
518
+ parameters: Type.Object({
519
+ commentId: Type.String({ description: "UUID of the comment thread." }),
520
+ }),
450
521
  async execute(_id, params, _signal, _onUpdate, ctx) {
451
522
  const result = await runDiffing(["unresolve", params.commentId], ctx.cwd);
452
- return textResult(describe(result, `diffing unresolve ${params.commentId}`), {
453
- exitCode: result.exitCode,
454
- stderr: result.stderr.trim() || undefined,
455
- });
523
+ return textResult(
524
+ describe(result, `diffing unresolve ${params.commentId}`),
525
+ {
526
+ exitCode: result.exitCode,
527
+ stderr: result.stderr.trim() || undefined,
528
+ },
529
+ );
456
530
  },
457
531
  });
458
532
 
@@ -462,18 +536,35 @@ export default function (pi: ExtensionAPI) {
462
536
  description:
463
537
  "Post a live progress toast to the review UI so the human sees status while work is underway. Safe to call frequently.",
464
538
  parameters: Type.Object({
465
- message: Type.String({ description: "Progress message, e.g. 'Addressing L42…'." }),
466
- pct: Type.Optional(Type.Number({ description: "Optional 0-100 completion percent." })),
467
- model: Type.Optional(Type.String({ description: "Model name to attribute. Defaults to the active model." })),
539
+ message: Type.String({
540
+ description: "Progress message, e.g. 'Addressing L42…'.",
541
+ }),
542
+ pct: Type.Optional(
543
+ Type.Number({ description: "Optional 0-100 completion percent." }),
544
+ ),
545
+ model: Type.Optional(
546
+ Type.String({
547
+ description: "Model name to attribute. Defaults to the active model.",
548
+ }),
549
+ ),
468
550
  }),
469
551
  async execute(_id, params, _signal, _onUpdate, ctx) {
470
- const args = ["progress", "--message", params.message, "--model", modelName(ctx, params.model)];
552
+ const args = [
553
+ "progress",
554
+ "--message",
555
+ params.message,
556
+ "--model",
557
+ modelName(ctx, params.model),
558
+ ];
471
559
  if (params.pct !== undefined) args.push("--pct", String(params.pct));
472
560
  const result = await runDiffing(args, ctx.cwd);
473
- return textResult(describe(result, `diffing progress --message "${params.message}"`), {
474
- exitCode: result.exitCode,
475
- stderr: result.stderr.trim() || undefined,
476
- });
561
+ return textResult(
562
+ describe(result, `diffing progress --message "${params.message}"`),
563
+ {
564
+ exitCode: result.exitCode,
565
+ stderr: result.stderr.trim() || undefined,
566
+ },
567
+ );
477
568
  },
478
569
  });
479
570
 
@@ -484,12 +575,19 @@ export default function (pi: ExtensionAPI) {
484
575
  "Synchronously wait (long-poll) until the human releases the review ('Send to agent'), then return the comments XML. Exit code 2 (or a timeout) is an expected park signal — do not silent-loop; resume once the human says the review is ready.",
485
576
  parameters: Type.Object({
486
577
  timeout: Type.Optional(
487
- Type.Number({ description: "Max seconds to block. Default: 60 (park beyond that).", default: 60 }),
578
+ Type.Number({
579
+ description: "Max seconds to block. Default: 60 (park beyond that).",
580
+ default: 60,
581
+ }),
488
582
  ),
489
583
  }),
490
584
  async execute(_id, params, signal, _onUpdate, ctx) {
491
585
  const timeout = Math.min(Math.max(params.timeout ?? 60, 1), 600);
492
- const result = await runDiffing(["await-review", "-t", String(timeout)], ctx.cwd, { signal });
586
+ const result = await runDiffing(
587
+ ["await-review", "-t", String(timeout)],
588
+ ctx.cwd,
589
+ { signal },
590
+ );
493
591
  const parked = result.exitCode === 2;
494
592
  if (parked) {
495
593
  return textResult(
@@ -512,26 +610,52 @@ export default function (pi: ExtensionAPI) {
512
610
  "Submit (or resubmit) a markdown plan for human review. Pass the plan as file (path) or body (inline text). Returns the plan id and review URL. Default handoff is async: share the URL, end the turn, act on the verdict when the human decides.",
513
611
  parameters: Type.Object({
514
612
  file: Type.Optional(
515
- Type.String({ description: "Path to the plan markdown file. Use the body parameter instead to submit inline text." }),
613
+ Type.String({
614
+ description:
615
+ "Path to the plan markdown file. Use the body parameter instead to submit inline text.",
616
+ }),
617
+ ),
618
+ body: Type.Optional(
619
+ Type.String({ description: "Inline plan markdown body." }),
620
+ ),
621
+ title: Type.Optional(
622
+ Type.String({
623
+ description: "Display title. Defaults to the plan's first heading.",
624
+ }),
516
625
  ),
517
- body: Type.Optional(Type.String({ description: "Inline plan markdown body." })),
518
- title: Type.Optional(Type.String({ description: "Display title. Defaults to the plan's first heading." })),
519
626
  planId: Type.Optional(
520
- Type.String({ description: "Resubmit a revised body for an existing plan id (bumps version, resets verdict)." }),
627
+ Type.String({
628
+ description:
629
+ "Resubmit a revised body for an existing plan id (bumps version, resets verdict).",
630
+ }),
631
+ ),
632
+ model: Type.Optional(
633
+ Type.String({
634
+ description: "Authoring model. Defaults to the active model.",
635
+ }),
521
636
  ),
522
- model: Type.Optional(Type.String({ description: "Authoring model. Defaults to the active model." })),
523
637
  }),
524
638
  async execute(_id, params, signal, _onUpdate, ctx) {
525
639
  let stdin: string | undefined;
526
640
  let fileArg = params.file;
527
641
  if (!fileArg) {
528
642
  if (!params.body) {
529
- return textResult("Provide either file (path) or body (inline plan markdown).", { submitted: false });
643
+ return textResult(
644
+ "Provide either file (path) or body (inline plan markdown).",
645
+ { submitted: false },
646
+ );
530
647
  }
531
648
  fileArg = "-";
532
649
  stdin = params.body;
533
650
  }
534
- const args = ["plan", "submit", fileArg, "--save-source", "--model", modelName(ctx, params.model)];
651
+ const args = [
652
+ "plan",
653
+ "submit",
654
+ fileArg,
655
+ "--save-source",
656
+ "--model",
657
+ modelName(ctx, params.model),
658
+ ];
535
659
  if (params.title) args.push("--title", params.title);
536
660
  if (params.planId) args.push("--id", params.planId);
537
661
  const result = await runDiffing(args, ctx.cwd, { signal, stdin });
@@ -559,12 +683,19 @@ export default function (pi: ExtensionAPI) {
559
683
  "Synchronously wait until the human decides on the submitted plan, then return the <plan-review> XML with the verdict. Exit code 2 is an expected park signal — do not silent-loop.",
560
684
  parameters: Type.Object({
561
685
  timeout: Type.Optional(
562
- Type.Number({ description: "Max seconds to block. Default: 60 (park beyond that).", default: 60 }),
686
+ Type.Number({
687
+ description: "Max seconds to block. Default: 60 (park beyond that).",
688
+ default: 60,
689
+ }),
563
690
  ),
564
691
  }),
565
692
  async execute(_id, params, signal, _onUpdate, ctx) {
566
693
  const timeout = Math.min(Math.max(params.timeout ?? 60, 1), 600);
567
- const result = await runDiffing(["plan", "await", "-t", String(timeout)], ctx.cwd, { signal });
694
+ const result = await runDiffing(
695
+ ["plan", "await", "-t", String(timeout)],
696
+ ctx.cwd,
697
+ { signal },
698
+ );
568
699
  if (result.exitCode === 2) {
569
700
  return textResult(
570
701
  `No plan verdict within ${timeout}s (park). Share the plan URL and end your turn; run diffing_plan_await again when the human says the verdict is ready.`,
@@ -582,9 +713,12 @@ export default function (pi: ExtensionAPI) {
582
713
  pi.registerTool({
583
714
  name: "diffing_plan_list",
584
715
  label: "Diffing Plan List",
585
- description: "List submitted plans for the current repo (id, decision, version, open comments, title).",
716
+ description:
717
+ "List submitted plans for the current repo (id, decision, version, open comments, title).",
586
718
  parameters: Type.Object({
587
- json: Type.Optional(Type.Boolean({ description: "Emit raw JSON instead of the table." })),
719
+ json: Type.Optional(
720
+ Type.Boolean({ description: "Emit raw JSON instead of the table." }),
721
+ ),
588
722
  }),
589
723
  async execute(_id, params, _signal, _onUpdate, ctx) {
590
724
  const args = ["plan", "list"];
@@ -600,10 +734,15 @@ export default function (pi: ExtensionAPI) {
600
734
  pi.registerTool({
601
735
  name: "diffing_plan_show",
602
736
  label: "Diffing Plan Show",
603
- description: "Show a single plan as <plan-review> XML (or raw JSON) including the verdict and open comments.",
737
+ description:
738
+ "Show a single plan as <plan-review> XML (or raw JSON) including the verdict and open comments.",
604
739
  parameters: Type.Object({
605
- planId: Type.Optional(Type.String({ description: "Plan id. Omit for the latest plan." })),
606
- json: Type.Optional(Type.Boolean({ description: "Emit raw JSON instead of XML." })),
740
+ planId: Type.Optional(
741
+ Type.String({ description: "Plan id. Omit for the latest plan." }),
742
+ ),
743
+ json: Type.Optional(
744
+ Type.Boolean({ description: "Emit raw JSON instead of XML." }),
745
+ ),
607
746
  }),
608
747
  async execute(_id, params, _signal, _onUpdate, ctx) {
609
748
  const args = ["plan", "show"];
@@ -620,22 +759,37 @@ export default function (pi: ExtensionAPI) {
620
759
  pi.registerTool({
621
760
  name: "diffing_plan_reply",
622
761
  label: "Diffing Plan Reply",
623
- description: "Reply to an inline plan comment (the owning plan is resolved automatically).",
762
+ description:
763
+ "Reply to an inline plan comment (the owning plan is resolved automatically).",
624
764
  parameters: Type.Object({
625
765
  commentId: Type.String({ description: "UUID of the plan comment." }),
626
766
  body: Type.String({ description: "Markdown reply body." }),
627
- model: Type.Optional(Type.String({ description: "Model name to attribute. Defaults to the active model." })),
767
+ model: Type.Optional(
768
+ Type.String({
769
+ description: "Model name to attribute. Defaults to the active model.",
770
+ }),
771
+ ),
628
772
  }),
629
773
  async execute(_id, params, _signal, _onUpdate, ctx) {
630
774
  const result = await runDiffing(
631
- ["plan", "reply", params.commentId, "-", "--model", modelName(ctx, params.model)],
775
+ [
776
+ "plan",
777
+ "reply",
778
+ params.commentId,
779
+ "-",
780
+ "--model",
781
+ modelName(ctx, params.model),
782
+ ],
632
783
  ctx.cwd,
633
784
  { stdin: params.body },
634
785
  );
635
- return textResult(describe(result, `diffing plan reply ${params.commentId}`), {
636
- exitCode: result.exitCode,
637
- stderr: result.stderr.trim() || undefined,
638
- });
786
+ return textResult(
787
+ describe(result, `diffing plan reply ${params.commentId}`),
788
+ {
789
+ exitCode: result.exitCode,
790
+ stderr: result.stderr.trim() || undefined,
791
+ },
792
+ );
639
793
  },
640
794
  });
641
795
 
@@ -643,25 +797,36 @@ export default function (pi: ExtensionAPI) {
643
797
  name: "diffing_plan_resolve",
644
798
  label: "Diffing Plan Resolve",
645
799
  description: "Mark a plan comment resolved.",
646
- parameters: Type.Object({ commentId: Type.String({ description: "UUID of the plan comment." }) }),
800
+ parameters: Type.Object({
801
+ commentId: Type.String({ description: "UUID of the plan comment." }),
802
+ }),
647
803
  async execute(_id, params, _signal, _onUpdate, ctx) {
648
- const result = await runDiffing(["plan", "resolve", params.commentId], ctx.cwd);
649
- return textResult(describe(result, `diffing plan resolve ${params.commentId}`), {
650
- exitCode: result.exitCode,
651
- stderr: result.stderr.trim() || undefined,
652
- });
804
+ const result = await runDiffing(
805
+ ["plan", "resolve", params.commentId],
806
+ ctx.cwd,
807
+ );
808
+ return textResult(
809
+ describe(result, `diffing plan resolve ${params.commentId}`),
810
+ {
811
+ exitCode: result.exitCode,
812
+ stderr: result.stderr.trim() || undefined,
813
+ },
814
+ );
653
815
  },
654
816
  });
655
817
 
656
818
  pi.registerTool({
657
819
  name: "diffing_url",
658
820
  label: "Diffing URL",
659
- description: "Print the base URL of the active diffing review server for this repo, or report that none is running.",
821
+ description:
822
+ "Print the base URL of the active diffing review server for this repo, or report that none is running.",
660
823
  parameters: Type.Object({}),
661
824
  async execute(_id, _params, _signal, _onUpdate, ctx) {
662
825
  const url = await diffingUrl(ctx.cwd);
663
826
  return textResult(
664
- url ? url : "No diffing server running for this repo. Start one with diffing_start_review or `/diffing`.",
827
+ url
828
+ ? url
829
+ : "No diffing server running for this repo. Start one with diffing_start_review or `/diffing`.",
665
830
  { url },
666
831
  );
667
832
  },
@@ -673,9 +838,19 @@ export default function (pi: ExtensionAPI) {
673
838
  description:
674
839
  "List, select, or stop live diffing review sessions for this repo. use: retarget agent commands to a session id prefix. stop: graceful shutdown of a session.",
675
840
  parameters: Type.Object({
676
- action: Type.Optional(StringEnum(["list", "use", "stop"] as const, { description: "Action. Default: list." })),
677
- value: Type.Optional(Type.String({ description: "Session id prefix (for use/stop), or 'active'/'all'." })),
678
- json: Type.Optional(Type.Boolean({ description: "Emit JSON for list. Default: false." })),
841
+ action: Type.Optional(
842
+ StringEnum(["list", "use", "stop"] as const, {
843
+ description: "Action. Default: list.",
844
+ }),
845
+ ),
846
+ value: Type.Optional(
847
+ Type.String({
848
+ description: "Session id prefix (for use/stop), or 'active'/'all'.",
849
+ }),
850
+ ),
851
+ json: Type.Optional(
852
+ Type.Boolean({ description: "Emit JSON for list. Default: false." }),
853
+ ),
679
854
  }),
680
855
  async execute(_id, params, _signal, _onUpdate, ctx) {
681
856
  const action = params.action ?? "list";
@@ -700,7 +875,11 @@ export default function (pi: ExtensionAPI) {
700
875
  description:
701
876
  "Probe the active GitHub PR review session: PR identity, head/base SHAs, patch size, and conversation/draft counts. Fails cleanly when no PR session is active.",
702
877
  parameters: Type.Object({
703
- json: Type.Optional(Type.Boolean({ description: "Emit raw JSON instead of human-readable output." })),
878
+ json: Type.Optional(
879
+ Type.Boolean({
880
+ description: "Emit raw JSON instead of human-readable output.",
881
+ }),
882
+ ),
704
883
  }),
705
884
  async execute(_id, params, _signal, _onUpdate, ctx) {
706
885
  const args = ["gh", "overview"];
@@ -719,15 +898,29 @@ export default function (pi: ExtensionAPI) {
719
898
  description:
720
899
  "Escape hatch: run any documented `diffing` agent subcommand with raw arguments. Examples: ['inspect','summary'], ['comment','edit',id,'--body','...'], ['gh','threads','--unresolved'], ['gh','reviews'], ['plan','versions',id], ['doctor'], ['mode']. Returns stdout + exit code; exit 3 means no server.",
721
900
  parameters: Type.Object({
722
- args: Type.Array(Type.String({ description: "Subcommand and arguments, e.g. ['inspect','summary']." })),
723
- stdin: Type.Optional(Type.String({ description: "Optional text to pipe to the command's stdin (e.g. reply bodies)." })),
901
+ args: Type.Array(
902
+ Type.String({
903
+ description: "Subcommand and arguments, e.g. ['inspect','summary'].",
904
+ }),
905
+ ),
906
+ stdin: Type.Optional(
907
+ Type.String({
908
+ description:
909
+ "Optional text to pipe to the command's stdin (e.g. reply bodies).",
910
+ }),
911
+ ),
724
912
  }),
725
913
  async execute(_id, params, signal, _onUpdate, ctx) {
726
914
  const args = params.args;
727
915
  if (!Array.isArray(args) || args.length === 0) {
728
- return textResult("Provide at least one subcommand argument.", { ran: false });
916
+ return textResult("Provide at least one subcommand argument.", {
917
+ ran: false,
918
+ });
729
919
  }
730
- const result = await runDiffing(args, ctx.cwd, { signal, stdin: params.stdin });
920
+ const result = await runDiffing(args, ctx.cwd, {
921
+ signal,
922
+ stdin: params.stdin,
923
+ });
731
924
  return textResult(describe(result, `diffing ${args.join(" ")}`), {
732
925
  exitCode: result.exitCode,
733
926
  stderr: result.stderr.trim() || undefined,
@@ -747,7 +940,10 @@ export default function (pi: ExtensionAPI) {
747
940
  await refreshStatus(ctx);
748
941
  ctx.ui.notify(`diffing review: ${url}`, "info");
749
942
  } catch (error) {
750
- ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
943
+ ctx.ui.notify(
944
+ error instanceof Error ? error.message : String(error),
945
+ "error",
946
+ );
751
947
  }
752
948
  },
753
949
  });
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Ambient declarations for pi-bundled modules.
3
+ *
4
+ * The pi harness provides `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`
5
+ * and `typebox` at extension load time — they are intentionally NOT installed in
6
+ * this repository (keeps the product lockfile free of the pi runtime tree). These
7
+ * declarations let the extension typecheck standalone for editors/LSP; the
8
+ * authoritative types are the real packages resolved by pi at runtime.
9
+ */
10
+
11
+ declare module "@earendil-works/pi-ai" {
12
+ export function StringEnum<T extends readonly string[]>(
13
+ values: T,
14
+ options?: { description?: string; default?: T[number] },
15
+ ): { type: "string"; enum: T; description?: string; default?: T[number] };
16
+ }
17
+
18
+ declare module "@earendil-works/pi-coding-agent" {
19
+ export interface ExtensionContext {
20
+ cwd: string;
21
+ hasUI: boolean;
22
+ model?: { id: string; provider?: string };
23
+ ui: {
24
+ setStatus(id: string, text: string): void;
25
+ notify(
26
+ text: string,
27
+ level?: "info" | "error" | "success" | "warning",
28
+ ): void;
29
+ setWidget?(id: string, lines: string[]): void;
30
+ };
31
+ }
32
+
33
+ export interface ToolResult {
34
+ content: { type: "text"; text: string }[];
35
+ details: Record<string, unknown>;
36
+ }
37
+
38
+ export interface ToolDefinition {
39
+ name: string;
40
+ label?: string;
41
+ description: string;
42
+ parameters: any;
43
+ execute(
44
+ toolCallId: string,
45
+ params: any,
46
+ signal: AbortSignal | undefined,
47
+ onUpdate: any,
48
+ ctx: ExtensionContext,
49
+ ): Promise<ToolResult> | ToolResult;
50
+ }
51
+
52
+ export interface ExtensionAPI {
53
+ on(
54
+ event: string,
55
+ handler: (event: any, ctx: ExtensionContext) => any,
56
+ ): void;
57
+ registerTool(definition: ToolDefinition): void;
58
+ registerCommand(
59
+ name: string,
60
+ definition: {
61
+ description?: string;
62
+ handler: (args: string | undefined, ctx: ExtensionContext) => any;
63
+ },
64
+ ): void;
65
+ }
66
+ }
67
+
68
+ declare module "typebox" {
69
+ export const Type: any;
70
+ }