@phi-code-admin/phi-code 0.74.2 → 0.75.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 (656) hide show
  1. package/CHANGELOG.md +1186 -4
  2. package/README.md +478 -379
  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 +9 -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/bun/restore-sandbox-env.d.ts +13 -0
  12. package/dist/bun/restore-sandbox-env.d.ts.map +1 -0
  13. package/dist/bun/restore-sandbox-env.js +32 -0
  14. package/dist/bun/restore-sandbox-env.js.map +1 -0
  15. package/dist/cli/args.d.ts +12 -7
  16. package/dist/cli/args.d.ts.map +1 -1
  17. package/dist/cli/args.js +87 -45
  18. package/dist/cli/args.js.map +1 -1
  19. package/dist/cli/config-selector.d.ts.map +1 -1
  20. package/dist/cli/config-selector.js.map +1 -1
  21. package/dist/cli/file-processor.d.ts.map +1 -1
  22. package/dist/cli/file-processor.js +4 -0
  23. package/dist/cli/file-processor.js.map +1 -1
  24. package/dist/cli/initial-message.d.ts +18 -0
  25. package/dist/cli/initial-message.d.ts.map +1 -0
  26. package/dist/cli/initial-message.js +22 -0
  27. package/dist/cli/initial-message.js.map +1 -0
  28. package/dist/cli/list-models.d.ts.map +1 -1
  29. package/dist/cli/list-models.js +7 -1
  30. package/dist/cli/list-models.js.map +1 -1
  31. package/dist/cli/session-picker.d.ts.map +1 -1
  32. package/dist/cli/session-picker.js +2 -1
  33. package/dist/cli/session-picker.js.map +1 -1
  34. package/dist/cli.d.ts.map +1 -1
  35. package/dist/cli.js +9 -5
  36. package/dist/cli.js.map +1 -1
  37. package/dist/config.d.ts +24 -0
  38. package/dist/config.d.ts.map +1 -1
  39. package/dist/config.js +226 -30
  40. package/dist/config.js.map +1 -1
  41. package/dist/core/agent-session-runtime.d.ts +117 -0
  42. package/dist/core/agent-session-runtime.d.ts.map +1 -0
  43. package/dist/core/agent-session-runtime.js +300 -0
  44. package/dist/core/agent-session-runtime.js.map +1 -0
  45. package/dist/core/agent-session-services.d.ts +86 -0
  46. package/dist/core/agent-session-services.d.ts.map +1 -0
  47. package/dist/core/agent-session-services.js +117 -0
  48. package/dist/core/agent-session-services.js.map +1 -0
  49. package/dist/core/agent-session.d.ts +63 -82
  50. package/dist/core/agent-session.d.ts.map +1 -1
  51. package/dist/core/agent-session.js +674 -628
  52. package/dist/core/agent-session.js.map +1 -1
  53. package/dist/core/api-key-store.d.ts +87 -0
  54. package/dist/core/api-key-store.d.ts.map +1 -0
  55. package/dist/core/api-key-store.js +168 -0
  56. package/dist/core/api-key-store.js.map +1 -0
  57. package/dist/core/auth-guidance.d.ts +5 -0
  58. package/dist/core/auth-guidance.d.ts.map +1 -0
  59. package/dist/core/auth-guidance.js +21 -0
  60. package/dist/core/auth-guidance.js.map +1 -0
  61. package/dist/core/auth-storage.d.ts +12 -5
  62. package/dist/core/auth-storage.d.ts.map +1 -1
  63. package/dist/core/auth-storage.js +34 -8
  64. package/dist/core/auth-storage.js.map +1 -1
  65. package/dist/core/bash-executor.d.ts +0 -15
  66. package/dist/core/bash-executor.d.ts.map +1 -1
  67. package/dist/core/bash-executor.js +28 -129
  68. package/dist/core/bash-executor.js.map +1 -1
  69. package/dist/core/compaction/branch-summarization.d.ts +2 -0
  70. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  71. package/dist/core/compaction/branch-summarization.js +3 -2
  72. package/dist/core/compaction/branch-summarization.js.map +1 -1
  73. package/dist/core/compaction/compaction.d.ts +4 -4
  74. package/dist/core/compaction/compaction.d.ts.map +1 -1
  75. package/dist/core/compaction/compaction.js +32 -27
  76. package/dist/core/compaction/compaction.js.map +1 -1
  77. package/dist/core/compaction/index.d.ts.map +1 -1
  78. package/dist/core/compaction/utils.d.ts.map +1 -1
  79. package/dist/core/compaction/utils.js.map +1 -1
  80. package/dist/core/config-watcher.d.ts +47 -0
  81. package/dist/core/config-watcher.d.ts.map +1 -0
  82. package/dist/core/config-watcher.js +135 -0
  83. package/dist/core/config-watcher.js.map +1 -0
  84. package/dist/core/default-models.json +80 -0
  85. package/dist/core/defaults.d.ts.map +1 -1
  86. package/dist/core/diagnostics.d.ts.map +1 -1
  87. package/dist/core/event-bus.d.ts.map +1 -1
  88. package/dist/core/event-bus.js.map +1 -1
  89. package/dist/core/exec.d.ts.map +1 -1
  90. package/dist/core/exec.js +7 -3
  91. package/dist/core/exec.js.map +1 -1
  92. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
  93. package/dist/core/export-html/ansi-to-html.js +1 -1
  94. package/dist/core/export-html/ansi-to-html.js.map +1 -1
  95. package/dist/core/export-html/index.d.ts +7 -4
  96. package/dist/core/export-html/index.d.ts.map +1 -1
  97. package/dist/core/export-html/index.js +15 -13
  98. package/dist/core/export-html/index.js.map +1 -1
  99. package/dist/core/export-html/template.css +112 -17
  100. package/dist/core/export-html/template.html +1 -0
  101. package/dist/core/export-html/template.js +312 -64
  102. package/dist/core/export-html/tool-renderer.d.ts +9 -10
  103. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  104. package/dist/core/export-html/tool-renderer.js +61 -16
  105. package/dist/core/export-html/tool-renderer.js.map +1 -1
  106. package/dist/core/extensions/index.d.ts +5 -4
  107. package/dist/core/extensions/index.d.ts.map +1 -1
  108. package/dist/core/extensions/index.js +2 -2
  109. package/dist/core/extensions/index.js.map +1 -1
  110. package/dist/core/extensions/loader.d.ts +0 -1
  111. package/dist/core/extensions/loader.d.ts.map +1 -1
  112. package/dist/core/extensions/loader.js +98 -18
  113. package/dist/core/extensions/loader.js.map +1 -1
  114. package/dist/core/extensions/runner.d.ts +27 -14
  115. package/dist/core/extensions/runner.d.ts.map +1 -1
  116. package/dist/core/extensions/runner.js +299 -115
  117. package/dist/core/extensions/runner.js.map +1 -1
  118. package/dist/core/extensions/types.d.ts +200 -44
  119. package/dist/core/extensions/types.d.ts.map +1 -1
  120. package/dist/core/extensions/types.js +10 -0
  121. package/dist/core/extensions/types.js.map +1 -1
  122. package/dist/core/extensions/wrapper.d.ts +4 -11
  123. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  124. package/dist/core/extensions/wrapper.js +7 -87
  125. package/dist/core/extensions/wrapper.js.map +1 -1
  126. package/dist/core/footer-data-provider.d.ts +22 -2
  127. package/dist/core/footer-data-provider.d.ts.map +1 -1
  128. package/dist/core/footer-data-provider.js +225 -49
  129. package/dist/core/footer-data-provider.js.map +1 -1
  130. package/dist/core/index.d.ts +5 -2
  131. package/dist/core/index.d.ts.map +1 -1
  132. package/dist/core/index.js +5 -2
  133. package/dist/core/index.js.map +1 -1
  134. package/dist/core/keybindings.d.ts +348 -50
  135. package/dist/core/keybindings.d.ts.map +1 -1
  136. package/dist/core/keybindings.js +276 -132
  137. package/dist/core/keybindings.js.map +1 -1
  138. package/dist/core/messages.d.ts.map +1 -1
  139. package/dist/core/messages.js.map +1 -1
  140. package/dist/core/model-registry.d.ts +41 -5
  141. package/dist/core/model-registry.d.ts.map +1 -1
  142. package/dist/core/model-registry.js +316 -136
  143. package/dist/core/model-registry.js.map +1 -1
  144. package/dist/core/model-resolver.d.ts +6 -0
  145. package/dist/core/model-resolver.d.ts.map +1 -1
  146. package/dist/core/model-resolver.js +70 -37
  147. package/dist/core/model-resolver.js.map +1 -1
  148. package/dist/core/output-guard.d.ts +6 -0
  149. package/dist/core/output-guard.d.ts.map +1 -0
  150. package/dist/core/output-guard.js +59 -0
  151. package/dist/core/output-guard.js.map +1 -0
  152. package/dist/core/package-manager.d.ts +49 -7
  153. package/dist/core/package-manager.d.ts.map +1 -1
  154. package/dist/core/package-manager.js +655 -122
  155. package/dist/core/package-manager.js.map +1 -1
  156. package/dist/core/prompt-templates.d.ts +12 -10
  157. package/dist/core/prompt-templates.d.ts.map +1 -1
  158. package/dist/core/prompt-templates.js +37 -38
  159. package/dist/core/prompt-templates.js.map +1 -1
  160. package/dist/core/provider-display-names.d.ts +2 -0
  161. package/dist/core/provider-display-names.d.ts.map +1 -0
  162. package/dist/core/provider-display-names.js +33 -0
  163. package/dist/core/provider-display-names.js.map +1 -0
  164. package/dist/core/resolve-config-value.d.ts +6 -0
  165. package/dist/core/resolve-config-value.d.ts.map +1 -1
  166. package/dist/core/resolve-config-value.js +75 -8
  167. package/dist/core/resolve-config-value.js.map +1 -1
  168. package/dist/core/resource-loader.d.ts +18 -8
  169. package/dist/core/resource-loader.d.ts.map +1 -1
  170. package/dist/core/resource-loader.js +217 -123
  171. package/dist/core/resource-loader.js.map +1 -1
  172. package/dist/core/sdk.d.ts +25 -8
  173. package/dist/core/sdk.d.ts.map +1 -1
  174. package/dist/core/sdk.js +84 -37
  175. package/dist/core/sdk.js.map +1 -1
  176. package/dist/core/session-cwd.d.ts +19 -0
  177. package/dist/core/session-cwd.d.ts.map +1 -0
  178. package/dist/core/session-cwd.js +38 -0
  179. package/dist/core/session-cwd.js.map +1 -0
  180. package/dist/core/session-manager.d.ts +11 -1
  181. package/dist/core/session-manager.d.ts.map +1 -1
  182. package/dist/core/session-manager.js +42 -27
  183. package/dist/core/session-manager.js.map +1 -1
  184. package/dist/core/settings-manager.d.ts +34 -5
  185. package/dist/core/settings-manager.d.ts.map +1 -1
  186. package/dist/core/settings-manager.js +113 -13
  187. package/dist/core/settings-manager.js.map +1 -1
  188. package/dist/core/skills.d.ts +13 -11
  189. package/dist/core/skills.d.ts.map +1 -1
  190. package/dist/core/skills.js +59 -19
  191. package/dist/core/skills.js.map +1 -1
  192. package/dist/core/slash-commands.d.ts +2 -3
  193. package/dist/core/slash-commands.d.ts.map +1 -1
  194. package/dist/core/slash-commands.js +9 -6
  195. package/dist/core/slash-commands.js.map +1 -1
  196. package/dist/core/source-info.d.ts +18 -0
  197. package/dist/core/source-info.d.ts.map +1 -0
  198. package/dist/core/source-info.js +19 -0
  199. package/dist/core/source-info.js.map +1 -0
  200. package/dist/core/system-prompt.d.ts +3 -3
  201. package/dist/core/system-prompt.d.ts.map +1 -1
  202. package/dist/core/system-prompt.js +16 -55
  203. package/dist/core/system-prompt.js.map +1 -1
  204. package/dist/core/telemetry.d.ts +3 -0
  205. package/dist/core/telemetry.d.ts.map +1 -0
  206. package/dist/core/telemetry.js +9 -0
  207. package/dist/core/telemetry.js.map +1 -0
  208. package/dist/core/timings.d.ts +1 -0
  209. package/dist/core/timings.d.ts.map +1 -1
  210. package/dist/core/timings.js +6 -0
  211. package/dist/core/timings.js.map +1 -1
  212. package/dist/core/tools/bash.d.ts +27 -14
  213. package/dist/core/tools/bash.d.ts.map +1 -1
  214. package/dist/core/tools/bash.js +301 -208
  215. package/dist/core/tools/bash.js.map +1 -1
  216. package/dist/core/tools/edit-diff.d.ts +23 -1
  217. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  218. package/dist/core/tools/edit-diff.js +154 -59
  219. package/dist/core/tools/edit-diff.js.map +1 -1
  220. package/dist/core/tools/edit.d.ts +22 -12
  221. package/dist/core/tools/edit.d.ts.map +1 -1
  222. package/dist/core/tools/edit.js +243 -65
  223. package/dist/core/tools/edit.js.map +1 -1
  224. package/dist/core/tools/file-mutation-queue.d.ts +6 -0
  225. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
  226. package/dist/core/tools/file-mutation-queue.js +37 -0
  227. package/dist/core/tools/file-mutation-queue.js.map +1 -0
  228. package/dist/core/tools/find.d.ts +10 -14
  229. package/dist/core/tools/find.d.ts.map +1 -1
  230. package/dist/core/tools/find.js +202 -110
  231. package/dist/core/tools/find.js.map +1 -1
  232. package/dist/core/tools/grep.d.ts +14 -22
  233. package/dist/core/tools/grep.d.ts.map +1 -1
  234. package/dist/core/tools/grep.js +100 -35
  235. package/dist/core/tools/grep.js.map +1 -1
  236. package/dist/core/tools/index.d.ts +27 -60
  237. package/dist/core/tools/index.d.ts.map +1 -1
  238. package/dist/core/tools/index.js +96 -45
  239. package/dist/core/tools/index.js.map +1 -1
  240. package/dist/core/tools/ls.d.ts +8 -11
  241. package/dist/core/tools/ls.d.ts.map +1 -1
  242. package/dist/core/tools/ls.js +66 -15
  243. package/dist/core/tools/ls.js.map +1 -1
  244. package/dist/core/tools/output-accumulator.d.ts +50 -0
  245. package/dist/core/tools/output-accumulator.d.ts.map +1 -0
  246. package/dist/core/tools/output-accumulator.js +178 -0
  247. package/dist/core/tools/output-accumulator.js.map +1 -0
  248. package/dist/core/tools/path-utils.d.ts.map +1 -1
  249. package/dist/core/tools/path-utils.js +1 -1
  250. package/dist/core/tools/path-utils.js.map +1 -1
  251. package/dist/core/tools/read.d.ts +9 -13
  252. package/dist/core/tools/read.d.ts.map +1 -1
  253. package/dist/core/tools/read.js +175 -52
  254. package/dist/core/tools/read.js.map +1 -1
  255. package/dist/core/tools/render-utils.d.ts +21 -0
  256. package/dist/core/tools/render-utils.d.ts.map +1 -0
  257. package/dist/core/tools/render-utils.js +49 -0
  258. package/dist/core/tools/render-utils.js.map +1 -0
  259. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  260. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  261. package/dist/core/tools/tool-definition-wrapper.js +34 -0
  262. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  263. package/dist/core/tools/truncate.d.ts.map +1 -1
  264. package/dist/core/tools/truncate.js.map +1 -1
  265. package/dist/core/tools/write.d.ts +8 -11
  266. package/dist/core/tools/write.d.ts.map +1 -1
  267. package/dist/core/tools/write.js +167 -32
  268. package/dist/core/tools/write.js.map +1 -1
  269. package/dist/index.d.ts +12 -9
  270. package/dist/index.d.ts.map +1 -1
  271. package/dist/index.js +12 -10
  272. package/dist/index.js.map +1 -1
  273. package/dist/main.d.ts +5 -1
  274. package/dist/main.d.ts.map +1 -1
  275. package/dist/main.js +326 -404
  276. package/dist/main.js.map +1 -1
  277. package/dist/migrations.d.ts +2 -2
  278. package/dist/migrations.d.ts.map +1 -1
  279. package/dist/migrations.js +24 -4
  280. package/dist/migrations.js.map +1 -1
  281. package/dist/modes/index.d.ts.map +1 -1
  282. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  283. package/dist/modes/interactive/components/armin.js +10 -6
  284. package/dist/modes/interactive/components/armin.js.map +1 -1
  285. package/dist/modes/interactive/components/assistant-message.d.ts +5 -1
  286. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  287. package/dist/modes/interactive/components/assistant-message.js +32 -3
  288. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  289. package/dist/modes/interactive/components/bash-execution.d.ts +0 -1
  290. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  291. package/dist/modes/interactive/components/bash-execution.js +31 -12
  292. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  293. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  294. package/dist/modes/interactive/components/bordered-loader.js +7 -1
  295. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  296. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  297. package/dist/modes/interactive/components/branch-summary-message.js +5 -3
  298. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  299. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  300. package/dist/modes/interactive/components/compaction-summary-message.js +5 -3
  301. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  302. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  303. package/dist/modes/interactive/components/config-selector.js +49 -16
  304. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  305. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  306. package/dist/modes/interactive/components/countdown-timer.js +5 -0
  307. package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
  308. package/dist/modes/interactive/components/custom-editor.d.ts +3 -3
  309. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  310. package/dist/modes/interactive/components/custom-editor.js +14 -7
  311. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  312. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  313. package/dist/modes/interactive/components/custom-message.js +6 -1
  314. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  315. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  316. package/dist/modes/interactive/components/daxnuts.js +8 -6
  317. package/dist/modes/interactive/components/daxnuts.js.map +1 -1
  318. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  319. package/dist/modes/interactive/components/diff.js.map +1 -1
  320. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  321. package/dist/modes/interactive/components/dynamic-border.js +1 -0
  322. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  323. package/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
  324. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -0
  325. package/dist/modes/interactive/components/earendil-announcement.js +40 -0
  326. package/dist/modes/interactive/components/earendil-announcement.js.map +1 -0
  327. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  328. package/dist/modes/interactive/components/extension-editor.js +16 -10
  329. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  330. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  331. package/dist/modes/interactive/components/extension-input.js +13 -7
  332. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  333. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  334. package/dist/modes/interactive/components/extension-selector.js +18 -11
  335. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  336. package/dist/modes/interactive/components/footer.d.ts +1 -0
  337. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  338. package/dist/modes/interactive/components/footer.js +7 -2
  339. package/dist/modes/interactive/components/footer.js.map +1 -1
  340. package/dist/modes/interactive/components/index.d.ts +1 -1
  341. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  342. package/dist/modes/interactive/components/index.js +1 -1
  343. package/dist/modes/interactive/components/index.js.map +1 -1
  344. package/dist/modes/interactive/components/keybinding-hints.d.ts +8 -36
  345. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  346. package/dist/modes/interactive/components/keybinding-hints.js +23 -48
  347. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  348. package/dist/modes/interactive/components/login-dialog.d.ts +5 -1
  349. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  350. package/dist/modes/interactive/components/login-dialog.js +35 -14
  351. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  352. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  353. package/dist/modes/interactive/components/model-selector.js +41 -22
  354. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  355. package/dist/modes/interactive/components/oauth-selector.d.ts +18 -6
  356. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  357. package/dist/modes/interactive/components/oauth-selector.js +104 -31
  358. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  359. package/dist/modes/interactive/components/scoped-models-selector.d.ts +5 -12
  360. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  361. package/dist/modes/interactive/components/scoped-models-selector.js +61 -42
  362. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  363. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  364. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  365. package/dist/modes/interactive/components/session-selector.d.ts +2 -1
  366. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  367. package/dist/modes/interactive/components/session-selector.js +109 -73
  368. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  369. package/dist/modes/interactive/components/settings-selector.d.ts +9 -0
  370. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  371. package/dist/modes/interactive/components/settings-selector.js +84 -4
  372. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  373. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  374. package/dist/modes/interactive/components/show-images-selector.js +6 -1
  375. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  376. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  377. package/dist/modes/interactive/components/skill-invocation-message.js +5 -3
  378. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  379. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  380. package/dist/modes/interactive/components/theme-selector.js +7 -1
  381. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  382. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  383. package/dist/modes/interactive/components/thinking-selector.js +6 -1
  384. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  385. package/dist/modes/interactive/components/tool-execution.d.ts +20 -34
  386. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  387. package/dist/modes/interactive/components/tool-execution.js +158 -636
  388. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  389. package/dist/modes/interactive/components/tree-selector.d.ts +21 -2
  390. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  391. package/dist/modes/interactive/components/tree-selector.js +224 -52
  392. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  393. package/dist/modes/interactive/components/user-message-selector.d.ts +2 -2
  394. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  395. package/dist/modes/interactive/components/user-message-selector.js +20 -16
  396. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  397. package/dist/modes/interactive/components/user-message.d.ts +1 -0
  398. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  399. package/dist/modes/interactive/components/user-message.js +8 -6
  400. package/dist/modes/interactive/components/user-message.js.map +1 -1
  401. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  402. package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
  403. package/dist/modes/interactive/interactive-mode.d.ts +67 -39
  404. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  405. package/dist/modes/interactive/interactive-mode.js +1556 -680
  406. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  407. package/dist/modes/interactive/theme/dark.json +1 -1
  408. package/dist/modes/interactive/theme/light.json +1 -1
  409. package/dist/modes/interactive/theme/theme.d.ts +3 -0
  410. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  411. package/dist/modes/interactive/theme/theme.js +101 -72
  412. package/dist/modes/interactive/theme/theme.js.map +1 -1
  413. package/dist/modes/print-mode.d.ts +2 -2
  414. package/dist/modes/print-mode.d.ts.map +1 -1
  415. package/dist/modes/print-mode.js +107 -77
  416. package/dist/modes/print-mode.js.map +1 -1
  417. package/dist/modes/rpc/jsonl.d.ts +17 -0
  418. package/dist/modes/rpc/jsonl.d.ts.map +1 -0
  419. package/dist/modes/rpc/jsonl.js +49 -0
  420. package/dist/modes/rpc/jsonl.js.map +1 -0
  421. package/dist/modes/rpc/rpc-client.d.ts +8 -1
  422. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  423. package/dist/modes/rpc/rpc-client.js +22 -16
  424. package/dist/modes/rpc/rpc-client.js.map +1 -1
  425. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  426. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  427. package/dist/modes/rpc/rpc-mode.js +184 -94
  428. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  429. package/dist/modes/rpc/rpc-types.d.ts +14 -4
  430. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  431. package/dist/modes/rpc/rpc-types.js.map +1 -1
  432. package/dist/package-manager-cli.d.ts +4 -0
  433. package/dist/package-manager-cli.d.ts.map +1 -0
  434. package/dist/package-manager-cli.js +460 -0
  435. package/dist/package-manager-cli.js.map +1 -0
  436. package/dist/utils/changelog.d.ts.map +1 -1
  437. package/dist/utils/changelog.js.map +1 -1
  438. package/dist/utils/child-process.d.ts +12 -0
  439. package/dist/utils/child-process.d.ts.map +1 -0
  440. package/dist/utils/child-process.js +86 -0
  441. package/dist/utils/child-process.js.map +1 -0
  442. package/dist/utils/clipboard-image.d.ts.map +1 -1
  443. package/dist/utils/clipboard-image.js +94 -11
  444. package/dist/utils/clipboard-image.js.map +1 -1
  445. package/dist/utils/clipboard-native.d.ts +1 -0
  446. package/dist/utils/clipboard-native.d.ts.map +1 -1
  447. package/dist/utils/clipboard-native.js.map +1 -1
  448. package/dist/utils/clipboard.d.ts +1 -1
  449. package/dist/utils/clipboard.d.ts.map +1 -1
  450. package/dist/utils/clipboard.js +96 -46
  451. package/dist/utils/clipboard.js.map +1 -1
  452. package/dist/utils/exif-orientation.d.ts +5 -0
  453. package/dist/utils/exif-orientation.d.ts.map +1 -0
  454. package/dist/utils/exif-orientation.js +158 -0
  455. package/dist/utils/exif-orientation.js.map +1 -0
  456. package/dist/utils/frontmatter.d.ts.map +1 -1
  457. package/dist/utils/frontmatter.js.map +1 -1
  458. package/dist/utils/fs-watch.d.ts +5 -0
  459. package/dist/utils/fs-watch.d.ts.map +1 -0
  460. package/dist/utils/fs-watch.js +25 -0
  461. package/dist/utils/fs-watch.js.map +1 -0
  462. package/dist/utils/git.d.ts.map +1 -1
  463. package/dist/utils/git.js.map +1 -1
  464. package/dist/utils/image-convert.d.ts.map +1 -1
  465. package/dist/utils/image-convert.js +5 -1
  466. package/dist/utils/image-convert.js.map +1 -1
  467. package/dist/utils/image-resize.d.ts +5 -5
  468. package/dist/utils/image-resize.d.ts.map +1 -1
  469. package/dist/utils/image-resize.js +51 -95
  470. package/dist/utils/image-resize.js.map +1 -1
  471. package/dist/utils/mime.d.ts.map +1 -1
  472. package/dist/utils/mime.js.map +1 -1
  473. package/dist/utils/paths.d.ts +16 -0
  474. package/dist/utils/paths.d.ts.map +1 -0
  475. package/dist/utils/paths.js +50 -0
  476. package/dist/utils/paths.js.map +1 -0
  477. package/dist/utils/photon.d.ts.map +1 -1
  478. package/dist/utils/photon.js.map +1 -1
  479. package/dist/utils/pi-user-agent.d.ts +2 -0
  480. package/dist/utils/pi-user-agent.d.ts.map +1 -0
  481. package/dist/utils/pi-user-agent.js +5 -0
  482. package/dist/utils/pi-user-agent.js.map +1 -0
  483. package/dist/utils/shell.d.ts +10 -6
  484. package/dist/utils/shell.d.ts.map +1 -1
  485. package/dist/utils/shell.js +29 -25
  486. package/dist/utils/shell.js.map +1 -1
  487. package/dist/utils/sleep.d.ts.map +1 -1
  488. package/dist/utils/sleep.js.map +1 -1
  489. package/dist/utils/tools-manager.d.ts.map +1 -1
  490. package/dist/utils/tools-manager.js +11 -6
  491. package/dist/utils/tools-manager.js.map +1 -1
  492. package/dist/utils/version-check.d.ts +14 -0
  493. package/dist/utils/version-check.d.ts.map +1 -0
  494. package/dist/utils/version-check.js +77 -0
  495. package/dist/utils/version-check.js.map +1 -0
  496. package/docs/compaction.md +394 -0
  497. package/docs/custom-provider.md +646 -0
  498. package/docs/development.md +71 -0
  499. package/docs/docs.json +148 -0
  500. package/docs/extensions.md +2596 -0
  501. package/docs/images/doom-extension.png +0 -0
  502. package/docs/images/exy.png +0 -0
  503. package/docs/images/interactive-mode.png +0 -0
  504. package/docs/images/tree-view.png +0 -0
  505. package/docs/index.md +70 -0
  506. package/docs/json.md +82 -0
  507. package/docs/keybindings.md +197 -0
  508. package/docs/models.md +474 -0
  509. package/docs/packages.md +223 -0
  510. package/docs/prompt-templates.md +88 -0
  511. package/docs/providers.md +243 -0
  512. package/docs/quickstart.md +142 -0
  513. package/docs/rpc.md +1407 -0
  514. package/docs/sdk.md +1149 -0
  515. package/docs/session-format.md +412 -0
  516. package/docs/sessions.md +137 -0
  517. package/docs/settings.md +279 -0
  518. package/docs/shell-aliases.md +13 -0
  519. package/docs/skills.md +232 -0
  520. package/docs/terminal-setup.md +106 -0
  521. package/docs/termux.md +127 -0
  522. package/docs/themes.md +295 -0
  523. package/docs/tmux.md +61 -0
  524. package/docs/tui.md +918 -0
  525. package/docs/usage.md +277 -0
  526. package/docs/windows.md +17 -0
  527. package/examples/README.md +25 -0
  528. package/examples/extensions/README.md +208 -0
  529. package/examples/extensions/auto-commit-on-exit.ts +49 -0
  530. package/examples/extensions/bash-spawn-hook.ts +30 -0
  531. package/examples/extensions/bookmark.ts +50 -0
  532. package/examples/extensions/border-status-editor.ts +150 -0
  533. package/examples/extensions/built-in-tool-renderer.ts +249 -0
  534. package/examples/extensions/claude-rules.ts +86 -0
  535. package/examples/extensions/commands.ts +72 -0
  536. package/examples/extensions/confirm-destructive.ts +59 -0
  537. package/examples/extensions/custom-compaction.ts +127 -0
  538. package/examples/extensions/custom-footer.ts +64 -0
  539. package/examples/extensions/custom-header.ts +73 -0
  540. package/examples/extensions/custom-provider-anthropic/index.ts +604 -0
  541. package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
  542. package/examples/extensions/custom-provider-anthropic/package.json +19 -0
  543. package/examples/extensions/custom-provider-gitlab-duo/index.ts +349 -0
  544. package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
  545. package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
  546. package/examples/extensions/dirty-repo-guard.ts +56 -0
  547. package/examples/extensions/doom-overlay/README.md +46 -0
  548. package/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
  549. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  550. package/examples/extensions/doom-overlay/doom/build.sh +152 -0
  551. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
  552. package/examples/extensions/doom-overlay/doom-component.ts +132 -0
  553. package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
  554. package/examples/extensions/doom-overlay/doom-keys.ts +104 -0
  555. package/examples/extensions/doom-overlay/index.ts +74 -0
  556. package/examples/extensions/doom-overlay/wad-finder.ts +51 -0
  557. package/examples/extensions/dynamic-resources/SKILL.md +8 -0
  558. package/examples/extensions/dynamic-resources/dynamic.json +79 -0
  559. package/examples/extensions/dynamic-resources/dynamic.md +5 -0
  560. package/examples/extensions/dynamic-resources/index.ts +15 -0
  561. package/examples/extensions/dynamic-tools.ts +74 -0
  562. package/examples/extensions/event-bus.ts +43 -0
  563. package/examples/extensions/file-trigger.ts +41 -0
  564. package/examples/extensions/git-checkpoint.ts +53 -0
  565. package/examples/extensions/github-issue-autocomplete.ts +185 -0
  566. package/examples/extensions/handoff.ts +191 -0
  567. package/examples/extensions/hello.ts +26 -0
  568. package/examples/extensions/hidden-thinking-label.ts +53 -0
  569. package/examples/extensions/inline-bash.ts +94 -0
  570. package/examples/extensions/input-transform.ts +43 -0
  571. package/examples/extensions/interactive-shell.ts +196 -0
  572. package/examples/extensions/mac-system-theme.ts +47 -0
  573. package/examples/extensions/message-renderer.ts +59 -0
  574. package/examples/extensions/minimal-mode.ts +426 -0
  575. package/examples/extensions/modal-editor.ts +85 -0
  576. package/examples/extensions/model-status.ts +31 -0
  577. package/examples/extensions/notify.ts +55 -0
  578. package/examples/extensions/overlay-qa-tests.ts +1348 -0
  579. package/examples/extensions/overlay-test.ts +150 -0
  580. package/examples/extensions/permission-gate.ts +34 -0
  581. package/examples/extensions/pirate.ts +47 -0
  582. package/examples/extensions/plan-mode/README.md +65 -0
  583. package/examples/extensions/plan-mode/index.ts +340 -0
  584. package/examples/extensions/plan-mode/utils.ts +168 -0
  585. package/examples/extensions/preset.ts +430 -0
  586. package/examples/extensions/prompt-customizer.ts +97 -0
  587. package/examples/extensions/protected-paths.ts +30 -0
  588. package/examples/extensions/provider-payload.ts +18 -0
  589. package/examples/extensions/qna.ts +122 -0
  590. package/examples/extensions/question.ts +264 -0
  591. package/examples/extensions/questionnaire.ts +427 -0
  592. package/examples/extensions/rainbow-editor.ts +88 -0
  593. package/examples/extensions/reload-runtime.ts +37 -0
  594. package/examples/extensions/rpc-demo.ts +118 -0
  595. package/examples/extensions/sandbox/index.ts +321 -0
  596. package/examples/extensions/sandbox/package-lock.json +92 -0
  597. package/examples/extensions/sandbox/package.json +19 -0
  598. package/examples/extensions/send-user-message.ts +97 -0
  599. package/examples/extensions/session-name.ts +27 -0
  600. package/examples/extensions/shutdown-command.ts +63 -0
  601. package/examples/extensions/snake.ts +343 -0
  602. package/examples/extensions/space-invaders.ts +560 -0
  603. package/examples/extensions/ssh.ts +220 -0
  604. package/examples/extensions/status-line.ts +32 -0
  605. package/examples/extensions/structured-output.ts +65 -0
  606. package/examples/extensions/subagent/README.md +172 -0
  607. package/examples/extensions/subagent/agents/planner.md +37 -0
  608. package/examples/extensions/subagent/agents/reviewer.md +35 -0
  609. package/examples/extensions/subagent/agents/scout.md +50 -0
  610. package/examples/extensions/subagent/agents/worker.md +24 -0
  611. package/examples/extensions/subagent/agents.ts +126 -0
  612. package/examples/extensions/subagent/index.ts +987 -0
  613. package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
  614. package/examples/extensions/subagent/prompts/implement.md +10 -0
  615. package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
  616. package/examples/extensions/summarize.ts +206 -0
  617. package/examples/extensions/system-prompt-header.ts +17 -0
  618. package/examples/extensions/tic-tac-toe.ts +1008 -0
  619. package/examples/extensions/timed-confirm.ts +70 -0
  620. package/examples/extensions/titlebar-spinner.ts +58 -0
  621. package/examples/extensions/todo.ts +297 -0
  622. package/examples/extensions/tool-override.ts +144 -0
  623. package/examples/extensions/tools.ts +141 -0
  624. package/examples/extensions/trigger-compact.ts +50 -0
  625. package/examples/extensions/truncated-tool.ts +195 -0
  626. package/examples/extensions/widget-placement.ts +9 -0
  627. package/examples/extensions/with-deps/index.ts +32 -0
  628. package/examples/extensions/with-deps/package-lock.json +31 -0
  629. package/examples/extensions/with-deps/package.json +22 -0
  630. package/examples/extensions/working-indicator.ts +123 -0
  631. package/examples/extensions/working-message-test.ts +25 -0
  632. package/examples/rpc-extension-ui.ts +632 -0
  633. package/examples/sdk/01-minimal.ts +22 -0
  634. package/examples/sdk/02-custom-model.ts +49 -0
  635. package/examples/sdk/03-custom-prompt.ts +62 -0
  636. package/examples/sdk/04-skills.ts +55 -0
  637. package/examples/sdk/05-tools.ts +44 -0
  638. package/examples/sdk/06-extensions.ts +90 -0
  639. package/examples/sdk/07-context-files.ts +42 -0
  640. package/examples/sdk/08-prompt-templates.ts +51 -0
  641. package/examples/sdk/09-api-keys-and-oauth.ts +48 -0
  642. package/examples/sdk/10-settings.ts +53 -0
  643. package/examples/sdk/11-sessions.ts +48 -0
  644. package/examples/sdk/12-full-control.ts +73 -0
  645. package/examples/sdk/13-session-runtime.ts +67 -0
  646. package/examples/sdk/README.md +147 -0
  647. package/extensions/phi/init.ts +15 -1
  648. package/extensions/phi/keys.ts +186 -0
  649. package/extensions/phi/providers/alibaba.ts +126 -0
  650. package/extensions/phi/providers/opencode-go.ts +204 -0
  651. package/extensions/phi/setup.ts +692 -0
  652. package/extensions/phi/smart-router.ts +8 -0
  653. package/extensions/phi/web-search.ts +432 -186
  654. package/package.json +111 -106
  655. package/scripts/copy-assets.sh +0 -0
  656. package/scripts/migrate-sessions.sh +0 -0
