@hyperspaceng/neural-coding-agent 0.62.1 → 0.63.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 (348) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +10 -0
  3. package/dist/bun/register-bedrock.d.ts.map +1 -1
  4. package/dist/bun/register-bedrock.js +2 -2
  5. package/dist/bun/register-bedrock.js.map +1 -1
  6. package/dist/cli/args.d.ts +1 -1
  7. package/dist/cli/args.d.ts.map +1 -1
  8. package/dist/cli/args.js.map +1 -1
  9. package/dist/cli/config-selector.d.ts.map +1 -1
  10. package/dist/cli/config-selector.js +1 -1
  11. package/dist/cli/config-selector.js.map +1 -1
  12. package/dist/cli/file-processor.d.ts +1 -1
  13. package/dist/cli/file-processor.d.ts.map +1 -1
  14. package/dist/cli/file-processor.js +4 -0
  15. package/dist/cli/file-processor.js.map +1 -1
  16. package/dist/cli/initial-message.d.ts +1 -1
  17. package/dist/cli/initial-message.d.ts.map +1 -1
  18. package/dist/cli/initial-message.js.map +1 -1
  19. package/dist/cli/list-models.d.ts.map +1 -1
  20. package/dist/cli/list-models.js +1 -1
  21. package/dist/cli/list-models.js.map +1 -1
  22. package/dist/cli/session-picker.d.ts.map +1 -1
  23. package/dist/cli/session-picker.js +1 -1
  24. package/dist/cli/session-picker.js.map +1 -1
  25. package/dist/core/agent-session.d.ts +12 -5
  26. package/dist/core/agent-session.d.ts.map +1 -1
  27. package/dist/core/agent-session.js +61 -47
  28. package/dist/core/agent-session.js.map +1 -1
  29. package/dist/core/auth-storage.d.ts +2 -2
  30. package/dist/core/auth-storage.d.ts.map +1 -1
  31. package/dist/core/auth-storage.js +2 -2
  32. package/dist/core/auth-storage.js.map +1 -1
  33. package/dist/core/compaction/branch-summarization.d.ts +2 -2
  34. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  35. package/dist/core/compaction/branch-summarization.js +1 -1
  36. package/dist/core/compaction/branch-summarization.js.map +1 -1
  37. package/dist/core/compaction/compaction.d.ts +2 -2
  38. package/dist/core/compaction/compaction.d.ts.map +1 -1
  39. package/dist/core/compaction/compaction.js +1 -1
  40. package/dist/core/compaction/compaction.js.map +1 -1
  41. package/dist/core/compaction/utils.d.ts +2 -2
  42. package/dist/core/compaction/utils.d.ts.map +1 -1
  43. package/dist/core/compaction/utils.js.map +1 -1
  44. package/dist/core/defaults.d.ts +1 -1
  45. package/dist/core/defaults.d.ts.map +1 -1
  46. package/dist/core/defaults.js.map +1 -1
  47. package/dist/core/export-html/index.d.ts +3 -3
  48. package/dist/core/export-html/index.d.ts.map +1 -1
  49. package/dist/core/export-html/index.js +2 -2
  50. package/dist/core/export-html/index.js.map +1 -1
  51. package/dist/core/export-html/tool-renderer.d.ts +2 -2
  52. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  53. package/dist/core/export-html/tool-renderer.js +41 -16
  54. package/dist/core/export-html/tool-renderer.js.map +1 -1
  55. package/dist/core/extensions/index.d.ts +3 -2
  56. package/dist/core/extensions/index.d.ts.map +1 -1
  57. package/dist/core/extensions/index.js.map +1 -1
  58. package/dist/core/extensions/loader.d.ts.map +1 -1
  59. package/dist/core/extensions/loader.js +27 -17
  60. package/dist/core/extensions/loader.js.map +1 -1
  61. package/dist/core/extensions/runner.d.ts +7 -10
  62. package/dist/core/extensions/runner.d.ts.map +1 -1
  63. package/dist/core/extensions/runner.js +27 -38
  64. package/dist/core/extensions/runner.js.map +1 -1
  65. package/dist/core/extensions/types.d.ts +49 -14
  66. package/dist/core/extensions/types.d.ts.map +1 -1
  67. package/dist/core/extensions/types.js.map +1 -1
  68. package/dist/core/extensions/wrapper.d.ts +1 -1
  69. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  70. package/dist/core/extensions/wrapper.js +2 -8
  71. package/dist/core/extensions/wrapper.js.map +1 -1
  72. package/dist/core/index.d.ts +2 -1
  73. package/dist/core/index.d.ts.map +1 -1
  74. package/dist/core/index.js +1 -0
  75. package/dist/core/index.js.map +1 -1
  76. package/dist/core/keybindings.d.ts +2 -2
  77. package/dist/core/keybindings.d.ts.map +1 -1
  78. package/dist/core/keybindings.js +1 -1
  79. package/dist/core/keybindings.js.map +1 -1
  80. package/dist/core/messages.d.ts +3 -3
  81. package/dist/core/messages.d.ts.map +1 -1
  82. package/dist/core/messages.js.map +1 -1
  83. package/dist/core/model-registry.d.ts +1 -1
  84. package/dist/core/model-registry.d.ts.map +1 -1
  85. package/dist/core/model-registry.js +2 -2
  86. package/dist/core/model-registry.js.map +1 -1
  87. package/dist/core/model-resolver.d.ts +2 -2
  88. package/dist/core/model-resolver.d.ts.map +1 -1
  89. package/dist/core/model-resolver.js +1 -1
  90. package/dist/core/model-resolver.js.map +1 -1
  91. package/dist/core/output-guard.d.ts +6 -0
  92. package/dist/core/output-guard.d.ts.map +1 -0
  93. package/dist/core/output-guard.js +59 -0
  94. package/dist/core/output-guard.js.map +1 -0
  95. package/dist/core/package-manager.d.ts +3 -0
  96. package/dist/core/package-manager.d.ts.map +1 -1
  97. package/dist/core/package-manager.js +69 -9
  98. package/dist/core/package-manager.js.map +1 -1
  99. package/dist/core/prompt-templates.d.ts +2 -1
  100. package/dist/core/prompt-templates.d.ts.map +1 -1
  101. package/dist/core/prompt-templates.js +30 -32
  102. package/dist/core/prompt-templates.js.map +1 -1
  103. package/dist/core/resource-loader.d.ts +6 -5
  104. package/dist/core/resource-loader.d.ts.map +1 -1
  105. package/dist/core/resource-loader.js +136 -108
  106. package/dist/core/resource-loader.js.map +1 -1
  107. package/dist/core/sdk.d.ts +4 -4
  108. package/dist/core/sdk.d.ts.map +1 -1
  109. package/dist/core/sdk.js +2 -2
  110. package/dist/core/sdk.js.map +1 -1
  111. package/dist/core/session-manager.d.ts +2 -2
  112. package/dist/core/session-manager.d.ts.map +1 -1
  113. package/dist/core/session-manager.js.map +1 -1
  114. package/dist/core/settings-manager.d.ts +1 -1
  115. package/dist/core/settings-manager.d.ts.map +1 -1
  116. package/dist/core/settings-manager.js.map +1 -1
  117. package/dist/core/skills.d.ts +2 -1
  118. package/dist/core/skills.d.ts.map +1 -1
  119. package/dist/core/skills.js +25 -1
  120. package/dist/core/skills.js.map +1 -1
  121. package/dist/core/slash-commands.d.ts +2 -3
  122. package/dist/core/slash-commands.d.ts.map +1 -1
  123. package/dist/core/slash-commands.js.map +1 -1
  124. package/dist/core/source-info.d.ts +18 -0
  125. package/dist/core/source-info.d.ts.map +1 -0
  126. package/dist/core/source-info.js +19 -0
  127. package/dist/core/source-info.js.map +1 -0
  128. package/dist/core/system-prompt.d.ts.map +1 -1
  129. package/dist/core/system-prompt.js +3 -38
  130. package/dist/core/system-prompt.js.map +1 -1
  131. package/dist/core/tools/bash.d.ts +20 -10
  132. package/dist/core/tools/bash.d.ts.map +1 -1
  133. package/dist/core/tools/bash.js +151 -59
  134. package/dist/core/tools/bash.js.map +1 -1
  135. package/dist/core/tools/edit.d.ts +15 -3
  136. package/dist/core/tools/edit.d.ts.map +1 -1
  137. package/dist/core/tools/edit.js +92 -21
  138. package/dist/core/tools/edit.js.map +1 -1
  139. package/dist/core/tools/find.d.ts +12 -5
  140. package/dist/core/tools/find.d.ts.map +1 -1
  141. package/dist/core/tools/find.js +76 -27
  142. package/dist/core/tools/find.js.map +1 -1
  143. package/dist/core/tools/grep.d.ts +16 -5
  144. package/dist/core/tools/grep.d.ts.map +1 -1
  145. package/dist/core/tools/grep.js +83 -29
  146. package/dist/core/tools/grep.js.map +1 -1
  147. package/dist/core/tools/index.d.ts +58 -20
  148. package/dist/core/tools/index.d.ts.map +1 -1
  149. package/dist/core/tools/index.js +50 -26
  150. package/dist/core/tools/index.js.map +1 -1
  151. package/dist/core/tools/ls.d.ts +10 -4
  152. package/dist/core/tools/ls.d.ts.map +1 -1
  153. package/dist/core/tools/ls.js +67 -13
  154. package/dist/core/tools/ls.js.map +1 -1
  155. package/dist/core/tools/read.d.ts +11 -4
  156. package/dist/core/tools/read.d.ts.map +1 -1
  157. package/dist/core/tools/read.js +110 -51
  158. package/dist/core/tools/read.js.map +1 -1
  159. package/dist/core/tools/render-utils.d.ts +21 -0
  160. package/dist/core/tools/render-utils.d.ts.map +1 -0
  161. package/dist/core/tools/render-utils.js +49 -0
  162. package/dist/core/tools/render-utils.js.map +1 -0
  163. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  164. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  165. package/dist/core/tools/tool-definition-wrapper.js +30 -0
  166. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  167. package/dist/core/tools/write.d.ts +10 -4
  168. package/dist/core/tools/write.d.ts.map +1 -1
  169. package/dist/core/tools/write.js +162 -27
  170. package/dist/core/tools/write.js.map +1 -1
  171. package/dist/index.d.ts +3 -2
  172. package/dist/index.d.ts.map +1 -1
  173. package/dist/index.js +2 -1
  174. package/dist/index.js.map +1 -1
  175. package/dist/main.d.ts.map +1 -1
  176. package/dist/main.js +30 -10
  177. package/dist/main.js.map +1 -1
  178. package/dist/modes/interactive/components/armin.d.ts +1 -1
  179. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  180. package/dist/modes/interactive/components/armin.js.map +1 -1
  181. package/dist/modes/interactive/components/assistant-message.d.ts +2 -2
  182. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  183. package/dist/modes/interactive/components/assistant-message.js +1 -1
  184. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  185. package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
  186. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  187. package/dist/modes/interactive/components/bash-execution.js +1 -1
  188. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  189. package/dist/modes/interactive/components/bordered-loader.d.ts +1 -1
  190. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  191. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  192. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  193. package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
  194. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  195. package/dist/modes/interactive/components/branch-summary-message.js +1 -1
  196. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  197. package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
  198. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  199. package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
  200. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  201. package/dist/modes/interactive/components/config-selector.d.ts +1 -1
  202. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  203. package/dist/modes/interactive/components/config-selector.js +1 -1
  204. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  205. package/dist/modes/interactive/components/countdown-timer.d.ts +1 -1
  206. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  207. package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
  208. package/dist/modes/interactive/components/custom-editor.d.ts +1 -1
  209. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  210. package/dist/modes/interactive/components/custom-editor.js +1 -1
  211. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  212. package/dist/modes/interactive/components/custom-message.d.ts +1 -1
  213. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  214. package/dist/modes/interactive/components/custom-message.js +1 -1
  215. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  216. package/dist/modes/interactive/components/daxnuts.d.ts +1 -1
  217. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  218. package/dist/modes/interactive/components/daxnuts.js.map +1 -1
  219. package/dist/modes/interactive/components/dynamic-border.d.ts +1 -1
  220. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  221. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  222. package/dist/modes/interactive/components/extension-editor.d.ts +1 -1
  223. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  224. package/dist/modes/interactive/components/extension-editor.js +1 -1
  225. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  226. package/dist/modes/interactive/components/extension-input.d.ts +1 -1
  227. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  228. package/dist/modes/interactive/components/extension-input.js +1 -1
  229. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  230. package/dist/modes/interactive/components/extension-selector.d.ts +1 -1
  231. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  232. package/dist/modes/interactive/components/extension-selector.js +1 -1
  233. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  234. package/dist/modes/interactive/components/footer.d.ts +1 -1
  235. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  236. package/dist/modes/interactive/components/footer.js +1 -1
  237. package/dist/modes/interactive/components/footer.js.map +1 -1
  238. package/dist/modes/interactive/components/keybinding-hints.d.ts +1 -1
  239. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  240. package/dist/modes/interactive/components/keybinding-hints.js +1 -1
  241. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  242. package/dist/modes/interactive/components/login-dialog.d.ts +1 -1
  243. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  244. package/dist/modes/interactive/components/login-dialog.js +2 -2
  245. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  246. package/dist/modes/interactive/components/model-selector.d.ts +2 -2
  247. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  248. package/dist/modes/interactive/components/model-selector.js +2 -2
  249. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  250. package/dist/modes/interactive/components/oauth-selector.d.ts +1 -1
  251. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  252. package/dist/modes/interactive/components/oauth-selector.js +2 -2
  253. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  254. package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -2
  255. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  256. package/dist/modes/interactive/components/scoped-models-selector.js +1 -1
  257. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  258. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  259. package/dist/modes/interactive/components/session-selector-search.js +1 -1
  260. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  261. package/dist/modes/interactive/components/session-selector.d.ts +1 -1
  262. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  263. package/dist/modes/interactive/components/session-selector.js +1 -1
  264. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  265. package/dist/modes/interactive/components/settings-selector.d.ts +3 -3
  266. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  267. package/dist/modes/interactive/components/settings-selector.js +1 -1
  268. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  269. package/dist/modes/interactive/components/show-images-selector.d.ts +1 -1
  270. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  271. package/dist/modes/interactive/components/show-images-selector.js +1 -1
  272. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  273. package/dist/modes/interactive/components/skill-invocation-message.d.ts +1 -1
  274. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  275. package/dist/modes/interactive/components/skill-invocation-message.js +1 -1
  276. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  277. package/dist/modes/interactive/components/theme-selector.d.ts +1 -1
  278. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  279. package/dist/modes/interactive/components/theme-selector.js +1 -1
  280. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  281. package/dist/modes/interactive/components/thinking-selector.d.ts +2 -2
  282. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  283. package/dist/modes/interactive/components/thinking-selector.js +1 -1
  284. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  285. package/dist/modes/interactive/components/tool-execution.d.ts +16 -41
  286. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  287. package/dist/modes/interactive/components/tool-execution.js +126 -679
  288. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  289. package/dist/modes/interactive/components/tree-selector.d.ts +1 -1
  290. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  291. package/dist/modes/interactive/components/tree-selector.js +1 -1
  292. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  293. package/dist/modes/interactive/components/user-message-selector.d.ts +1 -1
  294. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  295. package/dist/modes/interactive/components/user-message-selector.js +1 -1
  296. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  297. package/dist/modes/interactive/components/user-message.d.ts +1 -1
  298. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  299. package/dist/modes/interactive/components/user-message.js +1 -1
  300. package/dist/modes/interactive/components/user-message.js.map +1 -1
  301. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  302. package/dist/modes/interactive/components/visual-truncate.js +1 -1
  303. package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
  304. package/dist/modes/interactive/interactive-mode.d.ts +5 -12
  305. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  306. package/dist/modes/interactive/interactive-mode.js +147 -95
  307. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  308. package/dist/modes/interactive/theme/theme.d.ts +5 -2
  309. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  310. package/dist/modes/interactive/theme/theme.js +15 -1
  311. package/dist/modes/interactive/theme/theme.js.map +1 -1
  312. package/dist/modes/print-mode.d.ts +1 -1
  313. package/dist/modes/print-mode.d.ts.map +1 -1
  314. package/dist/modes/print-mode.js +5 -11
  315. package/dist/modes/print-mode.js.map +1 -1
  316. package/dist/modes/rpc/rpc-client.d.ts +2 -2
  317. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  318. package/dist/modes/rpc/rpc-client.js.map +1 -1
  319. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  320. package/dist/modes/rpc/rpc-mode.js +27 -20
  321. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  322. package/dist/modes/rpc/rpc-types.d.ts +5 -6
  323. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  324. package/dist/modes/rpc/rpc-types.js.map +1 -1
  325. package/dist/utils/image-resize.d.ts +6 -6
  326. package/dist/utils/image-resize.d.ts.map +1 -1
  327. package/dist/utils/image-resize.js +45 -94
  328. package/dist/utils/image-resize.js.map +1 -1
  329. package/docs/extensions.md +72 -32
  330. package/docs/tui.md +2 -2
  331. package/examples/extensions/built-in-tool-renderer.ts +8 -8
  332. package/examples/extensions/commands.ts +3 -3
  333. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  334. package/examples/extensions/custom-provider-anthropic/package.json +17 -17
  335. package/examples/extensions/custom-provider-gitlab-duo/package.json +14 -14
  336. package/examples/extensions/custom-provider-qwen-cli/package.json +14 -14
  337. package/examples/extensions/minimal-mode.ts +14 -14
  338. package/examples/extensions/question.ts +2 -2
  339. package/examples/extensions/questionnaire.ts +2 -2
  340. package/examples/extensions/subagent/index.ts +23 -3
  341. package/examples/extensions/todo.ts +2 -2
  342. package/examples/extensions/truncated-tool.ts +2 -2
  343. package/examples/extensions/with-deps/package-lock.json +2 -2
  344. package/examples/extensions/with-deps/package.json +20 -20
  345. package/examples/sdk/04-skills.ts +8 -2
  346. package/examples/sdk/08-prompt-templates.ts +2 -1
  347. package/examples/sdk/12-full-control.ts +0 -1
  348. package/package.json +4 -4
