@agimon-ai/doompi-runner 0.0.1-alpha.2 → 0.0.1-alpha.21

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 (293) hide show
  1. package/README.md +25 -16
  2. package/dist/adapters/RunnerPaths.cjs +2 -0
  3. package/dist/adapters/RunnerPaths.cjs.map +1 -0
  4. package/dist/adapters/RunnerPaths.d.cts +35 -0
  5. package/dist/adapters/RunnerPaths.d.cts.map +1 -0
  6. package/dist/adapters/RunnerPaths.d.mts +35 -0
  7. package/dist/adapters/RunnerPaths.d.mts.map +1 -0
  8. package/dist/adapters/RunnerPaths.mjs +2 -0
  9. package/dist/adapters/RunnerPaths.mjs.map +1 -0
  10. package/dist/adapters/pi/extension.cjs +3 -0
  11. package/dist/adapters/pi/extension.cjs.map +1 -0
  12. package/dist/adapters/pi/extension.d.cts +10 -0
  13. package/dist/adapters/pi/extension.d.cts.map +1 -0
  14. package/dist/adapters/pi/extension.d.mts +10 -0
  15. package/dist/adapters/pi/extension.d.mts.map +1 -0
  16. package/dist/adapters/pi/extension.mjs +3 -0
  17. package/dist/adapters/pi/extension.mjs.map +1 -0
  18. package/dist/adapters/pty/ptyProvider.cjs +2 -0
  19. package/dist/adapters/pty/ptyProvider.cjs.map +1 -0
  20. package/dist/adapters/pty/ptyProvider.d.cts +13 -0
  21. package/dist/adapters/pty/ptyProvider.d.cts.map +1 -0
  22. package/dist/adapters/pty/ptyProvider.d.mts +13 -0
  23. package/dist/adapters/pty/ptyProvider.d.mts.map +1 -0
  24. package/dist/adapters/pty/ptyProvider.mjs +2 -0
  25. package/dist/adapters/pty/ptyProvider.mjs.map +1 -0
  26. package/dist/bin/cli.cjs +1 -1
  27. package/dist/bin/cli.cjs.map +1 -1
  28. package/dist/bin/cli.mjs +1 -1
  29. package/dist/bin/cli.mjs.map +1 -1
  30. package/dist/bin/logSink.cjs.map +1 -1
  31. package/dist/bin/logSink.mjs.map +1 -1
  32. package/dist/bin/runnerHost.cjs.map +1 -1
  33. package/dist/bin/runnerHost.mjs.map +1 -1
  34. package/dist/cli/cliApp.cjs +1 -6
  35. package/dist/cli/cliApp.d.cts +2 -23
  36. package/dist/cli/cliApp.d.mts +2 -23
  37. package/dist/cli/cliApp.mjs +1 -6
  38. package/dist/commands/bash/bashTool.cjs +7 -0
  39. package/dist/commands/bash/bashTool.cjs.map +1 -0
  40. package/dist/commands/bash/bashTool.d.cts +25 -0
  41. package/dist/commands/bash/bashTool.d.cts.map +1 -0
  42. package/dist/commands/bash/bashTool.d.mts +25 -0
  43. package/dist/commands/bash/bashTool.d.mts.map +1 -0
  44. package/dist/commands/bash/bashTool.mjs +7 -0
  45. package/dist/commands/bash/bashTool.mjs.map +1 -0
  46. package/dist/commands/bash/responseEnvelope.cjs +10 -0
  47. package/dist/commands/bash/responseEnvelope.cjs.map +1 -0
  48. package/dist/commands/bash/responseEnvelope.d.cts +45 -0
  49. package/dist/commands/bash/responseEnvelope.d.cts.map +1 -0
  50. package/dist/commands/bash/responseEnvelope.d.mts +45 -0
  51. package/dist/commands/bash/responseEnvelope.d.mts.map +1 -0
  52. package/dist/commands/bash/responseEnvelope.mjs +10 -0
  53. package/dist/{tool/responseEnvelope.cjs.map → commands/bash/responseEnvelope.mjs.map} +1 -1
  54. package/dist/commands/cli/cliApp.cjs +6 -0
  55. package/dist/commands/cli/cliApp.cjs.map +1 -0
  56. package/dist/commands/cli/cliApp.d.cts +23 -0
  57. package/dist/commands/cli/cliApp.d.cts.map +1 -0
  58. package/dist/commands/cli/cliApp.d.mts +23 -0
  59. package/dist/commands/cli/cliApp.d.mts.map +1 -0
  60. package/dist/commands/cli/cliApp.mjs +6 -0
  61. package/dist/commands/cli/cliApp.mjs.map +1 -0
  62. package/dist/compaction.cjs +1 -4
  63. package/dist/compaction.d.cts +2 -14
  64. package/dist/compaction.d.mts +2 -14
  65. package/dist/compaction.mjs +1 -4
  66. package/dist/config.cjs +1 -2
  67. package/dist/config.d.cts +2 -32
  68. package/dist/config.d.mts +2 -32
  69. package/dist/config.mjs +1 -2
  70. package/dist/container/index.cjs +1 -1
  71. package/dist/container/index.cjs.map +1 -1
  72. package/dist/container/index.mjs +1 -1
  73. package/dist/container/index.mjs.map +1 -1
  74. package/dist/extension.cjs +1 -3
  75. package/dist/extension.d.cts +2 -10
  76. package/dist/extension.d.mts +2 -10
  77. package/dist/extension.mjs +1 -3
  78. package/dist/extensions/pi.cjs +1 -1
  79. package/dist/extensions/pi.cjs.map +1 -1
  80. package/dist/extensions/pi.d.cts +1 -1
  81. package/dist/extensions/pi.d.mts +1 -1
  82. package/dist/extensions/pi.mjs +1 -1
  83. package/dist/extensions/pi.mjs.map +1 -1
  84. package/dist/index.cjs +1 -1
  85. package/dist/index.d.cts +2 -2
  86. package/dist/index.d.mts +2 -2
  87. package/dist/index.mjs +1 -1
  88. package/dist/ptyProvider.cjs +1 -2
  89. package/dist/ptyProvider.d.cts +2 -13
  90. package/dist/ptyProvider.d.mts +2 -13
  91. package/dist/ptyProvider.mjs +1 -2
  92. package/dist/reconcile.cjs +1 -2
  93. package/dist/reconcile.d.cts +2 -37
  94. package/dist/reconcile.d.mts +2 -37
  95. package/dist/reconcile.mjs +1 -2
  96. package/dist/runnerSpec.cjs +1 -2
  97. package/dist/runnerSpec.d.cts +2 -50
  98. package/dist/runnerSpec.d.mts +2 -50
  99. package/dist/runnerSpec.mjs +1 -2
  100. package/dist/schemas/bashTool.cjs +2 -0
  101. package/dist/schemas/bashTool.cjs.map +1 -0
  102. package/dist/schemas/bashTool.d.cts +25 -0
  103. package/dist/schemas/bashTool.d.cts.map +1 -0
  104. package/dist/schemas/bashTool.d.mts +25 -0
  105. package/dist/schemas/bashTool.d.mts.map +1 -0
  106. package/dist/schemas/bashTool.mjs +2 -0
  107. package/dist/schemas/bashTool.mjs.map +1 -0
  108. package/dist/schemas/runnerSpec.cjs +2 -0
  109. package/dist/schemas/runnerSpec.cjs.map +1 -0
  110. package/dist/schemas/runnerSpec.d.cts +50 -0
  111. package/dist/schemas/runnerSpec.d.cts.map +1 -0
  112. package/dist/schemas/runnerSpec.d.mts +50 -0
  113. package/dist/schemas/runnerSpec.d.mts.map +1 -0
  114. package/dist/schemas/runnerSpec.mjs +2 -0
  115. package/dist/schemas/runnerSpec.mjs.map +1 -0
  116. package/dist/services/AnsiScrub/ansiScrub.cjs +1 -1
  117. package/dist/services/AnsiScrub/ansiScrub.cjs.map +1 -1
  118. package/dist/services/AnsiScrub/ansiScrub.mjs +1 -1
  119. package/dist/services/AnsiScrub/ansiScrub.mjs.map +1 -1
  120. package/dist/services/BashRunService/BashRunService.cjs +1 -1
  121. package/dist/services/BashRunService/BashRunService.cjs.map +1 -1
  122. package/dist/services/BashRunService/BashRunService.mjs +1 -1
  123. package/dist/services/BashRunService/BashRunService.mjs.map +1 -1
  124. package/dist/services/Launcher/Launcher.cjs +1 -1
  125. package/dist/services/Launcher/Launcher.cjs.map +1 -1
  126. package/dist/services/Launcher/Launcher.mjs +1 -1
  127. package/dist/services/Launcher/Launcher.mjs.map +1 -1
  128. package/dist/services/Lifeline/NodeLifeline.cjs +1 -1
  129. package/dist/services/Lifeline/NodeLifeline.cjs.map +1 -1
  130. package/dist/services/Lifeline/NodeLifeline.mjs +1 -1
  131. package/dist/services/Lifeline/NodeLifeline.mjs.map +1 -1
  132. package/dist/services/LogFile/LogFile.cjs +1 -1
  133. package/dist/services/LogFile/LogFile.cjs.map +1 -1
  134. package/dist/services/LogFile/LogFile.mjs +1 -1
  135. package/dist/services/LogFile/LogFile.mjs.map +1 -1
  136. package/dist/services/PtyHost/PtyHost.cjs +1 -1
  137. package/dist/services/PtyHost/PtyHost.cjs.map +1 -1
  138. package/dist/services/PtyHost/PtyHost.mjs +1 -1
  139. package/dist/services/PtyHost/PtyHost.mjs.map +1 -1
  140. package/dist/services/RmuxBackend/RmuxBackend.cjs +1 -1
  141. package/dist/services/RmuxBackend/RmuxBackend.cjs.map +1 -1
  142. package/dist/services/RmuxBackend/RmuxBackend.mjs +1 -1
  143. package/dist/services/RmuxBackend/RmuxBackend.mjs.map +1 -1
  144. package/dist/services/RunnerNamer/RunnerNamer.cjs +1 -1
  145. package/dist/services/RunnerNamer/RunnerNamer.cjs.map +1 -1
  146. package/dist/services/RunnerNamer/RunnerNamer.mjs +1 -1
  147. package/dist/services/RunnerNamer/RunnerNamer.mjs.map +1 -1
  148. package/dist/services/RunnerPaths/RunnerPaths.cjs +1 -2
  149. package/dist/services/RunnerPaths/RunnerPaths.d.cts +2 -31
  150. package/dist/services/RunnerPaths/RunnerPaths.d.mts +2 -31
  151. package/dist/services/RunnerPaths/RunnerPaths.mjs +1 -2
  152. package/dist/services/RunnerPaths/index.cjs +1 -1
  153. package/dist/services/RunnerPaths/index.d.cts +2 -1
  154. package/dist/services/RunnerPaths/index.d.mts +2 -1
  155. package/dist/services/RunnerPaths/index.mjs +1 -1
  156. package/dist/services/RunnerPaths/types.d.cts +2 -0
  157. package/dist/services/RunnerPaths/types.d.cts.map +1 -1
  158. package/dist/services/RunnerPaths/types.d.mts +2 -0
  159. package/dist/services/RunnerPaths/types.d.mts.map +1 -1
  160. package/dist/services/RunnerRegistry/RunnerRegistry.cjs +1 -1
  161. package/dist/services/RunnerRegistry/RunnerRegistry.cjs.map +1 -1
  162. package/dist/services/RunnerRegistry/RunnerRegistry.mjs +1 -1
  163. package/dist/services/RunnerRegistry/RunnerRegistry.mjs.map +1 -1
  164. package/dist/services/runs/compaction.cjs +4 -0
  165. package/dist/services/runs/compaction.cjs.map +1 -0
  166. package/dist/services/runs/compaction.d.cts +14 -0
  167. package/dist/services/runs/compaction.d.cts.map +1 -0
  168. package/dist/services/runs/compaction.d.mts +14 -0
  169. package/dist/services/runs/compaction.d.mts.map +1 -0
  170. package/dist/services/runs/compaction.mjs +4 -0
  171. package/dist/services/runs/compaction.mjs.map +1 -0
  172. package/dist/services/runs/reconcile.cjs +2 -0
  173. package/dist/services/runs/reconcile.cjs.map +1 -0
  174. package/dist/services/runs/reconcile.d.cts +37 -0
  175. package/dist/services/runs/reconcile.d.cts.map +1 -0
  176. package/dist/services/runs/reconcile.d.mts +37 -0
  177. package/dist/services/runs/reconcile.d.mts.map +1 -0
  178. package/dist/services/runs/reconcile.mjs +2 -0
  179. package/dist/services/runs/reconcile.mjs.map +1 -0
  180. package/dist/services/runs/session.cjs +2 -0
  181. package/dist/services/runs/session.cjs.map +1 -0
  182. package/dist/services/runs/session.d.cts +10 -0
  183. package/dist/services/runs/session.d.cts.map +1 -0
  184. package/dist/services/runs/session.d.mts +10 -0
  185. package/dist/services/runs/session.d.mts.map +1 -0
  186. package/dist/services/runs/session.mjs +2 -0
  187. package/dist/services/runs/session.mjs.map +1 -0
  188. package/dist/session.cjs +1 -2
  189. package/dist/session.d.cts +2 -10
  190. package/dist/session.d.mts +2 -10
  191. package/dist/session.mjs +1 -2
  192. package/dist/tool/bashTool.cjs +1 -7
  193. package/dist/tool/bashTool.d.cts +2 -25
  194. package/dist/tool/bashTool.d.mts +2 -25
  195. package/dist/tool/bashTool.mjs +1 -7
  196. package/dist/tool/responseEnvelope.cjs +1 -10
  197. package/dist/tool/responseEnvelope.d.cts +2 -45
  198. package/dist/tool/responseEnvelope.d.mts +2 -45
  199. package/dist/tool/responseEnvelope.mjs +1 -10
  200. package/dist/tool/schema.cjs +1 -2
  201. package/dist/tool/schema.d.cts +2 -25
  202. package/dist/tool/schema.d.mts +2 -25
  203. package/dist/tool/schema.mjs +1 -2
  204. package/dist/tui/bashRender.cjs +4 -7
  205. package/dist/tui/bashRender.cjs.map +1 -1
  206. package/dist/tui/bashRender.d.cts +5 -5
  207. package/dist/tui/bashRender.d.cts.map +1 -1
  208. package/dist/tui/bashRender.d.mts +5 -5
  209. package/dist/tui/bashRender.d.mts.map +1 -1
  210. package/dist/tui/bashRender.mjs +4 -7
  211. package/dist/tui/bashRender.mjs.map +1 -1
  212. package/dist/tui/format.cjs +1 -1
  213. package/dist/tui/format.cjs.map +1 -1
  214. package/dist/tui/format.mjs +1 -1
  215. package/dist/tui/format.mjs.map +1 -1
  216. package/dist/tui/runnerSpace.cjs +4 -4
  217. package/dist/tui/runnerSpace.cjs.map +1 -1
  218. package/dist/tui/runnerSpace.d.cts.map +1 -1
  219. package/dist/tui/runnerSpace.d.mts.map +1 -1
  220. package/dist/tui/runnerSpace.mjs +4 -4
  221. package/dist/tui/runnerSpace.mjs.map +1 -1
  222. package/dist/types/config.cjs +2 -0
  223. package/dist/types/config.cjs.map +1 -0
  224. package/dist/types/config.d.cts +32 -0
  225. package/dist/types/config.d.cts.map +1 -0
  226. package/dist/types/config.d.mts +32 -0
  227. package/dist/types/config.d.mts.map +1 -0
  228. package/dist/types/config.mjs +2 -0
  229. package/dist/types/config.mjs.map +1 -0
  230. package/dist/types/index.cjs +2 -0
  231. package/dist/types/index.cjs.map +1 -0
  232. package/dist/types/index.d.cts +28 -0
  233. package/dist/types/index.d.cts.map +1 -0
  234. package/dist/types/index.d.mts +28 -0
  235. package/dist/types/index.d.mts.map +1 -0
  236. package/dist/types/index.mjs +2 -0
  237. package/dist/types/index.mjs.map +1 -0
  238. package/dist/types.cjs +1 -2
  239. package/dist/types.d.cts +2 -28
  240. package/dist/types.d.mts +2 -28
  241. package/dist/types.mjs +1 -2
  242. package/package.json +20 -13
  243. package/dist/cli/cliApp.cjs.map +0 -1
  244. package/dist/cli/cliApp.d.cts.map +0 -1
  245. package/dist/cli/cliApp.d.mts.map +0 -1
  246. package/dist/cli/cliApp.mjs.map +0 -1
  247. package/dist/compaction.cjs.map +0 -1
  248. package/dist/compaction.d.cts.map +0 -1
  249. package/dist/compaction.d.mts.map +0 -1
  250. package/dist/compaction.mjs.map +0 -1
  251. package/dist/config.cjs.map +0 -1
  252. package/dist/config.d.cts.map +0 -1
  253. package/dist/config.d.mts.map +0 -1
  254. package/dist/config.mjs.map +0 -1
  255. package/dist/extension.cjs.map +0 -1
  256. package/dist/extension.d.cts.map +0 -1
  257. package/dist/extension.d.mts.map +0 -1
  258. package/dist/extension.mjs.map +0 -1
  259. package/dist/ptyProvider.cjs.map +0 -1
  260. package/dist/ptyProvider.d.cts.map +0 -1
  261. package/dist/ptyProvider.d.mts.map +0 -1
  262. package/dist/ptyProvider.mjs.map +0 -1
  263. package/dist/reconcile.cjs.map +0 -1
  264. package/dist/reconcile.d.cts.map +0 -1
  265. package/dist/reconcile.d.mts.map +0 -1
  266. package/dist/reconcile.mjs.map +0 -1
  267. package/dist/runnerSpec.cjs.map +0 -1
  268. package/dist/runnerSpec.d.cts.map +0 -1
  269. package/dist/runnerSpec.d.mts.map +0 -1
  270. package/dist/runnerSpec.mjs.map +0 -1
  271. package/dist/services/RunnerPaths/RunnerPaths.cjs.map +0 -1
  272. package/dist/services/RunnerPaths/RunnerPaths.d.cts.map +0 -1
  273. package/dist/services/RunnerPaths/RunnerPaths.d.mts.map +0 -1
  274. package/dist/services/RunnerPaths/RunnerPaths.mjs.map +0 -1
  275. package/dist/session.cjs.map +0 -1
  276. package/dist/session.d.cts.map +0 -1
  277. package/dist/session.d.mts.map +0 -1
  278. package/dist/session.mjs.map +0 -1
  279. package/dist/tool/bashTool.cjs.map +0 -1
  280. package/dist/tool/bashTool.d.cts.map +0 -1
  281. package/dist/tool/bashTool.d.mts.map +0 -1
  282. package/dist/tool/bashTool.mjs.map +0 -1
  283. package/dist/tool/responseEnvelope.d.cts.map +0 -1
  284. package/dist/tool/responseEnvelope.d.mts.map +0 -1
  285. package/dist/tool/responseEnvelope.mjs.map +0 -1
  286. package/dist/tool/schema.cjs.map +0 -1
  287. package/dist/tool/schema.d.cts.map +0 -1
  288. package/dist/tool/schema.d.mts.map +0 -1
  289. package/dist/tool/schema.mjs.map +0 -1
  290. package/dist/types.cjs.map +0 -1
  291. package/dist/types.d.cts.map +0 -1
  292. package/dist/types.d.mts.map +0 -1
  293. package/dist/types.mjs.map +0 -1
