@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
package/dist/tui/tui.js CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Minimal TUI implementation with differential rendering
3
3
  */
4
- import { getTerminalSize, isCtrlC, isCtrlZ, isEscape, } from "../terminal/control.js";
4
+ import { getTerminalSize, isCtrlC, isCtrlD, isCtrlM, isCtrlN, isCtrlO, isCtrlR, isCtrlZ, isEscape, } from "../terminal/control.js";
5
+ import { isShiftTab } from "../terminal/keys.js";
5
6
  import style from "../terminal/style.js";
6
7
  import { visibleWidth } from "./utils.js";
7
8
  export { visibleWidth };
@@ -19,6 +20,15 @@ export class Container {
19
20
  this.children.splice(index, 1);
20
21
  }
21
22
  }
23
+ insertChildBefore(beforeComponent, newComponent) {
24
+ const index = this.children.indexOf(beforeComponent);
25
+ if (index !== -1) {
26
+ this.children.splice(index, 0, newComponent);
27
+ }
28
+ else {
29
+ this.children.push(newComponent);
30
+ }
31
+ }
22
32
  clear() {
23
33
  this.children = [];
24
34
  }
@@ -28,6 +38,14 @@ export class Container {
28
38
  }
29
39
  /**
30
40
  * TUI - Main class for managing terminal UI with differential rendering
41
+ *
42
+ * The TUI splits its children into two regions:
43
+ * - Scrollable content: children added before setFixedFooterStart()
44
+ * - Fixed footer: children added after setFixedFooterStart()
45
+ *
46
+ * The scrollable content region supports virtual scrolling via trackpad/mouse
47
+ * wheel. The fixed footer (editor, footer bar, notifications) is always
48
+ * pinned to the bottom of the terminal.
31
49
  */
32
50
  // biome-ignore lint/style/useNamingConvention: override