@@ -976,8 +976,8 @@ pi.registerTool({
976
976
  },
977
977
 
978
978
  // Optional: Custom rendering
979
- renderCall(args, theme) { ... },
980
- renderResult(result, options, theme) { ... },
979
+ renderCall(args, theme, context) { ... },
980
+ renderResult(result, options, theme, context) { ... },
981
981
  });
982
982
  ```
983
983
 
@@ -1089,6 +1089,8 @@ Labels persist in the session and survive restarts. Use them to mark important p
1089
1089
 
1090
1090
  Register a command.
1091
1091
 
1092
+ 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`.
1093
+
1092
1094
  ```typescript
1093
1095
  pi.registerCommand("stats", {
1094
1096
  description: "Show session statistics",
@@ -1126,20 +1128,28 @@ The list matches the RPC `get_commands` ordering: extensions first, then templat
1126
1128
  ```typescript
1127
1129
  const commands = pi.getCommands();
1128
1130
  const bySource = commands.filter((command) => command.source === "extension");
1131
+ const userScoped = commands.filter((command) => command.sourceInfo.scope === "user");
1129
1132
  ```
1130
1133
 
1131
1134
  Each entry has this shape:
1132
1135
 
1133
1136
  ```typescript
1134
1137
  {
1135
- name: string; // Command name without the leading slash
1138
+ name: string; // Invokable command name without the leading slash. May be suffixed like "review:1"
1136
1139
  description?: string;
1137
1140
  source: "extension" | "prompt" | "skill";
1138
- location?: "user" | "project" | "path"; // For templates and skills
1139
- path?: string; // Files backing templates, skills, and extensions
1141
+ sourceInfo: {
1142
+ path: string;
1143
+ source: string;
1144
+ scope: "user" | "project" | "temporary";
1145
+ origin: "package" | "top-level";
1146
+ baseDir?: string;
1147
+ };
1140
1148
  }
