@hyperspaceng/neural-coding-agent 0.62.1 → 0.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +10 -0
  3. package/dist/bun/register-bedrock.d.ts.map +1 -1
  4. package/dist/bun/register-bedrock.js +2 -2
  5. package/dist/bun/register-bedrock.js.map +1 -1
  6. package/dist/cli/args.d.ts +1 -1
  7. package/dist/cli/args.d.ts.map +1 -1
  8. package/dist/cli/args.js.map +1 -1
  9. package/dist/cli/config-selector.d.ts.map +1 -1
  10. package/dist/cli/config-selector.js +1 -1
  11. package/dist/cli/config-selector.js.map +1 -1
  12. package/dist/cli/file-processor.d.ts +1 -1
  13. package/dist/cli/file-processor.d.ts.map +1 -1
  14. package/dist/cli/file-processor.js +4 -0
  15. package/dist/cli/file-processor.js.map +1 -1
  16. package/dist/cli/initial-message.d.ts +1 -1
  17. package/dist/cli/initial-message.d.ts.map +1 -1
  18. package/dist/cli/initial-message.js.map +1 -1
  19. package/dist/cli/list-models.d.ts.map +1 -1
  20. package/dist/cli/list-models.js +1 -1
  21. package/dist/cli/list-models.js.map +1 -1
  22. package/dist/cli/session-picker.d.ts.map +1 -1
  23. package/dist/cli/session-picker.js +1 -1
  24. package/dist/cli/session-picker.js.map +1 -1
  25. package/dist/core/agent-session.d.ts +12 -5
  26. package/dist/core/agent-session.d.ts.map +1 -1
  27. package/dist/core/agent-session.js +61 -47
  28. package/dist/core/agent-session.js.map +1 -1
  29. package/dist/core/auth-storage.d.ts +2 -2
  30. package/dist/core/auth-storage.d.ts.map +1 -1
  31. package/dist/core/auth-storage.js +2 -2
  32. package/dist/core/auth-storage.js.map +1 -1
  33. package/dist/core/compaction/branch-summarization.d.ts +2 -2
  34. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  35. package/dist/core/compaction/branch-summarization.js +1 -1
  36. package/dist/core/compaction/branch-summarization.js.map +1 -1
  37. package/dist/core/compaction/compaction.d.ts +2 -2
  38. package/dist/core/compaction/compaction.d.ts.map +1 -1
  39. package/dist/core/compaction/compaction.js +1 -1
  40. package/dist/core/compaction/compaction.js.map +1 -1
  41. package/dist/core/compaction/utils.d.ts +2 -2
  42. package/dist/core/compaction/utils.d.ts.map +1 -1
  43. package/dist/core/compaction/utils.js.map +1 -1
  44. package/dist/core/defaults.d.ts +1 -1
  45. package/dist/core/defaults.d.ts.map +1 -1
  46. package/dist/core/defaults.js.map +1 -1
  47. package/dist/core/export-html/index.d.ts +3 -3
  48. package/dist/core/export-html/index.d.ts.map +1 -1
  49. package/dist/core/export-html/index.js +2 -2
  50. package/dist/core/export-html/index.js.map +1 -1
  51. package/dist/core/export-html/tool-renderer.d.ts +2 -2
  52. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  53. package/dist/core/export-html/tool-renderer.js +41 -16
  54. package/dist/core/export-html/tool-renderer.js.map +1 -1
  55. package/dist/core/extensions/index.d.ts +3 -2
  56. package/dist/core/extensions/index.d.ts.map +1 -1
  57. package/dist/core/extensions/index.js.map +1 -1
  58. package/dist/core/extensions/loader.d.ts.map +1 -1
  59. package/dist/core/extensions/loader.js +27 -17
  60. package/dist/core/extensions/loader.js.map +1 -1
  61. package/dist/core/extensions/runner.d.ts +7 -10
  62. package/dist/core/extensions/runner.d.ts.map +1 -1
  63. package/dist/core/extensions/runner.js +27 -38
  64. package/dist/core/extensions/runner.js.map +1 -1
  65. package/dist/core/extensions/types.d.ts +49 -14
  66. package/dist/core/extensions/types.d.ts.map +1 -1
  67. package/dist/core/extensions/types.js.map +1 -1
  68. package/dist/core/extensions/wrapper.d.ts +1 -1
  69. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  70. package/dist/core/extensions/wrapper.js +2 -8
  71. package/dist/core/extensions/wrapper.js.map +1 -1
  72. package/dist/core/index.d.ts +2 -1
  73. package/dist/core/index.d.ts.map +1 -1
  74. package/dist/core/index.js +1 -0
  75. package/dist/core/index.js.map +1 -1
  76. package/dist/core/keybindings.d.ts +2 -2
  77. package/dist/core/keybindings.d.ts.map +1 -1
  78. package/dist/core/keybindings.js +1 -1
  79. package/dist/core/keybindings.js.map +1 -1
  80. package/dist/core/messages.d.ts +3 -3
  81. package/dist/core/messages.d.ts.map +1 -1
  82. package/dist/core/messages.js.map +1 -1
  83. package/dist/core/model-registry.d.ts +1 -1
  84. package/dist/core/model-registry.d.ts.map +1 -1
  85. package/dist/core/model-registry.js +2 -2
  86. package/dist/core/model-registry.js.map +1 -1
  87. package/dist/core/model-resolver.d.ts +2 -2
  88. package/dist/core/model-resolver.d.ts.map +1 -1
  89. package/dist/core/model-resolver.js +1 -1
  90. package/dist/core/model-resolver.js.map +1 -1
  91. package/dist/core/output-guard.d.ts +6 -0
  92. package/dist/core/output-guard.d.ts.map +1 -0
  93. package/dist/core/output-guard.js +59 -0
  94. package/dist/core/output-guard.js.map +1 -0
  95. package/dist/core/package-manager.d.ts +3 -0
  96. package/dist/core/package-manager.d.ts.map +1 -1
  97. package/dist/core/package-manager.js +69 -9
  98. package/dist/core/package-manager.js.map +1 -1
  99. package/dist/core/prompt-templates.d.ts +2 -1
  100. package/dist/core/prompt-templates.d.ts.map +1 -1
  101. package/dist/core/prompt-templates.js +30 -32
  102. package/dist/core/prompt-templates.js.map +1 -1
  103. package/dist/core/resource-loader.d.ts +6 -5
  104. package/dist/core/resource-loader.d.ts.map +1 -1
  105. package/dist/core/resource-loader.js +136 -108
  106. package/dist/core/resource-loader.js.map +1 -1
  107. package/dist/core/sdk.d.ts +4 -4
  108. package/dist/core/sdk.d.ts.map +1 -1
  109. package/dist/core/sdk.js +2 -2
  110. package/dist/core/sdk.js.map +1 -1
  111. package/dist/core/session-manager.d.ts +2 -2
  112. package/dist/core/session-manager.d.ts.map +1 -1
  113. package/dist/core/session-manager.js.map +1 -1
  114. package/dist/core/settings-manager.d.ts +1 -1
  115. package/dist/core/settings-manager.d.ts.map +1 -1
  116. package/dist/core/settings-manager.js.map +1 -1
  117. package/dist/core/skills.d.ts +2 -1
  118. package/dist/core/skills.d.ts.map +1 -1
  119. package/dist/core/skills.js +25 -1
  120. package/dist/core/skills.js.map +1 -1
  121. package/dist/core/slash-commands.d.ts +2 -3
  122. package/dist/core/slash-commands.d.ts.map +1 -1
  123. package/dist/core/slash-commands.js.map +1 -1
  124. package/dist/core/source-info.d.ts +18 -0
  125. package/dist/core/source-info.d.ts.map +1 -0
  126. package/dist/core/source-info.js +19 -0
  127. package/dist/core/source-info.js.map +1 -0
  128. package/dist/core/system-prompt.d.ts.map +1 -1
  129. package/dist/core/system-prompt.js +3 -38
  130. package/dist/core/system-prompt.js.map +1 -1
  131. package/dist/core/tools/bash.d.ts +20 -10
  132. package/dist/core/tools/bash.d.ts.map +1 -1
  133. package/dist/core/tools/bash.js +151 -59
  134. package/dist/core/tools/bash.js.map +1 -1
  135. package/dist/core/tools/edit.d.ts +15 -3
  136. package/dist/core/tools/edit.d.ts.map +1 -1
  137. package/dist/core/tools/edit.js +92 -21
  138. package/dist/core/tools/edit.js.map +1 -1
  139. package/dist/core/tools/find.d.ts +12 -5
  140. package/dist/core/tools/find.d.ts.map +1 -1
  141. package/dist/core/tools/find.js +76 -27
  142. package/dist/core/tools/find.js.map +1 -1
  143. package/dist/core/tools/grep.d.ts +16 -5
  144. package/dist/core/tools/grep.d.ts.map +1 -1
  145. package/dist/core/tools/grep.js +83 -29
  146. package/dist/core/tools/grep.js.map +1 -1
  147. package/dist/core/tools/index.d.ts +58 -20
  148. package/dist/core/tools/index.d.ts.map +1 -1
  149. package/dist/core/tools/index.js +50 -26
  150. package/dist/core/tools/index.js.map +1 -1
  151. package/dist/core/tools/ls.d.ts +10 -4
  152. package/dist/core/tools/ls.d.ts.map +1 -1
  153. package/dist/core/tools/ls.js +67 -13
  154. package/dist/core/tools/ls.js.map +1 -1
  155. package/dist/core/tools/read.d.ts +11 -4
  156. package/dist/core/tools/read.d.ts.map +1 -1
  157. package/dist/core/tools/read.js +110 -51
  158. package/dist/core/tools/read.js.map +1 -1
  159. package/dist/core/tools/render-utils.d.ts +21 -0
  160. package/dist/core/tools/render-utils.d.ts.map +1 -0
  161. package/dist/core/tools/render-utils.js +49 -0
  162. package/dist/core/tools/render-utils.js.map +1 -0
  163. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  164. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  165. package/dist/core/tools/tool-definition-wrapper.js +30 -0
  166. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  167. package/dist/core/tools/write.d.ts +10 -4
  168. package/dist/core/tools/write.d.ts.map +1 -1
  169. package/dist/core/tools/write.js +162 -27
  170. package/dist/core/tools/write.js.map +1 -1
  171. package/dist/index.d.ts +3 -2
  172. package/dist/index.d.ts.map +1 -1
  173. package/dist/index.js +2 -1
  174. package/dist/index.js.map +1 -1
  175. package/dist/main.d.ts.map +1 -1
  176. package/dist/main.js +30 -10
  177. package/dist/main.js.map +1 -1
  178. package/dist/modes/interactive/components/armin.d.ts +1 -1
  179. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  180. package/dist/modes/interactive/components/armin.js.map +1 -1
  181. package/dist/modes/interactive/components/assistant-message.d.ts +2 -2
  182. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  183. package/dist/modes/interactive/components/assistant-message.js +1 -1
  184. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  185. package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
  186. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  187. package/dist/modes/interactive/components/bash-execution.js +1 -1
  188. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  189. package/dist/modes/interactive/components/bordered-loader.d.ts +1 -1
  190. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  191. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  192. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  193. package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
  194. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  195. package/dist/modes/interactive/components/branch-summary-message.js +1 -1
  196. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  197. package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
  198. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  199. package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
  200. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  201. package/dist/modes/interactive/components/config-selector.d.ts +1 -1
  202. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  203. package/dist/modes/interactive/components/config-selector.js +1 -1
  204. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  205. package/dist/modes/interactive/components/countdown-timer.d.ts +1 -1
  206. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  207. package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
  208. package/dist/modes/interactive/components/custom-editor.d.ts +1 -1
  209. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  210. package/dist/modes/interactive/components/custom-editor.js +1 -1
  211. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  212. package/dist/modes/interactive/components/custom-message.d.ts +1 -1
  213. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  214. package/dist/modes/interactive/components/custom-message.js +1 -1
  215. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  216. package/dist/modes/interactive/components/daxnuts.d.ts +1 -1
  217. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  218. package/dist/modes/interactive/components/daxnuts.js.map +1 -1
  219. package/dist/modes/interactive/components/dynamic-border.d.ts +1 -1
  220. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  221. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  222. package/dist/modes/interactive/components/extension-editor.d.ts +1 -1
  223. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  224. package/dist/modes/interactive/components/extension-editor.js +1 -1
  225. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  226. package/dist/modes/interactive/components/extension-input.d.ts +1 -1
  227. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  228. package/dist/modes/interactive/components/extension-input.js +1 -1
  229. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  230. package/dist/modes/interactive/components/extension-selector.d.ts +1 -1
  231. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  232. package/dist/modes/interactive/components/extension-selector.js +1 -1
  233. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  234. package/dist/modes/interactive/components/footer.d.ts +1 -1
  235. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  236. package/dist/modes/interactive/components/footer.js +1 -1
  237. package/dist/modes/interactive/components/footer.js.map +1 -1
  238. package/dist/modes/interactive/components/keybinding-hints.d.ts +1 -1
  239. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  240. package/dist/modes/interactive/components/keybinding-hints.js +1 -1
  241. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  242. package/dist/modes/interactive/components/login-dialog.d.ts +1 -1
  243. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  244. package/dist/modes/interactive/components/login-dialog.js +2 -2
  245. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  246. package/dist/modes/interactive/components/model-selector.d.ts +2 -2
  247. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  248. package/dist/modes/interactive/components/model-selector.js +2 -2
  249. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  250. package/dist/modes/interactive/components/oauth-selector.d.ts +1 -1
  251. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  252. package/dist/modes/interactive/components/oauth-selector.js +2 -2
  253. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  254. package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -2
  255. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  256. package/dist/modes/interactive/components/scoped-models-selector.js +1 -1
  257. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  258. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  259. package/dist/modes/interactive/components/session-selector-search.js +1 -1
  260. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  261. package/dist/modes/interactive/components/session-selector.d.ts +1 -1
  262. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  263. package/dist/modes/interactive/components/session-selector.js +1 -1
  264. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  265. package/dist/modes/interactive/components/settings-selector.d.ts +3 -3
  266. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  267. package/dist/modes/interactive/components/settings-selector.js +1 -1
  268. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  269. package/dist/modes/interactive/components/show-images-selector.d.ts +1 -1
  270. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  271. package/dist/modes/interactive/components/show-images-selector.js +1 -1
  272. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  273. package/dist/modes/interactive/components/skill-invocation-message.d.ts +1 -1
  274. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  275. package/dist/modes/interactive/components/skill-invocation-message.js +1 -1
  276. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  277. package/dist/modes/interactive/components/theme-selector.d.ts +1 -1
  278. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  279. package/dist/modes/interactive/components/theme-selector.js +1 -1
  280. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  281. package/dist/modes/interactive/components/thinking-selector.d.ts +2 -2
  282. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  283. package/dist/modes/interactive/components/thinking-selector.js +1 -1
  284. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  285. package/dist/modes/interactive/components/tool-execution.d.ts +16 -41
  286. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  287. package/dist/modes/interactive/components/tool-execution.js +126 -679
  288. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  289. package/dist/modes/interactive/components/tree-selector.d.ts +1 -1
  290. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  291. package/dist/modes/interactive/components/tree-selector.js +1 -1
  292. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  293. package/dist/modes/interactive/components/user-message-selector.d.ts +1 -1
  294. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  295. package/dist/modes/interactive/components/user-message-selector.js +1 -1
  296. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  297. package/dist/modes/interactive/components/user-message.d.ts +1 -1
  298. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  299. package/dist/modes/interactive/components/user-message.js +1 -1
  300. package/dist/modes/interactive/components/user-message.js.map +1 -1
  301. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  302. package/dist/modes/interactive/components/visual-truncate.js +1 -1
  303. package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
  304. package/dist/modes/interactive/interactive-mode.d.ts +5 -12
  305. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  306. package/dist/modes/interactive/interactive-mode.js +147 -95
  307. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  308. package/dist/modes/interactive/theme/theme.d.ts +5 -2
  309. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  310. package/dist/modes/interactive/theme/theme.js +15 -1
  311. package/dist/modes/interactive/theme/theme.js.map +1 -1
  312. package/dist/modes/print-mode.d.ts +1 -1
  313. package/dist/modes/print-mode.d.ts.map +1 -1
  314. package/dist/modes/print-mode.js +5 -11
  315. package/dist/modes/print-mode.js.map +1 -1
  316. package/dist/modes/rpc/rpc-client.d.ts +2 -2
  317. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  318. package/dist/modes/rpc/rpc-client.js.map +1 -1
  319. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  320. package/dist/modes/rpc/rpc-mode.js +27 -20
  321. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  322. package/dist/modes/rpc/rpc-types.d.ts +5 -6
  323. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  324. package/dist/modes/rpc/rpc-types.js.map +1 -1
  325. package/dist/utils/image-resize.d.ts +6 -6
  326. package/dist/utils/image-resize.d.ts.map +1 -1
  327. package/dist/utils/image-resize.js +45 -94
  328. package/dist/utils/image-resize.js.map +1 -1
  329. package/docs/extensions.md +72 -32
  330. package/docs/tui.md +2 -2
  331. package/examples/extensions/built-in-tool-renderer.ts +8 -8
  332. package/examples/extensions/commands.ts +3 -3
  333. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  334. package/examples/extensions/custom-provider-anthropic/package.json +17 -17
  335. package/examples/extensions/custom-provider-gitlab-duo/package.json +14 -14
  336. package/examples/extensions/custom-provider-qwen-cli/package.json +14 -14
  337. package/examples/extensions/minimal-mode.ts +14 -14
  338. package/examples/extensions/question.ts +2 -2
  339. package/examples/extensions/questionnaire.ts +2 -2
  340. package/examples/extensions/subagent/index.ts +23 -3
  341. package/examples/extensions/todo.ts +2 -2
  342. package/examples/extensions/truncated-tool.ts +2 -2
  343. package/examples/extensions/with-deps/package-lock.json +2 -2
  344. package/examples/extensions/with-deps/package.json +20 -20
  345. package/examples/sdk/04-skills.ts +8 -2
  346. package/examples/sdk/08-prompt-templates.ts +2 -1
  347. package/examples/sdk/12-full-control.ts +0 -1
  348. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"slash-commands.js","sourceRoot":"","sources":["../../src/core/slash-commands.ts"],"names":[],"mappings":"AAiBA,MAAM,CAAC,MAAM,sBAAsB,GAAuC;IACzE,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,oBAAoB,EAAE;IACvD,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,kCAAkC,EAAE;IAClE,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,0CAA0C,EAAE;IAClF,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8DAA8D,EAAE;IAC/F,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+CAA+C,EAAE;IAChF,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,uCAAuC,EAAE;IACvE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,sCAAsC,EAAE;IACrE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,0BAA0B,EAAE;IACzD,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,EAAE;IAC/D,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,wBAAwB,EAAE;IAC5D,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,EAAE;IAC/D,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,2CAA2C,EAAE;IAC1E,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,yCAAyC,EAAE;IACxE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,2BAA2B,EAAE;IAC3D,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4BAA4B,EAAE;IAC7D,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE;IACnD,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,sCAAsC,EAAE;IACxE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4BAA4B,EAAE;IAC7D,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,6DAA6D,EAAE;IAC9F,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE;CACxC,CAAC","sourcesContent":["export type SlashCommandSource = \"extension\" | \"prompt\" | \"skill\";\n\nexport type SlashCommandLocation = \"user\" | \"project\" | \"path\";\n\nexport interface SlashCommandInfo {\n\tname: string;\n\tdescription?: string;\n\tsource: SlashCommandSource;\n\tlocation?: SlashCommandLocation;\n\tpath?: string;\n}\n\nexport interface BuiltinSlashCommand {\n\tname: string;\n\tdescription: string;\n}\n\nexport const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [\n\t{ name: \"settings\", description: \"Open settings menu\" },\n\t{ name: \"model\", description: \"Select model (opens selector UI)\" },\n\t{ name: \"scoped-models\", description: \"Enable/disable models for Ctrl+P cycling\" },\n\t{ name: \"export\", description: \"Export session (HTML default, or specify path: .html/.jsonl)\" },\n\t{ name: \"import\", description: \"Import and resume a session from a JSONL file\" },\n\t{ name: \"share\", description: \"Share session as a secret GitHub gist\" },\n\t{ name: \"copy\", description: \"Copy last agent message to clipboard\" },\n\t{ name: \"name\", description: \"Set session display name\" },\n\t{ name: \"session\", description: \"Show session info and stats\" },\n\t{ name: \"changelog\", description: \"Show changelog entries\" },\n\t{ name: \"hotkeys\", description: \"Show all keyboard shortcuts\" },\n\t{ name: \"fork\", description: \"Create a new fork from a previous message\" },\n\t{ name: \"tree\", description: \"Navigate session tree (switch branches)\" },\n\t{ name: \"login\", description: \"Login with OAuth provider\" },\n\t{ name: \"logout\", description: \"Logout from OAuth provider\" },\n\t{ name: \"new\", description: \"Start a new session\" },\n\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t{ name: \"resume\", description: \"Resume a different session\" },\n\t{ name: \"reload\", description: \"Reload keybindings, extensions, skills, prompts, and themes\" },\n\t{ name: \"quit\", description: \"Quit pi\" },\n];\n"]}