33
51
  export class TUI extends Container {
@@ -37,14 +55,41 @@ export class TUI extends Container {
37
55
  isRendering = false;
38
56
  renderAgain = false;
39
57
  activeModal = null;
58
+ scrollOffset = 0;
59
+ lastScrollableHeight = 0;
60
+ fixedFooterIndex = -1;
61
+ isUserScrolledUp = false;
40
62
  onCtrlC;
63
+ onCtrlD;
64
+ onReconstructSession;
65
+ onCtrlN;
66
+ onCtrlO;
67
+ /** Callback invoked when Ctrl+M is pressed - opens model selector. */
68
+ onCtrlM;
69
+ onCtrlR;
70
+ onShiftTab;
41
71
  constructor(terminal) {
42
72
  super();
43
73
  this.terminal = terminal;
44
74
  }
75
+ /**
76
+ * Mark the boundary between scrollable content and fixed footer.
77
+ * All children added after this call will be rendered as fixed
78
+ * footer content pinned to the bottom of the terminal.
79
+ */
80
+ setFixedFooterStart() {
81
+ this.fixedFooterIndex = this.children.length;
82
+ }
45
83
  setFocus(component) {
46
84
  this.focusedComponent = component;
47
85
  }
86
+ /**
87
+ * Get the underlying terminal instance for external mode operations.
88
+ * Used primarily for spawning external editors.
89
+ */
90
+ getTerminal() {
91
+ return this.terminal;
92
+ }
48
93
  start() {
49
94
  this.terminal.start((data) => this.handleInput(data), () => this.requestRender());
50
95
  this.terminal.onResume(() => this.requestRender());
@@ -69,7 +114,15 @@ export class TUI extends Container {
69
114
  });
70
115
  }
71
116
  inBracketedPaste = false;
117
+ focusedComponentWantsNavigation() {
118
+ return this.focusedComponent?.wantsNavigationKeys?.() ?? false;
119
+ }
72
120
  handleInput(data) {
121
+ // Handle mouse tracking events (SGR format: \x1b[<button;x;yM)
122
+ if (data.startsWith("\x1b[<")) {
123
+ this.handleMouseEvent(data);
124
+ return;
125
+ }
73
126
  if (data.includes("\x1b[200~")) {
74
127
  this.inBracketedPaste = true;
75
128
  }
@@ -94,6 +147,36 @@ export class TUI extends Container {
94
147
  process.exit(0);
95
148
  }
96
149
  }
150
+ // Handle Ctrl+D - exit only if editor is empty (handled by Repl)
151
+ if (isCtrlD(data)) {
152
+ if (this.onCtrlD) {
153
+ this.onCtrlD();
154
+ }
155
+ return;
156
+ }
157
+ const keyBindings = [
158
+ { check: isCtrlO, handler: this.onCtrlO },
159
+ { check: isCtrlR, handler: this.onCtrlR },
160
+ { check: isCtrlN, handler: this.onCtrlN },
161
+ { check: isCtrlM, handler: this.onCtrlM },
162
+ ];
163
+ for (const binding of keyBindings) {
164
+ if (binding.check(data)) {
165
+ binding.handler?.();
166
+ return;
167
+ }
168
+ }
169
+ // Handle Shift+Tab - cycle mode only when no modal is active,
170
+ // the focused component won't handle navigation (e.g., model selector),
171
+ // and the editor isn't showing autocomplete
172
+ if (isShiftTab(data) && !this.inBracketedPaste) {
173
+ if (!this.activeModal && !this.focusedComponentWantsNavigation()) {
174
+ if (this.onShiftTab) {
175
+ this.onShiftTab();
176
+ }
177
+ return;
178
+ }
179
+ }
97
180
  // Handle Escape key to close modal if one is active
98
181
  if (isEscape(data) && this.activeModal) {
99
182
  this.hideModal();
@@ -106,9 +189,69 @@ export class TUI extends Container {
106
189
  }
107
190
  else if (this.focusedComponent?.handleInput) {
108
191
  this.focusedComponent.handleInput(data);
192
+ this.scrollToBottom();
193
+ this.requestRender();
194
+ }
195
+ }
196
+ handleMouseEvent(data) {
197
+ // SGR mouse format: \x1b[<button;x;yM (press) or \x1b[<button;x;ym (release)
198
+ // Extract button code between "<" and first ";"
199
+ const start = data.indexOf("<");
200
+ const semi = data.indexOf(";", start);
201
+ if (start === -1 || semi === -1)
202
+ return;
203
+ const button = Number.parseInt(data.slice(start + 1, semi), 10);
204
+ const scrollLines = 3;
205
+ const { rows } = getTerminalSize();
206
+ const fixedHeight = this.getFixedFooterHeight(this.terminal.columns);
207
+ const scrollableViewport = rows - fixedHeight;
208
+ // Button 64 = scroll up, button 65 = scroll down
209
+ if (button === 64) {
210
+ this.scrollOffset = Math.max(0, this.scrollOffset - scrollLines);
211
+ const maxOffset = Math.max(0, this.lastScrollableHeight - scrollableViewport);
212
+ this.isUserScrolledUp = this.scrollOffset < maxOffset;
213
+ this.requestRender();
214
+ }
215
+ else if (button === 65) {
216
+ const maxOffset = Math.max(0, this.lastScrollableHeight - scrollableViewport);
217
+ this.scrollOffset = Math.min(maxOffset, this.scrollOffset + scrollLines);
218
+ this.isUserScrolledUp = this.scrollOffset < maxOffset;
109
219
  this.requestRender();
110
220
  }
111
221
  }
222
+ scrollToBottom() {
223
+ const { rows } = getTerminalSize();
224
+ const fixedHeight = this.getFixedFooterHeight(this.terminal.columns);
225
+ const scrollableViewport = rows - fixedHeight;
226
+ this.scrollOffset = Math.max(0, this.lastScrollableHeight - scrollableViewport);
227
+ this.isUserScrolledUp = false;
228
+ }
229
+ getFixedFooterHeight(width) {
230
+ if (this.fixedFooterIndex < 0)
231
+ return 0;
232
+ let height = 0;
233
+ for (let i = this.fixedFooterIndex; i < this.children.length; i++) {
234
+ height += this.children[i].render(width).length;
235
+ }
236
+ return height;
237
+ }
238
+ renderScrollableContent(width) {
239
+ const end = this.fixedFooterIndex >= 0 ? this.fixedFooterIndex : this.children.length;
240
+ const lines = [];
241
+ for (let i = 0; i < end; i++) {
242
+ lines.push(...this.children[i].render(width));
243
+ }
244
+ return lines;
245
+ }
246
+ renderFixedFooter(width) {
247
+ if (this.fixedFooterIndex < 0)
248
+ return [];
249
+ const lines = [];
250
+ for (let i = this.fixedFooterIndex; i < this.children.length; i++) {
251
+ lines.push(...this.children[i].render(width));
252
+ }
253
+ return lines;
254
+ }
112
255
  doRender() {
113
256
  if (this.isRendering) {
114
257
  this.renderAgain = true;
@@ -117,13 +260,32 @@ export class TUI extends Container {
117
260
  this.isRendering = true;
118
261
  try {
119
262
  const width = this.terminal.columns;
120
- // Render all components to get new lines
121
- const newLines = this.render(width);
263
+ const { rows } = getTerminalSize();
264
+ // Render fixed footer and scrollable content separately
265
+ const fixedLines = this.renderFixedFooter(width);
266
+ const scrollableLines = this.renderScrollableContent(width);
267
+ this.lastScrollableHeight = scrollableLines.length;
268
+ const fixedHeight = fixedLines.length;
269
+ const scrollableViewport = rows - fixedHeight;
270
+ // Auto-scroll to bottom when new content arrives,
271
+ // unless the user has explicitly scrolled up
272
+ if (!this.isUserScrolledUp) {
273
+ this.scrollOffset = Math.max(0, scrollableLines.length - scrollableViewport);
274
+ }
275
+ // Clamp scroll offset
276
+ const maxOffset = Math.max(0, scrollableLines.length - scrollableViewport);
277
+ if (this.scrollOffset > maxOffset) {
278
+ this.scrollOffset = maxOffset;
279
+ }
280
+ // Apply scroll offset to get visible scrollable lines
281
+ const visibleScrollable = scrollableLines.slice(this.scrollOffset, this.scrollOffset + scrollableViewport);
282
+ // Combine visible scrollable content with fixed footer
283
+ const visibleLines = [...visibleScrollable, ...fixedLines];
122
284
  // Build output buffer using array join (more efficient than string concat)
123
285
  const bufferParts = [
124
286
  "\x1b[?2026h", // Begin synchronized output
125
287
  "\x1b[3J\x1b[2J\x1b[H", // Clear scrollback, screen, and home
126
- newLines.join("\r\n"),
288
+ visibleLines.join("\r\n"),
127
289
  ];
128
290
  // Render modal on top if active
129
291
  if (this.activeModal) {
@@ -154,43 +316,6 @@ export class TUI extends Container {
154
316
  }
155
317
  }
156
318
  }
157
- // private positionCursor(
158
- // componentCursorPos: [number, number] | null,
159
- // ): void {
160
- // if (!componentCursorPos) {
161
- // // No cursor position from component, hide cursor
162
- // this.terminal.hideCursor();
163
- // return;
164
- // }
165
- // const [cursorRow, cursorCol] = componentCursorPos;
166
- //
167
- // // Calculate absolute cursor position in the terminal
168
- // // We need to find which line in newLines corresponds to the component's cursor row
169
- // // and then position the cursor at that line and column
170
- //
171
- // // Find the line offset for the focused component
172
- // let componentStartLine = 0;
173
- // if (this.focusedComponent) {
174
- // // Find the line where this component starts by summing heights of previous components
175
- // for (const child of this.children) {
176
- // if (child === this.focusedComponent) {
177
- // break;
178
- // }
179
- // // Use the already-rendered lines to calculate height, not re-render
180
- // const childLines = child.render(this.terminal.columns);
181
- // componentStartLine += childLines.length;
182
- // }
183
- // }
184
- //
185
- // const absoluteRow = componentStartLine + cursorRow;
186
- // const absoluteCol = cursorCol;
187
- //
188
- // // Position cursor using absolute positioning
189
- // // Move to home position first, then move down to row, then right to column
190
- // // Note: terminal rows/columns are 1-indexed, so we add 1
191
- // this.terminal.write(`\x1b[H\x1b[${absoluteRow + 1}B\x1b[${absoluteCol + 1}G`);
192
- // this.terminal.showCursor();
193
- // }
194
319
  /**
195
320
  * Show a modal dialog
196
321
  */
@@ -7,11 +7,7 @@
7
7
  * - Tabs (replaced with 3 spaces for consistent width)
8
8
  */
9
9
  export declare function visibleWidth(str: string): number;
10
- /**
11
- * Truncate text to fit within a maximum visible width, preserving ANSI codes.
12
- * Adds ellipsis if truncation occurs.
13
- */
14
- export declare function truncateToWidth(text: string, maxWidth: number, ellipsis?: string): string;
10
+ export declare function truncateToWidth(text: string, maxWidth: number, ellipsis?: string, pad?: boolean): string;
15
11
  /**
16
12
  * Apply background color to a line, padding to full width.
17
13
  *
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../source/tui/utils.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIhD;AA4BD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,SAAQ,GACf,MAAM,CA+DR;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAC7B,MAAM,CASR"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../source/tui/utils.ts"],"names":[],"mappings":"AAgBA;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIhD;AAwOD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,SAAQ,EAChB,GAAG,UAAQ,GACV,MAAM,CAuER;AAwHD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAC7B,MAAM,CASR"}
package/dist/tui/utils.js CHANGED
@@ -1,5 +1,17 @@
1
1
  import { getSegmenter } from "../terminal/segmenter.js";
2
2
  import stringWidth from "../terminal/string-width.js";
3
+ /**
4
+ * Check if a string contains only printable ASCII characters (0x20-0x7e).
5
+ */
6
+ function isPrintableAscii(str) {
7
+ for (let i = 0; i < str.length; i++) {
8
+ const code = str.charCodeAt(i);
9
+ if (code < 0x20 || code > 0x7e) {
10
+ return false;
11
+ }
12
+ }
13
+ return true;
14
+ }
3
15
  /**
4
16
  * Calculate the visible width of a string in terminal columns.
5
17
  * This correctly handles:
@@ -13,6 +25,12 @@ export function visibleWidth(str) {
13
25
  const normalized = str.replace(/\t/g, " ");
14
26
  return stringWidth(normalized);
15
27
  }
28
+ /**
29
+ * Get the width of a single grapheme cluster.
30
+ */
31
+ function graphemeWidth(grapheme) {
32
+ return stringWidth(grapheme);
33
+ }
16
34
  /**
17
35
  * Extract ANSI escape sequences from a string at the given position.
18
36
  */
@@ -32,64 +50,273 @@ function extractAnsiCode(str, pos) {
32
50
  }
33
51
  return null;
34
52
  }
35
- /**
36
- * Truncate text to fit within a maximum visible width, preserving ANSI codes.
37
- * Adds ellipsis if truncation occurs.
38
- */
39
- export function truncateToWidth(text, maxWidth, ellipsis = "...") {
40
- const textVisibleWidth = visibleWidth(text);
41
- if (textVisibleWidth <= maxWidth) {
42
- return text;
53
+ function createInitialState() {
54
+ return {
55
+ result: "",
56
+ pendingAnsi: "",
57
+ visibleSoFar: 0,
58
+ keptWidth: 0,
59
+ keepContiguousPrefix: true,
60
+ overflowed: false,
61
+ };
62
+ }
63
+ function processAnsiCode(state, text, pos) {
64
+ const ansi = extractAnsiCode(text, pos);
65
+ if (ansi) {
66
+ state.pendingAnsi += ansi.code;
67
+ return ansi.length;
68
+ }
69
+ return 0;
70
+ }
71
+ function processTab(state, targetWidth, maxWidth) {
72
+ if (state.keepContiguousPrefix && state.keptWidth + 3 <= targetWidth) {
73
+ if (state.pendingAnsi) {
74
+ state.result += state.pendingAnsi;
75
+ state.pendingAnsi = "";
76
+ }
77
+ state.result += "\t";
78
+ state.keptWidth += 3;
79
+ }
80
+ else {
81
+ state.keepContiguousPrefix = false;
82
+ state.pendingAnsi = "";
83
+ }
84
+ state.visibleSoFar += 3;
85
+ if (state.visibleSoFar > maxWidth) {
86
+ state.overflowed = true;
87
+ return true;
88
+ }
89
+ return false;
90
+ }
91
+ function processGraphemeSegment(state, segment, targetWidth, maxWidth) {
92
+ const width = graphemeWidth(segment);
93
+ if (state.keepContiguousPrefix && state.keptWidth + width <= targetWidth) {
94
+ if (state.pendingAnsi) {
95
+ state.result += state.pendingAnsi;
96
+ state.pendingAnsi = "";
97
+ }
98
+ state.result += segment;
99
+ state.keptWidth += width;
100
+ }
101
+ else {
102
+ state.keepContiguousPrefix = false;
103
+ state.pendingAnsi = "";
104
+ }
105
+ state.visibleSoFar += width;
106
+ if (state.visibleSoFar > maxWidth) {
107
+ state.overflowed = true;
108
+ return true;
109
+ }
110
+ return false;
111
+ }
112
+ function truncateSimple(text, targetWidth, maxWidth) {
113
+ const state = createInitialState();
114
+ for (const { segment } of getSegmenter().segment(text)) {
115
+ if (processGraphemeSegment(state, segment, targetWidth, maxWidth)) {
116
+ break;
117
+ }
118
+ }
119
+ return {
120
+ result: state.result,
121
+ keptWidth: state.keptWidth,
122
+ overflowed: state.overflowed,
123
+ visibleSoFar: state.visibleSoFar,
124
+ };
125
+ }
126
+ function truncateComplex(text, targetWidth, maxWidth) {
127
+ const state = createInitialState();
128
+ let i = 0;
129
+ while (i < text.length && !state.overflowed) {
130
+ const ansiSkip = processAnsiCode(state, text, i);
131
+ if (ansiSkip > 0) {
132
+ i += ansiSkip;
133
+ continue;
134
+ }
135
+ if (text[i] === "\t") {
136
+ if (processTab(state, targetWidth, maxWidth)) {
137
+ break;
138
+ }
139
+ i++;
140
+ continue;
141
+ }
142
+ // Find end of current text segment (until tab or ANSI)
143
+ let end = i;
144
+ while (end < text.length && text[end] !== "\t") {
145
+ const nextAnsi = extractAnsiCode(text, end);
146
+ if (nextAnsi) {
147
+ break;
148
+ }
149
+ end++;
150
+ }
151
+ // Process graphemes in this segment
152
+ for (const { segment } of getSegmenter().segment(text.slice(i, end))) {
153
+ if (processGraphemeSegment(state, segment, targetWidth, maxWidth)) {
154
+ break;
155
+ }
156
+ }
157
+ i = end;
158
+ }
159
+ return {
160
+ result: state.result,
161
+ keptWidth: state.keptWidth,
162
+ overflowed: state.overflowed,
163
+ exhaustedInput: i >= text.length,
164
+ visibleSoFar: state.visibleSoFar,
165
+ };
166
+ }
167
+ function handleOversizedEllipsis(text, maxWidth, ellipsis, pad) {
168
+ const textWidth = visibleWidth(text);
169
+ if (textWidth <= maxWidth) {
170
+ return pad ? text + " ".repeat(maxWidth - textWidth) : text;
171
+ }
172
+ const clippedEllipsis = truncateFragmentToWidth(ellipsis, maxWidth);
173
+ if (clippedEllipsis.width === 0) {
174
+ return pad ? " ".repeat(maxWidth) : "";
175
+ }
176
+ return finalizeTruncatedResult("", 0, clippedEllipsis.text, clippedEllipsis.width, maxWidth, pad);
177
+ }
178
+ export function truncateToWidth(text, maxWidth, ellipsis = "...", pad = false) {
179
+ if (maxWidth <= 0) {
180
+ return "";
181
+ }
182
+ if (text.length === 0) {
183
+ return pad ? " ".repeat(maxWidth) : "";
43
184
  }
44
185
  const ellipsisWidth = visibleWidth(ellipsis);
186
+ // Handle case where ellipsis is wider than maxWidth
187
+ if (ellipsisWidth >= maxWidth) {
188
+ const result = handleOversizedEllipsis(text, maxWidth, ellipsis, pad);
189
+ if (result !== null) {
190
+ return result;
191
+ }
192
+ }
193
+ // Fast path: pure ASCII printable
194
+ if (isPrintableAscii(text)) {
195
+ if (text.length <= maxWidth) {
196
+ return pad ? text + " ".repeat(maxWidth - text.length) : text;
197
+ }
198
+ const targetWidth = maxWidth - ellipsisWidth;
199
+ return finalizeTruncatedResult(text.slice(0, targetWidth), targetWidth, ellipsis, ellipsisWidth, maxWidth, pad);
200
+ }
45
201
  const targetWidth = maxWidth - ellipsisWidth;
46
- if (targetWidth <= 0) {
47
- return ellipsis.substring(0, maxWidth);
202
+ const hasAnsi = text.includes("\x1b");
203
+ const hasTabs = text.includes("\t");
204
+ let truncatedResult;
205
+ let exhaustedInput;
206
+ if (!hasAnsi && !hasTabs) {
207
+ truncatedResult = truncateSimple(text, targetWidth, maxWidth);
208
+ exhaustedInput = !truncatedResult.overflowed;
48
209
  }
49
- // Separate ANSI codes from visible content using grapheme segmentation
210
+ else {
211
+ const complexResult = truncateComplex(text, targetWidth, maxWidth);
212
+ truncatedResult = complexResult;
213
+ exhaustedInput = complexResult.exhaustedInput;
214
+ }
215
+ // Text fits completely
216
+ if (!truncatedResult.overflowed && exhaustedInput) {
217
+ return pad
218
+ ? text + " ".repeat(Math.max(0, maxWidth - truncatedResult.visibleSoFar))
219
+ : text;
220
+ }
221
+ return finalizeTruncatedResult(truncatedResult.result, truncatedResult.keptWidth, ellipsis, ellipsisWidth, maxWidth, pad);
222
+ }
223
+ /**
224
+ * Truncate a single fragment to a maximum width.
225
+ */
226
+ function truncateFragmentToWidth(text, maxWidth) {
227
+ if (maxWidth <= 0 || text.length === 0) {
228
+ return { text: "", width: 0 };
229
+ }
230
+ if (isPrintableAscii(text)) {
231
+ const clipped = text.slice(0, maxWidth);
232
+ return { text: clipped, width: clipped.length };
233
+ }
234
+ const hasAnsi = text.includes("\x1b");
235
+ const hasTabs = text.includes("\t");
236
+ if (!hasAnsi && !hasTabs) {
237
+ let result = "";
238
+ let width = 0;
239
+ for (const { segment } of getSegmenter().segment(text)) {
240
+ const w = graphemeWidth(segment);
241
+ if (width + w > maxWidth) {
242
+ break;
243
+ }
244
+ result += segment;
245
+ width += w;
246
+ }
247
+ return { text: result, width };
248
+ }
249
+ let result = "";
250
+ let width = 0;
50
251
  let i = 0;
51
- const segments = [];
252
+ let pendingAnsi = "";
52
253
  while (i < text.length) {
53
- const ansiResult = extractAnsiCode(text, i);
54
- if (ansiResult) {
55
- segments.push({ type: "ansi", value: ansiResult.code });
56
- i += ansiResult.length;
254
+ const ansi = extractAnsiCode(text, i);
255
+ if (ansi) {
256
+ pendingAnsi += ansi.code;
257
+ i += ansi.length;
258
+ continue;
57
259
  }
58
- else {
59
- // Find the next ANSI code or end of string
60
- let end = i;
61
- while (end < text.length) {
62
- const nextAnsi = extractAnsiCode(text, end);
63
- if (nextAnsi)
64
- break;
65
- end++;
260
+ if (text[i] === "\t") {
261
+ if (width + 3 > maxWidth) {
262
+ break;
263
+ }
264
+ if (pendingAnsi) {
265
+ result += pendingAnsi;
266
+ pendingAnsi = "";
267
+ }
268
+ result += "\t";
269
+ width += 3;
270
+ i++;
271
+ continue;
272
+ }
273
+ let end = i;
274
+ while (end < text.length && text[end] !== "\t") {
275
+ const nextAnsi = extractAnsiCode(text, end);
276
+ if (nextAnsi) {
277
+ break;
278
+ }
279
+ end++;
280
+ }
281
+ for (const { segment } of getSegmenter().segment(text.slice(i, end))) {
282
+ const w = graphemeWidth(segment);
283
+ if (width + w > maxWidth) {
284
+ return { text: result, width };
66
285
  }
67
- // Segment this non-ANSI portion into graphemes
68
- const textPortion = text.slice(i, end);
69
- for (const seg of getSegmenter().segment(textPortion)) {
70
- segments.push({ type: "grapheme", value: seg.segment });
286
+ if (pendingAnsi) {
287
+ result += pendingAnsi;
288
+ pendingAnsi = "";
71
289
  }
72
- i = end;
290
+ result += segment;
291
+ width += w;
73
292
  }
293
+ i = end;
74
294
  }
75
- // Build truncated string from segments
76
- let result = "";
77
- let currentWidth = 0;
78
- for (const seg of segments) {
79
- if (seg.type === "ansi") {
80
- result += seg.value;
81
- continue;
295
+ return { text: result, width };
296
+ }
297
+ /**
298
+ * Finalize a truncated result with proper ANSI reset codes.
299
+ * Only adds reset codes if the prefix contains ANSI sequences.
300
+ */
301
+ function finalizeTruncatedResult(prefix, prefixWidth, ellipsis, ellipsisWidth, maxWidth, pad) {
302
+ const hasAnsi = prefix.includes("\x1b");
303
+ const visibleWidth = prefixWidth + ellipsisWidth;
304
+ let result;
305
+ if (hasAnsi) {
306
+ const reset = "\x1b[0m";
307
+ if (ellipsis.length > 0) {
308
+ result = `${prefix}${reset}${ellipsis}${reset}`;
82
309
  }
83
- const grapheme = seg.value;
84
- const graphemeWidth = visibleWidth(grapheme);
85
- if (currentWidth + graphemeWidth > targetWidth) {
86
- break;
310
+ else {
311
+ result = `${prefix}${reset}`;
87
312
  }
88
- result += grapheme;
89
- currentWidth += graphemeWidth;
90
313
  }
91
- // Add reset code before ellipsis to prevent styling leaking into it
92
- return `${result}\x1b[0m${ellipsis}`;
314
+ else {
315
+ result = ellipsis.length > 0 ? `${prefix}${ellipsis}` : prefix;
316
+ }
317
+ return pad
318
+ ? result + " ".repeat(Math.max(0, maxWidth - visibleWidth))
319
+ : result;
93
320
  }
94
321
  /**
95
322
  * Apply background color to a line, padding to full width.
@@ -0,0 +1,19 @@
1
+ interface ControlOperator {
2
+ op: "||" | "&&" | ";;" | "|&" | "<(" | "<<<" | ">>" | ">&" | "&" | ";" | "(" | ")" | "|" | "<" | ">";
3
+ }
4
+ interface GlobOperator {
5
+ op: "glob";
6
+ pattern: string;
7
+ }
8
+ interface CommentOperator {
9
+ comment: string;
10
+ }
11
+ type ParseEntry = string | ControlOperator | GlobOperator | CommentOperator;
12
+ interface ParseOptions {
13
+ escape?: string;
14
+ }
15
+ type EnvFunction = (key: string) => unknown;
16
+ export declare function parse(s: string, env?: Record<string, string | undefined> | EnvFunction, opts?: ParseOptions): ParseEntry[];
17
+ export declare function parse<T extends object | string>(s: string, env: (key: string) => T | undefined, opts?: ParseOptions): (ParseEntry | T)[];
18
+ export {};
19
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../../source/utils/bash/parse.ts"],"names":[],"mappings":"AA+BA,UAAU,eAAe;IACvB,EAAE,EACE,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,IAAI,GACJ,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,GACH,GAAG,CAAC;CACT;AAED,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,KAAK,UAAU,GAAG,MAAM,GAAG,eAAe,GAAG,YAAY,GAAG,eAAe,CAAC;AAE5E,UAAU,YAAY;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,KAAK,WAAW,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;AA8L5C,wBAAgB,KAAK,CACnB,CAAC,EAAE,MAAM,EACT,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,WAAW,EACtD,IAAI,CAAC,EAAE,YAAY,GAClB,UAAU,EAAE,CAAC;AAChB,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC7C,CAAC,EAAE,MAAM,EACT,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC,GAAG,SAAS,EACnC,IAAI,CAAC,EAAE,YAAY,GAClB,CAAC,UAAU,GAAG,CAAC,CAAC,EAAE,CAAC"}