@machina.ai/cell-cli 1.10.0-rc1 → 1.13.0-rc1

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 (813) hide show
  1. package/dist/index.js +5 -5
  2. package/dist/index.js.map +1 -1
  3. package/dist/package.json +14 -12
  4. package/dist/src/commands/extensions/disable.d.ts +1 -1
  5. package/dist/src/commands/extensions/disable.js +19 -8
  6. package/dist/src/commands/extensions/disable.js.map +1 -1
  7. package/dist/src/commands/extensions/enable.d.ts +1 -1
  8. package/dist/src/commands/extensions/enable.js +19 -9
  9. package/dist/src/commands/extensions/enable.js.map +1 -1
  10. package/dist/src/commands/extensions/examples/mcp-server/example.js +46 -0
  11. package/dist/src/commands/extensions/examples/mcp-server/example.js.map +1 -0
  12. package/dist/src/commands/extensions/install.d.ts +1 -0
  13. package/dist/src/commands/extensions/install.js +29 -4
  14. package/dist/src/commands/extensions/install.js.map +1 -1
  15. package/dist/src/commands/extensions/install.test.js +39 -19
  16. package/dist/src/commands/extensions/install.test.js.map +1 -1
  17. package/dist/src/commands/extensions/link.js +16 -4
  18. package/dist/src/commands/extensions/link.js.map +1 -1
  19. package/dist/src/commands/extensions/list.js +17 -6
  20. package/dist/src/commands/extensions/list.js.map +1 -1
  21. package/dist/src/commands/extensions/new.js +14 -20
  22. package/dist/src/commands/extensions/new.js.map +1 -1
  23. package/dist/src/commands/extensions/uninstall.js +16 -4
  24. package/dist/src/commands/extensions/uninstall.js.map +1 -1
  25. package/dist/src/commands/extensions/update.js +28 -23
  26. package/dist/src/commands/extensions/update.js.map +1 -1
  27. package/dist/src/commands/extensions/validate.d.ts +12 -0
  28. package/dist/src/commands/extensions/validate.js +83 -0
  29. package/dist/src/commands/extensions/validate.js.map +1 -0
  30. package/dist/src/commands/extensions/validate.test.d.ts +6 -0
  31. package/dist/src/commands/extensions/validate.test.js +93 -0
  32. package/dist/src/commands/extensions/validate.test.js.map +1 -0
  33. package/dist/src/commands/extensions.js +3 -0
  34. package/dist/src/commands/extensions.js.map +1 -1
  35. package/dist/src/commands/mcp/add.js +7 -4
  36. package/dist/src/commands/mcp/add.js.map +1 -1
  37. package/dist/src/commands/mcp/add.test.d.ts +6 -0
  38. package/dist/src/commands/mcp/add.test.js +247 -0
  39. package/dist/src/commands/mcp/add.test.js.map +1 -0
  40. package/dist/src/commands/mcp/list.js +18 -9
  41. package/dist/src/commands/mcp/list.js.map +1 -1
  42. package/dist/src/commands/mcp/list.test.d.ts +6 -0
  43. package/dist/src/commands/mcp/list.test.js +128 -0
  44. package/dist/src/commands/mcp/list.test.js.map +1 -0
  45. package/dist/src/commands/mcp/remove.js +3 -2
  46. package/dist/src/commands/mcp/remove.js.map +1 -1
  47. package/dist/src/commands/mcp/remove.test.d.ts +6 -0
  48. package/dist/src/commands/mcp/remove.test.js +175 -0
  49. package/dist/src/commands/mcp/remove.test.js.map +1 -0
  50. package/dist/src/commands/mcp.test.d.ts +6 -0
  51. package/dist/src/commands/mcp.test.js +62 -0
  52. package/dist/src/commands/mcp.test.js.map +1 -0
  53. package/dist/src/config/auth.js +0 -3
  54. package/dist/src/config/auth.js.map +1 -1
  55. package/dist/src/config/config.d.ts +6 -15
  56. package/dist/src/config/config.integration.test.d.ts +6 -0
  57. package/dist/src/config/config.integration.test.js +321 -0
  58. package/dist/src/config/config.integration.test.js.map +1 -0
  59. package/dist/src/config/config.js +85 -164
  60. package/dist/src/config/config.js.map +1 -1
  61. package/dist/src/config/config.test.d.ts +6 -0
  62. package/dist/src/config/config.test.js +1972 -0
  63. package/dist/src/config/config.test.js.map +1 -0
  64. package/dist/src/config/extension-manager.d.ts +63 -0
  65. package/dist/src/config/extension-manager.js +450 -0
  66. package/dist/src/config/extension-manager.js.map +1 -0
  67. package/dist/src/config/extension.d.ts +4 -61
  68. package/dist/src/config/extension.js +1 -538
  69. package/dist/src/config/extension.js.map +1 -1
  70. package/dist/src/config/extension.test.d.ts +6 -0
  71. package/dist/src/config/extension.test.js +1412 -0
  72. package/dist/src/config/extension.test.js.map +1 -0
  73. package/dist/src/config/extensions/consent.d.ts +38 -0
  74. package/dist/src/config/extensions/consent.js +123 -0
  75. package/dist/src/config/extensions/consent.js.map +1 -0
  76. package/dist/src/config/extensions/extensionEnablement.d.ts +2 -2
  77. package/dist/src/config/extensions/extensionEnablement.js +7 -5
  78. package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
  79. package/dist/src/config/extensions/extensionEnablement.test.js +31 -28
  80. package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
  81. package/dist/src/config/extensions/extensionSettings.d.ts +15 -0
  82. package/dist/src/config/extensions/extensionSettings.js +113 -0
  83. package/dist/src/config/extensions/extensionSettings.js.map +1 -0
  84. package/dist/src/config/extensions/extensionSettings.test.d.ts +6 -0
  85. package/dist/src/config/extensions/extensionSettings.test.js +254 -0
  86. package/dist/src/config/extensions/extensionSettings.test.js.map +1 -0
  87. package/dist/src/config/extensions/github.d.ts +18 -9
  88. package/dist/src/config/extensions/github.js +108 -36
  89. package/dist/src/config/extensions/github.js.map +1 -1
  90. package/dist/src/config/extensions/github.test.js +158 -164
  91. package/dist/src/config/extensions/github.test.js.map +1 -1
  92. package/dist/src/config/extensions/github_fetch.d.ts +1 -1
  93. package/dist/src/config/extensions/github_fetch.js +13 -1
  94. package/dist/src/config/extensions/github_fetch.js.map +1 -1
  95. package/dist/src/config/extensions/github_fetch.test.d.ts +6 -0
  96. package/dist/src/config/extensions/github_fetch.test.js +169 -0
  97. package/dist/src/config/extensions/github_fetch.test.js.map +1 -0
  98. package/dist/src/config/extensions/storage.d.ts +14 -0
  99. package/dist/src/config/extensions/storage.js +32 -0
  100. package/dist/src/config/extensions/storage.js.map +1 -0
  101. package/dist/src/config/extensions/update.d.ts +5 -4
  102. package/dist/src/config/extensions/update.js +41 -37
  103. package/dist/src/config/extensions/update.js.map +1 -1
  104. package/dist/src/config/extensions/update.test.js +72 -74
  105. package/dist/src/config/extensions/update.test.js.map +1 -1
  106. package/dist/src/config/extensions/variableSchema.d.ts +0 -4
  107. package/dist/src/config/extensions/variableSchema.js.map +1 -1
  108. package/dist/src/config/extensions/variables.d.ts +4 -0
  109. package/dist/src/config/extensions/variables.js +6 -0
  110. package/dist/src/config/extensions/variables.js.map +1 -1
  111. package/dist/src/config/keyBindings.d.ts +5 -1
  112. package/dist/src/config/keyBindings.js +34 -10
  113. package/dist/src/config/keyBindings.js.map +1 -1
  114. package/dist/src/config/keyBindings.test.js +17 -0
  115. package/dist/src/config/keyBindings.test.js.map +1 -1
  116. package/dist/src/config/policies/read-only.toml +56 -0
  117. package/dist/src/config/policies/write.toml +63 -0
  118. package/dist/src/config/policies/yolo.toml +31 -0
  119. package/dist/src/config/policy-engine.integration.test.js +41 -38
  120. package/dist/src/config/policy-engine.integration.test.js.map +1 -1
  121. package/dist/src/config/policy.d.ts +4 -3
  122. package/dist/src/config/policy.js +13 -142
  123. package/dist/src/config/policy.js.map +1 -1
  124. package/dist/src/config/sandboxConfig.d.ts +1 -2
  125. package/dist/src/config/sandboxConfig.js +7 -6
  126. package/dist/src/config/sandboxConfig.js.map +1 -1
  127. package/dist/src/config/settings.d.ts +2 -1
  128. package/dist/src/config/settings.js +59 -15
  129. package/dist/src/config/settings.js.map +1 -1
  130. package/dist/src/config/settings.test.d.ts +6 -0
  131. package/dist/src/config/settings.test.js +2000 -0
  132. package/dist/src/config/settings.test.js.map +1 -0
  133. package/dist/src/config/settingsSchema.d.ts +170 -28
  134. package/dist/src/config/settingsSchema.js +418 -27
  135. package/dist/src/config/settingsSchema.js.map +1 -1
  136. package/dist/src/config/settingsSchema.test.js +42 -1
  137. package/dist/src/config/settingsSchema.test.js.map +1 -1
  138. package/dist/src/config/trustedFolders.d.ts +1 -1
  139. package/dist/src/config/trustedFolders.js +4 -2
  140. package/dist/src/config/trustedFolders.js.map +1 -1
  141. package/dist/src/core/initializer.js +2 -1
  142. package/dist/src/core/initializer.js.map +1 -1
  143. package/dist/src/gemini.d.ts +1 -1
  144. package/dist/src/gemini.js +63 -27
  145. package/dist/src/gemini.js.map +1 -1
  146. package/dist/src/gemini.test.js +123 -34
  147. package/dist/src/gemini.test.js.map +1 -1
  148. package/dist/src/generated/git-commit.d.ts +2 -2
  149. package/dist/src/generated/git-commit.js +2 -2
  150. package/dist/src/nonInteractiveCli.d.ts +9 -1
  151. package/dist/src/nonInteractiveCli.js +205 -10
  152. package/dist/src/nonInteractiveCli.js.map +1 -1
  153. package/dist/src/nonInteractiveCli.test.d.ts +6 -0
  154. package/dist/src/nonInteractiveCli.test.js +984 -0
  155. package/dist/src/nonInteractiveCli.test.js.map +1 -0
  156. package/dist/src/nonInteractiveCliCommands.js +2 -2
  157. package/dist/src/nonInteractiveCliCommands.js.map +1 -1
  158. package/dist/src/services/BuiltinCommandLoader.js +4 -0
  159. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  160. package/dist/src/services/BuiltinCommandLoader.test.js +22 -0
  161. package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
  162. package/dist/src/services/CommandService.js +2 -1
  163. package/dist/src/services/CommandService.js.map +1 -1
  164. package/dist/src/services/FeedbackService.js +2 -2
  165. package/dist/src/services/FeedbackService.js.map +1 -1
  166. package/dist/src/services/FileCommandLoader.test.d.ts +6 -0
  167. package/dist/src/services/FileCommandLoader.test.js +971 -0
  168. package/dist/src/services/FileCommandLoader.test.js.map +1 -0
  169. package/dist/src/services/McpPromptLoader.js +2 -2
  170. package/dist/src/services/McpPromptLoader.js.map +1 -1
  171. package/dist/src/services/McpPromptLoader.test.js +4 -2
  172. package/dist/src/services/McpPromptLoader.test.js.map +1 -1
  173. package/dist/src/services/prompt-processors/argumentProcessor.test.d.ts +6 -0
  174. package/dist/src/services/prompt-processors/argumentProcessor.test.js +40 -0
  175. package/dist/src/services/prompt-processors/argumentProcessor.test.js.map +1 -0
  176. package/dist/src/services/prompt-processors/atFileProcessor.js +3 -2
  177. package/dist/src/services/prompt-processors/atFileProcessor.js.map +1 -1
  178. package/dist/src/services/prompt-processors/shellProcessor.test.d.ts +6 -0
  179. package/dist/src/services/prompt-processors/shellProcessor.test.js +482 -0
  180. package/dist/src/services/prompt-processors/shellProcessor.test.js.map +1 -0
  181. package/dist/src/test-utils/async.d.ts +9 -0
  182. package/dist/src/test-utils/async.js +29 -0
  183. package/dist/src/test-utils/async.js.map +1 -0
  184. package/dist/src/test-utils/createExtension.d.ts +3 -1
  185. package/dist/src/test-utils/createExtension.js +3 -3
  186. package/dist/src/test-utils/createExtension.js.map +1 -1
  187. package/dist/src/test-utils/render.d.ts +17 -2
  188. package/dist/src/test-utils/render.js +69 -4
  189. package/dist/src/test-utils/render.js.map +1 -1
  190. package/dist/src/test-utils/render.test.d.ts +6 -0
  191. package/dist/src/test-utils/render.test.js +79 -0
  192. package/dist/src/test-utils/render.test.js.map +1 -0
  193. package/dist/src/ui/App.test.d.ts +6 -0
  194. package/dist/src/ui/App.test.js +110 -0
  195. package/dist/src/ui/App.test.js.map +1 -0
  196. package/dist/src/ui/AppContainer.js +223 -92
  197. package/dist/src/ui/AppContainer.js.map +1 -1
  198. package/dist/src/ui/AppContainer.test.js +531 -147
  199. package/dist/src/ui/AppContainer.test.js.map +1 -1
  200. package/dist/src/ui/IdeIntegrationNudge.js +1 -1
  201. package/dist/src/ui/IdeIntegrationNudge.js.map +1 -1
  202. package/dist/src/ui/auth/ApiAuthDialog.d.ts +14 -0
  203. package/dist/src/ui/auth/ApiAuthDialog.js +26 -0
  204. package/dist/src/ui/auth/ApiAuthDialog.js.map +1 -0
  205. package/dist/src/ui/auth/ApiAuthDialog.test.d.ts +6 -0
  206. package/dist/src/ui/auth/ApiAuthDialog.test.js +91 -0
  207. package/dist/src/ui/auth/ApiAuthDialog.test.js.map +1 -0
  208. package/dist/src/ui/auth/AuthDialog.d.ts +1 -1
  209. package/dist/src/ui/auth/AuthDialog.js +9 -3
  210. package/dist/src/ui/auth/AuthDialog.js.map +1 -1
  211. package/dist/src/ui/auth/useAuth.d.ts +3 -1
  212. package/dist/src/ui/auth/useAuth.js +35 -4
  213. package/dist/src/ui/auth/useAuth.js.map +1 -1
  214. package/dist/src/ui/colors.js +3 -0
  215. package/dist/src/ui/colors.js.map +1 -1
  216. package/dist/src/ui/commands/aboutCommand.js +1 -1
  217. package/dist/src/ui/commands/aboutCommand.test.d.ts +6 -0
  218. package/dist/src/ui/commands/aboutCommand.test.js +130 -0
  219. package/dist/src/ui/commands/aboutCommand.test.js.map +1 -0
  220. package/dist/src/ui/commands/authCommand.js +1 -1
  221. package/dist/src/ui/commands/authCommand.test.d.ts +6 -0
  222. package/dist/src/ui/commands/authCommand.test.js +30 -0
  223. package/dist/src/ui/commands/authCommand.test.js.map +1 -0
  224. package/dist/src/ui/commands/bugCommand.js +1 -1
  225. package/dist/src/ui/commands/bugCommand.test.d.ts +6 -0
  226. package/dist/src/ui/commands/bugCommand.test.js +105 -0
  227. package/dist/src/ui/commands/bugCommand.test.js.map +1 -0
  228. package/dist/src/ui/commands/chatCommand.js +1 -1
  229. package/dist/src/ui/commands/chatCommand.js.map +1 -1
  230. package/dist/src/ui/commands/chatCommand.test.d.ts +6 -0
  231. package/dist/src/ui/commands/chatCommand.test.js +555 -0
  232. package/dist/src/ui/commands/chatCommand.test.js.map +1 -0
  233. package/dist/src/ui/commands/clearCommand.js +1 -1
  234. package/dist/src/ui/commands/clearCommand.test.d.ts +6 -0
  235. package/dist/src/ui/commands/clearCommand.test.js +76 -0
  236. package/dist/src/ui/commands/clearCommand.test.js.map +1 -0
  237. package/dist/src/ui/commands/compressCommand.js +1 -1
  238. package/dist/src/ui/commands/compressCommand.js.map +1 -1
  239. package/dist/src/ui/commands/compressCommand.test.d.ts +6 -0
  240. package/dist/src/ui/commands/compressCommand.test.js +98 -0
  241. package/dist/src/ui/commands/compressCommand.test.js.map +1 -0
  242. package/dist/src/ui/commands/copyCommand.js +2 -1
  243. package/dist/src/ui/commands/copyCommand.js.map +1 -1
  244. package/dist/src/ui/commands/copyCommand.test.d.ts +6 -0
  245. package/dist/src/ui/commands/copyCommand.test.js +242 -0
  246. package/dist/src/ui/commands/copyCommand.test.js.map +1 -0
  247. package/dist/src/ui/commands/corgiCommand.js +1 -1
  248. package/dist/src/ui/commands/corgiCommand.js.map +1 -1
  249. package/dist/src/ui/commands/corgiCommand.test.d.ts +6 -0
  250. package/dist/src/ui/commands/corgiCommand.test.js +28 -0
  251. package/dist/src/ui/commands/corgiCommand.test.js.map +1 -0
  252. package/dist/src/ui/commands/directoryCommand.js +1 -1
  253. package/dist/src/ui/commands/directoryCommand.js.map +1 -1
  254. package/dist/src/ui/commands/directoryCommand.test.d.ts +6 -0
  255. package/dist/src/ui/commands/directoryCommand.test.js +144 -0
  256. package/dist/src/ui/commands/directoryCommand.test.js.map +1 -0
  257. package/dist/src/ui/commands/docsCommand.js +1 -1
  258. package/dist/src/ui/commands/docsCommand.test.d.ts +6 -0
  259. package/dist/src/ui/commands/docsCommand.test.js +72 -0
  260. package/dist/src/ui/commands/docsCommand.test.js.map +1 -0
  261. package/dist/src/ui/commands/editorCommand.js +1 -1
  262. package/dist/src/ui/commands/editorCommand.test.d.ts +6 -0
  263. package/dist/src/ui/commands/editorCommand.test.js +27 -0
  264. package/dist/src/ui/commands/editorCommand.test.js.map +1 -0
  265. package/dist/src/ui/commands/extensionsCommand.js +64 -11
  266. package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
  267. package/dist/src/ui/commands/extensionsCommand.test.d.ts +6 -0
  268. package/dist/src/ui/commands/extensionsCommand.test.js +315 -0
  269. package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -0
  270. package/dist/src/ui/commands/helpCommand.js +1 -1
  271. package/dist/src/ui/commands/helpCommand.test.d.ts +6 -0
  272. package/dist/src/ui/commands/helpCommand.test.js +42 -0
  273. package/dist/src/ui/commands/helpCommand.test.js.map +1 -0
  274. package/dist/src/ui/commands/ideCommand.js +6 -6
  275. package/dist/src/ui/commands/ideCommand.test.d.ts +6 -0
  276. package/dist/src/ui/commands/ideCommand.test.js +205 -0
  277. package/dist/src/ui/commands/ideCommand.test.js.map +1 -0
  278. package/dist/src/ui/commands/initCommand.js +1 -1
  279. package/dist/src/ui/commands/initCommand.js.map +1 -1
  280. package/dist/src/ui/commands/initCommand.test.d.ts +6 -0
  281. package/dist/src/ui/commands/initCommand.test.js +80 -0
  282. package/dist/src/ui/commands/initCommand.test.js.map +1 -0
  283. package/dist/src/ui/commands/mcpCommand.js +110 -100
  284. package/dist/src/ui/commands/mcpCommand.js.map +1 -1
  285. package/dist/src/ui/commands/mcpCommand.test.d.ts +6 -0
  286. package/dist/src/ui/commands/mcpCommand.test.js +152 -0
  287. package/dist/src/ui/commands/mcpCommand.test.js.map +1 -0
  288. package/dist/src/ui/commands/memoryCommand.js +6 -6
  289. package/dist/src/ui/commands/memoryCommand.js.map +1 -1
  290. package/dist/src/ui/commands/memoryCommand.test.d.ts +6 -0
  291. package/dist/src/ui/commands/memoryCommand.test.js +268 -0
  292. package/dist/src/ui/commands/memoryCommand.test.js.map +1 -0
  293. package/dist/src/ui/commands/policiesCommand.d.ts +7 -0
  294. package/dist/src/ui/commands/policiesCommand.js +59 -0
  295. package/dist/src/ui/commands/policiesCommand.js.map +1 -0
  296. package/dist/src/ui/commands/policiesCommand.test.d.ts +6 -0
  297. package/dist/src/ui/commands/policiesCommand.test.js +83 -0
  298. package/dist/src/ui/commands/policiesCommand.test.js.map +1 -0
  299. package/dist/src/ui/commands/privacyCommand.js +1 -1
  300. package/dist/src/ui/commands/privacyCommand.test.d.ts +6 -0
  301. package/dist/src/ui/commands/privacyCommand.test.js +32 -0
  302. package/dist/src/ui/commands/privacyCommand.test.js.map +1 -0
  303. package/dist/src/ui/commands/quitCommand.js +1 -1
  304. package/dist/src/ui/commands/quitCommand.test.d.ts +6 -0
  305. package/dist/src/ui/commands/quitCommand.test.js +50 -0
  306. package/dist/src/ui/commands/quitCommand.test.js.map +1 -0
  307. package/dist/src/ui/commands/restoreCommand.test.d.ts +6 -0
  308. package/dist/src/ui/commands/restoreCommand.test.js +190 -0
  309. package/dist/src/ui/commands/restoreCommand.test.js.map +1 -0
  310. package/dist/src/ui/commands/settingsCommand.test.d.ts +6 -0
  311. package/dist/src/ui/commands/settingsCommand.test.js +30 -0
  312. package/dist/src/ui/commands/settingsCommand.test.js.map +1 -0
  313. package/dist/src/ui/commands/setupGithubCommand.js +4 -3
  314. package/dist/src/ui/commands/setupGithubCommand.js.map +1 -1
  315. package/dist/src/ui/commands/setupGithubCommand.test.js +1 -2
  316. package/dist/src/ui/commands/setupGithubCommand.test.js.map +1 -1
  317. package/dist/src/ui/commands/statsCommand.js +3 -3
  318. package/dist/src/ui/commands/statsCommand.js.map +1 -1
  319. package/dist/src/ui/commands/statsCommand.test.d.ts +6 -0
  320. package/dist/src/ui/commands/statsCommand.test.js +53 -0
  321. package/dist/src/ui/commands/statsCommand.test.js.map +1 -0
  322. package/dist/src/ui/commands/terminalSetupCommand.test.d.ts +6 -0
  323. package/dist/src/ui/commands/terminalSetupCommand.test.js +66 -0
  324. package/dist/src/ui/commands/terminalSetupCommand.test.js.map +1 -0
  325. package/dist/src/ui/commands/themeCommand.js +1 -1
  326. package/dist/src/ui/commands/themeCommand.test.d.ts +6 -0
  327. package/dist/src/ui/commands/themeCommand.test.js +32 -0
  328. package/dist/src/ui/commands/themeCommand.test.js.map +1 -0
  329. package/dist/src/ui/commands/toolsCommand.js +1 -1
  330. package/dist/src/ui/commands/toolsCommand.test.d.ts +6 -0
  331. package/dist/src/ui/commands/toolsCommand.test.js +100 -0
  332. package/dist/src/ui/commands/toolsCommand.test.js.map +1 -0
  333. package/dist/src/ui/commands/types.d.ts +1 -0
  334. package/dist/src/ui/commands/vimCommand.js +1 -1
  335. package/dist/src/ui/components/AnsiOutput.test.js +1 -1
  336. package/dist/src/ui/components/AnsiOutput.test.js.map +1 -1
  337. package/dist/src/ui/components/AsciiArt.d.ts +3 -3
  338. package/dist/src/ui/components/AsciiArt.js +3 -3
  339. package/dist/src/ui/components/Composer.js +6 -4
  340. package/dist/src/ui/components/Composer.js.map +1 -1
  341. package/dist/src/ui/components/Composer.test.js +21 -3
  342. package/dist/src/ui/components/Composer.test.js.map +1 -1
  343. package/dist/src/ui/components/ConfigInitDisplay.js +4 -6
  344. package/dist/src/ui/components/ConfigInitDisplay.js.map +1 -1
  345. package/dist/src/ui/components/ConsentPrompt.test.js +18 -8
  346. package/dist/src/ui/components/ConsentPrompt.test.js.map +1 -1
  347. package/dist/src/ui/components/ConsoleSummaryDisplay.js +1 -1
  348. package/dist/src/ui/components/ConsoleSummaryDisplay.js.map +1 -1
  349. package/dist/src/ui/components/ContextSummaryDisplay.d.ts +0 -1
  350. package/dist/src/ui/components/ContextSummaryDisplay.js +2 -12
  351. package/dist/src/ui/components/ContextSummaryDisplay.js.map +1 -1
  352. package/dist/src/ui/components/ContextSummaryDisplay.test.d.ts +6 -0
  353. package/dist/src/ui/components/ContextSummaryDisplay.test.js +71 -0
  354. package/dist/src/ui/components/ContextSummaryDisplay.test.js.map +1 -0
  355. package/dist/src/ui/components/DetailedMessagesDisplay.js +1 -1
  356. package/dist/src/ui/components/DetailedMessagesDisplay.js.map +1 -1
  357. package/dist/src/ui/components/DialogManager.js +5 -5
  358. package/dist/src/ui/components/DialogManager.js.map +1 -1
  359. package/dist/src/ui/components/EditorSettingsDialog.js +1 -1
  360. package/dist/src/ui/components/EditorSettingsDialog.js.map +1 -1
  361. package/dist/src/ui/components/FolderTrustDialog.test.js +8 -3
  362. package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
  363. package/dist/src/ui/components/Footer.js +4 -3
  364. package/dist/src/ui/components/Footer.js.map +1 -1
  365. package/dist/src/ui/components/Footer.test.d.ts +6 -0
  366. package/dist/src/ui/components/Footer.test.js +314 -0
  367. package/dist/src/ui/components/Footer.test.js.map +1 -0
  368. package/dist/src/ui/components/Header.test.js +13 -5
  369. package/dist/src/ui/components/Header.test.js.map +1 -1
  370. package/dist/src/ui/components/Help.test.js +5 -4
  371. package/dist/src/ui/components/Help.test.js.map +1 -1
  372. package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
  373. package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
  374. package/dist/src/ui/components/InputPrompt.d.ts +4 -0
  375. package/dist/src/ui/components/InputPrompt.js +80 -12
  376. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  377. package/dist/src/ui/components/InputPrompt.test.d.ts +6 -0
  378. package/dist/src/ui/components/InputPrompt.test.js +1786 -0
  379. package/dist/src/ui/components/InputPrompt.test.js.map +1 -0
  380. package/dist/src/ui/components/LoadingIndicator.js +2 -2
  381. package/dist/src/ui/components/LoadingIndicator.js.map +1 -1
  382. package/dist/src/ui/components/LoadingIndicator.test.js +28 -15
  383. package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -1
  384. package/dist/src/ui/components/LoopDetectionConfirmation.js +1 -1
  385. package/dist/src/ui/components/LoopDetectionConfirmation.js.map +1 -1
  386. package/dist/src/ui/components/LoopDetectionConfirmation.test.js +2 -2
  387. package/dist/src/ui/components/LoopDetectionConfirmation.test.js.map +1 -1
  388. package/dist/src/ui/components/MainContent.js +15 -4
  389. package/dist/src/ui/components/MainContent.js.map +1 -1
  390. package/dist/src/ui/components/ModelDialog.js +1 -1
  391. package/dist/src/ui/components/ModelDialog.js.map +1 -1
  392. package/dist/src/ui/components/ModelDialog.test.js +23 -13
  393. package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
  394. package/dist/src/ui/components/ModelStatsDisplay.test.d.ts +6 -0
  395. package/dist/src/ui/components/ModelStatsDisplay.test.js +285 -0
  396. package/dist/src/ui/components/ModelStatsDisplay.test.js.map +1 -0
  397. package/dist/src/ui/components/Notifications.js +38 -5
  398. package/dist/src/ui/components/Notifications.js.map +1 -1
  399. package/dist/src/ui/components/PermissionsModifyTrustDialog.js +22 -18
  400. package/dist/src/ui/components/PermissionsModifyTrustDialog.js.map +1 -1
  401. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +12 -4
  402. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
  403. package/dist/src/ui/components/PrepareLabel.test.js +14 -8
  404. package/dist/src/ui/components/PrepareLabel.test.js.map +1 -1
  405. package/dist/src/ui/components/ProQuotaDialog.test.js +14 -6
  406. package/dist/src/ui/components/ProQuotaDialog.test.js.map +1 -1
  407. package/dist/src/ui/components/QueuedMessageDisplay.js +3 -3
  408. package/dist/src/ui/components/QueuedMessageDisplay.js.map +1 -1
  409. package/dist/src/ui/components/QueuedMessageDisplay.test.js +15 -6
  410. package/dist/src/ui/components/QueuedMessageDisplay.test.js.map +1 -1
  411. package/dist/src/ui/components/RawMarkdownIndicator.d.ts +7 -0
  412. package/dist/src/ui/components/RawMarkdownIndicator.js +8 -0
  413. package/dist/src/ui/components/RawMarkdownIndicator.js.map +1 -0
  414. package/dist/src/ui/components/SessionSummaryDisplay.test.d.ts +6 -0
  415. package/dist/src/ui/components/SessionSummaryDisplay.test.js +74 -0
  416. package/dist/src/ui/components/SessionSummaryDisplay.test.js.map +1 -0
  417. package/dist/src/ui/components/SettingsDialog.js +43 -35
  418. package/dist/src/ui/components/SettingsDialog.js.map +1 -1
  419. package/dist/src/ui/components/SettingsDialog.test.js +554 -545
  420. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  421. package/dist/src/ui/components/ShellConfirmationDialog.js +1 -1
  422. package/dist/src/ui/components/ShellConfirmationDialog.js.map +1 -1
  423. package/dist/src/ui/components/ShellConfirmationDialog.test.js +2 -2
  424. package/dist/src/ui/components/ShellConfirmationDialog.test.js.map +1 -1
  425. package/dist/src/ui/components/StatsDisplay.test.d.ts +6 -0
  426. package/dist/src/ui/components/StatsDisplay.test.js +351 -0
  427. package/dist/src/ui/components/StatsDisplay.test.js.map +1 -0
  428. package/dist/src/ui/components/SuggestionsDisplay.js +1 -1
  429. package/dist/src/ui/components/SuggestionsDisplay.js.map +1 -1
  430. package/dist/src/ui/components/ThemeDialog.d.ts +4 -2
  431. package/dist/src/ui/components/ThemeDialog.js +3 -3
  432. package/dist/src/ui/components/ThemeDialog.js.map +1 -1
  433. package/dist/src/ui/components/ThemeDialog.test.js +14 -1
  434. package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
  435. package/dist/src/ui/components/ToolStatsDisplay.test.d.ts +6 -0
  436. package/dist/src/ui/components/ToolStatsDisplay.test.js +227 -0
  437. package/dist/src/ui/components/ToolStatsDisplay.test.js.map +1 -0
  438. package/dist/src/ui/components/messages/CompressionMessage.test.js +25 -17
  439. package/dist/src/ui/components/messages/CompressionMessage.test.js.map +1 -1
  440. package/dist/src/ui/components/messages/DiffRenderer.test.js +1 -1
  441. package/dist/src/ui/components/messages/DiffRenderer.test.js.map +1 -1
  442. package/dist/src/ui/components/messages/GeminiMessage.js +3 -1
  443. package/dist/src/ui/components/messages/GeminiMessage.js.map +1 -1
  444. package/dist/src/ui/components/messages/GeminiMessage.test.d.ts +6 -0
  445. package/dist/src/ui/components/messages/GeminiMessage.test.js +35 -0
  446. package/dist/src/ui/components/messages/GeminiMessage.test.js.map +1 -0
  447. package/dist/src/ui/components/messages/GeminiMessageContent.js +3 -1
  448. package/dist/src/ui/components/messages/GeminiMessageContent.js.map +1 -1
  449. package/dist/src/ui/components/messages/InfoMessage.js +1 -1
  450. package/dist/src/ui/components/messages/InfoMessage.js.map +1 -1
  451. package/dist/src/ui/components/messages/Todo.d.ts +7 -0
  452. package/dist/src/ui/components/messages/Todo.js +91 -0
  453. package/dist/src/ui/components/messages/Todo.js.map +1 -0
  454. package/dist/src/ui/components/messages/Todo.test.d.ts +6 -0
  455. package/dist/src/ui/components/messages/Todo.test.js +114 -0
  456. package/dist/src/ui/components/messages/Todo.test.js.map +1 -0
  457. package/dist/src/ui/components/messages/ToolConfirmationMessage.js +1 -1
  458. package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
  459. package/dist/src/ui/components/messages/ToolGroupMessage.js +1 -1
  460. package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
  461. package/dist/src/ui/components/messages/ToolGroupMessage.test.js +29 -15
  462. package/dist/src/ui/components/messages/ToolGroupMessage.test.js.map +1 -1
  463. package/dist/src/ui/components/messages/ToolMessage.js +8 -3
  464. package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
  465. package/dist/src/ui/components/messages/ToolMessage.test.js +2 -2
  466. package/dist/src/ui/components/messages/ToolMessage.test.js.map +1 -1
  467. package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.d.ts +6 -0
  468. package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.js +30 -0
  469. package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.js.map +1 -0
  470. package/dist/src/ui/components/messages/UserShellMessage.js +1 -1
  471. package/dist/src/ui/components/messages/UserShellMessage.js.map +1 -1
  472. package/dist/src/ui/components/messages/WarningMessage.js +2 -2
  473. package/dist/src/ui/components/messages/WarningMessage.js.map +1 -1
  474. package/dist/src/ui/components/shared/BaseSelectionList.test.js +34 -25
  475. package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
  476. package/dist/src/ui/components/shared/MaxSizedBox.test.js +43 -22
  477. package/dist/src/ui/components/shared/MaxSizedBox.test.js.map +1 -1
  478. package/dist/src/ui/components/shared/TextInput.d.ts +15 -0
  479. package/dist/src/ui/components/shared/TextInput.js +38 -0
  480. package/dist/src/ui/components/shared/TextInput.js.map +1 -0
  481. package/dist/src/ui/components/shared/TextInput.test.d.ts +6 -0
  482. package/dist/src/ui/components/shared/TextInput.test.js +242 -0
  483. package/dist/src/ui/components/shared/TextInput.test.js.map +1 -0
  484. package/dist/src/ui/components/shared/text-buffer.d.ts +9 -2
  485. package/dist/src/ui/components/shared/text-buffer.js +52 -14
  486. package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
  487. package/dist/src/ui/components/shared/text-buffer.test.d.ts +6 -0
  488. package/dist/src/ui/components/shared/text-buffer.test.js +1761 -0
  489. package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -0
  490. package/dist/src/ui/components/shared/vim-buffer-actions.test.d.ts +6 -0
  491. package/dist/src/ui/components/shared/vim-buffer-actions.test.js +951 -0
  492. package/dist/src/ui/components/shared/vim-buffer-actions.test.js.map +1 -0
  493. package/dist/src/ui/components/views/ChatList.test.js +7 -4
  494. package/dist/src/ui/components/views/ChatList.test.js.map +1 -1
  495. package/dist/src/ui/components/views/ExtensionsList.d.ts +7 -1
  496. package/dist/src/ui/components/views/ExtensionsList.js +12 -15
  497. package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
  498. package/dist/src/ui/components/views/ExtensionsList.test.js +43 -29
  499. package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
  500. package/dist/src/ui/components/views/McpStatus.d.ts +0 -1
  501. package/dist/src/ui/components/views/McpStatus.js +4 -4
  502. package/dist/src/ui/components/views/McpStatus.js.map +1 -1
  503. package/dist/src/ui/components/views/McpStatus.test.js +23 -17
  504. package/dist/src/ui/components/views/McpStatus.test.js.map +1 -1
  505. package/dist/src/ui/components/views/ToolsList.test.js +4 -4
  506. package/dist/src/ui/components/views/ToolsList.test.js.map +1 -1
  507. package/dist/src/ui/contexts/KeypressContext.d.ts +4 -2
  508. package/dist/src/ui/contexts/KeypressContext.js +635 -439
  509. package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
  510. package/dist/src/ui/contexts/KeypressContext.test.js +634 -515
  511. package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
  512. package/dist/src/ui/contexts/MouseContext.d.ts +21 -0
  513. package/dist/src/ui/contexts/MouseContext.js +89 -0
  514. package/dist/src/ui/contexts/MouseContext.js.map +1 -0
  515. package/dist/src/ui/contexts/MouseContext.test.d.ts +6 -0
  516. package/dist/src/ui/contexts/MouseContext.test.js +164 -0
  517. package/dist/src/ui/contexts/MouseContext.test.js.map +1 -0
  518. package/dist/src/ui/contexts/SessionContext.test.d.ts +6 -0
  519. package/dist/src/ui/contexts/SessionContext.test.js +195 -0
  520. package/dist/src/ui/contexts/SessionContext.test.js.map +1 -0
  521. package/dist/src/ui/contexts/UIActionsContext.d.ts +7 -4
  522. package/dist/src/ui/contexts/UIStateContext.d.ts +5 -3
  523. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  524. package/dist/src/ui/hooks/atCommandProcessor.js +33 -11
  525. package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
  526. package/dist/src/ui/hooks/atCommandProcessor.test.js +163 -64
  527. package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
  528. package/dist/src/ui/hooks/shellCommandProcessor.js +0 -1
  529. package/dist/src/ui/hooks/shellCommandProcessor.js.map +1 -1
  530. package/dist/src/ui/hooks/shellCommandProcessor.test.js +64 -35
  531. package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -1
  532. package/dist/src/ui/hooks/slashCommandProcessor.js +2 -0
  533. package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
  534. package/dist/src/ui/hooks/slashCommandProcessor.test.d.ts +6 -0
  535. package/dist/src/ui/hooks/slashCommandProcessor.test.js +807 -0
  536. package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -0
  537. package/dist/src/ui/hooks/useAtCompletion.js +2 -2
  538. package/dist/src/ui/hooks/useAtCompletion.js.map +1 -1
  539. package/dist/src/ui/hooks/useAtCompletion.test.d.ts +6 -0
  540. package/dist/src/ui/hooks/useAtCompletion.test.js +396 -0
  541. package/dist/src/ui/hooks/useAtCompletion.test.js.map +1 -0
  542. package/dist/src/ui/hooks/useAutoAcceptIndicator.js +10 -0
  543. package/dist/src/ui/hooks/useAutoAcceptIndicator.js.map +1 -1
  544. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +32 -2
  545. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
  546. package/dist/src/ui/hooks/useCommandCompletion.d.ts +1 -1
  547. package/dist/src/ui/hooks/useCommandCompletion.js +5 -3
  548. package/dist/src/ui/hooks/useCommandCompletion.js.map +1 -1
  549. package/dist/src/ui/hooks/useCommandCompletion.test.d.ts +6 -0
  550. package/dist/src/ui/hooks/useCommandCompletion.test.js +377 -0
  551. package/dist/src/ui/hooks/useCommandCompletion.test.js.map +1 -0
  552. package/dist/src/ui/hooks/useConsoleMessages.test.d.ts +6 -0
  553. package/dist/src/ui/hooks/useConsoleMessages.test.js +127 -0
  554. package/dist/src/ui/hooks/useConsoleMessages.test.js.map +1 -0
  555. package/dist/src/ui/hooks/useEditorSettings.test.js +40 -34
  556. package/dist/src/ui/hooks/useEditorSettings.test.js.map +1 -1
  557. package/dist/src/ui/hooks/useExtensionUpdates.d.ts +14 -4
  558. package/dist/src/ui/hooks/useExtensionUpdates.js +18 -11
  559. package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
  560. package/dist/src/ui/hooks/useExtensionUpdates.test.js +52 -35
  561. package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
  562. package/dist/src/ui/hooks/useFlickerDetector.test.js +9 -5
  563. package/dist/src/ui/hooks/useFlickerDetector.test.js.map +1 -1
  564. package/dist/src/ui/hooks/useFocus.test.d.ts +6 -0
  565. package/dist/src/ui/hooks/useFocus.test.js +131 -0
  566. package/dist/src/ui/hooks/useFocus.test.js.map +1 -0
  567. package/dist/src/ui/hooks/useFolderTrust.test.d.ts +6 -0
  568. package/dist/src/ui/hooks/useFolderTrust.test.js +188 -0
  569. package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -0
  570. package/dist/src/ui/hooks/useGeminiStream.js +119 -74
  571. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  572. package/dist/src/ui/hooks/useGeminiStream.test.d.ts +6 -0
  573. package/dist/src/ui/hooks/useGeminiStream.test.js +1820 -0
  574. package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -0
  575. package/dist/src/ui/hooks/useGitBranchName.js +4 -0
  576. package/dist/src/ui/hooks/useGitBranchName.js.map +1 -1
  577. package/dist/src/ui/hooks/useGitBranchName.test.js +46 -34
  578. package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
  579. package/dist/src/ui/hooks/useHistoryManager.test.js +2 -1
  580. package/dist/src/ui/hooks/useHistoryManager.test.js.map +1 -1
  581. package/dist/src/ui/hooks/useIdeTrustListener.test.js +40 -9
  582. package/dist/src/ui/hooks/useIdeTrustListener.test.js.map +1 -1
  583. package/dist/src/ui/hooks/useInputHistory.test.js +2 -1
  584. package/dist/src/ui/hooks/useInputHistory.test.js.map +1 -1
  585. package/dist/src/ui/hooks/useInputHistoryStore.js +2 -1
  586. package/dist/src/ui/hooks/useInputHistoryStore.js.map +1 -1
  587. package/dist/src/ui/hooks/useInputHistoryStore.test.js +2 -1
  588. package/dist/src/ui/hooks/useInputHistoryStore.test.js.map +1 -1
  589. package/dist/src/ui/hooks/useKeypress.test.d.ts +6 -0
  590. package/dist/src/ui/hooks/useKeypress.test.js +223 -0
  591. package/dist/src/ui/hooks/useKeypress.test.js.map +1 -0
  592. package/dist/src/ui/hooks/useLoadingIndicator.test.js +29 -6
  593. package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
  594. package/dist/src/ui/hooks/useMemoryMonitor.test.js +10 -5
  595. package/dist/src/ui/hooks/useMemoryMonitor.test.js.map +1 -1
  596. package/dist/src/ui/hooks/useMessageQueue.d.ts +1 -0
  597. package/dist/src/ui/hooks/useMessageQueue.js +14 -0
  598. package/dist/src/ui/hooks/useMessageQueue.js.map +1 -1
  599. package/dist/src/ui/hooks/useMessageQueue.test.js +173 -35
  600. package/dist/src/ui/hooks/useMessageQueue.test.js.map +1 -1
  601. package/dist/src/ui/hooks/useModelCommand.test.js +21 -11
  602. package/dist/src/ui/hooks/useModelCommand.test.js.map +1 -1
  603. package/dist/src/ui/hooks/useMouse.d.ts +17 -0
  604. package/dist/src/ui/hooks/useMouse.js +27 -0
  605. package/dist/src/ui/hooks/useMouse.js.map +1 -0
  606. package/dist/src/ui/hooks/useMouse.test.d.ts +6 -0
  607. package/dist/src/ui/hooks/useMouse.test.js +57 -0
  608. package/dist/src/ui/hooks/useMouse.test.js.map +1 -0
  609. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js +2 -2
  610. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js.map +1 -1
  611. package/dist/src/ui/hooks/usePhraseCycler.d.ts +1 -0
  612. package/dist/src/ui/hooks/usePhraseCycler.js +156 -5
  613. package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
  614. package/dist/src/ui/hooks/usePhraseCycler.test.d.ts +6 -0
  615. package/dist/src/ui/hooks/usePhraseCycler.test.js +158 -0
  616. package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -0
  617. package/dist/src/ui/hooks/usePrivacySettings.test.js +26 -6
  618. package/dist/src/ui/hooks/usePrivacySettings.test.js.map +1 -1
  619. package/dist/src/ui/hooks/usePromptCompletion.js +2 -2
  620. package/dist/src/ui/hooks/usePromptCompletion.js.map +1 -1
  621. package/dist/src/ui/hooks/useQuotaAndFallback.js +13 -14
  622. package/dist/src/ui/hooks/useQuotaAndFallback.js.map +1 -1
  623. package/dist/src/ui/hooks/useQuotaAndFallback.test.js +55 -48
  624. package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -1
  625. package/dist/src/ui/hooks/useReactToolScheduler.d.ts +8 -1
  626. package/dist/src/ui/hooks/useReactToolScheduler.js +61 -36
  627. package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
  628. package/dist/src/ui/hooks/useReactToolScheduler.test.d.ts +6 -0
  629. package/dist/src/ui/hooks/useReactToolScheduler.test.js +65 -0
  630. package/dist/src/ui/hooks/useReactToolScheduler.test.js.map +1 -0
  631. package/dist/src/ui/hooks/useReverseSearchCompletion.test.js +2 -2
  632. package/dist/src/ui/hooks/useReverseSearchCompletion.test.js.map +1 -1
  633. package/dist/src/ui/hooks/useSelectionList.js +5 -4
  634. package/dist/src/ui/hooks/useSelectionList.js.map +1 -1
  635. package/dist/src/ui/hooks/useSelectionList.test.js +272 -183
  636. package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -1
  637. package/dist/src/ui/hooks/useShellHistory.test.js +52 -20
  638. package/dist/src/ui/hooks/useShellHistory.test.js.map +1 -1
  639. package/dist/src/ui/hooks/useShowMemoryCommand.d.ts +1 -1
  640. package/dist/src/ui/hooks/useShowMemoryCommand.js +4 -3
  641. package/dist/src/ui/hooks/useShowMemoryCommand.js.map +1 -1
  642. package/dist/src/ui/hooks/useSlashCompletion.js +20 -8
  643. package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
  644. package/dist/src/ui/hooks/useSlashCompletion.test.js +275 -137
  645. package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
  646. package/dist/src/ui/hooks/useThemeCommand.d.ts +2 -1
  647. package/dist/src/ui/hooks/useThemeCommand.js +6 -0
  648. package/dist/src/ui/hooks/useThemeCommand.js.map +1 -1
  649. package/dist/src/ui/hooks/useTimer.test.js +43 -14
  650. package/dist/src/ui/hooks/useTimer.test.js.map +1 -1
  651. package/dist/src/ui/hooks/useToolScheduler.test.js +229 -242
  652. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  653. package/dist/src/ui/hooks/vim.js +2 -1
  654. package/dist/src/ui/hooks/vim.js.map +1 -1
  655. package/dist/src/ui/hooks/vim.test.d.ts +6 -0
  656. package/dist/src/ui/hooks/vim.test.js +1269 -0
  657. package/dist/src/ui/hooks/vim.test.js.map +1 -0
  658. package/dist/src/ui/keyMatchers.test.js +39 -6
  659. package/dist/src/ui/keyMatchers.test.js.map +1 -1
  660. package/dist/src/ui/state/extensions.d.ts +1 -0
  661. package/dist/src/ui/state/extensions.js +1 -0
  662. package/dist/src/ui/state/extensions.js.map +1 -1
  663. package/dist/src/ui/themes/ansi-light.js +1 -0
  664. package/dist/src/ui/themes/ansi-light.js.map +1 -1
  665. package/dist/src/ui/themes/ansi.js +1 -0
  666. package/dist/src/ui/themes/ansi.js.map +1 -1
  667. package/dist/src/ui/themes/atom-one-dark.js +2 -0
  668. package/dist/src/ui/themes/atom-one-dark.js.map +1 -1
  669. package/dist/src/ui/themes/ayu-light.js +2 -0
  670. package/dist/src/ui/themes/ayu-light.js.map +1 -1
  671. package/dist/src/ui/themes/ayu.js +2 -0
  672. package/dist/src/ui/themes/ayu.js.map +1 -1
  673. package/dist/src/ui/themes/color-utils.d.ts +1 -0
  674. package/dist/src/ui/themes/color-utils.js +8 -1
  675. package/dist/src/ui/themes/color-utils.js.map +1 -1
  676. package/dist/src/ui/themes/color-utils.test.js +13 -1
  677. package/dist/src/ui/themes/color-utils.test.js.map +1 -1
  678. package/dist/src/ui/themes/dracula.js +2 -0
  679. package/dist/src/ui/themes/dracula.js.map +1 -1
  680. package/dist/src/ui/themes/github-dark.js +2 -0
  681. package/dist/src/ui/themes/github-dark.js.map +1 -1
  682. package/dist/src/ui/themes/github-light.js +2 -0
  683. package/dist/src/ui/themes/github-light.js.map +1 -1
  684. package/dist/src/ui/themes/googlecode.js +2 -0
  685. package/dist/src/ui/themes/googlecode.js.map +1 -1
  686. package/dist/src/ui/themes/no-color.js +3 -0
  687. package/dist/src/ui/themes/no-color.js.map +1 -1
  688. package/dist/src/ui/themes/semantic-tokens.d.ts +2 -0
  689. package/dist/src/ui/themes/semantic-tokens.js +6 -0
  690. package/dist/src/ui/themes/semantic-tokens.js.map +1 -1
  691. package/dist/src/ui/themes/shades-of-purple.js +2 -0
  692. package/dist/src/ui/themes/shades-of-purple.js.map +1 -1
  693. package/dist/src/ui/themes/theme-manager.js +8 -7
  694. package/dist/src/ui/themes/theme-manager.js.map +1 -1
  695. package/dist/src/ui/themes/theme.d.ts +3 -0
  696. package/dist/src/ui/themes/theme.js +14 -3
  697. package/dist/src/ui/themes/theme.js.map +1 -1
  698. package/dist/src/ui/themes/theme.test.d.ts +6 -0
  699. package/dist/src/ui/themes/theme.test.js +151 -0
  700. package/dist/src/ui/themes/theme.test.js.map +1 -0
  701. package/dist/src/ui/themes/xcode.js +2 -0
  702. package/dist/src/ui/themes/xcode.js.map +1 -1
  703. package/dist/src/ui/types.d.ts +3 -2
  704. package/dist/src/ui/types.js +2 -0
  705. package/dist/src/ui/types.js.map +1 -1
  706. package/dist/src/ui/utils/CodeColorizer.d.ts +1 -1
  707. package/dist/src/ui/utils/CodeColorizer.js +6 -3
  708. package/dist/src/ui/utils/CodeColorizer.js.map +1 -1
  709. package/dist/src/ui/utils/InlineMarkdownRenderer.d.ts +1 -0
  710. package/dist/src/ui/utils/InlineMarkdownRenderer.js +11 -10
  711. package/dist/src/ui/utils/InlineMarkdownRenderer.js.map +1 -1
  712. package/dist/src/ui/utils/MarkdownDisplay.d.ts +1 -0
  713. package/dist/src/ui/utils/MarkdownDisplay.js +19 -10
  714. package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
  715. package/dist/src/ui/utils/clipboardUtils.js +2 -2
  716. package/dist/src/ui/utils/clipboardUtils.js.map +1 -1
  717. package/dist/src/ui/utils/commandUtils.js +20 -3
  718. package/dist/src/ui/utils/commandUtils.js.map +1 -1
  719. package/dist/src/ui/utils/commandUtils.test.js +61 -6
  720. package/dist/src/ui/utils/commandUtils.test.js.map +1 -1
  721. package/dist/src/ui/utils/computeStats.js +5 -2
  722. package/dist/src/ui/utils/computeStats.js.map +1 -1
  723. package/dist/src/ui/utils/computeStats.test.d.ts +6 -0
  724. package/dist/src/ui/utils/computeStats.test.js +262 -0
  725. package/dist/src/ui/utils/computeStats.test.js.map +1 -0
  726. package/dist/src/ui/utils/input.d.ts +17 -0
  727. package/dist/src/ui/utils/input.js +51 -0
  728. package/dist/src/ui/utils/input.js.map +1 -0
  729. package/dist/src/ui/utils/input.test.d.ts +6 -0
  730. package/dist/src/ui/utils/input.test.js +44 -0
  731. package/dist/src/ui/utils/input.test.js.map +1 -0
  732. package/dist/src/ui/utils/kittyProtocolDetector.js +13 -4
  733. package/dist/src/ui/utils/kittyProtocolDetector.js.map +1 -1
  734. package/dist/src/ui/utils/mouse.d.ts +31 -0
  735. package/dist/src/ui/utils/mouse.js +164 -0
  736. package/dist/src/ui/utils/mouse.js.map +1 -0
  737. package/dist/src/ui/utils/mouse.test.d.ts +6 -0
  738. package/dist/src/ui/utils/mouse.test.js +131 -0
  739. package/dist/src/ui/utils/mouse.test.js.map +1 -0
  740. package/dist/src/ui/utils/terminalSetup.js +3 -2
  741. package/dist/src/ui/utils/terminalSetup.js.map +1 -1
  742. package/dist/src/ui/utils/textOutput.d.ts +25 -0
  743. package/dist/src/ui/utils/textOutput.js +49 -0
  744. package/dist/src/ui/utils/textOutput.js.map +1 -0
  745. package/dist/src/ui/utils/textOutput.test.d.ts +6 -0
  746. package/dist/src/ui/utils/textOutput.test.js +79 -0
  747. package/dist/src/ui/utils/textOutput.test.js.map +1 -0
  748. package/dist/src/ui/utils/updateCheck.d.ts +9 -2
  749. package/dist/src/ui/utils/updateCheck.js +38 -30
  750. package/dist/src/ui/utils/updateCheck.js.map +1 -1
  751. package/dist/src/ui/utils/updateCheck.test.js +48 -59
  752. package/dist/src/ui/utils/updateCheck.test.js.map +1 -1
  753. package/dist/src/utils/cleanup.test.d.ts +6 -0
  754. package/dist/src/utils/cleanup.test.js +49 -0
  755. package/dist/src/utils/cleanup.test.js.map +1 -0
  756. package/dist/src/utils/commentJson.js +2 -2
  757. package/dist/src/utils/commentJson.js.map +1 -1
  758. package/dist/src/utils/commentJson.test.js +7 -6
  759. package/dist/src/utils/commentJson.test.js.map +1 -1
  760. package/dist/src/utils/envVarResolver.d.ts +2 -2
  761. package/dist/src/utils/envVarResolver.js +10 -7
  762. package/dist/src/utils/envVarResolver.js.map +1 -1
  763. package/dist/src/utils/errors.d.ts +1 -0
  764. package/dist/src/utils/errors.js +66 -5
  765. package/dist/src/utils/errors.js.map +1 -1
  766. package/dist/src/utils/events.d.ts +11 -2
  767. package/dist/src/utils/events.js +1 -0
  768. package/dist/src/utils/events.js.map +1 -1
  769. package/dist/src/utils/gitUtils.js +3 -2
  770. package/dist/src/utils/gitUtils.js.map +1 -1
  771. package/dist/src/utils/handleAutoUpdate.js +9 -3
  772. package/dist/src/utils/handleAutoUpdate.js.map +1 -1
  773. package/dist/src/utils/handleAutoUpdate.test.d.ts +6 -0
  774. package/dist/src/utils/handleAutoUpdate.test.js +225 -0
  775. package/dist/src/utils/handleAutoUpdate.test.js.map +1 -0
  776. package/dist/src/utils/installationInfo.js +2 -2
  777. package/dist/src/utils/installationInfo.js.map +1 -1
  778. package/dist/src/utils/installationInfo.test.js +8 -4
  779. package/dist/src/utils/installationInfo.test.js.map +1 -1
  780. package/dist/src/utils/readStdin.js +2 -1
  781. package/dist/src/utils/readStdin.js.map +1 -1
  782. package/dist/src/utils/sandbox-macos-permissive-open.sb +2 -0
  783. package/dist/src/utils/sandbox.js +28 -30
  784. package/dist/src/utils/sandbox.js.map +1 -1
  785. package/dist/src/utils/sessionCleanup.js +4 -4
  786. package/dist/src/utils/sessionCleanup.js.map +1 -1
  787. package/dist/src/utils/startupWarnings.test.d.ts +6 -0
  788. package/dist/src/utils/startupWarnings.test.js +61 -0
  789. package/dist/src/utils/startupWarnings.test.js.map +1 -0
  790. package/dist/src/utils/version.js +6 -2
  791. package/dist/src/utils/version.js.map +1 -1
  792. package/dist/src/validateNonInterActiveAuth.js +2 -2
  793. package/dist/src/validateNonInterActiveAuth.js.map +1 -1
  794. package/dist/src/zed-integration/acp.js +2 -1
  795. package/dist/src/zed-integration/acp.js.map +1 -1
  796. package/dist/src/zed-integration/schema.d.ts +4 -4
  797. package/dist/src/zed-integration/zedIntegration.d.ts +2 -2
  798. package/dist/src/zed-integration/zedIntegration.js +16 -25
  799. package/dist/src/zed-integration/zedIntegration.js.map +1 -1
  800. package/dist/tsconfig.tsbuildinfo +1 -1
  801. package/package.json +17 -17
  802. package/dist/src/config/policy.test.js +0 -336
  803. package/dist/src/config/policy.test.js.map +0 -1
  804. package/dist/src/ui/components/WorkspaceMigrationDialog.d.ts +0 -11
  805. package/dist/src/ui/components/WorkspaceMigrationDialog.js +0 -44
  806. package/dist/src/ui/components/WorkspaceMigrationDialog.js.map +0 -1
  807. package/dist/src/ui/hooks/useWorkspaceMigration.d.ts +0 -13
  808. package/dist/src/ui/hooks/useWorkspaceMigration.js +0 -59
  809. package/dist/src/ui/hooks/useWorkspaceMigration.js.map +0 -1
  810. package/dist/src/utils/package.d.ts +0 -12
  811. package/dist/src/utils/package.js +0 -24
  812. package/dist/src/utils/package.js.map +0 -1
  813. /package/dist/src/{config/policy.test.d.ts → commands/extensions/examples/mcp-server/example.d.ts} +0 -0