1
+ {"version":3,"file":"slash-commands.js","sourceRoot":"","sources":["../../src/core/slash-commands.ts"],"names":[],"mappings":"AAgBA,MAAM,CAAC,MAAM,sBAAsB,GAAuC;IACzE,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,oBAAoB,EAAE;IACvD,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,kCAAkC,EAAE;IAClE,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,0CAA0C,EAAE;IAClF,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8DAA8D,EAAE;IAC/F,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+CAA+C,EAAE;IAChF,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,uCAAuC,EAAE;IACvE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,sCAAsC,EAAE;IACrE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,0BAA0B,EAAE;IACzD,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,EAAE;IAC/D,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,wBAAwB,EAAE;IAC5D,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,EAAE;IAC/D,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,2CAA2C,EAAE;IAC1E,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,yCAAyC,EAAE;IACxE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,2BAA2B,EAAE;IAC3D,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4BAA4B,EAAE;IAC7D,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE;IACnD,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,sCAAsC,EAAE;IACxE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4BAA4B,EAAE;IAC7D,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,6DAA6D,EAAE;IAC9F,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE;CACxC,CAAC","sourcesContent":["import type { SourceInfo } from \"./source-info.js\";\n\nexport type SlashCommandSource = \"extension\" | \"prompt\" | \"skill\";\n\nexport interface SlashCommandInfo {\n\tname: string;\n\tdescription?: string;\n\tsource: SlashCommandSource;\n\tsourceInfo: SourceInfo;\n}\n\nexport interface BuiltinSlashCommand {\n\tname: string;\n\tdescription: string;\n}\n\nexport const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [\n\t{ name: \"settings\", description: \"Open settings menu\" },\n\t{ name: \"model\", description: \"Select model (opens selector UI)\" },\n\t{ name: \"scoped-models\", description: \"Enable/disable models for Ctrl+P cycling\" },\n\t{ name: \"export\", description: \"Export session (HTML default, or specify path: .html/.jsonl)\" },\n\t{ name: \"import\", description: \"Import and resume a session from a JSONL file\" },\n\t{ name: \"share\", description: \"Share session as a secret GitHub gist\" },\n\t{ name: \"copy\", description: \"Copy last agent message to clipboard\" },\n\t{ name: \"name\", description: \"Set session display name\" },\n\t{ name: \"session\", description: \"Show session info and stats\" },\n\t{ name: \"changelog\", description: \"Show changelog entries\" },\n\t{ name: \"hotkeys\", description: \"Show all keyboard shortcuts\" },\n\t{ name: \"fork\", description: \"Create a new fork from a previous message\" },\n\t{ name: \"tree\", description: \"Navigate session tree (switch branches)\" },\n\t{ name: \"login\", description: \"Login with OAuth provider\" },\n\t{ name: \"logout\", description: \"Logout from OAuth provider\" },\n\t{ name: \"new\", description: \"Start a new session\" },\n\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t{ name: \"resume\", description: \"Resume a different session\" },\n\t{ name: \"reload\", description: \"Reload keybindings, extensions, skills, prompts, and themes\" },\n\t{ name: \"quit\", description: \"Quit pi\" },\n];\n"]}
@@ -0,0 +1,18 @@
1
+ import type { PathMetadata } from "./package-manager.js";
2
+ export type SourceScope = "user" | "project" | "temporary";
3
+ export type SourceOrigin = "package" | "top-level";
4
+ export interface SourceInfo {
5
+ path: string;
6
+ source: string;
7
+ scope: SourceScope;
8
+ origin: SourceOrigin;
9
+ baseDir?: string;
10
+ }
11
+ export declare function createSourceInfo(path: string, metadata: PathMetadata): SourceInfo;
12
+ export declare function createSyntheticSourceInfo(path: string, options: {
13
+ source: string;
14
+ scope?: SourceScope;
15
+ origin?: SourceOrigin;
16
+ baseDir?: string;
17
+ }): SourceInfo;
18
+ //# sourceMappingURL=source-info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-info.d.ts","sourceRoot":"","sources":["../../src/core/source-info.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;AAC3D,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,WAAW,CAAC;AAEnD,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,UAAU,CAQjF;AAED,wBAAgB,yBAAyB,CACxC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IACR,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,GACC,UAAU,CAQZ","sourcesContent":["import type { PathMetadata } from \"./package-manager.js\";\n\nexport type SourceScope = \"user\" | \"project\" | \"temporary\";\nexport type SourceOrigin = \"package\" | \"top-level\";\n\nexport interface SourceInfo {\n\tpath: string;\n\tsource: string;\n\tscope: SourceScope;\n\torigin: SourceOrigin;\n\tbaseDir?: string;\n}\n\nexport function createSourceInfo(path: string, metadata: PathMetadata): SourceInfo {\n\treturn {\n\t\tpath,\n\t\tsource: metadata.source,\n\t\tscope: metadata.scope,\n\t\torigin: metadata.origin,\n\t\tbaseDir: metadata.baseDir,\n\t};\n}\n\nexport function createSyntheticSourceInfo(\n\tpath: string,\n\toptions: {\n\t\tsource: string;\n\t\tscope?: SourceScope;\n\t\torigin?: SourceOrigin;\n\t\tbaseDir?: string;\n\t},\n): SourceInfo {\n\treturn {\n\t\tpath,\n\t\tsource: options.source,\n\t\tscope: options.scope ?? \"temporary\",\n\t\torigin: options.origin ?? \"top-level\",\n\t\tbaseDir: options.baseDir,\n\t};\n}\n"]}
@@ -0,0 +1,19 @@
1
+ export function createSourceInfo(path, metadata) {
2
+ return {
3
+ path,
4
+ source: metadata.source,
5
+ scope: metadata.scope,
6
+ origin: metadata.origin,
7
+ baseDir: metadata.baseDir,
8
+ };
9
+ }
10
+ export function createSyntheticSourceInfo(path, options) {
11
+ return {
12
+ path,
13
+ source: options.source,
14
+ scope: options.scope ?? "temporary",
15
+ origin: options.origin ?? "top-level",
16
+ baseDir: options.baseDir,
17
+ };
18
+ }
19
+ //# sourceMappingURL=source-info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-info.js","sourceRoot":"","sources":["../../src/core/source-info.ts"],"names":[],"mappings":"AAaA,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,QAAsB,EAAc;IAClF,OAAO;QACN,IAAI;QACJ,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;KACzB,CAAC;AAAA,CACF;AAED,MAAM,UAAU,yBAAyB,CACxC,IAAY,EACZ,OAKC,EACY;IACb,OAAO;QACN,IAAI;QACJ,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,WAAW;QACnC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,WAAW;QACrC,OAAO,EAAE,OAAO,CAAC,OAAO;KACxB,CAAC;AAAA,CACF","sourcesContent":["import type { PathMetadata } from \"./package-manager.js\";\n\nexport type SourceScope = \"user\" | \"project\" | \"temporary\";\nexport type SourceOrigin = \"package\" | \"top-level\";\n\nexport interface SourceInfo {\n\tpath: string;\n\tsource: string;\n\tscope: SourceScope;\n\torigin: SourceOrigin;\n\tbaseDir?: string;\n}\n\nexport function createSourceInfo(path: string, metadata: PathMetadata): SourceInfo {\n\treturn {\n\t\tpath,\n\t\tsource: metadata.source,\n\t\tscope: metadata.scope,\n\t\torigin: metadata.origin,\n\t\tbaseDir: metadata.baseDir,\n\t};\n}\n\nexport function createSyntheticSourceInfo(\n\tpath: string,\n\toptions: {\n\t\tsource: string;\n\t\tscope?: SourceScope;\n\t\torigin?: SourceOrigin;\n\t\tbaseDir?: string;\n\t},\n): SourceInfo {\n\treturn {\n\t\tpath,\n\t\tsource: options.source,\n\t\tscope: options.scope ?? \"temporary\",\n\t\torigin: options.origin ?? \"top-level\",\n\t\tbaseDir: options.baseDir,\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAyB,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAahE,MAAM,WAAW,wBAAwB;IACxC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,uCAAuC;IACvC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,yBAAyB;IACzB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,GAAG,MAAM,CA2KhF","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.js\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.js\";\n\n/** Tool descriptions for system prompt */\nconst toolDescriptions: Record<string, string> = {\n\tread: \"Read file contents\",\n\tbash: \"Execute bash commands (ls, grep, find, etc.)\",\n\tedit: \"Make surgical edits to files (find exact text and replace)\",\n\twrite: \"Create or overwrite files\",\n\tgrep: \"Search file contents for patterns (respects .gitignore)\",\n\tfind: \"Find files by glob pattern (respects .gitignore)\",\n\tls: \"List directory contents\",\n};\n\nexport interface BuildSystemPromptOptions {\n\t/** Custom system prompt (replaces default). */\n\tcustomPrompt?: string;\n\t/** Tools to include in prompt. Default: [read, bash, edit, write] */\n\tselectedTools?: string[];\n\t/** Optional one-line tool snippets keyed by tool name. */\n\ttoolSnippets?: Record<string, string>;\n\t/** Additional guideline bullets appended to the default system prompt guidelines. */\n\tpromptGuidelines?: string[];\n\t/** Text to append to system prompt. */\n\tappendSystemPrompt?: string;\n\t/** Working directory. Default: process.cwd() */\n\tcwd?: string;\n\t/** Pre-loaded context files. */\n\tcontextFiles?: Array<{ path: string; content: string }>;\n\t/** Pre-loaded skills. */\n\tskills?: Skill[];\n}\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions = {}): string {\n\tconst {\n\t\tcustomPrompt,\n\t\tselectedTools,\n\t\ttoolSnippets,\n\t\tpromptGuidelines,\n\t\tappendSystemPrompt,\n\t\tcwd,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst resolvedCwd = cwd ?? process.cwd();\n\tconst promptCwd = resolvedCwd.replace(/\\\\/g, \"/\");\n\n\tconst date = new Date().toISOString().slice(0, 10);\n\n\tconst appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n\n\tconst contextFiles = providedContextFiles ?? [];\n\tconst skills = providedSkills ?? [];\n\n\tif (customPrompt) {\n\t\tlet prompt = customPrompt;\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t\t}\n\t\t}\n\n\t\t// Append skills section (only if read tool is available)\n\t\tconst customPromptHasRead = !selectedTools || selectedTools.includes(\"read\");\n\t\tif (customPromptHasRead && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills);\n\t\t}\n\n\t\t// Add date and working directory last\n\t\tprompt += `\\nCurrent date: ${date}`;\n\t\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\t// Build tools list based on selected tools.\n\t// Built-ins use toolDescriptions. Custom tools can provide one-line snippets.\n\tconst tools = selectedTools || [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst visibleTools = tools.filter((name) => name in toolDescriptions || toolSnippets?.[name]);\n\tconst toolsList =\n\t\tvisibleTools.length > 0\n\t\t\t? visibleTools\n\t\t\t\t\t.map((name) => {\n\t\t\t\t\t\tconst snippet = toolSnippets?.[name] ?? toolDescriptions[name] ?? name;\n\t\t\t\t\t\treturn `- ${name}: ${snippet}`;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\")\n\t\t\t: \"(none)\";\n\n\t// Build guidelines based on which tools are actually available\n\tconst guidelinesList: string[] = [];\n\tconst guidelinesSet = new Set<string>();\n\tconst addGuideline = (guideline: string): void => {\n\t\tif (guidelinesSet.has(guideline)) {\n\t\t\treturn;\n\t\t}\n\t\tguidelinesSet.add(guideline);\n\t\tguidelinesList.push(guideline);\n\t};\n\n\tconst hasBash = tools.includes(\"bash\");\n\tconst hasEdit = tools.includes(\"edit\");\n\tconst hasWrite = tools.includes(\"write\");\n\tconst hasGrep = tools.includes(\"grep\");\n\tconst hasFind = tools.includes(\"find\");\n\tconst hasLs = tools.includes(\"ls\");\n\tconst hasRead = tools.includes(\"read\");\n\n\t// File exploration guidelines\n\tif (hasBash && !hasGrep && !hasFind && !hasLs) {\n\t\taddGuideline(\"Use bash for file operations like ls, rg, find\");\n\t} else if (hasBash && (hasGrep || hasFind || hasLs)) {\n\t\taddGuideline(\"Prefer grep/find/ls tools over bash for file exploration (faster, respects .gitignore)\");\n\t}\n\n\t// Read before edit guideline\n\tif (hasRead && hasEdit) {\n\t\taddGuideline(\"Use read to examine files before editing. You must use this tool instead of cat or sed.\");\n\t}\n\n\t// Edit guideline\n\tif (hasEdit) {\n\t\taddGuideline(\"Use edit for precise changes (old text must match exactly)\");\n\t}\n\n\t// Write guideline\n\tif (hasWrite) {\n\t\taddGuideline(\"Use write only for new files or complete rewrites\");\n\t}\n\n\t// Output guideline (only when actually writing or executing)\n\tif (hasEdit || hasWrite) {\n\t\taddGuideline(\n\t\t\t\"When summarizing your actions, output plain text directly - do NOT use cat or bash to display what you did\",\n\t\t);\n\t}\n\n\tfor (const guideline of promptGuidelines ?? []) {\n\t\tconst normalized = guideline.trim();\n\t\tif (normalized.length > 0) {\n\t\t\taddGuideline(normalized);\n\t\t}\n\t}\n\n\t// Always include these\n\taddGuideline(\"Be concise in your responses\");\n\taddGuideline(\"Show file paths clearly when working with files\");\n\n\tconst guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n\tlet prompt = `You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nPi documentation (read only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md)\n- When working on pi topics, read the docs and examples, and follow .md cross-references before implementing\n- Always read pi .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t}\n\t}\n\n\t// Append skills section (only if read tool is available)\n\tif (hasRead && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills);\n\t}\n\n\t// Add date and working directory last\n\tprompt += `\\nCurrent date: ${date}`;\n\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\treturn prompt;\n}\n"]}
1
+ {"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAyB,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAEhE,MAAM,WAAW,wBAAwB;IACxC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,uCAAuC;IACvC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,yBAAyB;IACzB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,GAAG,MAAM,CA4IhF","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.js\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.js\";\n\nexport interface BuildSystemPromptOptions {\n\t/** Custom system prompt (replaces default). */\n\tcustomPrompt?: string;\n\t/** Tools to include in prompt. Default: [read, bash, edit, write] */\n\tselectedTools?: string[];\n\t/** Optional one-line tool snippets keyed by tool name. */\n\ttoolSnippets?: Record<string, string>;\n\t/** Additional guideline bullets appended to the default system prompt guidelines. */\n\tpromptGuidelines?: string[];\n\t/** Text to append to system prompt. */\n\tappendSystemPrompt?: string;\n\t/** Working directory. Default: process.cwd() */\n\tcwd?: string;\n\t/** Pre-loaded context files. */\n\tcontextFiles?: Array<{ path: string; content: string }>;\n\t/** Pre-loaded skills. */\n\tskills?: Skill[];\n}\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions = {}): string {\n\tconst {\n\t\tcustomPrompt,\n\t\tselectedTools,\n\t\ttoolSnippets,\n\t\tpromptGuidelines,\n\t\tappendSystemPrompt,\n\t\tcwd,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst resolvedCwd = cwd ?? process.cwd();\n\tconst promptCwd = resolvedCwd.replace(/\\\\/g, \"/\");\n\n\tconst date = new Date().toISOString().slice(0, 10);\n\n\tconst appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n\n\tconst contextFiles = providedContextFiles ?? [];\n\tconst skills = providedSkills ?? [];\n\n\tif (customPrompt) {\n\t\tlet prompt = customPrompt;\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t\t}\n\t\t}\n\n\t\t// Append skills section (only if read tool is available)\n\t\tconst customPromptHasRead = !selectedTools || selectedTools.includes(\"read\");\n\t\tif (customPromptHasRead && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills);\n\t\t}\n\n\t\t// Add date and working directory last\n\t\tprompt += `\\nCurrent date: ${date}`;\n\t\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\t// Build tools list based on selected tools.\n\t// A tool appears in Available tools only when the caller provides a one-line snippet.\n\tconst tools = selectedTools || [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst visibleTools = tools.filter((name) => !!toolSnippets?.[name]);\n\tconst toolsList =\n\t\tvisibleTools.length > 0 ? visibleTools.map((name) => `- ${name}: ${toolSnippets![name]}`).join(\"\\n\") : \"(none)\";\n\n\t// Build guidelines based on which tools are actually available\n\tconst guidelinesList: string[] = [];\n\tconst guidelinesSet = new Set<string>();\n\tconst addGuideline = (guideline: string): void => {\n\t\tif (guidelinesSet.has(guideline)) {\n\t\t\treturn;\n\t\t}\n\t\tguidelinesSet.add(guideline);\n\t\tguidelinesList.push(guideline);\n\t};\n\n\tconst hasBash = tools.includes(\"bash\");\n\tconst hasGrep = tools.includes(\"grep\");\n\tconst hasFind = tools.includes(\"find\");\n\tconst hasLs = tools.includes(\"ls\");\n\tconst hasRead = tools.includes(\"read\");\n\n\t// File exploration guidelines\n\tif (hasBash && !hasGrep && !hasFind && !hasLs) {\n\t\taddGuideline(\"Use bash for file operations like ls, rg, find\");\n\t} else if (hasBash && (hasGrep || hasFind || hasLs)) {\n\t\taddGuideline(\"Prefer grep/find/ls tools over bash for file exploration (faster, respects .gitignore)\");\n\t}\n\n\tfor (const guideline of promptGuidelines ?? []) {\n\t\tconst normalized = guideline.trim();\n\t\tif (normalized.length > 0) {\n\t\t\taddGuideline(normalized);\n\t\t}\n\t}\n\n\t// Always include these\n\taddGuideline(\"Be concise in your responses\");\n\taddGuideline(\"Show file paths clearly when working with files\");\n\n\tconst guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n\tlet prompt = `You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nPi documentation (read only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md)\n- When working on pi topics, read the docs and examples, and follow .md cross-references before implementing\n- Always read pi .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t}\n\t}\n\n\t// Append skills section (only if read tool is available)\n\tif (hasRead && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills);\n\t}\n\n\t// Add date and working directory last\n\tprompt += `\\nCurrent date: ${date}`;\n\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\treturn prompt;\n}\n"]}
@@ -3,16 +3,6 @@
3
3
  */
