@vandeepunk/pi-coding-agent 0.0.6 → 1.0.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 (459) hide show
  1. package/CHANGELOG.md +814 -16
  2. package/README.md +66 -27
  3. package/dist/bun/cli.d.ts +3 -0
  4. package/dist/bun/cli.d.ts.map +1 -0
  5. package/dist/bun/cli.js +7 -0
  6. package/dist/bun/cli.js.map +1 -0
  7. package/dist/bun/register-bedrock.d.ts +2 -0
  8. package/dist/bun/register-bedrock.d.ts.map +1 -0
  9. package/dist/bun/register-bedrock.js +4 -0
  10. package/dist/bun/register-bedrock.js.map +1 -0
  11. package/dist/cli/args.d.ts +9 -4
  12. package/dist/cli/args.d.ts.map +1 -1
  13. package/dist/cli/args.js +61 -22
  14. package/dist/cli/args.js.map +1 -1
  15. package/dist/cli/file-processor.d.ts.map +1 -1
  16. package/dist/cli/file-processor.js +4 -0
  17. package/dist/cli/file-processor.js.map +1 -1
  18. package/dist/cli/initial-message.d.ts +18 -0
  19. package/dist/cli/initial-message.d.ts.map +1 -0
  20. package/dist/cli/initial-message.js +22 -0
  21. package/dist/cli/initial-message.js.map +1 -0
  22. package/dist/cli/session-picker.d.ts.map +1 -1
  23. package/dist/cli/session-picker.js +2 -1
  24. package/dist/cli/session-picker.js.map +1 -1
  25. package/dist/cli.d.ts.map +1 -1
  26. package/dist/cli.js +3 -0
  27. package/dist/cli.js.map +1 -1
  28. package/dist/config.d.ts +1 -1
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +2 -2
  31. package/dist/config.js.map +1 -1
  32. package/dist/core/agent-session-runtime.d.ts +83 -0
  33. package/dist/core/agent-session-runtime.d.ts.map +1 -0
  34. package/dist/core/agent-session-runtime.js +236 -0
  35. package/dist/core/agent-session-runtime.js.map +1 -0
  36. package/dist/core/agent-session-services.d.ts +86 -0
  37. package/dist/core/agent-session-services.d.ts.map +1 -0
  38. package/dist/core/agent-session-services.js +116 -0
  39. package/dist/core/agent-session-services.js.map +1 -0
  40. package/dist/core/agent-session.d.ts +63 -49
  41. package/dist/core/agent-session.d.ts.map +1 -1
  42. package/dist/core/agent-session.js +599 -370
  43. package/dist/core/agent-session.js.map +1 -1
  44. package/dist/core/auth-storage.d.ts +38 -8
  45. package/dist/core/auth-storage.d.ts.map +1 -1
  46. package/dist/core/auth-storage.js +220 -96
  47. package/dist/core/auth-storage.js.map +1 -1
  48. package/dist/core/bash-executor.d.ts +6 -7
  49. package/dist/core/bash-executor.d.ts.map +1 -1
  50. package/dist/core/bash-executor.js +27 -114
  51. package/dist/core/bash-executor.js.map +1 -1
  52. package/dist/core/compaction/branch-summarization.d.ts +2 -0
  53. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  54. package/dist/core/compaction/branch-summarization.js +3 -2
  55. package/dist/core/compaction/branch-summarization.js.map +1 -1
  56. package/dist/core/compaction/compaction.d.ts +3 -3
  57. package/dist/core/compaction/compaction.d.ts.map +1 -1
  58. package/dist/core/compaction/compaction.js +31 -25
  59. package/dist/core/compaction/compaction.js.map +1 -1
  60. package/dist/core/compaction/utils.d.ts +3 -0
  61. package/dist/core/compaction/utils.d.ts.map +1 -1
  62. package/dist/core/compaction/utils.js +16 -1
  63. package/dist/core/compaction/utils.js.map +1 -1
  64. package/dist/core/exec.d.ts.map +1 -1
  65. package/dist/core/exec.js +7 -3
  66. package/dist/core/exec.js.map +1 -1
  67. package/dist/core/export-html/index.d.ts +7 -4
  68. package/dist/core/export-html/index.d.ts.map +1 -1
  69. package/dist/core/export-html/index.js +10 -8
  70. package/dist/core/export-html/index.js.map +1 -1
  71. package/dist/core/export-html/template.css +43 -13
  72. package/dist/core/export-html/template.html +1 -0
  73. package/dist/core/export-html/template.js +118 -14
  74. package/dist/core/export-html/tool-renderer.d.ts +9 -4
  75. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  76. package/dist/core/export-html/tool-renderer.js +48 -10
  77. package/dist/core/export-html/tool-renderer.js.map +1 -1
  78. package/dist/core/extensions/index.d.ts +5 -4
  79. package/dist/core/extensions/index.d.ts.map +1 -1
  80. package/dist/core/extensions/index.js +2 -2
  81. package/dist/core/extensions/index.js.map +1 -1
  82. package/dist/core/extensions/loader.d.ts.map +1 -1
  83. package/dist/core/extensions/loader.js +49 -13
  84. package/dist/core/extensions/loader.js.map +1 -1
  85. package/dist/core/extensions/runner.d.ts +13 -11
  86. package/dist/core/extensions/runner.d.ts.map +1 -1
  87. package/dist/core/extensions/runner.js +139 -64
  88. package/dist/core/extensions/runner.js.map +1 -1
  89. package/dist/core/extensions/types.d.ts +174 -34
  90. package/dist/core/extensions/types.d.ts.map +1 -1
  91. package/dist/core/extensions/types.js +10 -0
  92. package/dist/core/extensions/types.js.map +1 -1
  93. package/dist/core/extensions/wrapper.d.ts +4 -11
  94. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  95. package/dist/core/extensions/wrapper.js +6 -86
  96. package/dist/core/extensions/wrapper.js.map +1 -1
  97. package/dist/core/footer-data-provider.d.ts +18 -2
  98. package/dist/core/footer-data-provider.d.ts.map +1 -1
  99. package/dist/core/footer-data-provider.js +220 -40
  100. package/dist/core/footer-data-provider.js.map +1 -1
  101. package/dist/core/index.d.ts +4 -1
  102. package/dist/core/index.d.ts.map +1 -1
  103. package/dist/core/index.js +4 -1
  104. package/dist/core/index.js.map +1 -1
  105. package/dist/core/keybindings.d.ts +283 -50
  106. package/dist/core/keybindings.d.ts.map +1 -1
  107. package/dist/core/keybindings.js +221 -134
  108. package/dist/core/keybindings.js.map +1 -1
  109. package/dist/core/model-registry.d.ts +33 -3
  110. package/dist/core/model-registry.d.ts.map +1 -1
  111. package/dist/core/model-registry.js +165 -97
  112. package/dist/core/model-registry.js.map +1 -1
  113. package/dist/core/model-resolver.d.ts +35 -1
  114. package/dist/core/model-resolver.d.ts.map +1 -1
  115. package/dist/core/model-resolver.js +205 -32
  116. package/dist/core/model-resolver.js.map +1 -1
  117. package/dist/core/output-guard.d.ts +6 -0
  118. package/dist/core/output-guard.d.ts.map +1 -0
  119. package/dist/core/output-guard.js +59 -0
  120. package/dist/core/output-guard.js.map +1 -0
  121. package/dist/core/package-manager.d.ts +43 -2
  122. package/dist/core/package-manager.d.ts.map +1 -1
  123. package/dist/core/package-manager.js +541 -102
  124. package/dist/core/package-manager.js.map +1 -1
  125. package/dist/core/prompt-templates.d.ts +5 -4
  126. package/dist/core/prompt-templates.d.ts.map +1 -1
  127. package/dist/core/prompt-templates.js +35 -37
  128. package/dist/core/prompt-templates.js.map +1 -1
  129. package/dist/core/resolve-config-value.d.ts +6 -0
  130. package/dist/core/resolve-config-value.d.ts.map +1 -1
  131. package/dist/core/resolve-config-value.js +75 -8
  132. package/dist/core/resolve-config-value.js.map +1 -1
  133. package/dist/core/resource-loader.d.ts +6 -5
  134. package/dist/core/resource-loader.d.ts.map +1 -1
  135. package/dist/core/resource-loader.js +166 -119
  136. package/dist/core/resource-loader.js.map +1 -1
  137. package/dist/core/sdk.d.ts +11 -8
  138. package/dist/core/sdk.d.ts.map +1 -1
  139. package/dist/core/sdk.js +31 -29
  140. package/dist/core/sdk.js.map +1 -1
  141. package/dist/core/session-cwd.d.ts +19 -0
  142. package/dist/core/session-cwd.d.ts.map +1 -0
  143. package/dist/core/session-cwd.js +38 -0
  144. package/dist/core/session-cwd.js.map +1 -0
  145. package/dist/core/session-manager.d.ts +11 -1
  146. package/dist/core/session-manager.d.ts.map +1 -1
  147. package/dist/core/session-manager.js +39 -25
  148. package/dist/core/session-manager.js.map +1 -1
  149. package/dist/core/settings-manager.d.ts +60 -10
  150. package/dist/core/settings-manager.d.ts.map +1 -1
  151. package/dist/core/settings-manager.js +291 -140
  152. package/dist/core/settings-manager.js.map +1 -1
  153. package/dist/core/skills.d.ts +5 -3
  154. package/dist/core/skills.d.ts.map +1 -1
  155. package/dist/core/skills.js +54 -9
  156. package/dist/core/skills.js.map +1 -1
  157. package/dist/core/slash-commands.d.ts +2 -3
  158. package/dist/core/slash-commands.d.ts.map +1 -1
  159. package/dist/core/slash-commands.js +3 -2
  160. package/dist/core/slash-commands.js.map +1 -1
  161. package/dist/core/source-info.d.ts +18 -0
  162. package/dist/core/source-info.d.ts.map +1 -0
  163. package/dist/core/source-info.js +19 -0
  164. package/dist/core/source-info.js.map +1 -0
  165. package/dist/core/system-prompt.d.ts +4 -0
  166. package/dist/core/system-prompt.d.ts.map +1 -1
  167. package/dist/core/system-prompt.js +31 -52
  168. package/dist/core/system-prompt.js.map +1 -1
  169. package/dist/core/timings.d.ts +1 -0
  170. package/dist/core/timings.d.ts.map +1 -1
  171. package/dist/core/timings.js +6 -0
  172. package/dist/core/timings.js.map +1 -1
  173. package/dist/core/tools/bash.d.ts +24 -6
  174. package/dist/core/tools/bash.d.ts.map +1 -1
  175. package/dist/core/tools/bash.js +225 -115
  176. package/dist/core/tools/bash.js.map +1 -1
  177. package/dist/core/tools/edit-diff.d.ts +23 -1
  178. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  179. package/dist/core/tools/edit-diff.js +151 -57
  180. package/dist/core/tools/edit-diff.js.map +1 -1
  181. package/dist/core/tools/edit.d.ts +20 -6
  182. package/dist/core/tools/edit.d.ts.map +1 -1
  183. package/dist/core/tools/edit.js +111 -61
  184. package/dist/core/tools/edit.js.map +1 -1
  185. package/dist/core/tools/file-mutation-queue.d.ts +6 -0
  186. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
  187. package/dist/core/tools/file-mutation-queue.js +37 -0
  188. package/dist/core/tools/file-mutation-queue.js.map +1 -0
  189. package/dist/core/tools/find.d.ts +11 -4
  190. package/dist/core/tools/find.d.ts.map +1 -1
  191. package/dist/core/tools/find.js +82 -30
  192. package/dist/core/tools/find.js.map +1 -1
  193. package/dist/core/tools/grep.d.ts +15 -4
  194. package/dist/core/tools/grep.d.ts.map +1 -1
  195. package/dist/core/tools/grep.js +83 -29
  196. package/dist/core/tools/grep.js.map +1 -1
  197. package/dist/core/tools/index.d.ts +63 -21
  198. package/dist/core/tools/index.d.ts.map +1 -1
  199. package/dist/core/tools/index.js +51 -26
  200. package/dist/core/tools/index.js.map +1 -1
  201. package/dist/core/tools/ls.d.ts +9 -3
  202. package/dist/core/tools/ls.d.ts.map +1 -1
  203. package/dist/core/tools/ls.js +67 -13
  204. package/dist/core/tools/ls.js.map +1 -1
  205. package/dist/core/tools/read.d.ts +10 -3
  206. package/dist/core/tools/read.d.ts.map +1 -1
  207. package/dist/core/tools/read.js +110 -51
  208. package/dist/core/tools/read.js.map +1 -1
  209. package/dist/core/tools/render-utils.d.ts +21 -0
  210. package/dist/core/tools/render-utils.d.ts.map +1 -0
  211. package/dist/core/tools/render-utils.js +49 -0
  212. package/dist/core/tools/render-utils.js.map +1 -0
  213. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  214. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  215. package/dist/core/tools/tool-definition-wrapper.js +32 -0
  216. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  217. package/dist/core/tools/write.d.ts +9 -3
  218. package/dist/core/tools/write.d.ts.map +1 -1
  219. package/dist/core/tools/write.js +168 -30
  220. package/dist/core/tools/write.js.map +1 -1
  221. package/dist/index.d.ts +7 -6
  222. package/dist/index.d.ts.map +1 -1
  223. package/dist/index.js +7 -6
  224. package/dist/index.js.map +1 -1
  225. package/dist/main.d.ts.map +1 -1
  226. package/dist/main.js +354 -379
  227. package/dist/main.js.map +1 -1
  228. package/dist/migrations.d.ts.map +1 -1
  229. package/dist/migrations.js +31 -11
  230. package/dist/migrations.js.map +1 -1
  231. package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  232. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  233. package/dist/modes/interactive/components/assistant-message.js +14 -3
  234. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  235. package/dist/modes/interactive/components/bash-execution.d.ts +0 -1
  236. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  237. package/dist/modes/interactive/components/bash-execution.js +22 -9
  238. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  239. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  240. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  241. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  242. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  243. package/dist/modes/interactive/components/branch-summary-message.js +2 -2
  244. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  245. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  246. package/dist/modes/interactive/components/compaction-summary-message.js +2 -2
  247. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  248. package/dist/modes/interactive/components/config-selector.d.ts +1 -1
  249. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  250. package/dist/modes/interactive/components/config-selector.js +14 -14
  251. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  252. package/dist/modes/interactive/components/custom-editor.d.ts +3 -3
  253. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  254. package/dist/modes/interactive/components/custom-editor.js +6 -6
  255. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  256. package/dist/modes/interactive/components/extension-editor.d.ts +5 -2
  257. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  258. package/dist/modes/interactive/components/extension-editor.js +18 -9
  259. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  260. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  261. package/dist/modes/interactive/components/extension-input.js +5 -5
  262. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  263. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  264. package/dist/modes/interactive/components/extension-selector.js +8 -8
  265. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  266. package/dist/modes/interactive/components/footer.d.ts +1 -0
  267. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  268. package/dist/modes/interactive/components/footer.js +21 -40
  269. package/dist/modes/interactive/components/footer.js.map +1 -1
  270. package/dist/modes/interactive/components/index.d.ts +1 -1
  271. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  272. package/dist/modes/interactive/components/index.js +1 -1
  273. package/dist/modes/interactive/components/index.js.map +1 -1
  274. package/dist/modes/interactive/components/keybinding-hints.d.ts +3 -36
  275. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  276. package/dist/modes/interactive/components/keybinding-hints.js +5 -44
  277. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  278. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  279. package/dist/modes/interactive/components/login-dialog.js +7 -7
  280. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  281. package/dist/modes/interactive/components/model-selector.d.ts +1 -1
  282. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  283. package/dist/modes/interactive/components/model-selector.js +13 -9
  284. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  285. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  286. package/dist/modes/interactive/components/oauth-selector.js +7 -7
  287. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  288. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  289. package/dist/modes/interactive/components/scoped-models-selector.js +4 -4
  290. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  291. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  292. package/dist/modes/interactive/components/session-selector.js +33 -36
  293. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  294. package/dist/modes/interactive/components/settings-selector.d.ts +5 -0
  295. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  296. package/dist/modes/interactive/components/settings-selector.js +25 -1
  297. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  298. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  299. package/dist/modes/interactive/components/show-images-selector.js +5 -1
  300. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  301. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  302. package/dist/modes/interactive/components/skill-invocation-message.js +2 -2
  303. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  304. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  305. package/dist/modes/interactive/components/theme-selector.js +5 -1
  306. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  307. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  308. package/dist/modes/interactive/components/thinking-selector.js +5 -1
  309. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  310. package/dist/modes/interactive/components/tool-execution.d.ts +17 -29
  311. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  312. package/dist/modes/interactive/components/tool-execution.js +139 -501
  313. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  314. package/dist/modes/interactive/components/tree-selector.d.ts +25 -4
  315. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  316. package/dist/modes/interactive/components/tree-selector.js +184 -34
  317. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  318. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  319. package/dist/modes/interactive/components/user-message-selector.js +6 -6
  320. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  321. package/dist/modes/interactive/components/user-message.d.ts +1 -0
  322. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  323. package/dist/modes/interactive/components/user-message.js +12 -0
  324. package/dist/modes/interactive/components/user-message.js.map +1 -1
  325. package/dist/modes/interactive/interactive-mode.d.ts +25 -17
  326. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  327. package/dist/modes/interactive/interactive-mode.js +669 -385
  328. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  329. package/dist/modes/interactive/theme/theme.d.ts +3 -0
  330. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  331. package/dist/modes/interactive/theme/theme.js +83 -48
  332. package/dist/modes/interactive/theme/theme.js.map +1 -1
  333. package/dist/modes/print-mode.d.ts +2 -2
  334. package/dist/modes/print-mode.d.ts.map +1 -1
  335. package/dist/modes/print-mode.js +90 -79
  336. package/dist/modes/print-mode.js.map +1 -1
  337. package/dist/modes/rpc/jsonl.d.ts +17 -0
  338. package/dist/modes/rpc/jsonl.d.ts.map +1 -0
  339. package/dist/modes/rpc/jsonl.js +49 -0
  340. package/dist/modes/rpc/jsonl.js.map +1 -0
  341. package/dist/modes/rpc/rpc-client.d.ts +1 -1
  342. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  343. package/dist/modes/rpc/rpc-client.js +8 -11
  344. package/dist/modes/rpc/rpc-client.js.map +1 -1
  345. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  346. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  347. package/dist/modes/rpc/rpc-mode.js +130 -87
  348. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  349. package/dist/modes/rpc/rpc-types.d.ts +3 -4
  350. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  351. package/dist/modes/rpc/rpc-types.js.map +1 -1
  352. package/dist/package-manager-cli.d.ts +4 -0
  353. package/dist/package-manager-cli.d.ts.map +1 -0
  354. package/dist/package-manager-cli.js +234 -0
  355. package/dist/package-manager-cli.js.map +1 -0
  356. package/dist/utils/child-process.d.ts +11 -0
  357. package/dist/utils/child-process.d.ts.map +1 -0
  358. package/dist/utils/child-process.js +78 -0
  359. package/dist/utils/child-process.js.map +1 -0
  360. package/dist/utils/clipboard-image.d.ts.map +1 -1
  361. package/dist/utils/clipboard-image.js +94 -11
  362. package/dist/utils/clipboard-image.js.map +1 -1
  363. package/dist/utils/clipboard-native.d.ts +1 -0
  364. package/dist/utils/clipboard-native.d.ts.map +1 -1
  365. package/dist/utils/clipboard-native.js.map +1 -1
  366. package/dist/utils/clipboard.d.ts +1 -1
  367. package/dist/utils/clipboard.d.ts.map +1 -1
  368. package/dist/utils/clipboard.js +27 -16
  369. package/dist/utils/clipboard.js.map +1 -1
  370. package/dist/utils/exif-orientation.d.ts +5 -0
  371. package/dist/utils/exif-orientation.d.ts.map +1 -0
  372. package/dist/utils/exif-orientation.js +158 -0
  373. package/dist/utils/exif-orientation.js.map +1 -0
  374. package/dist/utils/git.d.ts +5 -1
  375. package/dist/utils/git.d.ts.map +1 -1
  376. package/dist/utils/git.js +14 -3
  377. package/dist/utils/git.js.map +1 -1
  378. package/dist/utils/image-convert.d.ts.map +1 -1
  379. package/dist/utils/image-convert.js +5 -1
  380. package/dist/utils/image-convert.js.map +1 -1
  381. package/dist/utils/image-resize.d.ts +5 -5
  382. package/dist/utils/image-resize.d.ts.map +1 -1
  383. package/dist/utils/image-resize.js +51 -95
  384. package/dist/utils/image-resize.js.map +1 -1
  385. package/dist/utils/paths.d.ts +7 -0
  386. package/dist/utils/paths.d.ts.map +1 -0
  387. package/dist/utils/paths.js +19 -0
  388. package/dist/utils/paths.js.map +1 -0
  389. package/dist/utils/tools-manager.d.ts.map +1 -1
  390. package/dist/utils/tools-manager.js +67 -22
  391. package/dist/utils/tools-manager.js.map +1 -1
  392. package/docs/compaction.md +6 -2
  393. package/docs/custom-provider.md +57 -9
  394. package/docs/development.md +3 -1
  395. package/docs/extensions.md +437 -67
  396. package/docs/json.md +5 -2
  397. package/docs/keybindings.md +108 -107
  398. package/docs/models.md +50 -2
  399. package/docs/packages.md +17 -10
  400. package/docs/prompt-templates.md +6 -6
  401. package/docs/providers.md +10 -1
  402. package/docs/rpc.md +78 -18
  403. package/docs/sdk.md +261 -96
  404. package/docs/settings.md +28 -3
  405. package/docs/skills.md +9 -4
  406. package/docs/terminal-setup.md +39 -3
  407. package/docs/tmux.md +61 -0
  408. package/docs/tree.md +15 -3
  409. package/docs/tui.md +2 -2
  410. package/examples/extensions/README.md +3 -0
  411. package/examples/extensions/antigravity-image-gen.ts +12 -7
  412. package/examples/extensions/built-in-tool-renderer.ts +246 -0
  413. package/examples/extensions/commands.ts +3 -3
  414. package/examples/extensions/custom-compaction.ts +17 -4
  415. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  416. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  417. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  418. package/examples/extensions/custom-provider-gitlab-duo/test.ts +2 -2
  419. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  420. package/examples/extensions/doom-overlay/doom/build.sh +2 -2
  421. package/examples/extensions/dynamic-tools.ts +74 -0
  422. package/examples/extensions/handoff.ts +5 -2
  423. package/examples/extensions/hello.ts +18 -17
  424. package/examples/extensions/hidden-thinking-label.ts +53 -0
  425. package/examples/extensions/minimal-mode.ts +14 -14
  426. package/examples/extensions/overlay-qa-tests.ts +468 -1
  427. package/examples/extensions/preset.ts +2 -3
  428. package/examples/extensions/provider-payload.ts +14 -0
  429. package/examples/extensions/qna.ts +5 -2
  430. package/examples/extensions/question.ts +2 -2
  431. package/examples/extensions/questionnaire.ts +2 -2
  432. package/examples/extensions/rpc-demo.ts +3 -9
  433. package/examples/extensions/sandbox/index.ts +6 -3
  434. package/examples/extensions/status-line.ts +0 -8
  435. package/examples/extensions/subagent/README.md +4 -4
  436. package/examples/extensions/subagent/agents.ts +2 -3
  437. package/examples/extensions/subagent/index.ts +30 -8
  438. package/examples/extensions/summarize.ts +15 -4
  439. package/examples/extensions/todo.ts +2 -4
  440. package/examples/extensions/tool-override.ts +10 -9
  441. package/examples/extensions/tools.ts +0 -5
  442. package/examples/extensions/trigger-compact.ts +11 -1
  443. package/examples/extensions/truncated-tool.ts +8 -5
  444. package/examples/extensions/widget-placement.ts +4 -12
  445. package/examples/extensions/with-deps/index.ts +1 -5
  446. package/examples/extensions/with-deps/package-lock.json +2 -2
  447. package/examples/extensions/with-deps/package.json +1 -1
  448. package/examples/sdk/02-custom-model.ts +2 -2
  449. package/examples/sdk/04-skills.ts +8 -2
  450. package/examples/sdk/08-prompt-templates.ts +4 -3
  451. package/examples/sdk/09-api-keys-and-oauth.ts +5 -5
  452. package/examples/sdk/10-settings.ts +13 -0
  453. package/examples/sdk/12-full-control.ts +2 -3
  454. package/examples/sdk/13-session-runtime.ts +67 -0
  455. package/examples/sdk/README.md +10 -7
  456. package/package.json +98 -94
  457. /package/examples/extensions/subagent/{commands → prompts}/implement-and-review.md +0 -0
  458. /package/examples/extensions/subagent/{commands → prompts}/implement.md +0 -0
  459. /package/examples/extensions/subagent/{commands → prompts}/scout-and-plan.md +0 -0