1141
1149
  ```
1142
1150
 
1151
+ Use `sourceInfo` as the canonical provenance field. Do not infer ownership from command names or from ad hoc path parsing.
1152
+
1143
1153
  Built-in interactive commands (like `/model` and `/settings`) are not included here. They are handled only in interactive
1144
1154
  mode and would not execute if sent via `prompt`.
1145
1155
 
@@ -1191,12 +1201,27 @@ const result = await pi.exec("git", ["status"], { signal, timeout: 5000 });
1191
1201
  Manage active tools. This works for both built-in tools and dynamically registered tools.
1192
1202
 
1193
1203
  ```typescript
1194
- const active = pi.getActiveTools(); // ["read", "bash", "edit", "write"]
1195
- const all = pi.getAllTools(); // [{ name: "read", description: "Read file contents..." }, ...]
1196
- const names = all.map(t => t.name); // Just names if needed
1204
+ const active = pi.getActiveTools();
1205
+ const all = pi.getAllTools();
1206
+ // [{
1207
+ // name: "read",
1208
+ // description: "Read file contents...",
1209
+ // parameters: ...,
1210
+ // sourceInfo: { path: "<builtin:read>", source: "builtin", scope: "temporary", origin: "top-level" }
1211
+ // }, ...]
1212
+ const names = all.map(t => t.name);
1213
+ const builtinTools = all.filter((t) => t.sourceInfo.source === "builtin");
1214
+ const extensionTools = all.filter((t) => t.sourceInfo.source !== "builtin" && t.sourceInfo.source !== "sdk");
1197
1215
  pi.setActiveTools(["read", "bash"]); // Switch to read-only