@@ -0,0 +1,1786 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * @license
4
+ * Copyright 2025 Google LLC
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+ import { renderWithProviders } from '../../test-utils/render.js';
8
+ import { waitFor } from '../../test-utils/async.js';
9
+ import { act } from 'react';
10
+ import { InputPrompt } from './InputPrompt.js';
11
+ import { ApprovalMode } from '@google/gemini-cli-core';
12
+ import * as path from 'node:path';
13
+ import { CommandKind } from '../commands/types.js';
14
+ import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
15
+ import { useShellHistory } from '../hooks/useShellHistory.js';
16
+ import { useCommandCompletion } from '../hooks/useCommandCompletion.js';
17
+ import { useInputHistory } from '../hooks/useInputHistory.js';
18
+ import { useReverseSearchCompletion } from '../hooks/useReverseSearchCompletion.js';
19
+ import * as clipboardUtils from '../utils/clipboardUtils.js';
20
+ import { useKittyKeyboardProtocol } from '../hooks/useKittyKeyboardProtocol.js';
21
+ import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
22
+ import stripAnsi from 'strip-ansi';
23
+ import chalk from 'chalk';
24
+ import { StreamingState } from '../types.js';
25
+ vi.mock('../hooks/useShellHistory.js');
26
+ vi.mock('../hooks/useCommandCompletion.js');
27
+ vi.mock('../hooks/useInputHistory.js');
28
+ vi.mock('../hooks/useReverseSearchCompletion.js');
29
+ vi.mock('../utils/clipboardUtils.js');
30
+ vi.mock('../hooks/useKittyKeyboardProtocol.js');
31
+ const mockSlashCommands = [
32
+ {
33
+ name: 'clear',
34
+ kind: CommandKind.BUILT_IN,
35
+ description: 'Clear screen',
36
+ action: vi.fn(),
37
+ },
38
+ {
39
+ name: 'memory',
40
+ kind: CommandKind.BUILT_IN,
41
+ description: 'Manage memory',
42
+ subCommands: [
43
+ {
44
+ name: 'show',
45
+ kind: CommandKind.BUILT_IN,
46
+ description: 'Show memory',
47
+ action: vi.fn(),
48
+ },
49
+ {
50
+ name: 'add',
51
+ kind: CommandKind.BUILT_IN,
52
+ description: 'Add to memory',
53
+ action: vi.fn(),
54
+ },
55
+ {
56
+ name: 'refresh',
57
+ kind: CommandKind.BUILT_IN,
58
+ description: 'Refresh memory',
59
+ action: vi.fn(),
60
+ },
61
+ ],
62
+ },
63
+ {
64
+ name: 'chat',
65
+ description: 'Manage chats',
66
+ kind: CommandKind.BUILT_IN,
67
+ subCommands: [
68
+ {
69
+ name: 'resume',
70
+ description: 'Resume a chat',
71
+ kind: CommandKind.BUILT_IN,
72
+ action: vi.fn(),
73
+ completion: async () => ['fix-foo', 'fix-bar'],
74
+ },
75
+ ],
76
+ },
77
+ ];
78
+ describe('InputPrompt', () => {
79
+ let props;
80
+ let mockShellHistory;
81
+ let mockCommandCompletion;
82
+ let mockInputHistory;
83
+ let mockReverseSearchCompletion;
84
+ let mockBuffer;
85
+ let mockCommandContext;
86
+ const mockedUseShellHistory = vi.mocked(useShellHistory);
87
+ const mockedUseCommandCompletion = vi.mocked(useCommandCompletion);
88
+ const mockedUseInputHistory = vi.mocked(useInputHistory);
89
+ const mockedUseReverseSearchCompletion = vi.mocked(useReverseSearchCompletion);
90
+ const mockedUseKittyKeyboardProtocol = vi.mocked(useKittyKeyboardProtocol);
91
+ beforeEach(() => {
92
+ vi.resetAllMocks();
93
+ mockCommandContext = createMockCommandContext();
94
+ mockBuffer = {
95
+ text: '',
96
+ cursor: [0, 0],
97
+ lines: [''],
98
+ setText: vi.fn((newText) => {
99
+ mockBuffer.text = newText;
100
+ mockBuffer.lines = [newText];
101
+ mockBuffer.cursor = [0, newText.length];
102
+ mockBuffer.viewportVisualLines = [newText];
103
+ mockBuffer.allVisualLines = [newText];
104
+ mockBuffer.visualToLogicalMap = [[0, 0]];
105
+ }),
106
+ replaceRangeByOffset: vi.fn(),
107
+ viewportVisualLines: [''],
108
+ allVisualLines: [''],
109
+ visualCursor: [0, 0],
110
+ visualScrollRow: 0,
111
+ handleInput: vi.fn(),
112
+ move: vi.fn(),
113
+ moveToOffset: vi.fn((offset) => {
114
+ mockBuffer.cursor = [0, offset];
115
+ }),
116
+ moveToVisualPosition: vi.fn(),
117
+ killLineRight: vi.fn(),
118
+ killLineLeft: vi.fn(),
119
+ openInExternalEditor: vi.fn(),
120
+ newline: vi.fn(),
121
+ undo: vi.fn(),
122
+ redo: vi.fn(),
123
+ backspace: vi.fn(),
124
+ preferredCol: null,
125
+ selectionAnchor: null,
126
+ insert: vi.fn(),
127
+ del: vi.fn(),
128
+ replaceRange: vi.fn(),
129
+ deleteWordLeft: vi.fn(),
130
+ deleteWordRight: vi.fn(),
131
+ visualToLogicalMap: [[0, 0]],
132
+ };
133
+ mockShellHistory = {
134
+ history: [],
135
+ addCommandToHistory: vi.fn(),
136
+ getPreviousCommand: vi.fn().mockReturnValue(null),
137
+ getNextCommand: vi.fn().mockReturnValue(null),
138
+ resetHistoryPosition: vi.fn(),
139
+ };
140
+ mockedUseShellHistory.mockReturnValue(mockShellHistory);
141
+ mockCommandCompletion = {
142
+ suggestions: [],
143
+ activeSuggestionIndex: -1,
144
+ isLoadingSuggestions: false,
145
+ showSuggestions: false,
146
+ visibleStartIndex: 0,
147
+ isPerfectMatch: false,
148
+ navigateUp: vi.fn(),
149
+ navigateDown: vi.fn(),
150
+ resetCompletionState: vi.fn(),
151
+ setActiveSuggestionIndex: vi.fn(),
152
+ setShowSuggestions: vi.fn(),
153
+ handleAutocomplete: vi.fn(),
154
+ promptCompletion: {
155
+ text: '',
156
+ accept: vi.fn(),
157
+ clear: vi.fn(),
158
+ isLoading: false,
159
+ isActive: false,
160
+ markSelected: vi.fn(),
161
+ },
162
+ };
163
+ mockedUseCommandCompletion.mockReturnValue(mockCommandCompletion);
164
+ mockInputHistory = {
165
+ navigateUp: vi.fn(),
166
+ navigateDown: vi.fn(),
167
+ handleSubmit: vi.fn(),
168
+ };
169
+ mockedUseInputHistory.mockReturnValue(mockInputHistory);
170
+ mockReverseSearchCompletion = {
171
+ suggestions: [],
172
+ activeSuggestionIndex: -1,
173
+ visibleStartIndex: 0,
174
+ showSuggestions: false,
175
+ isLoadingSuggestions: false,
176
+ navigateUp: vi.fn(),
177
+ navigateDown: vi.fn(),
178
+ handleAutocomplete: vi.fn(),
179
+ resetCompletionState: vi.fn(),
180
+ };
181
+ mockedUseReverseSearchCompletion.mockReturnValue(mockReverseSearchCompletion);
182
+ mockedUseKittyKeyboardProtocol.mockReturnValue({
183
+ supported: false,
184
+ enabled: false,
185
+ checking: false,
186
+ });
187
+ props = {
188
+ buffer: mockBuffer,
189
+ onSubmit: vi.fn(),
190
+ userMessages: [],
191
+ onClearScreen: vi.fn(),
192
+ config: {
193
+ getProjectRoot: () => path.join('test', 'project'),
194
+ getTargetDir: () => path.join('test', 'project', 'src'),
195
+ getVimMode: () => false,
196
+ getWorkspaceContext: () => ({
197
+ getDirectories: () => ['/test/project/src'],
198
+ }),
199
+ },
200
+ slashCommands: mockSlashCommands,
201
+ commandContext: mockCommandContext,
202
+ shellModeActive: false,
203
+ setShellModeActive: vi.fn(),
204
+ approvalMode: ApprovalMode.DEFAULT,
205
+ inputWidth: 80,
206
+ suggestionsWidth: 80,
207
+ focus: true,
208
+ setQueueErrorMessage: vi.fn(),
209
+ streamingState: StreamingState.Idle,
210
+ };
211
+ });
212
+ it('should call shellHistory.getPreviousCommand on up arrow in shell mode', async () => {
213
+ props.shellModeActive = true;
214
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
215
+ await act(async () => {
216
+ stdin.write('\u001B[A');
217
+ });
218
+ await waitFor(() => expect(mockShellHistory.getPreviousCommand).toHaveBeenCalled());
219
+ unmount();
220
+ });
221
+ it('should call shellHistory.getNextCommand on down arrow in shell mode', async () => {
222
+ props.shellModeActive = true;
223
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
224
+ await act(async () => {
225
+ stdin.write('\u001B[B');
226
+ await waitFor(() => expect(mockShellHistory.getNextCommand).toHaveBeenCalled());
227
+ });
228
+ unmount();
229
+ });
230
+ it('should set the buffer text when a shell history command is retrieved', async () => {
231
+ props.shellModeActive = true;
232
+ vi.mocked(mockShellHistory.getPreviousCommand).mockReturnValue('previous command');
233
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
234
+ await act(async () => {
235
+ stdin.write('\u001B[A');
236
+ });
237
+ await waitFor(() => {
238
+ expect(mockShellHistory.getPreviousCommand).toHaveBeenCalled();
239
+ expect(props.buffer.setText).toHaveBeenCalledWith('previous command');
240
+ });
241
+ unmount();
242
+ });
243
+ it('should call shellHistory.addCommandToHistory on submit in shell mode', async () => {
244
+ props.shellModeActive = true;
245
+ props.buffer.setText('ls -l');
246
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
247
+ await act(async () => {
248
+ stdin.write('\r');
249
+ });
250
+ await waitFor(() => {
251
+ expect(mockShellHistory.addCommandToHistory).toHaveBeenCalledWith('ls -l');
252
+ expect(props.onSubmit).toHaveBeenCalledWith('ls -l');
253
+ });
254
+ unmount();
255
+ });
256
+ it('should NOT call shell history methods when not in shell mode', async () => {
257
+ props.buffer.setText('some text');
258
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
259
+ await act(async () => {
260
+ stdin.write('\u001B[A'); // Up arrow
261
+ });
262
+ await waitFor(() => expect(mockInputHistory.navigateUp).toHaveBeenCalled());
263
+ await act(async () => {
264
+ stdin.write('\u001B[B'); // Down arrow
265
+ });
266
+ await waitFor(() => expect(mockInputHistory.navigateDown).toHaveBeenCalled());
267
+ await act(async () => {
268
+ stdin.write('\r'); // Enter
269
+ });
270
+ await waitFor(() => expect(props.onSubmit).toHaveBeenCalledWith('some text'));
271
+ expect(mockShellHistory.getPreviousCommand).not.toHaveBeenCalled();
272
+ expect(mockShellHistory.getNextCommand).not.toHaveBeenCalled();
273
+ expect(mockShellHistory.addCommandToHistory).not.toHaveBeenCalled();
274
+ unmount();
275
+ });
276
+ it('should call completion.navigateUp for both up arrow and Ctrl+P when suggestions are showing', async () => {
277
+ mockedUseCommandCompletion.mockReturnValue({
278
+ ...mockCommandCompletion,
279
+ showSuggestions: true,
280
+ suggestions: [
281
+ { label: 'memory', value: 'memory' },
282
+ { label: 'memcache', value: 'memcache' },
283
+ ],
284
+ });
285
+ props.buffer.setText('/mem');
286
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
287
+ // Test up arrow
288
+ await act(async () => {
289
+ stdin.write('\u001B[A'); // Up arrow
290
+ });
291
+ await waitFor(() => expect(mockCommandCompletion.navigateUp).toHaveBeenCalledTimes(1));
292
+ await act(async () => {
293
+ stdin.write('\u0010'); // Ctrl+P
294
+ });
295
+ await waitFor(() => expect(mockCommandCompletion.navigateUp).toHaveBeenCalledTimes(2));
296
+ expect(mockCommandCompletion.navigateDown).not.toHaveBeenCalled();
297
+ unmount();
298
+ });
299
+ it('should call completion.navigateDown for both down arrow and Ctrl+N when suggestions are showing', async () => {
300
+ mockedUseCommandCompletion.mockReturnValue({
301
+ ...mockCommandCompletion,
302
+ showSuggestions: true,
303
+ suggestions: [
304
+ { label: 'memory', value: 'memory' },
305
+ { label: 'memcache', value: 'memcache' },
306
+ ],
307
+ });
308
+ props.buffer.setText('/mem');
309
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
310
+ // Test down arrow
311
+ await act(async () => {
312
+ stdin.write('\u001B[B'); // Down arrow
313
+ });
314
+ await waitFor(() => expect(mockCommandCompletion.navigateDown).toHaveBeenCalledTimes(1));
315
+ await act(async () => {
316
+ stdin.write('\u000E'); // Ctrl+N
317
+ });
318
+ await waitFor(() => expect(mockCommandCompletion.navigateDown).toHaveBeenCalledTimes(2));
319
+ expect(mockCommandCompletion.navigateUp).not.toHaveBeenCalled();
320
+ unmount();
321
+ });
322
+ it('should NOT call completion navigation when suggestions are not showing', async () => {
323
+ mockedUseCommandCompletion.mockReturnValue({
324
+ ...mockCommandCompletion,
325
+ showSuggestions: false,
326
+ });
327
+ props.buffer.setText('some text');
328
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
329
+ await act(async () => {
330
+ stdin.write('\u001B[A'); // Up arrow
331
+ });
332
+ await waitFor(() => expect(mockInputHistory.navigateUp).toHaveBeenCalled());
333
+ await act(async () => {
334
+ stdin.write('\u001B[B'); // Down arrow
335
+ });
336
+ await waitFor(() => expect(mockInputHistory.navigateDown).toHaveBeenCalled());
337
+ await act(async () => {
338
+ stdin.write('\u0010'); // Ctrl+P
339
+ });
340
+ await act(async () => {
341
+ stdin.write('\u000E'); // Ctrl+N
342
+ });
343
+ await waitFor(() => {
344
+ expect(mockCommandCompletion.navigateUp).not.toHaveBeenCalled();
345
+ expect(mockCommandCompletion.navigateDown).not.toHaveBeenCalled();
346
+ });
347
+ unmount();
348
+ });
349
+ describe('clipboard image paste', () => {
350
+ beforeEach(() => {
351
+ vi.mocked(clipboardUtils.clipboardHasImage).mockResolvedValue(false);
352
+ vi.mocked(clipboardUtils.saveClipboardImage).mockResolvedValue(null);
353
+ vi.mocked(clipboardUtils.cleanupOldClipboardImages).mockResolvedValue(undefined);
354
+ });
355
+ it('should handle Ctrl+V when clipboard has an image', async () => {
356
+ vi.mocked(clipboardUtils.clipboardHasImage).mockResolvedValue(true);
357
+ vi.mocked(clipboardUtils.saveClipboardImage).mockResolvedValue('/test/.cell-cli-clipboard/clipboard-123.png');
358
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
359
+ // Send Ctrl+V
360
+ await act(async () => {
361
+ stdin.write('\x16'); // Ctrl+V
362
+ });
363
+ await waitFor(() => {
364
+ expect(clipboardUtils.clipboardHasImage).toHaveBeenCalled();
365
+ expect(clipboardUtils.saveClipboardImage).toHaveBeenCalledWith(props.config.getTargetDir());
366
+ expect(clipboardUtils.cleanupOldClipboardImages).toHaveBeenCalledWith(props.config.getTargetDir());
367
+ expect(mockBuffer.replaceRangeByOffset).toHaveBeenCalled();
368
+ });
369
+ unmount();
370
+ });
371
+ it('should not insert anything when clipboard has no image', async () => {
372
+ vi.mocked(clipboardUtils.clipboardHasImage).mockResolvedValue(false);
373
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
374
+ await act(async () => {
375
+ stdin.write('\x16'); // Ctrl+V
376
+ });
377
+ await waitFor(() => {
378
+ expect(clipboardUtils.clipboardHasImage).toHaveBeenCalled();
379
+ });
380
+ expect(clipboardUtils.saveClipboardImage).not.toHaveBeenCalled();
381
+ expect(mockBuffer.setText).not.toHaveBeenCalled();
382
+ unmount();
383
+ });
384
+ it('should handle image save failure gracefully', async () => {
385
+ vi.mocked(clipboardUtils.clipboardHasImage).mockResolvedValue(true);
386
+ vi.mocked(clipboardUtils.saveClipboardImage).mockResolvedValue(null);
387
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
388
+ await act(async () => {
389
+ stdin.write('\x16'); // Ctrl+V
390
+ });
391
+ await waitFor(() => {
392
+ expect(clipboardUtils.saveClipboardImage).toHaveBeenCalled();
393
+ });
394
+ expect(mockBuffer.setText).not.toHaveBeenCalled();
395
+ unmount();
396
+ });
397
+ it('should insert image path at cursor position with proper spacing', async () => {
398
+ const imagePath = path.join('test', '.cell-cli-clipboard', 'clipboard-456.png');
399
+ vi.mocked(clipboardUtils.clipboardHasImage).mockResolvedValue(true);
400
+ vi.mocked(clipboardUtils.saveClipboardImage).mockResolvedValue(imagePath);
401
+ // Set initial text and cursor position
402
+ mockBuffer.text = 'Hello world';
403
+ mockBuffer.cursor = [0, 5]; // Cursor after "Hello"
404
+ mockBuffer.lines = ['Hello world'];
405
+ mockBuffer.replaceRangeByOffset = vi.fn();
406
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
407
+ await act(async () => {
408
+ stdin.write('\x16'); // Ctrl+V
409
+ });
410
+ await waitFor(() => {
411
+ // Should insert at cursor position with spaces
412
+ expect(mockBuffer.replaceRangeByOffset).toHaveBeenCalled();
413
+ });
414
+ // Get the actual call to see what path was used
415
+ const actualCall = vi.mocked(mockBuffer.replaceRangeByOffset).mock
416
+ .calls[0];
417
+ expect(actualCall[0]).toBe(5); // start offset
418
+ expect(actualCall[1]).toBe(5); // end offset
419
+ expect(actualCall[2]).toBe(' @' + path.relative(path.join('test', 'project', 'src'), imagePath));
420
+ unmount();
421
+ });
422
+ it('should handle errors during clipboard operations', async () => {
423
+ const consoleErrorSpy = vi
424
+ .spyOn(console, 'error')
425
+ .mockImplementation(() => { });
426
+ vi.mocked(clipboardUtils.clipboardHasImage).mockRejectedValue(new Error('Clipboard error'));
427
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
428
+ await act(async () => {
429
+ stdin.write('\x16'); // Ctrl+V
430
+ });
431
+ await waitFor(() => {
432
+ expect(consoleErrorSpy).toHaveBeenCalledWith('Error handling clipboard image:', expect.any(Error));
433
+ });
434
+ expect(mockBuffer.setText).not.toHaveBeenCalled();
435
+ consoleErrorSpy.mockRestore();
436
+ unmount();
437
+ });
438
+ });
439
+ it('should complete a partial parent command', async () => {
440
+ // SCENARIO: /mem -> Tab
441
+ mockedUseCommandCompletion.mockReturnValue({
442
+ ...mockCommandCompletion,
443
+ showSuggestions: true,
444
+ suggestions: [{ label: 'memory', value: 'memory', description: '...' }],
445
+ activeSuggestionIndex: 0,
446
+ });
447
+ props.buffer.setText('/mem');
448
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
449
+ await act(async () => {
450
+ stdin.write('\t'); // Press Tab
451
+ });
452
+ await waitFor(() => expect(mockCommandCompletion.handleAutocomplete).toHaveBeenCalledWith(0));
453
+ unmount();
454
+ });
455
+ it('should append a sub-command when the parent command is already complete', async () => {
456
+ // SCENARIO: /memory -> Tab (to accept 'add')
457
+ mockedUseCommandCompletion.mockReturnValue({
458
+ ...mockCommandCompletion,
459
+ showSuggestions: true,
460
+ suggestions: [
461
+ { label: 'show', value: 'show' },
462
+ { label: 'add', value: 'add' },
463
+ ],
464
+ activeSuggestionIndex: 1, // 'add' is highlighted
465
+ });
466
+ props.buffer.setText('/memory ');
467
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
468
+ await act(async () => {
469
+ stdin.write('\t'); // Press Tab
470
+ });
471
+ await waitFor(() => expect(mockCommandCompletion.handleAutocomplete).toHaveBeenCalledWith(1));
472
+ unmount();
473
+ });
474
+ it('should handle the "backspace" edge case correctly', async () => {
475
+ // SCENARIO: /memory -> Backspace -> /memory -> Tab (to accept 'show')
476
+ mockedUseCommandCompletion.mockReturnValue({
477
+ ...mockCommandCompletion,
478
+ showSuggestions: true,
479
+ suggestions: [
480
+ { label: 'show', value: 'show' },
481
+ { label: 'add', value: 'add' },
482
+ ],
483
+ activeSuggestionIndex: 0, // 'show' is highlighted
484
+ });
485
+ // The user has backspaced, so the query is now just '/memory'
486
+ props.buffer.setText('/memory');
487
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
488
+ await act(async () => {
489
+ stdin.write('\t'); // Press Tab
490
+ });
491
+ await waitFor(() =>
492
+ // It should NOT become '/show'. It should correctly become '/memory show'.
493
+ expect(mockCommandCompletion.handleAutocomplete).toHaveBeenCalledWith(0));
494
+ unmount();
495
+ });
496
+ it('should complete a partial argument for a command', async () => {
497
+ // SCENARIO: /chat resume fi- -> Tab
498
+ mockedUseCommandCompletion.mockReturnValue({
499
+ ...mockCommandCompletion,
500
+ showSuggestions: true,
501
+ suggestions: [{ label: 'fix-foo', value: 'fix-foo' }],
502
+ activeSuggestionIndex: 0,
503
+ });
504
+ props.buffer.setText('/chat resume fi-');
505
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
506
+ await act(async () => {
507
+ stdin.write('\t'); // Press Tab
508
+ });
509
+ await waitFor(() => expect(mockCommandCompletion.handleAutocomplete).toHaveBeenCalledWith(0));
510
+ unmount();
511
+ });
512
+ it('should autocomplete on Enter when suggestions are active, without submitting', async () => {
513
+ mockedUseCommandCompletion.mockReturnValue({
514
+ ...mockCommandCompletion,
515
+ showSuggestions: true,
516
+ suggestions: [{ label: 'memory', value: 'memory' }],
517
+ activeSuggestionIndex: 0,
518
+ });
519
+ props.buffer.setText('/mem');
520
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
521
+ await act(async () => {
522
+ stdin.write('\r');
523
+ });
524
+ await waitFor(() => {
525
+ // The app should autocomplete the text, NOT submit.
526
+ expect(mockCommandCompletion.handleAutocomplete).toHaveBeenCalledWith(0);
527
+ });
528
+ expect(props.onSubmit).not.toHaveBeenCalled();
529
+ unmount();
530
+ });
531
+ it('should complete a command based on its altNames', async () => {
532
+ props.slashCommands = [
533
+ {
534
+ name: 'help',
535
+ altNames: ['?'],
536
+ kind: CommandKind.BUILT_IN,
537
+ description: '...',
538
+ },
539
+ ];
540
+ mockedUseCommandCompletion.mockReturnValue({
541
+ ...mockCommandCompletion,
542
+ showSuggestions: true,
543
+ suggestions: [{ label: 'help', value: 'help' }],
544
+ activeSuggestionIndex: 0,
545
+ });
546
+ props.buffer.setText('/?');
547
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
548
+ await act(async () => {
549
+ stdin.write('\t'); // Press Tab for autocomplete
550
+ });
551
+ await waitFor(() => expect(mockCommandCompletion.handleAutocomplete).toHaveBeenCalledWith(0));
552
+ unmount();
553
+ });
554
+ it('should not submit on Enter when the buffer is empty or only contains whitespace', async () => {
555
+ props.buffer.setText(' '); // Set buffer to whitespace
556
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
557
+ await act(async () => {
558
+ stdin.write('\r'); // Press Enter
559
+ });
560
+ await waitFor(() => {
561
+ expect(props.onSubmit).not.toHaveBeenCalled();
562
+ });
563
+ unmount();
564
+ });
565
+ it('should submit directly on Enter when isPerfectMatch is true', async () => {
566
+ mockedUseCommandCompletion.mockReturnValue({
567
+ ...mockCommandCompletion,
568
+ showSuggestions: false,
569
+ isPerfectMatch: true,
570
+ });
571
+ props.buffer.setText('/clear');
572
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
573
+ await act(async () => {
574
+ stdin.write('\r');
575
+ });
576
+ await waitFor(() => expect(props.onSubmit).toHaveBeenCalledWith('/clear'));
577
+ unmount();
578
+ });
579
+ it('should submit directly on Enter when a complete leaf command is typed', async () => {
580
+ mockedUseCommandCompletion.mockReturnValue({
581
+ ...mockCommandCompletion,
582
+ showSuggestions: false,
583
+ isPerfectMatch: false, // Added explicit isPerfectMatch false
584
+ });
585
+ props.buffer.setText('/clear');
586
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
587
+ await act(async () => {
588
+ stdin.write('\r');
589
+ });
590
+ await waitFor(() => expect(props.onSubmit).toHaveBeenCalledWith('/clear'));
591
+ unmount();
592
+ });
593
+ it('should autocomplete an @-path on Enter without submitting', async () => {
594
+ mockedUseCommandCompletion.mockReturnValue({
595
+ ...mockCommandCompletion,
596
+ showSuggestions: true,
597
+ suggestions: [{ label: 'index.ts', value: 'index.ts' }],
598
+ activeSuggestionIndex: 0,
599
+ });
600
+ props.buffer.setText('@src/components/');
601
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
602
+ await act(async () => {
603
+ stdin.write('\r');
604
+ });
605
+ await waitFor(() => expect(mockCommandCompletion.handleAutocomplete).toHaveBeenCalledWith(0));
606
+ expect(props.onSubmit).not.toHaveBeenCalled();
607
+ unmount();
608
+ });
609
+ it('should add a newline on enter when the line ends with a backslash', async () => {
610
+ // This test simulates multi-line input, not submission
611
+ mockBuffer.text = 'first line\\';
612
+ mockBuffer.cursor = [0, 11];
613
+ mockBuffer.lines = ['first line\\'];
614
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
615
+ await act(async () => {
616
+ stdin.write('\r');
617
+ });
618
+ await waitFor(() => {
619
+ expect(props.buffer.backspace).toHaveBeenCalled();
620
+ expect(props.buffer.newline).toHaveBeenCalled();
621
+ });
622
+ expect(props.onSubmit).not.toHaveBeenCalled();
623
+ unmount();
624
+ });
625
+ it('should clear the buffer on Ctrl+C if it has text', async () => {
626
+ await act(async () => {
627
+ props.buffer.setText('some text to clear');
628
+ });
629
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
630
+ await act(async () => {
631
+ stdin.write('\x03'); // Ctrl+C character
632
+ });
633
+ await waitFor(() => {
634
+ expect(props.buffer.setText).toHaveBeenCalledWith('');
635
+ expect(mockCommandCompletion.resetCompletionState).toHaveBeenCalled();
636
+ });
637
+ expect(props.onSubmit).not.toHaveBeenCalled();
638
+ unmount();
639
+ });
640
+ it('should NOT clear the buffer on Ctrl+C if it is empty', async () => {
641
+ props.buffer.text = '';
642
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
643
+ await act(async () => {
644
+ stdin.write('\x03'); // Ctrl+C character
645
+ });
646
+ await waitFor(() => {
647
+ expect(props.buffer.setText).not.toHaveBeenCalled();
648
+ });
649
+ unmount();
650
+ });
651
+ describe('cursor-based completion trigger', () => {
652
+ it.each([
653
+ {
654
+ name: 'should trigger completion when cursor is after @ without spaces',
655
+ text: '@src/components',
656
+ cursor: [0, 15],
657
+ showSuggestions: true,
658
+ },
659
+ {
660
+ name: 'should trigger completion when cursor is after / without spaces',
661
+ text: '/memory',
662
+ cursor: [0, 7],
663
+ showSuggestions: true,
664
+ },
665
+ {
666
+ name: 'should NOT trigger completion when cursor is after space following @',
667
+ text: '@src/file.ts hello',
668
+ cursor: [0, 18],
669
+ showSuggestions: false,
670
+ },
671
+ {
672
+ name: 'should NOT trigger completion when cursor is after space following /',
673
+ text: '/memory add',
674
+ cursor: [0, 11],
675
+ showSuggestions: false,
676
+ },
677
+ {
678
+ name: 'should NOT trigger completion when cursor is not after @ or /',
679
+ text: 'hello world',
680
+ cursor: [0, 5],
681
+ showSuggestions: false,
682
+ },
683
+ {
684
+ name: 'should handle multiline text correctly',
685
+ text: 'first line\n/memory',
686
+ cursor: [1, 7],
687
+ showSuggestions: false,
688
+ },
689
+ {
690
+ name: 'should handle Unicode characters (emojis) correctly in paths',
691
+ text: '@src/file👍.txt',
692
+ cursor: [0, 14],
693
+ showSuggestions: true,
694
+ },
695
+ {
696
+ name: 'should handle Unicode characters with spaces after them',
697
+ text: '@src/file👍.txt hello',
698
+ cursor: [0, 20],
699
+ showSuggestions: false,
700
+ },
701
+ {
702
+ name: 'should handle escaped spaces in paths correctly',
703
+ text: '@src/my\\ file.txt',
704
+ cursor: [0, 16],
705
+ showSuggestions: true,
706
+ },
707
+ {
708
+ name: 'should NOT trigger completion after unescaped space following escaped space',
709
+ text: '@path/my\\ file.txt hello',
710
+ cursor: [0, 24],
711
+ showSuggestions: false,
712
+ },
713
+ {
714
+ name: 'should handle multiple escaped spaces in paths',
715
+ text: '@docs/my\\ long\\ file\\ name.md',
716
+ cursor: [0, 29],
717
+ showSuggestions: true,
718
+ },
719
+ {
720
+ name: 'should handle escaped spaces in slash commands',
721
+ text: '/memory\\ test',
722
+ cursor: [0, 13],
723
+ showSuggestions: true,
724
+ },
725
+ {
726
+ name: 'should handle Unicode characters with escaped spaces',
727
+ text: `@${path.join('files', 'emoji\\ 👍\\ test.txt')}`,
728
+ cursor: [0, 25],
729
+ showSuggestions: true,
730
+ },
731
+ ])('$name', async ({ text, cursor, showSuggestions }) => {
732
+ mockBuffer.text = text;
733
+ mockBuffer.lines = text.split('\n');
734
+ mockBuffer.cursor = cursor;
735
+ mockedUseCommandCompletion.mockReturnValue({
736
+ ...mockCommandCompletion,
737
+ showSuggestions,
738
+ suggestions: showSuggestions
739
+ ? [{ label: 'suggestion', value: 'suggestion' }]
740
+ : [],
741
+ });
742
+ const { unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
743
+ await waitFor(() => {
744
+ expect(mockedUseCommandCompletion).toHaveBeenCalledWith(mockBuffer, ['/test/project/src'], path.join('test', 'project', 'src'), mockSlashCommands, mockCommandContext, false, false, expect.any(Object));
745
+ });
746
+ unmount();
747
+ });
748
+ });
749
+ describe('vim mode', () => {
750
+ it('should not call buffer.handleInput when vim mode is enabled and vim handles the input', async () => {
751
+ props.vimHandleInput = vi.fn().mockReturnValue(true); // Mock that vim handled it.
752
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
753
+ await act(async () => {
754
+ stdin.write('i');
755
+ });
756
+ await waitFor(() => {
757
+ expect(props.vimHandleInput).toHaveBeenCalled();
758
+ });
759
+ expect(mockBuffer.handleInput).not.toHaveBeenCalled();
760
+ unmount();
761
+ });
762
+ it('should call buffer.handleInput when vim mode is enabled but vim does not handle the input', async () => {
763
+ props.vimHandleInput = vi.fn().mockReturnValue(false); // Mock that vim did NOT handle it.
764
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
765
+ await act(async () => {
766
+ stdin.write('i');
767
+ });
768
+ await waitFor(() => {
769
+ expect(props.vimHandleInput).toHaveBeenCalled();
770
+ expect(mockBuffer.handleInput).toHaveBeenCalled();
771
+ });
772
+ unmount();
773
+ });
774
+ it('should call handleInput when vim mode is disabled', async () => {
775
+ // Mock vimHandleInput to return false (vim didn't handle the input)
776
+ props.vimHandleInput = vi.fn().mockReturnValue(false);
777
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
778
+ await act(async () => {
779
+ stdin.write('i');
780
+ });
781
+ await waitFor(() => {
782
+ expect(props.vimHandleInput).toHaveBeenCalled();
783
+ expect(mockBuffer.handleInput).toHaveBeenCalled();
784
+ });
785
+ unmount();
786
+ });
787
+ });
788
+ describe('unfocused paste', () => {
789
+ it('should handle bracketed paste when not focused', async () => {
790
+ props.focus = false;
791
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
792
+ await act(async () => {
793
+ stdin.write('\x1B[200~pasted text\x1B[201~');
794
+ });
795
+ await waitFor(() => {
796
+ expect(mockBuffer.handleInput).toHaveBeenCalledWith(expect.objectContaining({
797
+ paste: true,
798
+ sequence: 'pasted text',
799
+ }));
800
+ });
801
+ unmount();
802
+ });
803
+ it('should ignore regular keypresses when not focused', async () => {
804
+ props.focus = false;
805
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
806
+ await act(async () => {
807
+ stdin.write('a');
808
+ });
809
+ await waitFor(() => { });
810
+ expect(mockBuffer.handleInput).not.toHaveBeenCalled();
811
+ unmount();
812
+ });
813
+ });
814
+ describe('Highlighting and Cursor Display', () => {
815
+ describe('single-line scenarios', () => {
816
+ it.each([
817
+ {
818
+ name: 'mid-word',
819
+ text: 'hello world',
820
+ visualCursor: [0, 3],
821
+ expected: `hel${chalk.inverse('l')}o world`,
822
+ },
823
+ {
824
+ name: 'at the beginning of the line',
825
+ text: 'hello',
826
+ visualCursor: [0, 0],
827
+ expected: `${chalk.inverse('h')}ello`,
828
+ },
829
+ {
830
+ name: 'at the end of the line',
831
+ text: 'hello',
832
+ visualCursor: [0, 5],
833
+ expected: `hello${chalk.inverse(' ')}`,
834
+ },
835
+ {
836
+ name: 'on a highlighted token',
837
+ text: 'run @path/to/file',
838
+ visualCursor: [0, 9],
839
+ expected: `@path/${chalk.inverse('t')}o/file`,
840
+ },
841
+ {
842
+ name: 'for multi-byte unicode characters',
843
+ text: 'hello 👍 world',
844
+ visualCursor: [0, 6],
845
+ expected: `hello ${chalk.inverse('👍')} world`,
846
+ },
847
+ {
848
+ name: 'at the end of a line with unicode characters',
849
+ text: 'hello 👍',
850
+ visualCursor: [0, 8],
851
+ expected: `hello 👍${chalk.inverse(' ')}`,
852
+ },
853
+ {
854
+ name: 'on an empty line',
855
+ text: '',
856
+ visualCursor: [0, 0],
857
+ expected: chalk.inverse(' '),
858
+ },
859
+ {
860
+ name: 'on a space between words',
861
+ text: 'hello world',
862
+ visualCursor: [0, 5],
863
+ expected: `hello${chalk.inverse(' ')}world`,
864
+ },
865
+ ])('should display cursor correctly $name', async ({ text, visualCursor, expected }) => {
866
+ mockBuffer.text = text;
867
+ mockBuffer.lines = [text];
868
+ mockBuffer.viewportVisualLines = [text];
869
+ mockBuffer.visualCursor = visualCursor;
870
+ const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
871
+ await waitFor(() => {
872
+ const frame = stdout.lastFrame();
873
+ expect(frame).toContain(expected);
874
+ });
875
+ unmount();
876
+ });
877
+ });
878
+ describe('multi-line scenarios', () => {
879
+ it.each([
880
+ {
881
+ name: 'in the middle of a line',
882
+ text: 'first line\nsecond line\nthird line',
883
+ visualCursor: [1, 3],
884
+ visualToLogicalMap: [
885
+ [0, 0],
886
+ [1, 0],
887
+ [2, 0],
888
+ ],
889
+ expected: `sec${chalk.inverse('o')}nd line`,
890
+ },
891
+ {
892
+ name: 'at the beginning of a line',
893
+ text: 'first line\nsecond line',
894
+ visualCursor: [1, 0],
895
+ visualToLogicalMap: [
896
+ [0, 0],
897
+ [1, 0],
898
+ ],
899
+ expected: `${chalk.inverse('s')}econd line`,
900
+ },
901
+ {
902
+ name: 'at the end of a line',
903
+ text: 'first line\nsecond line',
904
+ visualCursor: [0, 10],
905
+ visualToLogicalMap: [
906
+ [0, 0],
907
+ [1, 0],
908
+ ],
909
+ expected: `first line${chalk.inverse(' ')}`,
910
+ },
911
+ ])('should display cursor correctly $name in a multiline block', async ({ text, visualCursor, expected, visualToLogicalMap }) => {
912
+ mockBuffer.text = text;
913
+ mockBuffer.lines = text.split('\n');
914
+ mockBuffer.viewportVisualLines = text.split('\n');
915
+ mockBuffer.visualCursor = visualCursor;
916
+ mockBuffer.visualToLogicalMap = visualToLogicalMap;
917
+ const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
918
+ await waitFor(() => {
919
+ const frame = stdout.lastFrame();
920
+ expect(frame).toContain(expected);
921
+ });
922
+ unmount();
923
+ });
924
+ it('should display cursor on a blank line in a multiline block', async () => {
925
+ const text = 'first line\n\nthird line';
926
+ mockBuffer.text = text;
927
+ mockBuffer.lines = text.split('\n');
928
+ mockBuffer.viewportVisualLines = text.split('\n');
929
+ mockBuffer.visualCursor = [1, 0]; // cursor on the blank line
930
+ mockBuffer.visualToLogicalMap = [
931
+ [0, 0],
932
+ [1, 0],
933
+ [2, 0],
934
+ ];
935
+ const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
936
+ await waitFor(() => {
937
+ const frame = stdout.lastFrame();
938
+ const lines = frame.split('\n');
939
+ // The line with the cursor should just be an inverted space inside the box border
940
+ expect(lines.find((l) => l.includes(chalk.inverse(' ')))).not.toBeUndefined();
941
+ });
942
+ unmount();
943
+ });
944
+ });
945
+ });
946
+ describe('multiline rendering', () => {
947
+ it('should correctly render multiline input including blank lines', async () => {
948
+ const text = 'hello\n\nworld';
949
+ mockBuffer.text = text;
950
+ mockBuffer.lines = text.split('\n');
951
+ mockBuffer.viewportVisualLines = text.split('\n');
952
+ mockBuffer.allVisualLines = text.split('\n');
953
+ mockBuffer.visualCursor = [2, 5]; // cursor at the end of "world"
954
+ // Provide a visual-to-logical mapping for each visual line
955
+ mockBuffer.visualToLogicalMap = [
956
+ [0, 0], // 'hello' starts at col 0 of logical line 0
957
+ [1, 0], // '' (blank) is logical line 1, col 0
958
+ [2, 0], // 'world' is logical line 2, col 0
959
+ ];
960
+ const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
961
+ await waitFor(() => {
962
+ const frame = stdout.lastFrame();
963
+ // Check that all lines, including the empty one, are rendered.
964
+ // This implicitly tests that the Box wrapper provides height for the empty line.
965
+ expect(frame).toContain('hello');
966
+ expect(frame).toContain(`world${chalk.inverse(' ')}`);
967
+ const outputLines = frame.split('\n');
968
+ // The number of lines should be 2 for the border plus 3 for the content.
969
+ expect(outputLines.length).toBe(5);
970
+ });
971
+ unmount();
972
+ });
973
+ });
974
+ describe('multiline paste', () => {
975
+ it.each([
976
+ {
977
+ description: 'with \n newlines',
978
+ pastedText: 'This \n is \n a \n multiline \n paste.',
979
+ },
980
+ {
981
+ description: 'with extra slashes before \n newlines',
982
+ pastedText: 'This \\\n is \\\n a \\\n multiline \\\n paste.',
983
+ },
984
+ {
985
+ description: 'with \r\n newlines',
986
+ pastedText: 'This\r\nis\r\na\r\nmultiline\r\npaste.',
987
+ },
988
+ ])('should handle multiline paste $description', async ({ pastedText }) => {
989
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
990
+ // Simulate a bracketed paste event from the terminal
991
+ await act(async () => {
992
+ stdin.write(`\x1b[200~${pastedText}\x1b[201~`);
993
+ });
994
+ await waitFor(() => {
995
+ // Verify that the buffer's handleInput was called once with the full text
996
+ expect(props.buffer.handleInput).toHaveBeenCalledTimes(1);
997
+ expect(props.buffer.handleInput).toHaveBeenCalledWith(expect.objectContaining({
998
+ paste: true,
999
+ sequence: pastedText,
1000
+ }));
1001
+ });
1002
+ unmount();
1003
+ });
1004
+ });
1005
+ describe('paste auto-submission protection', () => {
1006
+ beforeEach(() => {
1007
+ vi.useFakeTimers();
1008
+ mockedUseKittyKeyboardProtocol.mockReturnValue({
1009
+ supported: false,
1010
+ enabled: false,
1011
+ checking: false,
1012
+ });
1013
+ });
1014
+ afterEach(() => {
1015
+ vi.useRealTimers();
1016
+ });
1017
+ it('should prevent auto-submission immediately after an unsafe paste', async () => {
1018
+ // isTerminalPasteTrusted will be false due to beforeEach setup.
1019
+ props.buffer.text = 'some command';
1020
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1021
+ await act(async () => {
1022
+ await vi.runAllTimersAsync();
1023
+ });
1024
+ // Simulate a paste operation (this should set the paste protection)
1025
+ await act(async () => {
1026
+ stdin.write(`\x1b[200~pasted content\x1b[201~`);
1027
+ });
1028
+ // Simulate an Enter key press immediately after paste
1029
+ await act(async () => {
1030
+ stdin.write('\r');
1031
+ });
1032
+ await act(async () => {
1033
+ await vi.runAllTimersAsync();
1034
+ });
1035
+ // Verify that onSubmit was NOT called due to recent paste protection
1036
+ expect(props.onSubmit).not.toHaveBeenCalled();
1037
+ // It should call newline() instead
1038
+ expect(props.buffer.newline).toHaveBeenCalled();
1039
+ unmount();
1040
+ });
1041
+ it('should allow submission after unsafe paste protection timeout', async () => {
1042
+ // isTerminalPasteTrusted will be false due to beforeEach setup.
1043
+ props.buffer.text = 'pasted text';
1044
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1045
+ await act(async () => {
1046
+ await vi.runAllTimersAsync();
1047
+ });
1048
+ // Simulate a paste operation (this sets the protection)
1049
+ await act(async () => {
1050
+ stdin.write('\x1b[200~pasted text\x1b[201~');
1051
+ });
1052
+ await act(async () => {
1053
+ await vi.runAllTimersAsync();
1054
+ });
1055
+ // Advance timers past the protection timeout
1056
+ await act(async () => {
1057
+ await vi.advanceTimersByTimeAsync(50);
1058
+ });
1059
+ // Now Enter should work normally
1060
+ await act(async () => {
1061
+ stdin.write('\r');
1062
+ });
1063
+ await act(async () => {
1064
+ await vi.runAllTimersAsync();
1065
+ });
1066
+ expect(props.onSubmit).toHaveBeenCalledWith('pasted text');
1067
+ expect(props.buffer.newline).not.toHaveBeenCalled();
1068
+ unmount();
1069
+ });
1070
+ it.each([
1071
+ {
1072
+ name: 'kitty',
1073
+ setup: () => mockedUseKittyKeyboardProtocol.mockReturnValue({
1074
+ supported: true,
1075
+ enabled: true,
1076
+ checking: false,
1077
+ }),
1078
+ },
1079
+ ])('should allow immediate submission for a trusted paste ($name)', async ({ setup }) => {
1080
+ setup();
1081
+ props.buffer.text = 'pasted command';
1082
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), { kittyProtocolEnabled: true });
1083
+ await act(async () => {
1084
+ await vi.runAllTimersAsync();
1085
+ });
1086
+ // Simulate a paste operation
1087
+ await act(async () => {
1088
+ stdin.write('\x1b[200~some pasted stuff\x1b[201~');
1089
+ });
1090
+ await act(async () => {
1091
+ await vi.runAllTimersAsync();
1092
+ });
1093
+ // Simulate an Enter key press immediately after paste
1094
+ await act(async () => {
1095
+ stdin.write('\r');
1096
+ });
1097
+ await act(async () => {
1098
+ await vi.runAllTimersAsync();
1099
+ });
1100
+ // Verify that onSubmit was called
1101
+ expect(props.onSubmit).toHaveBeenCalledWith('pasted command');
1102
+ unmount();
1103
+ });
1104
+ it('should not interfere with normal Enter key submission when no recent paste', async () => {
1105
+ // Set up buffer with text before rendering to ensure submission works
1106
+ props.buffer.text = 'normal command';
1107
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1108
+ await act(async () => {
1109
+ await vi.runAllTimersAsync();
1110
+ });
1111
+ // Press Enter without any recent paste
1112
+ await act(async () => {
1113
+ stdin.write('\r');
1114
+ });
1115
+ await act(async () => {
1116
+ await vi.runAllTimersAsync();
1117
+ });
1118
+ // Verify that onSubmit was called normally
1119
+ expect(props.onSubmit).toHaveBeenCalledWith('normal command');
1120
+ unmount();
1121
+ });
1122
+ });
1123
+ describe('enhanced input UX - double ESC clear functionality', () => {
1124
+ it('should clear buffer on second ESC press', async () => {
1125
+ const onEscapePromptChange = vi.fn();
1126
+ props.onEscapePromptChange = onEscapePromptChange;
1127
+ props.buffer.setText('text to clear');
1128
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), { kittyProtocolEnabled: false });
1129
+ await act(async () => {
1130
+ stdin.write('\x1B');
1131
+ await waitFor(() => {
1132
+ expect(onEscapePromptChange).toHaveBeenCalledWith(false);
1133
+ });
1134
+ });
1135
+ await act(async () => {
1136
+ stdin.write('\x1B');
1137
+ await waitFor(() => {
1138
+ expect(props.buffer.setText).toHaveBeenCalledWith('');
1139
+ expect(mockCommandCompletion.resetCompletionState).toHaveBeenCalled();
1140
+ });
1141
+ });
1142
+ unmount();
1143
+ });
1144
+ it('should reset escape state on any non-ESC key', async () => {
1145
+ const onEscapePromptChange = vi.fn();
1146
+ props.onEscapePromptChange = onEscapePromptChange;
1147
+ props.buffer.setText('some text');
1148
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), { kittyProtocolEnabled: false });
1149
+ await act(async () => {
1150
+ stdin.write('\x1B');
1151
+ await waitFor(() => {
1152
+ expect(onEscapePromptChange).toHaveBeenCalledWith(false);
1153
+ });
1154
+ });
1155
+ await act(async () => {
1156
+ stdin.write('a');
1157
+ await waitFor(() => {
1158
+ expect(onEscapePromptChange).toHaveBeenCalledWith(false);
1159
+ });
1160
+ });
1161
+ unmount();
1162
+ });
1163
+ it('should handle ESC in shell mode by disabling shell mode', async () => {
1164
+ props.shellModeActive = true;
1165
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), { kittyProtocolEnabled: false });
1166
+ await act(async () => {
1167
+ stdin.write('\x1B');
1168
+ await waitFor(() => expect(props.setShellModeActive).toHaveBeenCalledWith(false));
1169
+ });
1170
+ unmount();
1171
+ });
1172
+ it('should handle ESC when completion suggestions are showing', async () => {
1173
+ mockedUseCommandCompletion.mockReturnValue({
1174
+ ...mockCommandCompletion,
1175
+ showSuggestions: true,
1176
+ suggestions: [{ label: 'suggestion', value: 'suggestion' }],
1177
+ });
1178
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), { kittyProtocolEnabled: false });
1179
+ await act(async () => {
1180
+ stdin.write('\x1B');
1181
+ });
1182
+ await waitFor(() => expect(mockCommandCompletion.resetCompletionState).toHaveBeenCalled());
1183
+ unmount();
1184
+ });
1185
+ it('should not call onEscapePromptChange when not provided', async () => {
1186
+ vi.useFakeTimers();
1187
+ props.onEscapePromptChange = undefined;
1188
+ props.buffer.setText('some text');
1189
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), { kittyProtocolEnabled: false });
1190
+ await act(async () => {
1191
+ await vi.runAllTimersAsync();
1192
+ });
1193
+ await act(async () => {
1194
+ stdin.write('\x1B');
1195
+ });
1196
+ await act(async () => {
1197
+ await vi.runAllTimersAsync();
1198
+ });
1199
+ vi.useRealTimers();
1200
+ unmount();
1201
+ });
1202
+ it('should not interfere with existing keyboard shortcuts', async () => {
1203
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), { kittyProtocolEnabled: false });
1204
+ await act(async () => {
1205
+ stdin.write('\x0C');
1206
+ });
1207
+ await waitFor(() => expect(props.onClearScreen).toHaveBeenCalled());
1208
+ await act(async () => {
1209
+ stdin.write('\x01');
1210
+ });
1211
+ await waitFor(() => expect(props.buffer.move).toHaveBeenCalledWith('home'));
1212
+ unmount();
1213
+ });
1214
+ });
1215
+ describe('reverse search', () => {
1216
+ beforeEach(async () => {
1217
+ props.shellModeActive = true;
1218
+ vi.mocked(useShellHistory).mockReturnValue({
1219
+ history: ['echo hello', 'echo world', 'ls'],
1220
+ getPreviousCommand: vi.fn(),
1221
+ getNextCommand: vi.fn(),
1222
+ addCommandToHistory: vi.fn(),
1223
+ resetHistoryPosition: vi.fn(),
1224
+ });
1225
+ });
1226
+ it('invokes reverse search on Ctrl+R', async () => {
1227
+ // Mock the reverse search completion to return suggestions
1228
+ mockedUseReverseSearchCompletion.mockReturnValue({
1229
+ ...mockReverseSearchCompletion,
1230
+ suggestions: [
1231
+ { label: 'echo hello', value: 'echo hello' },
1232
+ { label: 'echo world', value: 'echo world' },
1233
+ { label: 'ls', value: 'ls' },
1234
+ ],
1235
+ showSuggestions: true,
1236
+ activeSuggestionIndex: 0,
1237
+ });
1238
+ const { stdin, stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1239
+ // Trigger reverse search with Ctrl+R
1240
+ await act(async () => {
1241
+ stdin.write('\x12');
1242
+ });
1243
+ await waitFor(() => {
1244
+ const frame = stdout.lastFrame();
1245
+ expect(frame).toContain('(r:)');
1246
+ expect(frame).toContain('echo hello');
1247
+ expect(frame).toContain('echo world');
1248
+ expect(frame).toContain('ls');
1249
+ });
1250
+ unmount();
1251
+ });
1252
+ it.each([
1253
+ { name: 'standard', kittyProtocolEnabled: false, escapeSequence: '\x1B' },
1254
+ {
1255
+ name: 'kitty',
1256
+ kittyProtocolEnabled: true,
1257
+ escapeSequence: '\u001b[27u',
1258
+ },
1259
+ ])('resets reverse search state on Escape ($name)', async ({ kittyProtocolEnabled, escapeSequence }) => {
1260
+ const { stdin, stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), { kittyProtocolEnabled });
1261
+ await act(async () => {
1262
+ stdin.write('\x12');
1263
+ });
1264
+ // Wait for reverse search to be active
1265
+ await waitFor(() => {
1266
+ expect(stdout.lastFrame()).toContain('(r:)');
1267
+ });
1268
+ await act(async () => {
1269
+ stdin.write(escapeSequence);
1270
+ });
1271
+ await waitFor(() => {
1272
+ expect(stdout.lastFrame()).not.toContain('(r:)');
1273
+ expect(stdout.lastFrame()).not.toContain('echo hello');
1274
+ });
1275
+ unmount();
1276
+ });
1277
+ it('completes the highlighted entry on Tab and exits reverse-search', async () => {
1278
+ // Mock the reverse search completion
1279
+ const mockHandleAutocomplete = vi.fn(() => {
1280
+ props.buffer.setText('echo hello');
1281
+ });
1282
+ mockedUseReverseSearchCompletion.mockImplementation((buffer, shellHistory, reverseSearchActive) => ({
1283
+ ...mockReverseSearchCompletion,
1284
+ suggestions: reverseSearchActive
1285
+ ? [
1286
+ { label: 'echo hello', value: 'echo hello' },
1287
+ { label: 'echo world', value: 'echo world' },
1288
+ { label: 'ls', value: 'ls' },
1289
+ ]
1290
+ : [],
1291
+ showSuggestions: reverseSearchActive,
1292
+ activeSuggestionIndex: reverseSearchActive ? 0 : -1,
1293
+ handleAutocomplete: mockHandleAutocomplete,
1294
+ }));
1295
+ const { stdin, stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1296
+ // Enter reverse search mode with Ctrl+R
1297
+ await act(async () => {
1298
+ stdin.write('\x12');
1299
+ });
1300
+ // Verify reverse search is active
1301
+ await waitFor(() => {
1302
+ expect(stdout.lastFrame()).toContain('(r:)');
1303
+ });
1304
+ // Press Tab to complete the highlighted entry
1305
+ await act(async () => {
1306
+ stdin.write('\t');
1307
+ });
1308
+ await waitFor(() => {
1309
+ expect(mockHandleAutocomplete).toHaveBeenCalledWith(0);
1310
+ expect(props.buffer.setText).toHaveBeenCalledWith('echo hello');
1311
+ });
1312
+ unmount();
1313
+ }, 15000);
1314
+ it('submits the highlighted entry on Enter and exits reverse-search', async () => {
1315
+ // Mock the reverse search completion to return suggestions
1316
+ mockedUseReverseSearchCompletion.mockReturnValue({
1317
+ ...mockReverseSearchCompletion,
1318
+ suggestions: [
1319
+ { label: 'echo hello', value: 'echo hello' },
1320
+ { label: 'echo world', value: 'echo world' },
1321
+ { label: 'ls', value: 'ls' },
1322
+ ],
1323
+ showSuggestions: true,
1324
+ activeSuggestionIndex: 0,
1325
+ });
1326
+ const { stdin, stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1327
+ await act(async () => {
1328
+ stdin.write('\x12');
1329
+ });
1330
+ await waitFor(() => {
1331
+ expect(stdout.lastFrame()).toContain('(r:)');
1332
+ });
1333
+ await act(async () => {
1334
+ stdin.write('\r');
1335
+ });
1336
+ await waitFor(() => {
1337
+ expect(stdout.lastFrame()).not.toContain('(r:)');
1338
+ });
1339
+ expect(props.onSubmit).toHaveBeenCalledWith('echo hello');
1340
+ unmount();
1341
+ });
1342
+ it('should restore text and cursor position after reverse search"', async () => {
1343
+ const initialText = 'initial text';
1344
+ const initialCursor = [0, 3];
1345
+ props.buffer.setText(initialText);
1346
+ props.buffer.cursor = initialCursor;
1347
+ // Mock the reverse search completion to be active and then reset
1348
+ mockedUseReverseSearchCompletion.mockImplementation((buffer, shellHistory, reverseSearchActiveFromInputPrompt) => ({
1349
+ ...mockReverseSearchCompletion,
1350
+ suggestions: reverseSearchActiveFromInputPrompt
1351
+ ? [{ label: 'history item', value: 'history item' }]
1352
+ : [],
1353
+ showSuggestions: reverseSearchActiveFromInputPrompt,
1354
+ }));
1355
+ const { stdin, stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1356
+ // reverse search with Ctrl+R
1357
+ await act(async () => {
1358
+ stdin.write('\x12');
1359
+ });
1360
+ await waitFor(() => {
1361
+ expect(stdout.lastFrame()).toContain('(r:)');
1362
+ });
1363
+ // Press kitty escape key
1364
+ await act(async () => {
1365
+ stdin.write('\u001b[27u');
1366
+ });
1367
+ await waitFor(() => {
1368
+ expect(stdout.lastFrame()).not.toContain('(r:)');
1369
+ expect(props.buffer.text).toBe(initialText);
1370
+ expect(props.buffer.cursor).toEqual(initialCursor);
1371
+ });
1372
+ unmount();
1373
+ });
1374
+ });
1375
+ describe('Ctrl+E keyboard shortcut', () => {
1376
+ it('should move cursor to end of current line in multiline input', async () => {
1377
+ props.buffer.text = 'line 1\nline 2\nline 3';
1378
+ props.buffer.cursor = [1, 2];
1379
+ props.buffer.lines = ['line 1', 'line 2', 'line 3'];
1380
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1381
+ await act(async () => {
1382
+ stdin.write('\x05'); // Ctrl+E
1383
+ });
1384
+ await waitFor(() => {
1385
+ expect(props.buffer.move).toHaveBeenCalledWith('end');
1386
+ });
1387
+ expect(props.buffer.moveToOffset).not.toHaveBeenCalled();
1388
+ unmount();
1389
+ });
1390
+ it('should move cursor to end of current line for single line input', async () => {
1391
+ props.buffer.text = 'single line text';
1392
+ props.buffer.cursor = [0, 5];
1393
+ props.buffer.lines = ['single line text'];
1394
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1395
+ await act(async () => {
1396
+ stdin.write('\x05'); // Ctrl+E
1397
+ });
1398
+ await waitFor(() => {
1399
+ expect(props.buffer.move).toHaveBeenCalledWith('end');
1400
+ });
1401
+ expect(props.buffer.moveToOffset).not.toHaveBeenCalled();
1402
+ unmount();
1403
+ });
1404
+ });
1405
+ describe('command search (Ctrl+R when not in shell)', () => {
1406
+ it('enters command search on Ctrl+R and shows suggestions', async () => {
1407
+ props.shellModeActive = false;
1408
+ vi.mocked(useReverseSearchCompletion).mockImplementation((buffer, data, isActive) => ({
1409
+ ...mockReverseSearchCompletion,
1410
+ suggestions: isActive
1411
+ ? [
1412
+ { label: 'git commit -m "msg"', value: 'git commit -m "msg"' },
1413
+ { label: 'git push', value: 'git push' },
1414
+ ]
1415
+ : [],
1416
+ showSuggestions: !!isActive,
1417
+ activeSuggestionIndex: isActive ? 0 : -1,
1418
+ }));
1419
+ const { stdin, stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1420
+ await act(async () => {
1421
+ stdin.write('\x12'); // Ctrl+R
1422
+ });
1423
+ await waitFor(() => {
1424
+ const frame = stdout.lastFrame() ?? '';
1425
+ expect(frame).toContain('(r:)');
1426
+ expect(frame).toContain('git commit');
1427
+ expect(frame).toContain('git push');
1428
+ });
1429
+ unmount();
1430
+ });
1431
+ it('expands and collapses long suggestion via Right/Left arrows', async () => {
1432
+ props.shellModeActive = false;
1433
+ const longValue = 'l'.repeat(200);
1434
+ vi.mocked(useReverseSearchCompletion).mockReturnValue({
1435
+ ...mockReverseSearchCompletion,
1436
+ suggestions: [{ label: longValue, value: longValue, matchedIndex: 0 }],
1437
+ showSuggestions: true,
1438
+ activeSuggestionIndex: 0,
1439
+ visibleStartIndex: 0,
1440
+ isLoadingSuggestions: false,
1441
+ });
1442
+ const { stdin, stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1443
+ await act(async () => {
1444
+ stdin.write('\x12');
1445
+ });
1446
+ await waitFor(() => {
1447
+ expect(clean(stdout.lastFrame())).toContain('→');
1448
+ });
1449
+ await act(async () => {
1450
+ stdin.write('\u001B[C');
1451
+ });
1452
+ await waitFor(() => {
1453
+ expect(clean(stdout.lastFrame())).toContain('←');
1454
+ });
1455
+ expect(stdout.lastFrame()).toMatchSnapshot('command-search-render-expanded-match');
1456
+ await act(async () => {
1457
+ stdin.write('\u001B[D');
1458
+ });
1459
+ await waitFor(() => {
1460
+ expect(clean(stdout.lastFrame())).toContain('→');
1461
+ });
1462
+ expect(stdout.lastFrame()).toMatchSnapshot('command-search-render-collapsed-match');
1463
+ unmount();
1464
+ });
1465
+ it('renders match window and expanded view (snapshots)', async () => {
1466
+ props.shellModeActive = false;
1467
+ props.buffer.setText('commit');
1468
+ const label = 'git commit -m "feat: add search" in src/app';
1469
+ const matchedIndex = label.indexOf('commit');
1470
+ vi.mocked(useReverseSearchCompletion).mockReturnValue({
1471
+ ...mockReverseSearchCompletion,
1472
+ suggestions: [{ label, value: label, matchedIndex }],
1473
+ showSuggestions: true,
1474
+ activeSuggestionIndex: 0,
1475
+ visibleStartIndex: 0,
1476
+ isLoadingSuggestions: false,
1477
+ });
1478
+ const { stdin, stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1479
+ await act(async () => {
1480
+ stdin.write('\x12');
1481
+ });
1482
+ await waitFor(() => {
1483
+ expect(stdout.lastFrame()).toMatchSnapshot('command-search-render-collapsed-match');
1484
+ });
1485
+ await act(async () => {
1486
+ stdin.write('\u001B[C');
1487
+ });
1488
+ await waitFor(() => {
1489
+ expect(stdout.lastFrame()).toMatchSnapshot('command-search-render-expanded-match');
1490
+ });
1491
+ unmount();
1492
+ });
1493
+ it('does not show expand/collapse indicator for short suggestions', async () => {
1494
+ props.shellModeActive = false;
1495
+ const shortValue = 'echo hello';
1496
+ vi.mocked(useReverseSearchCompletion).mockReturnValue({
1497
+ ...mockReverseSearchCompletion,
1498
+ suggestions: [{ label: shortValue, value: shortValue }],
1499
+ showSuggestions: true,
1500
+ activeSuggestionIndex: 0,
1501
+ visibleStartIndex: 0,
1502
+ isLoadingSuggestions: false,
1503
+ });
1504
+ const { stdin, stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1505
+ await act(async () => {
1506
+ stdin.write('\x12');
1507
+ });
1508
+ await waitFor(() => {
1509
+ const frame = clean(stdout.lastFrame());
1510
+ // Ensure it rendered the search mode
1511
+ expect(frame).toContain('(r:)');
1512
+ expect(frame).not.toContain('→');
1513
+ expect(frame).not.toContain('←');
1514
+ });
1515
+ unmount();
1516
+ });
1517
+ });
1518
+ describe('mouse interaction', () => {
1519
+ it.each([
1520
+ {
1521
+ name: 'first line, first char',
1522
+ relX: 0,
1523
+ relY: 0,
1524
+ mouseCol: 5,
1525
+ mouseRow: 2,
1526
+ },
1527
+ {
1528
+ name: 'first line, middle char',
1529
+ relX: 6,
1530
+ relY: 0,
1531
+ mouseCol: 11,
1532
+ mouseRow: 2,
1533
+ },
1534
+ {
1535
+ name: 'second line, first char',
1536
+ relX: 0,
1537
+ relY: 1,
1538
+ mouseCol: 5,
1539
+ mouseRow: 3,
1540
+ },
1541
+ {
1542
+ name: 'second line, end char',
1543
+ relX: 5,
1544
+ relY: 1,
1545
+ mouseCol: 10,
1546
+ mouseRow: 3,
1547
+ },
1548
+ ])('should move cursor on mouse click - $name', async ({ relX, relY, mouseCol, mouseRow }) => {
1549
+ props.buffer.text = 'hello world\nsecond line';
1550
+ props.buffer.lines = ['hello world', 'second line'];
1551
+ props.buffer.viewportVisualLines = ['hello world', 'second line'];
1552
+ props.buffer.visualToLogicalMap = [
1553
+ [0, 0],
1554
+ [1, 0],
1555
+ ];
1556
+ props.buffer.visualCursor = [0, 11];
1557
+ props.buffer.visualScrollRow = 0;
1558
+ const { stdin, stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), { mouseEventsEnabled: true });
1559
+ // Wait for initial render
1560
+ await waitFor(() => {
1561
+ expect(stdout.lastFrame()).toContain('hello world');
1562
+ });
1563
+ // Simulate left mouse press at calculated coordinates.
1564
+ // Assumes inner box is at x=4, y=1 based on border(1)+padding(1)+prompt(2) and border-top(1).
1565
+ await act(async () => {
1566
+ stdin.write(`\x1b[<0;${mouseCol};${mouseRow}M`);
1567
+ });
1568
+ await waitFor(() => {
1569
+ expect(props.buffer.moveToVisualPosition).toHaveBeenCalledWith(relY, relX);
1570
+ });
1571
+ unmount();
1572
+ });
1573
+ });
1574
+ describe('queued message editing', () => {
1575
+ it('should load all queued messages when up arrow is pressed with empty input', async () => {
1576
+ const mockPopAllMessages = vi.fn();
1577
+ props.popAllMessages = mockPopAllMessages;
1578
+ props.buffer.text = '';
1579
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1580
+ await act(async () => {
1581
+ stdin.write('\u001B[A');
1582
+ });
1583
+ await waitFor(() => expect(mockPopAllMessages).toHaveBeenCalled());
1584
+ const callback = mockPopAllMessages.mock.calls[0][0];
1585
+ await act(async () => {
1586
+ callback('Message 1\n\nMessage 2\n\nMessage 3');
1587
+ });
1588
+ expect(props.buffer.setText).toHaveBeenCalledWith('Message 1\n\nMessage 2\n\nMessage 3');
1589
+ unmount();
1590
+ });
1591
+ it('should not load queued messages when input is not empty', async () => {
1592
+ const mockPopAllMessages = vi.fn();
1593
+ props.popAllMessages = mockPopAllMessages;
1594
+ props.buffer.text = 'some text';
1595
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1596
+ await act(async () => {
1597
+ stdin.write('\u001B[A');
1598
+ });
1599
+ await waitFor(() => expect(mockInputHistory.navigateUp).toHaveBeenCalled());
1600
+ expect(mockPopAllMessages).not.toHaveBeenCalled();
1601
+ unmount();
1602
+ });
1603
+ it('should handle undefined messages from popAllMessages', async () => {
1604
+ const mockPopAllMessages = vi.fn();
1605
+ props.popAllMessages = mockPopAllMessages;
1606
+ props.buffer.text = '';
1607
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1608
+ await act(async () => {
1609
+ stdin.write('\u001B[A');
1610
+ });
1611
+ await waitFor(() => expect(mockPopAllMessages).toHaveBeenCalled());
1612
+ const callback = mockPopAllMessages.mock.calls[0][0];
1613
+ await act(async () => {
1614
+ callback(undefined);
1615
+ });
1616
+ expect(props.buffer.setText).not.toHaveBeenCalled();
1617
+ expect(mockInputHistory.navigateUp).toHaveBeenCalled();
1618
+ unmount();
1619
+ });
1620
+ it('should work with NAVIGATION_UP key as well', async () => {
1621
+ const mockPopAllMessages = vi.fn();
1622
+ props.popAllMessages = mockPopAllMessages;
1623
+ props.buffer.text = '';
1624
+ props.buffer.allVisualLines = [''];
1625
+ props.buffer.visualCursor = [0, 0];
1626
+ props.buffer.visualScrollRow = 0;
1627
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1628
+ await act(async () => {
1629
+ stdin.write('\u001B[A');
1630
+ });
1631
+ await waitFor(() => expect(mockPopAllMessages).toHaveBeenCalled());
1632
+ unmount();
1633
+ });
1634
+ it('should handle single queued message', async () => {
1635
+ const mockPopAllMessages = vi.fn();
1636
+ props.popAllMessages = mockPopAllMessages;
1637
+ props.buffer.text = '';
1638
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1639
+ await act(async () => {
1640
+ stdin.write('\u001B[A');
1641
+ });
1642
+ await waitFor(() => expect(mockPopAllMessages).toHaveBeenCalled());
1643
+ const callback = mockPopAllMessages.mock.calls[0][0];
1644
+ await act(async () => {
1645
+ callback('Single message');
1646
+ });
1647
+ expect(props.buffer.setText).toHaveBeenCalledWith('Single message');
1648
+ unmount();
1649
+ });
1650
+ it('should only check for queued messages when buffer text is trimmed empty', async () => {
1651
+ const mockPopAllMessages = vi.fn();
1652
+ props.popAllMessages = mockPopAllMessages;
1653
+ props.buffer.text = ' '; // Whitespace only
1654
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1655
+ await act(async () => {
1656
+ stdin.write('\u001B[A');
1657
+ });
1658
+ await waitFor(() => expect(mockPopAllMessages).toHaveBeenCalled());
1659
+ unmount();
1660
+ });
1661
+ it('should not call popAllMessages if it is not provided', async () => {
1662
+ props.popAllMessages = undefined;
1663
+ props.buffer.text = '';
1664
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1665
+ await act(async () => {
1666
+ stdin.write('\u001B[A');
1667
+ });
1668
+ await waitFor(() => expect(mockInputHistory.navigateUp).toHaveBeenCalled());
1669
+ unmount();
1670
+ });
1671
+ it('should navigate input history on fresh start when no queued messages exist', async () => {
1672
+ const mockPopAllMessages = vi.fn();
1673
+ props.popAllMessages = mockPopAllMessages;
1674
+ props.buffer.text = '';
1675
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1676
+ await act(async () => {
1677
+ stdin.write('\u001B[A');
1678
+ });
1679
+ await waitFor(() => expect(mockPopAllMessages).toHaveBeenCalled());
1680
+ const callback = mockPopAllMessages.mock.calls[0][0];
1681
+ await act(async () => {
1682
+ callback(undefined);
1683
+ });
1684
+ expect(mockInputHistory.navigateUp).toHaveBeenCalled();
1685
+ expect(props.buffer.setText).not.toHaveBeenCalled();
1686
+ unmount();
1687
+ });
1688
+ });
1689
+ describe('snapshots', () => {
1690
+ it('should render correctly in shell mode', async () => {
1691
+ props.shellModeActive = true;
1692
+ const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1693
+ await waitFor(() => expect(stdout.lastFrame()).toMatchSnapshot());
1694
+ unmount();
1695
+ });
1696
+ it('should render correctly when accepting edits', async () => {
1697
+ props.approvalMode = ApprovalMode.AUTO_EDIT;
1698
+ const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1699
+ await waitFor(() => expect(stdout.lastFrame()).toMatchSnapshot());
1700
+ unmount();
1701
+ });
1702
+ it('should render correctly in yolo mode', async () => {
1703
+ props.approvalMode = ApprovalMode.YOLO;
1704
+ const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1705
+ await waitFor(() => expect(stdout.lastFrame()).toMatchSnapshot());
1706
+ unmount();
1707
+ });
1708
+ it('should not show inverted cursor when shell is focused', async () => {
1709
+ props.isEmbeddedShellFocused = true;
1710
+ props.focus = false;
1711
+ const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1712
+ await waitFor(() => {
1713
+ expect(stdout.lastFrame()).not.toContain(`{chalk.inverse(' ')}`);
1714
+ // This snapshot is good to make sure there was an input prompt but does
1715
+ // not show the inverted cursor because snapshots do not show colors.
1716
+ expect(stdout.lastFrame()).toMatchSnapshot();
1717
+ });
1718
+ unmount();
1719
+ });
1720
+ });
1721
+ it('should still allow input when shell is not focused', async () => {
1722
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
1723
+ shellFocus: false,
1724
+ });
1725
+ await act(async () => {
1726
+ stdin.write('a');
1727
+ });
1728
+ await waitFor(() => expect(mockBuffer.handleInput).toHaveBeenCalled());
1729
+ unmount();
1730
+ });
1731
+ describe('command queuing while streaming', () => {
1732
+ beforeEach(() => {
1733
+ props.streamingState = StreamingState.Responding;
1734
+ props.setQueueErrorMessage = vi.fn();
1735
+ props.onSubmit = vi.fn();
1736
+ });
1737
+ it.each([
1738
+ {
1739
+ name: 'should prevent slash commands',
1740
+ bufferText: '/help',
1741
+ shellMode: false,
1742
+ shouldSubmit: false,
1743
+ errorMessage: 'Slash commands cannot be queued',
1744
+ },
1745
+ {
1746
+ name: 'should prevent shell commands',
1747
+ bufferText: 'ls',
1748
+ shellMode: true,
1749
+ shouldSubmit: false,
1750
+ errorMessage: 'Shell commands cannot be queued',
1751
+ },
1752
+ {
1753
+ name: 'should allow regular messages',
1754
+ bufferText: 'regular message',
1755
+ shellMode: false,
1756
+ shouldSubmit: true,
1757
+ errorMessage: null,
1758
+ },
1759
+ ])('$name', async ({ bufferText, shellMode, shouldSubmit, errorMessage }) => {
1760
+ props.buffer.text = bufferText;
1761
+ props.shellModeActive = shellMode;
1762
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1763
+ await act(async () => {
1764
+ stdin.write('\r');
1765
+ });
1766
+ await waitFor(() => {
1767
+ if (shouldSubmit) {
1768
+ expect(props.onSubmit).toHaveBeenCalledWith(bufferText);
1769
+ expect(props.setQueueErrorMessage).not.toHaveBeenCalled();
1770
+ }
1771
+ else {
1772
+ expect(props.onSubmit).not.toHaveBeenCalled();
1773
+ expect(props.setQueueErrorMessage).toHaveBeenCalledWith(errorMessage);
1774
+ }
1775
+ });
1776
+ unmount();
1777
+ });
1778
+ });
1779
+ });
1780
+ function clean(str) {
1781
+ if (!str)
1782
+ return '';
1783
+ // Remove ANSI escape codes and trim whitespace
1784
+ return stripAnsi(str).trim();
1785
+ }
1786
+ //# sourceMappingURL=InputPrompt.test.js.map