@@ -37,6 +37,7 @@ See [examples/extensions/](../examples/extensions/) for working implementations.
37
37
  - [Extension Styles](#extension-styles)
38
38
  - [Events](#events)
39
39
  - [Lifecycle Overview](#lifecycle-overview)
40
+ - [Resource Events](#resource-events)
40
41
  - [Session Events](#session-events)
41
42
  - [Agent Events](#agent-events)
42
43
  - [Tool Events](#tool-events)
@@ -227,7 +228,8 @@ Run `npm install` in the extension directory, then imports from `node_modules/`
227
228
  ```
228
229
  pi starts
229
230
 
230
- └─► session_start
231
+ ├─► session_start { reason: "startup" }
232
+ └─► resources_discover { reason: "startup" }
231
233
 
232
234
 
233
235
  user sends prompt ─────────────────────────────────────────┐
@@ -237,16 +239,20 @@ user sends prompt ────────────────────
237
239
  ├─► (skill/template expansion if not handled) │
238
240
  ├─► before_agent_start (can inject message, modify system prompt)
239
241
  ├─► agent_start │
242
+ ├─► message_start / message_update / message_end │
240
243
  │ │
241
244
  │ ┌─── turn (repeats while LLM calls tools) ───┐ │
242
245
  │ │ │ │
243
246
  │ ├─► turn_start │ │
244
247
  │ ├─► context (can modify messages) │ │
248
+ │ ├─► before_provider_request (can inspect or replace payload)
245
249
  │ │ │ │
246
250
  │ │ LLM responds, may call tools: │ │
251
+ │ │ ├─► tool_execution_start │ │
247
252
  │ │ ├─► tool_call (can block) │ │
248
- │ │ │ tool executes │ │
249
- │ │ └─► tool_result (can modify) │ │
253
+ │ │ ├─► tool_execution_update │ │
254
+ │ │ ├─► tool_result (can modify) │ │
255
+ │ │ └─► tool_execution_end │ │
250
256
  │ │ │ │
251
257
  │ └─► turn_end │ │
252
258
  │ │
@@ -256,11 +262,15 @@ user sends another prompt ◄─────────────────
256
262
 
257
263
  /new (new session) or /resume (switch session)
258
264
  ├─► session_before_switch (can cancel)
259
- └─► session_switch
265
+ ├─► session_shutdown
266
+ ├─► session_start { reason: "new" | "resume", previousSessionFile? }
267
+ └─► resources_discover { reason: "startup" }
260
268
 
261
269
  /fork
262
270
  ├─► session_before_fork (can cancel)
263
- └─► session_fork
271
+ ├─► session_shutdown
272
+ ├─► session_start { reason: "fork", previousSessionFile }
273
+ └─► resources_discover { reason: "startup" }
264
274
 
265
275
  /compact or auto-compaction
266
276
  ├─► session_before_compact (can cancel or customize)
@@ -277,23 +287,44 @@ exit (Ctrl+C, Ctrl+D)
277
287
  └─► session_shutdown
278
288
  ```
279
289
 
290
+ ### Resource Events
291
+
292
+ #### resources_discover
293
+
294
+ Fired after `session_start` so extensions can contribute additional skill, prompt, and theme paths.
295
+ The startup path uses `reason: "startup"`. Reload uses `reason: "reload"`.
296
+
297
+ ```typescript
298
+ pi.on("resources_discover", async (event, _ctx) => {
299
+ // event.cwd - current working directory
300
+ // event.reason - "startup" | "reload"
301
+ return {
302
+ skillPaths: ["/path/to/skills"],
303
+ promptPaths: ["/path/to/prompts"],
304
+ themePaths: ["/path/to/themes"],
305
+ };
306
+ });
307
+ ```
308
+
280
309
  ### Session Events
281
310
 
282
311
  See [session.md](session.md) for session storage internals and the SessionManager API.
283
312
 
284
313
  #### session_start
285
314
 
286
- Fired on initial session load.
315
+ Fired when a session is started, loaded, or reloaded.
287
316
 
288
317
  ```typescript
289
- pi.on("session_start", async (_event, ctx) => {
318
+ pi.on("session_start", async (event, ctx) => {
319
+ // event.reason - "startup" | "reload" | "new" | "resume" | "fork"
320
+ // event.previousSessionFile - present for "new", "resume", and "fork"
290
321
  ctx.ui.notify(`Session: ${ctx.sessionManager.getSessionFile() ?? "ephemeral"}`, "info");
291
322
  });
292
323
  ```
293
324
 
294
- #### session_before_switch / session_switch
325
+ #### session_before_switch
295
326
 
296
- Fired when starting a new session (`/new`) or switching sessions (`/resume`).
327
+ Fired before starting a new session (`/new`) or switching sessions (`/resume`).
297
328
 
298
329
  ```typescript
299
330
  pi.on("session_before_switch", async (event, ctx) => {
@@ -305,14 +336,12 @@ pi.on("session_before_switch", async (event, ctx) => {
305
336
  if (!ok) return { cancel: true };
306
337
  }
307
338
  });
308
-
309
- pi.on("session_switch", async (event, ctx) => {
310
- // event.reason - "new" or "resume"
311
- // event.previousSessionFile - session we came from
312
- });
313
339
  ```
314
340
 
315
- #### session_before_fork / session_fork
341
+ After a successful switch or new-session action, pi emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "new" | "resume"` and `previousSessionFile`.
342
+ Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
343
+
344
+ #### session_before_fork
316
345
 
317
346
  Fired when forking via `/fork`.
318
347
 
@@ -323,12 +352,11 @@ pi.on("session_before_fork", async (event, ctx) => {
323
352
  // OR
324
353
  return { skipConversationRestore: true }; // Fork but don't rewind messages
325
354
  });
326
-
327
- pi.on("session_fork", async (event, ctx) => {
328
- // event.previousSessionFile - previous session file
329
- });
330
355
  ```
331
356
 
357
+ After a successful fork, pi emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "fork"` and `previousSessionFile`.
358
+ Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
359
+
332
360
  #### session_before_compact / session_compact
333
361
 
334
362
  Fired on compaction. See [compaction.md](compaction.md) for details.
@@ -434,6 +462,51 @@ pi.on("turn_end", async (event, ctx) => {
434
462
  });
435
463
  ```
436
464
 
465
+ #### message_start / message_update / message_end
466
+
467
+ Fired for message lifecycle updates.
468
+
469
+ - `message_start` and `message_end` fire for user, assistant, and toolResult messages.
470
+ - `message_update` fires for assistant streaming updates.
471
+
472
+ ```typescript
473
+ pi.on("message_start", async (event, ctx) => {
474
+ // event.message
475
+ });
476
+
477
+ pi.on("message_update", async (event, ctx) => {
478
+ // event.message
479
+ // event.assistantMessageEvent (token-by-token stream event)
480
+ });
481
+
482
+ pi.on("message_end", async (event, ctx) => {
483
+ // event.message
484
+ });
485
+ ```
486
+
487
+ #### tool_execution_start / tool_execution_update / tool_execution_end
488
+
489
+ Fired for tool execution lifecycle updates.
490
+
491
+ In parallel tool mode:
492
+ - `tool_execution_start` is emitted in assistant source order during the preflight phase
493
+ - `tool_execution_update` events may interleave across tools
494
+ - `tool_execution_end` is emitted in assistant source order, matching final tool result message order
495
+
496
+ ```typescript
497
+ pi.on("tool_execution_start", async (event, ctx) => {
498
+ // event.toolCallId, event.toolName, event.args
499
+ });
500
+
501
+ pi.on("tool_execution_update", async (event, ctx) => {
502
+ // event.toolCallId, event.toolName, event.args, event.partialResult
503
+ });
504
+
505
+ pi.on("tool_execution_end", async (event, ctx) => {
506
+ // event.toolCallId, event.toolName, event.result, event.isError
507
+ });
508
+ ```
509
+
437
510
  #### context
438
511
 
439
512
  Fired before each LLM call. Modify messages non-destructively. See [session.md](session.md) for message types.
@@ -446,6 +519,21 @@ pi.on("context", async (event, ctx) => {
446
519
  });
447
520
  ```
448
521
 
522
+ #### before_provider_request
523
+
524
+ Fired after the provider-specific payload is built, right before the request is sent. Handlers run in extension load order. Returning `undefined` keeps the payload unchanged. Returning any other value replaces the payload for later handlers and for the actual request.
525
+
526
+ ```typescript
527
+ pi.on("before_provider_request", (event, ctx) => {
528
+ console.log(JSON.stringify(event.payload, null, 2));
529
+
530
+ // Optional: replace payload
531
+ // return { ...event.payload, temperature: 0 };
532
+ });
533
+ ```
534
+
535
+ This is mainly useful for debugging provider serialization and cache behavior.
536
+
449
537
  ### Model Events
450
538
 
451
539
  #### model_select
@@ -473,7 +561,19 @@ Use this to update UI elements (status bars, footers) or perform model-specific
473
561
 
474
562
  #### tool_call
475
563
 
476
- Fired before tool executes. **Can block.** Use `isToolCallEventType` to narrow and get typed inputs.
564
+ Fired after `tool_execution_start`, before the tool executes. **Can block.** Use `isToolCallEventType` to narrow and get typed inputs.
565
+
566
+ Before `tool_call` runs, pi waits for previously emitted Agent events to finish draining through `AgentSession`. This means `ctx.sessionManager` is up to date through the current assistant tool-calling message.
567
+
568
+ In the default parallel tool execution mode, sibling tool calls from the same assistant message are preflighted sequentially, then executed concurrently. `tool_call` is not guaranteed to see sibling tool results from that same assistant message in `ctx.sessionManager`.
569
+
570
+ `event.input` is mutable. Mutate it in place to patch tool arguments before execution.
571
+
572
+ Behavior guarantees:
573
+ - Mutations to `event.input` affect the actual tool execution
574
+ - Later `tool_call` handlers see mutations made by earlier handlers
575
+ - No re-validation is performed after your mutation
576
+ - Return values from `tool_call` only control blocking via `{ block: true, reason?: string }`
477
577
 
478
578
  ```typescript
479
579
  import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
@@ -481,11 +581,13 @@ import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
481
581
  pi.on("tool_call", async (event, ctx) => {
482
582
  // event.toolName - "bash", "read", "write", "edit", etc.
483
583
  // event.toolCallId
484
- // event.input - tool parameters
584
+ // event.input - tool parameters (mutable)
485
585
 
486
586
  // Built-in tools: no type params needed
487
587
  if (isToolCallEventType("bash", event)) {
488
588
  // event.input is { command: string; timeout?: number }
589
+ event.input.command = `source ~/.profile\n${event.input.command}`;
590
+
489
591
  if (event.input.command.includes("rm -rf")) {
490
592
  return { block: true, reason: "Dangerous command" };
491
593
  }
@@ -522,13 +624,15 @@ pi.on("tool_call", (event) => {
522
624
 
523
625
  #### tool_result
524
626
 
525
- Fired after tool executes. **Can modify result.**
627
+ Fired after tool execution finishes and before `tool_execution_end` plus the final tool result message events are emitted. **Can modify result.**
526
628
 
527
629
  `tool_result` handlers chain like middleware:
528
630
  - Handlers run in extension load order
529
631
  - Each handler sees the latest result after previous handler changes
530
632
  - Handlers can return partial patches (`content`, `details`, or `isError`); omitted fields keep their current values
531
633
 
634
+ Use `ctx.signal` for nested async work inside the handler. This lets Esc cancel model calls, `fetch()`, and other abort-aware operations started by the extension.
635
+
532
636
  ```typescript
533
637
  import { isBashToolResult } from "@mariozechner/pi-coding-agent";
534
638
 
@@ -540,6 +644,12 @@ pi.on("tool_result", async (event, ctx) => {
540
644
  // event.details is typed as BashToolDetails
541
645
  }
542
646
 
647
+ const response = await fetch("https://example.com/summarize", {
648
+ method: "POST",
649
+ body: JSON.stringify({ content: event.content }),
650
+ signal: ctx.signal,
651
+ });
652
+
543
653
  // Modify result:
544
654
  return { content: [...], details: {...}, isError: false };
545
655
  });
@@ -552,6 +662,8 @@ pi.on("tool_result", async (event, ctx) => {
552
662
  Fired when user executes `!` or `!!` commands. **Can intercept.**
553
663
 
554
664
  ```typescript
665
+ import { createLocalBashOperations } from "@mariozechner/pi-coding-agent";
666
+
555
667
  pi.on("user_bash", (event, ctx) => {
556
668
  // event.command - the bash command
557
669
  // event.excludeFromContext - true if !! prefix
@@ -560,7 +672,17 @@ pi.on("user_bash", (event, ctx) => {
560
672
  // Option 1: Provide custom operations (e.g., SSH)
561
673
  return { operations: remoteBashOps };
562
674
 
563
- // Option 2: Full replacement - return result directly
675
+ // Option 2: Wrap pi's built-in local bash backend
676
+ const local = createLocalBashOperations();
677
+ return {
678
+ operations: {
679
+ exec(command, cwd, options) {
680
+ return local.exec(`source ~/.profile\n${command}`, cwd, options);
681
+ }
682
+ }
683
+ };
684
+
685
+ // Option 3: Full replacement - return result directly
564
686
  return { result: { output: "...", exitCode: 0, cancelled: false, truncated: false } };
565
687
  });
566
688
  ```
@@ -615,7 +737,7 @@ Transforms chain across handlers. See [input-transform.ts](../examples/extension
615
737
 
616
738
  ## ExtensionContext
617
739
 
618
- Every handler receives `ctx: ExtensionContext`:
740
+ All handlers receive `ctx: ExtensionContext`.
619
741
 
620
742
  ### ctx.ui
621
743
 
@@ -633,6 +755,8 @@ Current working directory.
633
755
 
634
756
  Read-only access to session state. See [session.md](session.md) for the full SessionManager API and entry types.
635
757
 
758
+ For `tool_call`, this state is synchronized through the current assistant message before handlers run. In parallel tool execution mode it is still not guaranteed to include sibling tool results from the same assistant message.
759
+
636
760
  ```typescript
637
761
  ctx.sessionManager.getEntries() // All entries
638
762
  ctx.sessionManager.getBranch() // Current branch
@@ -643,6 +767,31 @@ ctx.sessionManager.getLeafId() // Current leaf entry ID
643
767
 
644
768
  Access to models and API keys.
645
769
 
770
+ ### ctx.signal
771
+
772
+ The current agent abort signal, or `undefined` when no agent turn is active.
773
+
774
+ Use this for abort-aware nested work started by extension handlers, for example:
775
+ - `fetch(..., { signal: ctx.signal })`
776
+ - model calls that accept `signal`
777
+ - file or process helpers that accept `AbortSignal`
778
+
779
+ `ctx.signal` is typically defined during active turn events such as `tool_call`, `tool_result`, `message_update`, and `turn_end`.
780
+ It is usually `undefined` in idle or non-turn contexts such as session events, extension commands, and shortcuts fired while pi is idle.
781
+
782
+ ```typescript
783
+ pi.on("tool_result", async (event, ctx) => {
784
+ const response = await fetch("https://example.com/api", {
785
+ method: "POST",
786
+ body: JSON.stringify(event),
787
+ signal: ctx.signal,
788
+ });
789
+
790
+ const data = await response.json();
791
+ return { details: data };
792
+ });
793
+ ```
794
+
646
795
  ### ctx.isIdle() / ctx.abort() / ctx.hasPendingMessages()
647
796
 
648
797
  Control flow helpers.
@@ -771,6 +920,38 @@ Options:
771
920
  - `replaceInstructions`: If true, `customInstructions` replaces the default prompt instead of being appended
772
921
  - `label`: Label to attach to the branch summary entry (or target entry if not summarizing)
773
922
 
923
+ ### ctx.switchSession(sessionPath)
924
+
925
+ Switch to a different session file:
926
+
927
+ ```typescript
928
+ const result = await ctx.switchSession("/path/to/session.jsonl");
929
+ if (result.cancelled) {
930
+ // An extension cancelled the switch via session_before_switch
931
+ }
932
+ ```
933
+
934
+ To discover available sessions, use the static `SessionManager.list()` or `SessionManager.listAll()` methods:
935
+
936
+ ```typescript
937
+ import { SessionManager } from "@mariozechner/pi-coding-agent";
938
+
939
+ pi.registerCommand("switch", {
940
+ description: "Switch to another session",
941
+ handler: async (args, ctx) => {
942
+ const sessions = await SessionManager.list(ctx.cwd);
943
+ if (sessions.length === 0) return;
944
+ const choice = await ctx.ui.select(
945
+ "Pick session:",
946
+ sessions.map(s => s.file),
947
+ );
948
+ if (choice) {
949
+ await ctx.switchSession(choice);
950
+ }
951
+ },
952
+ });
953
+ ```
954
+
774
955
  ### ctx.reload()
775
956
 
776
957
  Run the same reload flow as `/reload`.
@@ -787,7 +968,7 @@ pi.registerCommand("reload-runtime", {
787
968
 
788
969
  Important behavior:
789
970
  - `await ctx.reload()` emits `session_shutdown` for the current extension runtime
790
- - It then reloads resources and emits `session_start` (and `resources_discover` with reason `"reload"`) for the new runtime
971
+ - It then reloads resources and emits `session_start` with `reason: "reload"` and `resources_discover` with reason `"reload"`
791
972
  - The currently running command handler still continues in the old call frame
792
973
  - Code after `await ctx.reload()` still runs from the pre-reload version
793
974
  - Code after `await ctx.reload()` must not assume old in-memory extension state is still valid
@@ -837,6 +1018,14 @@ Subscribe to events. See [Events](#events) for event types and return values.
837
1018
 
838
1019
  Register a custom tool callable by the LLM. See [Custom Tools](#custom-tools) for full details.
839
1020
 
1021
+ `pi.registerTool()` works both during extension load and after startup. You can call it inside `session_start`, command handlers, or other event handlers. New tools are refreshed immediately in the same session, so they appear in `pi.getAllTools()` and are callable by the LLM without `/reload`.
1022
+
1023
+ Use `pi.setActiveTools()` to enable or disable tools (including dynamically added tools) at runtime.
1024
+
1025
+ Use `promptSnippet` to opt a custom tool into a one-line entry in `Available tools`, and `promptGuidelines` to append tool-specific bullets to the default `Guidelines` section when the tool is active.
1026
+
1027
+ See [dynamic-tools.ts](../examples/extensions/dynamic-tools.ts) for a full example.
1028
+
840
1029
  ```typescript
841
1030
  import { Type } from "@sinclair/typebox";
842
1031
  import { StringEnum } from "@mariozechner/pi-ai";
@@ -845,10 +1034,18 @@ pi.registerTool({
845
1034
  name: "my_tool",
846
1035
  label: "My Tool",
847
1036
  description: "What this tool does",
1037
+ promptSnippet: "Summarize or transform text according to action",
1038
+ promptGuidelines: ["Use this tool when the user asks to summarize previously generated text."],
848
1039
  parameters: Type.Object({
849
1040
  action: StringEnum(["list", "add"] as const),
850
1041
  text: Type.Optional(Type.String()),
851
1042
  }),
1043
+ prepareArguments(args) {
1044
+ // Optional compatibility shim. Runs before schema validation.
1045
+ // Return the current schema shape, for example to fold legacy fields
1046
+ // into the modern parameter object.
1047
+ return args;
1048
+ },
852
1049
 
853
1050
  async execute(toolCallId, params, signal, onUpdate, ctx) {
854
1051
  // Stream progress
@@ -861,8 +1058,8 @@ pi.registerTool({
861
1058
  },
862
1059
 
863
1060
  // Optional: Custom rendering
864
- renderCall(args, theme) { ... },
865
- renderResult(result, options, theme) { ... },
1061
+ renderCall(args, theme, context) { ... },
1062
+ renderResult(result, options, theme, context) { ... },
866
1063
  });
867
1064
  ```
868
1065
 
@@ -884,7 +1081,7 @@ pi.sendMessage({
884
1081
 
885
1082
  **Options:**
886
1083
  - `deliverAs` - Delivery mode:
887
- - `"steer"` (default) - Interrupts streaming. Delivered after current tool finishes, remaining tools skipped.
1084
+ - `"steer"` (default) - Queues the message while streaming. Delivered after the current assistant turn finishes executing its tool calls, before the next LLM call.
888
1085
  - `"followUp"` - Waits for agent to finish. Delivered only when agent has no more tool calls.
889
1086
  - `"nextTurn"` - Queued for next user prompt. Does not interrupt or trigger anything.
890
1087
  - `triggerTurn: true` - If agent is idle, trigger an LLM response immediately. Only applies to `"steer"` and `"followUp"` modes (ignored for `"nextTurn"`).
@@ -910,7 +1107,7 @@ pi.sendUserMessage("And then summarize", { deliverAs: "followUp" });
910
1107
 
911
1108
  **Options:**
912
1109
  - `deliverAs` - Required when agent is streaming:
913
- - `"steer"` - Interrupts after current tool, remaining tools skipped
1110
+ - `"steer"` - Queues the message for delivery after the current assistant turn finishes executing its tool calls
914
1111
  - `"followUp"` - Waits for agent to finish all tools
915
1112
 
916
1113
  When not streaming, the message is sent immediately and triggers a new turn. When streaming without `deliverAs`, throws an error.
@@ -974,6 +1171,8 @@ Labels persist in the session and survive restarts. Use them to mark important p
974
1171
 
975
1172
  Register a command.
976
1173
 
1174
+ If multiple extensions register the same command name, pi keeps them all and assigns numeric invocation suffixes in load order, for example `/review:1` and `/review:2`.
1175
+
977
1176
  ```typescript
978
1177
  pi.registerCommand("stats", {
979
1178
  description: "Show session statistics",
@@ -1011,20 +1210,28 @@ The list matches the RPC `get_commands` ordering: extensions first, then templat
1011
1210
  ```typescript
1012
1211
  const commands = pi.getCommands();
1013
1212
  const bySource = commands.filter((command) => command.source === "extension");
1213
+ const userScoped = commands.filter((command) => command.sourceInfo.scope === "user");
1014
1214
  ```
1015
1215
 
1016
1216
  Each entry has this shape:
1017
1217
 
1018
1218
  ```typescript
1019
1219
  {
1020
- name: string; // Command name without the leading slash
1220
+ name: string; // Invokable command name without the leading slash. May be suffixed like "review:1"
1021
1221
  description?: string;
1022
1222
  source: "extension" | "prompt" | "skill";
1023
- location?: "user" | "project" | "path"; // For templates and skills
1024
- path?: string; // Files backing templates, skills, and extensions
1223
+ sourceInfo: {
1224
+ path: string;
1225
+ source: string;
1226
+ scope: "user" | "project" | "temporary";
1227
+ origin: "package" | "top-level";
1228
+ baseDir?: string;
1229
+ };
1025
1230
  }
1026
1231
  ```
1027
1232
 
1233
+ Use `sourceInfo` as the canonical provenance field. Do not infer ownership from command names or from ad hoc path parsing.
1234
+
1028
1235
  Built-in interactive commands (like `/model` and `/settings`) are not included here. They are handled only in interactive
1029
1236
  mode and would not execute if sent via `prompt`.
1030
1237
 
@@ -1073,15 +1280,30 @@ const result = await pi.exec("git", ["status"], { signal, timeout: 5000 });
1073
1280
 
1074
1281
  ### pi.getActiveTools() / pi.getAllTools() / pi.setActiveTools(names)
1075
1282
 
1076
- Manage active tools.
1283
+ Manage active tools. This works for both built-in tools and dynamically registered tools.
1077
1284
 
1078
1285
  ```typescript
1079
- const active = pi.getActiveTools(); // ["read", "bash", "edit", "write"]
1080
- const all = pi.getAllTools(); // [{ name: "read", description: "Read file contents..." }, ...]
1081
- const names = all.map(t => t.name); // Just names if needed
1286
+ const active = pi.getActiveTools();
1287
+ const all = pi.getAllTools();
1288
+ // [{
1289
+ // name: "read",
1290
+ // description: "Read file contents...",
1291
+ // parameters: ...,
1292
+ // sourceInfo: { path: "<builtin:read>", source: "builtin", scope: "temporary", origin: "top-level" }
1293
+ // }, ...]
1294
+ const names = all.map(t => t.name);
1295
+ const builtinTools = all.filter((t) => t.sourceInfo.source === "builtin");
1296
+ const extensionTools = all.filter((t) => t.sourceInfo.source !== "builtin" && t.sourceInfo.source !== "sdk");
1082
1297
  pi.setActiveTools(["read", "bash"]); // Switch to read-only
1083
1298
  ```
1084
1299
 
1300
+ `pi.getAllTools()` returns `name`, `description`, `parameters`, and `sourceInfo`.
1301
+
1302
+ Typical `sourceInfo.source` values:
1303
+ - `builtin` for built-in tools
1304
+ - `sdk` for tools passed via `createAgentSession({ customTools })`
1305
+ - extension source metadata for tools registered by extensions
1306
+
1085
1307
  ### pi.setModel(model)
1086
1308
 
1087
1309
  Set the current model. Returns `false` if no API key is available for the model. See [models.md](models.md) for configuring custom models.
@@ -1118,6 +1340,8 @@ pi.events.emit("my:event", { ... });
1118
1340
 
1119
1341
  Register or override a model provider dynamically. Useful for proxies, custom endpoints, or team-wide model configurations.
1120
1342
 
1343
+ Calls made during the extension factory function are queued and applied once the runner initialises. Calls made after that — for example from a command handler following a user setup flow — take effect immediately without requiring a `/reload`.
1344
+
1121
1345
  ```typescript
1122
1346
  // Register a new provider with custom models
1123
1347
  pi.registerProvider("my-proxy", {
@@ -1178,6 +1402,21 @@ pi.registerProvider("corporate-ai", {
1178
1402
 
1179
1403
  See [custom-provider.md](custom-provider.md) for advanced topics: custom streaming APIs, OAuth details, model definition reference.
1180
1404
 
1405
+ ### pi.unregisterProvider(name)
1406
+
1407
+ Remove a previously registered provider and its models. Built-in models that were overridden by the provider are restored. Has no effect if the provider was not registered.
1408
+
1409
+ Like `registerProvider`, this takes effect immediately when called after the initial load phase, so a `/reload` is not required.
1410
+
1411
+ ```typescript
1412
+ pi.registerCommand("my-setup-teardown", {
1413
+ description: "Remove the custom proxy provider",
1414
+ handler: async (_args, _ctx) => {
1415
+ pi.unregisterProvider("my-proxy");
1416
+ },
1417
+ });
1418
+ ```
1419
+
1181
1420
  ## State Management
1182
1421
 
1183
1422
  Extensions with state should store it in tool result `details` for proper branching support:
@@ -1216,8 +1455,42 @@ export default function (pi: ExtensionAPI) {
1216
1455
 
1217
1456
  Register tools the LLM can call via `pi.registerTool()`. Tools appear in the system prompt and can have custom rendering.
1218
1457
 
1458
+ Use `promptSnippet` for a short one-line entry in the `Available tools` section in the default system prompt. If omitted, custom tools are left out of that section.
1459
+
1460
+ Use `promptGuidelines` to add tool-specific bullets to the default system prompt `Guidelines` section. These bullets are included only while the tool is active (for example, after `pi.setActiveTools([...])`).
1461
+
1219
1462
  Note: Some models are idiots and include the @ prefix in tool path arguments. Built-in tools strip a leading @ before resolving paths. If your custom tool accepts a path, normalize a leading @ as well.
1220
1463
 
1464
+ If your custom tool mutates files, use `withFileMutationQueue()` so it participates in the same per-file queue as built-in `edit` and `write`. This matters because tool calls run in parallel by default. Without the queue, two tools can read the same old file contents, compute different updates, and then whichever write lands last overwrites the other.
1465
+
1466
+ Example failure case: your custom tool edits `foo.ts` while built-in `edit` also changes `foo.ts` in the same assistant turn. If your tool does not participate in the queue, both can read the original `foo.ts`, apply separate changes, and one of those changes is lost.
1467
+
1468
+ Pass the real target file path to `withFileMutationQueue()`, not the raw user argument. Resolve it to an absolute path first, relative to `ctx.cwd` or your tool's working directory. For existing files, the helper canonicalizes through `realpath()`, so symlink aliases for the same file share one queue. For new files, it falls back to the resolved absolute path because there is nothing to `realpath()` yet.
1469
+
1470
+ Queue the entire mutation window on that target path. That includes read-modify-write logic, not just the final write.
1471
+
1472
+ ```typescript
1473
+ import { withFileMutationQueue } from "@mariozechner/pi-coding-agent";
1474
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
1475
+ import { dirname, resolve } from "node:path";
1476
+
1477
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
1478
+ const absolutePath = resolve(ctx.cwd, params.path);
1479
+
1480
+ return withFileMutationQueue(absolutePath, async () => {
1481
+ await mkdir(dirname(absolutePath), { recursive: true });
1482
+ const current = await readFile(absolutePath, "utf8");
1483
+ const next = current.replace(params.oldText, params.newText);
1484
+ await writeFile(absolutePath, next, "utf8");
1485
+
1486
+ return {
1487
+ content: [{ type: "text", text: `Updated ${params.path}` }],
1488
+ details: {},
1489
+ };
1490
+ });
1491
+ }
1492
+ ```
1493
+
1221
1494
  ### Tool Definition
1222
1495
 
1223
1496
  ```typescript
@@ -1229,10 +1502,22 @@ pi.registerTool({
1229
1502
  name: "my_tool",
1230
1503
  label: "My Tool",
1231
1504
  description: "What this tool does (shown to LLM)",
1505
+ promptSnippet: "List or add items in the project todo list",
1506
+ promptGuidelines: [
1507
+ "Use this tool for todo planning instead of direct file edits when the user asks for a task list."
1508
+ ],
1232
1509
  parameters: Type.Object({
1233
1510
  action: StringEnum(["list", "add"] as const), // Use StringEnum for Google compatibility
1234
1511
  text: Type.Optional(Type.String()),
1235
1512
  }),
1513
+ prepareArguments(args) {
1514
+ if (!args || typeof args !== "object") return args;
1515
+ const input = args as { action?: string; oldAction?: string };
1516
+ if (typeof input.oldAction === "string" && input.action === undefined) {
1517
+ return { ...input, action: input.oldAction };
1518
+ }
1519
+ return args;
1520
+ },
1236
1521
 
1237
1522
  async execute(toolCallId, params, signal, onUpdate, ctx) {
1238
1523
  // Check for cancellation
@@ -1257,13 +1542,72 @@ pi.registerTool({
1257
1542
  },
1258
1543
 
1259
1544
  // Optional: Custom rendering
1260
- renderCall(args, theme) { ... },
1261
- renderResult(result, options, theme) { ... },
1545
+ renderCall(args, theme, context) { ... },
1546
+ renderResult(result, options, theme, context) { ... },
1262
1547
  });
1263
1548
  ```
1264
1549
 
1550
+ **Signaling errors:** To mark a tool execution as failed (sets `isError: true` on the result and reports it to the LLM), throw an error from `execute`. Returning a value never sets the error flag regardless of what properties you include in the return object.
1551
+
1552
+ ```typescript
1553
+ // Correct: throw to signal an error
1554
+ async execute(toolCallId, params) {
1555
+ if (!isValid(params.input)) {
1556
+ throw new Error(`Invalid input: ${params.input}`);
1557
+ }
1558
+ return { content: [{ type: "text", text: "OK" }], details: {} };
1559
+ }
1560
+ ```
1561
+
1265
1562
  **Important:** Use `StringEnum` from `@mariozechner/pi-ai` for string enums. `Type.Union`/`Type.Literal` doesn't work with Google's API.
1266
1563
 
1564
+ **Argument preparation:** `prepareArguments(args)` is optional. If defined, it runs before schema validation and before `execute()`. Use it to mimic an older accepted input shape when pi resumes an older session whose stored tool call arguments no longer match the current schema. Return the object you want validated against `parameters`. Keep the public schema strict. Do not add deprecated compatibility fields to `parameters` just to keep old resumed sessions working.
1565
+
1566
+ Example: an older session may contain an `edit` tool call with top-level `oldText` and `newText`, while the current schema only accepts `edits: [{ oldText, newText }]`.
1567
+
1568
+ ```typescript
1569
+ pi.registerTool({
1570
+ name: "edit",
1571
+ label: "Edit",
1572
+ description: "Edit a single file using exact text replacement",
1573
+ parameters: Type.Object({
1574
+ path: Type.String(),
1575
+ edits: Type.Array(
1576
+ Type.Object({
1577
+ oldText: Type.String(),
1578
+ newText: Type.String(),
1579
+ }),
1580
+ ),
1581
+ }),
1582
+ prepareArguments(args) {
1583
+ if (!args || typeof args !== "object") return args;
1584
+
1585
+ const input = args as {
1586
+ path?: string;
1587
+ edits?: Array<{ oldText: string; newText: string }>;
1588
+ oldText?: unknown;
1589
+ newText?: unknown;
1590
+ };
1591
+
1592
+ if (typeof input.oldText !== "string" || typeof input.newText !== "string") {
1593
+ return args;
1594
+ }
1595
+
1596
+ return {
1597
+ ...input,
1598
+ edits: [...(input.edits ?? []), { oldText: input.oldText, newText: input.newText }],
1599
+ };
1600
+ },
1601
+ async execute(toolCallId, params, signal, onUpdate, ctx) {
1602
+ // params now matches the current schema
1603
+ return {
1604
+ content: [{ type: "text", text: `Applying ${params.edits.length} edit block(s)` }],
1605
+ details: {},
1606
+ };
1607
+ },
1608
+ });
1609
+ ```
1610
+
1267
1611
  ### Overriding Built-in Tools
1268
1612
 
1269
1613
  Extensions can override built-in tools (`read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`) by registering a tool with the same name. Interactive mode displays a warning when this happens.
@@ -1281,7 +1625,9 @@ pi --no-tools -e ./my-extension.ts
1281
1625
 
1282
1626
  See [examples/extensions/tool-override.ts](../examples/extensions/tool-override.ts) for a complete example that overrides `read` with logging and access control.
1283
1627
 
1284
- **Rendering:** If your override doesn't provide custom `renderCall`/`renderResult` functions, the built-in renderer is used automatically (syntax highlighting, diffs, etc.). This lets you wrap built-in tools for logging or access control without reimplementing the UI.
1628
+ **Rendering:** Built-in renderer inheritance is resolved per slot. Execution override and rendering override are independent. If your override omits `renderCall`, the built-in `renderCall` is used. If your override omits `renderResult`, the built-in `renderResult` is used. If your override omits both, the built-in renderer is used automatically (syntax highlighting, diffs, etc.). This lets you wrap built-in tools for logging or access control without reimplementing the UI.
1629
+
1630
+ **Prompt metadata:** `promptSnippet` and `promptGuidelines` are not inherited from the built-in tool. If your override should keep those prompt instructions, define them on the override explicitly.
1285
1631
 
1286
1632
  **Your implementation must match the exact result shape**, including the `details` type. The UI and session logic depend on these shapes for rendering and state tracking.
1287
1633
 
@@ -1325,6 +1671,8 @@ pi.registerTool({
1325
1671
 
1326
1672
  **Operations interfaces:** `ReadOperations`, `WriteOperations`, `EditOperations`, `BashOperations`, `LsOperations`, `GrepOperations`, `FindOperations`
1327
1673
 
1674
+ For `user_bash`, extensions can reuse pi's local shell backend via `createLocalBashOperations()` instead of reimplementing local process spawning, shell resolution, and process-tree termination.
1675
+
1328
1676
  The bash tool also supports a spawn hook to adjust the command, cwd, or env before execution:
1329
1677
 
1330
1678
  ```typescript
@@ -1413,44 +1761,52 @@ export default function (pi: ExtensionAPI) {
1413
1761
 
1414
1762
  ### Custom Rendering
1415
1763
 
1416
- Tools can provide `renderCall` and `renderResult` for custom TUI display. See [tui.md](tui.md) for the full component API and [tool-execution.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/modes/interactive/components/tool-execution.ts) for how built-in tools render.
1764
+ Tools can provide `renderCall` and `renderResult` for custom TUI display. See [tui.md](tui.md) for the full component API and [tool-execution.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/modes/interactive/components/tool-execution.ts) for how tool rows are composed.
1765
+
1766
+ Tool output is wrapped in a `Box` that handles padding and background. A defined `renderCall` or `renderResult` must return a `Component`. If a slot renderer is not defined, `tool-execution.ts` uses fallback rendering for that slot.
1767
+
1768
+ `renderCall` and `renderResult` each receive a `context` object with:
1769
+ - `args` - the current tool call arguments
1770
+ - `state` - shared row-local state across `renderCall` and `renderResult`
1771
+ - `lastComponent` - the previously returned component for that slot, if any
1772
+ - `invalidate()` - request a rerender of this tool row
1773
+ - `toolCallId`, `cwd`, `executionStarted`, `argsComplete`, `isPartial`, `expanded`, `showImages`, `isError`
1417
1774
 
1418
- Tool output is wrapped in a `Box` that handles padding and background. Your render methods return `Component` instances (typically `Text`).
1775
+ Use `context.state` for cross-slot shared state. Keep slot-local caches on the returned component instance when you want to reuse and mutate the same component across renders.
1419
1776
 
1420
1777
  #### renderCall
1421
1778
 
1422
- Renders the tool call (before/during execution):
1779
+ Renders the tool call or header:
1423
1780
 
1424
1781
  ```typescript
1425
1782
  import { Text } from "@mariozechner/pi-tui";
1426
1783
 
1427
- renderCall(args, theme) {
1428
- let text = theme.fg("toolTitle", theme.bold("my_tool "));
1429
- text += theme.fg("muted", args.action);
1784
+ renderCall(args, theme, context) {
1785
+ const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
1786
+ let content = theme.fg("toolTitle", theme.bold("my_tool "));
1787
+ content += theme.fg("muted", args.action);
1430
1788
  if (args.text) {
1431
- text += " " + theme.fg("dim", `"${args.text}"`);
1789
+ content += " " + theme.fg("dim", `"${args.text}"`);
1432
1790
  }
1433
- return new Text(text, 0, 0); // 0,0 padding - Box handles it
1791
+ text.setText(content);
1792
+ return text;
1434
1793
  }
1435
1794
  ```
1436
1795
 
1437
1796
  #### renderResult
1438
1797
 
1439
- Renders the tool result:
1798
+ Renders the tool result or output:
1440
1799
 
1441
1800
  ```typescript
1442
- renderResult(result, { expanded, isPartial }, theme) {
1443
- // Handle streaming
1801
+ renderResult(result, { expanded, isPartial }, theme, context) {
1444
1802
  if (isPartial) {
1445
1803
  return new Text(theme.fg("warning", "Processing..."), 0, 0);
1446
1804
  }
1447
1805
 
1448
- // Handle errors
1449
1806
  if (result.details?.error) {
1450
1807
  return new Text(theme.fg("error", `Error: ${result.details.error}`), 0, 0);
1451
1808
  }
1452
1809
 
1453
- // Normal result - support expanded view (Ctrl+O)
1454
1810
  let text = theme.fg("success", "✓ Done");
1455
1811
  if (expanded && result.details?.items) {
1456
1812
  for (const item of result.details.items) {
@@ -1461,40 +1817,52 @@ renderResult(result, { expanded, isPartial }, theme) {
1461
1817
  }
1462
1818
  ```
1463
1819
 
1820
+ If a slot intentionally has no visible content, return an empty `Component` such as an empty `Container`.
1821
+
1464
1822
  #### Keybinding Hints
1465
1823
 
1466
- Use `keyHint()` to display keybinding hints that respect user's keybinding configuration:
1824
+ Use `keyHint()` to display keybinding hints that respect the active keybinding configuration:
1467
1825
 
1468
1826
  ```typescript
1469
1827
  import { keyHint } from "@mariozechner/pi-coding-agent";
1470
1828
 
1471
- renderResult(result, { expanded }, theme) {
1829
+ renderResult(result, { expanded }, theme, context) {
1472
1830
  let text = theme.fg("success", "✓ Done");
1473
1831
  if (!expanded) {
1474
- text += ` (${keyHint("expandTools", "to expand")})`;
1832
+ text += ` (${keyHint("app.tools.expand", "to expand")})`;
1475
1833
  }
1476
1834
  return new Text(text, 0, 0);
1477
1835
  }
1478
1836
  ```
1479
1837
 
1480
1838
  Available functions:
1481
- - `keyHint(action, description)` - Editor actions (e.g., `"expandTools"`, `"selectConfirm"`)
1482
- - `appKeyHint(keybindings, action, description)` - App actions (requires `KeybindingsManager`)
1483
- - `editorKey(action)` - Get raw key string for editor action
1839
+ - `keyHint(keybinding, description)` - Formats a configured keybinding id such as `"app.tools.expand"` or `"tui.select.confirm"`
1840
+ - `keyText(keybinding)` - Returns the raw configured key text for a keybinding id
1484
1841
  - `rawKeyHint(key, description)` - Format a raw key string
1485
1842
 
1843
+ Use namespaced keybinding ids:
1844
+ - Coding-agent ids use the `app.*` namespace, for example `app.tools.expand`, `app.editor.external`, `app.session.rename`
1845
+ - Shared TUI ids use the `tui.*` namespace, for example `tui.select.confirm`, `tui.select.cancel`, `tui.input.tab`
1846
+
1847
+ For the exhaustive list of keybinding ids and defaults, see [keybindings.md](keybindings.md). `keybindings.json` uses those same namespaced ids.
1848
+
1849
+ Custom editors and `ctx.ui.custom()` components receive `keybindings: KeybindingsManager` as an injected argument. They should use that injected manager directly instead of calling `getKeybindings()` or `setKeybindings()`.
1850
+
1486
1851
  #### Best Practices
1487
1852
 
1488
- - Use `Text` with padding `(0, 0)` - the Box handles padding
1489
- - Use `\n` for multi-line content
1490
- - Handle `isPartial` for streaming progress
1491
- - Support `expanded` for detail on demand
1492
- - Keep default view compact
1853
+ - Use `Text` with padding `(0, 0)`. The Box handles padding.
1854
+ - Use `\n` for multi-line content.
1855
+ - Handle `isPartial` for streaming progress.
1856
+ - Support `expanded` for detail on demand.
1857
+ - Keep default view compact.
1858
+ - Read `context.args` in `renderResult` instead of copying args into `context.state`.
1859
+ - Use `context.state` only for data that must be shared across call and result slots.
1860
+ - Reuse `context.lastComponent` when the same component instance can be updated in place.
1493
1861
 
1494
1862
  #### Fallback
1495
1863
 
1496
- If `renderCall`/`renderResult` is not defined or throws:
1497
- - `renderCall`: Shows tool name
1864
+ If a slot renderer is not defined or throws:
1865
+ - `renderCall`: Shows the tool name
1498
1866
  - `renderResult`: Shows raw text from `content`
1499
1867
 
1500
1868
  ## Custom UI
@@ -1802,7 +2170,7 @@ const highlighted = highlightCode(code, lang, theme);
1802
2170
 
1803
2171
  - Extension errors are logged, agent continues
1804
2172
  - `tool_call` errors block the tool (fail-safe)
1805
- - Tool `execute` errors are reported to the LLM with `isError: true`
2173
+ - Tool `execute` errors must be signaled by throwing; the thrown error is caught, reported to the LLM with `isError: true`, and execution continues
1806
2174
 
1807
2175
  ## Mode Behavior
1808
2176
 
@@ -1826,6 +2194,7 @@ All examples in [examples/extensions/](../examples/extensions/).
1826
2194
  | `question.ts` | Tool with user interaction | `registerTool`, `ui.select` |
1827
2195
  | `questionnaire.ts` | Multi-step wizard tool | `registerTool`, `ui.custom` |
1828
2196
  | `todo.ts` | Stateful tool with persistence | `registerTool`, `appendEntry`, `renderResult`, session events |
2197
+ | `dynamic-tools.ts` | Register tools after startup and during commands | `registerTool`, `session_start`, `registerCommand` |
1829
2198
  | `truncated-tool.ts` | Output truncation example | `registerTool`, `truncateHead` |
1830
2199
  | `tool-override.ts` | Override built-in read tool | `registerTool` (same name as built-in) |
1831
2200
  | **Commands** |||
@@ -1843,13 +2212,14 @@ All examples in [examples/extensions/](../examples/extensions/).
1843
2212
  | `dirty-repo-guard.ts` | Warn on dirty git repo | `on("session_before_*")`, `exec` |
1844
2213
  | `input-transform.ts` | Transform user input | `on("input")` |
1845
2214
  | `model-status.ts` | React to model changes | `on("model_select")`, `setStatus` |
2215
+ | `provider-payload.ts` | Inspect or patch provider payloads | `on("before_provider_request")` |
1846
2216
  | `system-prompt-header.ts` | Display system prompt info | `on("agent_start")`, `getSystemPrompt` |
1847
2217
  | `claude-rules.ts` | Load rules from files | `on("session_start")`, `on("before_agent_start")` |
1848
2218
  | `file-trigger.ts` | File watcher triggers messages | `sendMessage` |
1849
2219
  | **Compaction & Sessions** |||
1850
2220
  | `custom-compaction.ts` | Custom compaction summary | `on("session_before_compact")` |
1851
2221
  | `trigger-compact.ts` | Trigger compaction manually | `compact()` |
1852
- | `git-checkpoint.ts` | Git stash on turns | `on("turn_end")`, `on("session_fork")`, `exec` |
2222
+ | `git-checkpoint.ts` | Git stash on turns | `on("turn_start")`, `on("session_before_fork")`, `exec` |
1853
2223
  | `auto-commit-on-exit.ts` | Commit on shutdown | `on("session_shutdown")`, `exec` |
1854
2224
  | **UI Components** |||
1855
2225
  | `status-line.ts` | Footer status indicator | `setStatus`, session events |