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
@@ -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
  // ────────────────────────────────────────────────────────────────────────────
@@ -34,6 +45,7 @@ const SKILL_NAMES = [
34
45
  "diffing-plan-review",
35
46
  "diffing-pr-address",
36
47
  "diffing-pr-read",
48
+ "diffing-release",
37
49
  "diffing-review",
38
50
  "diffing-start-review",
39
51
  ] as const;
@@ -134,12 +146,12 @@ function describe(result: RunResult, command: string): string {
134
146
  return out || "(no output)";
135
147
  }
136
148
  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
- : "";
149
+ let hint = "";
150
+ if (result.exitCode === 127) {
151
+ hint = `\n\n\`diffing\` was not found on PATH (ran: diffing ${command}). Install it with \`npm i -g diffing\` or run \`diffing setup\`.`;
152
+ } else if (result.exitCode === 3) {
153
+ hint = `\n\nNo diffing server is running for this repo (ran: diffing ${command}). Start one with \`diffing\` (or the diffing_start_review tool).`;
154
+ }
143
155
  return err ? `${err}${hint}` : `exit code ${result.exitCode}${hint}`;
144
156
  }
145
157
 
@@ -179,7 +191,10 @@ async function refreshStatus(ctx: ExtensionContext): Promise<void> {
179
191
  const sessions = await activeSessions(ctx.cwd);
180
192
  const active = sessions.find((s) => s.active) ?? sessions[0];
181
193
  if (active) {
182
- ctx.ui.setStatus("diffing", `diffing: ${active.mode ?? "session"} — ${active.url ?? ""}`);
194
+ ctx.ui.setStatus(
195
+ "diffing",
196
+ `diffing: ${active.mode ?? "session"} — ${active.url ?? ""}`,
197
+ );
183
198
  } else {
184
199
  ctx.ui.setStatus("diffing", "diffing: no server");
185
200
  }
@@ -209,7 +224,9 @@ function findGitRepoRoot(startDir: string): string | null {
209
224
 
210
225
  function isDiffingRoot(dir: string): boolean {
211
226
  try {
212
- const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8")) as { name?: string };
227
+ const pkg = JSON.parse(
228
+ readFileSync(join(dir, "package.json"), "utf-8"),
229
+ ) as { name?: string };
213
230
  if (pkg.name !== "diffing") return false;
214
231
  return existsSync(join(dir, SKILLS_REL));
215
232
  } catch {
@@ -262,7 +279,11 @@ function selfHealSkillLinks(canonical: string): void {
262
279
 
263
280
  function spawnDetachedReview(cwd: string, args: string[]): void {
264
281
  try {
265
- const child = spawn("diffing", args, { cwd, stdio: "ignore", detached: true });
282
+ const child = spawn("diffing", args, {
283
+ cwd,
284
+ stdio: "ignore",
285
+ detached: true,
286
+ });
266
287
  child.unref();
267
288
  } catch {
268
289
  // surfaced by the URL poll below
@@ -279,7 +300,12 @@ async function ensureReviewUrl(
279
300
  const existing = await diffingUrl(cwd);
280
301
  if (existing && reuse) return { url: existing, reused: true };
281
302
  if (existing && !reuse) return { url: existing, reused: true };
282
- spawnDetachedReview(cwd, ["--web", "--no-open", "--skip-setup", ...extraArgs]);
303
+ spawnDetachedReview(cwd, [
304
+ "--web",
305
+ "--no-open",
306
+ "--skip-setup",
307
+ ...extraArgs,
308
+ ]);
283
309
  const deadline = Date.now() + REVIEW_START_TIMEOUT_MS;
284
310
  for (;;) {
285
311
  if (signal?.aborted) break;
@@ -366,20 +392,33 @@ export default function (pi: ExtensionAPI) {
366
392
  }),
367
393
  ),
368
394
  reuse: Type.Optional(
369
- Type.Boolean({ description: "Reuse the active session if one is running. Default: true.", default: true }),
395
+ Type.Boolean({
396
+ description:
397
+ "Reuse the active session if one is running. Default: true.",
398
+ default: true,
399
+ }),
370
400
  ),
371
401
  }),
372
402
  async execute(_id, params, signal, _onUpdate, ctx) {
373
403
  try {
374
404
  const extra = params.prRef ? ["--gh-pr", params.prRef] : [];
375
- const { url, reused } = await ensureReviewUrl(ctx.cwd, extra, params.reuse ?? true, signal);
405
+ const { url, reused } = await ensureReviewUrl(
406
+ ctx.cwd,
407
+ extra,
408
+ params.reuse ?? true,
409
+ signal,
410
+ );
376
411
  await refreshStatus(ctx);
412
+ return textResult(`Review ${reused ? "reused" : "started"}: ${url}`, {
413
+ url,
414
+ reused,
415
+ mode: params.prRef ? "gh-pr" : "web",
416
+ });
417
+ } catch (error) {
377
418
  return textResult(
378
- `Review ${reused ? "reused" : "started"}: ${url}`,
379
- { url, reused, mode: params.prRef ? "gh-pr" : "web" },
419
+ error instanceof Error ? error.message : String(error),
420
+ { started: false },
380
421
  );
381
- } catch (error) {
382
- return textResult(error instanceof Error ? error.message : String(error), { started: false });
383
422
  }
384
423
  },
385
424
  });
@@ -391,9 +430,17 @@ export default function (pi: ExtensionAPI) {
391
430
  "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
431
  parameters: Type.Object({
393
432
  open: Type.Optional(
394
- Type.Boolean({ description: "Only include open (unresolved) comment threads. Default: true.", default: true }),
433
+ Type.Boolean({
434
+ description:
435
+ "Only include open (unresolved) comment threads. Default: true.",
436
+ default: true,
437
+ }),
438
+ ),
439
+ format: Type.Optional(
440
+ StringEnum(["xml", "json", "md"] as const, {
441
+ description: "Output format. Default: xml.",
442
+ }),
395
443
  ),
396
- format: Type.Optional(StringEnum(["xml", "json", "md"] as const, { description: "Output format. Default: xml." })),
397
444
  }),
398
445
  async execute(_id, params, _signal, _onUpdate, ctx) {
399
446
  const args = ["comments"];
@@ -411,16 +458,33 @@ export default function (pi: ExtensionAPI) {
411
458
  pi.registerTool({
412
459
  name: "diffing_reply",
413
460
  label: "Diffing Reply",
414
- description: "Append a reply to an existing comment thread by id. Answers questions; does not resolve.",
461
+ description:
462
+ "Append a reply to an existing comment thread by id. Answers questions; does not resolve.",
415
463
  parameters: Type.Object({
416
- commentId: Type.String({ description: "UUID of the comment thread to reply to." }),
464
+ commentId: Type.String({
465
+ description: "UUID of the comment thread to reply to.",
466
+ }),
417
467
  body: Type.String({ description: "Markdown reply body." }),
418
- model: Type.Optional(Type.String({ description: "Model name to attribute. Defaults to the active model." })),
468
+ model: Type.Optional(
469
+ Type.String({
470
+ description: "Model name to attribute. Defaults to the active model.",
471
+ }),
472
+ ),
419
473
  }),
420
474
  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
- });
475
+ const result = await runDiffing(
476
+ [
477
+ "reply",
478
+ params.commentId,
479
+ "-",
480
+ "--model",
481
+ modelName(ctx, params.model),
482
+ ],
483
+ ctx.cwd,
484
+ {
485
+ stdin: params.body,
486
+ },
487
+ );
424
488
  return textResult(describe(result, `diffing reply ${params.commentId}`), {
425
489
  exitCode: result.exitCode,
426
490
  stderr: result.stderr.trim() || undefined,
@@ -431,14 +495,20 @@ export default function (pi: ExtensionAPI) {
431
495
  pi.registerTool({
432
496
  name: "diffing_resolve",
433
497
  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." }) }),
498
+ description:
499
+ "Mark a review comment thread resolved. Use after addressing a change request.",
500
+ parameters: Type.Object({
501
+ commentId: Type.String({ description: "UUID of the comment thread." }),
502
+ }),
436
503
  async execute(_id, params, _signal, _onUpdate, ctx) {
437
504
  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
- });
505
+ return textResult(
506
+ describe(result, `diffing resolve ${params.commentId}`),
507
+ {
508
+ exitCode: result.exitCode,
509
+ stderr: result.stderr.trim() || undefined,
510
+ },
511
+ );
442
512
  },
443
513
  });
444
514
 
@@ -446,13 +516,18 @@ export default function (pi: ExtensionAPI) {
446
516
  name: "diffing_unresolve",
447
517
  label: "Diffing Unresolve",
448
518
  description: "Re-open a previously resolved comment thread.",
449
- parameters: Type.Object({ commentId: Type.String({ description: "UUID of the comment thread." }) }),
519
+ parameters: Type.Object({
520
+ commentId: Type.String({ description: "UUID of the comment thread." }),
521
+ }),
450
522
  async execute(_id, params, _signal, _onUpdate, ctx) {
451
523
  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
- });
524
+ return textResult(
525
+ describe(result, `diffing unresolve ${params.commentId}`),
526
+ {
527
+ exitCode: result.exitCode,
528
+ stderr: result.stderr.trim() || undefined,
529
+ },
530
+ );
456
531
  },
457
532
  });
458
533
 
@@ -462,18 +537,35 @@ export default function (pi: ExtensionAPI) {
462
537
  description:
463
538
  "Post a live progress toast to the review UI so the human sees status while work is underway. Safe to call frequently.",
464
539
  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." })),
540
+ message: Type.String({
541
+ description: "Progress message, e.g. 'Addressing L42…'.",
542
+ }),
543
+ pct: Type.Optional(
544
+ Type.Number({ description: "Optional 0-100 completion percent." }),
545
+ ),
546
+ model: Type.Optional(
547
+ Type.String({
548
+ description: "Model name to attribute. Defaults to the active model.",
549
+ }),
550
+ ),
468
551
  }),
469
552
  async execute(_id, params, _signal, _onUpdate, ctx) {
470
- const args = ["progress", "--message", params.message, "--model", modelName(ctx, params.model)];
553
+ const args = [
554
+ "progress",
555
+ "--message",
556
+ params.message,
557
+ "--model",
558
+ modelName(ctx, params.model),
559
+ ];
471
560
  if (params.pct !== undefined) args.push("--pct", String(params.pct));
472
561
  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
- });
562
+ return textResult(
563
+ describe(result, `diffing progress --message "${params.message}"`),
564
+ {
565
+ exitCode: result.exitCode,
566
+ stderr: result.stderr.trim() || undefined,
567
+ },
568
+ );
477
569
  },
478
570
  });
