@travisennis/acai 0.0.9 → 0.0.11

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 (403) hide show
  1. package/README.md +51 -760
  2. package/bin/acai +52 -0
  3. package/dist/agent/index.d.ts +12 -2
  4. package/dist/agent/index.d.ts.map +1 -1
  5. package/dist/agent/index.js +380 -199
  6. package/dist/agent/sub-agent.d.ts +23 -0
  7. package/dist/agent/sub-agent.d.ts.map +1 -0
  8. package/dist/agent/sub-agent.js +109 -0
  9. package/dist/cli/index.d.ts +26 -0
  10. package/dist/cli/index.d.ts.map +1 -0
  11. package/dist/{cli.js → cli/index.js} +84 -77
  12. package/dist/{stdin.d.ts → cli/stdin.d.ts} +2 -1
  13. package/dist/cli/stdin.d.ts.map +1 -0
  14. package/dist/{stdin.js → cli/stdin.js} +11 -0
  15. package/dist/commands/copy/index.js +2 -2
  16. package/dist/commands/copy/utils.d.ts.map +1 -1
  17. package/dist/commands/copy/utils.js +15 -13
  18. package/dist/commands/generate-rules/index.d.ts +1 -1
  19. package/dist/commands/generate-rules/index.d.ts.map +1 -1
  20. package/dist/commands/generate-rules/index.js +16 -101
  21. package/dist/commands/generate-rules/service.d.ts +22 -0
  22. package/dist/commands/generate-rules/service.d.ts.map +1 -0
  23. package/dist/commands/generate-rules/service.js +103 -0
  24. package/dist/commands/handoff/index.js +2 -2
  25. package/dist/commands/health/index.js +1 -1
  26. package/dist/commands/health/utils.d.ts +3 -2
  27. package/dist/commands/health/utils.d.ts.map +1 -1
  28. package/dist/commands/health/utils.js +6 -0
  29. package/dist/commands/history/index.d.ts +1 -1
  30. package/dist/commands/history/index.d.ts.map +1 -1
  31. package/dist/commands/history/index.js +17 -18
  32. package/dist/commands/history/types.d.ts +38 -0
  33. package/dist/commands/history/types.d.ts.map +1 -1
  34. package/dist/commands/history/utils.d.ts.map +1 -1
  35. package/dist/commands/history/utils.js +63 -58
  36. package/dist/commands/init/index.d.ts.map +1 -1
  37. package/dist/commands/init/index.js +3 -8
  38. package/dist/commands/init-project/index.d.ts.map +1 -1
  39. package/dist/commands/init-project/index.js +3 -3
  40. package/dist/commands/init-project/utils.d.ts +2 -1
  41. package/dist/commands/init-project/utils.d.ts.map +1 -1
  42. package/dist/commands/init-project/utils.js +10 -2
  43. package/dist/commands/list-tools/index.d.ts.map +1 -1
  44. package/dist/commands/list-tools/index.js +7 -31
  45. package/dist/commands/manager.d.ts +2 -2
  46. package/dist/commands/manager.d.ts.map +1 -1
  47. package/dist/commands/manager.js +55 -33
  48. package/dist/commands/model/index.d.ts.map +1 -1
  49. package/dist/commands/model/index.js +20 -151
  50. package/dist/commands/model/model-panel.d.ts +4 -0
  51. package/dist/commands/model/model-panel.d.ts.map +1 -0
  52. package/dist/commands/model/model-panel.js +144 -0
  53. package/dist/commands/paste/index.d.ts.map +1 -1
  54. package/dist/commands/paste/index.js +59 -62
  55. package/dist/commands/paste/utils.d.ts.map +1 -1
  56. package/dist/commands/paste/utils.js +88 -58
  57. package/dist/commands/pickup/index.d.ts.map +1 -1
  58. package/dist/commands/pickup/index.js +6 -3
  59. package/dist/commands/pickup/utils.js +3 -3
  60. package/dist/commands/resources/index.d.ts.map +1 -1
  61. package/dist/commands/resources/index.js +33 -50
  62. package/dist/commands/review/index.d.ts.map +1 -1
  63. package/dist/commands/review/index.js +3 -117
  64. package/dist/commands/review/review-panel.d.ts +3 -0
  65. package/dist/commands/review/review-panel.d.ts.map +1 -0
  66. package/dist/commands/review/review-panel.js +186 -0
  67. package/dist/commands/review/utils.d.ts +15 -1
  68. package/dist/commands/review/utils.d.ts.map +1 -1
  69. package/dist/commands/review/utils.js +127 -68
  70. package/dist/commands/session/index.d.ts +1 -1
  71. package/dist/commands/session/index.d.ts.map +1 -1
  72. package/dist/commands/session/index.js +124 -135
  73. package/dist/commands/shell/index.d.ts.map +1 -1
  74. package/dist/commands/shell/index.js +16 -1
  75. package/dist/commands/types.d.ts +2 -2
  76. package/dist/commands/types.d.ts.map +1 -1
  77. package/dist/{config.d.ts → config/index.d.ts} +20 -9
  78. package/dist/config/index.d.ts.map +1 -0
  79. package/dist/{config.js → config/index.js} +43 -42
  80. package/dist/execution/index.d.ts.map +1 -1
  81. package/dist/execution/index.js +75 -55
  82. package/dist/index.d.ts +1 -0
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +148 -141
  85. package/dist/middleware/cache.d.ts.map +1 -1
  86. package/dist/middleware/cache.js +18 -36
  87. package/dist/models/ai-config.d.ts +1 -0
  88. package/dist/models/ai-config.d.ts.map +1 -1
  89. package/dist/models/ai-config.js +4 -3
  90. package/dist/models/anthropic-provider.d.ts +2 -5
  91. package/dist/models/anthropic-provider.d.ts.map +1 -1
  92. package/dist/models/anthropic-provider.js +3 -70
  93. package/dist/models/deepseek-provider.d.ts +1 -0
  94. package/dist/models/deepseek-provider.d.ts.map +1 -1
  95. package/dist/models/google-provider.d.ts +2 -3
  96. package/dist/models/google-provider.d.ts.map +1 -1
  97. package/dist/models/google-provider.js +0 -26
  98. package/dist/models/groq-provider.d.ts +1 -0
  99. package/dist/models/groq-provider.d.ts.map +1 -1
  100. package/dist/models/manager.d.ts +13 -2
  101. package/dist/models/manager.d.ts.map +1 -1
  102. package/dist/models/manager.js +20 -8
  103. package/dist/models/openai-provider.d.ts +2 -5
  104. package/dist/models/openai-provider.d.ts.map +1 -1
  105. package/dist/models/openai-provider.js +0 -52
  106. package/dist/models/opencode-go-provider.d.ts +25 -0
  107. package/dist/models/opencode-go-provider.d.ts.map +1 -0
  108. package/dist/models/opencode-go-provider.js +78 -0
  109. package/dist/models/opencode-zen-provider.d.ts +7 -3
  110. package/dist/models/opencode-zen-provider.d.ts.map +1 -1
  111. package/dist/models/opencode-zen-provider.js +49 -10
  112. package/dist/models/openrouter-provider.d.ts +27 -31
  113. package/dist/models/openrouter-provider.d.ts.map +1 -1
  114. package/dist/models/openrouter-provider.js +121 -180
  115. package/dist/models/providers.d.ts +3 -3
  116. package/dist/models/providers.d.ts.map +1 -1
  117. package/dist/models/providers.js +6 -0
  118. package/dist/models/xai-provider.d.ts +4 -3
  119. package/dist/models/xai-provider.d.ts.map +1 -1
  120. package/dist/models/xai-provider.js +18 -18
  121. package/dist/modes/manager.d.ts +24 -0
  122. package/dist/modes/manager.d.ts.map +1 -0
  123. package/dist/modes/manager.js +77 -0
  124. package/dist/modes/prompts.d.ts +2 -0
  125. package/dist/modes/prompts.d.ts.map +1 -0
  126. package/dist/modes/prompts.js +142 -0
  127. package/dist/prompts/mentions.d.ts +11 -0
  128. package/dist/prompts/mentions.d.ts.map +1 -0
  129. package/dist/{mentions.js → prompts/mentions.js} +55 -85
  130. package/dist/{prompts.d.ts → prompts/system-prompt.d.ts} +7 -2
  131. package/dist/prompts/system-prompt.d.ts.map +1 -0
  132. package/dist/{prompts.js → prompts/system-prompt.js} +31 -16
  133. package/dist/repl/index.d.ts +174 -0
  134. package/dist/repl/index.d.ts.map +1 -0
  135. package/dist/{repl-new.js → repl/index.js} +397 -76
  136. package/dist/repl/project-status.d.ts +1 -0
  137. package/dist/repl/project-status.d.ts.map +1 -1
  138. package/dist/repl/project-status.js +4 -1
  139. package/dist/sessions/manager.d.ts +92 -0
  140. package/dist/sessions/manager.d.ts.map +1 -1
  141. package/dist/sessions/manager.js +262 -9
  142. package/dist/sessions/summary.d.ts +4 -0
  143. package/dist/sessions/summary.d.ts.map +1 -0
  144. package/dist/sessions/summary.js +30 -0
  145. package/dist/skills/index.d.ts +29 -0
  146. package/dist/skills/index.d.ts.map +1 -0
  147. package/dist/skills/index.js +294 -0
  148. package/dist/subagents/index.d.ts +16 -0
  149. package/dist/subagents/index.d.ts.map +1 -0
  150. package/dist/subagents/index.js +231 -0
  151. package/dist/terminal/control.d.ts +1 -1
  152. package/dist/terminal/control.d.ts.map +1 -1
  153. package/dist/terminal/control.js +3 -3
  154. package/dist/terminal/east-asian-width.d.ts.map +1 -1
  155. package/dist/terminal/east-asian-width.js +404 -351
  156. package/dist/terminal/keys.d.ts +17 -0
  157. package/dist/terminal/keys.d.ts.map +1 -1
  158. package/dist/terminal/keys.js +37 -0
  159. package/dist/terminal/select-prompt.d.ts.map +1 -1
  160. package/dist/terminal/select-prompt.js +24 -12
  161. package/dist/terminal/string-width.d.ts.map +1 -1
  162. package/dist/terminal/string-width.js +25 -27
  163. package/dist/terminal/style.d.ts.map +1 -1
  164. package/dist/terminal/style.js +4 -7
  165. package/dist/terminal/supports-color.d.ts.map +1 -1
  166. package/dist/terminal/supports-color.js +41 -27
  167. package/dist/terminal/table/cell.d.ts +12 -0
  168. package/dist/terminal/table/cell.d.ts.map +1 -1
  169. package/dist/terminal/table/cell.js +40 -25
  170. package/dist/terminal/table/layout-manager.d.ts.map +1 -1
  171. package/dist/terminal/table/layout-manager.js +100 -68
  172. package/dist/terminal/table/utils.d.ts +1 -1
  173. package/dist/terminal/table/utils.d.ts.map +1 -1
  174. package/dist/terminal/table/utils.js +17 -10
  175. package/dist/terminal/wrap-ansi.d.ts.map +1 -1
  176. package/dist/terminal/wrap-ansi.js +174 -105
  177. package/dist/tokens/tracker.d.ts +1 -0
  178. package/dist/tokens/tracker.d.ts.map +1 -1
  179. package/dist/tokens/tracker.js +3 -0
  180. package/dist/tools/agent.d.ts +27 -0
  181. package/dist/tools/agent.d.ts.map +1 -0
  182. package/dist/tools/agent.js +81 -0
  183. package/dist/tools/apply-patch.d.ts +62 -0
  184. package/dist/tools/apply-patch.d.ts.map +1 -0
  185. package/dist/tools/apply-patch.js +377 -0
  186. package/dist/tools/bash.d.ts +4 -3
  187. package/dist/tools/bash.d.ts.map +1 -1
  188. package/dist/tools/bash.js +349 -141
  189. package/dist/tools/directory-tree.d.ts +3 -3
  190. package/dist/tools/directory-tree.d.ts.map +1 -1
  191. package/dist/tools/directory-tree.js +8 -5
  192. package/dist/tools/dynamic-tool-loader.d.ts +3 -6
  193. package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
  194. package/dist/tools/dynamic-tool-loader.js +20 -4
  195. package/dist/tools/edit-file.d.ts +7 -7
  196. package/dist/tools/edit-file.d.ts.map +1 -1
  197. package/dist/tools/edit-file.js +292 -85
  198. package/dist/tools/glob.d.ts +6 -6
  199. package/dist/tools/glob.d.ts.map +1 -1
  200. package/dist/tools/glob.js +110 -63
  201. package/dist/tools/grep.d.ts +15 -12
  202. package/dist/tools/grep.d.ts.map +1 -1
  203. package/dist/tools/grep.js +315 -193
  204. package/dist/tools/index.d.ts +114 -9
  205. package/dist/tools/index.d.ts.map +1 -1
  206. package/dist/tools/index.js +39 -24
  207. package/dist/tools/ls.d.ts +2 -2
  208. package/dist/tools/ls.d.ts.map +1 -1
  209. package/dist/tools/ls.js +7 -5
  210. package/dist/tools/read-file.d.ts +4 -6
  211. package/dist/tools/read-file.d.ts.map +1 -1
  212. package/dist/tools/read-file.js +84 -39
  213. package/dist/tools/save-file.d.ts +3 -3
  214. package/dist/tools/save-file.d.ts.map +1 -1
  215. package/dist/tools/save-file.js +36 -31
  216. package/dist/tools/skill.d.ts +23 -0
  217. package/dist/tools/skill.d.ts.map +1 -0
  218. package/dist/tools/skill.js +65 -0
  219. package/dist/tools/think.d.ts.map +1 -1
  220. package/dist/tools/think.js +2 -9
  221. package/dist/tools/utils.d.ts +2 -0
  222. package/dist/tools/utils.d.ts.map +1 -1
  223. package/dist/tools/utils.js +12 -0
  224. package/dist/tools/web-fetch.d.ts +50 -0
  225. package/dist/tools/web-fetch.d.ts.map +1 -0
  226. package/dist/tools/web-fetch.js +446 -0
  227. package/dist/tools/web-search.d.ts +44 -0
  228. package/dist/tools/web-search.d.ts.map +1 -0
  229. package/dist/tools/web-search.js +226 -0
  230. package/dist/tui/autocomplete/attachment-provider.d.ts +3 -6
  231. package/dist/tui/autocomplete/attachment-provider.d.ts.map +1 -1
  232. package/dist/tui/autocomplete/attachment-provider.js +25 -78
  233. package/dist/tui/autocomplete/base-provider.d.ts +1 -0
  234. package/dist/tui/autocomplete/base-provider.d.ts.map +1 -1
  235. package/dist/tui/autocomplete/combined-provider.d.ts +1 -4
  236. package/dist/tui/autocomplete/combined-provider.d.ts.map +1 -1
  237. package/dist/tui/autocomplete/combined-provider.js +3 -17
  238. package/dist/tui/autocomplete/command-provider.d.ts +1 -0
  239. package/dist/tui/autocomplete/command-provider.d.ts.map +1 -1
  240. package/dist/tui/autocomplete/command-provider.js +3 -0
  241. package/dist/tui/autocomplete/file-search-provider.d.ts +2 -1
  242. package/dist/tui/autocomplete/file-search-provider.d.ts.map +1 -1
  243. package/dist/tui/autocomplete/file-search-provider.js +37 -17
  244. package/dist/tui/autocomplete/skill-provider.d.ts +17 -0
  245. package/dist/tui/autocomplete/skill-provider.d.ts.map +1 -0
  246. package/dist/tui/autocomplete/skill-provider.js +49 -0
  247. package/dist/tui/autocomplete/utils.d.ts +2 -1
  248. package/dist/tui/autocomplete/utils.d.ts.map +1 -1
  249. package/dist/tui/autocomplete/utils.js +25 -23
  250. package/dist/tui/autocomplete.d.ts +2 -2
  251. package/dist/tui/autocomplete.d.ts.map +1 -1
  252. package/dist/tui/autocomplete.js +3 -5
  253. package/dist/tui/components/assistant-message.d.ts.map +1 -1
  254. package/dist/tui/components/assistant-message.js +0 -4
  255. package/dist/tui/components/editor.d.ts +18 -3
  256. package/dist/tui/components/editor.d.ts.map +1 -1
  257. package/dist/tui/components/editor.js +211 -237
  258. package/dist/tui/components/footer.d.ts +6 -4
  259. package/dist/tui/components/footer.d.ts.map +1 -1
  260. package/dist/tui/components/footer.js +49 -25
  261. package/dist/tui/components/markdown.d.ts +10 -7
  262. package/dist/tui/components/markdown.d.ts.map +1 -1
  263. package/dist/tui/components/markdown.js +57 -39
  264. package/dist/tui/components/modal.d.ts.map +1 -1
  265. package/dist/tui/components/modal.js +35 -33
  266. package/dist/tui/components/notification.d.ts +13 -2
  267. package/dist/tui/components/notification.d.ts.map +1 -1
  268. package/dist/tui/components/notification.js +36 -2
  269. package/dist/tui/components/progress-bar.js +1 -1
  270. package/dist/tui/components/select-list.d.ts +1 -0
  271. package/dist/tui/components/select-list.d.ts.map +1 -1
  272. package/dist/tui/components/select-list.js +14 -11
  273. package/dist/tui/components/text.d.ts +16 -0
  274. package/dist/tui/components/text.d.ts.map +1 -1
  275. package/dist/tui/components/text.js +72 -57
  276. package/dist/tui/components/thinking-block.d.ts +9 -0
  277. package/dist/tui/components/thinking-block.d.ts.map +1 -1
  278. package/dist/tui/components/thinking-block.js +43 -11
  279. package/dist/tui/components/tool-execution.d.ts +5 -1
  280. package/dist/tui/components/tool-execution.d.ts.map +1 -1
  281. package/dist/tui/components/tool-execution.js +19 -10
  282. package/dist/tui/components/user-message.d.ts.map +1 -1
  283. package/dist/tui/components/user-message.js +0 -3
  284. package/dist/tui/components/welcome.d.ts +2 -1
  285. package/dist/tui/components/welcome.d.ts.map +1 -1
  286. package/dist/tui/components/welcome.js +2 -2
  287. package/dist/tui/editor-launcher.d.ts +3 -2
  288. package/dist/tui/editor-launcher.d.ts.map +1 -1
  289. package/dist/tui/index.d.ts +0 -1
  290. package/dist/tui/index.d.ts.map +1 -1
  291. package/dist/tui/terminal.d.ts.map +1 -1
  292. package/dist/tui/terminal.js +10 -2
  293. package/dist/tui/tui.d.ts +43 -0
  294. package/dist/tui/tui.d.ts.map +1 -1
  295. package/dist/tui/tui.js +166 -41
  296. package/dist/tui/utils.d.ts +1 -5
  297. package/dist/tui/utils.d.ts.map +1 -1
  298. package/dist/tui/utils.js +271 -44
  299. package/dist/utils/bash/parse.d.ts +19 -0
  300. package/dist/utils/bash/parse.d.ts.map +1 -0
  301. package/dist/utils/bash/parse.js +223 -0
  302. package/dist/utils/bash/quote.d.ts +6 -0
  303. package/dist/utils/bash/quote.d.ts.map +1 -0
  304. package/dist/utils/bash/quote.js +23 -0
  305. package/dist/utils/bash.d.ts.map +1 -1
  306. package/dist/utils/bash.js +211 -126
  307. package/dist/utils/command-protection.d.ts +28 -0
  308. package/dist/utils/command-protection.d.ts.map +1 -0
  309. package/dist/utils/command-protection.js +324 -0
  310. package/dist/utils/dedent.d.ts.map +1 -0
  311. package/dist/utils/env-expand.d.ts +2 -0
  312. package/dist/utils/env-expand.d.ts.map +1 -0
  313. package/dist/utils/env-expand.js +8 -0
  314. package/dist/utils/filesystem/path-display.d.ts +11 -0
  315. package/dist/utils/filesystem/path-display.d.ts.map +1 -0
  316. package/dist/utils/filesystem/path-display.js +32 -0
  317. package/dist/utils/filesystem/security.d.ts +2 -2
  318. package/dist/utils/filesystem/security.d.ts.map +1 -1
  319. package/dist/utils/filesystem/security.js +28 -30
  320. package/dist/utils/formatting.d.ts.map +1 -0
  321. package/dist/{formatting.js → utils/formatting.js} +1 -1
  322. package/dist/utils/git.d.ts +4 -0
  323. package/dist/utils/git.d.ts.map +1 -1
  324. package/dist/utils/git.js +30 -0
  325. package/dist/utils/glob.d.ts +1 -1
  326. package/dist/utils/glob.d.ts.map +1 -1
  327. package/dist/utils/logger.d.ts.map +1 -0
  328. package/dist/{logger.js → utils/logger.js} +1 -1
  329. package/dist/utils/parsing.d.ts.map +1 -0
  330. package/dist/utils/process.d.ts.map +1 -1
  331. package/dist/utils/process.js +90 -37
  332. package/dist/utils/templates.d.ts +2 -0
  333. package/dist/utils/templates.d.ts.map +1 -0
  334. package/dist/utils/templates.js +24 -0
  335. package/dist/utils/version.d.ts.map +1 -0
  336. package/dist/{version.js → utils/version.js} +1 -1
  337. package/package.json +35 -26
  338. package/dist/cli.d.ts +0 -23
  339. package/dist/cli.d.ts.map +0 -1
  340. package/dist/commands/add-directory/types.d.ts +0 -6
  341. package/dist/commands/add-directory/types.d.ts.map +0 -1
  342. package/dist/commands/add-directory/types.js +0 -1
  343. package/dist/commands/copy/types.d.ts +0 -3
  344. package/dist/commands/copy/types.d.ts.map +0 -1
  345. package/dist/commands/copy/types.js +0 -1
  346. package/dist/commands/exit/index.d.ts +0 -10
  347. package/dist/commands/exit/index.d.ts.map +0 -1
  348. package/dist/commands/exit/index.js +0 -21
  349. package/dist/commands/exit/types.d.ts +0 -8
  350. package/dist/commands/exit/types.d.ts.map +0 -1
  351. package/dist/commands/exit/types.js +0 -1
  352. package/dist/commands/exit/utils.d.ts +0 -2
  353. package/dist/commands/exit/utils.d.ts.map +0 -1
  354. package/dist/commands/exit/utils.js +0 -13
  355. package/dist/commands/prompt/index.d.ts +0 -5
  356. package/dist/commands/prompt/index.d.ts.map +0 -1
  357. package/dist/commands/prompt/index.js +0 -122
  358. package/dist/commands/prompt/types.d.ts +0 -15
  359. package/dist/commands/prompt/types.d.ts.map +0 -1
  360. package/dist/commands/prompt/types.js +0 -1
  361. package/dist/commands/prompt/utils.d.ts +0 -12
  362. package/dist/commands/prompt/utils.d.ts.map +0 -1
  363. package/dist/commands/prompt/utils.js +0 -107
  364. package/dist/commands/reset/index.d.ts +0 -3
  365. package/dist/commands/reset/index.d.ts.map +0 -1
  366. package/dist/commands/reset/index.js +0 -25
  367. package/dist/commands/reset/types.d.ts +0 -1
  368. package/dist/commands/reset/types.d.ts.map +0 -1
  369. package/dist/commands/reset/types.js +0 -3
  370. package/dist/commands/review/types.d.ts +0 -12
  371. package/dist/commands/review/types.d.ts.map +0 -1
  372. package/dist/commands/review/types.js +0 -1
  373. package/dist/commands/save/index.d.ts +0 -3
  374. package/dist/commands/save/index.d.ts.map +0 -1
  375. package/dist/commands/save/index.js +0 -19
  376. package/dist/config.d.ts.map +0 -1
  377. package/dist/dedent.d.ts.map +0 -1
  378. package/dist/formatting.d.ts.map +0 -1
  379. package/dist/logger.d.ts.map +0 -1
  380. package/dist/mentions.d.ts +0 -14
  381. package/dist/mentions.d.ts.map +0 -1
  382. package/dist/parsing.d.ts.map +0 -1
  383. package/dist/prompts.d.ts.map +0 -1
  384. package/dist/repl-new.d.ts +0 -65
  385. package/dist/repl-new.d.ts.map +0 -1
  386. package/dist/skills.d.ts +0 -16
  387. package/dist/skills.d.ts.map +0 -1
  388. package/dist/skills.js +0 -233
  389. package/dist/stdin.d.ts.map +0 -1
  390. package/dist/tui/autocomplete/path-provider.d.ts +0 -21
  391. package/dist/tui/autocomplete/path-provider.d.ts.map +0 -1
  392. package/dist/tui/autocomplete/path-provider.js +0 -164
  393. package/dist/utils/iterables.d.ts +0 -2
  394. package/dist/utils/iterables.d.ts.map +0 -1
  395. package/dist/utils/iterables.js +0 -6
  396. package/dist/version.d.ts.map +0 -1
  397. /package/dist/{dedent.d.ts → utils/dedent.d.ts} +0 -0
  398. /package/dist/{dedent.js → utils/dedent.js} +0 -0
  399. /package/dist/{formatting.d.ts → utils/formatting.d.ts} +0 -0
  400. /package/dist/{logger.d.ts → utils/logger.d.ts} +0 -0
  401. /package/dist/{parsing.d.ts → utils/parsing.d.ts} +0 -0
  402. /package/dist/{parsing.js → utils/parsing.js} +0 -0
  403. /package/dist/{version.d.ts → utils/version.d.ts} +0 -0
