@travisennis/acai 0.0.8 → 0.0.10

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 (386) hide show
  1. package/README.md +48 -729
  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 +378 -168
  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 -76
  12. package/dist/cli/stdin.d.ts +9 -0
  13. package/dist/cli/stdin.d.ts.map +1 -0
  14. package/dist/cli/stdin.js +37 -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 -100
  21. package/dist/commands/generate-rules/service.d.ts +21 -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.map +1 -1
  27. package/dist/commands/health/utils.js +6 -0
  28. package/dist/commands/history/index.d.ts +1 -1
  29. package/dist/commands/history/index.d.ts.map +1 -1
  30. package/dist/commands/history/index.js +17 -18
  31. package/dist/commands/history/types.d.ts +38 -0
  32. package/dist/commands/history/types.d.ts.map +1 -1
  33. package/dist/commands/history/utils.d.ts.map +1 -1
  34. package/dist/commands/history/utils.js +63 -58
  35. package/dist/commands/init/index.d.ts.map +1 -1
  36. package/dist/commands/init/index.js +3 -8
  37. package/dist/commands/init-project/index.d.ts.map +1 -1
  38. package/dist/commands/init-project/index.js +3 -3
  39. package/dist/commands/init-project/utils.d.ts.map +1 -1
  40. package/dist/commands/init-project/utils.js +10 -2
  41. package/dist/commands/list-tools/index.d.ts.map +1 -1
  42. package/dist/commands/list-tools/index.js +7 -31
  43. package/dist/commands/manager.d.ts +2 -2
  44. package/dist/commands/manager.d.ts.map +1 -1
  45. package/dist/commands/manager.js +57 -33
  46. package/dist/commands/model/index.d.ts.map +1 -1
  47. package/dist/commands/model/index.js +20 -151
  48. package/dist/commands/model/model-panel.d.ts +4 -0
  49. package/dist/commands/model/model-panel.d.ts.map +1 -0
  50. package/dist/commands/model/model-panel.js +144 -0
  51. package/dist/commands/paste/index.d.ts.map +1 -1
  52. package/dist/commands/paste/index.js +59 -62
  53. package/dist/commands/paste/utils.d.ts.map +1 -1
  54. package/dist/commands/paste/utils.js +88 -58
  55. package/dist/commands/pickup/index.d.ts.map +1 -1
  56. package/dist/commands/pickup/index.js +6 -3
  57. package/dist/commands/pickup/utils.js +3 -3
  58. package/dist/commands/resources/index.d.ts.map +1 -1
  59. package/dist/commands/resources/index.js +33 -50
  60. package/dist/commands/review/index.d.ts.map +1 -1
  61. package/dist/commands/review/index.js +3 -117
  62. package/dist/commands/review/review-panel.d.ts +3 -0
  63. package/dist/commands/review/review-panel.d.ts.map +1 -0
  64. package/dist/commands/review/review-panel.js +186 -0
  65. package/dist/commands/review/utils.d.ts +9 -0
  66. package/dist/commands/review/utils.d.ts.map +1 -1
  67. package/dist/commands/review/utils.js +127 -68
  68. package/dist/commands/session/index.d.ts +1 -1
  69. package/dist/commands/session/index.d.ts.map +1 -1
  70. package/dist/commands/session/index.js +134 -112
  71. package/dist/commands/session/types.d.ts +7 -0
  72. package/dist/commands/session/types.d.ts.map +1 -1
  73. package/dist/commands/share/html-renderer.d.ts +25 -0
  74. package/dist/commands/share/html-renderer.d.ts.map +1 -0
  75. package/dist/commands/share/html-renderer.js +384 -0
  76. package/dist/commands/share/index.d.ts +3 -0
  77. package/dist/commands/share/index.d.ts.map +1 -0
  78. package/dist/commands/share/index.js +122 -0
  79. package/dist/commands/shell/index.d.ts.map +1 -1
  80. package/dist/commands/shell/index.js +16 -1
  81. package/dist/commands/types.d.ts +2 -2
  82. package/dist/commands/types.d.ts.map +1 -1
  83. package/dist/{config.d.ts → config/index.d.ts} +20 -9
  84. package/dist/config/index.d.ts.map +1 -0
  85. package/dist/{config.js → config/index.js} +43 -42
  86. package/dist/execution/index.d.ts.map +1 -1
  87. package/dist/execution/index.js +75 -55
  88. package/dist/index.d.ts +1 -0
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +170 -127
  91. package/dist/middleware/cache.d.ts.map +1 -1
  92. package/dist/middleware/cache.js +18 -36
  93. package/dist/models/ai-config.d.ts +1 -0
  94. package/dist/models/ai-config.d.ts.map +1 -1
  95. package/dist/models/ai-config.js +4 -3
  96. package/dist/models/anthropic-provider.d.ts +2 -5
  97. package/dist/models/anthropic-provider.d.ts.map +1 -1
  98. package/dist/models/anthropic-provider.js +3 -70
  99. package/dist/models/deepseek-provider.d.ts +1 -0
  100. package/dist/models/deepseek-provider.d.ts.map +1 -1
  101. package/dist/models/google-provider.d.ts +2 -3
  102. package/dist/models/google-provider.d.ts.map +1 -1
  103. package/dist/models/google-provider.js +0 -26
  104. package/dist/models/groq-provider.d.ts +1 -0
  105. package/dist/models/groq-provider.d.ts.map +1 -1
  106. package/dist/models/manager.d.ts +13 -2
  107. package/dist/models/manager.d.ts.map +1 -1
  108. package/dist/models/manager.js +20 -8
  109. package/dist/models/openai-provider.d.ts +5 -5
  110. package/dist/models/openai-provider.d.ts.map +1 -1
  111. package/dist/models/openai-provider.js +27 -40
  112. package/dist/models/opencode-zen-provider.d.ts +8 -3
  113. package/dist/models/opencode-zen-provider.d.ts.map +1 -1
  114. package/dist/models/opencode-zen-provider.js +68 -11
  115. package/dist/models/openrouter-provider.d.ts +24 -30
  116. package/dist/models/openrouter-provider.d.ts.map +1 -1
  117. package/dist/models/openrouter-provider.js +92 -177
  118. package/dist/models/providers.d.ts +1 -1
  119. package/dist/models/providers.d.ts.map +1 -1
  120. package/dist/models/xai-provider.d.ts +4 -3
  121. package/dist/models/xai-provider.d.ts.map +1 -1
  122. package/dist/models/xai-provider.js +18 -18
  123. package/dist/modes/manager.d.ts +23 -0
  124. package/dist/modes/manager.d.ts.map +1 -0
  125. package/dist/modes/manager.js +77 -0
  126. package/dist/modes/prompts.d.ts +2 -0
  127. package/dist/modes/prompts.d.ts.map +1 -0
  128. package/dist/modes/prompts.js +143 -0
  129. package/dist/prompts/mentions.d.ts +11 -0
  130. package/dist/prompts/mentions.d.ts.map +1 -0
  131. package/dist/{mentions.js → prompts/mentions.js} +21 -80
  132. package/dist/prompts/system-prompt.d.ts +26 -0
  133. package/dist/prompts/system-prompt.d.ts.map +1 -0
  134. package/dist/{prompts.js → prompts/system-prompt.js} +50 -22
  135. package/dist/repl/index.d.ts +174 -0
  136. package/dist/repl/index.d.ts.map +1 -0
  137. package/dist/{repl-new.js → repl/index.js} +399 -76
  138. package/dist/repl/project-status.d.ts +1 -0
  139. package/dist/repl/project-status.d.ts.map +1 -1
  140. package/dist/repl/project-status.js +4 -1
  141. package/dist/sessions/manager.d.ts +93 -1
  142. package/dist/sessions/manager.d.ts.map +1 -1
  143. package/dist/sessions/manager.js +264 -9
  144. package/dist/sessions/summary.d.ts +4 -0
  145. package/dist/sessions/summary.d.ts.map +1 -0
  146. package/dist/sessions/summary.js +30 -0
  147. package/dist/{skills.d.ts → skills/index.d.ts} +14 -2
  148. package/dist/skills/index.d.ts.map +1 -0
  149. package/dist/skills/index.js +294 -0
  150. package/dist/subagents/index.d.ts +15 -0
  151. package/dist/subagents/index.d.ts.map +1 -0
  152. package/dist/subagents/index.js +231 -0
  153. package/dist/terminal/control.d.ts +1 -1
  154. package/dist/terminal/control.d.ts.map +1 -1
  155. package/dist/terminal/control.js +30 -9
  156. package/dist/terminal/east-asian-width.d.ts.map +1 -1
  157. package/dist/terminal/east-asian-width.js +404 -351
  158. package/dist/terminal/keys.d.ts +17 -0
  159. package/dist/terminal/keys.d.ts.map +1 -1
  160. package/dist/terminal/keys.js +37 -0
  161. package/dist/terminal/select-prompt.d.ts.map +1 -1
  162. package/dist/terminal/select-prompt.js +24 -12
  163. package/dist/terminal/string-width.d.ts.map +1 -1
  164. package/dist/terminal/string-width.js +25 -27
  165. package/dist/terminal/style.d.ts.map +1 -1
  166. package/dist/terminal/style.js +4 -7
  167. package/dist/terminal/supports-color.d.ts.map +1 -1
  168. package/dist/terminal/supports-color.js +41 -27
  169. package/dist/terminal/table/cell.d.ts +12 -0
  170. package/dist/terminal/table/cell.d.ts.map +1 -1
  171. package/dist/terminal/table/cell.js +40 -25
  172. package/dist/terminal/table/layout-manager.d.ts.map +1 -1
  173. package/dist/terminal/table/layout-manager.js +100 -68
  174. package/dist/terminal/table/utils.d.ts.map +1 -1
  175. package/dist/terminal/table/utils.js +17 -10
  176. package/dist/terminal/wrap-ansi.d.ts.map +1 -1
  177. package/dist/terminal/wrap-ansi.js +172 -103
  178. package/dist/tokens/tracker.d.ts +1 -0
  179. package/dist/tokens/tracker.d.ts.map +1 -1
  180. package/dist/tokens/tracker.js +3 -0
  181. package/dist/tools/agent.d.ts +27 -0
  182. package/dist/tools/agent.d.ts.map +1 -0
  183. package/dist/tools/agent.js +81 -0
  184. package/dist/tools/bash.d.ts +4 -3
  185. package/dist/tools/bash.d.ts.map +1 -1
  186. package/dist/tools/bash.js +343 -121
  187. package/dist/tools/code-search.d.ts +41 -0
  188. package/dist/tools/code-search.d.ts.map +1 -0
  189. package/dist/tools/code-search.js +195 -0
  190. package/dist/tools/directory-tree.d.ts +3 -3
  191. package/dist/tools/directory-tree.d.ts.map +1 -1
  192. package/dist/tools/directory-tree.js +8 -5
  193. package/dist/tools/dynamic-tool-loader.d.ts +2 -5
  194. package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
  195. package/dist/tools/dynamic-tool-loader.js +20 -4
  196. package/dist/tools/edit-file.d.ts +7 -7
  197. package/dist/tools/edit-file.d.ts.map +1 -1
  198. package/dist/tools/edit-file.js +164 -66
  199. package/dist/tools/glob.d.ts +6 -6
  200. package/dist/tools/glob.d.ts.map +1 -1
  201. package/dist/tools/glob.js +95 -55
  202. package/dist/tools/grep.d.ts +15 -12
  203. package/dist/tools/grep.d.ts.map +1 -1
  204. package/dist/tools/grep.js +300 -192
  205. package/dist/tools/index.d.ts +143 -5
  206. package/dist/tools/index.d.ts.map +1 -1
  207. package/dist/tools/index.js +39 -24
  208. package/dist/tools/ls.d.ts +2 -2
  209. package/dist/tools/ls.d.ts.map +1 -1
  210. package/dist/tools/ls.js +7 -5
  211. package/dist/tools/read-file.d.ts +3 -3
  212. package/dist/tools/read-file.d.ts.map +1 -1
  213. package/dist/tools/read-file.js +74 -34
  214. package/dist/tools/save-file.d.ts +3 -3
  215. package/dist/tools/save-file.d.ts.map +1 -1
  216. package/dist/tools/save-file.js +11 -11
  217. package/dist/tools/skill.d.ts +23 -0
  218. package/dist/tools/skill.d.ts.map +1 -0
  219. package/dist/tools/skill.js +65 -0
  220. package/dist/tools/think.d.ts.map +1 -1
  221. package/dist/tools/think.js +2 -9
  222. package/dist/tools/utils.d.ts +2 -0
  223. package/dist/tools/utils.d.ts.map +1 -1
  224. package/dist/tools/utils.js +12 -0
  225. package/dist/tools/web-fetch.d.ts +62 -0
  226. package/dist/tools/web-fetch.d.ts.map +1 -0
  227. package/dist/tools/web-fetch.js +429 -0
  228. package/dist/tools/web-search.d.ts +62 -0
  229. package/dist/tools/web-search.d.ts.map +1 -0
  230. package/dist/tools/web-search.js +226 -0
  231. package/dist/tui/autocomplete/attachment-provider.d.ts +3 -6
  232. package/dist/tui/autocomplete/attachment-provider.d.ts.map +1 -1
  233. package/dist/tui/autocomplete/attachment-provider.js +25 -78
  234. package/dist/tui/autocomplete/base-provider.d.ts +1 -0
  235. package/dist/tui/autocomplete/base-provider.d.ts.map +1 -1
  236. package/dist/tui/autocomplete/combined-provider.d.ts +1 -4
  237. package/dist/tui/autocomplete/combined-provider.d.ts.map +1 -1
  238. package/dist/tui/autocomplete/combined-provider.js +3 -17
  239. package/dist/tui/autocomplete/command-provider.d.ts +1 -0
  240. package/dist/tui/autocomplete/command-provider.d.ts.map +1 -1
  241. package/dist/tui/autocomplete/command-provider.js +3 -0
  242. package/dist/tui/autocomplete/file-search-provider.d.ts +2 -1
  243. package/dist/tui/autocomplete/file-search-provider.d.ts.map +1 -1
  244. package/dist/tui/autocomplete/file-search-provider.js +36 -16
  245. package/dist/tui/autocomplete/skill-provider.d.ts +17 -0
  246. package/dist/tui/autocomplete/skill-provider.d.ts.map +1 -0
  247. package/dist/tui/autocomplete/skill-provider.js +49 -0
  248. package/dist/tui/autocomplete.d.ts +2 -2
  249. package/dist/tui/autocomplete.d.ts.map +1 -1
  250. package/dist/tui/autocomplete.js +3 -5
  251. package/dist/tui/components/assistant-message.d.ts.map +1 -1
  252. package/dist/tui/components/assistant-message.js +0 -4
  253. package/dist/tui/components/editor.d.ts +21 -2
  254. package/dist/tui/components/editor.d.ts.map +1 -1
  255. package/dist/tui/components/editor.js +228 -236
  256. package/dist/tui/components/footer.d.ts +6 -4
  257. package/dist/tui/components/footer.d.ts.map +1 -1
  258. package/dist/tui/components/footer.js +49 -25
  259. package/dist/tui/components/markdown.d.ts +8 -5
  260. package/dist/tui/components/markdown.d.ts.map +1 -1
  261. package/dist/tui/components/markdown.js +57 -39
  262. package/dist/tui/components/modal.d.ts.map +1 -1
  263. package/dist/tui/components/modal.js +35 -33
  264. package/dist/tui/components/notification.d.ts +13 -2
  265. package/dist/tui/components/notification.d.ts.map +1 -1
  266. package/dist/tui/components/notification.js +37 -2
  267. package/dist/tui/components/progress-bar.js +1 -1
  268. package/dist/tui/components/select-list.d.ts +1 -0
  269. package/dist/tui/components/select-list.d.ts.map +1 -1
  270. package/dist/tui/components/select-list.js +14 -11
  271. package/dist/tui/components/text.d.ts +16 -0
  272. package/dist/tui/components/text.d.ts.map +1 -1
  273. package/dist/tui/components/text.js +72 -57
  274. package/dist/tui/components/thinking-block.d.ts +9 -0
  275. package/dist/tui/components/thinking-block.d.ts.map +1 -1
  276. package/dist/tui/components/thinking-block.js +43 -11
  277. package/dist/tui/components/tool-execution.d.ts +5 -1
  278. package/dist/tui/components/tool-execution.d.ts.map +1 -1
  279. package/dist/tui/components/tool-execution.js +19 -10
  280. package/dist/tui/components/user-message.d.ts.map +1 -1
  281. package/dist/tui/components/user-message.js +0 -3
  282. package/dist/tui/components/welcome.js +2 -2
  283. package/dist/tui/editor-launcher.d.ts +13 -0
  284. package/dist/tui/editor-launcher.d.ts.map +1 -0
  285. package/dist/tui/editor-launcher.js +39 -0
  286. package/dist/tui/index.d.ts +3 -1
  287. package/dist/tui/index.d.ts.map +1 -1
  288. package/dist/tui/index.js +1 -0
  289. package/dist/tui/terminal.d.ts +27 -0
  290. package/dist/tui/terminal.d.ts.map +1 -1
  291. package/dist/tui/terminal.js +144 -15
  292. package/dist/tui/tui.d.ts +43 -0
  293. package/dist/tui/tui.d.ts.map +1 -1
  294. package/dist/tui/tui.js +172 -41
  295. package/dist/utils/bash/parse.d.ts +19 -0
  296. package/dist/utils/bash/parse.d.ts.map +1 -0
  297. package/dist/utils/bash/parse.js +223 -0
  298. package/dist/utils/bash/quote.d.ts +6 -0
  299. package/dist/utils/bash/quote.d.ts.map +1 -0
  300. package/dist/utils/bash/quote.js +23 -0
  301. package/dist/utils/bash.d.ts.map +1 -1
  302. package/dist/utils/bash.js +211 -126
  303. package/dist/utils/command-protection.d.ts +28 -0
  304. package/dist/utils/command-protection.d.ts.map +1 -0
  305. package/dist/utils/command-protection.js +324 -0
  306. package/dist/utils/dedent.d.ts.map +1 -0
  307. package/dist/utils/env-expand.d.ts +2 -0
  308. package/dist/utils/env-expand.d.ts.map +1 -0
  309. package/dist/utils/env-expand.js +8 -0
  310. package/dist/utils/filesystem/path-display.d.ts +11 -0
  311. package/dist/utils/filesystem/path-display.d.ts.map +1 -0
  312. package/dist/utils/filesystem/path-display.js +32 -0
  313. package/dist/utils/filesystem/security.d.ts +2 -2
  314. package/dist/utils/filesystem/security.d.ts.map +1 -1
  315. package/dist/utils/filesystem/security.js +32 -31
  316. package/dist/utils/formatting.d.ts.map +1 -0
  317. package/dist/{formatting.js → utils/formatting.js} +1 -1
  318. package/dist/utils/git.d.ts +4 -0
  319. package/dist/utils/git.d.ts.map +1 -1
  320. package/dist/utils/git.js +30 -0
  321. package/dist/utils/glob.d.ts +1 -1
  322. package/dist/utils/glob.d.ts.map +1 -1
  323. package/dist/utils/logger.d.ts.map +1 -0
  324. package/dist/{logger.js → utils/logger.js} +1 -1
  325. package/dist/utils/parsing.d.ts.map +1 -0
  326. package/dist/utils/process.d.ts.map +1 -1
  327. package/dist/utils/process.js +90 -37
  328. package/dist/utils/templates.d.ts +2 -0
  329. package/dist/utils/templates.d.ts.map +1 -0
  330. package/dist/utils/templates.js +24 -0
  331. package/dist/utils/version.d.ts.map +1 -0
  332. package/dist/{version.js → utils/version.js} +1 -1
  333. package/package.json +34 -25
  334. package/dist/cli.d.ts +0 -23
  335. package/dist/cli.d.ts.map +0 -1
  336. package/dist/commands/exit/index.d.ts +0 -10
  337. package/dist/commands/exit/index.d.ts.map +0 -1
  338. package/dist/commands/exit/index.js +0 -21
  339. package/dist/commands/exit/types.d.ts +0 -8
  340. package/dist/commands/exit/types.d.ts.map +0 -1
  341. package/dist/commands/exit/types.js +0 -1
  342. package/dist/commands/exit/utils.d.ts +0 -2
  343. package/dist/commands/exit/utils.d.ts.map +0 -1
  344. package/dist/commands/exit/utils.js +0 -13
  345. package/dist/commands/prompt/index.d.ts +0 -5
  346. package/dist/commands/prompt/index.d.ts.map +0 -1
  347. package/dist/commands/prompt/index.js +0 -126
  348. package/dist/commands/prompt/types.d.ts +0 -15
  349. package/dist/commands/prompt/types.d.ts.map +0 -1
  350. package/dist/commands/prompt/types.js +0 -1
  351. package/dist/commands/prompt/utils.d.ts +0 -12
  352. package/dist/commands/prompt/utils.d.ts.map +0 -1
  353. package/dist/commands/prompt/utils.js +0 -107
  354. package/dist/commands/reset/index.d.ts +0 -3
  355. package/dist/commands/reset/index.d.ts.map +0 -1
  356. package/dist/commands/reset/index.js +0 -25
  357. package/dist/commands/reset/types.d.ts +0 -1
  358. package/dist/commands/reset/types.d.ts.map +0 -1
  359. package/dist/commands/reset/types.js +0 -3
  360. package/dist/commands/save/index.d.ts +0 -3
  361. package/dist/commands/save/index.d.ts.map +0 -1
  362. package/dist/commands/save/index.js +0 -19
  363. package/dist/config.d.ts.map +0 -1
  364. package/dist/dedent.d.ts.map +0 -1
  365. package/dist/formatting.d.ts.map +0 -1
  366. package/dist/logger.d.ts.map +0 -1
  367. package/dist/mentions.d.ts +0 -14
  368. package/dist/mentions.d.ts.map +0 -1
  369. package/dist/parsing.d.ts.map +0 -1
  370. package/dist/prompts.d.ts +0 -10
  371. package/dist/prompts.d.ts.map +0 -1
  372. package/dist/repl-new.d.ts +0 -62
  373. package/dist/repl-new.d.ts.map +0 -1
  374. package/dist/skills.d.ts.map +0 -1
  375. package/dist/skills.js +0 -233
  376. package/dist/tui/autocomplete/path-provider.d.ts +0 -21
  377. package/dist/tui/autocomplete/path-provider.d.ts.map +0 -1
  378. package/dist/tui/autocomplete/path-provider.js +0 -164
  379. package/dist/version.d.ts.map +0 -1
  380. /package/dist/{dedent.d.ts → utils/dedent.d.ts} +0 -0
  381. /package/dist/{dedent.js → utils/dedent.js} +0 -0
  382. /package/dist/{formatting.d.ts → utils/formatting.d.ts} +0 -0
  383. /package/dist/{logger.d.ts → utils/logger.d.ts} +0 -0
  384. /package/dist/{parsing.d.ts → utils/parsing.d.ts} +0 -0
  385. /package/dist/{parsing.js → utils/parsing.js} +0 -0
  386. /package/dist/{version.d.ts → utils/version.d.ts} +0 -0