@@ -1,10 +1,2 @@
1
- //#region src/session.d.ts
2
- /** Session identity inherited by commands launched from a Pi session. */
3
- declare const PI_SESSION_ID_ENV = "PI_SESSION_ID";
4
- /** Returns a usable inherited session id, failing closed on blank values. */
5
- declare function inheritedSessionId(env?: NodeJS.ProcessEnv): string | undefined;
6
- /** Returns the inherited Pi session id or fails closed outside a session. */
7
- declare function requiredSessionId(env?: NodeJS.ProcessEnv): string;
8
- //#endregion
9
- export { PI_SESSION_ID_ENV, inheritedSessionId, requiredSessionId };
10
- //# sourceMappingURL=session.d.mts.map
1
+ import { PI_SESSION_ID_ENV, inheritedSessionId, requiredSessionId } from "./services/runs/session.mjs";
2
+ export { PI_SESSION_ID_ENV, inheritedSessionId, requiredSessionId };
package/dist/session.mjs CHANGED
@@ -1,2 +1 @@
1
- const e=`PI_SESSION_ID`;function t(t=process.env){return t[e]?.trim()||void 0}function n(n=process.env){let r=t(n);if(!r)throw Error(`${e} is required for doom-runner`);return r}export{e as PI_SESSION_ID_ENV,t as inheritedSessionId,n as requiredSessionId};
2
- //# sourceMappingURL=session.mjs.map
1
+ import{PI_SESSION_ID_ENV as e,inheritedSessionId as t,requiredSessionId as n}from"./services/runs/session.mjs";export{e as PI_SESSION_ID_ENV,t as inheritedSessionId,n as requiredSessionId};
@@ -1,7 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../config.cjs"),t=require("../services/AnsiScrub/ansiScrub.cjs");require("../services/AnsiScrub/index.cjs");const n=require("./responseEnvelope.cjs"),r=require("../tui/bashRender.cjs"),i=require("./schema.cjs"),a=1e3,o=`Options:`,s={requested:`Started in the background`,threshold:`Still running after the background threshold, so it was moved to the background`,interactive:`Started on a terminal so it can prompt, which means it runs in the background`},c=`Execute bash commands, with long-running ones supervised in the background`;function l(t=e.getBackgroundThresholdMs()){return[`A command still running after ${Math.round(t/a)} seconds remains active as a background runner. You get its unique id and streaming log path, then inspect it with the doom-runner CLI.`,`Pass background: true for commands you already know will not finish on their own (dev servers, watchers, tails). Waiting the full threshold first wastes a turn.`,`Pass interactive: true only when the command will prompt for confirmation or input. Use Runner Space for terminal input. Interactive logs are noisier, so avoid it otherwise.`,`Do not re-run a command to recover output. Read its saved log with doom-runner logs <id>.`,`Stop runners you no longer need. Every runner is stopped automatically when the session ends.`]}function u(e,t){e.registerTool({name:i.BASH_TOOL_NAME,label:i.BASH_TOOL_LABEL,description:`Execute a bash command in the current working directory. A command that outlives the background threshold keeps running under doom-runner and returns a unique id plus a streaming log path. Completed commands return a bounded log tail with exact file metadata. Optionally provide a timeout in seconds, background: true to background it immediately, or interactive: true if it will prompt.`,promptSnippet:c,promptGuidelines:l(),parameters:i.BashParamsSchema,async execute(e,r,i,s,c){let{command:l,timeout:u,background:f,interactive:p,name:m}=r,h;s&&s(n.textResult(`Starting ${p===!0?`interactive runner`:f===!0?`background runner`:`command`}...`)),f!==!0&&p!==!0&&s&&(h=e=>s(n.textResult(e)));let g;try{g=await t.bashRunService.run({command:l,timeoutMs:u===void 0?void 0:u*a,background:f,interactive:p,name:m,...h?{onOutput:h}:{},sessionId:t.getSessionId()})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error([`Could not execute the command: ${t}`,``,o,`- Check the command, runtime, and working directory, then retry once.`,`- Use a read-only diagnostic to confirm the missing dependency or environment value.`,`- Ask the user if recovery requires changing the requested approach.`].join(`
2
- `),{cause:e})}return g.kind===`promoted`&&t.onRunnerStarted(g.id),d(g)},renderCall(e,t,n){return r.renderBashCall(e,t)},renderResult(e,t,n,i){return r.renderBashResult(e,t,n)}})}function d(e){if(e.kind===`failed`)throw Error([`Could not start runner "${e.name}": ${e.error}`,``,o,`- Correct the command or missing executable, then retry once.`,`- Use a read-only diagnostic to inspect the runtime environment.`,`- Ask the user if a different execution approach is required.`].join(`
3
- `));if(e.kind===`promoted`){let t=[`${s[e.reason]} as runner "${e.name}".`,`Runner ID: ${e.id}`,`Backend: ${e.backend}`,`Log (streaming): ${e.logPath}`,`Inspect it with: doom-runner logs ${e.id}`].filter(e=>e.length>0).join(`
4
- `);return{...n.textResult(t,{id:e.id,runner:e.name,pid:e.pid,logPath:e.logPath,promoted:!0,reason:e.reason})}}let r=n.summarizeLog(e.logPath),i=[];e.timedOut?i.push(`Command stopped: it exceeded the requested timeout.`):e.exitCode===null?e.signal!==null&&i.push(`Terminated by ${e.signal}`):i.push(`Exit: completed with code ${e.exitCode}.`);let a=t.stripAnsi(r.tail),c=[a.length>0?`Log tail (${r.tailLines} lines):\n${a}`:`Log tail: (no output)`,`Runner ID: ${e.id}`,...i,`Log: ${e.logPath}`,`File size: ${n.formatSize(r.bytes)}`,`Lines: ${r.lines.toLocaleString(`en-US`)}`].join(`
5
- `),l={id:e.id,runner:e.name,exitCode:e.exitCode,logPath:e.logPath,backend:e.backend,fileSize:r.bytes,lines:r.lines,tail:r.tail,tailLines:r.tailLines,...e.timedOut?{timedOut:!0}:{}};if(e.timedOut||e.signal!==null||e.exitCode!==null&&e.exitCode!==0)throw Error([c,``,o,`- Inspect the full log at ${e.logPath}, correct the cause, then retry once.`,`- Run a read-only diagnostic if the output does not explain the failure.`,`- Ask the user if the failure requires changing the requested approach.`].join(`
6
- `));return n.textResult(c,l)}exports.BASH_PROMPT_SNIPPET=c,exports.bashPromptGuidelines=l,exports.formatRunResult=d,exports.registerBashTool=u;
7
- //# sourceMappingURL=bashTool.cjs.map
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../commands/bash/bashTool.cjs");exports.BASH_PROMPT_SNIPPET=e.BASH_PROMPT_SNIPPET,exports.bashPromptGuidelines=e.bashPromptGuidelines,exports.formatRunResult=e.formatRunResult,exports.registerBashTool=e.registerBashTool;
@@ -1,25 +1,2 @@
1
- import { BashRunResult, IBashRunService } from "../services/BashRunService/types.cjs";
2
- import "../services/BashRunService/index.cjs";
3
- import { ToolResult } from "./responseEnvelope.cjs";
4
- import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
5
- //#region src/tool/bashTool.d.ts
6
- declare const BASH_PROMPT_SNIPPET = "Execute bash commands, with long-running ones supervised in the background";
7
- /** Written at registration time so the stated threshold matches the configured one. */
8
- declare function bashPromptGuidelines(thresholdMs?: number): string[];
9
- interface BashToolDependencies {
10
- bashRunService: IBashRunService;
11
- getSessionId(): string;
12
- /** Called after a runner is promoted, so UI state can refresh. */
13
- onRunnerStarted(id: string): void;
14
- }
15
- /**
16
- * Registers a tool named `bash`, replacing pi's built-in.
17
- *
18
- * The name is deliberate: hooks, guardrails and doom-pi's dispatcher all key on
19
- * `bash`, and they keep working only while the replacement keeps the name.
20
- */
21
- declare function registerBashTool(pi: ExtensionAPI, dependencies: BashToolDependencies): void;
22
- declare function formatRunResult(result: BashRunResult): ToolResult;
23
- //#endregion
24
- export { BASH_PROMPT_SNIPPET, BashToolDependencies, bashPromptGuidelines, formatRunResult, registerBashTool };
25
- //# sourceMappingURL=bashTool.d.cts.map
1
+ import { BASH_PROMPT_SNIPPET, BashToolDependencies, bashPromptGuidelines, formatRunResult, registerBashTool } from "../commands/bash/bashTool.cjs";
2
+ export { BASH_PROMPT_SNIPPET, BashToolDependencies, bashPromptGuidelines, formatRunResult, registerBashTool };
@@ -1,25 +1,2 @@
1
- import { BashRunResult, IBashRunService } from "../services/BashRunService/types.mjs";
2
- import "../services/BashRunService/index.mjs";
3
- import { ToolResult } from "./responseEnvelope.mjs";
4
- import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
5
- //#region src/tool/bashTool.d.ts
6
- declare const BASH_PROMPT_SNIPPET = "Execute bash commands, with long-running ones supervised in the background";
7
- /** Written at registration time so the stated threshold matches the configured one. */
8
- declare function bashPromptGuidelines(thresholdMs?: number): string[];
9
- interface BashToolDependencies {
10
- bashRunService: IBashRunService;
11
- getSessionId(): string;
12
- /** Called after a runner is promoted, so UI state can refresh. */
13
- onRunnerStarted(id: string): void;
14
- }
15
- /**
16
- * Registers a tool named `bash`, replacing pi's built-in.
17
- *
18
- * The name is deliberate: hooks, guardrails and doom-pi's dispatcher all key on
19
- * `bash`, and they keep working only while the replacement keeps the name.
20
- */
21
- declare function registerBashTool(pi: ExtensionAPI, dependencies: BashToolDependencies): void;
22
- declare function formatRunResult(result: BashRunResult): ToolResult;
23
- //#endregion
24
- export { BASH_PROMPT_SNIPPET, BashToolDependencies, bashPromptGuidelines, formatRunResult, registerBashTool };
25
- //# sourceMappingURL=bashTool.d.mts.map
1
+ import { BASH_PROMPT_SNIPPET, BashToolDependencies, bashPromptGuidelines, formatRunResult, registerBashTool } from "../commands/bash/bashTool.mjs";
2
+ export { BASH_PROMPT_SNIPPET, BashToolDependencies, bashPromptGuidelines, formatRunResult, registerBashTool };
@@ -1,7 +1 @@
1
- import{getBackgroundThresholdMs as e}from"../config.mjs";import{stripAnsi as t}from"../services/AnsiScrub/ansiScrub.mjs";import"../services/AnsiScrub/index.mjs";import{formatSize as n,summarizeLog as r,textResult as i}from"./responseEnvelope.mjs";import{renderBashCall as a,renderBashResult as o}from"../tui/bashRender.mjs";import{BASH_TOOL_LABEL as s,BASH_TOOL_NAME as c,BashParamsSchema as l}from"./schema.mjs";const u=1e3,d=`Options:`,f={requested:`Started in the background`,threshold:`Still running after the background threshold, so it was moved to the background`,interactive:`Started on a terminal so it can prompt, which means it runs in the background`},p=`Execute bash commands, with long-running ones supervised in the background`;function m(t=e()){return[`A command still running after ${Math.round(t/u)} seconds remains active as a background runner. You get its unique id and streaming log path, then inspect it with the doom-runner CLI.`,`Pass background: true for commands you already know will not finish on their own (dev servers, watchers, tails). Waiting the full threshold first wastes a turn.`,`Pass interactive: true only when the command will prompt for confirmation or input. Use Runner Space for terminal input. Interactive logs are noisier, so avoid it otherwise.`,`Do not re-run a command to recover output. Read its saved log with doom-runner logs <id>.`,`Stop runners you no longer need. Every runner is stopped automatically when the session ends.`]}function h(e,t){e.registerTool({name:c,label:s,description:`Execute a bash command in the current working directory. A command that outlives the background threshold keeps running under doom-runner and returns a unique id plus a streaming log path. Completed commands return a bounded log tail with exact file metadata. Optionally provide a timeout in seconds, background: true to background it immediately, or interactive: true if it will prompt.`,promptSnippet:p,promptGuidelines:m(),parameters:l,async execute(e,n,r,a,o){let{command:s,timeout:c,background:l,interactive:f,name:p}=n,m;a&&a(i(`Starting ${f===!0?`interactive runner`:l===!0?`background runner`:`command`}...`)),l!==!0&&f!==!0&&a&&(m=e=>a(i(e)));let h;try{h=await t.bashRunService.run({command:s,timeoutMs:c===void 0?void 0:c*u,background:l,interactive:f,name:p,...m?{onOutput:m}:{},sessionId:t.getSessionId()})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error([`Could not execute the command: ${t}`,``,d,`- Check the command, runtime, and working directory, then retry once.`,`- Use a read-only diagnostic to confirm the missing dependency or environment value.`,`- Ask the user if recovery requires changing the requested approach.`].join(`
2
- `),{cause:e})}return h.kind===`promoted`&&t.onRunnerStarted(h.id),g(h)},renderCall(e,t,n){return a(e,t)},renderResult(e,t,n,r){return o(e,t,n)}})}function g(e){if(e.kind===`failed`)throw Error([`Could not start runner "${e.name}": ${e.error}`,``,d,`- Correct the command or missing executable, then retry once.`,`- Use a read-only diagnostic to inspect the runtime environment.`,`- Ask the user if a different execution approach is required.`].join(`
3
- `));if(e.kind===`promoted`){let t=[`${f[e.reason]} as runner "${e.name}".`,`Runner ID: ${e.id}`,`Backend: ${e.backend}`,`Log (streaming): ${e.logPath}`,`Inspect it with: doom-runner logs ${e.id}`].filter(e=>e.length>0).join(`
4
- `);return{...i(t,{id:e.id,runner:e.name,pid:e.pid,logPath:e.logPath,promoted:!0,reason:e.reason})}}let a=r(e.logPath),o=[];e.timedOut?o.push(`Command stopped: it exceeded the requested timeout.`):e.exitCode===null?e.signal!==null&&o.push(`Terminated by ${e.signal}`):o.push(`Exit: completed with code ${e.exitCode}.`);let s=t(a.tail),c=[s.length>0?`Log tail (${a.tailLines} lines):\n${s}`:`Log tail: (no output)`,`Runner ID: ${e.id}`,...o,`Log: ${e.logPath}`,`File size: ${n(a.bytes)}`,`Lines: ${a.lines.toLocaleString(`en-US`)}`].join(`
5
- `),l={id:e.id,runner:e.name,exitCode:e.exitCode,logPath:e.logPath,backend:e.backend,fileSize:a.bytes,lines:a.lines,tail:a.tail,tailLines:a.tailLines,...e.timedOut?{timedOut:!0}:{}};if(e.timedOut||e.signal!==null||e.exitCode!==null&&e.exitCode!==0)throw Error([c,``,d,`- Inspect the full log at ${e.logPath}, correct the cause, then retry once.`,`- Run a read-only diagnostic if the output does not explain the failure.`,`- Ask the user if the failure requires changing the requested approach.`].join(`
6
- `));return i(c,l)}export{p as BASH_PROMPT_SNIPPET,m as bashPromptGuidelines,g as formatRunResult,h as registerBashTool};
7
- //# sourceMappingURL=bashTool.mjs.map
1
+ import{BASH_PROMPT_SNIPPET as e,bashPromptGuidelines as t,formatRunResult as n,registerBashTool as r}from"../commands/bash/bashTool.mjs";export{e as BASH_PROMPT_SNIPPET,t as bashPromptGuidelines,n as formatRunResult,r as registerBashTool};
@@ -1,10 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../config.cjs");let n=require("node:fs");n=e.__toESM(n,1);const r=1024,i=r*r;function a(e,t={}){return{content:[{type:`text`,text:e}],details:t}}function o(e){return a(`Error: ${e}`,{error:e})}function s(e){return e<r?`${e} B`:e<i?`${(e/r).toFixed(1)} KB`:`${(e/i).toFixed(1)} MB`}function c(e){return e.toLocaleString(`en-US`)}function l(e){return e.length===0?0:e.endsWith(`
2
- `)?e.split(`
3
- `).length-1:e.split(`
4
- `).length}function u(e,r=200,i=t.getResultMaxBytes()){try{let t=n.default.readFileSync(e,`utf8`),a=l(t),o=t.split(`
5
- `).slice(-Math.max(1,r)-+!!t.endsWith(`
6
- `)).join(`
7
- `),s=Buffer.byteLength(o,`utf8`),c=s>i?Buffer.from(o,`utf8`).subarray(s-i).toString(`utf8`):o,u=s>i?c.indexOf(`
8
- `):-1,d=u>=0?c.slice(u+1):c;return{tail:d,bytes:n.default.statSync(e).size,lines:a,tailLines:l(d)}}catch{return{tail:``,bytes:0,lines:0,tailLines:0}}}function d(e,n,r=t.getResultMaxBytes()){let i=Buffer.byteLength(e,`utf8`);if(i<=r)return{text:e,truncated:!1,outputLines:l(e)};let a=Buffer.from(e,`utf8`).subarray(i-r).toString(`utf8`),o=a.indexOf(`
9
- `),s=o===-1?a:a.slice(o+1);return{text:`${s}\n${f(s,n,e,i)}`,truncated:!0,outputLines:l(s)}}function f(e,t,n,r){let i=l(e),a=p(t),o=a?.lines??l(n),u=a?.bytes??r;return[`[output truncated: showing the last ${c(i)} lines.`,`Full output is at ${t} (${s(u)}, ${c(o)} lines).`,`Inspect it with doom-runner logs, or read the file with an offset near line ${c(o)}.]`].join(` `)}function p(e){try{let t=n.default.readFileSync(e,`utf8`);return{bytes:n.default.statSync(e).size,lines:l(t)}}catch{return}}function m(e,t=Date.now()){let n=e.interactive?` interactive`:``;return`${e.name} pid ${e.pid} up ${h(e.startedAt,t)}${n} ${e.command}`}function h(e,t){let n=Date.parse(e);if(Number.isNaN(n))return`unknown`;let r=Math.max(0,Math.floor((t-n)/1e3));if(r<60)return`${r}s`;let i=Math.floor(r/60);if(i<60)return`${i}m`;let a=Math.floor(i/60);return a<24?`${a}h`:`${Math.floor(a/24)}d`}exports.countLines=l,exports.errorResult=o,exports.formatRunnerLine=m,exports.formatSize=s,exports.formatUptime=h,exports.summarizeLog=u,exports.textResult=a,exports.truncateForResult=d;
10
- //# sourceMappingURL=responseEnvelope.cjs.map
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../commands/bash/responseEnvelope.cjs");exports.countLines=e.countLines,exports.errorResult=e.errorResult,exports.formatRunnerLine=e.formatRunnerLine,exports.formatSize=e.formatSize,exports.formatUptime=e.formatUptime,exports.summarizeLog=e.summarizeLog,exports.textResult=e.textResult,exports.truncateForResult=e.truncateForResult;
@@ -1,45 +1,2 @@
1
- import { RunnerRecord } from "../services/RunnerRegistry/types.cjs";
2
- import "../services/RunnerRegistry/index.cjs";
3
- //#region src/tool/responseEnvelope.d.ts
4
- interface ToolResult {
5
- content: Array<{
6
- type: 'text';
7
- text: string;
8
- }>;
9
- details: Record<string, unknown>;
10
- /** Tools this result makes available from here on in the transcript. */
11
- addedToolNames?: string[];
12
- }
13
- declare function textResult(text: string, details?: Record<string, unknown>): ToolResult;
14
- declare function errorResult(message: string): ToolResult;
15
- /** Human-readable byte count, matching how pi reports output sizes. */
16
- declare function formatSize(bytes: number): string;
17
- declare function countLines(text: string): number;
18
- interface TruncatedOutput {
19
- text: string;
20
- truncated: boolean;
21
- /** Lines returned, which is what the model actually received. */
22
- outputLines: number;
23
- }
24
- interface LogSummary {
25
- tail: string;
26
- bytes: number;
27
- lines: number;
28
- tailLines: number;
29
- }
30
- /** Reads a bounded tail plus exact metadata after the writer has flushed. */
31
- declare function summarizeLog(path: string, maxLines?: number, maxBytes?: number): LogSummary;
32
- /**
33
- * Keeps the tail of an oversized result and points at the file holding the rest.
34
- *
35
- * The notice states the file's real size and line count so the model does not
36
- * guess at offsets when it goes on to read the file, which is the failure this
37
- * whole policy exists to prevent.
38
- */
39
- declare function truncateForResult(text: string, fullOutputPath: string, maxBytes?: number): TruncatedOutput;
40
- /** `name pid 1234 up 3m sleep 60` — one runner per line. */
41
- declare function formatRunnerLine(record: RunnerRecord, now?: number): string;
42
- declare function formatUptime(startedAt: string, now: number): string;
43
- //#endregion
44
- export { LogSummary, ToolResult, TruncatedOutput, countLines, errorResult, formatRunnerLine, formatSize, formatUptime, summarizeLog, textResult, truncateForResult };
45
- //# sourceMappingURL=responseEnvelope.d.cts.map
1
+ import { LogSummary, ToolResult, TruncatedOutput, countLines, errorResult, formatRunnerLine, formatSize, formatUptime, summarizeLog, textResult, truncateForResult } from "../commands/bash/responseEnvelope.cjs";
2
+ export { LogSummary, ToolResult, TruncatedOutput, countLines, errorResult, formatRunnerLine, formatSize, formatUptime, summarizeLog, textResult, truncateForResult };
@@ -1,45 +1,2 @@
1
- import { RunnerRecord } from "../services/RunnerRegistry/types.mjs";
2
- import "../services/RunnerRegistry/index.mjs";
3
- //#region src/tool/responseEnvelope.d.ts
4
- interface ToolResult {
5
- content: Array<{
6
- type: 'text';
7
- text: string;
8
- }>;
9
- details: Record<string, unknown>;
10
- /** Tools this result makes available from here on in the transcript. */
11
- addedToolNames?: string[];
12
- }
13
- declare function textResult(text: string, details?: Record<string, unknown>): ToolResult;
14
- declare function errorResult(message: string): ToolResult;
15
- /** Human-readable byte count, matching how pi reports output sizes. */
16
- declare function formatSize(bytes: number): string;
17
- declare function countLines(text: string): number;
18
- interface TruncatedOutput {
19
- text: string;
20
- truncated: boolean;
21
- /** Lines returned, which is what the model actually received. */
22
- outputLines: number;
23
- }
24
- interface LogSummary {
25
- tail: string;
26
- bytes: number;
27
- lines: number;
28
- tailLines: number;
29
- }
30
- /** Reads a bounded tail plus exact metadata after the writer has flushed. */
31
- declare function summarizeLog(path: string, maxLines?: number, maxBytes?: number): LogSummary;
32
- /**
33
- * Keeps the tail of an oversized result and points at the file holding the rest.
34
- *
35
- * The notice states the file's real size and line count so the model does not
36
- * guess at offsets when it goes on to read the file, which is the failure this
37
- * whole policy exists to prevent.
38
- */
39
- declare function truncateForResult(text: string, fullOutputPath: string, maxBytes?: number): TruncatedOutput;
40
- /** `name pid 1234 up 3m sleep 60` — one runner per line. */
41
- declare function formatRunnerLine(record: RunnerRecord, now?: number): string;
42
- declare function formatUptime(startedAt: string, now: number): string;
43
- //#endregion
44
- export { LogSummary, ToolResult, TruncatedOutput, countLines, errorResult, formatRunnerLine, formatSize, formatUptime, summarizeLog, textResult, truncateForResult };
45
- //# sourceMappingURL=responseEnvelope.d.mts.map
1
+ import { LogSummary, ToolResult, TruncatedOutput, countLines, errorResult, formatRunnerLine, formatSize, formatUptime, summarizeLog, textResult, truncateForResult } from "../commands/bash/responseEnvelope.mjs";
2
+ export { LogSummary, ToolResult, TruncatedOutput, countLines, errorResult, formatRunnerLine, formatSize, formatUptime, summarizeLog, textResult, truncateForResult };
@@ -1,10 +1 @@
1
- import{getResultMaxBytes as e}from"../config.mjs";import t from"node:fs";const n=1024,r=n*n;function i(e,t={}){return{content:[{type:`text`,text:e}],details:t}}function a(e){return i(`Error: ${e}`,{error:e})}function o(e){return e<n?`${e} B`:e<r?`${(e/n).toFixed(1)} KB`:`${(e/r).toFixed(1)} MB`}function s(e){return e.toLocaleString(`en-US`)}function c(e){return e.length===0?0:e.endsWith(`
2
- `)?e.split(`
3
- `).length-1:e.split(`
4
- `).length}function l(n,r=200,i=e()){try{let e=t.readFileSync(n,`utf8`),a=c(e),o=e.split(`
5
- `).slice(-Math.max(1,r)-+!!e.endsWith(`
6
- `)).join(`
7
- `),s=Buffer.byteLength(o,`utf8`),l=s>i?Buffer.from(o,`utf8`).subarray(s-i).toString(`utf8`):o,u=s>i?l.indexOf(`
8
- `):-1,d=u>=0?l.slice(u+1):l;return{tail:d,bytes:t.statSync(n).size,lines:a,tailLines:c(d)}}catch{return{tail:``,bytes:0,lines:0,tailLines:0}}}function u(t,n,r=e()){let i=Buffer.byteLength(t,`utf8`);if(i<=r)return{text:t,truncated:!1,outputLines:c(t)};let a=Buffer.from(t,`utf8`).subarray(i-r).toString(`utf8`),o=a.indexOf(`
9
- `),s=o===-1?a:a.slice(o+1);return{text:`${s}\n${d(s,n,t,i)}`,truncated:!0,outputLines:c(s)}}function d(e,t,n,r){let i=c(e),a=f(t),l=a?.lines??c(n),u=a?.bytes??r;return[`[output truncated: showing the last ${s(i)} lines.`,`Full output is at ${t} (${o(u)}, ${s(l)} lines).`,`Inspect it with doom-runner logs, or read the file with an offset near line ${s(l)}.]`].join(` `)}function f(e){try{let n=t.readFileSync(e,`utf8`);return{bytes:t.statSync(e).size,lines:c(n)}}catch{return}}function p(e,t=Date.now()){let n=e.interactive?` interactive`:``;return`${e.name} pid ${e.pid} up ${m(e.startedAt,t)}${n} ${e.command}`}function m(e,t){let n=Date.parse(e);if(Number.isNaN(n))return`unknown`;let r=Math.max(0,Math.floor((t-n)/1e3));if(r<60)return`${r}s`;let i=Math.floor(r/60);if(i<60)return`${i}m`;let a=Math.floor(i/60);return a<24?`${a}h`:`${Math.floor(a/24)}d`}export{c as countLines,a as errorResult,p as formatRunnerLine,o as formatSize,m as formatUptime,l as summarizeLog,i as textResult,u as truncateForResult};
10
- //# sourceMappingURL=responseEnvelope.mjs.map
1
+ import{countLines as e,errorResult as t,formatRunnerLine as n,formatSize as r,formatUptime as i,summarizeLog as a,textResult as o,truncateForResult as s}from"../commands/bash/responseEnvelope.mjs";export{e as countLines,t as errorResult,n as formatRunnerLine,r as formatSize,i as formatUptime,a as summarizeLog,o as textResult,s as truncateForResult};
@@ -1,2 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("typebox");const t=e.Type.Object({command:e.Type.String({description:`Bash command to execute`}),timeout:e.Type.Optional(e.Type.Number({description:`Timeout in seconds (optional, no default timeout)`})),background:e.Type.Optional(e.Type.Boolean({description:`Start the command in the background immediately instead of waiting. Use for dev servers, watchers and other commands you already know will not finish.`})),interactive:e.Type.Optional(e.Type.Boolean({description:`Run under a pseudo terminal so the command can prompt. Use only when the command asks for confirmation or input; answer it with doom-runner input. Logs from interactive runs are noisier.`})),name:e.Type.Optional(e.Type.String({description:`Preferred runner name if the command ends up in the background. A name in use gets a suffix.`})),alarm:e.Type.Optional(e.Type.Number({description:`Send you a snapshot of this runner every N seconds, so you never have to poll it. Implies background. Use it to watch a dev server or a long build, or to wake yourself on a schedule by backgrounding a sleep loop. Minimum 10 seconds. It stops on its own when the process exits, or early with doom-runner alarm stop <id>.`}))});exports.BASH_TOOL_LABEL=`bash`,exports.BASH_TOOL_NAME=`bash`,exports.BashParamsSchema=t,exports.MIN_ALARM_SECONDS=10;
2
- //# sourceMappingURL=schema.cjs.map
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../schemas/bashTool.cjs");exports.BASH_TOOL_LABEL=e.BASH_TOOL_LABEL,exports.BASH_TOOL_NAME=e.BASH_TOOL_NAME,exports.BashParamsSchema=e.BashParamsSchema,exports.MIN_ALARM_SECONDS=e.MIN_ALARM_SECONDS;
@@ -1,25 +1,2 @@
1
- import { Static, Type } from "typebox";
2
- //#region src/tool/schema.d.ts
3
- /** Replaces pi's built-in tool of the same name, so hooks keyed on `bash` keep working. */
4
- declare const BASH_TOOL_NAME = "bash";
5
- declare const BASH_TOOL_LABEL = "bash";
6
- /** Floor for `alarm`, so a short interval cannot flood the conversation. */
7
- declare const MIN_ALARM_SECONDS = 10;
8
- /**
9
- * Built-in bash parameters plus the backgrounding controls.
10
- *
11
- * `command` and `timeout` keep their original names and wording so the tool is
12
- * a drop-in replacement. Every `description` is prompt copy the model reads.
13
- */
14
- declare const BashParamsSchema: Type.TObject<{
15
- command: Type.TString;
16
- timeout: Type.TOptional<Type.TNumber>;
17
- background: Type.TOptional<Type.TBoolean>;
18
- interactive: Type.TOptional<Type.TBoolean>;
19
- name: Type.TOptional<Type.TString>;
20
- alarm: Type.TOptional<Type.TNumber>;
21
- }>;
22
- type BashParams = Static<typeof BashParamsSchema>;
23
- //#endregion
24
- export { BASH_TOOL_LABEL, BASH_TOOL_NAME, BashParams, BashParamsSchema, MIN_ALARM_SECONDS };
25
- //# sourceMappingURL=schema.d.cts.map
1
+ import { BASH_TOOL_LABEL, BASH_TOOL_NAME, BashParams, BashParamsSchema, MIN_ALARM_SECONDS } from "../schemas/bashTool.cjs";
2
+ export { BASH_TOOL_LABEL, BASH_TOOL_NAME, BashParams, BashParamsSchema, MIN_ALARM_SECONDS };
@@ -1,25 +1,2 @@
1
- import { Static, Type } from "typebox";
2
- //#region src/tool/schema.d.ts
3
- /** Replaces pi's built-in tool of the same name, so hooks keyed on `bash` keep working. */
4
- declare const BASH_TOOL_NAME = "bash";
5
- declare const BASH_TOOL_LABEL = "bash";
6
- /** Floor for `alarm`, so a short interval cannot flood the conversation. */
7
- declare const MIN_ALARM_SECONDS = 10;
8
- /**
9
- * Built-in bash parameters plus the backgrounding controls.
10
- *
11
- * `command` and `timeout` keep their original names and wording so the tool is
12
- * a drop-in replacement. Every `description` is prompt copy the model reads.
13
- */
14
- declare const BashParamsSchema: Type.TObject<{
15
- command: Type.TString;
16
- timeout: Type.TOptional<Type.TNumber>;
17
- background: Type.TOptional<Type.TBoolean>;
18
- interactive: Type.TOptional<Type.TBoolean>;
19
- name: Type.TOptional<Type.TString>;
20
- alarm: Type.TOptional<Type.TNumber>;
21
- }>;
22
- type BashParams = Static<typeof BashParamsSchema>;
23
- //#endregion
24
- export { BASH_TOOL_LABEL, BASH_TOOL_NAME, BashParams, BashParamsSchema, MIN_ALARM_SECONDS };
25
- //# sourceMappingURL=schema.d.mts.map
1
+ import { BASH_TOOL_LABEL, BASH_TOOL_NAME, BashParams, BashParamsSchema, MIN_ALARM_SECONDS } from "../schemas/bashTool.mjs";
2
+ export { BASH_TOOL_LABEL, BASH_TOOL_NAME, BashParams, BashParamsSchema, MIN_ALARM_SECONDS };
@@ -1,2 +1 @@
1
- import{Type as e}from"typebox";const t=`bash`,n=`bash`,r=10,i=e.Object({command:e.String({description:`Bash command to execute`}),timeout:e.Optional(e.Number({description:`Timeout in seconds (optional, no default timeout)`})),background:e.Optional(e.Boolean({description:`Start the command in the background immediately instead of waiting. Use for dev servers, watchers and other commands you already know will not finish.`})),interactive:e.Optional(e.Boolean({description:`Run under a pseudo terminal so the command can prompt. Use only when the command asks for confirmation or input; answer it with doom-runner input. Logs from interactive runs are noisier.`})),name:e.Optional(e.String({description:`Preferred runner name if the command ends up in the background. A name in use gets a suffix.`})),alarm:e.Optional(e.Number({description:`Send you a snapshot of this runner every N seconds, so you never have to poll it. Implies background. Use it to watch a dev server or a long build, or to wake yourself on a schedule by backgrounding a sleep loop. Minimum 10 seconds. It stops on its own when the process exits, or early with doom-runner alarm stop <id>.`}))});export{n as BASH_TOOL_LABEL,t as BASH_TOOL_NAME,i as BashParamsSchema,r as MIN_ALARM_SECONDS};
2
- //# sourceMappingURL=schema.mjs.map
1
+ import{BASH_TOOL_LABEL as e,BASH_TOOL_NAME as t,BashParamsSchema as n,MIN_ALARM_SECONDS as r}from"../schemas/bashTool.mjs";export{e as BASH_TOOL_LABEL,t as BASH_TOOL_NAME,n as BashParamsSchema,r as MIN_ALARM_SECONDS};
@@ -1,8 +1,5 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../tool/responseEnvelope.cjs");let n=require("node:os");n=e.__toESM(n,1);let r=require("@earendil-works/pi-coding-agent"),i=require("@earendil-works/pi-tui");const a=/\[[0-9;]*m/;function o(e,t,n){return a.test(e)?e:n.fg(t,e)}function s(e,t=n.default.homedir()){return t.length>0?e.split(t).join(`~`):e}function c(e){let t=e.split(`
2
- `).map(e=>e.trim()).filter(e=>e.length>0),n=t[0]??``;return t.length>1?`${n} … +${t.length-1} lines`:n}function l(e,t=160){if(e.length<=t)return e;let n=t-3,r=Math.ceil(n/2),i=e.slice(0,r),a=e.slice(e.length-(n-r)),o=i.lastIndexOf(` `);o>i.length/2&&(i=i.slice(0,o));let s=a.indexOf(` `);return s>=0&&s<a.length/2&&(a=a.slice(s+1)),`${i} … ${a}`}function u(e){return l(s(c(e)))}function d(e){let t=[];return e.background===!0&&t.push(`bg`),e.interactive===!0&&t.push(`tty`),e.alarm!==void 0&&t.push(`alarm ${e.alarm}s`),e.timeout!==void 0&&t.push(`${e.timeout}s`),e.name!==void 0&&e.name.length>0&&t.push(e.name),t}function f(e,t,n=r.highlightCode){let a=n(u(e.command),`bash`).join(` `),o=t.fg(`toolTitle`,t.bold(`bash `))+a,s=d(e);return s.length>0&&(o+=` ${t.fg(`muted`,`· ${s.join(` · `)}`)}`),new i.Text(o,0,0)}function p(e){let n=[];return e.lines!==void 0&&n.push(`${e.lines.toLocaleString(`en-US`)} lines`),e.fileSize!==void 0&&n.push(t.formatSize(e.fileSize)),e.runner!==void 0&&n.push(e.runner),n.join(` · `)}function m(e,t){let n=(e.tail??``).split(`
3
- `);for(;n.length>0&&n[n.length-1]?.trim()===``;)n.pop();return t||n.length<=12?n:n.slice(-12)}function h(e){return(e.content??[]).map(e=>e.text??``).join(``)}function g(e,t){let n=e.split(`
4
- `);for(;n.length>0&&n[n.length-1]?.trim()===``;)n.pop();return n.length<=t?n:n.slice(-t)}function _(e,t,n){let r=e.details??{};if(t.isPartial){let t=g(h(e),12),r=n.fg(`warning`,`◐`)+n.fg(`dim`,` running`);return new i.Text([...t.map(e=>o(e,`toolOutput`,n)),r].join(`
5
- `),0,0)}if(e.isError===!0){let a=g(h(e),t.expanded?2**53-1:12),s=p(r),c=s.length>0?` ${s}`:``;return new i.Text([...a.map(e=>o(e,`toolOutput`,n)),n.fg(`error`,`✗`)+n.fg(`dim`,c)].join(`
6
- `),0,0)}if(r.promoted===!0){let e=r.runner??r.id??`runner`,t=r.id===void 0?``:` · ${r.id}`;return new i.Text(n.fg(`accent`,`● ${e}`)+n.fg(`muted`,`${t} · background`),0,0)}let a=m(r,t.expanded),s=r.exitCode!==void 0&&r.exitCode!==null,c=r.timedOut===!0||s&&r.exitCode!==0,l=c?n.fg(`error`,`✗`):n.fg(`success`,`✓`),u=[];r.timedOut===!0?u.push(`timed out`):c&&u.push(`exit ${r.exitCode}`);let d=p(r);d.length>0&&u.push(d),!t.expanded&&(r.tailLines??0)>a.length&&u.push(`ctrl+o`);let f=a.map(e=>o(e,`toolOutput`,n));return f.push(l+n.fg(`dim`,u.length>0?` ${u.join(` · `)}`:``)),new i.Text(f.join(`
7
- `),0,0)}exports.abbreviateHome=s,exports.collapseCommand=c,exports.formatBashCommand=u,exports.formatBashFlags=d,exports.formatResultSummary=p,exports.renderBashCall=f,exports.renderBashResult=_,exports.truncateMiddle=l;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../commands/bash/responseEnvelope.cjs");let n=require("node:os");n=e.__toESM(n,1);let r=require("@agimon-ai/doompi-ui/toolChrome"),i=require("@earendil-works/pi-coding-agent"),a=require("@earendil-works/pi-tui");var o=class{lines;layout;constructor(e,t){this.lines=e,this.layout=t}render(e){if(e<=0)return[];let t=Math.max(1,e-2),n=this.layout.wrap?this.lines.flatMap(e=>(0,a.wrapTextWithAnsi)(e,t)):this.lines.map(e=>(0,a.truncateToWidth)(e,t,`…`));this.layout.separator&&n.unshift(this.layout.separator(`─`.repeat(t)));let r=n.map(e=>` ${e}`);return this.layout.trailingBlank&&r.push(``),r}invalidate(){}};function s(e,t,n){return new o(e,{wrap:t,separator:e=>n.fg(`borderMuted`,e),trailingBlank:!0})}const c=/\u001b\[[0-9;]*m/;function l(e,t,n){return c.test(e)?e:n.fg(t,e)}function u(e,t=n.default.homedir()){return t.length>0?e.split(t).join(`~`):e}function d(e){let t=e.split(`
2
+ `).map(e=>e.trim()).filter(e=>e.length>0),n=t[0]??``;return t.length>1?`${n} … +${t.length-1} lines`:n}function f(e,t=160){if(e.length<=t)return e;let n=t-3,r=Math.ceil(n/2),i=e.slice(0,r),a=e.slice(e.length-(n-r)),o=i.lastIndexOf(` `);o>i.length/2&&(i=i.slice(0,o));let s=a.indexOf(` `);return s>=0&&s<a.length/2&&(a=a.slice(s+1)),`${i} … ${a}`}function p(e){return u(d(e))}function m(e){let t=[];return e.background===!0&&t.push(`bg`),e.interactive===!0&&t.push(`tty`),e.alarm!==void 0&&t.push(`alarm ${e.alarm}s`),e.timeout!==void 0&&t.push(`${e.timeout}s`),e.name!==void 0&&e.name.length>0&&t.push(e.name),t}function h(e,t,n=i.highlightCode){let a=n(p(e.command),`bash`).join(` `),o=`${(0,r.renderToolBadge)(`bash`,t)} ${a}`,s=m(e);return s.length>0&&(o+=` ${t.fg(`muted`,`· ${s.join(` · `)}`)}`),new r.DoomToolCall(o)}function g(e){let n=[];return e.lines!==void 0&&n.push(`${e.lines.toLocaleString(`en-US`)} lines`),e.fileSize!==void 0&&n.push(t.formatSize(e.fileSize)),e.runner!==void 0&&n.push(e.runner),n.join(` · `)}function _(e,t){let n=(e.tail??``).split(`
3
+ `);for(;n.length>0&&n[n.length-1]?.trim()===``;)n.pop();return t||n.length<=12?n:n.slice(-12)}function v(e){return(e.content??[]).map(e=>e.text??``).join(``)}function y(e,t){let n=e.split(`
4
+ `);for(;n.length>0&&n[n.length-1]?.trim()===``;)n.pop();return n.length<=t?n:n.slice(-t)}function b(e,t,n){let r=e.details??{};if(t.isPartial){let t=y(v(e),12),r=n.fg(`warning`,`◐`)+n.fg(`dim`,` running`);return s([...t.map(e=>l(e,`toolOutput`,n)),r],!1,n)}if(t.isError===!0){let i=y(v(e),t.expanded?2**53-1:12),a=g(r),o=a.length>0?` ${a}`:` failed`;return s([...i.map(e=>l(e,`toolOutput`,n)),n.fg(`error`,`✗`)+n.fg(`dim`,o)],t.expanded,n)}if(r.promoted===!0){let e=r.runner??r.id??`runner`,t=r.id===void 0?``:` · ${r.id}`;return s([n.fg(`accent`,`● ${e}`)+n.fg(`muted`,`${t} · background`)],!1,n)}let i=_(r,t.expanded),a=r.exitCode!==void 0&&r.exitCode!==null,o=r.timedOut===!0||a&&r.exitCode!==0,c=o?n.fg(`error`,`✗`):n.fg(`success`,`✓`),u=[];r.timedOut===!0?u.push(`timed out`):o&&u.push(`exit ${r.exitCode}`);let d=g(r);d.length>0&&u.push(d),!t.expanded&&(r.tailLines??0)>i.length&&u.push(`ctrl+o`);let f=i.map(e=>l(e,`toolOutput`,n)),p=u.length>0?` ${u.join(` · `)}`:o?` failed`:i.length===0?` done`:``;return p.length>0&&f.push(c+n.fg(`dim`,p)),s(f,t.expanded,n)}exports.abbreviateHome=u,exports.collapseCommand=d,exports.formatBashCommand=p,exports.formatBashFlags=m,exports.formatResultSummary=g,exports.renderBashCall=h,exports.renderBashResult=b,exports.truncateMiddle=f;
8
5
  //# sourceMappingURL=bashRender.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"bashRender.cjs","names":["os","highlightCode","Text","formatSize"],"sources":["../../src/tui/bashRender.ts"],"sourcesContent":["import os from 'node:os';\nimport { highlightCode, type Theme, type ThemeColor } from '@earendil-works/pi-coding-agent';\nimport { Text } from '@earendil-works/pi-tui';\nimport { formatSize } from '../tool/responseEnvelope.ts';\nimport type { BashParams } from '../tool/schema.ts';\n\nconst ELLIPSIS = '…';\n/** Spaced so the join never glues two words into one unreadable token. */\nconst GAP = ' … ';\n/** Wide enough to keep a realistic command intact, short enough to stay under three wrapped rows. */\nconst MAX_COMMAND_LENGTH = 160;\n\n/**\n * Syntax highlighter for the command line.\n *\n * `highlightCode` reads Pi's global theme singleton rather than the theme passed\n * to a renderer, so it is injected here to keep these functions testable.\n */\nexport type Highlighter = (code: string, lang?: string) => string[];\n/** Lines of log kept in the collapsed result, before `ctrl+o` reveals the rest. */\nconst COLLAPSED_TAIL_LINES = 12;\n/** Live output is unbounded while a command runs, so the running state keeps only a window. */\nconst STREAM_TAIL_LINES = 12;\n\n/** An SGR colour sequence, the only escape the log pipeline is allowed to keep. */\nconst SGR_PATTERN = /\u001b\\[[0-9;]*m/;\n\n/**\n * Applies a theme colour only to lines that carry none of their own.\n *\n * A command that emits colour has already said how its output should look, and\n * wrapping it would reset to the terminal default at the first embedded escape.\n */\nfunction paint(line: string, color: ThemeColor, theme: Theme): string {\n return SGR_PATTERN.test(line) ? line : theme.fg(color, line);\n}\n\n/** Structured view of what `formatRunResult` reports, read only by the renderers. */\nexport interface BashResultDetails {\n id?: string;\n runner?: string;\n exitCode?: number | null;\n logPath?: string;\n fileSize?: number;\n lines?: number;\n timedOut?: boolean;\n promoted?: boolean;\n reason?: string;\n tail?: string;\n tailLines?: number;\n}\n\n/** `/Users/me/x` becomes `~/x`. Absolute home paths are what push these lines onto extra rows. */\nexport function abbreviateHome(text: string, home: string = os.homedir()): string {\n return home.length > 0 ? text.split(home).join('~') : text;\n}\n\n/** Collapse a heredoc or multi-line script to one scannable line plus a count. */\nexport function collapseCommand(command: string): string {\n const lines = command\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n const first = lines[0] ?? '';\n return lines.length > 1 ? `${first} ${ELLIPSIS} +${lines.length - 1} lines` : first;\n}\n\n/** Drop the middle rather than the end, so a long pipeline keeps both its source and its sink. */\nexport function truncateMiddle(text: string, max: number = MAX_COMMAND_LENGTH): string {\n if (text.length <= max) return text;\n const keep = max - GAP.length;\n const head = Math.ceil(keep / 2);\n let start = text.slice(0, head);\n let end = text.slice(text.length - (keep - head));\n\n // Snap both cuts to a token boundary, so neither side ends mid-word. Each is\n // only moved when a boundary exists in the half nearest the cut, otherwise a\n // single long argument would swallow the whole budget.\n const lastSpace = start.lastIndexOf(' ');\n if (lastSpace > start.length / 2) start = start.slice(0, lastSpace);\n const firstSpace = end.indexOf(' ');\n if (firstSpace >= 0 && firstSpace < end.length / 2) end = end.slice(firstSpace + 1);\n\n return `${start}${GAP}${end}`;\n}\n\nexport function formatBashCommand(command: string): string {\n return truncateMiddle(abbreviateHome(collapseCommand(command)));\n}\n\n/** Modifiers worth seeing at a glance, in the widget's `·` idiom. */\nexport function formatBashFlags(args: BashParams): string[] {\n const flags: string[] = [];\n if (args.background === true) flags.push('bg');\n if (args.interactive === true) flags.push('tty');\n if (args.alarm !== undefined) flags.push(`alarm ${args.alarm}s`);\n if (args.timeout !== undefined) flags.push(`${args.timeout}s`);\n if (args.name !== undefined && args.name.length > 0) flags.push(args.name);\n return flags;\n}\n\nexport function renderBashCall(args: BashParams, theme: Theme, highlight: Highlighter = highlightCode): Text {\n // Highlight after shortening: the ANSI codes would break any later slicing.\n const command = highlight(formatBashCommand(args.command), 'bash').join(' ');\n let text = theme.fg('toolTitle', theme.bold('bash ')) + command;\n const flags = formatBashFlags(args);\n if (flags.length > 0) text += ` ${theme.fg('muted', `· ${flags.join(' · ')}`)}`;\n return new Text(text, 0, 0);\n}\n\n/** The metadata footer, collapsed from five lines to one. */\nexport function formatResultSummary(details: BashResultDetails): string {\n const parts: string[] = [];\n if (details.lines !== undefined) parts.push(`${details.lines.toLocaleString('en-US')} lines`);\n if (details.fileSize !== undefined) parts.push(formatSize(details.fileSize));\n if (details.runner !== undefined) parts.push(details.runner);\n return parts.join(' · ');\n}\n\nfunction tailLines(details: BashResultDetails, expanded: boolean): string[] {\n const lines = (details.tail ?? '').split('\\n');\n while (lines.length > 0 && lines[lines.length - 1]?.trim() === '') lines.pop();\n if (expanded || lines.length <= COLLAPSED_TAIL_LINES) return lines;\n return lines.slice(-COLLAPSED_TAIL_LINES);\n}\n\n/** Concatenated text of a tool result, which is all a thrown failure leaves behind. */\nfunction contentText(result: { content?: Array<{ type: string; text?: string }> }): string {\n return (result.content ?? []).map((block) => block.text ?? '').join('');\n}\n\n/** Last `max` lines, so a chatty command cannot push the transcript off screen. */\nfunction lastLines(text: string, max: number): string[] {\n const all = text.split('\\n');\n while (all.length > 0 && all[all.length - 1]?.trim() === '') all.pop();\n return all.length <= max ? all : all.slice(-max);\n}\n\nexport function renderBashResult(\n result: { content?: Array<{ type: string; text?: string }>; details?: unknown; isError?: boolean },\n options: { expanded: boolean; isPartial: boolean },\n theme: Theme,\n): Text {\n const details = (result.details ?? {}) as BashResultDetails;\n\n // Running. Details do not exist yet, so the streamed text is the only source,\n // and it is bounded here because it grows without limit while the command runs.\n if (options.isPartial) {\n const streamed = lastLines(contentText(result), STREAM_TAIL_LINES);\n const label = theme.fg('warning', '◐') + theme.fg('dim', ' running');\n return new Text([...streamed.map((line) => paint(line, 'toolOutput', theme)), label].join('\\n'), 0, 0);\n }\n\n // Failed. formatRunResult throws on a bad exit, so Pi hands back the error\n // text with no details at all; without this the block would render a bare tick.\n if (result.isError === true) {\n const body = lastLines(contentText(result), options.expanded ? Number.MAX_SAFE_INTEGER : COLLAPSED_TAIL_LINES);\n const summary = formatResultSummary(details);\n const meta = summary.length > 0 ? ` ${summary}` : '';\n return new Text(\n [...body.map((line) => paint(line, 'toolOutput', theme)), theme.fg('error', '✗') + theme.fg('dim', meta)].join(\n '\\n',\n ),\n 0,\n 0,\n );\n }\n\n if (details.promoted === true) {\n const label = details.runner ?? details.id ?? 'runner';\n const suffix = details.id === undefined ? '' : ` · ${details.id}`;\n return new Text(theme.fg('accent', `● ${label}`) + theme.fg('muted', `${suffix} · background`), 0, 0);\n }\n\n const body = tailLines(details, options.expanded);\n // An absent exit code means the runner never reported one, which is not a failure.\n const exited = details.exitCode !== undefined && details.exitCode !== null;\n const failed = details.timedOut === true || (exited && details.exitCode !== 0);\n const glyph = failed ? theme.fg('error', '✗') : theme.fg('success', '✓');\n\n const meta: string[] = [];\n if (details.timedOut === true) meta.push('timed out');\n else if (failed) meta.push(`exit ${details.exitCode}`);\n const summary = formatResultSummary(details);\n if (summary.length > 0) meta.push(summary);\n if (!options.expanded && (details.tailLines ?? 0) > body.length) meta.push('ctrl+o');\n\n const lines = body.map((line) => paint(line, 'toolOutput', theme));\n // `dim`, not `muted`: muted sits too close to toolOutput to read as chrome.\n lines.push(glyph + theme.fg('dim', meta.length > 0 ? ` ${meta.join(' · ')}` : ''));\n return new Text(lines.join('\\n'), 0, 0);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bashRender.cjs","names":["wrapTextWithAnsi","truncateToWidth","os","highlightCode","renderToolBadge","DoomToolCall","formatSize"],"sources":["../../src/tui/bashRender.ts"],"sourcesContent":["import os from 'node:os';\nimport { DoomToolCall, renderToolBadge } from '@agimon-ai/doompi-ui/toolChrome';\nimport { highlightCode, type Theme, type ThemeColor } from '@earendil-works/pi-coding-agent';\nimport { type Component, truncateToWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';\nimport { formatSize } from '../commands/bash/responseEnvelope.ts';\nimport type { BashParams } from '../schemas/bashTool.ts';\n\nconst ELLIPSIS = '…';\n/** Spaced so the join never glues two words into one unreadable token. */\nconst GAP = ' … ';\n/** Wide enough to keep a realistic command intact, short enough to stay under three wrapped rows. */\nconst MAX_COMMAND_LENGTH = 160;\n\n/**\n * Syntax highlighter for the command line.\n *\n * `highlightCode` reads Pi's global theme singleton rather than the theme passed\n * to a renderer, so it is injected here to keep these functions testable.\n */\nexport type Highlighter = (code: string, lang?: string) => string[];\n/** Lines of log kept in the collapsed result, before `ctrl+o` reveals the rest. */\nconst COLLAPSED_TAIL_LINES = 12;\n/** Live output is unbounded while a command runs, so the running state keeps only a window. */\nconst STREAM_TAIL_LINES = 12;\n\n/**\n * Width-aware tool text.\n *\n * Pi's Text component wraps every long line. That is useful for prose, but it\n * makes shell pipelines and grep output consume several visual rows and blurs\n * the boundary between separate log lines. Collapsed tool output is instead\n * clipped per logical line; expanded output keeps all content by wrapping it.\n */\ninterface BashToolTextLayout {\n wrap: boolean;\n separator?: (line: string) => string;\n trailingBlank?: boolean;\n}\n\nclass BashToolText implements Component {\n constructor(\n private readonly lines: readonly string[],\n private readonly layout: BashToolTextLayout,\n ) {}\n\n render(width: number): string[] {\n if (width <= 0) return [];\n const contentWidth = Math.max(1, width - 2);\n const rendered = this.layout.wrap\n ? this.lines.flatMap((line) => wrapTextWithAnsi(line, contentWidth))\n : this.lines.map((line) => truncateToWidth(line, contentWidth, ELLIPSIS));\n if (this.layout.separator) rendered.unshift(this.layout.separator('─'.repeat(contentWidth)));\n const padded = rendered.map((line) => ` ${line}`);\n if (this.layout.trailingBlank) padded.push('');\n return padded;\n }\n\n invalidate(): void {\n // Content is immutable and rendering reads the current width every time.\n }\n}\n\nfunction renderResultText(lines: readonly string[], wrap: boolean, theme: Theme): Component {\n return new BashToolText(lines, {\n wrap,\n // State belongs in the result summary below; keep this divider structural.\n separator: (line) => theme.fg('borderMuted', line),\n trailingBlank: true,\n });\n}\n\n/** An SGR colour sequence, the only escape the log pipeline is allowed to keep. */\n/* oxlint-disable no-control-regex -- matching SGR requires the ESC control character */\n// biome-ignore lint/suspicious/noControlCharactersInRegex: Matching SGR requires the ESC control character.\nconst SGR_PATTERN = /\\u001b\\[[0-9;]*m/;\n/* oxlint-enable no-control-regex */\n\n/**\n * Applies a theme colour only to lines that carry none of their own.\n *\n * A command that emits colour has already said how its output should look, and\n * wrapping it would reset to the terminal default at the first embedded escape.\n */\nfunction paint(line: string, color: ThemeColor, theme: Theme): string {\n return SGR_PATTERN.test(line) ? line : theme.fg(color, line);\n}\n\n/** Structured view of what `formatRunResult` reports, read only by the renderers. */\nexport interface BashResultDetails {\n id?: string;\n runner?: string;\n exitCode?: number | null;\n logPath?: string;\n fileSize?: number;\n lines?: number;\n timedOut?: boolean;\n promoted?: boolean;\n reason?: string;\n tail?: string;\n tailLines?: number;\n}\n\n/** `/Users/me/x` becomes `~/x`. Absolute home paths are what push these lines onto extra rows. */\nexport function abbreviateHome(text: string, home: string = os.homedir()): string {\n return home.length > 0 ? text.split(home).join('~') : text;\n}\n\n/** Collapse a heredoc or multi-line script to one scannable line plus a count. */\nexport function collapseCommand(command: string): string {\n const lines = command\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n const first = lines[0] ?? '';\n return lines.length > 1 ? `${first} ${ELLIPSIS} +${lines.length - 1} lines` : first;\n}\n\n/** Drop the middle rather than the end, so a long pipeline keeps both its source and its sink. */\nexport function truncateMiddle(text: string, max: number = MAX_COMMAND_LENGTH): string {\n if (text.length <= max) return text;\n const keep = max - GAP.length;\n const head = Math.ceil(keep / 2);\n let start = text.slice(0, head);\n let end = text.slice(text.length - (keep - head));\n\n // Snap both cuts to a token boundary, so neither side ends mid-word. Each is\n // only moved when a boundary exists in the half nearest the cut, otherwise a\n // single long argument would swallow the whole budget.\n const lastSpace = start.lastIndexOf(' ');\n if (lastSpace > start.length / 2) start = start.slice(0, lastSpace);\n const firstSpace = end.indexOf(' ');\n if (firstSpace >= 0 && firstSpace < end.length / 2) end = end.slice(firstSpace + 1);\n\n return `${start}${GAP}${end}`;\n}\n\nexport function formatBashCommand(command: string): string {\n return abbreviateHome(collapseCommand(command));\n}\n\n/** Modifiers worth seeing at a glance, in the widget's `·` idiom. */\nexport function formatBashFlags(args: BashParams): string[] {\n const flags: string[] = [];\n if (args.background === true) flags.push('bg');\n if (args.interactive === true) flags.push('tty');\n if (args.alarm !== undefined) flags.push(`alarm ${args.alarm}s`);\n if (args.timeout !== undefined) flags.push(`${args.timeout}s`);\n if (args.name !== undefined && args.name.length > 0) flags.push(args.name);\n return flags;\n}\n\nexport function renderBashCall(args: BashParams, theme: Theme, highlight: Highlighter = highlightCode): Component {\n // Highlight after collapsing multi-line scripts: ANSI codes would break that transformation.\n const command = highlight(formatBashCommand(args.command), 'bash').join(' ');\n let text = `${renderToolBadge('bash', theme)} ${command}`;\n const flags = formatBashFlags(args);\n if (flags.length > 0) text += ` ${theme.fg('muted', `· ${flags.join(' · ')}`)}`;\n // A command is operator input, not log output: wrap it so the complete\n // pipeline remains inspectable instead of clipping its most important tail.\n return new DoomToolCall(text);\n}\n\n/** The metadata footer, collapsed from five lines to one. */\nexport function formatResultSummary(details: BashResultDetails): string {\n const parts: string[] = [];\n if (details.lines !== undefined) parts.push(`${details.lines.toLocaleString('en-US')} lines`);\n if (details.fileSize !== undefined) parts.push(formatSize(details.fileSize));\n if (details.runner !== undefined) parts.push(details.runner);\n return parts.join(' · ');\n}\n\nfunction tailLines(details: BashResultDetails, expanded: boolean): string[] {\n const lines = (details.tail ?? '').split('\\n');\n while (lines.length > 0 && lines[lines.length - 1]?.trim() === '') lines.pop();\n if (expanded || lines.length <= COLLAPSED_TAIL_LINES) return lines;\n return lines.slice(-COLLAPSED_TAIL_LINES);\n}\n\n/** Concatenated text of a tool result, which is all a thrown failure leaves behind. */\nfunction contentText(result: { content?: Array<{ type: string; text?: string }> }): string {\n return (result.content ?? []).map((block) => block.text ?? '').join('');\n}\n\n/** Last `max` lines, so a chatty command cannot push the transcript off screen. */\nfunction lastLines(text: string, max: number): string[] {\n const all = text.split('\\n');\n while (all.length > 0 && all[all.length - 1]?.trim() === '') all.pop();\n return all.length <= max ? all : all.slice(-max);\n}\n\nexport function renderBashResult(\n result: { content?: Array<{ type: string; text?: string }>; details?: unknown },\n options: { expanded: boolean; isPartial: boolean; isError?: boolean },\n theme: Theme,\n): Component {\n const details = (result.details ?? {}) as BashResultDetails;\n\n // Running. Details do not exist yet, so the streamed text is the only source,\n // and it is bounded here because it grows without limit while the command runs.\n if (options.isPartial) {\n const streamed = lastLines(contentText(result), STREAM_TAIL_LINES);\n const label = theme.fg('warning', '◐') + theme.fg('dim', ' running');\n return renderResultText([...streamed.map((line) => paint(line, 'toolOutput', theme)), label], false, theme);\n }\n\n // Failed. formatRunResult throws on a bad exit, so Pi hands back the error\n // text with no details at all; without this the block would render a bare tick.\n if (options.isError === true) {\n const body = lastLines(contentText(result), options.expanded ? Number.MAX_SAFE_INTEGER : COLLAPSED_TAIL_LINES);\n const summary = formatResultSummary(details);\n const meta = summary.length > 0 ? ` ${summary}` : ' failed';\n return renderResultText(\n [...body.map((line) => paint(line, 'toolOutput', theme)), theme.fg('error', '✗') + theme.fg('dim', meta)],\n options.expanded,\n theme,\n );\n }\n\n if (details.promoted === true) {\n const label = details.runner ?? details.id ?? 'runner';\n const suffix = details.id === undefined ? '' : ` · ${details.id}`;\n return renderResultText(\n [theme.fg('accent', `● ${label}`) + theme.fg('muted', `${suffix} · background`)],\n false,\n theme,\n );\n }\n\n const body = tailLines(details, options.expanded);\n // An absent exit code means the runner never reported one, which is not a failure.\n const exited = details.exitCode !== undefined && details.exitCode !== null;\n const failed = details.timedOut === true || (exited && details.exitCode !== 0);\n const glyph = failed ? theme.fg('error', '✗') : theme.fg('success', '✓');\n\n const meta: string[] = [];\n if (details.timedOut === true) meta.push('timed out');\n else if (failed) meta.push(`exit ${details.exitCode}`);\n const summary = formatResultSummary(details);\n if (summary.length > 0) meta.push(summary);\n if (!options.expanded && (details.tailLines ?? 0) > body.length) meta.push('ctrl+o');\n\n const lines = body.map((line) => paint(line, 'toolOutput', theme));\n // `dim`, not `muted`: muted sits too close to toolOutput to read as chrome.\n const status = meta.length > 0 ? ` ${meta.join(' · ')}` : failed ? ' failed' : body.length === 0 ? ' done' : '';\n if (status.length > 0) lines.push(glyph + theme.fg('dim', status));\n return renderResultText(lines, options.expanded, theme);\n}\n"],"mappings":"uVAuCA,IAAM,EAAN,KAAwC,CAEnB,MACA,OAFnB,YACE,EACA,EACA,CAFiB,KAAA,MAAA,EACA,KAAA,OAAA,CAChB,CAEH,OAAO,EAAyB,CAC9B,GAAI,GAAS,EAAG,MAAO,CAAC,EACxB,IAAM,EAAe,KAAK,IAAI,EAAG,EAAQ,CAAC,EACpC,EAAW,KAAK,OAAO,KACzB,KAAK,MAAM,QAAS,IAAA,EAASA,EAAAA,iBAAAA,CAAiB,EAAM,CAAY,CAAC,EACjE,KAAK,MAAM,IAAK,IAAA,EAASC,EAAAA,gBAAAA,CAAgB,EAAM,EAAc,GAAQ,CAAC,EACtE,KAAK,OAAO,WAAW,EAAS,QAAQ,KAAK,OAAO,UAAU,IAAI,OAAO,CAAY,CAAC,CAAC,EAC3F,IAAM,EAAS,EAAS,IAAK,GAAS,IAAI,GAAM,EAEhD,OADI,KAAK,OAAO,eAAe,EAAO,KAAK,EAAE,EACtC,CACT,CAEA,YAAmB,CAEnB,CACF,EAEA,SAAS,EAAiB,EAA0B,EAAe,EAAyB,CAC1F,OAAO,IAAI,EAAa,EAAO,CAC7B,OAEA,UAAY,GAAS,EAAM,GAAG,cAAe,CAAI,EACjD,cAAe,EACjB,CAAC,CACH,CAKA,MAAM,EAAc,mBASpB,SAAS,EAAM,EAAc,EAAmB,EAAsB,CACpE,OAAO,EAAY,KAAK,CAAI,EAAI,EAAO,EAAM,GAAG,EAAO,CAAI,CAC7D,CAkBA,SAAgB,EAAe,EAAc,EAAeC,EAAAA,QAAG,QAAQ,EAAW,CAChF,OAAO,EAAK,OAAS,EAAI,EAAK,MAAM,CAAI,CAAC,CAAC,KAAK,GAAG,EAAI,CACxD,CAGA,SAAgB,EAAgB,EAAyB,CACvD,IAAM,EAAQ,EACX,MAAM;CAAI,CAAC,CACX,IAAK,GAAS,EAAK,KAAK,CAAC,CAAC,CAC1B,OAAQ,GAAS,EAAK,OAAS,CAAC,EAC7B,EAAQ,EAAM,IAAM,GAC1B,OAAO,EAAM,OAAS,EAAI,GAAG,EAAM,MAAgB,EAAM,OAAS,EAAE,QAAU,CAChF,CAGA,SAAgB,EAAe,EAAc,EAAc,IAA4B,CACrF,GAAI,EAAK,QAAU,EAAK,OAAO,EAC/B,IAAM,EAAO,EAAM,EACb,EAAO,KAAK,KAAK,EAAO,CAAC,EAC3B,EAAQ,EAAK,MAAM,EAAG,CAAI,EAC1B,EAAM,EAAK,MAAM,EAAK,QAAU,EAAO,EAAK,EAK1C,EAAY,EAAM,YAAY,GAAG,EACnC,EAAY,EAAM,OAAS,IAAG,EAAQ,EAAM,MAAM,EAAG,CAAS,GAClE,IAAM,EAAa,EAAI,QAAQ,GAAG,EAGlC,OAFI,GAAc,GAAK,EAAa,EAAI,OAAS,IAAG,EAAM,EAAI,MAAM,EAAa,CAAC,GAE3E,GAAG,OAAc,GAC1B,CAEA,SAAgB,EAAkB,EAAyB,CACzD,OAAO,EAAe,EAAgB,CAAO,CAAC,CAChD,CAGA,SAAgB,EAAgB,EAA4B,CAC1D,IAAM,EAAkB,CAAC,EAMzB,OALI,EAAK,aAAe,IAAM,EAAM,KAAK,IAAI,EACzC,EAAK,cAAgB,IAAM,EAAM,KAAK,KAAK,EAC3C,EAAK,QAAU,IAAA,IAAW,EAAM,KAAK,SAAS,EAAK,MAAM,EAAE,EAC3D,EAAK,UAAY,IAAA,IAAW,EAAM,KAAK,GAAG,EAAK,QAAQ,EAAE,EACzD,EAAK,OAAS,IAAA,IAAa,EAAK,KAAK,OAAS,GAAG,EAAM,KAAK,EAAK,IAAI,EAClE,CACT,CAEA,SAAgB,EAAe,EAAkB,EAAc,EAAyBC,EAAAA,cAA0B,CAEhH,IAAM,EAAU,EAAU,EAAkB,EAAK,OAAO,EAAG,MAAM,CAAC,CAAC,KAAK,GAAG,EACvE,EAAO,IAAA,EAAGC,EAAAA,gBAAAA,CAAgB,OAAQ,CAAK,EAAE,GAAG,IAC1C,EAAQ,EAAgB,CAAI,EAIlC,OAHI,EAAM,OAAS,IAAG,GAAQ,IAAI,EAAM,GAAG,QAAS,KAAK,EAAM,KAAK,KAAK,GAAG,KAGrE,IAAIC,EAAAA,aAAa,CAAI,CAC9B,CAGA,SAAgB,EAAoB,EAAoC,CACtE,IAAM,EAAkB,CAAC,EAIzB,OAHI,EAAQ,QAAU,IAAA,IAAW,EAAM,KAAK,GAAG,EAAQ,MAAM,eAAe,OAAO,EAAE,OAAO,EACxF,EAAQ,WAAa,IAAA,IAAW,EAAM,KAAKC,EAAAA,WAAW,EAAQ,QAAQ,CAAC,EACvE,EAAQ,SAAW,IAAA,IAAW,EAAM,KAAK,EAAQ,MAAM,EACpD,EAAM,KAAK,KAAK,CACzB,CAEA,SAAS,EAAU,EAA4B,EAA6B,CAC1E,IAAM,GAAS,EAAQ,MAAQ,GAAA,CAAI,MAAM;CAAI,EAC7C,KAAO,EAAM,OAAS,GAAK,EAAM,EAAM,OAAS,EAAE,EAAE,KAAK,IAAM,IAAI,EAAM,IAAI,EAE7E,OADI,GAAY,EAAM,QAAU,GAA6B,EACtD,EAAM,MAAM,GAAqB,CAC1C,CAGA,SAAS,EAAY,EAAsE,CACzF,OAAQ,EAAO,SAAW,CAAC,EAAA,CAAG,IAAK,GAAU,EAAM,MAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,CACxE,CAGA,SAAS,EAAU,EAAc,EAAuB,CACtD,IAAM,EAAM,EAAK,MAAM;CAAI,EAC3B,KAAO,EAAI,OAAS,GAAK,EAAI,EAAI,OAAS,EAAE,EAAE,KAAK,IAAM,IAAI,EAAI,IAAI,EACrE,OAAO,EAAI,QAAU,EAAM,EAAM,EAAI,MAAM,CAAC,CAAG,CACjD,CAEA,SAAgB,EACd,EACA,EACA,EACW,CACX,IAAM,EAAW,EAAO,SAAW,CAAC,EAIpC,GAAI,EAAQ,UAAW,CACrB,IAAM,EAAW,EAAU,EAAY,CAAM,EAAG,EAAiB,EAC3D,EAAQ,EAAM,GAAG,UAAW,GAAG,EAAI,EAAM,GAAG,MAAO,UAAU,EACnE,OAAO,EAAiB,CAAC,GAAG,EAAS,IAAK,GAAS,EAAM,EAAM,aAAc,CAAK,CAAC,EAAG,CAAK,EAAG,GAAO,CAAK,CAC5G,CAIA,GAAI,EAAQ,UAAY,GAAM,CAC5B,IAAM,EAAO,EAAU,EAAY,CAAM,EAAG,EAAQ,iBAAqC,EAAoB,EACvG,EAAU,EAAoB,CAAO,EACrC,EAAO,EAAQ,OAAS,EAAI,IAAI,IAAY,UAClD,OAAO,EACL,CAAC,GAAG,EAAK,IAAK,GAAS,EAAM,EAAM,aAAc,CAAK,CAAC,EAAG,EAAM,GAAG,QAAS,GAAG,EAAI,EAAM,GAAG,MAAO,CAAI,CAAC,EACxG,EAAQ,SACR,CACF,CACF,CAEA,GAAI,EAAQ,WAAa,GAAM,CAC7B,IAAM,EAAQ,EAAQ,QAAU,EAAQ,IAAM,SACxC,EAAS,EAAQ,KAAO,IAAA,GAAY,GAAK,MAAM,EAAQ,KAC7D,OAAO,EACL,CAAC,EAAM,GAAG,SAAU,KAAK,GAAO,EAAI,EAAM,GAAG,QAAS,GAAG,EAAO,cAAc,CAAC,EAC/E,GACA,CACF,CACF,CAEA,IAAM,EAAO,EAAU,EAAS,EAAQ,QAAQ,EAE1C,EAAS,EAAQ,WAAa,IAAA,IAAa,EAAQ,WAAa,KAChE,EAAS,EAAQ,WAAa,IAAS,GAAU,EAAQ,WAAa,EACtE,EAAQ,EAAS,EAAM,GAAG,QAAS,GAAG,EAAI,EAAM,GAAG,UAAW,GAAG,EAEjE,EAAiB,CAAC,EACpB,EAAQ,WAAa,GAAM,EAAK,KAAK,WAAW,EAC3C,GAAQ,EAAK,KAAK,QAAQ,EAAQ,UAAU,EACrD,IAAM,EAAU,EAAoB,CAAO,EACvC,EAAQ,OAAS,GAAG,EAAK,KAAK,CAAO,EACrC,CAAC,EAAQ,WAAa,EAAQ,WAAa,GAAK,EAAK,QAAQ,EAAK,KAAK,QAAQ,EAEnF,IAAM,EAAQ,EAAK,IAAK,GAAS,EAAM,EAAM,aAAc,CAAK,CAAC,EAE3D,EAAS,EAAK,OAAS,EAAI,IAAI,EAAK,KAAK,KAAK,IAAM,EAAS,UAAY,EAAK,SAAW,EAAI,QAAU,GAE7G,OADI,EAAO,OAAS,GAAG,EAAM,KAAK,EAAQ,EAAM,GAAG,MAAO,CAAM,CAAC,EAC1D,EAAiB,EAAO,EAAQ,SAAU,CAAK,CACxD"}
@@ -1,6 +1,6 @@
1
- import { BashParams } from "../tool/schema.cjs";
1
+ import { BashParams } from "../schemas/bashTool.cjs";
2
2
  import { Theme } from "@earendil-works/pi-coding-agent";
3
- import { Text } from "@earendil-works/pi-tui";
3
+ import { Component } from "@earendil-works/pi-tui";
4
4
  //#region src/tui/bashRender.d.ts
5
5
  /**
6
6
  * Syntax highlighter for the command line.
@@ -32,7 +32,7 @@ declare function truncateMiddle(text: string, max?: number): string;
32
32
  declare function formatBashCommand(command: string): string;
33
33
  /** Modifiers worth seeing at a glance, in the widget's `·` idiom. */
34
34
  declare function formatBashFlags(args: BashParams): string[];
35
- declare function renderBashCall(args: BashParams, theme: Theme, highlight?: Highlighter): Text;
35
+ declare function renderBashCall(args: BashParams, theme: Theme, highlight?: Highlighter): Component;
36
36
  /** The metadata footer, collapsed from five lines to one. */
37
37
  declare function formatResultSummary(details: BashResultDetails): string;
38
38
  declare function renderBashResult(result: {
@@ -41,11 +41,11 @@ declare function renderBashResult(result: {
41
41
  text?: string;
42
42
  }>;
43
43
  details?: unknown;
44
- isError?: boolean;
45
44
  }, options: {
46
45
  expanded: boolean;
47
46
  isPartial: boolean;
48
- }, theme: Theme): Text;
47
+ isError?: boolean;
48
+ }, theme: Theme): Component;
49
49
  //#endregion
50
50
  export { BashResultDetails, Highlighter, abbreviateHome, collapseCommand, formatBashCommand, formatBashFlags, formatResultSummary, renderBashCall, renderBashResult, truncateMiddle };
51
51
  //# sourceMappingURL=bashRender.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bashRender.d.cts","names":[],"sources":["../../src/tui/bashRender.ts"],"mappings":";;;;;;;;;;KAkBY,eAAe,cAAc;;UAoBxB;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;iBAIc,eAAe,cAAc;;iBAK7B,gBAAgB;;iBAUhB,eAAe,cAAc;iBAkB7B,kBAAkB;;iBAKlB,gBAAgB,MAAM;iBAUtB,eAAe,MAAM,YAAY,OAAO,OAAO,YAAW,cAA8B;;iBAUxF,oBAAoB,SAAS;iBA2B7B,iBACd;EAAU,UAAU;IAAQ;IAAc;;EAAkB;EAAmB;GAC/E;EAAW;EAAmB;GAC9B,OAAO,QACN"}
1
+ {"version":3,"file":"bashRender.d.cts","names":[],"sources":["../../src/tui/bashRender.ts"],"mappings":";;;;;;;;;;KAmBY,eAAe,cAAc;;UAqExB;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;iBAIc,eAAe,cAAc;;iBAK7B,gBAAgB;;iBAUhB,eAAe,cAAc;iBAkB7B,kBAAkB;;iBAKlB,gBAAgB,MAAM;iBAUtB,eAAe,MAAM,YAAY,OAAO,OAAO,YAAW,cAA8B;;iBAYxF,oBAAoB,SAAS;iBA2B7B,iBACd;EAAU,UAAU;IAAQ;IAAc;;EAAkB;GAC5D;EAAW;EAAmB;EAAoB;GAClD,OAAO,QACN"}
@@ -1,6 +1,6 @@
1
- import { BashParams } from "../tool/schema.mjs";
1
+ import { BashParams } from "../schemas/bashTool.mjs";
2
2
  import { Theme } from "@earendil-works/pi-coding-agent";
3
- import { Text } from "@earendil-works/pi-tui";
3
+ import { Component } from "@earendil-works/pi-tui";
4
4
  //#region src/tui/bashRender.d.ts
5
5
  /**
6
6
  * Syntax highlighter for the command line.
@@ -32,7 +32,7 @@ declare function truncateMiddle(text: string, max?: number): string;
32
32
  declare function formatBashCommand(command: string): string;
33
33
  /** Modifiers worth seeing at a glance, in the widget's `·` idiom. */
34
34
  declare function formatBashFlags(args: BashParams): string[];
35
- declare function renderBashCall(args: BashParams, theme: Theme, highlight?: Highlighter): Text;
35
+ declare function renderBashCall(args: BashParams, theme: Theme, highlight?: Highlighter): Component;
36
36
  /** The metadata footer, collapsed from five lines to one. */
37
37
  declare function formatResultSummary(details: BashResultDetails): string;
38
38
  declare function renderBashResult(result: {
@@ -41,11 +41,11 @@ declare function renderBashResult(result: {
41
41
  text?: string;
42
42
  }>;
43
43
  details?: unknown;
44
- isError?: boolean;
45
44
  }, options: {
46
45
  expanded: boolean;
47
46
  isPartial: boolean;
48
- }, theme: Theme): Text;
47
+ isError?: boolean;
48
+ }, theme: Theme): Component;
49
49
  //#endregion
50
50
  export { BashResultDetails, Highlighter, abbreviateHome, collapseCommand, formatBashCommand, formatBashFlags, formatResultSummary, renderBashCall, renderBashResult, truncateMiddle };
51
51
  //# sourceMappingURL=bashRender.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bashRender.d.mts","names":[],"sources":["../../src/tui/bashRender.ts"],"mappings":";;;;;;;;;;KAkBY,eAAe,cAAc;;UAoBxB;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;iBAIc,eAAe,cAAc;;iBAK7B,gBAAgB;;iBAUhB,eAAe,cAAc;iBAkB7B,kBAAkB;;iBAKlB,gBAAgB,MAAM;iBAUtB,eAAe,MAAM,YAAY,OAAO,OAAO,YAAW,cAA8B;;iBAUxF,oBAAoB,SAAS;iBA2B7B,iBACd;EAAU,UAAU;IAAQ;IAAc;;EAAkB;EAAmB;GAC/E;EAAW;EAAmB;GAC9B,OAAO,QACN"}
1
+ {"version":3,"file":"bashRender.d.mts","names":[],"sources":["../../src/tui/bashRender.ts"],"mappings":";;;;;;;;;;KAmBY,eAAe,cAAc;;UAqExB;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;iBAIc,eAAe,cAAc;;iBAK7B,gBAAgB;;iBAUhB,eAAe,cAAc;iBAkB7B,kBAAkB;;iBAKlB,gBAAgB,MAAM;iBAUtB,eAAe,MAAM,YAAY,OAAO,OAAO,YAAW,cAA8B;;iBAYxF,oBAAoB,SAAS;iBA2B7B,iBACd;EAAU,UAAU;IAAQ;IAAc;;EAAkB;GAC5D;EAAW;EAAmB;EAAoB;GAClD,OAAO,QACN"}
@@ -1,8 +1,5 @@
1
- import{formatSize as e}from"../tool/responseEnvelope.mjs";import t from"node:os";import{highlightCode as n}from"@earendil-works/pi-coding-agent";import{Text as r}from"@earendil-works/pi-tui";const i=/\[[0-9;]*m/;function a(e,t,n){return i.test(e)?e:n.fg(t,e)}function o(e,n=t.homedir()){return n.length>0?e.split(n).join(`~`):e}function s(e){let t=e.split(`
2
- `).map(e=>e.trim()).filter(e=>e.length>0),n=t[0]??``;return t.length>1?`${n} … +${t.length-1} lines`:n}function c(e,t=160){if(e.length<=t)return e;let n=t-3,r=Math.ceil(n/2),i=e.slice(0,r),a=e.slice(e.length-(n-r)),o=i.lastIndexOf(` `);o>i.length/2&&(i=i.slice(0,o));let s=a.indexOf(` `);return s>=0&&s<a.length/2&&(a=a.slice(s+1)),`${i} … ${a}`}function l(e){return c(o(s(e)))}function u(e){let t=[];return e.background===!0&&t.push(`bg`),e.interactive===!0&&t.push(`tty`),e.alarm!==void 0&&t.push(`alarm ${e.alarm}s`),e.timeout!==void 0&&t.push(`${e.timeout}s`),e.name!==void 0&&e.name.length>0&&t.push(e.name),t}function d(e,t,i=n){let a=i(l(e.command),`bash`).join(` `),o=t.fg(`toolTitle`,t.bold(`bash `))+a,s=u(e);return s.length>0&&(o+=` ${t.fg(`muted`,`· ${s.join(` · `)}`)}`),new r(o,0,0)}function f(t){let n=[];return t.lines!==void 0&&n.push(`${t.lines.toLocaleString(`en-US`)} lines`),t.fileSize!==void 0&&n.push(e(t.fileSize)),t.runner!==void 0&&n.push(t.runner),n.join(` · `)}function p(e,t){let n=(e.tail??``).split(`
3
- `);for(;n.length>0&&n[n.length-1]?.trim()===``;)n.pop();return t||n.length<=12?n:n.slice(-12)}function m(e){return(e.content??[]).map(e=>e.text??``).join(``)}function h(e,t){let n=e.split(`
4
- `);for(;n.length>0&&n[n.length-1]?.trim()===``;)n.pop();return n.length<=t?n:n.slice(-t)}function g(e,t,n){let i=e.details??{};if(t.isPartial){let t=h(m(e),12),i=n.fg(`warning`,`◐`)+n.fg(`dim`,` running`);return new r([...t.map(e=>a(e,`toolOutput`,n)),i].join(`
5
- `),0,0)}if(e.isError===!0){let o=h(m(e),t.expanded?2**53-1:12),s=f(i),c=s.length>0?` ${s}`:``;return new r([...o.map(e=>a(e,`toolOutput`,n)),n.fg(`error`,`✗`)+n.fg(`dim`,c)].join(`
6
- `),0,0)}if(i.promoted===!0){let e=i.runner??i.id??`runner`,t=i.id===void 0?``:` · ${i.id}`;return new r(n.fg(`accent`,`● ${e}`)+n.fg(`muted`,`${t} · background`),0,0)}let o=p(i,t.expanded),s=i.exitCode!==void 0&&i.exitCode!==null,c=i.timedOut===!0||s&&i.exitCode!==0,l=c?n.fg(`error`,`✗`):n.fg(`success`,`✓`),u=[];i.timedOut===!0?u.push(`timed out`):c&&u.push(`exit ${i.exitCode}`);let d=f(i);d.length>0&&u.push(d),!t.expanded&&(i.tailLines??0)>o.length&&u.push(`ctrl+o`);let g=o.map(e=>a(e,`toolOutput`,n));return g.push(l+n.fg(`dim`,u.length>0?` ${u.join(` · `)}`:``)),new r(g.join(`
7
- `),0,0)}export{o as abbreviateHome,s as collapseCommand,l as formatBashCommand,u as formatBashFlags,f as formatResultSummary,d as renderBashCall,g as renderBashResult,c as truncateMiddle};
1
+ import{formatSize as e}from"../commands/bash/responseEnvelope.mjs";import t from"node:os";import{DoomToolCall as n,renderToolBadge as r}from"@agimon-ai/doompi-ui/toolChrome";import{highlightCode as i}from"@earendil-works/pi-coding-agent";import{truncateToWidth as a,wrapTextWithAnsi as o}from"@earendil-works/pi-tui";var s=class{lines;layout;constructor(e,t){this.lines=e,this.layout=t}render(e){if(e<=0)return[];let t=Math.max(1,e-2),n=this.layout.wrap?this.lines.flatMap(e=>o(e,t)):this.lines.map(e=>a(e,t,`…`));this.layout.separator&&n.unshift(this.layout.separator(`─`.repeat(t)));let r=n.map(e=>` ${e}`);return this.layout.trailingBlank&&r.push(``),r}invalidate(){}};function c(e,t,n){return new s(e,{wrap:t,separator:e=>n.fg(`borderMuted`,e),trailingBlank:!0})}const l=/\u001b\[[0-9;]*m/;function u(e,t,n){return l.test(e)?e:n.fg(t,e)}function d(e,n=t.homedir()){return n.length>0?e.split(n).join(`~`):e}function f(e){let t=e.split(`
2
+ `).map(e=>e.trim()).filter(e=>e.length>0),n=t[0]??``;return t.length>1?`${n} … +${t.length-1} lines`:n}function p(e,t=160){if(e.length<=t)return e;let n=t-3,r=Math.ceil(n/2),i=e.slice(0,r),a=e.slice(e.length-(n-r)),o=i.lastIndexOf(` `);o>i.length/2&&(i=i.slice(0,o));let s=a.indexOf(` `);return s>=0&&s<a.length/2&&(a=a.slice(s+1)),`${i} … ${a}`}function m(e){return d(f(e))}function h(e){let t=[];return e.background===!0&&t.push(`bg`),e.interactive===!0&&t.push(`tty`),e.alarm!==void 0&&t.push(`alarm ${e.alarm}s`),e.timeout!==void 0&&t.push(`${e.timeout}s`),e.name!==void 0&&e.name.length>0&&t.push(e.name),t}function g(e,t,a=i){let o=a(m(e.command),`bash`).join(` `),s=`${r(`bash`,t)} ${o}`,c=h(e);return c.length>0&&(s+=` ${t.fg(`muted`,`· ${c.join(` · `)}`)}`),new n(s)}function _(t){let n=[];return t.lines!==void 0&&n.push(`${t.lines.toLocaleString(`en-US`)} lines`),t.fileSize!==void 0&&n.push(e(t.fileSize)),t.runner!==void 0&&n.push(t.runner),n.join(` · `)}function v(e,t){let n=(e.tail??``).split(`
3
+ `);for(;n.length>0&&n[n.length-1]?.trim()===``;)n.pop();return t||n.length<=12?n:n.slice(-12)}function y(e){return(e.content??[]).map(e=>e.text??``).join(``)}function b(e,t){let n=e.split(`
4
+ `);for(;n.length>0&&n[n.length-1]?.trim()===``;)n.pop();return n.length<=t?n:n.slice(-t)}function x(e,t,n){let r=e.details??{};if(t.isPartial){let t=b(y(e),12),r=n.fg(`warning`,`◐`)+n.fg(`dim`,` running`);return c([...t.map(e=>u(e,`toolOutput`,n)),r],!1,n)}if(t.isError===!0){let i=b(y(e),t.expanded?2**53-1:12),a=_(r),o=a.length>0?` ${a}`:` failed`;return c([...i.map(e=>u(e,`toolOutput`,n)),n.fg(`error`,`✗`)+n.fg(`dim`,o)],t.expanded,n)}if(r.promoted===!0){let e=r.runner??r.id??`runner`,t=r.id===void 0?``:` · ${r.id}`;return c([n.fg(`accent`,`● ${e}`)+n.fg(`muted`,`${t} · background`)],!1,n)}let i=v(r,t.expanded),a=r.exitCode!==void 0&&r.exitCode!==null,o=r.timedOut===!0||a&&r.exitCode!==0,s=o?n.fg(`error`,`✗`):n.fg(`success`,`✓`),l=[];r.timedOut===!0?l.push(`timed out`):o&&l.push(`exit ${r.exitCode}`);let d=_(r);d.length>0&&l.push(d),!t.expanded&&(r.tailLines??0)>i.length&&l.push(`ctrl+o`);let f=i.map(e=>u(e,`toolOutput`,n)),p=l.length>0?` ${l.join(` · `)}`:o?` failed`:i.length===0?` done`:``;return p.length>0&&f.push(s+n.fg(`dim`,p)),c(f,t.expanded,n)}export{d as abbreviateHome,f as collapseCommand,m as formatBashCommand,h as formatBashFlags,_ as formatResultSummary,g as renderBashCall,x as renderBashResult,p as truncateMiddle};
8
5
  //# sourceMappingURL=bashRender.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"bashRender.mjs","names":[],"sources":["../../src/tui/bashRender.ts"],"sourcesContent":["import os from 'node:os';\nimport { highlightCode, type Theme, type ThemeColor } from '@earendil-works/pi-coding-agent';\nimport { Text } from '@earendil-works/pi-tui';\nimport { formatSize } from '../tool/responseEnvelope.ts';\nimport type { BashParams } from '../tool/schema.ts';\n\nconst ELLIPSIS = '…';\n/** Spaced so the join never glues two words into one unreadable token. */\nconst GAP = ' … ';\n/** Wide enough to keep a realistic command intact, short enough to stay under three wrapped rows. */\nconst MAX_COMMAND_LENGTH = 160;\n\n/**\n * Syntax highlighter for the command line.\n *\n * `highlightCode` reads Pi's global theme singleton rather than the theme passed\n * to a renderer, so it is injected here to keep these functions testable.\n */\nexport type Highlighter = (code: string, lang?: string) => string[];\n/** Lines of log kept in the collapsed result, before `ctrl+o` reveals the rest. */\nconst COLLAPSED_TAIL_LINES = 12;\n/** Live output is unbounded while a command runs, so the running state keeps only a window. */\nconst STREAM_TAIL_LINES = 12;\n\n/** An SGR colour sequence, the only escape the log pipeline is allowed to keep. */\nconst SGR_PATTERN = /\u001b\\[[0-9;]*m/;\n\n/**\n * Applies a theme colour only to lines that carry none of their own.\n *\n * A command that emits colour has already said how its output should look, and\n * wrapping it would reset to the terminal default at the first embedded escape.\n */\nfunction paint(line: string, color: ThemeColor, theme: Theme): string {\n return SGR_PATTERN.test(line) ? line : theme.fg(color, line);\n}\n\n/** Structured view of what `formatRunResult` reports, read only by the renderers. */\nexport interface BashResultDetails {\n id?: string;\n runner?: string;\n exitCode?: number | null;\n logPath?: string;\n fileSize?: number;\n lines?: number;\n timedOut?: boolean;\n promoted?: boolean;\n reason?: string;\n tail?: string;\n tailLines?: number;\n}\n\n/** `/Users/me/x` becomes `~/x`. Absolute home paths are what push these lines onto extra rows. */\nexport function abbreviateHome(text: string, home: string = os.homedir()): string {\n return home.length > 0 ? text.split(home).join('~') : text;\n}\n\n/** Collapse a heredoc or multi-line script to one scannable line plus a count. */\nexport function collapseCommand(command: string): string {\n const lines = command\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n const first = lines[0] ?? '';\n return lines.length > 1 ? `${first} ${ELLIPSIS} +${lines.length - 1} lines` : first;\n}\n\n/** Drop the middle rather than the end, so a long pipeline keeps both its source and its sink. */\nexport function truncateMiddle(text: string, max: number = MAX_COMMAND_LENGTH): string {\n if (text.length <= max) return text;\n const keep = max - GAP.length;\n const head = Math.ceil(keep / 2);\n let start = text.slice(0, head);\n let end = text.slice(text.length - (keep - head));\n\n // Snap both cuts to a token boundary, so neither side ends mid-word. Each is\n // only moved when a boundary exists in the half nearest the cut, otherwise a\n // single long argument would swallow the whole budget.\n const lastSpace = start.lastIndexOf(' ');\n if (lastSpace > start.length / 2) start = start.slice(0, lastSpace);\n const firstSpace = end.indexOf(' ');\n if (firstSpace >= 0 && firstSpace < end.length / 2) end = end.slice(firstSpace + 1);\n\n return `${start}${GAP}${end}`;\n}\n\nexport function formatBashCommand(command: string): string {\n return truncateMiddle(abbreviateHome(collapseCommand(command)));\n}\n\n/** Modifiers worth seeing at a glance, in the widget's `·` idiom. */\nexport function formatBashFlags(args: BashParams): string[] {\n const flags: string[] = [];\n if (args.background === true) flags.push('bg');\n if (args.interactive === true) flags.push('tty');\n if (args.alarm !== undefined) flags.push(`alarm ${args.alarm}s`);\n if (args.timeout !== undefined) flags.push(`${args.timeout}s`);\n if (args.name !== undefined && args.name.length > 0) flags.push(args.name);\n return flags;\n}\n\nexport function renderBashCall(args: BashParams, theme: Theme, highlight: Highlighter = highlightCode): Text {\n // Highlight after shortening: the ANSI codes would break any later slicing.\n const command = highlight(formatBashCommand(args.command), 'bash').join(' ');\n let text = theme.fg('toolTitle', theme.bold('bash ')) + command;\n const flags = formatBashFlags(args);\n if (flags.length > 0) text += ` ${theme.fg('muted', `· ${flags.join(' · ')}`)}`;\n return new Text(text, 0, 0);\n}\n\n/** The metadata footer, collapsed from five lines to one. */\nexport function formatResultSummary(details: BashResultDetails): string {\n const parts: string[] = [];\n if (details.lines !== undefined) parts.push(`${details.lines.toLocaleString('en-US')} lines`);\n if (details.fileSize !== undefined) parts.push(formatSize(details.fileSize));\n if (details.runner !== undefined) parts.push(details.runner);\n return parts.join(' · ');\n}\n\nfunction tailLines(details: BashResultDetails, expanded: boolean): string[] {\n const lines = (details.tail ?? '').split('\\n');\n while (lines.length > 0 && lines[lines.length - 1]?.trim() === '') lines.pop();\n if (expanded || lines.length <= COLLAPSED_TAIL_LINES) return lines;\n return lines.slice(-COLLAPSED_TAIL_LINES);\n}\n\n/** Concatenated text of a tool result, which is all a thrown failure leaves behind. */\nfunction contentText(result: { content?: Array<{ type: string; text?: string }> }): string {\n return (result.content ?? []).map((block) => block.text ?? '').join('');\n}\n\n/** Last `max` lines, so a chatty command cannot push the transcript off screen. */\nfunction lastLines(text: string, max: number): string[] {\n const all = text.split('\\n');\n while (all.length > 0 && all[all.length - 1]?.trim() === '') all.pop();\n return all.length <= max ? all : all.slice(-max);\n}\n\nexport function renderBashResult(\n result: { content?: Array<{ type: string; text?: string }>; details?: unknown; isError?: boolean },\n options: { expanded: boolean; isPartial: boolean },\n theme: Theme,\n): Text {\n const details = (result.details ?? {}) as BashResultDetails;\n\n // Running. Details do not exist yet, so the streamed text is the only source,\n // and it is bounded here because it grows without limit while the command runs.\n if (options.isPartial) {\n const streamed = lastLines(contentText(result), STREAM_TAIL_LINES);\n const label = theme.fg('warning', '◐') + theme.fg('dim', ' running');\n return new Text([...streamed.map((line) => paint(line, 'toolOutput', theme)), label].join('\\n'), 0, 0);\n }\n\n // Failed. formatRunResult throws on a bad exit, so Pi hands back the error\n // text with no details at all; without this the block would render a bare tick.\n if (result.isError === true) {\n const body = lastLines(contentText(result), options.expanded ? Number.MAX_SAFE_INTEGER : COLLAPSED_TAIL_LINES);\n const summary = formatResultSummary(details);\n const meta = summary.length > 0 ? ` ${summary}` : '';\n return new Text(\n [...body.map((line) => paint(line, 'toolOutput', theme)), theme.fg('error', '✗') + theme.fg('dim', meta)].join(\n '\\n',\n ),\n 0,\n 0,\n );\n }\n\n if (details.promoted === true) {\n const label = details.runner ?? details.id ?? 'runner';\n const suffix = details.id === undefined ? '' : ` · ${details.id}`;\n return new Text(theme.fg('accent', `● ${label}`) + theme.fg('muted', `${suffix} · background`), 0, 0);\n }\n\n const body = tailLines(details, options.expanded);\n // An absent exit code means the runner never reported one, which is not a failure.\n const exited = details.exitCode !== undefined && details.exitCode !== null;\n const failed = details.timedOut === true || (exited && details.exitCode !== 0);\n const glyph = failed ? theme.fg('error', '✗') : theme.fg('success', '✓');\n\n const meta: string[] = [];\n if (details.timedOut === true) meta.push('timed out');\n else if (failed) meta.push(`exit ${details.exitCode}`);\n const summary = formatResultSummary(details);\n if (summary.length > 0) meta.push(summary);\n if (!options.expanded && (details.tailLines ?? 0) > body.length) meta.push('ctrl+o');\n\n const lines = body.map((line) => paint(line, 'toolOutput', theme));\n // `dim`, not `muted`: muted sits too close to toolOutput to read as chrome.\n lines.push(glyph + theme.fg('dim', meta.length > 0 ? ` ${meta.join(' · ')}` : ''));\n return new Text(lines.join('\\n'), 0, 0);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bashRender.mjs","names":[],"sources":["../../src/tui/bashRender.ts"],"sourcesContent":["import os from 'node:os';\nimport { DoomToolCall, renderToolBadge } from '@agimon-ai/doompi-ui/toolChrome';\nimport { highlightCode, type Theme, type ThemeColor } from '@earendil-works/pi-coding-agent';\nimport { type Component, truncateToWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';\nimport { formatSize } from '../commands/bash/responseEnvelope.ts';\nimport type { BashParams } from '../schemas/bashTool.ts';\n\nconst ELLIPSIS = '…';\n/** Spaced so the join never glues two words into one unreadable token. */\nconst GAP = ' … ';\n/** Wide enough to keep a realistic command intact, short enough to stay under three wrapped rows. */\nconst MAX_COMMAND_LENGTH = 160;\n\n/**\n * Syntax highlighter for the command line.\n *\n * `highlightCode` reads Pi's global theme singleton rather than the theme passed\n * to a renderer, so it is injected here to keep these functions testable.\n */\nexport type Highlighter = (code: string, lang?: string) => string[];\n/** Lines of log kept in the collapsed result, before `ctrl+o` reveals the rest. */\nconst COLLAPSED_TAIL_LINES = 12;\n/** Live output is unbounded while a command runs, so the running state keeps only a window. */\nconst STREAM_TAIL_LINES = 12;\n\n/**\n * Width-aware tool text.\n *\n * Pi's Text component wraps every long line. That is useful for prose, but it\n * makes shell pipelines and grep output consume several visual rows and blurs\n * the boundary between separate log lines. Collapsed tool output is instead\n * clipped per logical line; expanded output keeps all content by wrapping it.\n */\ninterface BashToolTextLayout {\n wrap: boolean;\n separator?: (line: string) => string;\n trailingBlank?: boolean;\n}\n\nclass BashToolText implements Component {\n constructor(\n private readonly lines: readonly string[],\n private readonly layout: BashToolTextLayout,\n ) {}\n\n render(width: number): string[] {\n if (width <= 0) return [];\n const contentWidth = Math.max(1, width - 2);\n const rendered = this.layout.wrap\n ? this.lines.flatMap((line) => wrapTextWithAnsi(line, contentWidth))\n : this.lines.map((line) => truncateToWidth(line, contentWidth, ELLIPSIS));\n if (this.layout.separator) rendered.unshift(this.layout.separator('─'.repeat(contentWidth)));\n const padded = rendered.map((line) => ` ${line}`);\n if (this.layout.trailingBlank) padded.push('');\n return padded;\n }\n\n invalidate(): void {\n // Content is immutable and rendering reads the current width every time.\n }\n}\n\nfunction renderResultText(lines: readonly string[], wrap: boolean, theme: Theme): Component {\n return new BashToolText(lines, {\n wrap,\n // State belongs in the result summary below; keep this divider structural.\n separator: (line) => theme.fg('borderMuted', line),\n trailingBlank: true,\n });\n}\n\n/** An SGR colour sequence, the only escape the log pipeline is allowed to keep. */\n/* oxlint-disable no-control-regex -- matching SGR requires the ESC control character */\n// biome-ignore lint/suspicious/noControlCharactersInRegex: Matching SGR requires the ESC control character.\nconst SGR_PATTERN = /\\u001b\\[[0-9;]*m/;\n/* oxlint-enable no-control-regex */\n\n/**\n * Applies a theme colour only to lines that carry none of their own.\n *\n * A command that emits colour has already said how its output should look, and\n * wrapping it would reset to the terminal default at the first embedded escape.\n */\nfunction paint(line: string, color: ThemeColor, theme: Theme): string {\n return SGR_PATTERN.test(line) ? line : theme.fg(color, line);\n}\n\n/** Structured view of what `formatRunResult` reports, read only by the renderers. */\nexport interface BashResultDetails {\n id?: string;\n runner?: string;\n exitCode?: number | null;\n logPath?: string;\n fileSize?: number;\n lines?: number;\n timedOut?: boolean;\n promoted?: boolean;\n reason?: string;\n tail?: string;\n tailLines?: number;\n}\n\n/** `/Users/me/x` becomes `~/x`. Absolute home paths are what push these lines onto extra rows. */\nexport function abbreviateHome(text: string, home: string = os.homedir()): string {\n return home.length > 0 ? text.split(home).join('~') : text;\n}\n\n/** Collapse a heredoc or multi-line script to one scannable line plus a count. */\nexport function collapseCommand(command: string): string {\n const lines = command\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n const first = lines[0] ?? '';\n return lines.length > 1 ? `${first} ${ELLIPSIS} +${lines.length - 1} lines` : first;\n}\n\n/** Drop the middle rather than the end, so a long pipeline keeps both its source and its sink. */\nexport function truncateMiddle(text: string, max: number = MAX_COMMAND_LENGTH): string {\n if (text.length <= max) return text;\n const keep = max - GAP.length;\n const head = Math.ceil(keep / 2);\n let start = text.slice(0, head);\n let end = text.slice(text.length - (keep - head));\n\n // Snap both cuts to a token boundary, so neither side ends mid-word. Each is\n // only moved when a boundary exists in the half nearest the cut, otherwise a\n // single long argument would swallow the whole budget.\n const lastSpace = start.lastIndexOf(' ');\n if (lastSpace > start.length / 2) start = start.slice(0, lastSpace);\n const firstSpace = end.indexOf(' ');\n if (firstSpace >= 0 && firstSpace < end.length / 2) end = end.slice(firstSpace + 1);\n\n return `${start}${GAP}${end}`;\n}\n\nexport function formatBashCommand(command: string): string {\n return abbreviateHome(collapseCommand(command));\n}\n\n/** Modifiers worth seeing at a glance, in the widget's `·` idiom. */\nexport function formatBashFlags(args: BashParams): string[] {\n const flags: string[] = [];\n if (args.background === true) flags.push('bg');\n if (args.interactive === true) flags.push('tty');\n if (args.alarm !== undefined) flags.push(`alarm ${args.alarm}s`);\n if (args.timeout !== undefined) flags.push(`${args.timeout}s`);\n if (args.name !== undefined && args.name.length > 0) flags.push(args.name);\n return flags;\n}\n\nexport function renderBashCall(args: BashParams, theme: Theme, highlight: Highlighter = highlightCode): Component {\n // Highlight after collapsing multi-line scripts: ANSI codes would break that transformation.\n const command = highlight(formatBashCommand(args.command), 'bash').join(' ');\n let text = `${renderToolBadge('bash', theme)} ${command}`;\n const flags = formatBashFlags(args);\n if (flags.length > 0) text += ` ${theme.fg('muted', `· ${flags.join(' · ')}`)}`;\n // A command is operator input, not log output: wrap it so the complete\n // pipeline remains inspectable instead of clipping its most important tail.\n return new DoomToolCall(text);\n}\n\n/** The metadata footer, collapsed from five lines to one. */\nexport function formatResultSummary(details: BashResultDetails): string {\n const parts: string[] = [];\n if (details.lines !== undefined) parts.push(`${details.lines.toLocaleString('en-US')} lines`);\n if (details.fileSize !== undefined) parts.push(formatSize(details.fileSize));\n if (details.runner !== undefined) parts.push(details.runner);\n return parts.join(' · ');\n}\n\nfunction tailLines(details: BashResultDetails, expanded: boolean): string[] {\n const lines = (details.tail ?? '').split('\\n');\n while (lines.length > 0 && lines[lines.length - 1]?.trim() === '') lines.pop();\n if (expanded || lines.length <= COLLAPSED_TAIL_LINES) return lines;\n return lines.slice(-COLLAPSED_TAIL_LINES);\n}\n\n/** Concatenated text of a tool result, which is all a thrown failure leaves behind. */\nfunction contentText(result: { content?: Array<{ type: string; text?: string }> }): string {\n return (result.content ?? []).map((block) => block.text ?? '').join('');\n}\n\n/** Last `max` lines, so a chatty command cannot push the transcript off screen. */\nfunction lastLines(text: string, max: number): string[] {\n const all = text.split('\\n');\n while (all.length > 0 && all[all.length - 1]?.trim() === '') all.pop();\n return all.length <= max ? all : all.slice(-max);\n}\n\nexport function renderBashResult(\n result: { content?: Array<{ type: string; text?: string }>; details?: unknown },\n options: { expanded: boolean; isPartial: boolean; isError?: boolean },\n theme: Theme,\n): Component {\n const details = (result.details ?? {}) as BashResultDetails;\n\n // Running. Details do not exist yet, so the streamed text is the only source,\n // and it is bounded here because it grows without limit while the command runs.\n if (options.isPartial) {\n const streamed = lastLines(contentText(result), STREAM_TAIL_LINES);\n const label = theme.fg('warning', '◐') + theme.fg('dim', ' running');\n return renderResultText([...streamed.map((line) => paint(line, 'toolOutput', theme)), label], false, theme);\n }\n\n // Failed. formatRunResult throws on a bad exit, so Pi hands back the error\n // text with no details at all; without this the block would render a bare tick.\n if (options.isError === true) {\n const body = lastLines(contentText(result), options.expanded ? Number.MAX_SAFE_INTEGER : COLLAPSED_TAIL_LINES);\n const summary = formatResultSummary(details);\n const meta = summary.length > 0 ? ` ${summary}` : ' failed';\n return renderResultText(\n [...body.map((line) => paint(line, 'toolOutput', theme)), theme.fg('error', '✗') + theme.fg('dim', meta)],\n options.expanded,\n theme,\n );\n }\n\n if (details.promoted === true) {\n const label = details.runner ?? details.id ?? 'runner';\n const suffix = details.id === undefined ? '' : ` · ${details.id}`;\n return renderResultText(\n [theme.fg('accent', `● ${label}`) + theme.fg('muted', `${suffix} · background`)],\n false,\n theme,\n );\n }\n\n const body = tailLines(details, options.expanded);\n // An absent exit code means the runner never reported one, which is not a failure.\n const exited = details.exitCode !== undefined && details.exitCode !== null;\n const failed = details.timedOut === true || (exited && details.exitCode !== 0);\n const glyph = failed ? theme.fg('error', '✗') : theme.fg('success', '✓');\n\n const meta: string[] = [];\n if (details.timedOut === true) meta.push('timed out');\n else if (failed) meta.push(`exit ${details.exitCode}`);\n const summary = formatResultSummary(details);\n if (summary.length > 0) meta.push(summary);\n if (!options.expanded && (details.tailLines ?? 0) > body.length) meta.push('ctrl+o');\n\n const lines = body.map((line) => paint(line, 'toolOutput', theme));\n // `dim`, not `muted`: muted sits too close to toolOutput to read as chrome.\n const status = meta.length > 0 ? ` ${meta.join(' · ')}` : failed ? ' failed' : body.length === 0 ? ' done' : '';\n if (status.length > 0) lines.push(glyph + theme.fg('dim', status));\n return renderResultText(lines, options.expanded, theme);\n}\n"],"mappings":"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"}