@@ -1,16 +1,28 @@
1
- import { logger } from "./logger.js";
2
- import { PromptError, processPrompt } from "./mentions.js";
3
- import { getProjectStatus, } from "./repl/project-status.js";
4
- import { alert, startProgress, stopProgress } from "./terminal/control.js";
5
- import style from "./terminal/style.js";
6
- import { AssistantMessageComponent } from "./tui/components/assistant-message.js";
7
- import { FooterComponent } from "./tui/components/footer.js";
8
- import { ThinkingBlockComponent } from "./tui/components/thinking-block.js";
9
- import { ToolExecutionComponent } from "./tui/components/tool-execution.js";
10
- import { Welcome } from "./tui/components/welcome.js";
11
- import { launchEditor } from "./tui/editor-launcher.js";
12
- import { Container, Editor, Loader, NotificationComponent, ProcessTerminal, Spacer, Text, TUI, UserMessageComponent, } from "./tui/index.js";
13
- export class NewRepl {
1
+ import { generateRulesFromSession } from "../commands/generate-rules/service.js";
2
+ import { showModelSelector } from "../commands/model/model-panel.js";
3
+ import { showReviewPanel } from "../commands/review/review-panel.js";
4
+ import { ModeManager } from "../modes/manager.js";
5
+ import { processPrompt } from "../prompts/mentions.js";
6
+ import { createUserMessage } from "../sessions/manager.js";
7
+ import { loadSkills } from "../skills/index.js";
8
+ import { alert, setTerminalTitle, startProgress, stopProgress, } from "../terminal/control.js";
9
+ import style from "../terminal/style.js";
10
+ import { AttachmentProvider, CombinedProvider, CommandProvider, FileSearchProvider, SkillProvider, } from "../tui/autocomplete.js";
11
+ import { AssistantMessageComponent } from "../tui/components/assistant-message.js";
12
+ import { FooterComponent } from "../tui/components/footer.js";
13
+ import { ThinkingBlockComponent } from "../tui/components/thinking-block.js";
14
+ import { ToolExecutionComponent } from "../tui/components/tool-execution.js";
15
+ import { Welcome } from "../tui/components/welcome.js";
16
+ import { launchEditor } from "../tui/editor-launcher.js";
17
+ import { Container, Editor, Loader, NotificationComponent, ProcessTerminal, Spacer, Text, TUI, UserMessageComponent, } from "../tui/index.js";
18
+ import { logger } from "../utils/logger.js";
19
+ import { getProjectStatus } from "./project-status.js";
20
+ /**
21
+ * Interactive Read-Eval-Print Loop that provides the primary user interface
22
+ * for the AI assistant CLI. Manages the TUI layout, handles keyboard shortcuts,
23
+ * processes agent events for streaming responses, and coordinates session lifecycle.
24
+ */
25
+ export class Repl {
14
26
  options;
15
27
  terminal;
16
28
  tui;
@@ -34,6 +46,16 @@ export class NewRepl {
34
46
  streamingComponent = null;
35
47
  // thinking block tracking
36
48
  thinkingBlockComponent = null;
49
+ // Track all verbose-aware components for re-rendering on toggle
50
+ allThinkingBlocks = [];
51
+ allToolExecutions = [];
52
+ // verbose mode state
53
+ verboseMode = false;
54
+ // mode manager
55
+ modeManager;
56
+ // ProjectConfig - initialized in init()
57
+ config;
58
+ /** Creates a new Repl instance, initializing the TUI layout and components. */
37
59
  constructor(options) {
38
60
  this.options = options;
39
61
  this.terminal = new ProcessTerminal(options.terminalOptions);
@@ -45,7 +67,7 @@ export class NewRepl {
45
67
  this.chatContainer = new Container();
46
68
  this.statusContainer = new Container();
47
69
  this.editorContainer = new Container(); // Container to hold editor or selector
48
- this.footer = new FooterComponent(options.modelManager, {
70
+ this.footer = new FooterComponent(options.modelManager, options.tokenTracker, {
49
71
  projectStatus: {
50
72
  path: "",
51
73
  isGitRepository: false,
@@ -55,7 +77,6 @@ export class NewRepl {
55
77
  },
56
78
  currentContextWindow: 0,
57
79
  contextWindow: options.modelManager.getModelMetadata("repl").contextWindow,
58
- usage: this.options.tokenTracker.getUsageByApp("repl"),
59
80
  });
60
81
  this.editorContainer.addChild(this.editor); // Start with editor
61
82
  this.editor.onRenderRequested = () => this.tui.requestRender();
@@ -69,17 +90,30 @@ export class NewRepl {
69
90
  this.isInitialized = false;
70
91
  this.pendingTools = new Map();
71
92
  this.tools = options.tools;
72
- this.notification = new NotificationComponent("", { r: 64, g: 64, b: 64 }, style.yellow, 1);
93
+ this.modeManager = new ModeManager();
94
+ this.notification = new NotificationComponent("", { r: 52, g: 53, b: 65 }, style.yellow, 1, () => this.tui.requestRender());
73
95
  }
96
+ /**
97
+ * Initializes the REPL by setting up autocomplete,
98
+ * keyboard handlers, and the TUI component tree.
99
+ */
74
100
  async init() {
75
101
  if (this.isInitialized) {
102
+ this.notification.setMessage("initialized");
76
103
  return;
77
104
  }
78
- // Setup autocomplete for file paths and slash commands
79
- const { createDefaultProvider } = await import("./tui/autocomplete.js");
80
- const autocompleteProvider = createDefaultProvider([...(await this.options.commands.getCompletions())], this.options.workspace.allowedDirs);
105
+ // Setup autocomplete for file paths, slash commands, and skills
106
+ const skills = await loadSkills();
107
+ const commandsList = await this.options.commands.getCompletions();
108
+ const autocompleteProvider = new CombinedProvider([
109
+ new CommandProvider(commandsList),
110
+ new AttachmentProvider(),
111
+ new FileSearchProvider(),
112
+ new SkillProvider(skills.getModelInvocable()),
113
+ ]);
81
114
  this.editor.setAutocompleteProvider(autocompleteProvider);
82
- const { promptManager, modelManager, messageHistory, commands, promptHistory, } = this.options;
115
+ const { promptManager, modelManager, sessionManager, commands, promptHistory, configManager, } = this.options;
116
+ this.config = await configManager.getConfig();
83
117
  // Listen for session title updates
84
118
  // messageHistory.on("update-title", (title: string) => {
85
119
  // this.footer.setTitle(title);
@@ -90,16 +124,45 @@ export class NewRepl {
90
124
  projectStatus: await getProjectStatus(),
91
125
  currentContextWindow: 0,
92
126
  contextWindow: modelConfig.contextWindow,
93
- usage: this.options.tokenTracker.getUsageByApp("repl"),
127
+ currentMode: this.modeManager.getDisplayName(),
94
128
  });
95
129
  this.tui.onCtrlC = () => {
96
130
  this.handleCtrlC();
97
131
  };
132
+ this.tui.onCtrlD = () => {
133
+ this.handleCtrlD();
134
+ };
135
+ this.tui.onCtrlO = () => {
136
+ this.handleCtrlO();
137
+ };
138
+ this.tui.onCtrlN = () => {
139
+ void this.handleCtrlN();
140
+ };
141
+ this.tui.onCtrlR = () => {
142
+ void showReviewPanel(this.tui, this.chatContainer, this.editorContainer, this.editor);
143
+ };
144
+ this.tui.onCtrlM = () => {
145
+ void this.handleCtrlM();
146
+ };
147
+ this.tui.onShiftTab = () => {
148
+ this.modeManager.cycleMode();
149
+ this.footer.setState({
150
+ projectStatus: this.footer.getProjectStatus(),
151
+ currentContextWindow: this.options.sessionManager.getLastTurnContextWindow(),
152
+ contextWindow: this.options.modelManager.getModelMetadata("repl").contextWindow,
153
+ currentMode: this.modeManager.getDisplayName(),
154
+ });
155
+ this.tui.requestRender();
156
+ };
157
+ // Set callback for session reconstruction (used by /history command)
158
+ this.tui.onReconstructSession = () => this.rerender();
98
159
  this.tui.addChild(this.welcome);
99
160
  // Initialize footer with current title if one exists
100
161
  // this.footer.setTitle(messageHistory.getTitle());
101
162
  this.tui.addChild(this.chatContainer);
102
163
  this.tui.addChild(this.statusContainer);
164
+ // Everything below here is fixed to the bottom of the terminal
165
+ this.tui.setFixedFooterStart();
103
166
  this.tui.addChild(new Spacer(1));
104
167
  this.tui.addChild(this.editorContainer); // Use container that can hold editor or selector
105
168
  this.tui.addChild(this.footer);
@@ -122,38 +185,20 @@ export class NewRepl {
122
185
  inputContainer: this.editorContainer,
123
186
  editor: this.editor,
124
187
  });
125
- if (commandResult.break) {
126
- this.stop(true);
127
- process.exit(0);
128
- }
129
188
  if (commandResult.continue) {
130
189
  this.editor.setText("");
131
190
  this.tui.requestRender();
132
191
  return;
133
192
  }
134
193
  if (!promptManager.isPending()) {
135
- try {
136
- const processedPrompt = await processPrompt(text, {
137
- baseDir: process.cwd(),
138
- model: modelConfig,
139
- });
140
- for (const context of processedPrompt.context) {
141
- promptManager.addContext(context);
142
- }
143
- promptManager.set(processedPrompt.message);
144
- }
145
- catch (error) {
146
- if (error instanceof PromptError) {
147
- this.chatContainer.addChild(new Text(style.red(`Prompt processing failed: ${error.message}`), 1, 1));
148
- if (error.cause &&
149
- typeof error.cause === "object" &&
150
- "command" in error.cause &&
151
- typeof error.cause.command === "string") {
152
- this.chatContainer.addChild(new Text(style.red(`Command: ${error.cause.command}`, 1, 1)));
153
- }
154
- }
155
- throw error; // Re-throw other errors
194
+ const processedPrompt = await processPrompt(text, {
195
+ baseDir: process.cwd(),
196
+ model: modelConfig,
197
+ });
198
+ for (const context of processedPrompt.context) {
199
+ promptManager.addContext(context);
156
200
  }
201
+ promptManager.set(processedPrompt.message);
157
202
  }
158
203
  else {
159
204
  promptHistory.push(promptManager.get());
@@ -163,11 +208,34 @@ export class NewRepl {
163
208
  const hasAddedContext = promptManager.hasContext();
164
209
  if (hasAddedContext) {
165
210
  const contextTokenCount = promptManager.getContextTokenCount();
166
- this.chatContainer.addChild(new Text(style.green(`Context will be added to prompt. (${contextTokenCount} tokens)`)));
211
+ this.addComponentWithSpacing(new Text(style.green(`Context will be added to prompt. (${contextTokenCount} tokens)`)));
167
212
  }
168
213
  const userPrompt = promptManager.get();
169
214
  const userMsg = promptManager.getUserMessage();
170
- messageHistory.appendUserMessage(userMsg);
215
+ if (!this.modeManager.isNormal()) {
216
+ if (this.modeManager.isFirstMessage()) {
217
+ const initialPrompt = this.modeManager.getInitialPrompt();
218
+ if (initialPrompt) {
219
+ const modeMessage = createUserMessage([], initialPrompt);
220
+ sessionManager.appendUserMessage(modeMessage);
221
+ }
222
+ sessionManager.appendUserMessage(userMsg);
223
+ this.modeManager.markFirstMessageSent();
224
+ }
225
+ else {
226
+ sessionManager.appendUserMessage(userMsg);
227
+ const reminderMessage = this.modeManager.getReminderMessage();
228
+ if (reminderMessage) {
229
+ sessionManager.setTransientMessages([reminderMessage]);
230
+ }
231
+ }
232
+ }
233
+ else {
234
+ sessionManager.appendUserMessage(userMsg);
235
+ }
236
+ this.addMessageToChat({ role: "user", content: userPrompt });
237
+ this.editor.setText("");
238
+ this.tui.requestRender();
171
239
  if (this.onInputCallback) {
172
240
  this.onInputCallback(userPrompt);
173
241
  }
@@ -177,18 +245,25 @@ export class NewRepl {
177
245
  this.tui.start();
178
246
  this.isInitialized = true;
179
247
  }
248
+ /**
249
+ * Handles an agent event by updating the TUI accordingly.
250
+ * Processes events such as streaming messages, tool calls, thinking blocks,
251
+ * and agent lifecycle transitions (start, stop, error).
252
+ */
180
253
  async handle(event, state) {
181
254
  if (!this.isInitialized) {
182
255
  await this.init();
183
256
  }
184
257
  // Update footer with current stats
185
258
  // this.footer.updateState(state);
259
+ // Use cached project status for all events to avoid blocking on git
260
+ // subprocess calls; refresh asynchronously at agent-stop
186
261
  this.footer.setState({
187
- projectStatus: await getProjectStatus(),
188
- currentContextWindow: this.options.messageHistory.getContextWindow(),
262
+ projectStatus: this.footer.getProjectStatus(),
263
+ currentContextWindow: this.options.sessionManager.getLastTurnContextWindow(),
189
264
  contextWindow: this.options.modelManager.getModelMetadata("repl").contextWindow,
190
265
  agentState: state,
191
- usage: this.options.tokenTracker.getUsageByApp("repl"),
266
+ currentMode: this.modeManager.getDisplayName(),
192
267
  });
193
268
  const eventType = event.type;
194
269
  switch (eventType) {
@@ -219,19 +294,13 @@ export class NewRepl {
219
294
  // Create assistant component for streaming
220
295
  const assistantMessageComponent = new AssistantMessageComponent();
221
296
  this.streamingComponent = assistantMessageComponent;
222
- this.chatContainer.addChild(assistantMessageComponent);
297
+ this.addComponentWithSpacing(assistantMessageComponent);
223
298
  this.streamingComponent.updateContent(event);
224
299
  this.tui.requestRender();
225
300
  }
226
301
  break;
227
302
  case "message":
228
- if (event.role === "user") {
229
- // Show user message immediately and clear editor
230
- this.addMessageToChat(event);
231
- this.editor.setText("");
232
- this.tui.requestRender();
233
- }
234
- else if (event.role === "assistant") {
303
+ if (event.role === "assistant") {
235
304
  // Update streaming component
236
305
  if (this.streamingComponent && event.role === "assistant") {
237
306
  this.streamingComponent.updateContent(event);
@@ -253,9 +322,12 @@ export class NewRepl {
253
322
  }
254
323
  else {
255
324
  // Create tool component for new tool call
256
- const newComponent = new ToolExecutionComponent(event.events);
325
+ const newComponent = new ToolExecutionComponent(event.events, {
326
+ verboseMode: this.verboseMode,
327
+ });
257
328
  this.pendingTools.set(event.toolCallId, newComponent);
258
- this.chatContainer.addChild(newComponent);
329
+ this.allToolExecutions.push(newComponent);
330
+ this.addComponentWithSpacing(newComponent);
259
331
  }
260
332
  this.tui.requestRender();
261
333
  break;
@@ -277,10 +349,30 @@ export class NewRepl {
277
349
  }
278
350
  this.pendingTools.clear();
279
351
  this.editor.disableSubmit = false;
352
+ this.options.sessionManager.clearTransientMessages();
353
+ this.options.sessionManager.setMetadata("modeState", this.modeManager.toJson());
354
+ if (!this.options.noSession) {
355
+ await this.options.sessionManager.save();
356
+ }
357
+ // Refresh project status now that agent may have modified files
358
+ await getProjectStatus().then((ps) => {
359
+ this.footer.setState({
360
+ projectStatus: ps,
361
+ currentContextWindow: this.options.sessionManager.getLastTurnContextWindow(),
362
+ contextWindow: this.options.modelManager.getModelMetadata("repl").contextWindow,
363
+ currentMode: this.modeManager.getDisplayName(),
364
+ });
365
+ this.tui.requestRender();
366
+ });
280
367
  this.tui.requestRender();
281
368
  break;
282
369
  case "agent-error":
283
370
  logger.error(event, "agent-error");
371
+ this.options.sessionManager.clearTransientMessages();
372
+ this.options.sessionManager.setMetadata("modeState", this.modeManager.toJson());
373
+ if (!this.options.noSession) {
374
+ await this.options.sessionManager.save();
375
+ }
284
376
  // Stop loading animation
285
377
  if (this.loadingAnimation) {
286
378
  this.loadingAnimation.stop();
@@ -296,9 +388,20 @@ export class NewRepl {
296
388
  this.tui.requestRender();
297
389
  break;
298
390
  case "thinking-start": {
299
- const component = new ThinkingBlockComponent();
391
+ const component = new ThinkingBlockComponent(undefined, {
392
+ verboseMode: this.verboseMode,
393
+ });
300
394
  this.thinkingBlockComponent = component;
301
- this.chatContainer.addChild(component);
395
+ this.allThinkingBlocks.push(component);
396
+ // If a streaming message component already exists (text came before thinking),
397
+ // insert the thinking block BEFORE it to ensure correct visual order
398
+ if (this.streamingComponent) {
399
+ this.chatContainer.insertChildBefore(this.streamingComponent, component);
400
+ this.chatContainer.insertChildBefore(this.streamingComponent, new Spacer(1));
401
+ }
402
+ else {
403
+ this.addComponentWithSpacing(component);
404
+ }
302
405
  this.thinkingBlockComponent.updateContent(event);
303
406
  this.tui.requestRender();
304
407
  break;
@@ -311,7 +414,7 @@ export class NewRepl {
311
414
  break;
312
415
  case "thinking-end":
313
416
  if (this.thinkingBlockComponent) {
314
- this.thinkingBlockComponent.updateContent(event);
417
+ this.thinkingBlockComponent.endThinking();
315
418
  this.thinkingBlockComponent = null;
316
419
  this.tui.requestRender();
317
420
  }
@@ -320,16 +423,26 @@ export class NewRepl {
320
423
  eventType;
321
424
  }
322
425
  }
426
+ /** Adds a user message component to the chat container. */
323
427
  addMessageToChat(message) {
324
428
  if (message.role === "user") {
325
429
  // Extract text content from content blocks
326
430
  const textContent = message.content;
327
431
  if (textContent) {
328
432
  const userComponent = new UserMessageComponent(textContent);
329
- this.chatContainer.addChild(userComponent);
433
+ this.addComponentWithSpacing(userComponent);
330
434
  }
331
435
  }
332
436
  }
437
+ /**
438
+ * Adds a component to the chat container with spacing.
439
+ * Adds a Spacer(1) before every component (including the first).
440
+ */
441
+ addComponentWithSpacing(component) {
442
+ this.chatContainer.addChild(new Spacer(1));
443
+ this.chatContainer.addChild(component);
444
+ }
445
+ /** Returns a promise that resolves with the user's next input submission. */
333
446
  async getUserInput() {
334
447
  return new Promise((resolve) => {
335
448
  this.onInputCallback = (text) => {
@@ -338,33 +451,75 @@ export class NewRepl {
338
451
  };
339
452
  });
340
453
  }
454
+ /** Clears the editor input and triggers a re-render. */
341
455
  clearEditor() {
342
456
  this.editor.setText("");
343
457
  this.tui.requestRender();
344
458
  }
459
+ /**
460
+ * Sets the callback invoked when the user presses
461
+ * Escape to interrupt the agent.
462
+ */
345
463
  setInterruptCallback(callback) {
346
464
  this.onInterruptCallback = callback;
347
465
  }
466
+ /** Sets the callback invoked on exit, receiving the current session ID. */
348
467
  setExitCallback(callback) {
349
468
  this.onExitCallback = callback;
350
469
  }
470
+ /**
471
+ * Re-renders the entire session by restoring mode state,
472
+ * replaying token usage, and reconstructing the chat
473
+ * display.
474
+ */
351
475
  async rerender() {
476
+ const modeState = this.options.sessionManager.getMetadata("modeState");
477
+ if (modeState && typeof modeState === "object" && "mode" in modeState) {
478
+ this.modeManager.fromJson(modeState);
479
+ }
480
+ // When resuming a session, populate tokenTracker with historical usage
481
+ // so the footer displays the correct total session usage
482
+ const totalUsage = this.options.sessionManager.getTotalTokenUsage();
483
+ if (totalUsage.inputTokens > 0 || totalUsage.outputTokens > 0) {
484
+ this.options.tokenTracker.trackUsage("repl", {
485
+ inputTokens: totalUsage.inputTokens,
486
+ outputTokens: totalUsage.outputTokens,
487
+ totalTokens: totalUsage.totalTokens,
488
+ inputTokenDetails: {
489
+ noCacheTokens: totalUsage.inputTokens - totalUsage.cachedInputTokens,
490
+ cacheReadTokens: totalUsage.cachedInputTokens,
491
+ cacheWriteTokens: 0,
492
+ },
493
+ outputTokenDetails: {
494
+ textTokens: totalUsage.outputTokens,
495
+ reasoningTokens: totalUsage.reasoningTokens,
496
+ },
497
+ });
498
+ }
352
499
  this.footer.setState({
353
500
  projectStatus: await getProjectStatus(),
354
- currentContextWindow: this.options.messageHistory.getContextWindow(),
501
+ currentContextWindow: this.options.sessionManager.getLastTurnContextWindow(),
355
502
  contextWindow: this.options.modelManager.getModelMetadata("repl").contextWindow,
356
- usage: this.options.tokenTracker.getUsageByApp("repl"),
503
+ currentMode: this.modeManager.getDisplayName(),
357
504
  });
358
505
  // Reconstruct entire session display from messages
359
506
  this.reconstructSession();
507
+ this.tui.scrollToBottom();
360
508
  this.tui.requestRender();
361
509
  }
510
+ /**
511
+ * Rebuilds the chat container from the full session
512
+ * message history, including user messages, assistant
513
+ * responses, and tool executions.
514
+ */
362
515
  reconstructSession() {
363
516
  // Clear existing display
364
517
  this.pendingTools.clear();
518
+ this.allThinkingBlocks = [];
519
+ this.allToolExecutions = [];
365
520
  this.chatContainer.clear();
366
521
  // Get session messages
367
- const messages = this.options.messageHistory.get();
522
+ const messages = this.options.sessionManager.get();
368
523
  // First pass: collect all tool results
369
524
  const toolResults = new Map();
370
525
  for (const message of messages) {
@@ -395,7 +550,7 @@ export class NewRepl {
395
550
  const textContent = this.extractUserMessageText(message);
396
551
  if (textContent) {
397
552
  const userComponent = new UserMessageComponent(textContent);
398
- this.chatContainer.addChild(userComponent);
553
+ this.addComponentWithSpacing(userComponent);
399
554
  }
400
555
  }
401
556
  else if (message.role === "assistant") {
@@ -407,15 +562,22 @@ export class NewRepl {
407
562
  const toolCallId = toolCallContent.toolCallId;
408
563
  const events = this.createToolEvents(toolCallContent);
409
564
  if (events.length > 0) {
410
- const component = new ToolExecutionComponent(events);
565
+ const component = new ToolExecutionComponent(events, {
566
+ verboseMode: this.verboseMode,
567
+ });
411
568
  this.pendingTools.set(toolCallId, component);
412
- this.chatContainer.addChild(component);
569
+ this.allToolExecutions.push(component);
570
+ this.addComponentWithSpacing(component);
413
571
  }
414
572
  }
415
573
  }
416
574
  // Tool messages are handled through their associated assistant message
417
575
  }
418
576
  }
577
+ /**
578
+ * Extracts the text content from a user message,
579
+ * handling both string and array content formats.
580
+ */
419
581
  extractUserMessageText(message) {
420
582
  if (typeof message.content === "string") {
421
583
  return message.content;
@@ -428,6 +590,10 @@ export class NewRepl {
428
590
  }
429
591
  return null;
430
592
  }
593
+ /**
594
+ * Renders an assistant message into the chat container,
595
+ * including reasoning/thinking blocks and text content.
596
+ */
431
597
  renderAssistantMessage(message) {
432
598
  if (typeof message.content === "string") {
433
599
  if (message.content.trim()) {
@@ -437,10 +603,25 @@ export class NewRepl {
437
603
  role: "assistant",
438
604
  content: message.content,
439
605
  });
440
- this.chatContainer.addChild(assistantComponent);
606
+ this.addComponentWithSpacing(assistantComponent);
441
607
  }
442
608
  }
443
609
  else if (Array.isArray(message.content)) {
610
+ const reasoningParts = message.content
611
+ .filter((part) => part.type === "reasoning" &&
612
+ typeof part.text === "string" &&
613
+ part.text.trim().length > 0)
614
+ .map((part) => part.text)
615
+ .join("\n");
616
+ if (reasoningParts.trim()) {
617
+ const thinkingComponent = new ThinkingBlockComponent(undefined, {
618
+ verboseMode: this.verboseMode,
619
+ });
620
+ thinkingComponent.updateContent({ content: reasoningParts });
621
+ thinkingComponent.endThinking();
622
+ this.allThinkingBlocks.push(thinkingComponent);
623
+ this.addComponentWithSpacing(thinkingComponent);
624
+ }
444
625
  const textParts = message.content
445
626
  .filter((part) => part.type === "text" && part.text?.trim() !== undefined)
446
627
  .map((part) => part.text)
@@ -452,10 +633,15 @@ export class NewRepl {
452
633
  role: "assistant",
453
634
  content: textParts,
454
635
  });
455
- this.chatContainer.addChild(assistantComponent);
636
+ this.addComponentWithSpacing(assistantComponent);
456
637
  }
457
638
  }
458
639
  }
640
+ /**
641
+ * Extracts tool call content parts from an assistant
642
+ * message, matching them with their corresponding
643
+ * tool results.
644
+ */
459
645
  extractToolCallsFromAssistant(message, toolResults) {
460
646
  const toolCallContents = [];
461
647
  if (typeof message.content === "string") {
@@ -480,6 +666,11 @@ export class NewRepl {
480
666
  }
481
667
  return toolCallContents;
482
668
  }
669
+ /**
670
+ * Creates start and end/error ToolEvent pairs from a
671
+ * resolved tool call, using the tool's display function
672
+ * when available.
673
+ */
483
674
  createToolEvents(toolCallContent) {
484
675
  const events = [];
485
676
  // tool-call-start: use the tool's display function
@@ -508,6 +699,88 @@ export class NewRepl {
508
699
  });
509
700
  return events;
510
701
  }
702
+ /**
703
+ * Toggles verbose mode on/off, updating all thinking
704
+ * block and tool execution components.
705
+ */
706
+ handleCtrlO() {
707
+ this.verboseMode = !this.verboseMode;
708
+ const modeText = this.verboseMode ? "ON" : "OFF";
709
+ this.notification.setMessage(`Verbose mode: ${modeText}`);
710
+ // Update all verbose-aware components to reflect new verbose mode
711
+ for (const component of this.allThinkingBlocks) {
712
+ component.setVerboseMode(this.verboseMode);
713
+ }
714
+ for (const component of this.allToolExecutions) {
715
+ component.setVerboseMode(this.verboseMode);
716
+ }
717
+ this.tui.requestRender();
718
+ }
719
+ /**
720
+ * Starts a new session by saving the current one,
721
+ * resetting mode/tokens/UI, and clearing the chat.
722
+ */
723
+ async handleCtrlN() {
724
+ if (!this.options.sessionManager.isEmpty()) {
725
+ // Auto-generate rules before starting new session if enabled
726
+ // Run in background - don't block new session from starting
727
+ this.maybeGenerateRules().catch((err) => logger.debug({ err }, "Background rule generation failed"));
728
+ this.options.sessionManager.setMetadata("modeState", this.modeManager.toJson());
729
+ if (!this.options.noSession) {
730
+ await this.options.sessionManager.save();
731
+ }
732
+ this.options.sessionManager.create(this.options.modelManager.getModel("repl").modelId);
733
+ }
734
+ this.config = await this.options.configManager.getConfig();
735
+ this.modeManager.reset();
736
+ this.options.sessionManager.clearTransientMessages();
737
+ this.options.tokenTracker.reset();
738
+ setTerminalTitle(`acai: ${process.cwd()}`);
739
+ this.chatContainer.clear();
740
+ this.editor.setText("");
741
+ // Reset footer state to clear usage/cost/steps/tools/time
742
+ const footer = this.tui.children.find((child) => child.constructor.name === "FooterComponent");
743
+ if (footer) {
744
+ footer.resetState();
745
+ }
746
+ this.footer.setState({
747
+ projectStatus: this.footer.getProjectStatus(),
748
+ currentContextWindow: 0,
749
+ contextWindow: this.options.modelManager.getModelMetadata("repl").contextWindow,
750
+ currentMode: this.modeManager.getDisplayName(),
751
+ });
752
+ this.tui.requestRender();
753
+ }
754
+ handleCtrlM() {
755
+ showModelSelector(this.tui, this.editorContainer, this.editor, this.options.modelManager);
756
+ }
757
+ /** Handles Ctrl+D to exit the REPL when the editor is empty. */
758
+ handleCtrlD() {
759
+ // Only exit if the editor is empty
760
+ if (this.editor.getText().trim() !== "") {
761
+ // Editor has content, do nothing
762
+ return;
763
+ }
764
+ // Editor is empty - proceed with exit
765
+ // Clear any pending notification timer
766
+ if (this.exitNotificationTimer) {
767
+ clearTimeout(this.exitNotificationTimer);
768
+ this.exitNotificationTimer = undefined;
769
+ }
770
+ this.notification.setMessage("");
771
+ this.tui.requestRender();
772
+ this.options.sessionManager.setMetadata("modeState", this.modeManager.toJson());
773
+ if (!this.options.noSession) {
774
+ void this.options.sessionManager.save();
775
+ }
776
+ this.stop(true);
777
+ process.exit(0);
778
+ }
779
+ /**
780
+ * Handles Ctrl+C with double-press-to-exit logic.
781
+ * First press clears the editor; second press within
782
+ * 1 second exits.
783
+ */
511
784
  handleCtrlC() {
512
785
  // Handle Ctrl+C double-press logic
513
786
  const now = Date.now();
@@ -522,14 +795,17 @@ export class NewRepl {
522
795
  }
523
796
  this.notification.setMessage("");
524
797
  this.tui.requestRender();
525
- void this.options.messageHistory.save();
798
+ this.options.sessionManager.setMetadata("modeState", this.modeManager.toJson());
799
+ if (!this.options.noSession) {
800
+ void this.options.sessionManager.save();
801
+ }
526
802
  this.stop(true);
527
803
  process.exit(0);
528
804
  }
529
805
  else {
530
806
  // First Ctrl+C - clear the editor and show notification
531
807
  this.clearEditor();
532
- this.notification.setMessage("Press Ctrl+C again to exit");
808
+ this.notification.setMessage("Press Ctrl+C again to exit", 1000);
533
809
  this.tui.requestRender();
534
810
  this.lastSigintTime = now;
535
811
  // Clear notification after threshold if no second Ctrl+C
@@ -545,6 +821,10 @@ export class NewRepl {
545
821
  }, DoublePressThreshold);
546
822
  }
547
823
  }
824
+ /**
825
+ * Stops the REPL, cleaning up timers, animations,
826
+ * and the TUI. Optionally triggers the exit callback.
827
+ */
548
828
  stop(showExitMessage = false) {
549
829
  this.notification.setMessage("");
550
830
  // Clear any pending notification timer
@@ -553,7 +833,11 @@ export class NewRepl {
553
833
  this.exitNotificationTimer = undefined;
554
834
  }
555
835
  if (showExitMessage && this.onExitCallback) {
556
- this.onExitCallback(this.options.messageHistory.getSessionId());
836
+ const result = this.onExitCallback(this.options.sessionManager.getSessionId());
837
+ // Await if the callback is async
838
+ if (result instanceof Promise) {
839
+ void result;
840
+ }
557
841
  }
558
842
  if (this.loadingAnimation) {
559
843
  this.loadingAnimation.stop();
@@ -564,4 +848,41 @@ export class NewRepl {
564
848
  this.isInitialized = false;
565
849
  }
566
850
  }
851
+ /**
852
+ * Generates rules automatically if autoGenerateRules is enabled
853
+ * and the session has messages.
854
+ */
855
+ async maybeGenerateRules() {
856
+ try {
857
+ const config = this.config;
858
+ if (!config.autoGenerateRules) {
859
+ return;
860
+ }
861
+ if (this.options.sessionManager.isEmpty()) {
862
+ return;
863
+ }
864
+ const { rules } = await generateRulesFromSession({
865
+ modelManager: this.options.modelManager,
866
+ messages: this.options.sessionManager.get(),
867
+ tokenTracker: this.options.tokenTracker,
868
+ config: this.options.configManager,
869
+ workspace: this.options.workspace,
870
+ });
871
+ if (rules.length > 0) {
872
+ this.notification.setMessage(`Auto-generated ${rules.length} rule(s) saved`);
873
+ this.tui.requestRender();
874
+ }
875
+ }
876
+ catch (error) {
877
+ // Don't fail the session transition on rule generation errors
878
+ logger.debug({ error }, "Auto rule generation failed");
879
+ }
880
+ }
881
+ /**
882
+ * Triggers rule generation for use before exit.
883
+ * Returns a promise that resolves when rules are generated.
884
+ */
885
+ async triggerRuleGeneration() {
886
+ await this.maybeGenerateRules();
887
+ }
567
888
  }