@@ -1,4 +1,4 @@
1
- import { isCtrlC, isEnter, isEscape, isNavigationKey, isTab, } from "../../terminal/keys.js";
1
+ import { isCtrlC, isCtrlG, isEnter, isEscape, isNavigationKey, isTab, } from "../../terminal/keys.js";
2
2
  import { getSegmenter } from "../../terminal/segmenter.js";
3
3
  import style from "../../terminal/style.js";
4
4
  import { visibleWidth } from "../utils.js";
@@ -174,6 +174,7 @@ export class Editor {
174
174
  // Custom key handlers for coding-agent
175
175
  onEscape;
176
176
  onRenderRequested;
177
+ onExternalEditor;
177
178
  constructor(theme) {
178
179
  // Default theme if none provided (backward compatibility)
179
180
  this.theme = theme || {
@@ -181,6 +182,12 @@ export class Editor {
181
182
  };
182
183
  this.borderColor = this.theme.borderColor;
183
184
  }
185
+ /**
186
+ * Get the current history (for testing purposes).
187
+ */
188
+ getHistory() {
189
+ return this.history;
190
+ }
184
191
  /**
185
192
  * Add a prompt to history for up/down arrow navigation.
186
193
  * Called after successful submission.
@@ -201,6 +208,18 @@ export class Editor {
201
208
  setAutocompleteProvider(provider) {
202
209
  this.autocompleteProvider = provider;
203
210
  }
211
+ async launchExternalEditor() {
212
+ if (!this.onExternalEditor)
213
+ return;
214
+ const currentContent = this.getText();
215
+ const result = await this.onExternalEditor(currentContent);
216
+ if (!result.aborted) {
217
+ this.setText(result.content);
218
+ if (this.onRenderRequested) {
219
+ this.onRenderRequested();
220
+ }
221
+ }
222
+ }
204
223
  isEditorEmpty() {
205
224
  return this.state.lines.length === 1 && this.state.lines[0] === "";
206
225
  }
@@ -311,193 +330,247 @@ export class Editor {
311
330
  return result;
312
331
  }
313
332
  handleInput(data) {
314
- // Handle bracketed paste mode
315
- // Start of paste: \x1b[200~
316
- // End of paste: \x1b[201~
317
- // Check if we're starting a bracketed paste
318
- if (data.includes("\x1b[200~")) {
319
- this.isInPaste = true;
320
- this.pasteBuffer = "";
321
- // Remove the start marker and keep the rest
322
- const cleanedData = data.replace("\x1b[200~", "");
323
- // Process the remaining data
324
- this.processInputData(cleanedData);
325
- return;
326
- }
327
- // If we're in a paste, buffer the data
328
- if (this.isInPaste) {
329
- // Append data to buffer first (end marker could be split across chunks)
330
- this.pasteBuffer += data;
331
- // Check if the accumulated buffer contains the end marker
332
- const endIndex = this.pasteBuffer.indexOf("\x1b[201~");
333
- if (endIndex !== -1) {
334
- // Extract content before the end marker
335
- const pasteContent = this.pasteBuffer.substring(0, endIndex);
336
- // Process the complete paste
337
- this.handlePaste(pasteContent);
338
- // Reset paste state
339
- this.isInPaste = false;
340
- // Process any remaining data after the end marker
341
- const remaining = this.pasteBuffer.substring(endIndex + 6); // 6 = length of \x1b[201~
342
- this.pasteBuffer = "";
343
- if (remaining.length > 0) {
344
- this.handleInput(remaining);
333
+ const pasteStart = "\x1b[200~";
334
+ const pasteEnd = "\x1b[201~";
335
+ let input = data;
336
+ while (input.length > 0) {
337
+ if (!this.isInPaste) {
338
+ const startIndex = input.indexOf(pasteStart);
339
+ if (startIndex === -1)
340
+ break;
341
+ const before = input.slice(0, startIndex);
342
+ if (before.length > 0) {
343
+ this.processInputData(before);
345
344
  }
346
- return;
345
+ this.isInPaste = true;
346
+ this.pasteBuffer = "";
347
+ input = input.slice(startIndex + pasteStart.length);
348
+ continue;
347
349
  }
348
- // Still accumulating, wait for more data
349
- return;
350
+ this.pasteBuffer += input;
351
+ const endIndex = this.pasteBuffer.indexOf(pasteEnd);
352
+ if (endIndex === -1)
353
+ return;
354
+ const pasteContent = this.pasteBuffer.slice(0, endIndex);
355
+ const remaining = this.pasteBuffer.slice(endIndex + pasteEnd.length);
356
+ this.handlePaste(pasteContent);
357
+ this.isInPaste = false;
358
+ this.pasteBuffer = "";
359
+ input = remaining;
350
360
  }
351
- // Intercept Escape key - but only if autocomplete is NOT active
352
- // (let parent handle escape for autocomplete cancellation)
353
- if (isEscape(data) && this.onEscape && !this.isShowingAutocomplete()) {
361
+ if (input.length === 0)
362
+ return;
363
+ if (isEscape(input) && this.onEscape && !this.isShowingAutocomplete()) {
354
364
  this.onEscape();
355
365
  return;
356
366
  }
357
- // Intercept Ctrl+C
358
- if (isCtrlC(data)) {
367
+ if (isCtrlC(input)) {
359
368
  return;
360
369
  }
361
- // Process regular input data
362
- this.processInputData(data);
370
+ this.processInputData(input);
363
371
  }
364
372
  processInputData(data) {
365
- // Handle special key combinations first
366
373
  // Ctrl+C - Exit (let parent handle this)
367
374
  if (data.charCodeAt(0) === 3) {
368
375
  return;
369
376
  }
370
- // Handle autocomplete special keys first (but don't block other input)
371
- if (this.isAutocompleting && this.autocompleteList) {
372
- // Escape - cancel autocomplete
373
- if (isEscape(data)) {
377
+ // Delegate to specialized handlers - return true if input was handled
378
+ if (this.handleAutocompleteInput(data))
379
+ return;
380
+ if (this.handleTabKey(data))
381
+ return;
382
+ if (this.handleExternalEditor(data))
383
+ return;
384
+ if (this.handleControlKey(data))
385
+ return;
386
+ if (this.handlePlainEnter(data))
387
+ return;
388
+ if (this.handleModifiedEnterKey(data))
389
+ return;
390
+ if (this.handleBackspaceKey(data))
391
+ return;
392
+ if (this.handleHomeEndKeys(data))
393
+ return;
394
+ if (this.handleForwardDeleteKey(data))
395
+ return;
396
+ if (this.handleWordNavigation(data))
397
+ return;
398
+ if (this.handleArrowKeys(data))
399
+ return;
400
+ // Default: insert printable character
401
+ if (data.charCodeAt(0) >= 32) {
402
+ this.insertCharacter(data);
403
+ }
404
+ }
405
+ handleAutocompleteInput(data) {
406
+ if (!this.isAutocompleting || !this.autocompleteList) {
407
+ return false;
408
+ }
409
+ // Escape - cancel autocomplete
410
+ if (isEscape(data)) {
411
+ this.cancelAutocomplete();
412
+ return true;
413
+ }
414
+ // Enter - apply selection
415
+ if (isEnter(data)) {
416
+ const selected = this.autocompleteList.getSelectedItem();
417
+ if (selected && this.autocompleteProvider) {
418
+ const result = this.autocompleteProvider.applyCompletion(this.state.lines, this.state.cursorLine, this.state.cursorCol, selected, this.autocompletePrefix);
419
+ this.state.lines = result.lines;
420
+ this.state.cursorLine = result.cursorLine;
421
+ this.state.cursorCol = result.cursorCol;
374
422
  this.cancelAutocomplete();
375
- return;
376
- }
377
- // Enter - apply selection
378
- if (isEnter(data)) {
379
- const selected = this.autocompleteList.getSelectedItem();
380
- if (selected && this.autocompleteProvider) {
381
- const result = this.autocompleteProvider.applyCompletion(this.state.lines, this.state.cursorLine, this.state.cursorCol, selected, this.autocompletePrefix);
382
- this.state.lines = result.lines;
383
- this.state.cursorLine = result.cursorLine;
384
- this.state.cursorCol = result.cursorCol;
385
- this.cancelAutocomplete();
386
- if (this.onChange) {
387
- this.onChange(this.getText());
388
- }
423
+ if (this.onChange) {
424
+ this.onChange(this.getText());
389
425
  }
390
- return;
391
426
  }
392
- // Navigation keys (arrows, Tab, Shift+Tab) - pass to autocomplete list
393
- if (isNavigationKey(data)) {
394
- this.autocompleteList.handleInput(data);
395
- return;
396
- }
397
- // For other keys (like regular typing), DON'T return here
398
- // Let them fall through to normal character handling
427
+ return true;
399
428
  }
400
- // Tab key - context-aware completion (but not when already autocompleting)
429
+ // Navigation keys (arrows, Tab, Shift+Tab) - pass to autocomplete list
430
+ if (isNavigationKey(data)) {
431
+ this.autocompleteList.handleInput(data);
432
+ return true;
433
+ }
434
+ return false;
435
+ }
436
+ handleTabKey(data) {
401
437
  if (isTab(data) && !this.isAutocompleting) {
402
438
  void this.handleTabCompletion();
403
- return;
439
+ return true;
440
+ }
441
+ return false;
442
+ }
443
+ handleExternalEditor(data) {
444
+ if (isCtrlG(data) && this.onExternalEditor) {
445
+ void this.launchExternalEditor();
446
+ return true;
404
447
  }
405
- // Continue with rest of input handling
448
+ return false;
449
+ }
450
+ handleControlKey(data) {
451
+ const charCode = data.charCodeAt(0);
406
452
  // Ctrl+K - Delete to end of line
407
- if (data.charCodeAt(0) === 11) {
453
+ if (charCode === 11) {
408
454
  this.deleteToEndOfLine();
455
+ return true;
409
456
  }
410
457
  // Ctrl+U - Delete to start of line
411
- else if (data.charCodeAt(0) === 21) {
458
+ if (charCode === 21) {
412
459
  this.deleteToStartOfLine();
460
+ return true;
413
461
  }
414
462
  // Ctrl+W - Delete word backwards
415
- else if (data.charCodeAt(0) === 23) {
463
+ if (charCode === 23) {
416
464
  this.deleteWordBackwards();
465
+ return true;
417
466
  }
418
467
  // Option/Alt+Backspace (e.g. Ghostty sends ESC + DEL)
419
- else if (data === "\x1b\x7f") {
468
+ if (data === "\x1b\x7f") {
420
469
  this.deleteWordBackwards();
470
+ return true;
421
471
  }
422
472
  // Ctrl+A - Move to start of line
423
- else if (data.charCodeAt(0) === 1) {
473
+ if (charCode === 1) {
424
474
  this.moveToLineStart();
475
+ return true;
425
476
  }
426
477
  // Ctrl+E - Move to end of line
427
- else if (data.charCodeAt(0) === 5) {
478
+ if (charCode === 5) {
428
479
  this.moveToLineEnd();
480
+ return true;
429
481
  }
482
+ return false;
483
+ }
484
+ handlePlainEnter(data) {
430
485
  // Plain Enter (char code 13 for CR) - create new line
431
- else if (data.charCodeAt(0) === 13 && data.length === 1) {
486
+ if (data.charCodeAt(0) === 13 && data.length === 1) {
432
487
  this.addNewLine();
488
+ return true;
433
489
  }
434
- // Modified Enter keys (Shift+Enter, Ctrl+Enter, etc.) - submit
435
- else if (this.isModifiedEnter(data)) {
436
- // If submit is disabled, do nothing
437
- if (this.disableSubmit) {
438
- return;
439
- }
440
- // Get text and substitute paste markers with actual content
441
- let result = this.state.lines.join("\n").trim();
442
- // Replace all [paste #N +xxx lines] or [paste #N xxx chars] markers with actual paste content
443
- for (const [pasteId, pasteContent] of this.pastes) {
444
- // Match formats: [paste #N], [paste #N +xxx lines], or [paste #N xxx chars]
445
- const markerRegex = new RegExp(`\\[paste #${pasteId}( (\\+\\d+ lines|\\d+ chars))?\\]`, "g");
446
- result = result.replace(markerRegex, pasteContent);
447
- }
448
- // Reset editor and clear pastes
449
- this.state = {
450
- lines: [""],
451
- cursorLine: 0,
452
- cursorCol: 0,
453
- };
454
- this.pastes.clear();
455
- this.pasteCounter = 0;
456
- this.historyIndex = -1; // Exit history browsing mode
457
- // Notify that editor is now empty
458
- if (this.onChange) {
459
- this.onChange("");
460
- }
461
- if (this.onSubmit) {
462
- this.onSubmit(result);
463
- }
490
+ return false;
491
+ }
492
+ handleModifiedEnterKey(data) {
493
+ if (!this.isModifiedEnter(data)) {
494
+ return false;
464
495
  }
465
- // Backspace
466
- else if (data.charCodeAt(0) === 127 || data.charCodeAt(0) === 8) {
496
+ // If submit is disabled, do nothing
497
+ if (this.disableSubmit) {
498
+ return true;
499
+ }
500
+ // Get text and substitute paste markers with actual content
501
+ let result = this.state.lines.join("\n").trim();
502
+ // Replace all [paste #N +xxx lines] or [paste #N xxx chars] markers with actual paste content
503
+ for (const [pasteId, pasteContent] of this.pastes) {
504
+ // Match formats: [paste #N], [paste #N +xxx lines], or [paste #N xxx chars]
505
+ const markerRegex = new RegExp(`\\[paste #${pasteId}( (\\+\\d+ lines|\\d+ chars))?\\]`, "g");
506
+ result = result.replace(markerRegex, pasteContent);
507
+ }
508
+ // Reset editor and clear pastes
509
+ this.state = {
510
+ lines: [""],
511
+ cursorLine: 0,
512
+ cursorCol: 0,
513
+ };
514
+ this.pastes.clear();
515
+ this.pasteCounter = 0;
516
+ this.historyIndex = -1; // Exit history browsing mode
517
+ this.addToHistory(result); // Save submitted text to history
518
+ // Notify that editor is now empty
519
+ if (this.onChange) {
520
+ this.onChange("");
521
+ }
522
+ if (this.onSubmit) {
523
+ this.onSubmit(result);
524
+ }
525
+ return true;
526
+ }
527
+ handleBackspaceKey(data) {
528
+ if (data.charCodeAt(0) === 127 || data.charCodeAt(0) === 8) {
467
529
  this.handleBackspace();
530
+ return true;
468
531
  }
469
- // Line navigation shortcuts (Home/End keys)
470
- else if (data === "\x1b[H" || data === "\x1b[1~" || data === "\x1b[7~") {
471
- // Home key
532
+ return false;
533
+ }
534
+ handleHomeEndKeys(data) {
535
+ // Home key: \x1b[H, \x1b[1~, or \x1b[7~
536
+ if (data === "\x1b[H" || data === "\x1b[1~" || data === "\x1b[7~") {
472
537
  this.moveToLineStart();
538
+ return true;
473
539
  }
474
- else if (data === "\x1b[F" || data === "\x1b[4~" || data === "\x1b[8~") {
475
- // End key
540
+ // End key: \x1b[F, \x1b[4~, or \x1b[8~
541
+ if (data === "\x1b[F" || data === "\x1b[4~" || data === "\x1b[8~") {
476
542
  this.moveToLineEnd();
543
+ return true;
477
544
  }
545
+ return false;
546
+ }
547
+ handleForwardDeleteKey(data) {
478
548
  // Forward delete (Fn+Backspace or Delete key)
479
- else if (data === "\x1b[3~") {
480
- // Delete key
549
+ if (data === "\x1b[3~") {
481
550
  this.handleForwardDelete();
551
+ return true;
482
552
  }
553
+ return false;
554
+ }
555
+ handleWordNavigation(data) {
483
556
  // Word navigation (Option/Alt + Arrow or Ctrl + Arrow)
484
557
  // Option+Left: \x1b[1;3D or \x1bb
485
558
  // Option+Right: \x1b[1;3C or \x1bf
486
559
  // Ctrl+Left: \x1b[1;5D
487
560
  // Ctrl+Right: \x1b[1;5C
488
- else if (data === "\x1b[1;3D" || data === "\x1bb" || data === "\x1b[1;5D") {
489
- // Word left
561
+ if (data === "\x1b[1;3D" || data === "\x1bb" || data === "\x1b[1;5D") {
490
562
  this.moveWordBackwards();
563
+ return true;
491
564
  }
492
- else if (data === "\x1b[1;3C" ||
493
- data === "\x1bf" ||
494
- data === "\x1b[1;5C") {
495
- // Word right
565
+ if (data === "\x1b[1;3C" || data === "\x1bf" || data === "\x1b[1;5C") {
496
566
  this.moveWordForwards();
567
+ return true;
497
568
  }
498
- // Arrow keys
499
- else if (data === "\x1b[A") {
500
- // Up - history navigation or cursor movement
569
+ return false;
570
+ }
571
+ handleArrowKeys(data) {
572
+ // Up arrow
573
+ if (data === "\x1b[A") {
501
574
  if (this.isEditorEmpty()) {
502
575
  this.navigateHistory(-1); // Start browsing history
503
576
  }
@@ -507,28 +580,29 @@ export class Editor {
507
580
  else {
508
581
  this.moveCursor(-1, 0); // Cursor movement (within text or history entry)
509
582
  }
583
+ return true;
510
584
  }
511
- else if (data === "\x1b[B") {
512
- // Down - history navigation or cursor movement
585
+ // Down arrow
586
+ if (data === "\x1b[B") {
513
587
  if (this.historyIndex > -1 && this.isOnLastVisualLine()) {
514
588
  this.navigateHistory(1); // Navigate to newer history entry or clear
515
589
  }
516
590
  else {
517
591
  this.moveCursor(1, 0); // Cursor movement (within text or history entry)
518
592
  }
593
+ return true;
519
594
  }
520
- else if (data === "\x1b[C") {
521
- // Right
595
+ // Right arrow
596
+ if (data === "\x1b[C") {
522
597
  this.moveCursor(0, 1);
598
+ return true;
523
599
  }
524
- else if (data === "\x1b[D") {
525
- // Left
600
+ // Left arrow
601
+ if (data === "\x1b[D") {
526
602
  this.moveCursor(0, -1);
603
+ return true;
527
604
  }
528
- // Regular characters (printable characters and unicode, but not control characters)
529
- else if (data.charCodeAt(0) >= 32) {
530
- this.insertCharacter(data);
531
- }
605
+ return false;
532
606
  }
533
607
  layoutText(contentWidth) {
534
608
  const layoutLines = [];
@@ -610,7 +684,7 @@ export class Editor {
610
684
  this.setTextInternal(text);
611
685
  }
612
686
  // All the editor methods from before...
613
- insertCharacter(char) {
687
+ insertCharacter(char, suppressAutocomplete = false) {
614
688
  this.historyIndex = -1; // Exit history browsing mode
615
689
  const line = this.state.lines[this.state.cursorLine] || "";
616
690
  const before = line.slice(0, this.state.cursorCol);
@@ -620,50 +694,16 @@ export class Editor {
620
694
  if (this.onChange) {
621
695
  this.onChange(this.getText());
622
696
  }
697
+ // Skip autocomplete triggering during paste operations
698
+ if (suppressAutocomplete) {
699
+ return;
700
+ }
623
701
  // Check if we should trigger or update autocomplete
624
702
  if (!this.isAutocompleting) {
625
- // Auto-trigger for "/" at the start of a line (slash commands)
626
- if (char === "/" && this.isAtStartOfMessage()) {
627
- void this.tryTriggerAutocomplete();
628
- }
629
- // Auto-trigger for "@" file reference (fuzzy search)
630
- else if (char === "@") {
703
+ if (this.autocompleteProvider) {
631
704
  const currentLine = this.state.lines[this.state.cursorLine] || "";
632
705
  const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
633
- // Only trigger if @ is after whitespace or at start of line
634
- const charBeforeAt = textBeforeCursor[textBeforeCursor.length - 2];
635
- if (textBeforeCursor.length === 1 ||
636
- charBeforeAt === " " ||
637
- charBeforeAt === "\t") {
638
- void this.tryTriggerAutocomplete();
639
- }
640
- }
641
- // Auto-trigger for "#" file search
642
- else if (char === "#") {
643
- const currentLine = this.state.lines[this.state.cursorLine] || "";
644
- const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
645
- // Only trigger if # is after whitespace or at start of line
646
- const charBeforeHash = textBeforeCursor[textBeforeCursor.length - 2];
647
- if (textBeforeCursor.length === 1 ||
648
- charBeforeHash === " " ||
649
- charBeforeHash === "\t") {
650
- void this.tryTriggerAutocomplete();
651
- }
652
- }
653
- // Also auto-trigger when typing letters in a slash command context
654
- else if (/[a-zA-Z0-9]/.test(char)) {
655
- const currentLine = this.state.lines[this.state.cursorLine] || "";
656
- const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
657
- // Check if we're in a slash command (with or without space for arguments)
658
- if (textBeforeCursor.trimStart().startsWith("/")) {
659
- void this.tryTriggerAutocomplete();
660
- }
661
- // Check if we're in an @ file reference context
662
- else if (textBeforeCursor.match(/(?:^|[\s])@[^\s]*$/)) {
663
- void this.tryTriggerAutocomplete();
664
- }
665
- // Check if we're in a # file search context
666
- else if (textBeforeCursor.match(/(?:^|[\s])#[^\s]*$/)) {
706
+ if (this.autocompleteProvider.matchesContext(textBeforeCursor)) {
667
707
  void this.tryTriggerAutocomplete();
668
708
  }
669
709
  }
@@ -696,7 +736,7 @@ export class Editor {
696
736
  ? `[paste #${pasteId} +${pastedLines.length} lines]`
697
737
  : `[paste #${pasteId} ${totalChars} chars]`;
698
738
  for (const char of marker) {
699
- this.insertCharacter(char);
739
+ this.insertCharacter(char, true);
700
740
  }
701
741
  return;
702
742
  }
@@ -704,7 +744,7 @@ export class Editor {
704
744
  // Single line - just insert each character
705
745
  const text = pastedLines[0] || "";
706
746
  for (const char of text) {
707
- this.insertCharacter(char);
747
+ this.insertCharacter(char, true);
708
748
  }
709
749
  return;
710
750
  }
@@ -779,12 +819,10 @@ export class Editor {
779
819
  if (this.isAutocompleting) {
780
820
  void this.updateAutocomplete();
781
821
  }
782
- else {
783
- // Check if we should trigger autocomplete after backspace in slash command context
822
+ else if (this.autocompleteProvider) {
784
823
  const currentLine = this.state.lines[this.state.cursorLine] || "";
785
824
  const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
786
- // Trigger autocomplete if we're in a slash command context (typing command name)
787
- if (textBeforeCursor.startsWith("/") && !textBeforeCursor.includes(" ")) {
825
+ if (this.autocompleteProvider.matchesContext(textBeforeCursor)) {
788
826
  void this.tryTriggerAutocomplete();
789
827
  }
790
828
  }
@@ -1063,13 +1101,6 @@ export class Editor {
1063
1101
  }
1064
1102
  this.state.cursorCol = newCol;
1065
1103
  }
1066
- // Helper method to check if cursor is at start of message (for slash command detection)
1067
- isAtStartOfMessage() {
1068
- const currentLine = this.state.lines[this.state.cursorLine] || "";
1069
- const beforeCursor = currentLine.slice(0, this.state.cursorCol);
1070
- // At start if line is empty, only contains whitespace, or is just "/"
1071
- return beforeCursor.trim() === "" || beforeCursor.trim() === "/";
1072
- }
1073
1104
  // Autocomplete methods
1074
1105
  async tryTriggerAutocomplete(explicitTab = false) {
1075
1106
  if (!this.autocompleteProvider)
@@ -1111,41 +1142,12 @@ export class Editor {
1111
1142
  if (beforeCursor.trimStart().startsWith("/")) {
1112
1143
  await this.handleSlashCommandCompletion();
1113
1144
  }
1114
- else {
1115
- await this.forceFileAutocomplete();
1116
- }
1117
1145
  }
1118
1146
  async handleSlashCommandCompletion() {
1119
1147
  // For now, fall back to regular autocomplete (slash commands)
1120
1148
  // This can be extended later to handle command-specific argument completion
1121
1149
  await this.tryTriggerAutocomplete(true);
1122
1150
  }
1123
- async forceFileAutocomplete() {
1124
- if (!this.autocompleteProvider)
1125
- return;
1126
- // Check if provider has the force method
1127
- const provider = this.autocompleteProvider;
1128
- if (!provider.getForceFileSuggestions) {
1129
- await this.tryTriggerAutocomplete(true);
1130
- return;
1131
- }
1132
- const suggestions = await provider.getForceFileSuggestions(this.state.lines, this.state.cursorLine, this.state.cursorCol);
1133
- if (suggestions && suggestions.items.length > 0) {
1134
- this.autocompletePrefix = suggestions.prefix;
1135
- if (this.autocompleteList) {
1136
- this.autocompleteList.updateItems(suggestions.items);
1137
- }
1138
- else {
1139
- this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList);
1140
- }
1141
- this.isAutocompleting = true;
1142
- // Request re-render to show autocomplete list
1143
- this.onRenderRequested?.();
1144
- }
1145
- else {
1146
- this.cancelAutocomplete();
1147
- }
1148
- }
1149
1151
  cancelAutocomplete() {
1150
1152
  this.isAutocompleting = false;
1151
1153
  this.autocompleteList = undefined;
@@ -1158,28 +1160,18 @@ export class Editor {
1158
1160
  isShowingAutocomplete() {
1159
1161
  return this.isAutocompleting;
1160
1162
  }
1163
+ wantsNavigationKeys() {
1164
+ return this.isAutocompleting;
1165
+ }
1161
1166
  async updateAutocomplete() {
1162
1167
  if (!this.isAutocompleting || !this.autocompleteProvider)
1163
1168
  return;
1164
1169
  // Check if the current text still matches our autocomplete context
1165
- // This prevents unnecessary updates when typing unrelated text
1166
1170
  const currentLine = this.state.lines[this.state.cursorLine] || "";
1167
1171
  const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
1168
- // If we're no longer in the context that triggered autocomplete, cancel it
1169
- // For slash commands, @ file attachments, and # file search, allow progressive typing
1170
- // For other file paths, check if we're still in the same path context
1171
- if (textBeforeCursor.startsWith("/") ||
1172
- textBeforeCursor.match(/(?:^|[\s])@[^\s]*$/) ||
1173
- textBeforeCursor.match(/(?:^|[\s])#[^\s]*$/)) {
1174
- // For slash commands, @ file attachments, and # file search,
1175
- // continue autocomplete as long as we're in the right context
1176
- }
1177
- else {
1178
- // For other file paths, check if we're still in the same path context
1179
- if (!textBeforeCursor.endsWith(this.autocompletePrefix)) {
1180
- this.cancelAutocomplete();
1181
- return;
1182
- }
1172
+ if (!this.autocompleteProvider.matchesContext(textBeforeCursor)) {
1173
+ this.cancelAutocomplete();
1174
+ return;
1183
1175
  }
1184
1176
  // Clear any existing debounce timer
1185
1177
  if (this.autocompleteDebounceTimer) {
@@ -1,24 +1,26 @@
1
- import type { LanguageModelUsage } from "ai";
2
1
  import type { AgentState } from "../../agent/index.ts";
3
2
  import type { ModelManager } from "../../models/manager.ts";
4
3
  import type { ProjectStatusData } from "../../repl/project-status.ts";
4
+ import type { TokenTracker } from "../../tokens/tracker.ts";
5
5
  import { type Component } from "../tui.ts";
6
6
  type State = {
7
7
  projectStatus: ProjectStatusData;
8
8
  currentContextWindow: number;
9
9
  contextWindow: number;
10
- usage?: LanguageModelUsage;
11
10
  agentState?: AgentState;
11
+ currentMode?: string;
12
12
  };
13
13
  export declare class FooterComponent implements Component {
14
14
  private modelManager;
15
+ private tokenTracker?;
15
16
  private state;
16
17
  private progressBar;
17
- private usage?;
18
18
  private agentState?;
19
- constructor(modelManager: ModelManager, state: State);
19
+ private currentMode;
20
+ constructor(modelManager: ModelManager, tokenTracker: TokenTracker | undefined, state: State);
20
21
  setState(state: State): void;
21
22
  resetState(): void;
23
+ getProjectStatus(): ProjectStatusData;
22
24
  render(width: number): string[];
23
25
  }
24
26
  export {};