1198
1216
  ```
1199
1217
 
1218
+ `pi.getAllTools()` returns `name`, `description`, `parameters`, and `sourceInfo`.
1219
+
1220
+ Typical `sourceInfo.source` values:
1221
+ - `builtin` for built-in tools
1222
+ - `sdk` for tools passed via `createAgentSession({ customTools })`
1223
+ - extension source metadata for tools registered by extensions
1224
+
1200
1225
  ### pi.setModel(model)
1201
1226
 
1202
1227
  Set the current model. Returns `false` if no API key is available for the model. See [models.md](models.md) for configuring custom models.
@@ -1427,8 +1452,8 @@ pi.registerTool({
1427
1452
  },
1428
1453
 
1429
1454
  // Optional: Custom rendering
1430
- renderCall(args, theme) { ... },
1431
- renderResult(result, options, theme) { ... },
1455
+ renderCall(args, theme, context) { ... },
1456
+ renderResult(result, options, theme, context) { ... },
1432
1457
  });
1433
1458
  ```
1434
1459
 
@@ -1463,7 +1488,9 @@ pi --no-tools -e ./my-extension.ts
1463
1488
 
1464
1489
  See [examples/extensions/tool-override.ts](../examples/extensions/tool-override.ts) for a complete example that overrides `read` with logging and access control.
1465
1490
 
1466
- **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.
1491
+ **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.
1492
+
1493
+ **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.
1467
1494
 
1468
1495
  **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.
1469
1496
 