4
4
  import { getDocsPath, getExamplesPath, getReadmePath } from "../config.js";
5
5
  import { formatSkillsForPrompt } from "./skills.js";
6
- /** Tool descriptions for system prompt */
7
- const toolDescriptions = {
8
- read: "Read file contents",
9
- bash: "Execute bash commands (ls, grep, find, etc.)",
10
- edit: "Make surgical edits to files (find exact text and replace)",
11
- write: "Create or overwrite files",
12
- grep: "Search file contents for patterns (respects .gitignore)",
13
- find: "Find files by glob pattern (respects .gitignore)",
14
- ls: "List directory contents",
15
- };
16
6
  /** Build the system prompt with tools, guidelines, and context */
17
7
  export function buildSystemPrompt(options = {}) {
18
8
  const { customPrompt, selectedTools, toolSnippets, promptGuidelines, appendSystemPrompt, cwd, contextFiles: providedContextFiles, skills: providedSkills, } = options;
@@ -50,17 +40,10 @@ export function buildSystemPrompt(options = {}) {
50
40
  const docsPath = getDocsPath();
51
41
  const examplesPath = getExamplesPath();
52
42
  // Build tools list based on selected tools.
53
- // Built-ins use toolDescriptions. Custom tools can provide one-line snippets.
43
+ // A tool appears in Available tools only when the caller provides a one-line snippet.
54
44
  const tools = selectedTools || ["read", "bash", "edit", "write"];
55
- const visibleTools = tools.filter((name) => name in toolDescriptions || toolSnippets?.[name]);
56
- const toolsList = visibleTools.length > 0
57
- ? visibleTools
58
- .map((name) => {
59
- const snippet = toolSnippets?.[name] ?? toolDescriptions[name] ?? name;
60
- return `- ${name}: ${snippet}`;
61
- })
62
- .join("\n")
63
- : "(none)";
45
+ const visibleTools = tools.filter((name) => !!toolSnippets?.[name]);
46
+ const toolsList = visibleTools.length > 0 ? visibleTools.map((name) => `- ${name}: ${toolSnippets[name]}`).join("\n") : "(none)";
64
47
  // Build guidelines based on which tools are actually available
65
48
  const guidelinesList = [];
66
49
  const guidelinesSet = new Set();
@@ -72,8 +55,6 @@ export function buildSystemPrompt(options = {}) {
72
55
  guidelinesList.push(guideline);
73
56
  };
74
57
  const hasBash = tools.includes("bash");
75
- const hasEdit = tools.includes("edit");
76
- const hasWrite = tools.includes("write");
77
58
  const hasGrep = tools.includes("grep");
78
59
  const hasFind = tools.includes("find");
79
60
  const hasLs = tools.includes("ls");
@@ -85,22 +66,6 @@ export function buildSystemPrompt(options = {}) {
85
66
  else if (hasBash && (hasGrep || hasFind || hasLs)) {
86
67
  addGuideline("Prefer grep/find/ls tools over bash for file exploration (faster, respects .gitignore)");
87
68
  }
88
- // Read before edit guideline
89
- if (hasRead && hasEdit) {
90
- addGuideline("Use read to examine files before editing. You must use this tool instead of cat or sed.");
91
- }
92
- // Edit guideline
93
- if (hasEdit) {
94
- addGuideline("Use edit for precise changes (old text must match exactly)");
95
- }
96
- // Write guideline
97
- if (hasWrite) {
98
- addGuideline("Use write only for new files or complete rewrites");
99
- }
100
- // Output guideline (only when actually writing or executing)
101
- if (hasEdit || hasWrite) {
102
- addGuideline("When summarizing your actions, output plain text directly - do NOT use cat or bash to display what you did");
103
- }
104
69
  for (const guideline of promptGuidelines ?? []) {
105
70
  const normalized = guideline.trim();
106
71
  if (normalized.length > 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.js\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.js\";\n\n/** Tool descriptions for system prompt */\nconst toolDescriptions: Record<string, string> = {\n\tread: \"Read file contents\",\n\tbash: \"Execute bash commands (ls, grep, find, etc.)\",\n\tedit: \"Make surgical edits to files (find exact text and replace)\",\n\twrite: \"Create or overwrite files\",\n\tgrep: \"Search file contents for patterns (respects .gitignore)\",\n\tfind: \"Find files by glob pattern (respects .gitignore)\",\n\tls: \"List directory contents\",\n};\n\nexport interface BuildSystemPromptOptions {\n\t/** Custom system prompt (replaces default). */\n\tcustomPrompt?: string;\n\t/** Tools to include in prompt. Default: [read, bash, edit, write] */\n\tselectedTools?: string[];\n\t/** Optional one-line tool snippets keyed by tool name. */\n\ttoolSnippets?: Record<string, string>;\n\t/** Additional guideline bullets appended to the default system prompt guidelines. */\n\tpromptGuidelines?: string[];\n\t/** Text to append to system prompt. */\n\tappendSystemPrompt?: string;\n\t/** Working directory. Default: process.cwd() */\n\tcwd?: string;\n\t/** Pre-loaded context files. */\n\tcontextFiles?: Array<{ path: string; content: string }>;\n\t/** Pre-loaded skills. */\n\tskills?: Skill[];\n}\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions = {}): string {\n\tconst {\n\t\tcustomPrompt,\n\t\tselectedTools,\n\t\ttoolSnippets,\n\t\tpromptGuidelines,\n\t\tappendSystemPrompt,\n\t\tcwd,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst resolvedCwd = cwd ?? process.cwd();\n\tconst promptCwd = resolvedCwd.replace(/\\\\/g, \"/\");\n\n\tconst date = new Date().toISOString().slice(0, 10);\n\n\tconst appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n\n\tconst contextFiles = providedContextFiles ?? [];\n\tconst skills = providedSkills ?? [];\n\n\tif (customPrompt) {\n\t\tlet prompt = customPrompt;\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t\t}\n\t\t}\n\n\t\t// Append skills section (only if read tool is available)\n\t\tconst customPromptHasRead = !selectedTools || selectedTools.includes(\"read\");\n\t\tif (customPromptHasRead && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills);\n\t\t}\n\n\t\t// Add date and working directory last\n\t\tprompt += `\\nCurrent date: ${date}`;\n\t\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\t// Build tools list based on selected tools.\n\t// Built-ins use toolDescriptions. Custom tools can provide one-line snippets.\n\tconst tools = selectedTools || [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst visibleTools = tools.filter((name) => name in toolDescriptions || toolSnippets?.[name]);\n\tconst toolsList =\n\t\tvisibleTools.length > 0\n\t\t\t? visibleTools\n\t\t\t\t\t.map((name) => {\n\t\t\t\t\t\tconst snippet = toolSnippets?.[name] ?? toolDescriptions[name] ?? name;\n\t\t\t\t\t\treturn `- ${name}: ${snippet}`;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\")\n\t\t\t: \"(none)\";\n\n\t// Build guidelines based on which tools are actually available\n\tconst guidelinesList: string[] = [];\n\tconst guidelinesSet = new Set<string>();\n\tconst addGuideline = (guideline: string): void => {\n\t\tif (guidelinesSet.has(guideline)) {\n\t\t\treturn;\n\t\t}\n\t\tguidelinesSet.add(guideline);\n\t\tguidelinesList.push(guideline);\n\t};\n\n\tconst hasBash = tools.includes(\"bash\");\n\tconst hasEdit = tools.includes(\"edit\");\n\tconst hasWrite = tools.includes(\"write\");\n\tconst hasGrep = tools.includes(\"grep\");\n\tconst hasFind = tools.includes(\"find\");\n\tconst hasLs = tools.includes(\"ls\");\n\tconst hasRead = tools.includes(\"read\");\n\n\t// File exploration guidelines\n\tif (hasBash && !hasGrep && !hasFind && !hasLs) {\n\t\taddGuideline(\"Use bash for file operations like ls, rg, find\");\n\t} else if (hasBash && (hasGrep || hasFind || hasLs)) {\n\t\taddGuideline(\"Prefer grep/find/ls tools over bash for file exploration (faster, respects .gitignore)\");\n\t}\n\n\t// Read before edit guideline\n\tif (hasRead && hasEdit) {\n\t\taddGuideline(\"Use read to examine files before editing. You must use this tool instead of cat or sed.\");\n\t}\n\n\t// Edit guideline\n\tif (hasEdit) {\n\t\taddGuideline(\"Use edit for precise changes (old text must match exactly)\");\n\t}\n\n\t// Write guideline\n\tif (hasWrite) {\n\t\taddGuideline(\"Use write only for new files or complete rewrites\");\n\t}\n\n\t// Output guideline (only when actually writing or executing)\n\tif (hasEdit || hasWrite) {\n\t\taddGuideline(\n\t\t\t\"When summarizing your actions, output plain text directly - do NOT use cat or bash to display what you did\",\n\t\t);\n\t}\n\n\tfor (const guideline of promptGuidelines ?? []) {\n\t\tconst normalized = guideline.trim();\n\t\tif (normalized.length > 0) {\n\t\t\taddGuideline(normalized);\n\t\t}\n\t}\n\n\t// Always include these\n\taddGuideline(\"Be concise in your responses\");\n\taddGuideline(\"Show file paths clearly when working with files\");\n\n\tconst guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n\tlet prompt = `You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nPi documentation (read only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md)\n- When working on pi topics, read the docs and examples, and follow .md cross-references before implementing\n- Always read pi .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t}\n\t}\n\n\t// Append skills section (only if read tool is available)\n\tif (hasRead && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills);\n\t}\n\n\t// Add date and working directory last\n\tprompt += `\\nCurrent date: ${date}`;\n\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\treturn prompt;\n}\n"]}
1
+ {"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.js\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.js\";\n\nexport interface BuildSystemPromptOptions {\n\t/** Custom system prompt (replaces default). */\n\tcustomPrompt?: string;\n\t/** Tools to include in prompt. Default: [read, bash, edit, write] */\n\tselectedTools?: string[];\n\t/** Optional one-line tool snippets keyed by tool name. */\n\ttoolSnippets?: Record<string, string>;\n\t/** Additional guideline bullets appended to the default system prompt guidelines. */\n\tpromptGuidelines?: string[];\n\t/** Text to append to system prompt. */\n\tappendSystemPrompt?: string;\n\t/** Working directory. Default: process.cwd() */\n\tcwd?: string;\n\t/** Pre-loaded context files. */\n\tcontextFiles?: Array<{ path: string; content: string }>;\n\t/** Pre-loaded skills. */\n\tskills?: Skill[];\n}\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions = {}): string {\n\tconst {\n\t\tcustomPrompt,\n\t\tselectedTools,\n\t\ttoolSnippets,\n\t\tpromptGuidelines,\n\t\tappendSystemPrompt,\n\t\tcwd,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst resolvedCwd = cwd ?? process.cwd();\n\tconst promptCwd = resolvedCwd.replace(/\\\\/g, \"/\");\n\n\tconst date = new Date().toISOString().slice(0, 10);\n\n\tconst appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n\n\tconst contextFiles = providedContextFiles ?? [];\n\tconst skills = providedSkills ?? [];\n\n\tif (customPrompt) {\n\t\tlet prompt = customPrompt;\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t\t}\n\t\t}\n\n\t\t// Append skills section (only if read tool is available)\n\t\tconst customPromptHasRead = !selectedTools || selectedTools.includes(\"read\");\n\t\tif (customPromptHasRead && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills);\n\t\t}\n\n\t\t// Add date and working directory last\n\t\tprompt += `\\nCurrent date: ${date}`;\n\t\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\t// Build tools list based on selected tools.\n\t// A tool appears in Available tools only when the caller provides a one-line snippet.\n\tconst tools = selectedTools || [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst visibleTools = tools.filter((name) => !!toolSnippets?.[name]);\n\tconst toolsList =\n\t\tvisibleTools.length > 0 ? visibleTools.map((name) => `- ${name}: ${toolSnippets![name]}`).join(\"\\n\") : \"(none)\";\n\n\t// Build guidelines based on which tools are actually available\n\tconst guidelinesList: string[] = [];\n\tconst guidelinesSet = new Set<string>();\n\tconst addGuideline = (guideline: string): void => {\n\t\tif (guidelinesSet.has(guideline)) {\n\t\t\treturn;\n\t\t}\n\t\tguidelinesSet.add(guideline);\n\t\tguidelinesList.push(guideline);\n\t};\n\n\tconst hasBash = tools.includes(\"bash\");\n\tconst hasGrep = tools.includes(\"grep\");\n\tconst hasFind = tools.includes(\"find\");\n\tconst hasLs = tools.includes(\"ls\");\n\tconst hasRead = tools.includes(\"read\");\n\n\t// File exploration guidelines\n\tif (hasBash && !hasGrep && !hasFind && !hasLs) {\n\t\taddGuideline(\"Use bash for file operations like ls, rg, find\");\n\t} else if (hasBash && (hasGrep || hasFind || hasLs)) {\n\t\taddGuideline(\"Prefer grep/find/ls tools over bash for file exploration (faster, respects .gitignore)\");\n\t}\n\n\tfor (const guideline of promptGuidelines ?? []) {\n\t\tconst normalized = guideline.trim();\n\t\tif (normalized.length > 0) {\n\t\t\taddGuideline(normalized);\n\t\t}\n\t}\n\n\t// Always include these\n\taddGuideline(\"Be concise in your responses\");\n\taddGuideline(\"Show file paths clearly when working with files\");\n\n\tconst guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n\tlet prompt = `You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nPi documentation (read only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md)\n- When working on pi topics, read the docs and examples, and follow .md cross-references before implementing\n- Always read pi .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t}\n\t}\n\n\t// Append skills section (only if read tool is available)\n\tif (hasRead && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills);\n\t}\n\n\t// Add date and working directory last\n\tprompt += `\\nCurrent date: ${date}`;\n\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\treturn prompt;\n}\n"]}
@@ -1,5 +1,6 @@
1
- import type { AgentTool } from "@mariozechner/pi-agent-core";
1
+ import type { AgentTool } from "@hyperspaceng/neural-agent-core";
2
2
  import { type Static } from "@sinclair/typebox";
3
+ import type { ToolDefinition } from "../extensions/types.js";
3
4
  import { type TruncationResult } from "./truncate.js";
4
5
  declare const bashSchema: import("@sinclair/typebox").TObject<{
5
6
  command: import("@sinclair/typebox").TString;
@@ -12,14 +13,14 @@ export interface BashToolDetails {
12
13
  }
13
14
  /**
14
15
  * Pluggable operations for the bash tool.
15
- * Override these to delegate command execution to remote systems (e.g., SSH).
16
+ * Override these to delegate command execution to remote systems (for example SSH).
16
17
  */
17
18
  export interface BashOperations {
18
19
  /**
19
20
  * Execute a command and stream output.
20
- * @param command - The command to execute
21
- * @param cwd - Working directory
22
- * @param options - Execution options
21
+ * @param command The command to execute
22
+ * @param cwd Working directory
23
+ * @param options Execution options
23
24
  * @returns Promise resolving to exit code (null if killed)
24
25
  */
25
26
  exec: (command: string, cwd: string, options: {
@@ -34,9 +35,8 @@ export interface BashOperations {
34
35
  /**
35
36
  * Create bash operations using pi's built-in local shell execution backend.
36
37
  *
37
- * This is useful for extensions that intercept user_bash and want to keep
38
- * pi's standard local shell behavior while still wrapping or rewriting
39
- * commands before execution.
38
+ * This is useful for extensions that intercept user_bash and still want pi's
39
+ * standard local shell behavior while wrapping or rewriting commands.
40
40
  */
41
41
  export declare function createLocalBashOperations(): BashOperations;
42
42
  export interface BashSpawnContext {
@@ -48,13 +48,23 @@ export type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;
48
48
  export interface BashToolOptions {
49
49
  /** Custom operations for command execution. Default: local shell */
50
50
  operations?: BashOperations;
51
- /** Command prefix prepended to every command (e.g., "shopt -s expand_aliases" for alias support) */
51
+ /** Command prefix prepended to every command (for example shell setup commands) */
52
52
  commandPrefix?: string;
53
53
  /** Hook to adjust command, cwd, or env before execution */
54
54
  spawnHook?: BashSpawnHook;
55
55
  }
56
+ type BashRenderState = {
57
+ startedAt: number | undefined;
58
+ endedAt: number | undefined;
59
+ interval: NodeJS.Timeout | undefined;
60
+ };
61
+ export declare function createBashToolDefinition(cwd: string, options?: BashToolOptions): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState>;
56
62
  export declare function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema>;
57
- /** Default bash tool using process.cwd() - for backwards compatibility */
63
+ /** Default bash tool using process.cwd() for backwards compatibility. */
64
+ export declare const bashToolDefinition: ToolDefinition<import("@sinclair/typebox").TObject<{
65
+ command: import("@sinclair/typebox").TString;
66
+ timeout: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
67
+ }>, BashToolDetails | undefined, BashRenderState>;
58
68
  export declare const bashTool: AgentTool<import("@sinclair/typebox").TObject<{
59
69
  command: import("@sinclair/typebox").TString;
60
70
  timeout: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
@@ -1 +1 @@
1
- {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AAItD,OAAO,EAAoD,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAUtH,QAAA,MAAM,UAAU;;;EAGd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;OAMG;IACH,IAAI,EAAE,CACL,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QACR,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;KACxB,KACG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CAC1C;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,IAAI,cAAc,CAiF1D;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;AAY5E,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,oGAAoG;IACpG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAwJnG;AAED,0EAA0E;AAC1E,eAAO,MAAM,QAAQ;;;QAAgC,CAAC","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { createWriteStream, existsSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentTool } from \"@mariozechner/pi-agent-core\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { spawn } from \"child_process\";\nimport { waitForChildProcess } from \"../../utils/child-process.js\";\nimport { getShellConfig, getShellEnv, killProcessTree } from \"../../utils/shell.js\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateTail } from \"./truncate.js\";\n\n/**\n * Generate a unique temp file path for bash output\n */\nfunction getTempFilePath(): string {\n\tconst id = randomBytes(8).toString(\"hex\");\n\treturn join(tmpdir(), `pi-bash-${id}.log`);\n}\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (e.g., SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command - The command to execute\n\t * @param cwd - Working directory\n\t * @param options - Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<{ exitCode: number | null }>;\n}\n\n/**\n * Create bash operations using pi's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and want to keep\n * pi's standard local shell behavior while still wrapping or rewriting\n * commands before execution.\n */\nexport function createLocalBashOperations(): BashOperations {\n\treturn {\n\t\texec: (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tconst { shell, args } = getShellConfig();\n\n\t\t\t\tif (!existsSync(cwd)) {\n\t\t\t\t\treject(new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst child = spawn(shell, [...args, command], {\n\t\t\t\t\tcwd,\n\t\t\t\t\tdetached: true,\n\t\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t});\n\n\t\t\t\tlet timedOut = false;\n\n\t\t\t\t// Set timeout if provided\n\t\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\t\tif (timeout !== undefined && timeout > 0) {\n\t\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\tif (child.pid) {\n\t\t\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t\t\t}\n\t\t\t\t\t}, timeout * 1000);\n\t\t\t\t}\n\n\t\t\t\t// Stream stdout and stderr\n\t\t\t\tif (child.stdout) {\n\t\t\t\t\tchild.stdout.on(\"data\", onData);\n\t\t\t\t}\n\t\t\t\tif (child.stderr) {\n\t\t\t\t\tchild.stderr.on(\"data\", onData);\n\t\t\t\t}\n\n\t\t\t\t// Handle abort signal - kill entire process tree\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tif (child.pid) {\n\t\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tif (signal) {\n\t\t\t\t\tif (signal.aborted) {\n\t\t\t\t\t\tonAbort();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Handle shell spawn errors and wait for the process to terminate without hanging\n\t\t\t\t// on inherited stdio handles held by detached descendants.\n\t\t\t\twaitForChildProcess(child)\n\t\t\t\t\t.then((code) => {\n\t\t\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\treject(new Error(\"aborted\"));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (timedOut) {\n\t\t\t\t\t\t\treject(new Error(`timeout:${timeout}`));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tresolve({ exitCode: code });\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err) => {\n\t\t\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nfunction resolveSpawnContext(command: string, cwd: string, spawnHook?: BashSpawnHook): BashSpawnContext {\n\tconst baseContext: BashSpawnContext = {\n\t\tcommand,\n\t\tcwd,\n\t\tenv: { ...getShellEnv() },\n\t};\n\n\treturn spawnHook ? spawnHook(baseContext) : baseContext;\n}\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (e.g., \"shopt -s expand_aliases\" for alias support) */\n\tcommandPrefix?: string;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\tconst ops = options?.operations ?? createLocalBashOperations();\n\tconst commandPrefix = options?.commandPrefix;\n\tconst spawnHook = options?.spawnHook;\n\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tparameters: bashSchema,\n\t\texecute: async (\n\t\t\t_toolCallId: string,\n\t\t\t{ command, timeout }: { command: string; timeout?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t) => {\n\t\t\t// Apply command prefix if configured (e.g., \"shopt -s expand_aliases\" for alias support)\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t// We'll stream to a temp file if output gets large\n\t\t\t\tlet tempFilePath: string | undefined;\n\t\t\t\tlet tempFileStream: ReturnType<typeof createWriteStream> | undefined;\n\t\t\t\tlet totalBytes = 0;\n\n\t\t\t\t// Keep a rolling buffer of the last chunk for tail truncation\n\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\tlet chunksBytes = 0;\n\t\t\t\t// Keep more than we need so we have enough for truncation\n\t\t\t\tconst maxChunksBytes = DEFAULT_MAX_BYTES * 2;\n\n\t\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\t\ttotalBytes += data.length;\n\n\t\t\t\t\t// Start writing to temp file once we exceed the threshold\n\t\t\t\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\t\t\t\ttempFilePath = getTempFilePath();\n\t\t\t\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\t\t\t\t// Write all buffered chunks to the file\n\t\t\t\t\t\tfor (const chunk of chunks) {\n\t\t\t\t\t\t\ttempFileStream.write(chunk);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Write to temp file if we have one\n\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\ttempFileStream.write(data);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Keep rolling buffer of recent data\n\t\t\t\t\tchunks.push(data);\n\t\t\t\t\tchunksBytes += data.length;\n\n\t\t\t\t\t// Trim old chunks if buffer is too large\n\t\t\t\t\twhile (chunksBytes > maxChunksBytes && chunks.length > 1) {\n\t\t\t\t\t\tconst removed = chunks.shift()!;\n\t\t\t\t\t\tchunksBytes -= removed.length;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Stream partial output to callback (truncated rolling buffer)\n\t\t\t\t\tif (onUpdate) {\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tconst fullText = fullBuffer.toString(\"utf-8\");\n\t\t\t\t\t\tconst truncation = truncateTail(fullText);\n\t\t\t\t\t\tonUpdate({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: truncation.content || \"\" }],\n\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\ttruncation: truncation.truncated ? truncation : undefined,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tops.exec(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\tonData: handleData,\n\t\t\t\t\tsignal,\n\t\t\t\t\ttimeout,\n\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t})\n\t\t\t\t\t.then(({ exitCode }) => {\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Combine all buffered chunks\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tconst fullOutput = fullBuffer.toString(\"utf-8\");\n\n\t\t\t\t\t\t// Apply tail truncation\n\t\t\t\t\t\tconst truncation = truncateTail(fullOutput);\n\t\t\t\t\t\tlet outputText = truncation.content || \"(no output)\";\n\n\t\t\t\t\t\t// Build details with truncation info\n\t\t\t\t\t\tlet details: BashToolDetails | undefined;\n\n\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\tdetails = {\n\t\t\t\t\t\t\t\ttruncation,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t// Build actionable notice\n\t\t\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\t\t\tconst endLine = truncation.totalLines;\n\n\t\t\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\t\t\t// Edge case: last line alone > 30KB\n\t\t\t\t\t\t\t\tconst lastLineSize = formatSize(Buffer.byteLength(fullOutput.split(\"\\n\").pop() || \"\", \"utf-8\"));\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\t\t\toutputText += `\\n\\nCommand exited with code ${exitCode}`;\n\t\t\t\t\t\t\treject(new Error(outputText));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: outputText }], details });\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Combine all buffered chunks for error output\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tlet output = fullBuffer.toString(\"utf-8\");\n\n\t\t\t\t\t\tif (err.message === \"aborted\") {\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += \"Command aborted\";\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else if (err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += `Command timed out after ${timeoutSecs} seconds`;\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n\n/** Default bash tool using process.cwd() - for backwards compatibility */\nexport const bashTool = createBashTool(process.cwd());\n"]}
1
+ {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAE7D,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AAOtD,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AAGtF,OAAO,EAAoD,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAUtH,QAAA,MAAM,UAAU;;;EAGd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;OAMG;IACH,IAAI,EAAE,CACL,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QACR,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;KACxB,KACG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CAC1C;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,IAAI,cAAc,CA2D1D;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;AAO5E,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,aAAa,CAAC;CAC1B;AAID,KAAK,eAAe,GAAG;IACtB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC,OAAO,GAAG,SAAS,CAAC;CACrC,CAAC;AAwGF,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,EAAE,eAAe,CAAC,CA8JjF;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG;AAED,yEAAyE;AACzE,eAAO,MAAM,kBAAkB;;;iDAA0C,CAAC;AAC1E,eAAO,MAAM,QAAQ;;;QAAgC,CAAC","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { createWriteStream, existsSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentTool } from \"@hyperspaceng/neural-agent-core\";\nimport { Container, Text, truncateToWidth } from \"@hyperspaceng/neural-tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { spawn } from \"child_process\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.js\";\nimport { truncateToVisualLines } from \"../../modes/interactive/components/visual-truncate.js\";\nimport { theme } from \"../../modes/interactive/theme/theme.js\";\nimport { waitForChildProcess } from \"../../utils/child-process.js\";\nimport { getShellConfig, getShellEnv, killProcessTree } from \"../../utils/shell.js\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.js\";\nimport { getTextOutput, invalidArgText, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateTail } from \"./truncate.js\";\n\n/**\n * Generate a unique temp file path for bash output.\n */\nfunction getTempFilePath(): string {\n\tconst id = randomBytes(8).toString(\"hex\");\n\treturn join(tmpdir(), `pi-bash-${id}.log`);\n}\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (for example SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command The command to execute\n\t * @param cwd Working directory\n\t * @param options Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<{ exitCode: number | null }>;\n}\n\n/**\n * Create bash operations using pi's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want pi's\n * standard local shell behavior while wrapping or rewriting commands.\n */\nexport function createLocalBashOperations(): BashOperations {\n\treturn {\n\t\texec: (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tconst { shell, args } = getShellConfig();\n\t\t\t\tif (!existsSync(cwd)) {\n\t\t\t\t\treject(new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst child = spawn(shell, [...args, command], {\n\t\t\t\t\tcwd,\n\t\t\t\t\tdetached: true,\n\t\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t});\n\t\t\t\tlet timedOut = false;\n\t\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\t\t// Set timeout if provided.\n\t\t\t\tif (timeout !== undefined && timeout > 0) {\n\t\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t\t\t}, timeout * 1000);\n\t\t\t\t}\n\t\t\t\t// Stream stdout and stderr.\n\t\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\t\t// Handle abort signal by killing the entire process tree.\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t\t};\n\t\t\t\tif (signal) {\n\t\t\t\t\tif (signal.aborted) onAbort();\n\t\t\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t\t// Handle shell spawn errors and wait for the process to terminate without hanging\n\t\t\t\t// on inherited stdio handles held by detached descendants.\n\t\t\t\twaitForChildProcess(child)\n\t\t\t\t\t.then((code) => {\n\t\t\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\treject(new Error(\"aborted\"));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (timedOut) {\n\t\t\t\t\t\t\treject(new Error(`timeout:${timeout}`));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresolve({ exitCode: code });\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err) => {\n\t\t\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nfunction resolveSpawnContext(command: string, cwd: string, spawnHook?: BashSpawnHook): BashSpawnContext {\n\tconst baseContext: BashSpawnContext = { command, cwd, env: { ...getShellEnv() } };\n\treturn spawnHook ? spawnHook(baseContext) : baseContext;\n}\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (for example shell setup commands) */\n\tcommandPrefix?: string;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n}\n\nconst BASH_PREVIEW_LINES = 5;\n\ntype BashRenderState = {\n\tstartedAt: number | undefined;\n\tendedAt: number | undefined;\n\tinterval: NodeJS.Timeout | undefined;\n};\n\ntype BashResultRenderState = {\n\tcachedWidth: number | undefined;\n\tcachedLines: string[] | undefined;\n\tcachedSkipped: number | undefined;\n};\n\nclass BashResultRenderComponent extends Container {\n\tstate: BashResultRenderState = {\n\t\tcachedWidth: undefined,\n\t\tcachedLines: undefined,\n\t\tcachedSkipped: undefined,\n\t};\n}\n\nfunction formatDuration(ms: number): string {\n\treturn `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction formatBashCall(args: { command?: string; timeout?: number } | undefined): string {\n\tconst command = str(args?.command);\n\tconst timeout = args?.timeout as number | undefined;\n\tconst timeoutSuffix = timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n\tconst commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg(\"toolOutput\", \"...\");\n\treturn theme.fg(\"toolTitle\", theme.bold(`$ ${commandDisplay}`)) + timeoutSuffix;\n}\n\nfunction rebuildBashResultRenderComponent(\n\tcomponent: BashResultRenderComponent,\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: BashToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\tshowImages: boolean,\n\tstartedAt: number | undefined,\n\tendedAt: number | undefined,\n): void {\n\tconst state = component.state;\n\tcomponent.clear();\n\n\tconst output = getTextOutput(result as any, showImages).trim();\n\n\tif (output) {\n\t\tconst styledOutput = output\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t\t.join(\"\\n\");\n\n\t\tif (options.expanded) {\n\t\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\t} else {\n\t\t\tcomponent.addChild({\n\t\t\t\trender: (width: number) => {\n\t\t\t\t\tif (state.cachedLines === undefined || state.cachedWidth !== width) {\n\t\t\t\t\t\tconst preview = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);\n\t\t\t\t\t\tstate.cachedLines = preview.visualLines;\n\t\t\t\t\t\tstate.cachedSkipped = preview.skippedCount;\n\t\t\t\t\t\tstate.cachedWidth = width;\n\t\t\t\t\t}\n\t\t\t\t\tif (state.cachedSkipped && state.cachedSkipped > 0) {\n\t\t\t\t\t\tconst hint =\n\t\t\t\t\t\t\ttheme.fg(\"muted\", `... (${state.cachedSkipped} earlier lines,`) +\n\t\t\t\t\t\t\t` ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t\t\t\t\t\treturn [\"\", truncateToWidth(hint, width, \"...\"), ...(state.cachedLines ?? [])];\n\t\t\t\t\t}\n\t\t\t\t\treturn [\"\", ...(state.cachedLines ?? [])];\n\t\t\t\t},\n\t\t\t\tinvalidate: () => {\n\t\t\t\t\tstate.cachedWidth = undefined;\n\t\t\t\t\tstate.cachedLines = undefined;\n\t\t\t\t\tstate.cachedSkipped = undefined;\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (truncation?.truncated || fullOutputPath) {\n\t\tconst warnings: string[] = [];\n\t\tif (fullOutputPath) {\n\t\t\twarnings.push(`Full output: ${fullOutputPath}`);\n\t\t}\n\t\tif (truncation?.truncated) {\n\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n\t}\n\n\tif (startedAt !== undefined) {\n\t\tconst label = options.isPartial ? \"Elapsed\" : \"Took\";\n\t\tconst endTime = endedAt ?? Date.now();\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));\n\t}\n}\n\nexport function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops = options?.operations ?? createLocalBashOperations();\n\tconst commandPrefix = options?.commandPrefix;\n\tconst spawnHook = options?.spawnHook;\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tpromptSnippet: \"Execute bash commands (ls, grep, find, etc.)\",\n\t\tparameters: bashSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ command, timeout }: { command: string; timeout?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);\n\t\t\tif (onUpdate) {\n\t\t\t\tonUpdate({ content: [], details: undefined });\n\t\t\t}\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tlet tempFilePath: string | undefined;\n\t\t\t\tlet tempFileStream: ReturnType<typeof createWriteStream> | undefined;\n\t\t\t\tlet totalBytes = 0;\n\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\tlet chunksBytes = 0;\n\t\t\t\tconst maxChunksBytes = DEFAULT_MAX_BYTES * 2;\n\n\t\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\t\ttotalBytes += data.length;\n\t\t\t\t\t// Start writing to a temp file once output exceeds the in-memory threshold.\n\t\t\t\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\t\t\t\ttempFilePath = getTempFilePath();\n\t\t\t\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\t\t\t\t// Write all buffered chunks to the file.\n\t\t\t\t\t\tfor (const chunk of chunks) tempFileStream.write(chunk);\n\t\t\t\t\t}\n\t\t\t\t\t// Write to temp file if we have one.\n\t\t\t\t\tif (tempFileStream) tempFileStream.write(data);\n\t\t\t\t\t// Keep a rolling buffer of recent output for tail truncation.\n\t\t\t\t\tchunks.push(data);\n\t\t\t\t\tchunksBytes += data.length;\n\t\t\t\t\t// Trim old chunks if the rolling buffer grows too large.\n\t\t\t\t\twhile (chunksBytes > maxChunksBytes && chunks.length > 1) {\n\t\t\t\t\t\tconst removed = chunks.shift()!;\n\t\t\t\t\t\tchunksBytes -= removed.length;\n\t\t\t\t\t}\n\t\t\t\t\t// Stream partial output using the rolling tail buffer.\n\t\t\t\t\tif (onUpdate) {\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tconst fullText = fullBuffer.toString(\"utf-8\");\n\t\t\t\t\t\tconst truncation = truncateTail(fullText);\n\t\t\t\t\t\tonUpdate({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: truncation.content || \"\" }],\n\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\ttruncation: truncation.truncated ? truncation : undefined,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tops.exec(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\tonData: handleData,\n\t\t\t\t\tsignal,\n\t\t\t\t\ttimeout,\n\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t})\n\t\t\t\t\t.then(({ exitCode }) => {\n\t\t\t\t\t\t// Close temp file stream before building the final result.\n\t\t\t\t\t\tif (tempFileStream) tempFileStream.end();\n\t\t\t\t\t\t// Combine the rolling buffer chunks.\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tconst fullOutput = fullBuffer.toString(\"utf-8\");\n\t\t\t\t\t\t// Apply tail truncation for the final display payload.\n\t\t\t\t\t\tconst truncation = truncateTail(fullOutput);\n\t\t\t\t\t\tlet outputText = truncation.content || \"(no output)\";\n\t\t\t\t\t\tlet details: BashToolDetails | undefined;\n\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\t// Build truncation details and an actionable notice.\n\t\t\t\t\t\t\tdetails = { truncation, fullOutputPath: tempFilePath };\n\t\t\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\t\t\tconst endLine = truncation.totalLines;\n\t\t\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\t\t\t// Edge case: the last line alone is larger than the byte limit.\n\t\t\t\t\t\t\t\tconst lastLineSize = formatSize(Buffer.byteLength(fullOutput.split(\"\\n\").pop() || \"\", \"utf-8\"));\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\t\t\toutputText += `\\n\\nCommand exited with code ${exitCode}`;\n\t\t\t\t\t\t\treject(new Error(outputText));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: outputText }], details });\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\t\t// Close temp file stream and include buffered output in the error message.\n\t\t\t\t\t\tif (tempFileStream) tempFileStream.end();\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tlet output = fullBuffer.toString(\"utf-8\");\n\t\t\t\t\t\tif (err.message === \"aborted\") {\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += \"Command aborted\";\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else if (err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += `Command timed out after ${timeoutSecs} seconds`;\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\trenderCall(args, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (context.executionStarted && state.startedAt === undefined) {\n\t\t\t\tstate.startedAt = Date.now();\n\t\t\t\tstate.endedAt = undefined;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatBashCall(args));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (state.startedAt !== undefined && options.isPartial && !state.interval) {\n\t\t\t\tstate.interval = setInterval(() => context.invalidate(), 1000);\n\t\t\t}\n\t\t\tif (!options.isPartial || context.isError) {\n\t\t\t\tstate.endedAt ??= Date.now();\n\t\t\t\tif (state.interval) {\n\t\t\t\t\tclearInterval(state.interval);\n\t\t\t\t\tstate.interval = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashResultRenderComponent | undefined) ?? new BashResultRenderComponent();\n\t\t\trebuildBashResultRenderComponent(\n\t\t\t\tcomponent,\n\t\t\t\tresult as any,\n\t\t\t\toptions,\n\t\t\t\tcontext.showImages,\n\t\t\t\tstate.startedAt,\n\t\t\t\tstate.endedAt,\n\t\t\t);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\treturn wrapToolDefinition(createBashToolDefinition(cwd, options));\n}\n\n/** Default bash tool using process.cwd() for backwards compatibility. */\nexport const bashToolDefinition = createBashToolDefinition(process.cwd());\nexport const bashTool = createBashTool(process.cwd());\n"]}