@@ -1,260 +1,153 @@
1
- import * as os from "node:os";
2
- import { Box, Container, getCapabilities, getImageDimensions, Image, imageFallback, Spacer, Text, truncateToWidth, } from "phi-code-tui";
3
- import stripAnsi from "strip-ansi";
4
- import { computeEditDiff } from "../../../core/tools/edit-diff.js";
5
- import { allTools } from "../../../core/tools/index.js";
6
- import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "../../../core/tools/truncate.js";
1
+ import { Box, Container, getCapabilities, Image, Spacer, Text } from "phi-code-tui";
2
+ import { createAllToolDefinitions } from "../../../core/tools/index.js";
3
+ import { getTextOutput as getRenderedTextOutput } from "../../../core/tools/render-utils.js";
7
4
  import { convertToPng } from "../../../utils/image-convert.js";
8
- import { sanitizeBinaryOutput } from "../../../utils/shell.js";
9
- import { getLanguageFromPath, highlightCode, theme } from "../theme/theme.js";
10
- import { renderDiff } from "./diff.js";
11
- import { keyHint } from "./keybinding-hints.js";
12
- import { truncateToVisualLines } from "./visual-truncate.js";
13
- // Preview line limit for bash when not expanded
14
- const BASH_PREVIEW_LINES = 5;
15
- // During partial write tool-call streaming, re-highlight the first N lines fully
16
- // to keep multiline tokenization mostly correct without re-highlighting the full file.
17
- const WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;
18
- /**
19
- * Convert absolute path to tilde notation if it's in home directory
20
- */
21
- function shortenPath(path) {
22
- if (typeof path !== "string")
23
- return "";
24
- const home = os.homedir();
25
- if (path.startsWith(home)) {
26
- return `~${path.slice(home.length)}`;
27
- }
28
- return path;
29
- }
30
- /**
31
- * Replace tabs with spaces for consistent rendering
32
- */
33
- function replaceTabs(text) {
34
- return text.replace(/\t/g, " ");
35
- }
36
- /**
37
- * Normalize control characters for terminal preview rendering.
38
- * Keep tool arguments unchanged, sanitize only display text.
39
- */
40
- function normalizeDisplayText(text) {
41
- return text.replace(/\r/g, "");
42
- }
43
- /** Safely coerce value to string for display. Returns null if invalid type. */
44
- function str(value) {
45
- if (typeof value === "string")
46
- return value;
47
- if (value == null)
48
- return "";
49
- return null; // Invalid type
50
- }
51
- /**
52
- * Component that renders a tool call with its result (updateable)
53
- */
5
+ import { theme } from "../theme/theme.js";
54
6
  export class ToolExecutionComponent extends Container {
55
- constructor(toolName, args, options = {}, toolDefinition, ui, cwd = process.cwd()) {
7
+ contentBox;
8
+ contentText;
9
+ selfRenderContainer;
10
+ callRendererComponent;
11
+ resultRendererComponent;
12
+ rendererState = {};
13
+ imageComponents = [];
14
+ imageSpacers = [];
15
+ toolName;
16
+ toolCallId;
17
+ args;
18
+ expanded = false;
19
+ showImages;
20
+ imageWidthCells;
21
+ isPartial = true;
22
+ toolDefinition;
23
+ builtInToolDefinition;
24
+ ui;
25
+ cwd;
26
+ executionStarted = false;
27
+ argsComplete = false;
28
+ result;
29
+ convertedImages = new Map();
30
+ hideComponent = false;
31
+ constructor(toolName, toolCallId, args, options = {}, toolDefinition, ui, cwd) {
56
32
  super();
57
- this.imageComponents = [];
58
- this.imageSpacers = [];
59
- this.expanded = false;
60
- this.isPartial = true;
61
- // Cached converted images for Kitty protocol (which requires PNG), keyed by index
62
- this.convertedImages = new Map();
63
- // When true, this component intentionally renders no lines
64
- this.hideComponent = false;
65
33
  this.toolName = toolName;
34
+ this.toolCallId = toolCallId;
66
35
  this.args = args;
67
- this.showImages = options.showImages ?? true;
68
36
  this.toolDefinition = toolDefinition;
37
+ this.builtInToolDefinition = createAllToolDefinitions(cwd)[toolName];
38
+ this.showImages = options.showImages ?? true;
39
+ this.imageWidthCells = options.imageWidthCells ?? 60;
69
40
  this.ui = ui;
70
41
  this.cwd = cwd;
71
42
  this.addChild(new Spacer(1));
72
- // Always create both - contentBox for custom tools/bash, contentText for other built-ins
43
+ // Always create all shell variants. contentBox is used for default renderer-based composition.
44
+ // selfRenderContainer is used when the tool renders its own framing.
45
+ // contentText is reserved for generic fallback rendering when no tool definition exists.
73
46
  this.contentBox = new Box(1, 1, (text) => theme.bg("toolPendingBg", text));
74
47
  this.contentText = new Text("", 1, 1, (text) => theme.bg("toolPendingBg", text));
75
- // Use contentBox for bash (visual truncation) or custom tools with custom renderers
76
- // Use contentText for built-in tools (including overrides without custom renderers)
77
- if (toolName === "bash" || (toolDefinition && !this.shouldUseBuiltInRenderer())) {
78
- this.addChild(this.contentBox);
48
+ this.selfRenderContainer = new Container();
49
+ if (this.hasRendererDefinition()) {
50
+ this.addChild(this.getRenderShell() === "self" ? this.selfRenderContainer : this.contentBox);
79
51
  }
80
52
  else {
81
53
  this.addChild(this.contentText);
82
54
  }
83
55
  this.updateDisplay();
84
56
  }
85
- /**
86
- * Check if we should use built-in rendering for this tool.
87
- * Returns true if the tool name is a built-in AND either there's no toolDefinition
88
- * or the toolDefinition doesn't provide custom renderers.
89
- */
90
- shouldUseBuiltInRenderer() {
91
- const isBuiltInName = this.toolName in allTools;
92
- const hasCustomRenderers = this.toolDefinition?.renderCall || this.toolDefinition?.renderResult;
93
- return isBuiltInName && !hasCustomRenderers;
57
+ getCallRenderer() {
58
+ if (!this.builtInToolDefinition) {
59
+ return this.toolDefinition?.renderCall;
60
+ }
61
+ if (!this.toolDefinition) {
62
+ return this.builtInToolDefinition.renderCall;
63
+ }
64
+ return this.toolDefinition.renderCall ?? this.builtInToolDefinition.renderCall;
94
65
  }
95
- updateArgs(args) {
96
- this.args = args;
97
- if (this.toolName === "write" && this.isPartial) {
98
- this.updateWriteHighlightCacheIncremental();
66
+ getResultRenderer() {
67
+ if (!this.builtInToolDefinition) {
68
+ return this.toolDefinition?.renderResult;
99
69
  }
100
- this.updateDisplay();
70
+ if (!this.toolDefinition) {
71
+ return this.builtInToolDefinition.renderResult;
72
+ }
73
+ return this.toolDefinition.renderResult ?? this.builtInToolDefinition.renderResult;
101
74
  }
102
- highlightSingleLine(line, lang) {
103
- const highlighted = highlightCode(line, lang);
104
- return highlighted[0] ?? "";
75
+ hasRendererDefinition() {
76
+ return this.builtInToolDefinition !== undefined || this.toolDefinition !== undefined;
105
77
  }
106
- refreshWriteHighlightPrefix(cache) {
107
- const prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);
108
- if (prefixCount === 0)
109
- return;
110
- const prefixSource = cache.normalizedLines.slice(0, prefixCount).join("\n");
111
- const prefixHighlighted = highlightCode(prefixSource, cache.lang);
112
- for (let i = 0; i < prefixCount; i++) {
113
- cache.highlightedLines[i] =
114
- prefixHighlighted[i] ?? this.highlightSingleLine(cache.normalizedLines[i] ?? "", cache.lang);
78
+ getRenderShell() {
79
+ if (!this.builtInToolDefinition) {
80
+ return this.toolDefinition?.renderShell ?? "default";
115
81
  }
116
- }
117
- rebuildWriteHighlightCacheFull(rawPath, fileContent) {
118
- const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
119
- if (!lang) {
120
- this.writeHighlightCache = undefined;
121
- return;
82
+ if (!this.toolDefinition) {
83
+ return this.builtInToolDefinition.renderShell ?? "default";
122
84
  }
123
- const displayContent = normalizeDisplayText(fileContent);
124
- const normalized = replaceTabs(displayContent);
125
- this.writeHighlightCache = {
126
- rawPath,
127
- lang,
128
- rawContent: fileContent,
129
- normalizedLines: normalized.split("\n"),
130
- highlightedLines: highlightCode(normalized, lang),
85
+ return this.toolDefinition.renderShell ?? this.builtInToolDefinition.renderShell ?? "default";
86
+ }
87
+ getRenderContext(lastComponent) {
88
+ return {
89
+ args: this.args,
90
+ toolCallId: this.toolCallId,
91
+ invalidate: () => {
92
+ this.invalidate();
93
+ this.ui.requestRender();
94
+ },
95
+ lastComponent,
96
+ state: this.rendererState,
97
+ cwd: this.cwd,
98
+ executionStarted: this.executionStarted,
99
+ argsComplete: this.argsComplete,
100
+ isPartial: this.isPartial,
101
+ expanded: this.expanded,
102
+ showImages: this.showImages,
103
+ isError: this.result?.isError ?? false,
131
104
  };
132
105
  }
133
- updateWriteHighlightCacheIncremental() {
134
- const rawPath = str(this.args?.file_path ?? this.args?.path);
135
- const fileContent = str(this.args?.content);
136
- if (rawPath === null || fileContent === null) {
137
- this.writeHighlightCache = undefined;
138
- return;
139
- }
140
- const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
141
- if (!lang) {
142
- this.writeHighlightCache = undefined;
143
- return;
144
- }
145
- if (!this.writeHighlightCache) {
146
- this.rebuildWriteHighlightCacheFull(rawPath, fileContent);
147
- return;
148
- }
149
- const cache = this.writeHighlightCache;
150
- if (cache.lang !== lang || cache.rawPath !== rawPath) {
151
- this.rebuildWriteHighlightCacheFull(rawPath, fileContent);
152
- return;
153
- }
154
- if (!fileContent.startsWith(cache.rawContent)) {
155
- this.rebuildWriteHighlightCacheFull(rawPath, fileContent);
156
- return;
157
- }
158
- if (fileContent.length === cache.rawContent.length) {
159
- return;
160
- }
161
- const deltaRaw = fileContent.slice(cache.rawContent.length);
162
- const deltaDisplay = normalizeDisplayText(deltaRaw);
163
- const deltaNormalized = replaceTabs(deltaDisplay);
164
- cache.rawContent = fileContent;
165
- if (cache.normalizedLines.length === 0) {
166
- cache.normalizedLines.push("");
167
- cache.highlightedLines.push("");
168
- }
169
- const segments = deltaNormalized.split("\n");
170
- const lastIndex = cache.normalizedLines.length - 1;
171
- cache.normalizedLines[lastIndex] += segments[0];
172
- cache.highlightedLines[lastIndex] = this.highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);
173
- for (let i = 1; i < segments.length; i++) {
174
- cache.normalizedLines.push(segments[i]);
175
- cache.highlightedLines.push(this.highlightSingleLine(segments[i], cache.lang));
176
- }
177
- this.refreshWriteHighlightPrefix(cache);
106
+ createCallFallback() {
107
+ return new Text(theme.fg("toolTitle", theme.bold(this.toolName)), 0, 0);
178
108
  }
179
- /**
180
- * Signal that args are complete (tool is about to execute).
181
- * This triggers diff computation for edit tool.
182
- */
183
- setArgsComplete() {
184
- if (this.toolName === "write") {
185
- const rawPath = str(this.args?.file_path ?? this.args?.path);
186
- const fileContent = str(this.args?.content);
187
- if (rawPath !== null && fileContent !== null) {
188
- this.rebuildWriteHighlightCacheFull(rawPath, fileContent);
189
- }
109
+ createResultFallback() {
110
+ const output = this.getTextOutput();
111
+ if (!output) {
112
+ return undefined;
190
113
  }
191
- this.maybeComputeEditDiff();
114
+ return new Text(theme.fg("toolOutput", output), 0, 0);
192
115
  }
193
- /**
194
- * Compute edit diff preview when we have complete args.
195
- * This runs async and updates display when done.
196
- */
197
- maybeComputeEditDiff() {
198
- if (this.toolName !== "edit")
199
- return;
200
- const path = this.args?.path;
201
- const oldText = this.args?.oldText;
202
- const newText = this.args?.newText;
203
- // Need all three params to compute diff
204
- if (!path || oldText === undefined || newText === undefined)
205
- return;
206
- // Create a key to track which args this computation is for
207
- const argsKey = JSON.stringify({ path, oldText, newText });
208
- // Skip if we already computed for these exact args
209
- if (this.editDiffArgsKey === argsKey)
210
- return;
211
- this.editDiffArgsKey = argsKey;
212
- // Compute diff async
213
- computeEditDiff(path, oldText, newText, this.cwd).then((result) => {
214
- // Only update if args haven't changed since we started
215
- if (this.editDiffArgsKey === argsKey) {
216
- this.editDiffPreview = result;
217
- this.updateDisplay();
218
- this.ui.requestRender();
219
- }
220
- });
116
+ updateArgs(args) {
117
+ this.args = args;
118
+ this.updateDisplay();
119
+ }
120
+ markExecutionStarted() {
121
+ this.executionStarted = true;
122
+ this.updateDisplay();
123
+ this.ui.requestRender();
124
+ }
125
+ setArgsComplete() {
126
+ this.argsComplete = true;
127
+ this.updateDisplay();
128
+ this.ui.requestRender();
221
129
  }
222
130
  updateResult(result, isPartial = false) {
223
131
  this.result = result;
224
132
  this.isPartial = isPartial;
225
- if (this.toolName === "write" && !isPartial) {
226
- const rawPath = str(this.args?.file_path ?? this.args?.path);
227
- const fileContent = str(this.args?.content);
228
- if (rawPath !== null && fileContent !== null) {
229
- this.rebuildWriteHighlightCacheFull(rawPath, fileContent);
230
- }
231
- }
232
133
  this.updateDisplay();
233
- // Convert non-PNG images to PNG for Kitty protocol (async)
234
134
  this.maybeConvertImagesForKitty();
235
135
  }
236
- /**
237
- * Convert non-PNG images to PNG for Kitty graphics protocol.
238
- * Kitty requires PNG format (f=100), so JPEG/GIF/WebP won't display.
239
- */
240
136
  maybeConvertImagesForKitty() {
241
137
  const caps = getCapabilities();
242
- // Only needed for Kitty protocol
243
138
  if (caps.images !== "kitty")
244
139
  return;
245
140
  if (!this.result)
246
141
  return;
247
- const imageBlocks = this.result.content?.filter((c) => c.type === "image") || [];
142
+ const imageBlocks = this.result.content.filter((c) => c.type === "image");
248
143
  for (let i = 0; i < imageBlocks.length; i++) {
249
144
  const img = imageBlocks[i];
250
145
  if (!img.data || !img.mimeType)
251
146
  continue;
252
- // Skip if already PNG or already converted
253
147
  if (img.mimeType === "image/png")
254
148
  continue;
255
149
  if (this.convertedImages.has(i))
256
150
  continue;
257
- // Convert async
258
151
  const index = i;
259
152
  convertToPng(img.data, img.mimeType).then((converted) => {
260
153
  if (converted) {
@@ -273,6 +166,10 @@ export class ToolExecutionComponent extends Container {
273
166
  this.showImages = show;
274
167
  this.updateDisplay();
275
168
  }
169
+ setImageWidthCells(width) {
170
+ this.imageWidthCells = Math.max(1, Math.floor(width));
171
+ this.updateDisplay();
172
+ }
276
173
  invalidate() {
277
174
  super.invalidate();
278
175
  this.updateDisplay();
@@ -284,86 +181,69 @@ export class ToolExecutionComponent extends Container {
284
181
  return super.render(width);
285
182
  }
286
183
  updateDisplay() {
287
- // Set background based on state
288
184
  const bgFn = this.isPartial
289
185
  ? (text) => theme.bg("toolPendingBg", text)
290
186
  : this.result?.isError
291
187
  ? (text) => theme.bg("toolErrorBg", text)
292
188
  : (text) => theme.bg("toolSuccessBg", text);
293
- const useBuiltInRenderer = this.shouldUseBuiltInRenderer();
294
- let customRendererHasContent = false;
189
+ let hasContent = false;
295
190
  this.hideComponent = false;
296
- // Use built-in rendering for built-in tools (or overrides without custom renderers)
297
- if (useBuiltInRenderer) {
298
- if (this.toolName === "bash") {
299
- // Bash uses Box with visual line truncation
300
- this.contentBox.setBgFn(bgFn);
301
- this.contentBox.clear();
302
- this.renderBashContent();
191
+ if (this.hasRendererDefinition()) {
192
+ const renderContainer = this.getRenderShell() === "self" ? this.selfRenderContainer : this.contentBox;
193
+ if (renderContainer instanceof Box) {
194
+ renderContainer.setBgFn(bgFn);
303
195
  }
304
- else {
305
- // Other built-in tools: use Text directly with caching
306
- this.contentText.setCustomBgFn(bgFn);
307
- this.contentText.setText(this.formatToolExecution());
196
+ renderContainer.clear();
197
+ const callRenderer = this.getCallRenderer();
198
+ if (!callRenderer) {
199
+ renderContainer.addChild(this.createCallFallback());
200
+ hasContent = true;
308
201
  }
309
- }
310
- else if (this.toolDefinition) {
311
- // Custom tools use Box for flexible component rendering
312
- this.contentBox.setBgFn(bgFn);
313
- this.contentBox.clear();
314
- // Render call component
315
- if (this.toolDefinition.renderCall) {
202
+ else {
316
203
  try {
317
- const callComponent = this.toolDefinition.renderCall(this.args, theme);
318
- if (callComponent !== undefined) {
319
- this.contentBox.addChild(callComponent);
320
- customRendererHasContent = true;
321
- }
204
+ const component = callRenderer(this.args, theme, this.getRenderContext(this.callRendererComponent));
205
+ this.callRendererComponent = component;
206
+ renderContainer.addChild(component);
207
+ hasContent = true;
322
208
  }
323
209
  catch {
324
- // Fall back to default on error
325
- this.contentBox.addChild(new Text(theme.fg("toolTitle", theme.bold(this.toolName)), 0, 0));
326
- customRendererHasContent = true;
210
+ this.callRendererComponent = undefined;
211
+ renderContainer.addChild(this.createCallFallback());
212
+ hasContent = true;
327
213
  }
328
214
  }
329
- else {
330
- // No custom renderCall, show tool name
331
- this.contentBox.addChild(new Text(theme.fg("toolTitle", theme.bold(this.toolName)), 0, 0));
332
- customRendererHasContent = true;
333
- }
334
- // Render result component if we have a result
335
- if (this.result && this.toolDefinition.renderResult) {
336
- try {
337
- const resultComponent = this.toolDefinition.renderResult({ content: this.result.content, details: this.result.details }, { expanded: this.expanded, isPartial: this.isPartial }, theme);
338
- if (resultComponent !== undefined) {
339
- this.contentBox.addChild(resultComponent);
340
- customRendererHasContent = true;
215
+ if (this.result) {
216
+ const resultRenderer = this.getResultRenderer();
217
+ if (!resultRenderer) {
218
+ const component = this.createResultFallback();
219
+ if (component) {
220
+ renderContainer.addChild(component);
221
+ hasContent = true;
341
222
  }
342
223
  }
343
- catch {
344
- // Fall back to showing raw output on error
345
- const output = this.getTextOutput();
346
- if (output) {
347
- this.contentBox.addChild(new Text(theme.fg("toolOutput", output), 0, 0));
348
- customRendererHasContent = true;
224
+ else {
225
+ try {
226
+ const component = resultRenderer({ content: this.result.content, details: this.result.details }, { expanded: this.expanded, isPartial: this.isPartial }, theme, this.getRenderContext(this.resultRendererComponent));
227
+ this.resultRendererComponent = component;
228
+ renderContainer.addChild(component);
229
+ hasContent = true;
230
+ }
231
+ catch {
232
+ this.resultRendererComponent = undefined;
233
+ const component = this.createResultFallback();
234
+ if (component) {
235
+ renderContainer.addChild(component);
236
+ hasContent = true;
237
+ }
349
238
  }
350
- }
351
- }
352
- else if (this.result) {
353
- // Has result but no custom renderResult
354
- const output = this.getTextOutput();
355
- if (output) {
356
- this.contentBox.addChild(new Text(theme.fg("toolOutput", output), 0, 0));
357
- customRendererHasContent = true;
358
239
  }
359
240
  }
360
241
  }
361
242
  else {
362
- // Unknown tool with no registered definition - show generic fallback
363
243
  this.contentText.setCustomBgFn(bgFn);
364
244
  this.contentText.setText(this.formatToolExecution());
245
+ hasContent = true;
365
246
  }
366
- // Handle images (same for both custom and built-in)
367
247
  for (const img of this.imageComponents) {
368
248
  this.removeChild(img);
369
249
  }
@@ -373,399 +253,41 @@ export class ToolExecutionComponent extends Container {
373
253
  }
374
254
  this.imageSpacers = [];
375
255
  if (this.result) {
376
- const imageBlocks = this.result.content?.filter((c) => c.type === "image") || [];
256
+ const imageBlocks = this.result.content.filter((c) => c.type === "image");
377
257
  const caps = getCapabilities();
378
258
  for (let i = 0; i < imageBlocks.length; i++) {
379
259
  const img = imageBlocks[i];
380
260
  if (caps.images && this.showImages && img.data && img.mimeType) {
381
- // Use converted PNG for Kitty protocol if available
382
261
  const converted = this.convertedImages.get(i);
383
262
  const imageData = converted?.data ?? img.data;
384
263
  const imageMimeType = converted?.mimeType ?? img.mimeType;
385
- // For Kitty, skip non-PNG images that haven't been converted yet
386
- if (caps.images === "kitty" && imageMimeType !== "image/png") {
264
+ if (caps.images === "kitty" && imageMimeType !== "image/png")
387
265
  continue;
388
- }
389
266
  const spacer = new Spacer(1);
390
267
  this.addChild(spacer);
391
268
  this.imageSpacers.push(spacer);
392
- const imageComponent = new Image(imageData, imageMimeType, { fallbackColor: (s) => theme.fg("toolOutput", s) }, { maxWidthCells: 60 });
269
+ const imageComponent = new Image(imageData, imageMimeType, { fallbackColor: (s) => theme.fg("toolOutput", s) }, { maxWidthCells: this.imageWidthCells });
393
270
  this.imageComponents.push(imageComponent);
394
271
  this.addChild(imageComponent);
395
272
  }
396
273
  }
397
274
  }
398
- if (!useBuiltInRenderer && this.toolDefinition) {
399
- this.hideComponent = !customRendererHasContent && this.imageComponents.length === 0;
400
- }
401
- }
402
- /**
403
- * Render bash content using visual line truncation (like bash-execution.ts)
404
- */
405
- renderBashContent() {
406
- const command = str(this.args?.command);
407
- const timeout = this.args?.timeout;
408
- // Header
409
- const timeoutSuffix = timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
410
- const commandDisplay = command === null ? theme.fg("error", "[invalid arg]") : command ? command : theme.fg("toolOutput", "...");
411
- this.contentBox.addChild(new Text(theme.fg("toolTitle", theme.bold(`$ ${commandDisplay}`)) + timeoutSuffix, 0, 0));
412
- if (this.result) {
413
- const output = this.getTextOutput().trim();
414
- if (output) {
415
- // Style each line for the output
416
- const styledOutput = output
417
- .split("\n")
418
- .map((line) => theme.fg("toolOutput", line))
419
- .join("\n");
420
- if (this.expanded) {
421
- // Show all lines when expanded
422
- this.contentBox.addChild(new Text(`\n${styledOutput}`, 0, 0));
423
- }
424
- else {
425
- // Use visual line truncation when collapsed with width-aware caching
426
- let cachedWidth;
427
- let cachedLines;
428
- let cachedSkipped;
429
- this.contentBox.addChild({
430
- render: (width) => {
431
- if (cachedLines === undefined || cachedWidth !== width) {
432
- const result = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);
433
- cachedLines = result.visualLines;
434
- cachedSkipped = result.skippedCount;
435
- cachedWidth = width;
436
- }
437
- if (cachedSkipped && cachedSkipped > 0) {
438
- const hint = theme.fg("muted", `... (${cachedSkipped} earlier lines,`) +
439
- ` ${keyHint("expandTools", "to expand")})`;
440
- return ["", truncateToWidth(hint, width, "..."), ...cachedLines];
441
- }
442
- // Add blank line for spacing (matches expanded case)
443
- return ["", ...cachedLines];
444
- },
445
- invalidate: () => {
446
- cachedWidth = undefined;
447
- cachedLines = undefined;
448
- cachedSkipped = undefined;
449
- },
450
- });
451
- }
452
- }
453
- // Truncation warnings
454
- const truncation = this.result.details?.truncation;
455
- const fullOutputPath = this.result.details?.fullOutputPath;
456
- if (truncation?.truncated || fullOutputPath) {
457
- const warnings = [];
458
- if (fullOutputPath) {
459
- warnings.push(`Full output: ${fullOutputPath}`);
460
- }
461
- if (truncation?.truncated) {
462
- if (truncation.truncatedBy === "lines") {
463
- warnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);
464
- }
465
- else {
466
- warnings.push(`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`);
467
- }
468
- }
469
- this.contentBox.addChild(new Text(`\n${theme.fg("warning", `[${warnings.join(". ")}]`)}`, 0, 0));
470
- }
275
+ if (this.hasRendererDefinition() && !hasContent && this.imageComponents.length === 0) {
276
+ this.hideComponent = true;
471
277
  }
472
278
  }
473
279
  getTextOutput() {
474
- if (!this.result)
475
- return "";
476
- const textBlocks = this.result.content?.filter((c) => c.type === "text") || [];
477
- const imageBlocks = this.result.content?.filter((c) => c.type === "image") || [];
478
- let output = textBlocks
479
- .map((c) => {
480
- // Use sanitizeBinaryOutput to handle binary data that crashes string-width
481
- return sanitizeBinaryOutput(stripAnsi(c.text || "")).replace(/\r/g, "");
482
- })
483
- .join("\n");
484
- const caps = getCapabilities();
485
- if (imageBlocks.length > 0 && (!caps.images || !this.showImages)) {
486
- const imageIndicators = imageBlocks
487
- .map((img) => {
488
- const dims = img.data ? (getImageDimensions(img.data, img.mimeType) ?? undefined) : undefined;
489
- return imageFallback(img.mimeType, dims);
490
- })
491
- .join("\n");
492
- output = output ? `${output}\n${imageIndicators}` : imageIndicators;
493
- }
494
- return output;
280
+ return getRenderedTextOutput(this.result, this.showImages);
495
281
  }
496
282
  formatToolExecution() {
497
- let text = "";
498
- const invalidArg = theme.fg("error", "[invalid arg]");
499
- if (this.toolName === "read") {
500
- const rawPath = str(this.args?.file_path ?? this.args?.path);
501
- const path = rawPath !== null ? shortenPath(rawPath) : null;
502
- const offset = this.args?.offset;
503
- const limit = this.args?.limit;
504
- let pathDisplay = path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
505
- if (offset !== undefined || limit !== undefined) {
506
- const startLine = offset ?? 1;
507
- const endLine = limit !== undefined ? startLine + limit - 1 : "";
508
- pathDisplay += theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`);
509
- }
510
- text = `${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}`;
511
- if (this.result) {
512
- const output = this.getTextOutput();
513
- const rawPath = str(this.args?.file_path ?? this.args?.path);
514
- const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
515
- const lines = lang ? highlightCode(replaceTabs(output), lang) : output.split("\n");
516
- const maxLines = this.expanded ? lines.length : 10;
517
- const displayLines = lines.slice(0, maxLines);
518
- const remaining = lines.length - maxLines;
519
- text +=
520
- "\n\n" +
521
- displayLines
522
- .map((line) => (lang ? replaceTabs(line) : theme.fg("toolOutput", replaceTabs(line))))
523
- .join("\n");
524
- if (remaining > 0) {
525
- text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("expandTools", "to expand")})`;
526
- }
527
- const truncation = this.result.details?.truncation;
528
- if (truncation?.truncated) {
529
- if (truncation.firstLineExceedsLimit) {
530
- text +=
531
- "\n" +
532
- theme.fg("warning", `[First line exceeds ${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit]`);
533
- }
534
- else if (truncation.truncatedBy === "lines") {
535
- text +=
536
- "\n" +
537
- theme.fg("warning", `[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${truncation.maxLines ?? DEFAULT_MAX_LINES} line limit)]`);
538
- }
539
- else {
540
- text +=
541
- "\n" +
542
- theme.fg("warning", `[Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)]`);
543
- }
544
- }
545
- }
546
- }
547
- else if (this.toolName === "write") {
548
- const rawPath = str(this.args?.file_path ?? this.args?.path);
549
- const fileContent = str(this.args?.content);
550
- const path = rawPath !== null ? shortenPath(rawPath) : null;
551
- text =
552
- theme.fg("toolTitle", theme.bold("write")) +
553
- " " +
554
- (path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "..."));
555
- if (fileContent === null) {
556
- text += `\n\n${theme.fg("error", "[invalid content arg - expected string]")}`;
557
- }
558
- else if (fileContent) {
559
- const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
560
- let lines;
561
- if (lang) {
562
- const cache = this.writeHighlightCache;
563
- if (cache && cache.lang === lang && cache.rawPath === rawPath && cache.rawContent === fileContent) {
564
- lines = cache.highlightedLines;
565
- }
566
- else {
567
- const displayContent = normalizeDisplayText(fileContent);
568
- const normalized = replaceTabs(displayContent);
569
- lines = highlightCode(normalized, lang);
570
- this.writeHighlightCache = {
571
- rawPath,
572
- lang,
573
- rawContent: fileContent,
574
- normalizedLines: normalized.split("\n"),
575
- highlightedLines: lines,
576
- };
577
- }
578
- }
579
- else {
580
- lines = normalizeDisplayText(fileContent).split("\n");
581
- this.writeHighlightCache = undefined;
582
- }
583
- const totalLines = lines.length;
584
- const maxLines = this.expanded ? lines.length : 10;
585
- const displayLines = lines.slice(0, maxLines);
586
- const remaining = lines.length - maxLines;
587
- text +=
588
- "\n\n" +
589
- displayLines.map((line) => (lang ? line : theme.fg("toolOutput", replaceTabs(line)))).join("\n");
590
- if (remaining > 0) {
591
- text +=
592
- theme.fg("muted", `\n... (${remaining} more lines, ${totalLines} total,`) +
593
- ` ${keyHint("expandTools", "to expand")})`;
594
- }
595
- }
596
- // Show error if tool execution failed
597
- if (this.result?.isError) {
598
- const errorText = this.getTextOutput();
599
- if (errorText) {
600
- text += `\n\n${theme.fg("error", errorText)}`;
601
- }
602
- }
603
- }
604
- else if (this.toolName === "edit") {
605
- const rawPath = str(this.args?.file_path ?? this.args?.path);
606
- const path = rawPath !== null ? shortenPath(rawPath) : null;
607
- // Build path display, appending :line if we have diff info
608
- let pathDisplay = path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
609
- const firstChangedLine = (this.editDiffPreview && "firstChangedLine" in this.editDiffPreview
610
- ? this.editDiffPreview.firstChangedLine
611
- : undefined) ||
612
- (this.result && !this.result.isError ? this.result.details?.firstChangedLine : undefined);
613
- if (firstChangedLine) {
614
- pathDisplay += theme.fg("warning", `:${firstChangedLine}`);
615
- }
616
- text = `${theme.fg("toolTitle", theme.bold("edit"))} ${pathDisplay}`;
617
- if (this.result?.isError) {
618
- // Show error from result
619
- const errorText = this.getTextOutput();
620
- if (errorText) {
621
- text += `\n\n${theme.fg("error", errorText)}`;
622
- }
623
- }
624
- else if (this.result?.details?.diff) {
625
- // Tool executed successfully - use the diff from result
626
- // This takes priority over editDiffPreview which may have a stale error
627
- // due to race condition (async preview computed after file was modified)
628
- text += `\n\n${renderDiff(this.result.details.diff, { filePath: rawPath ?? undefined })}`;
629
- }
630
- else if (this.editDiffPreview) {
631
- // Use cached diff preview (before tool executes)
632
- if ("error" in this.editDiffPreview) {
633
- text += `\n\n${theme.fg("error", this.editDiffPreview.error)}`;
634
- }
635
- else if (this.editDiffPreview.diff) {
636
- text += `\n\n${renderDiff(this.editDiffPreview.diff, { filePath: rawPath ?? undefined })}`;
637
- }
638
- }
639
- }
640
- else if (this.toolName === "ls") {
641
- const rawPath = str(this.args?.path);
642
- const path = rawPath !== null ? shortenPath(rawPath || ".") : null;
643
- const limit = this.args?.limit;
644
- text = `${theme.fg("toolTitle", theme.bold("ls"))} ${path === null ? invalidArg : theme.fg("accent", path)}`;
645
- if (limit !== undefined) {
646
- text += theme.fg("toolOutput", ` (limit ${limit})`);
647
- }
648
- if (this.result) {
649
- const output = this.getTextOutput().trim();
650
- if (output) {
651
- const lines = output.split("\n");
652
- const maxLines = this.expanded ? lines.length : 20;
653
- const displayLines = lines.slice(0, maxLines);
654
- const remaining = lines.length - maxLines;
655
- text += `\n\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
656
- if (remaining > 0) {
657
- text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("expandTools", "to expand")})`;
658
- }
659
- }
660
- const entryLimit = this.result.details?.entryLimitReached;
661
- const truncation = this.result.details?.truncation;
662
- if (entryLimit || truncation?.truncated) {
663
- const warnings = [];
664
- if (entryLimit) {
665
- warnings.push(`${entryLimit} entries limit`);
666
- }
667
- if (truncation?.truncated) {
668
- warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
669
- }
670
- text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
671
- }
672
- }
673
- }
674
- else if (this.toolName === "find") {
675
- const pattern = str(this.args?.pattern);
676
- const rawPath = str(this.args?.path);
677
- const path = rawPath !== null ? shortenPath(rawPath || ".") : null;
678
- const limit = this.args?.limit;
679
- text =
680
- theme.fg("toolTitle", theme.bold("find")) +
681
- " " +
682
- (pattern === null ? invalidArg : theme.fg("accent", pattern || "")) +
683
- theme.fg("toolOutput", ` in ${path === null ? invalidArg : path}`);
684
- if (limit !== undefined) {
685
- text += theme.fg("toolOutput", ` (limit ${limit})`);
686
- }
687
- if (this.result) {
688
- const output = this.getTextOutput().trim();
689
- if (output) {
690
- const lines = output.split("\n");
691
- const maxLines = this.expanded ? lines.length : 20;
692
- const displayLines = lines.slice(0, maxLines);
693
- const remaining = lines.length - maxLines;
694
- text += `\n\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
695
- if (remaining > 0) {
696
- text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("expandTools", "to expand")})`;
697
- }
698
- }
699
- const resultLimit = this.result.details?.resultLimitReached;
700
- const truncation = this.result.details?.truncation;
701
- if (resultLimit || truncation?.truncated) {
702
- const warnings = [];
703
- if (resultLimit) {
704
- warnings.push(`${resultLimit} results limit`);
705
- }
706
- if (truncation?.truncated) {
707
- warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
708
- }
709
- text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
710
- }
711
- }
712
- }
713
- else if (this.toolName === "grep") {
714
- const pattern = str(this.args?.pattern);
715
- const rawPath = str(this.args?.path);
716
- const path = rawPath !== null ? shortenPath(rawPath || ".") : null;
717
- const glob = str(this.args?.glob);
718
- const limit = this.args?.limit;
719
- text =
720
- theme.fg("toolTitle", theme.bold("grep")) +
721
- " " +
722
- (pattern === null ? invalidArg : theme.fg("accent", `/${pattern || ""}/`)) +
723
- theme.fg("toolOutput", ` in ${path === null ? invalidArg : path}`);
724
- if (glob) {
725
- text += theme.fg("toolOutput", ` (${glob})`);
726
- }
727
- if (limit !== undefined) {
728
- text += theme.fg("toolOutput", ` limit ${limit}`);
729
- }
730
- if (this.result) {
731
- const output = this.getTextOutput().trim();
732
- if (output) {
733
- const lines = output.split("\n");
734
- const maxLines = this.expanded ? lines.length : 15;
735
- const displayLines = lines.slice(0, maxLines);
736
- const remaining = lines.length - maxLines;
737
- text += `\n\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
738
- if (remaining > 0) {
739
- text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("expandTools", "to expand")})`;
740
- }
741
- }
742
- const matchLimit = this.result.details?.matchLimitReached;
743
- const truncation = this.result.details?.truncation;
744
- const linesTruncated = this.result.details?.linesTruncated;
745
- if (matchLimit || truncation?.truncated || linesTruncated) {
746
- const warnings = [];
747
- if (matchLimit) {
748
- warnings.push(`${matchLimit} matches limit`);
749
- }
750
- if (truncation?.truncated) {
751
- warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
752
- }
753
- if (linesTruncated) {
754
- warnings.push("some lines truncated");
755
- }
756
- text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
757
- }
758
- }
759
- }
760
- else {
761
- // Generic tool (shouldn't reach here for custom tools)
762
- text = theme.fg("toolTitle", theme.bold(this.toolName));
763
- const content = JSON.stringify(this.args, null, 2);
283
+ let text = theme.fg("toolTitle", theme.bold(this.toolName));
284
+ const content = JSON.stringify(this.args, null, 2);
285
+ if (content) {
764
286
  text += `\n\n${content}`;
765
- const output = this.getTextOutput();
766
- if (output) {
767
- text += `\n${output}`;
768
- }
287
+ }
288
+ const output = this.getTextOutput();
289
+ if (output) {
290
+ text += `\n${output}`;
769
291
  }
770
292
  return text;
771
293
  }