479
571
 
@@ -484,12 +576,19 @@ export default function (pi: ExtensionAPI) {
484
576
  "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
577
  parameters: Type.Object({
486
578
  timeout: Type.Optional(
487
- Type.Number({ description: "Max seconds to block. Default: 60 (park beyond that).", default: 60 }),
579
+ Type.Number({
580
+ description: "Max seconds to block. Default: 60 (park beyond that).",
581
+ default: 60,
582
+ }),
488
583
  ),
489
584
  }),
490
585
  async execute(_id, params, signal, _onUpdate, ctx) {
491
586
  const timeout = Math.min(Math.max(params.timeout ?? 60, 1), 600);
492
- const result = await runDiffing(["await-review", "-t", String(timeout)], ctx.cwd, { signal });
587
+ const result = await runDiffing(
588
+ ["await-review", "-t", String(timeout)],
589
+ ctx.cwd,
590
+ { signal },
591
+ );
493
592
  const parked = result.exitCode === 2;
494
593
  if (parked) {
495
594
  return textResult(
@@ -512,26 +611,52 @@ export default function (pi: ExtensionAPI) {
512
611
  "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
612
  parameters: Type.Object({
514
613
  file: Type.Optional(
515
- Type.String({ description: "Path to the plan markdown file. Use the body parameter instead to submit inline text." }),
614
+ Type.String({
615
+ description:
616
+ "Path to the plan markdown file. Use the body parameter instead to submit inline text.",
617
+ }),
618
+ ),
619
+ body: Type.Optional(
620
+ Type.String({ description: "Inline plan markdown body." }),
621
+ ),
622
+ title: Type.Optional(
623
+ Type.String({
624
+ description: "Display title. Defaults to the plan's first heading.",
625
+ }),
516
626
  ),
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
627
  planId: Type.Optional(
520
- Type.String({ description: "Resubmit a revised body for an existing plan id (bumps version, resets verdict)." }),
628
+ Type.String({
629
+ description:
630
+ "Resubmit a revised body for an existing plan id (bumps version, resets verdict).",
631
+ }),
632
+ ),
633
+ model: Type.Optional(
634
+ Type.String({
635
+ description: "Authoring model. Defaults to the active model.",
636
+ }),
521
637
  ),
522
- model: Type.Optional(Type.String({ description: "Authoring model. Defaults to the active model." })),
523
638
  }),
524
639
  async execute(_id, params, signal, _onUpdate, ctx) {
525
640
  let stdin: string | undefined;
526
641
  let fileArg = params.file;
527
642
  if (!fileArg) {
528
643
  if (!params.body) {
529
- return textResult("Provide either file (path) or body (inline plan markdown).", { submitted: false });
644
+ return textResult(
645
+ "Provide either file (path) or body (inline plan markdown).",
646
+ { submitted: false },
647
+ );
530
648
  }
531
649
  fileArg = "-";
532
650
  stdin = params.body;
533
651
  }
534
- const args = ["plan", "submit", fileArg, "--save-source", "--model", modelName(ctx, params.model)];
652
+ const args = [
653
+ "plan",
654
+ "submit",
655
+ fileArg,
656
+ "--save-source",
657
+ "--model",
658
+ modelName(ctx, params.model),
659
+ ];
535
660
  if (params.title) args.push("--title", params.title);
536
661
  if (params.planId) args.push("--id", params.planId);
537
662
  const result = await runDiffing(args, ctx.cwd, { signal, stdin });
@@ -559,12 +684,19 @@ export default function (pi: ExtensionAPI) {
559
684
  "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
685
  parameters: Type.Object({
561
686
  timeout: Type.Optional(
562
- Type.Number({ description: "Max seconds to block. Default: 60 (park beyond that).", default: 60 }),
687
+ Type.Number({
688
+ description: "Max seconds to block. Default: 60 (park beyond that).",
689
+ default: 60,
690
+ }),
563
691
  ),
564
692
  }),
565
693
  async execute(_id, params, signal, _onUpdate, ctx) {
566
694
  const timeout = Math.min(Math.max(params.timeout ?? 60, 1), 600);
567
- const result = await runDiffing(["plan", "await", "-t", String(timeout)], ctx.cwd, { signal });
695
+ const result = await runDiffing(
696
+ ["plan", "await", "-t", String(timeout)],
697
+ ctx.cwd,
698
+ { signal },
699
+ );
568
700
  if (result.exitCode === 2) {
569
701
  return textResult(
570
702
  `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 +714,12 @@ export default function (pi: ExtensionAPI) {
582
714
  pi.registerTool({
583
715
  name: "diffing_plan_list",
584
716
  label: "Diffing Plan List",
585
- description: "List submitted plans for the current repo (id, decision, version, open comments, title).",
717
+ description:
718
+ "List submitted plans for the current repo (id, decision, version, open comments, title).",
586
719
  parameters: Type.Object({
587
- json: Type.Optional(Type.Boolean({ description: "Emit raw JSON instead of the table." })),
720
+ json: Type.Optional(
721
+ Type.Boolean({ description: "Emit raw JSON instead of the table." }),
722
+ ),
588
723
  }),
589
724
  async execute(_id, params, _signal, _onUpdate, ctx) {
590
725
  const args = ["plan", "list"];
@@ -600,10 +735,15 @@ export default function (pi: ExtensionAPI) {
600
735
  pi.registerTool({
601
736
  name: "diffing_plan_show",
602
737
  label: "Diffing Plan Show",
603
- description: "Show a single plan as <plan-review> XML (or raw JSON) including the verdict and open comments.",
738
+ description:
739
+ "Show a single plan as <plan-review> XML (or raw JSON) including the verdict and open comments.",
604
740
  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." })),
741
+ planId: Type.Optional(
742
+ Type.String({ description: "Plan id. Omit for the latest plan." }),
743
+ ),
744
+ json: Type.Optional(
745
+ Type.Boolean({ description: "Emit raw JSON instead of XML." }),
746
+ ),
607
747
  }),
608
748
  async execute(_id, params, _signal, _onUpdate, ctx) {
609
749
  const args = ["plan", "show"];
@@ -620,22 +760,37 @@ export default function (pi: ExtensionAPI) {
620
760
  pi.registerTool({
621
761
  name: "diffing_plan_reply",
622
762
  label: "Diffing Plan Reply",
623
- description: "Reply to an inline plan comment (the owning plan is resolved automatically).",
763
+ description:
764
+ "Reply to an inline plan comment (the owning plan is resolved automatically).",
624
765
  parameters: Type.Object({
625
766
  commentId: Type.String({ description: "UUID of the plan comment." }),
626
767
  body: Type.String({ description: "Markdown reply body." }),
627
- model: Type.Optional(Type.String({ description: "Model name to attribute. Defaults to the active model." })),
768
+ model: Type.Optional(
769
+ Type.String({
770
+ description: "Model name to attribute. Defaults to the active model.",
771
+ }),
772
+ ),
628
773
  }),
629
774
  async execute(_id, params, _signal, _onUpdate, ctx) {
630
775
  const result = await runDiffing(
631
- ["plan", "reply", params.commentId, "-", "--model", modelName(ctx, params.model)],
776
+ [
777
+ "plan",
778
+ "reply",
779
+ params.commentId,
780
+ "-",
781
+ "--model",
782
+ modelName(ctx, params.model),
783
+ ],
632
784
  ctx.cwd,
633
785
  { stdin: params.body },
634
786
  );
635
- return textResult(describe(result, `diffing plan reply ${params.commentId}`), {
636
- exitCode: result.exitCode,
637
- stderr: result.stderr.trim() || undefined,
638
- });
787
+ return textResult(
788
+ describe(result, `diffing plan reply ${params.commentId}`),
789
+ {
790
+ exitCode: result.exitCode,
791
+ stderr: result.stderr.trim() || undefined,
792
+ },
793
+ );
639
794
  },
640
795
  });
641
796
 
@@ -643,25 +798,36 @@ export default function (pi: ExtensionAPI) {
643
798
  name: "diffing_plan_resolve",
644
799
  label: "Diffing Plan Resolve",
645
800
  description: "Mark a plan comment resolved.",
646
- parameters: Type.Object({ commentId: Type.String({ description: "UUID of the plan comment." }) }),
801
+ parameters: Type.Object({
802
+ commentId: Type.String({ description: "UUID of the plan comment." }),
803
+ }),
647
804
  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
- });
805
+ const result = await runDiffing(
806
+ ["plan", "resolve", params.commentId],
807
+ ctx.cwd,
808
+ );
809
+ return textResult(
810
+ describe(result, `diffing plan resolve ${params.commentId}`),
811
+ {
812
+ exitCode: result.exitCode,
813
+ stderr: result.stderr.trim() || undefined,
814
+ },
815
+ );
653
816
  },
654
817
  });
655
818
 
656
819
  pi.registerTool({
657
820
  name: "diffing_url",
658
821
  label: "Diffing URL",
659
- description: "Print the base URL of the active diffing review server for this repo, or report that none is running.",
822
+ description:
823
+ "Print the base URL of the active diffing review server for this repo, or report that none is running.",
660
824
  parameters: Type.Object({}),
661
825
  async execute(_id, _params, _signal, _onUpdate, ctx) {
662
826
  const url = await diffingUrl(ctx.cwd);
663
827
  return textResult(
664
- url ? url : "No diffing server running for this repo. Start one with diffing_start_review or `/diffing`.",
828
+ url
829
+ ? url
830
+ : "No diffing server running for this repo. Start one with diffing_start_review or `/diffing`.",
665
831
  { url },
666
832
  );
667
833
  },
@@ -673,9 +839,19 @@ export default function (pi: ExtensionAPI) {
673
839
  description:
674
840
  "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
841
  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." })),
842
+ action: Type.Optional(
843
+ StringEnum(["list", "use", "stop"] as const, {
844
+ description: "Action. Default: list.",
845
+ }),
846
+ ),
847
+ value: Type.Optional(
848
+ Type.String({
849
+ description: "Session id prefix (for use/stop), or 'active'/'all'.",
850
+ }),
851
+ ),
852
+ json: Type.Optional(
853
+ Type.Boolean({ description: "Emit JSON for list. Default: false." }),
854
+ ),
679
855
  }),
680
856
  async execute(_id, params, _signal, _onUpdate, ctx) {
681
857
  const action = params.action ?? "list";
@@ -700,7 +876,11 @@ export default function (pi: ExtensionAPI) {
700
876
  description:
701
877
  "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
878
  parameters: Type.Object({
703
- json: Type.Optional(Type.Boolean({ description: "Emit raw JSON instead of human-readable output." })),
879
+ json: Type.Optional(
880
+ Type.Boolean({
881
+ description: "Emit raw JSON instead of human-readable output.",
882
+ }),
883
+ ),
704
884
  }),
705
885
  async execute(_id, params, _signal, _onUpdate, ctx) {
706
886
  const args = ["gh", "overview"];
@@ -719,15 +899,29 @@ export default function (pi: ExtensionAPI) {
719
899
  description:
720
900
  "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
901
  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)." })),
902
+ args: Type.Array(
903
+ Type.String({
904
+ description: "Subcommand and arguments, e.g. ['inspect','summary'].",
905
+ }),
906
+ ),
907
+ stdin: Type.Optional(
908
+ Type.String({
909
+ description:
910
+ "Optional text to pipe to the command's stdin (e.g. reply bodies).",
911
+ }),
912
+ ),
724
913
  }),
725
914
  async execute(_id, params, signal, _onUpdate, ctx) {
726
915
  const args = params.args;
727
916
  if (!Array.isArray(args) || args.length === 0) {
728
- return textResult("Provide at least one subcommand argument.", { ran: false });
917
+ return textResult("Provide at least one subcommand argument.", {
918
+ ran: false,
919
+ });
729
920
  }
730
- const result = await runDiffing(args, ctx.cwd, { signal, stdin: params.stdin });
921
+ const result = await runDiffing(args, ctx.cwd, {
922
+ signal,
923
+ stdin: params.stdin,
924
+ });
731
925
  return textResult(describe(result, `diffing ${args.join(" ")}`), {
732
926
  exitCode: result.exitCode,
733
927
  stderr: result.stderr.trim() || undefined,
@@ -747,7 +941,10 @@ export default function (pi: ExtensionAPI) {
747
941
  await refreshStatus(ctx);
748
942
  ctx.ui.notify(`diffing review: ${url}`, "info");
749
943
  } catch (error) {
750
- ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
944
+ ctx.ui.notify(
945
+ error instanceof Error ? error.message : String(error),
946
+ "error",
947
+ );
751
948
  }
752
949
  },
753
950
  });
@@ -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
+ }