@@ -1597,44 +1624,52 @@ export default function (pi: ExtensionAPI) {
1597
1624
 
1598
1625
  ### Custom Rendering
1599
1626
 
1600
- 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.
1627
+ 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.
1628
+
1629
+ 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.
1630
+
1631
+ `renderCall` and `renderResult` each receive a `context` object with:
1632
+ - `args` - the current tool call arguments
1633
+ - `state` - shared row-local state across `renderCall` and `renderResult`
1634
+ - `lastComponent` - the previously returned component for that slot, if any
1635
+ - `invalidate()` - request a rerender of this tool row
1636
+ - `toolCallId`, `cwd`, `executionStarted`, `argsComplete`, `isPartial`, `expanded`, `showImages`, `isError`
1601
1637
 
1602
- Tool output is wrapped in a `Box` that handles padding and background. Your render methods return `Component` instances (typically `Text`).
1638
+ 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.
1603
1639
 
1604
1640
  #### renderCall
1605
1641
 
1606
- Renders the tool call (before/during execution):
1642
+ Renders the tool call or header:
1607
1643
 
1608
1644
  ```typescript
1609
1645
  import { Text } from "@mariozechner/pi-tui";
1610
1646
 
1611
- renderCall(args, theme) {
1612
- let text = theme.fg("toolTitle", theme.bold("my_tool "));
1613
- text += theme.fg("muted", args.action);
1647
+ renderCall(args, theme, context) {
1648
+ const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
1649
+ let content = theme.fg("toolTitle", theme.bold("my_tool "));
1650
+ content += theme.fg("muted", args.action);
1614
1651
  if (args.text) {
1615
- text += " " + theme.fg("dim", `"${args.text}"`);
1652
+ content += " " + theme.fg("dim", `"${args.text}"`);
1616
1653
  }
1617
- return new Text(text, 0, 0); // 0,0 padding - Box handles it
1654
+ text.setText(content);
1655
+ return text;
1618
1656
  }
1619
1657
  ```
1620
1658
 
1621
1659
  #### renderResult
1622
1660
 
1623
- Renders the tool result:
1661
+ Renders the tool result or output:
1624
1662
 
1625
1663
  ```typescript
1626
- renderResult(result, { expanded, isPartial }, theme) {
1627
- // Handle streaming
1664
+ renderResult(result, { expanded, isPartial }, theme, context) {
1628
1665
  if (isPartial) {
1629
1666
  return new Text(theme.fg("warning", "Processing..."), 0, 0);
1630
1667
  }
1631
1668
 
1632
- // Handle errors
1633
1669
  if (result.details?.error) {
1634
1670
  return new Text(theme.fg("error", `Error: ${result.details.error}`), 0, 0);
1635
1671
  }
1636
1672
 
1637
- // Normal result - support expanded view (Ctrl+O)
1638
1673
  let text = theme.fg("success", "✓ Done");
1639
1674
  if (expanded && result.details?.items) {
1640
1675
  for (const item of result.details.items) {
@@ -1645,6 +1680,8 @@ renderResult(result, { expanded, isPartial }, theme) {
1645
1680
  }
1646
1681
  ```
1647
1682
 
1683
+ If a slot intentionally has no visible content, return an empty `Component` such as an empty `Container`.
1684
+
1648
1685
  #### Keybinding Hints
1649
1686
 
1650
1687
  Use `keyHint()` to display keybinding hints that respect the active keybinding configuration:
@@ -1652,7 +1689,7 @@ Use `keyHint()` to display keybinding hints that respect the active keybinding c
1652
1689
  ```typescript
1653
1690
  import { keyHint } from "@mariozechner/pi-coding-agent";
1654
1691
 
1655
- renderResult(result, { expanded }, theme) {
1692
+ renderResult(result, { expanded }, theme, context) {
1656
1693
  let text = theme.fg("success", "✓ Done");
1657
1694
  if (!expanded) {
1658
1695
  text += ` (${keyHint("app.tools.expand", "to expand")})`;
@@ -1676,16 +1713,19 @@ Custom editors and `ctx.ui.custom()` components receive `keybindings: Keybinding
1676
1713
 
1677
1714
  #### Best Practices
1678
1715
 
1679
- - Use `Text` with padding `(0, 0)` - the Box handles padding
1680
- - Use `\n` for multi-line content
1681
- - Handle `isPartial` for streaming progress
1682
- - Support `expanded` for detail on demand
1683
- - Keep default view compact
1716
+ - Use `Text` with padding `(0, 0)`. The Box handles padding.
1717
+ - Use `\n` for multi-line content.
1718
+ - Handle `isPartial` for streaming progress.
1719
+ - Support `expanded` for detail on demand.
1720
+ - Keep default view compact.
1721
+ - Read `context.args` in `renderResult` instead of copying args into `context.state`.
1722
+ - Use `context.state` only for data that must be shared across call and result slots.
1723
+ - Reuse `context.lastComponent` when the same component instance can be updated in place.
1684
1724
 
1685
1725
  #### Fallback
1686
1726
 
1687
- If `renderCall`/`renderResult` is not defined or throws:
1688
- - `renderCall`: Shows tool name
1727
+ If a slot renderer is not defined or throws:
1728
+ - `renderCall`: Shows the tool name
1689
1729
  - `renderResult`: Shows raw text from `content`
1690
1730
 
1691
1731
  ## Custom UI
package/docs/tui.md CHANGED
@@ -394,7 +394,7 @@ Components accept theme objects for styling.
394
394
  **In `renderCall`/`renderResult`**, use the `theme` parameter:
395
395
 
396
396
  ```typescript
397
- renderResult(result, options, theme) {
397
+ renderResult(result, options, theme, context) {
398
398
  // Use theme.fg() for foreground colors
399
399
  return new Text(theme.fg("success", "Done!"), 0, 0);
400
400
 
@@ -428,7 +428,7 @@ renderResult(result, options, theme) {
428
428
  import { getMarkdownTheme } from "@mariozechner/pi-coding-agent";
429
429
  import { Markdown } from "@mariozechner/pi-tui";
430
430
 
431
- renderResult(result, options, theme) {
431
+ renderResult(result, options, theme, context) {
432
432
  const mdTheme = getMarkdownTheme();
433
433
  return new Markdown(result.details.markdown, 0, 0, mdTheme);
434
434
  }
@@ -42,7 +42,7 @@ export default function (pi: ExtensionAPI) {
42
42
  return originalRead.execute(toolCallId, params, signal, onUpdate);
43
43
  },
44
44
 
45
- renderCall(args, theme) {
45
+ renderCall(args, theme, _context) {
46
46
  let text = theme.fg("toolTitle", theme.bold("read "));
47
47
  text += theme.fg("accent", args.path);
48
48
  if (args.offset || args.limit) {
@@ -54,7 +54,7 @@ export default function (pi: ExtensionAPI) {
54
54
  return new Text(text, 0, 0);
55
55
  },
56
56
 
57
- renderResult(result, { expanded, isPartial }, theme) {
57
+ renderResult(result, { expanded, isPartial }, theme, _context) {
58
58
  if (isPartial) return new Text(theme.fg("warning", "Reading..."), 0, 0);
59
59
 
60
60
  const details = result.details as ReadToolDetails | undefined;
@@ -101,7 +101,7 @@ export default function (pi: ExtensionAPI) {
101
101
  return originalBash.execute(toolCallId, params, signal, onUpdate);
102
102
  },
103
103
 
104
- renderCall(args, theme) {
104
+ renderCall(args, theme, _context) {
105
105
  let text = theme.fg("toolTitle", theme.bold("$ "));
106
106
  const cmd = args.command.length > 80 ? `${args.command.slice(0, 77)}...` : args.command;
107
107
  text += theme.fg("accent", cmd);
@@ -111,7 +111,7 @@ export default function (pi: ExtensionAPI) {
111
111
  return new Text(text, 0, 0);
112
112
  },
113
113
 
114
- renderResult(result, { expanded, isPartial }, theme) {
114
+ renderResult(result, { expanded, isPartial }, theme, _context) {
115
115
  if (isPartial) return new Text(theme.fg("warning", "Running..."), 0, 0);
116
116
 
117
117
  const details = result.details as BashToolDetails | undefined;
@@ -160,13 +160,13 @@ export default function (pi: ExtensionAPI) {
160
160
  return originalEdit.execute(toolCallId, params, signal, onUpdate);
161
161
  },
162
162
 
163
- renderCall(args, theme) {
163
+ renderCall(args, theme, _context) {
164
164
  let text = theme.fg("toolTitle", theme.bold("edit "));
165
165
  text += theme.fg("accent", args.path);
166
166
  return new Text(text, 0, 0);
167
167
  },
168
168
 
169
- renderResult(result, { expanded, isPartial }, theme) {
169
+ renderResult(result, { expanded, isPartial }, theme, _context) {
170
170
  if (isPartial) return new Text(theme.fg("warning", "Editing..."), 0, 0);
171
171
 
172
172
  const details = result.details as EditToolDetails | undefined;
@@ -224,7 +224,7 @@ export default function (pi: ExtensionAPI) {
224
224
  return originalWrite.execute(toolCallId, params, signal, onUpdate);
225
225
  },
226
226
 
227
- renderCall(args, theme) {
227
+ renderCall(args, theme, _context) {
228
228
  let text = theme.fg("toolTitle", theme.bold("write "));
229
229
  text += theme.fg("accent", args.path);
230
230
  const lineCount = args.content.split("\n").length;
@@ -232,7 +232,7 @@ export default function (pi: ExtensionAPI) {
232
232
  return new Text(text, 0, 0);
233
233
  },
234
234
 
235
- renderResult(result, { isPartial }, theme) {
235
+ renderResult(result, { isPartial }, theme, _context) {
236
236
  if (isPartial) return new Text(theme.fg("warning", "Writing..."), 0, 0);
237
237
 
238
238
  const content = result.content[0];
@@ -60,10 +60,10 @@ export default function commandsExtension(pi: ExtensionAPI) {
60
60
  if (selected && !selected.startsWith("---")) {
61
61
  const cmdName = selected.split(" - ")[0].slice(1); // Remove leading /
62
62
  const cmd = commands.find((c) => c.name === cmdName);
63
- if (cmd?.path) {
64
- const showPath = await ctx.ui.confirm(cmd.name, `View source path?\n${cmd.path}`);
63
+ if (cmd?.sourceInfo.path) {
64
+ const showPath = await ctx.ui.confirm(cmd.name, `View source path?\n${cmd.sourceInfo.path}`);
65
65
  if (showPath) {
66
- ctx.ui.notify(cmd.path, "info");
66
+ ctx.ui.notify(cmd.sourceInfo.path, "info");
67
67
  }
68
68
  }
69
69
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "0.62.1",
3
+ "version": "1.14.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "0.62.1",
9
+ "version": "1.14.0",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,19 +1,19 @@
1
1
  {
2
- "name": "pi-extension-custom-provider-anthropic",
3
- "private": true,
4
- "version": "0.62.1",
5
- "type": "module",
6
- "scripts": {
7
- "clean": "echo 'nothing to clean'",
8
- "build": "echo 'nothing to build'",
9
- "check": "echo 'nothing to check'"
10
- },
11
- "pi": {
12
- "extensions": [
13
- "./index.ts"
14
- ]
15
- },
16
- "dependencies": {
17
- "@anthropic-ai/sdk": "^0.52.0"
18
- }
2
+ "name": "pi-extension-custom-provider-anthropic",
3
+ "private": true,
4
+ "version": "1.14.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "clean": "echo 'nothing to clean'",
8
+ "build": "echo 'nothing to build'",
9
+ "check": "echo 'nothing to check'"
10
+ },
11
+ "pi": {
12
+ "extensions": [
13
+ "./index.ts"
14
+ ]
15
+ },
16
+ "dependencies": {
17
+ "@anthropic-ai/sdk": "^0.52.0"
18
+ }
19
19
  }
@@ -1,16 +1,16 @@
1
1
  {
2
- "name": "pi-extension-custom-provider-gitlab-duo",
3
- "private": true,
4
- "version": "0.62.1",
5
- "type": "module",
6
- "scripts": {
7
- "clean": "echo 'nothing to clean'",
8
- "build": "echo 'nothing to build'",
9
- "check": "echo 'nothing to check'"
10
- },
11
- "pi": {
12
- "extensions": [
13
- "./index.ts"
14
- ]
15
- }
2
+ "name": "pi-extension-custom-provider-gitlab-duo",
3
+ "private": true,
4
+ "version": "1.14.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "clean": "echo 'nothing to clean'",
8
+ "build": "echo 'nothing to build'",
9
+ "check": "echo 'nothing to check'"
10
+ },
11
+ "pi": {
12
+ "extensions": [
13
+ "./index.ts"
14
+ ]
15
+ }
16
16
  }
@@ -1,16 +1,16 @@
1
1
  {
2
- "name": "pi-extension-custom-provider-qwen-cli",
3
- "private": true,
4
- "version": "0.62.1",
5
- "type": "module",
6
- "scripts": {
7
- "clean": "echo 'nothing to clean'",
8
- "build": "echo 'nothing to build'",
9
- "check": "echo 'nothing to check'"
10
- },
11
- "pi": {
12
- "extensions": [
13
- "./index.ts"
14
- ]
15
- }
2
+ "name": "pi-extension-custom-provider-qwen-cli",
3
+ "private": true,
4
+ "version": "1.13.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "clean": "echo 'nothing to clean'",
8
+ "build": "echo 'nothing to build'",
9
+ "check": "echo 'nothing to check'"
10
+ },
11
+ "pi": {
12
+ "extensions": [
13
+ "./index.ts"
14
+ ]
15
+ }
16
16
  }
@@ -80,7 +80,7 @@ export default function (pi: ExtensionAPI) {
80
80
  return tools.read.execute(toolCallId, params, signal, onUpdate);
81
81
  },
82
82
 
83
- renderCall(args, theme) {
83
+ renderCall(args, theme, _context) {
84
84
  const path = shortenPath(args.path || "");
85
85
  let pathDisplay = path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
86
86
 
@@ -94,7 +94,7 @@ export default function (pi: ExtensionAPI) {
94
94
  return new Text(`${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}`, 0, 0);
95
95
  },
96
96
 
97
- renderResult(result, { expanded }, theme) {
97
+ renderResult(result, { expanded }, theme, _context) {
98
98
  // Minimal mode: show nothing in collapsed state
99
99
  if (!expanded) {
100
100
  return new Text("", 0, 0);
@@ -127,7 +127,7 @@ export default function (pi: ExtensionAPI) {
127
127
  return tools.bash.execute(toolCallId, params, signal, onUpdate);
128
128
  },
129
129
 
130
- renderCall(args, theme) {
130
+ renderCall(args, theme, _context) {
131
131
  const command = args.command || "...";
132
132
  const timeout = args.timeout as number | undefined;
133
133
  const timeoutSuffix = timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
@@ -135,7 +135,7 @@ export default function (pi: ExtensionAPI) {
135
135
  return new Text(theme.fg("toolTitle", theme.bold(`$ ${command}`)) + timeoutSuffix, 0, 0);
136
136
  },
137
137
 
138
- renderResult(result, { expanded }, theme) {
138
+ renderResult(result, { expanded }, theme, _context) {
139
139
  // Minimal mode: show nothing in collapsed state
140
140
  if (!expanded) {
141
141
  return new Text("", 0, 0);
@@ -176,7 +176,7 @@ export default function (pi: ExtensionAPI) {
176
176
  return tools.write.execute(toolCallId, params, signal, onUpdate);
177
177
  },
178
178
 
179
- renderCall(args, theme) {
179
+ renderCall(args, theme, _context) {
180
180
  const path = shortenPath(args.path || "");
181
181
  const pathDisplay = path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
182
182
  const lineCount = args.content ? args.content.split("\n").length : 0;
@@ -185,7 +185,7 @@ export default function (pi: ExtensionAPI) {
185
185
  return new Text(`${theme.fg("toolTitle", theme.bold("write"))} ${pathDisplay}${lineInfo}`, 0, 0);
186
186
  },
187
187
 
188
- renderResult(result, { expanded }, theme) {
188
+ renderResult(result, { expanded }, theme, _context) {
189
189
  // Minimal mode: show nothing (file was written)
190
190
  if (!expanded) {
191
191
  return new Text("", 0, 0);
@@ -218,14 +218,14 @@ export default function (pi: ExtensionAPI) {
218
218
  return tools.edit.execute(toolCallId, params, signal, onUpdate);
219
219
  },
220
220
 
221
- renderCall(args, theme) {
221
+ renderCall(args, theme, _context) {
222
222
  const path = shortenPath(args.path || "");
223
223
  const pathDisplay = path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
224
224
 
225
225
  return new Text(`${theme.fg("toolTitle", theme.bold("edit"))} ${pathDisplay}`, 0, 0);
226
226
  },
227
227
 
228
- renderResult(result, { expanded }, theme) {
228
+ renderResult(result, { expanded }, theme, _context) {
229
229
  // Minimal mode: show nothing in collapsed state
230
230
  if (!expanded) {
231
231
  return new Text("", 0, 0);
@@ -263,7 +263,7 @@ export default function (pi: ExtensionAPI) {
263
263
  return tools.find.execute(toolCallId, params, signal, onUpdate);
264
264
  },
265
265
 
266
- renderCall(args, theme) {
266
+ renderCall(args, theme, _context) {
267
267
  const pattern = args.pattern || "";
268
268
  const path = shortenPath(args.path || ".");
269
269
  const limit = args.limit;
@@ -277,7 +277,7 @@ export default function (pi: ExtensionAPI) {
277
277
  return new Text(text, 0, 0);
278
278
  },
279
279
 
280
- renderResult(result, { expanded }, theme) {
280
+ renderResult(result, { expanded }, theme, _context) {
281
281
  if (!expanded) {
282
282
  // Minimal: just show count
283
283
  const textContent = result.content.find((c) => c.type === "text");
@@ -321,7 +321,7 @@ export default function (pi: ExtensionAPI) {
321
321
  return tools.grep.execute(toolCallId, params, signal, onUpdate);
322
322
  },
323
323
 
324
- renderCall(args, theme) {
324
+ renderCall(args, theme, _context) {
325
325
  const pattern = args.pattern || "";
326
326
  const path = shortenPath(args.path || ".");
327
327
  const glob = args.glob;
@@ -339,7 +339,7 @@ export default function (pi: ExtensionAPI) {
339
339
  return new Text(text, 0, 0);
340
340
  },
341
341
 
342
- renderResult(result, { expanded }, theme) {
342
+ renderResult(result, { expanded }, theme, _context) {
343
343
  if (!expanded) {
344
344
  // Minimal: just show match count
345
345
  const textContent = result.content.find((c) => c.type === "text");
@@ -383,7 +383,7 @@ export default function (pi: ExtensionAPI) {
383
383
  return tools.ls.execute(toolCallId, params, signal, onUpdate);
384
384
  },
385
385
 
386
- renderCall(args, theme) {
386
+ renderCall(args, theme, _context) {
387
387
  const path = shortenPath(args.path || ".");
388
388
  const limit = args.limit;
389
389
 
@@ -395,7 +395,7 @@ export default function (pi: ExtensionAPI) {
395
395
  return new Text(text, 0, 0);
396
396
  },
397
397
 
398
- renderResult(result, { expanded }, theme) {
398
+ renderResult(result, { expanded }, theme, _context) {
399
399
  if (!expanded) {
400
400
  // Minimal: just show entry count
401
401
  const textContent = result.content.find((c) => c.type === "text");
@@ -227,7 +227,7 @@ export default function question(pi: ExtensionAPI) {
227
227
  };
228
228
  },
229
229
 
230
- renderCall(args, theme) {
230
+ renderCall(args, theme, _context) {
231
231
  let text = theme.fg("toolTitle", theme.bold("question ")) + theme.fg("muted", args.question);
232
232
  const opts = Array.isArray(args.options) ? args.options : [];
233
233
  if (opts.length) {
@@ -238,7 +238,7 @@ export default function question(pi: ExtensionAPI) {
238
238
  return new Text(text, 0, 0);
239
239
  },
240
240
 
241
- renderResult(result, _options, theme) {
241
+ renderResult(result, _options, theme, _context) {
242
242
  const details = result.details as QuestionDetails | undefined;
243
243
  if (!details) {
244
244
  const text = result.content[0];
@@ -393,7 +393,7 @@ export default function questionnaire(pi: ExtensionAPI) {
393
393
  };
394
394
  },
395
395
 
396
- renderCall(args, theme) {
396
+ renderCall(args, theme, _context) {
397
397
  const qs = (args.questions as Question[]) || [];
398
398
  const count = qs.length;
399
399
  const labels = qs.map((q) => q.label || q.id).join(", ");
@@ -405,7 +405,7 @@ export default function questionnaire(pi: ExtensionAPI) {
405
405
  return new Text(text, 0, 0);
406
406
  },
407
407
 
408
- renderResult(result, _options, theme) {
408
+ renderResult(result, _options, theme, _context) {
409
409
  const details = result.details as QuestionnaireResult | undefined;
410
410
  if (!details) {
411
411
  const text = result.content[0];
@@ -217,6 +217,21 @@ async function writePromptToTempFile(agentName: string, prompt: string): Promise
217
217
  return { dir: tmpDir, filePath };
218
218
  }
219
219
 
220
+ function getPiInvocation(args: string[]): { command: string; args: string[] } {
221
+ const currentScript = process.argv[1];
222
+ if (currentScript && fs.existsSync(currentScript)) {
223
+ return { command: process.execPath, args: [currentScript, ...args] };
224
+ }
225
+
226
+ const execName = path.basename(process.execPath).toLowerCase();
227
+ const isGenericRuntime = /^(node|bun)(\.exe)?$/.test(execName);
228
+ if (!isGenericRuntime) {
229
+ return { command: process.execPath, args };
230
+ }
231
+
232
+ return { command: "pi", args };
233
+ }
234
+
220
235
  type OnUpdateCallback = (partial: AgentToolResult<SubagentDetails>) => void;
221
236
 
222
237
  async function runSingleAgent(
@@ -286,7 +301,12 @@ async function runSingleAgent(
286
301
  let wasAborted = false;
287
302
 
288
303
  const exitCode = await new Promise<number>((resolve) => {
289
- const proc = spawn("pi", args, { cwd: cwd ?? defaultCwd, shell: false, stdio: ["ignore", "pipe", "pipe"] });
304
+ const invocation = getPiInvocation(args);
305
+ const proc = spawn(invocation.command, invocation.args, {
306
+ cwd: cwd ?? defaultCwd,
307
+ shell: false,
308
+ stdio: ["ignore", "pipe", "pipe"],
309
+ });
290
310
  let buffer = "";
291
311
 
292
312
  const processLine = (line: string) => {
@@ -648,7 +668,7 @@ export default function (pi: ExtensionAPI) {
648
668
  };
649
669
  },
650
670
 
651
- renderCall(args, theme) {
671
+ renderCall(args, theme, _context) {
652
672
  const scope: AgentScope = args.agentScope ?? "user";
653
673
  if (args.chain && args.chain.length > 0) {
654
674
  let text =
@@ -692,7 +712,7 @@ export default function (pi: ExtensionAPI) {
692
712
  return new Text(text, 0, 0);
693
713
  },
694
714
 
695
- renderResult(result, { expanded }, theme) {
715
+ renderResult(result, { expanded }, theme, _context) {
696
716
  const details = result.details as SubagentDetails | undefined;
697
717
  if (!details || details.results.length === 0) {
698
718
  const text = result.content[0];
@@ -220,14 +220,14 @@ export default function (pi: ExtensionAPI) {
220
220
  }
221
221
  },
222
222
 
223
- renderCall(args, theme) {
223
+ renderCall(args, theme, _context) {
224
224
  let text = theme.fg("toolTitle", theme.bold("todo ")) + theme.fg("muted", args.action);
225
225
  if (args.text) text += ` ${theme.fg("dim", `"${args.text}"`)}`;
226
226
  if (args.id !== undefined) text += ` ${theme.fg("accent", `#${args.id}`)}`;
227
227
  return new Text(text, 0, 0);
228
228
  },
229
229
 
230
- renderResult(result, { expanded }, theme) {
230
+ renderResult(result, { expanded }, theme, _context) {
231
231
  const details = result.details as TodoDetails | undefined;
232
232
  if (!details) {
233
233
  const text = result.content[0];