@mmmbuto/gemini-cli-termux 0.30.3-termux → 0.30.5-termux

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 (400) hide show
  1. package/README.md +42 -193
  2. package/bundle/docs/cli/settings.md +8 -9
  3. package/bundle/docs/extensions/reference.md +14 -38
  4. package/bundle/docs/hooks/reference.md +0 -8
  5. package/bundle/docs/reference/configuration.md +0 -8
  6. package/bundle/gemini.js +10730 -11704
  7. package/bundle/node_modules/@google/gemini-cli-devtools/package.json +1 -1
  8. package/bundle/package.json +2 -1
  9. package/package.json +67 -5
  10. package/packages/cli/dist/index.js +0 -0
  11. package/packages/cli/dist/package.json +2 -2
  12. package/packages/cli/dist/src/commands/extensions/examples/custom-commands/gemini-extension.json +2 -2
  13. package/packages/cli/dist/src/commands/extensions/examples/exclude-tools/gemini-extension.json +3 -3
  14. package/packages/cli/dist/src/commands/extensions/examples/hooks/gemini-extension.json +2 -2
  15. package/packages/cli/dist/src/commands/extensions/examples/hooks/hooks/hooks.json +11 -11
  16. package/packages/cli/dist/src/commands/extensions/examples/mcp-server/example.js +1 -1
  17. package/packages/cli/dist/src/commands/extensions/examples/mcp-server/gemini-extension.json +8 -8
  18. package/packages/cli/dist/src/commands/extensions/examples/mcp-server/package.json +9 -9
  19. package/packages/cli/dist/src/commands/extensions/examples/skills/gemini-extension.json +2 -2
  20. package/packages/cli/dist/src/commands/extensions/examples/themes-example/README.md +8 -5
  21. package/packages/cli/dist/src/commands/extensions/examples/themes-example/gemini-extension.json +27 -27
  22. package/packages/cli/dist/src/config/config.js +4 -65
  23. package/packages/cli/dist/src/config/config.js.map +1 -1
  24. package/packages/cli/dist/src/config/settingsSchema.d.ts +1 -235
  25. package/packages/cli/dist/src/config/settingsSchema.js +1 -235
  26. package/packages/cli/dist/src/config/settingsSchema.js.map +1 -1
  27. package/packages/cli/dist/src/gemini.js +0 -2
  28. package/packages/cli/dist/src/gemini.js.map +1 -1
  29. package/packages/cli/dist/src/generated/git-commit.d.ts +2 -2
  30. package/packages/cli/dist/src/generated/git-commit.js +2 -2
  31. package/packages/cli/dist/src/patches/empty-module.d.ts +2 -0
  32. package/packages/cli/dist/src/patches/empty-module.js +2 -0
  33. package/packages/cli/dist/src/patches/empty-module.js.map +1 -0
  34. package/packages/cli/dist/src/services/McpPromptLoader.js +3 -6
  35. package/packages/cli/dist/src/services/McpPromptLoader.js.map +1 -1
  36. package/packages/cli/dist/src/ui/components/AgentConfigDialog.js +19 -5
  37. package/packages/cli/dist/src/ui/components/AgentConfigDialog.js.map +1 -1
  38. package/packages/cli/dist/src/ui/components/AgentConfigDialog.test.js +41 -19
  39. package/packages/cli/dist/src/ui/components/AgentConfigDialog.test.js.map +1 -1
  40. package/packages/cli/dist/src/ui/components/DialogManager.d.ts +1 -1
  41. package/packages/cli/dist/src/ui/components/DialogManager.js +34 -7
  42. package/packages/cli/dist/src/ui/components/DialogManager.js.map +1 -1
  43. package/packages/cli/dist/src/ui/components/InputPrompt.d.ts +9 -0
  44. package/packages/cli/dist/src/ui/components/InputPrompt.js +350 -125
  45. package/packages/cli/dist/src/ui/components/InputPrompt.js.map +1 -1
  46. package/packages/cli/dist/src/ui/components/InputPrompt.test.js +788 -35
  47. package/packages/cli/dist/src/ui/components/InputPrompt.test.js.map +1 -1
  48. package/packages/cli/dist/src/ui/components/messages/ShellToolMessage.js +2 -2
  49. package/packages/cli/dist/src/ui/components/messages/ShellToolMessage.js.map +1 -1
  50. package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessage.js +4 -77
  51. package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
  52. package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessage.test.js +0 -51
  53. package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessage.test.js.map +1 -1
  54. package/packages/cli/dist/src/ui/components/messages/ToolMessage.js +2 -2
  55. package/packages/cli/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
  56. package/packages/cli/dist/src/ui/components/messages/ToolShared.d.ts +0 -1
  57. package/packages/cli/dist/src/ui/components/messages/ToolShared.js +2 -2
  58. package/packages/cli/dist/src/ui/components/messages/ToolShared.js.map +1 -1
  59. package/packages/cli/dist/src/ui/components/shared/BaseSettingsDialog.js +4 -1
  60. package/packages/cli/dist/src/ui/components/shared/BaseSettingsDialog.js.map +1 -1
  61. package/packages/cli/dist/src/ui/components/shared/BaseSettingsDialog.test.js +139 -40
  62. package/packages/cli/dist/src/ui/components/shared/BaseSettingsDialog.test.js.map +1 -1
  63. package/packages/cli/dist/src/ui/components/shared/TextInput.js +2 -1
  64. package/packages/cli/dist/src/ui/components/shared/TextInput.js.map +1 -1
  65. package/packages/cli/dist/src/ui/hooks/toolMapping.js +0 -1
  66. package/packages/cli/dist/src/ui/hooks/toolMapping.js.map +1 -1
  67. package/packages/cli/dist/src/ui/hooks/toolMapping.test.js +0 -13
  68. package/packages/cli/dist/src/ui/hooks/toolMapping.test.js.map +1 -1
  69. package/packages/cli/dist/src/ui/hooks/useToolScheduler.js +2 -18
  70. package/packages/cli/dist/src/ui/hooks/useToolScheduler.js.map +1 -1
  71. package/packages/cli/dist/src/ui/hooks/useToolScheduler.test.js +0 -44
  72. package/packages/cli/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  73. package/packages/cli/dist/src/ui/keyMatchers.test.js.map +1 -1
  74. package/packages/cli/dist/src/ui/types.d.ts +0 -1
  75. package/packages/cli/dist/src/ui/types.js.map +1 -1
  76. package/packages/cli/dist/src/ui/utils/commandUtils.js +4 -1
  77. package/packages/cli/dist/src/ui/utils/commandUtils.js.map +1 -1
  78. package/packages/cli/dist/src/utils/activityLogger.js +23 -94
  79. package/packages/cli/dist/src/utils/activityLogger.js.map +1 -1
  80. package/packages/cli/dist/src/utils/handleAutoUpdate.d.ts +1 -1
  81. package/packages/cli/dist/src/utils/handleAutoUpdate.js +1 -1
  82. package/packages/cli/dist/src/utils/handleAutoUpdate.js.map +1 -1
  83. package/packages/cli/dist/src/utils/handleAutoUpdate.test.js +11 -11
  84. package/packages/cli/dist/src/utils/handleAutoUpdate.test.js.map +1 -1
  85. package/{bundle/sandbox-macos-restrictive-closed.sb → packages/cli/dist/src/utils/sandbox-macos-strict-open.sb} +42 -4
  86. package/packages/cli/dist/src/utils/{sandbox-macos-restrictive-closed.sb → sandbox-macos-strict-proxied.sb} +44 -4
  87. package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
  88. package/packages/core/dist/docs/admin/enterprise-controls.md +115 -0
  89. package/packages/core/dist/docs/changelogs/index.md +57 -1
  90. package/packages/core/dist/docs/changelogs/latest.md +357 -314
  91. package/packages/core/dist/docs/changelogs/preview.md +288 -411
  92. package/packages/core/dist/docs/cli/checkpointing.md +2 -3
  93. package/packages/core/dist/docs/cli/cli-reference.md +42 -28
  94. package/packages/core/dist/docs/cli/custom-commands.md +3 -0
  95. package/packages/core/dist/docs/cli/enterprise.md +28 -8
  96. package/packages/core/dist/docs/cli/gemini-md.md +21 -13
  97. package/packages/core/dist/docs/cli/headless.md +34 -372
  98. package/packages/core/dist/docs/cli/model.md +1 -1
  99. package/packages/core/dist/docs/cli/plan-mode.md +245 -0
  100. package/packages/core/dist/docs/cli/rewind.md +11 -11
  101. package/packages/core/dist/docs/cli/sandbox.md +6 -5
  102. package/packages/core/dist/docs/cli/session-management.md +61 -44
  103. package/packages/core/dist/docs/cli/settings.md +64 -49
  104. package/packages/core/dist/docs/cli/skills.md +29 -7
  105. package/packages/core/dist/docs/cli/telemetry.md +41 -5
  106. package/packages/core/dist/docs/cli/themes.md +88 -54
  107. package/packages/core/dist/docs/cli/trusted-folders.md +31 -0
  108. package/packages/core/dist/docs/cli/tutorials/automation.md +187 -0
  109. package/packages/core/dist/docs/cli/tutorials/file-management.md +142 -0
  110. package/packages/core/dist/docs/cli/tutorials/mcp-setup.md +105 -0
  111. package/packages/core/dist/docs/cli/tutorials/memory-management.md +126 -0
  112. package/packages/core/dist/docs/cli/tutorials/session-management.md +105 -0
  113. package/packages/core/dist/docs/cli/tutorials/shell-commands.md +107 -0
  114. package/packages/core/dist/docs/cli/tutorials/skills-getting-started.md +36 -31
  115. package/packages/core/dist/docs/cli/tutorials/task-planning.md +93 -0
  116. package/packages/core/dist/docs/cli/tutorials/web-tools.md +78 -0
  117. package/packages/core/dist/docs/core/index.md +7 -7
  118. package/packages/core/dist/docs/core/subagents.md +40 -40
  119. package/packages/core/dist/docs/extensions/best-practices.md +102 -53
  120. package/packages/core/dist/docs/extensions/index.md +37 -21
  121. package/packages/core/dist/docs/extensions/reference.md +148 -219
  122. package/packages/core/dist/docs/extensions/releasing.md +93 -122
  123. package/packages/core/dist/docs/extensions/writing-extensions.md +87 -76
  124. package/packages/core/dist/docs/get-started/authentication.md +4 -4
  125. package/packages/core/dist/docs/get-started/examples.md +39 -119
  126. package/packages/core/dist/docs/get-started/gemini-3.md +17 -3
  127. package/packages/core/dist/docs/get-started/index.md +16 -5
  128. package/packages/core/dist/docs/get-started/installation.md +110 -77
  129. package/packages/core/dist/docs/hooks/best-practices.md +1 -1
  130. package/packages/core/dist/docs/hooks/reference.md +2 -2
  131. package/packages/core/dist/docs/index.md +142 -149
  132. package/packages/core/dist/docs/redirects.json +19 -0
  133. package/packages/core/dist/docs/reference/commands.md +523 -0
  134. package/{bundle/docs/get-started → packages/core/dist/docs/reference}/configuration.md +180 -71
  135. package/packages/core/dist/docs/{cli → reference}/keyboard-shortcuts.md +49 -35
  136. package/packages/core/dist/docs/{core → reference}/policy-engine.md +76 -32
  137. package/packages/core/dist/docs/releases.md +2 -2
  138. package/{bundle/docs → packages/core/dist/docs/resources}/faq.md +1 -1
  139. package/packages/core/dist/docs/{quota-and-pricing.md → resources/quota-and-pricing.md} +12 -5
  140. package/{bundle/docs → packages/core/dist/docs/resources}/tos-privacy.md +3 -3
  141. package/packages/core/dist/docs/{troubleshooting.md → resources/troubleshooting.md} +1 -1
  142. package/packages/core/dist/docs/sidebar.json +194 -113
  143. package/packages/core/dist/docs/tools/activate-skill.md +43 -0
  144. package/packages/core/dist/docs/tools/ask-user.md +95 -0
  145. package/packages/core/dist/docs/tools/file-system.md +55 -143
  146. package/packages/core/dist/docs/tools/index.md +97 -93
  147. package/packages/core/dist/docs/tools/internal-docs.md +46 -0
  148. package/packages/core/dist/docs/tools/mcp-server.md +65 -16
  149. package/packages/core/dist/docs/tools/memory.md +21 -40
  150. package/packages/core/dist/docs/tools/planning.md +57 -0
  151. package/packages/core/dist/docs/tools/shell.md +44 -88
  152. package/packages/core/dist/docs/tools/todos.md +22 -44
  153. package/packages/core/dist/docs/tools/web-fetch.md +22 -46
  154. package/packages/core/dist/docs/tools/web-search.md +19 -29
  155. package/packages/core/dist/src/code_assist/types.d.ts +14 -14
  156. package/packages/core/dist/src/config/config.d.ts +1 -13
  157. package/packages/core/dist/src/config/config.js +6 -39
  158. package/packages/core/dist/src/config/config.js.map +1 -1
  159. package/packages/core/dist/src/confirmation-bus/types.d.ts +0 -3
  160. package/packages/core/dist/src/confirmation-bus/types.js.map +1 -1
  161. package/packages/core/dist/src/core/coreToolHookTriggers.d.ts +1 -1
  162. package/packages/core/dist/src/core/coreToolHookTriggers.js +3 -8
  163. package/packages/core/dist/src/core/coreToolHookTriggers.js.map +1 -1
  164. package/packages/core/dist/src/generated/git-commit.d.ts +2 -2
  165. package/packages/core/dist/src/generated/git-commit.js +2 -2
  166. package/packages/core/dist/src/hooks/hookEventHandler.d.ts +2 -2
  167. package/packages/core/dist/src/hooks/hookEventHandler.js +2 -8
  168. package/packages/core/dist/src/hooks/hookEventHandler.js.map +1 -1
  169. package/packages/core/dist/src/hooks/hookSystem.d.ts +2 -2
  170. package/packages/core/dist/src/hooks/hookSystem.js +4 -4
  171. package/packages/core/dist/src/hooks/hookSystem.js.map +1 -1
  172. package/packages/core/dist/src/hooks/types.d.ts +0 -18
  173. package/packages/core/dist/src/hooks/types.js +0 -17
  174. package/packages/core/dist/src/hooks/types.js.map +1 -1
  175. package/packages/core/dist/src/ide/ide-client.js +1 -1
  176. package/packages/core/dist/src/ide/ide-client.js.map +1 -1
  177. package/packages/core/dist/src/ide/types.d.ts +8 -8
  178. package/packages/core/dist/src/index.d.ts +2 -0
  179. package/packages/core/dist/src/index.js +2 -0
  180. package/packages/core/dist/src/index.js.map +1 -1
  181. package/packages/core/dist/src/policy/policies/plan.toml +29 -43
  182. package/packages/core/dist/src/policy/policies/read-only.toml +12 -11
  183. package/packages/core/dist/src/policy/policies/write.toml +11 -10
  184. package/packages/core/dist/src/policy/policies/yolo.toml +24 -12
  185. package/packages/core/dist/src/policy/policy-engine.js +1 -5
  186. package/packages/core/dist/src/policy/policy-engine.js.map +1 -1
  187. package/packages/core/dist/src/policy/types.d.ts +1 -2
  188. package/packages/core/dist/src/policy/types.js +0 -1
  189. package/packages/core/dist/src/policy/types.js.map +1 -1
  190. package/packages/core/dist/src/safety/context-builder.d.ts +3 -3
  191. package/packages/core/dist/src/safety/context-builder.js +4 -60
  192. package/packages/core/dist/src/safety/context-builder.js.map +1 -1
  193. package/packages/core/dist/src/safety/context-builder.test.js +18 -98
  194. package/packages/core/dist/src/safety/context-builder.test.js.map +1 -1
  195. package/packages/core/dist/src/safety/protocol.d.ts +0 -4
  196. package/packages/core/dist/src/safety/registry.d.ts +1 -2
  197. package/packages/core/dist/src/safety/registry.js +4 -14
  198. package/packages/core/dist/src/safety/registry.js.map +1 -1
  199. package/packages/core/dist/src/safety/registry.test.js +2 -5
  200. package/packages/core/dist/src/safety/registry.test.js.map +1 -1
  201. package/packages/core/dist/src/scheduler/scheduler.d.ts +1 -1
  202. package/packages/core/dist/src/scheduler/scheduler.js +4 -49
  203. package/packages/core/dist/src/scheduler/scheduler.js.map +1 -1
  204. package/packages/core/dist/src/scheduler/scheduler.test.js +0 -91
  205. package/packages/core/dist/src/scheduler/scheduler.test.js.map +1 -1
  206. package/packages/core/dist/src/scheduler/state-manager.d.ts +0 -6
  207. package/packages/core/dist/src/scheduler/state-manager.js +0 -12
  208. package/packages/core/dist/src/scheduler/state-manager.js.map +1 -1
  209. package/packages/core/dist/src/scheduler/tool-executor.js +7 -9
  210. package/packages/core/dist/src/scheduler/tool-executor.js.map +1 -1
  211. package/packages/core/dist/src/scheduler/tool-executor.test.js +1 -1
  212. package/packages/core/dist/src/scheduler/tool-executor.test.js.map +1 -1
  213. package/packages/core/dist/src/scheduler/types.d.ts +0 -13
  214. package/packages/core/dist/src/services/shellExecutionService.d.ts +1 -1
  215. package/packages/core/dist/src/services/shellExecutionService.js +7 -8
  216. package/packages/core/dist/src/services/shellExecutionService.js.map +1 -1
  217. package/packages/core/dist/src/services/shellExecutionService.test.js +5 -5
  218. package/packages/core/dist/src/services/shellExecutionService.test.js.map +1 -1
  219. package/packages/core/dist/src/services/test-data/resolved-aliases-retry.golden.json +251 -251
  220. package/packages/core/dist/src/services/test-data/resolved-aliases.golden.json +251 -251
  221. package/packages/core/dist/src/skills/builtin/skill-creator/scripts/init_skill.cjs +5 -1
  222. package/packages/core/dist/src/skills/builtin/skill-creator/scripts/package_skill.cjs +5 -1
  223. package/packages/core/dist/src/skills/builtin/skill-creator/scripts/validate_skill.cjs +5 -1
  224. package/packages/core/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +1 -3
  225. package/packages/core/dist/src/telemetry/clearcut-logger/clearcut-logger.js +0 -2
  226. package/packages/core/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  227. package/packages/core/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +1 -9
  228. package/packages/core/dist/src/telemetry/clearcut-logger/event-metadata-key.js +1 -19
  229. package/packages/core/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  230. package/packages/core/dist/src/telemetry/index.d.ts +1 -2
  231. package/packages/core/dist/src/telemetry/index.js +1 -2
  232. package/packages/core/dist/src/telemetry/index.js.map +1 -1
  233. package/packages/core/dist/src/telemetry/loggers.js +0 -4
  234. package/packages/core/dist/src/telemetry/loggers.js.map +1 -1
  235. package/packages/core/dist/src/telemetry/trace.js.map +1 -1
  236. package/packages/core/dist/src/telemetry/types.d.ts +0 -26
  237. package/packages/core/dist/src/telemetry/types.js +2 -76
  238. package/packages/core/dist/src/telemetry/types.js.map +1 -1
  239. package/packages/core/dist/src/tools/diffOptions.d.ts +2 -2
  240. package/packages/core/dist/src/tools/diffOptions.js.map +1 -1
  241. package/packages/core/dist/src/tools/mcp-client.test.js.map +1 -1
  242. package/packages/core/dist/src/tools/mcp-tool.d.ts +1 -3
  243. package/packages/core/dist/src/tools/mcp-tool.js +2 -9
  244. package/packages/core/dist/src/tools/mcp-tool.js.map +1 -1
  245. package/packages/core/dist/src/tools/memoryTool.d.ts +0 -10
  246. package/packages/core/dist/src/tools/memoryTool.js +30 -25
  247. package/packages/core/dist/src/tools/memoryTool.js.map +1 -1
  248. package/packages/core/dist/src/tools/tool-names.d.ts +6 -0
  249. package/packages/core/dist/src/tools/tool-names.js +16 -1
  250. package/packages/core/dist/src/tools/tool-names.js.map +1 -1
  251. package/packages/core/dist/src/tools/tools.d.ts +0 -11
  252. package/packages/core/dist/src/tools/tools.js.map +1 -1
  253. package/packages/core/dist/src/utils/getPty.d.ts +14 -1
  254. package/packages/core/dist/src/utils/getPty.js +67 -2
  255. package/packages/core/dist/src/utils/getPty.js.map +1 -1
  256. package/packages/core/dist/src/utils/memoryDiscovery.d.ts +11 -3
  257. package/packages/core/dist/src/utils/memoryDiscovery.js +56 -21
  258. package/packages/core/dist/src/utils/memoryDiscovery.js.map +1 -1
  259. package/packages/core/dist/src/utils/secure-browser-launcher.js +40 -29
  260. package/packages/core/dist/src/utils/secure-browser-launcher.js.map +1 -1
  261. package/packages/core/dist/src/utils/textUtils.d.ts +0 -9
  262. package/packages/core/dist/src/utils/textUtils.js +0 -15
  263. package/packages/core/dist/src/utils/textUtils.js.map +1 -1
  264. package/packages/core/dist/src/utils/textUtils.test.js +1 -42
  265. package/packages/core/dist/src/utils/textUtils.test.js.map +1 -1
  266. package/packages/core/dist/tsconfig.tsbuildinfo +1 -1
  267. package/packages/test-utils/dist/tsconfig.tsbuildinfo +1 -1
  268. package/bundle/docs/ROADMAP.md +0 -113
  269. package/bundle/docs/TERMUX.md +0 -95
  270. package/bundle/docs/architecture.md +0 -80
  271. package/bundle/docs/assets/hero.png +0 -0
  272. package/bundle/docs/cli/authentication.md +0 -3
  273. package/bundle/docs/cli/commands.md +0 -430
  274. package/bundle/docs/cli/context-memory.md +0 -69
  275. package/bundle/docs/cli/index.md +0 -65
  276. package/bundle/docs/cli/keyboard-shortcuts.md +0 -140
  277. package/bundle/docs/cli/tutorials.md +0 -87
  278. package/bundle/docs/core/policy-engine.md +0 -305
  279. package/bundle/docs/get-started/configuration-v1.md +0 -888
  280. package/bundle/docs/patches/README.md +0 -74
  281. package/bundle/docs/patches/mcp-sdk-typings-shim.md +0 -27
  282. package/bundle/docs/quota-and-pricing.md +0 -158
  283. package/bundle/docs/termux-api/COMMANDS.md +0 -592
  284. package/bundle/docs/termux-api/DISCOVERY_SETUP.md +0 -670
  285. package/bundle/docs/termux-api/EXECUTION_PLAN.md +0 -532
  286. package/bundle/docs/termux-api/MERGE_STRATEGY.md +0 -365
  287. package/bundle/docs/termux-api/PATCHES.md +0 -480
  288. package/bundle/docs/termux-api/README.md +0 -416
  289. package/bundle/docs/troubleshooting.md +0 -173
  290. package/bundle/policies/conseca.toml +0 -6
  291. package/bundle/sandbox-macos-permissive-closed.sb +0 -32
  292. package/packages/cli/README.md +0 -173
  293. package/packages/cli/dist/src/commands/extensions/examples/mcp-server/example.d.ts +0 -6
  294. package/packages/cli/dist/src/commands/extensions/examples/mcp-server/example.js.map +0 -1
  295. package/packages/cli/dist/src/commands/extensions/examples/mcp-server/example.test.d.ts +0 -6
  296. package/packages/cli/dist/src/commands/extensions/examples/mcp-server/example.test.js +0 -111
  297. package/packages/cli/dist/src/commands/extensions/examples/mcp-server/example.test.js.map +0 -1
  298. package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessageOverflow.test.d.ts +0 -6
  299. package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessageOverflow.test.js +0 -93
  300. package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessageOverflow.test.js.map +0 -1
  301. package/packages/cli/dist/src/ui/hooks/useReactToolScheduler.d.ts +0 -42
  302. package/packages/cli/dist/src/ui/hooks/useReactToolScheduler.js +0 -105
  303. package/packages/cli/dist/src/ui/hooks/useReactToolScheduler.js.map +0 -1
  304. package/packages/cli/dist/src/ui/hooks/useReactToolScheduler.test.d.ts +0 -6
  305. package/packages/cli/dist/src/ui/hooks/useReactToolScheduler.test.js +0 -58
  306. package/packages/cli/dist/src/ui/hooks/useReactToolScheduler.test.js.map +0 -1
  307. package/packages/cli/dist/src/ui/hooks/useRefreshMemoryCommand.d.ts +0 -6
  308. package/packages/cli/dist/src/ui/hooks/useRefreshMemoryCommand.js +0 -7
  309. package/packages/cli/dist/src/ui/hooks/useRefreshMemoryCommand.js.map +0 -1
  310. package/packages/cli/dist/src/ui/hooks/useShowMemoryCommand.d.ts +0 -9
  311. package/packages/cli/dist/src/ui/hooks/useShowMemoryCommand.js +0 -59
  312. package/packages/cli/dist/src/ui/hooks/useShowMemoryCommand.js.map +0 -1
  313. package/packages/cli/dist/src/ui/hooks/useToolExecutionScheduler.d.ts +0 -30
  314. package/packages/cli/dist/src/ui/hooks/useToolExecutionScheduler.js +0 -149
  315. package/packages/cli/dist/src/ui/hooks/useToolExecutionScheduler.js.map +0 -1
  316. package/packages/cli/dist/src/ui/hooks/useToolExecutionScheduler.test.d.ts +0 -6
  317. package/packages/cli/dist/src/ui/hooks/useToolExecutionScheduler.test.js +0 -376
  318. package/packages/cli/dist/src/ui/hooks/useToolExecutionScheduler.test.js.map +0 -1
  319. package/packages/cli/dist/src/ui/hooks/useToolSchedulerFacade.test.d.ts +0 -6
  320. package/packages/cli/dist/src/ui/hooks/useToolSchedulerFacade.test.js +0 -45
  321. package/packages/cli/dist/src/ui/hooks/useToolSchedulerFacade.test.js.map +0 -1
  322. package/packages/cli/dist/src/ui/utils/InlineMarkdownRenderer.test.d.ts +0 -6
  323. package/packages/cli/dist/src/ui/utils/InlineMarkdownRenderer.test.js +0 -21
  324. package/packages/cli/dist/src/ui/utils/InlineMarkdownRenderer.test.js.map +0 -1
  325. package/packages/cli/dist/src/ui/utils/terminalUtils.test.d.ts +0 -6
  326. package/packages/cli/dist/src/ui/utils/terminalUtils.test.js +0 -40
  327. package/packages/cli/dist/src/ui/utils/terminalUtils.test.js.map +0 -1
  328. package/packages/cli/dist/src/utils/checks.d.ts +0 -19
  329. package/packages/cli/dist/src/utils/checks.js +0 -24
  330. package/packages/cli/dist/src/utils/checks.js.map +0 -1
  331. package/packages/cli/dist/src/utils/checks.test.d.ts +0 -6
  332. package/packages/cli/dist/src/utils/checks.test.js +0 -29
  333. package/packages/cli/dist/src/utils/checks.test.js.map +0 -1
  334. package/packages/cli/dist/src/utils/sandbox-macos-permissive-closed.sb +0 -32
  335. package/packages/cli/index.js +0 -2
  336. package/packages/core/dist/docs/TERMUX.md +0 -95
  337. package/packages/core/dist/docs/architecture.md +0 -80
  338. package/packages/core/dist/docs/assets/hero.png +0 -0
  339. package/packages/core/dist/docs/cli/authentication.md +0 -3
  340. package/packages/core/dist/docs/cli/commands.md +0 -430
  341. package/packages/core/dist/docs/cli/context-memory.md +0 -69
  342. package/packages/core/dist/docs/cli/index.md +0 -65
  343. package/packages/core/dist/docs/cli/tutorials.md +0 -87
  344. package/packages/core/dist/docs/cli/uninstall.md +0 -65
  345. package/packages/core/dist/docs/core/memport.md +0 -246
  346. package/packages/core/dist/docs/core/tools-api.md +0 -131
  347. package/packages/core/dist/docs/faq.md +0 -154
  348. package/packages/core/dist/docs/get-started/configuration-v1.md +0 -888
  349. package/packages/core/dist/docs/get-started/configuration.md +0 -1585
  350. package/packages/core/dist/docs/patches/README.md +0 -74
  351. package/packages/core/dist/docs/patches/mcp-sdk-typings-shim.md +0 -27
  352. package/packages/core/dist/docs/termux-api/COMMANDS.md +0 -592
  353. package/packages/core/dist/docs/termux-api/DISCOVERY_SETUP.md +0 -670
  354. package/packages/core/dist/docs/termux-api/EXECUTION_PLAN.md +0 -532
  355. package/packages/core/dist/docs/termux-api/MERGE_STRATEGY.md +0 -365
  356. package/packages/core/dist/docs/termux-api/PATCHES.md +0 -480
  357. package/packages/core/dist/docs/termux-api/README.md +0 -416
  358. package/packages/core/dist/docs/tos-privacy.md +0 -96
  359. package/packages/core/dist/src/safety/conseca/conseca.d.ts +0 -31
  360. package/packages/core/dist/src/safety/conseca/conseca.js +0 -105
  361. package/packages/core/dist/src/safety/conseca/conseca.js.map +0 -1
  362. package/packages/core/dist/src/safety/conseca/conseca.test.d.ts +0 -6
  363. package/packages/core/dist/src/safety/conseca/conseca.test.js +0 -226
  364. package/packages/core/dist/src/safety/conseca/conseca.test.js.map +0 -1
  365. package/packages/core/dist/src/safety/conseca/integration.test.d.ts +0 -6
  366. package/packages/core/dist/src/safety/conseca/integration.test.js +0 -19
  367. package/packages/core/dist/src/safety/conseca/integration.test.js.map +0 -1
  368. package/packages/core/dist/src/safety/conseca/policy-enforcer.d.ts +0 -13
  369. package/packages/core/dist/src/safety/conseca/policy-enforcer.js +0 -135
  370. package/packages/core/dist/src/safety/conseca/policy-enforcer.js.map +0 -1
  371. package/packages/core/dist/src/safety/conseca/policy-enforcer.test.d.ts +0 -6
  372. package/packages/core/dist/src/safety/conseca/policy-enforcer.test.js +0 -141
  373. package/packages/core/dist/src/safety/conseca/policy-enforcer.test.js.map +0 -1
  374. package/packages/core/dist/src/safety/conseca/policy-generator.d.ts +0 -15
  375. package/packages/core/dist/src/safety/conseca/policy-generator.js +0 -144
  376. package/packages/core/dist/src/safety/conseca/policy-generator.js.map +0 -1
  377. package/packages/core/dist/src/safety/conseca/policy-generator.test.d.ts +0 -6
  378. package/packages/core/dist/src/safety/conseca/policy-generator.test.js +0 -84
  379. package/packages/core/dist/src/safety/conseca/policy-generator.test.js.map +0 -1
  380. package/packages/core/dist/src/safety/conseca/types.d.ts +0 -15
  381. package/packages/core/dist/src/safety/conseca/types.js +0 -7
  382. package/packages/core/dist/src/safety/conseca/types.js.map +0 -1
  383. package/packages/core/dist/src/telemetry/conseca-logger.d.ts +0 -9
  384. package/packages/core/dist/src/telemetry/conseca-logger.js +0 -91
  385. package/packages/core/dist/src/telemetry/conseca-logger.js.map +0 -1
  386. package/packages/core/dist/src/telemetry/conseca-logger.test.d.ts +0 -6
  387. package/packages/core/dist/src/telemetry/conseca-logger.test.js +0 -89
  388. package/packages/core/dist/src/telemetry/conseca-logger.test.js.map +0 -1
  389. package/packages/core/dist/src/tools/mcpImportTool.d.ts +0 -31
  390. package/packages/core/dist/src/tools/mcpImportTool.js +0 -143
  391. package/packages/core/dist/src/tools/mcpImportTool.js.map +0 -1
  392. package/packages/core/dist/src/utils/contextMemory.d.ts +0 -67
  393. package/packages/core/dist/src/utils/contextMemory.js +0 -493
  394. package/packages/core/dist/src/utils/contextMemory.js.map +0 -1
  395. package/packages/core/dist/src/utils/contextMemory.test.d.ts +0 -6
  396. package/packages/core/dist/src/utils/contextMemory.test.js +0 -183
  397. package/packages/core/dist/src/utils/contextMemory.test.js.map +0 -1
  398. /package/{bundle/docs/core → packages/core/dist/docs/reference}/memport.md +0 -0
  399. /package/{bundle/docs/core → packages/core/dist/docs/reference}/tools-api.md +0 -0
  400. /package/{bundle/docs/cli → packages/core/dist/docs/resources}/uninstall.md +0 -0
@@ -4,10 +4,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
4
4
  * Copyright 2025 Google LLC
5
5
  * SPDX-License-Identifier: Apache-2.0
6
6
  */
7
- import { renderWithProviders, createMockSettings, } from '../../test-utils/render.js';
7
+ import { renderWithProviders } from '../../test-utils/render.js';
8
+ import { createMockSettings } from '../../test-utils/settings.js';
8
9
  import { waitFor } from '../../test-utils/async.js';
9
10
  import { act, useState } from 'react';
10
- import { InputPrompt } from './InputPrompt.js';
11
+ import { InputPrompt, tryTogglePasteExpansion } from './InputPrompt.js';
11
12
  import { calculateTransformationsForLine, calculateTransformedLine, } from './shared/text-buffer.js';
12
13
  import { ApprovalMode, debugLogger } from '@google/gemini-cli-core';
13
14
  import * as path from 'node:path';
@@ -27,7 +28,9 @@ import chalk from 'chalk';
27
28
  import { StreamingState } from '../types.js';
28
29
  import { terminalCapabilityManager } from '../utils/terminalCapabilityManager.js';
29
30
  import { isLowColorDepth } from '../utils/terminalUtils.js';
31
+ import { cpLen } from '../utils/textUtils.js';
30
32
  import { keyMatchers, Command } from '../keyMatchers.js';
33
+ import { appEvents, AppEvent, TransientMessageType, } from '../../utils/events.js';
31
34
  vi.mock('../hooks/useShellHistory.js');
32
35
  vi.mock('../hooks/useCommandCompletion.js');
33
36
  vi.mock('../hooks/useInputHistory.js');
@@ -46,6 +49,9 @@ vi.mock('ink', async (importOriginal) => {
46
49
  Text: vi.fn(({ children, ...props }) => (_jsx(actual.Text, { ...props, children: children }))),
47
50
  };
48
51
  });
52
+ afterEach(() => {
53
+ vi.restoreAllMocks();
54
+ });
49
55
  const mockSlashCommands = [
50
56
  {
51
57
  name: 'clear',
@@ -113,8 +119,14 @@ describe('InputPrompt', () => {
113
119
  const mockedUseReverseSearchCompletion = vi.mocked(useReverseSearchCompletion);
114
120
  const mockedUseKittyKeyboardProtocol = vi.mocked(useKittyKeyboardProtocol);
115
121
  const mockSetEmbeddedShellFocused = vi.fn();
122
+ const mockSetCleanUiDetailsVisible = vi.fn();
123
+ const mockToggleCleanUiDetailsVisible = vi.fn();
124
+ const mockRevealCleanUiDetailsTemporarily = vi.fn();
116
125
  const uiActions = {
117
126
  setEmbeddedShellFocused: mockSetEmbeddedShellFocused,
127
+ setCleanUiDetailsVisible: mockSetCleanUiDetailsVisible,
128
+ toggleCleanUiDetailsVisible: mockToggleCleanUiDetailsVisible,
129
+ revealCleanUiDetailsTemporarily: mockRevealCleanUiDetailsTemporarily,
118
130
  };
119
131
  beforeEach(() => {
120
132
  vi.resetAllMocks();
@@ -124,13 +136,24 @@ describe('InputPrompt', () => {
124
136
  text: '',
125
137
  cursor: [0, 0],
126
138
  lines: [''],
127
- setText: vi.fn((newText) => {
139
+ setText: vi.fn((newText, cursorPosition) => {
128
140
  mockBuffer.text = newText;
129
141
  mockBuffer.lines = [newText];
130
- mockBuffer.cursor = [0, newText.length];
142
+ let col = 0;
143
+ if (typeof cursorPosition === 'number') {
144
+ col = cursorPosition;
145
+ }
146
+ else if (cursorPosition === 'start') {
147
+ col = 0;
148
+ }
149
+ else {
150
+ col = newText.length;
151
+ }
152
+ mockBuffer.cursor = [0, col];
131
153
  mockBuffer.viewportVisualLines = [newText];
132
154
  mockBuffer.allVisualLines = [newText];
133
155
  mockBuffer.visualToLogicalMap = [[0, 0]];
156
+ mockBuffer.visualCursor = [0, col];
134
157
  }),
135
158
  replaceRangeByOffset: vi.fn(),
136
159
  viewportVisualLines: [''],
@@ -147,7 +170,15 @@ describe('InputPrompt', () => {
147
170
  }
148
171
  return false;
149
172
  }),
150
- move: vi.fn(),
173
+ move: vi.fn((dir) => {
174
+ if (dir === 'home') {
175
+ mockBuffer.visualCursor = [mockBuffer.visualCursor[0], 0];
176
+ }
177
+ else if (dir === 'end') {
178
+ const line = mockBuffer.allVisualLines[mockBuffer.visualCursor[0]] || '';
179
+ mockBuffer.visualCursor = [mockBuffer.visualCursor[0], cpLen(line)];
180
+ }
181
+ }),
151
182
  moveToOffset: vi.fn((offset) => {
152
183
  mockBuffer.cursor = [0, offset];
153
184
  }),
@@ -191,7 +222,6 @@ describe('InputPrompt', () => {
191
222
  navigateDown: vi.fn(),
192
223
  resetCompletionState: vi.fn(),
193
224
  setActiveSuggestionIndex: vi.fn(),
194
- setShowSuggestions: vi.fn(),
195
225
  handleAutocomplete: vi.fn(),
196
226
  promptCompletion: {
197
227
  text: '',
@@ -218,7 +248,10 @@ describe('InputPrompt', () => {
218
248
  navigateDown: vi.fn(),
219
249
  handleSubmit: vi.fn(),
220
250
  };
221
- mockedUseInputHistory.mockReturnValue(mockInputHistory);
251
+ mockedUseInputHistory.mockImplementation(({ onSubmit }) => {
252
+ mockInputHistory.handleSubmit = vi.fn((val) => onSubmit(val));
253
+ return mockInputHistory;
254
+ });
222
255
  mockReverseSearchCompletion = {
223
256
  suggestions: [],
224
257
  activeSuggestionIndex: -1,
@@ -322,11 +355,11 @@ describe('InputPrompt', () => {
322
355
  uiActions,
323
356
  });
324
357
  await act(async () => {
325
- stdin.write('\u001B[A'); // Up arrow
358
+ stdin.write('\u0010'); // Ctrl+P
326
359
  });
327
360
  await waitFor(() => expect(mockInputHistory.navigateUp).toHaveBeenCalled());
328
361
  await act(async () => {
329
- stdin.write('\u001B[B'); // Down arrow
362
+ stdin.write('\u000E'); // Ctrl+N
330
363
  });
331
364
  await waitFor(() => expect(mockInputHistory.navigateDown).toHaveBeenCalled());
332
365
  await act(async () => {
@@ -338,6 +371,72 @@ describe('InputPrompt', () => {
338
371
  expect(mockShellHistory.addCommandToHistory).not.toHaveBeenCalled();
339
372
  unmount();
340
373
  });
374
+ describe('arrow key navigation', () => {
375
+ it('should move to start of line on Up arrow if on first line but not at start', async () => {
376
+ mockBuffer.allVisualLines = ['line 1', 'line 2'];
377
+ mockBuffer.visualCursor = [0, 5]; // First line, not at start
378
+ mockBuffer.visualScrollRow = 0;
379
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
380
+ uiActions,
381
+ });
382
+ await act(async () => {
383
+ stdin.write('\u001B[A'); // Up arrow
384
+ });
385
+ await waitFor(() => {
386
+ expect(mockBuffer.move).toHaveBeenCalledWith('home');
387
+ expect(mockInputHistory.navigateUp).not.toHaveBeenCalled();
388
+ });
389
+ unmount();
390
+ });
391
+ it('should navigate history on Up arrow if on first line and at start', async () => {
392
+ mockBuffer.allVisualLines = ['line 1', 'line 2'];
393
+ mockBuffer.visualCursor = [0, 0]; // First line, at start
394
+ mockBuffer.visualScrollRow = 0;
395
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
396
+ uiActions,
397
+ });
398
+ await act(async () => {
399
+ stdin.write('\u001B[A'); // Up arrow
400
+ });
401
+ await waitFor(() => {
402
+ expect(mockBuffer.move).not.toHaveBeenCalledWith('home');
403
+ expect(mockInputHistory.navigateUp).toHaveBeenCalled();
404
+ });
405
+ unmount();
406
+ });
407
+ it('should move to end of line on Down arrow if on last line but not at end', async () => {
408
+ mockBuffer.allVisualLines = ['line 1', 'line 2'];
409
+ mockBuffer.visualCursor = [1, 0]; // Last line, not at end
410
+ mockBuffer.visualScrollRow = 0;
411
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
412
+ uiActions,
413
+ });
414
+ await act(async () => {
415
+ stdin.write('\u001B[B'); // Down arrow
416
+ });
417
+ await waitFor(() => {
418
+ expect(mockBuffer.move).toHaveBeenCalledWith('end');
419
+ expect(mockInputHistory.navigateDown).not.toHaveBeenCalled();
420
+ });
421
+ unmount();
422
+ });
423
+ it('should navigate history on Down arrow if on last line and at end', async () => {
424
+ mockBuffer.allVisualLines = ['line 1', 'line 2'];
425
+ mockBuffer.visualCursor = [1, 6]; // Last line, at end ("line 2" is length 6)
426
+ mockBuffer.visualScrollRow = 0;
427
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
428
+ uiActions,
429
+ });
430
+ await act(async () => {
431
+ stdin.write('\u001B[B'); // Down arrow
432
+ });
433
+ await waitFor(() => {
434
+ expect(mockBuffer.move).not.toHaveBeenCalledWith('end');
435
+ expect(mockInputHistory.navigateDown).toHaveBeenCalled();
436
+ });
437
+ unmount();
438
+ });
439
+ });
341
440
  it('should call completion.navigateUp for both up arrow and Ctrl+P when suggestions are showing', async () => {
342
441
  mockedUseCommandCompletion.mockReturnValue({
343
442
  ...mockCommandCompletion,
@@ -398,11 +497,11 @@ describe('InputPrompt', () => {
398
497
  uiActions,
399
498
  });
400
499
  await act(async () => {
401
- stdin.write('\u001B[A'); // Up arrow
500
+ stdin.write('\u0010'); // Ctrl+P
402
501
  });
403
502
  await waitFor(() => expect(mockInputHistory.navigateUp).toHaveBeenCalled());
404
503
  await act(async () => {
405
- stdin.write('\u001B[B'); // Down arrow
504
+ stdin.write('\u000E'); // Ctrl+N
406
505
  });
407
506
  await waitFor(() => expect(mockInputHistory.navigateDown).toHaveBeenCalled());
408
507
  await act(async () => {
@@ -741,6 +840,50 @@ describe('InputPrompt', () => {
741
840
  await waitFor(() => expect(props.onSubmit).toHaveBeenCalledWith('/clear'));
742
841
  unmount();
743
842
  });
843
+ it('should submit on Enter when an @-path is a perfect match', async () => {
844
+ mockedUseCommandCompletion.mockReturnValue({
845
+ ...mockCommandCompletion,
846
+ showSuggestions: true,
847
+ suggestions: [{ label: 'file.txt', value: 'file.txt' }],
848
+ activeSuggestionIndex: 0,
849
+ isPerfectMatch: true,
850
+ completionMode: CompletionMode.AT,
851
+ });
852
+ props.buffer.text = '@file.txt';
853
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
854
+ uiActions,
855
+ });
856
+ await act(async () => {
857
+ stdin.write('\r');
858
+ });
859
+ await waitFor(() => {
860
+ // Should submit directly
861
+ expect(props.onSubmit).toHaveBeenCalledWith('@file.txt');
862
+ });
863
+ unmount();
864
+ });
865
+ it('should NOT submit on Shift+Enter even if an @-path is a perfect match', async () => {
866
+ mockedUseCommandCompletion.mockReturnValue({
867
+ ...mockCommandCompletion,
868
+ showSuggestions: true,
869
+ suggestions: [{ label: 'file.txt', value: 'file.txt' }],
870
+ activeSuggestionIndex: 0,
871
+ isPerfectMatch: true,
872
+ completionMode: CompletionMode.AT,
873
+ });
874
+ props.buffer.text = '@file.txt';
875
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
876
+ uiActions,
877
+ });
878
+ await act(async () => {
879
+ // Simulate Shift+Enter using CSI u sequence
880
+ stdin.write('\x1b[13;2u');
881
+ });
882
+ // Should NOT submit, should call newline instead
883
+ expect(props.onSubmit).not.toHaveBeenCalled();
884
+ expect(props.buffer.newline).toHaveBeenCalled();
885
+ unmount();
886
+ });
744
887
  it('should auto-execute commands with autoExecute: true on Enter', async () => {
745
888
  const aboutCommand = {
746
889
  name: 'about',
@@ -849,6 +992,29 @@ describe('InputPrompt', () => {
849
992
  });
850
993
  unmount();
851
994
  });
995
+ it('should NOT autocomplete on Shift+Tab', async () => {
996
+ const suggestion = { label: 'about', value: 'about' };
997
+ mockedUseCommandCompletion.mockReturnValue({
998
+ ...mockCommandCompletion,
999
+ showSuggestions: true,
1000
+ suggestions: [suggestion],
1001
+ activeSuggestionIndex: 0,
1002
+ getCompletedText: vi.fn().mockReturnValue('/about'),
1003
+ });
1004
+ props.buffer.setText('/ab');
1005
+ props.buffer.lines = ['/ab'];
1006
+ props.buffer.cursor = [0, 3];
1007
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
1008
+ uiActions,
1009
+ });
1010
+ await act(async () => {
1011
+ stdin.write('\x1b[Z'); // Shift+Tab
1012
+ });
1013
+ // We need to wait a bit to ensure handleAutocomplete was NOT called
1014
+ await new Promise((resolve) => setTimeout(resolve, 100));
1015
+ expect(mockCommandCompletion.handleAutocomplete).not.toHaveBeenCalled();
1016
+ unmount();
1017
+ });
852
1018
  it('should autocomplete custom commands from .toml files on Enter', async () => {
853
1019
  const customCommand = {
854
1020
  name: 'find-capital',
@@ -1124,7 +1290,6 @@ describe('InputPrompt', () => {
1124
1290
  { color: 'black', name: 'black' },
1125
1291
  { color: '#000000', name: '#000000' },
1126
1292
  { color: '#000', name: '#000' },
1127
- { color: undefined, name: 'default (black)' },
1128
1293
  { color: 'white', name: 'white' },
1129
1294
  { color: '#ffffff', name: '#ffffff' },
1130
1295
  { color: '#fff', name: '#fff' },
@@ -1172,7 +1337,11 @@ describe('InputPrompt', () => {
1172
1337
  });
1173
1338
  it('should handle 4-bit color mode (16 colors) as low color depth', async () => {
1174
1339
  vi.mocked(isLowColorDepth).mockReturnValue(true);
1175
- const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1340
+ const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
1341
+ uiState: {
1342
+ terminalBackgroundColor: 'black',
1343
+ },
1344
+ });
1176
1345
  await waitFor(() => {
1177
1346
  const frame = stdout.lastFrame();
1178
1347
  expect(frame).toContain('▀');
@@ -1308,7 +1477,16 @@ describe('InputPrompt', () => {
1308
1477
  uiActions,
1309
1478
  });
1310
1479
  await waitFor(() => {
1311
- expect(mockedUseCommandCompletion).toHaveBeenCalledWith(mockBuffer, path.join('test', 'project', 'src'), mockSlashCommands, mockCommandContext, false, false, expect.any(Object));
1480
+ expect(mockedUseCommandCompletion).toHaveBeenCalledWith({
1481
+ buffer: mockBuffer,
1482
+ cwd: path.join('test', 'project', 'src'),
1483
+ slashCommands: mockSlashCommands,
1484
+ commandContext: mockCommandContext,
1485
+ reverseSearchActive: false,
1486
+ shellModeActive: false,
1487
+ config: expect.any(Object),
1488
+ active: expect.anything(),
1489
+ });
1312
1490
  });
1313
1491
  unmount();
1314
1492
  });
@@ -1541,7 +1719,7 @@ describe('InputPrompt', () => {
1541
1719
  // This implicitly tests that the Box wrapper provides height for the empty line.
1542
1720
  expect(frame).toContain('hello');
1543
1721
  expect(frame).toContain(`world${chalk.inverse(' ')}`);
1544
- const outputLines = frame.split('\n');
1722
+ const outputLines = frame.trim().split('\n');
1545
1723
  // The number of lines should be 2 for the border plus 3 for the content.
1546
1724
  expect(outputLines.length).toBe(5);
1547
1725
  });
@@ -1671,6 +1849,29 @@ describe('InputPrompt', () => {
1671
1849
  expect(props.buffer.newline).toHaveBeenCalled();
1672
1850
  unmount();
1673
1851
  });
1852
+ it('should prevent perfect match auto-submission immediately after an unsafe paste', async () => {
1853
+ // isTerminalPasteTrusted will be false due to beforeEach setup.
1854
+ mockedUseCommandCompletion.mockReturnValue({
1855
+ ...mockCommandCompletion,
1856
+ isPerfectMatch: true,
1857
+ completionMode: CompletionMode.AT,
1858
+ });
1859
+ props.buffer.text = '@file.txt';
1860
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
1861
+ // Simulate an unsafe paste of a perfect match
1862
+ await act(async () => {
1863
+ stdin.write(`\x1b[200~@file.txt\x1b[201~`);
1864
+ });
1865
+ // Simulate an Enter key press immediately after paste
1866
+ await act(async () => {
1867
+ stdin.write('\r');
1868
+ });
1869
+ // Verify that onSubmit was NOT called due to recent paste protection
1870
+ expect(props.onSubmit).not.toHaveBeenCalled();
1871
+ // It should call newline() instead
1872
+ expect(props.buffer.newline).toHaveBeenCalled();
1873
+ unmount();
1874
+ });
1674
1875
  it('should allow submission after unsafe paste protection timeout', async () => {
1675
1876
  // isTerminalPasteTrusted will be false due to beforeEach setup.
1676
1877
  props.buffer.text = 'pasted text';
@@ -1967,6 +2168,28 @@ describe('InputPrompt', () => {
1967
2168
  });
1968
2169
  unmount();
1969
2170
  }, 15000);
2171
+ it('should NOT autocomplete on Shift+Tab in reverse search', async () => {
2172
+ const mockHandleAutocomplete = vi.fn();
2173
+ mockedUseReverseSearchCompletion.mockReturnValue({
2174
+ ...mockReverseSearchCompletion,
2175
+ suggestions: [{ label: 'echo hello', value: 'echo hello' }],
2176
+ showSuggestions: true,
2177
+ activeSuggestionIndex: 0,
2178
+ handleAutocomplete: mockHandleAutocomplete,
2179
+ });
2180
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
2181
+ uiActions,
2182
+ });
2183
+ await act(async () => {
2184
+ stdin.write('\x12'); // Ctrl+R
2185
+ });
2186
+ await act(async () => {
2187
+ stdin.write('\x1b[Z'); // Shift+Tab
2188
+ });
2189
+ await new Promise((resolve) => setTimeout(resolve, 100));
2190
+ expect(mockHandleAutocomplete).not.toHaveBeenCalled();
2191
+ unmount();
2192
+ });
1970
2193
  it('submits the highlighted entry on Enter and exits reverse-search', async () => {
1971
2194
  // Mock the reverse search completion to return suggestions
1972
2195
  mockedUseReverseSearchCompletion.mockReturnValue({
@@ -2174,32 +2397,42 @@ describe('InputPrompt', () => {
2174
2397
  });
2175
2398
  unmount();
2176
2399
  });
2400
+ it('ensures Ctrl+R search results are prioritized newest-to-oldest by reversing userMessages', async () => {
2401
+ props.shellModeActive = false;
2402
+ props.userMessages = ['oldest', 'middle', 'newest'];
2403
+ renderWithProviders(_jsx(InputPrompt, { ...props }));
2404
+ const calls = vi.mocked(useReverseSearchCompletion).mock.calls;
2405
+ const commandSearchCall = calls.find((call) => call[1] === props.userMessages ||
2406
+ (Array.isArray(call[1]) && call[1][0] === 'newest'));
2407
+ expect(commandSearchCall).toBeDefined();
2408
+ expect(commandSearchCall[1]).toEqual(['newest', 'middle', 'oldest']);
2409
+ });
2177
2410
  });
2178
- describe('Tab focus toggle', () => {
2411
+ describe('Tab clean UI toggle', () => {
2179
2412
  it.each([
2180
2413
  {
2181
- name: 'should toggle focus in on Tab when no suggestions or ghost text',
2414
+ name: 'should toggle clean UI details on double-Tab when no suggestions or ghost text',
2182
2415
  showSuggestions: false,
2183
2416
  ghostText: '',
2184
2417
  suggestions: [],
2185
- expectedFocusToggle: true,
2418
+ expectedUiToggle: true,
2186
2419
  },
2187
2420
  {
2188
- name: 'should accept ghost text and NOT toggle focus on Tab',
2421
+ name: 'should accept ghost text and NOT toggle clean UI details on Tab',
2189
2422
  showSuggestions: false,
2190
2423
  ghostText: 'ghost text',
2191
2424
  suggestions: [],
2192
- expectedFocusToggle: false,
2425
+ expectedUiToggle: false,
2193
2426
  expectedAcceptCall: true,
2194
2427
  },
2195
2428
  {
2196
- name: 'should NOT toggle focus on Tab when suggestions are present',
2429
+ name: 'should NOT toggle clean UI details on Tab when suggestions are present',
2197
2430
  showSuggestions: true,
2198
2431
  ghostText: '',
2199
2432
  suggestions: [{ label: 'test', value: 'test' }],
2200
- expectedFocusToggle: false,
2433
+ expectedUiToggle: false,
2201
2434
  },
2202
- ])('$name', async ({ showSuggestions, ghostText, suggestions, expectedFocusToggle, expectedAcceptCall, }) => {
2435
+ ])('$name', async ({ showSuggestions, ghostText, suggestions, expectedUiToggle, expectedAcceptCall, }) => {
2203
2436
  const mockAccept = vi.fn();
2204
2437
  mockedUseCommandCompletion.mockReturnValue({
2205
2438
  ...mockCommandCompletion,
@@ -2216,17 +2449,20 @@ describe('InputPrompt', () => {
2216
2449
  });
2217
2450
  const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
2218
2451
  uiActions,
2219
- uiState: { activePtyId: 1 },
2452
+ uiState: {},
2220
2453
  });
2221
2454
  await act(async () => {
2222
2455
  stdin.write('\t');
2456
+ if (expectedUiToggle) {
2457
+ stdin.write('\t');
2458
+ }
2223
2459
  });
2224
2460
  await waitFor(() => {
2225
- if (expectedFocusToggle) {
2226
- expect(uiActions.setEmbeddedShellFocused).toHaveBeenCalledWith(true);
2461
+ if (expectedUiToggle) {
2462
+ expect(uiActions.toggleCleanUiDetailsVisible).toHaveBeenCalled();
2227
2463
  }
2228
2464
  else {
2229
- expect(uiActions.setEmbeddedShellFocused).not.toHaveBeenCalled();
2465
+ expect(uiActions.toggleCleanUiDetailsVisible).not.toHaveBeenCalled();
2230
2466
  }
2231
2467
  if (expectedAcceptCall) {
2232
2468
  expect(mockAccept).toHaveBeenCalled();
@@ -2234,6 +2470,84 @@ describe('InputPrompt', () => {
2234
2470
  });
2235
2471
  unmount();
2236
2472
  });
2473
+ it('should NOT accept ghost text on Shift+Tab', async () => {
2474
+ const mockAccept = vi.fn();
2475
+ mockedUseCommandCompletion.mockReturnValue({
2476
+ ...mockCommandCompletion,
2477
+ showSuggestions: false,
2478
+ suggestions: [],
2479
+ promptCompletion: {
2480
+ text: 'ghost text',
2481
+ accept: mockAccept,
2482
+ clear: vi.fn(),
2483
+ isLoading: false,
2484
+ isActive: true,
2485
+ markSelected: vi.fn(),
2486
+ },
2487
+ });
2488
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
2489
+ uiActions,
2490
+ });
2491
+ await act(async () => {
2492
+ stdin.write('\x1b[Z'); // Shift+Tab
2493
+ });
2494
+ await new Promise((resolve) => setTimeout(resolve, 100));
2495
+ expect(mockAccept).not.toHaveBeenCalled();
2496
+ unmount();
2497
+ });
2498
+ it('should not reveal clean UI details on Shift+Tab when hidden', async () => {
2499
+ mockedUseCommandCompletion.mockReturnValue({
2500
+ ...mockCommandCompletion,
2501
+ showSuggestions: false,
2502
+ suggestions: [],
2503
+ promptCompletion: {
2504
+ text: '',
2505
+ accept: vi.fn(),
2506
+ clear: vi.fn(),
2507
+ isLoading: false,
2508
+ isActive: false,
2509
+ markSelected: vi.fn(),
2510
+ },
2511
+ });
2512
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
2513
+ uiActions,
2514
+ uiState: { activePtyId: 1, cleanUiDetailsVisible: false },
2515
+ });
2516
+ await act(async () => {
2517
+ stdin.write('\x1b[Z');
2518
+ });
2519
+ await waitFor(() => {
2520
+ expect(uiActions.revealCleanUiDetailsTemporarily).not.toHaveBeenCalled();
2521
+ });
2522
+ unmount();
2523
+ });
2524
+ it('should toggle clean UI details on double-Tab by default', async () => {
2525
+ mockedUseCommandCompletion.mockReturnValue({
2526
+ ...mockCommandCompletion,
2527
+ showSuggestions: false,
2528
+ suggestions: [],
2529
+ promptCompletion: {
2530
+ text: '',
2531
+ accept: vi.fn(),
2532
+ clear: vi.fn(),
2533
+ isLoading: false,
2534
+ isActive: false,
2535
+ markSelected: vi.fn(),
2536
+ },
2537
+ });
2538
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
2539
+ uiActions,
2540
+ uiState: {},
2541
+ });
2542
+ await act(async () => {
2543
+ stdin.write('\t');
2544
+ stdin.write('\t');
2545
+ });
2546
+ await waitFor(() => {
2547
+ expect(uiActions.toggleCleanUiDetailsVisible).toHaveBeenCalled();
2548
+ });
2549
+ unmount();
2550
+ });
2237
2551
  });
2238
2552
  describe('mouse interaction', () => {
2239
2553
  it.each([
@@ -2354,7 +2668,7 @@ describe('InputPrompt', () => {
2354
2668
  };
2355
2669
  return _jsx(InputPrompt, { ...baseProps, buffer: buffer });
2356
2670
  };
2357
- const { stdin, stdout, unmount, simulateClick } = renderWithProviders(_jsx(TestWrapper, {}), {
2671
+ const { stdout, unmount, simulateClick } = renderWithProviders(_jsx(TestWrapper, {}), {
2358
2672
  mouseEventsEnabled: true,
2359
2673
  useAlternateBuffer: true,
2360
2674
  uiActions,
@@ -2364,15 +2678,15 @@ describe('InputPrompt', () => {
2364
2678
  expect(stdout.lastFrame()).toMatchSnapshot();
2365
2679
  });
2366
2680
  // Simulate double-click to expand
2367
- await simulateClick(stdin, 5, 2);
2368
- await simulateClick(stdin, 5, 2);
2681
+ await simulateClick(5, 2);
2682
+ await simulateClick(5, 2);
2369
2683
  // 2. Verify expanded content is visible
2370
2684
  await waitFor(() => {
2371
2685
  expect(stdout.lastFrame()).toMatchSnapshot();
2372
2686
  });
2373
2687
  // Simulate double-click to collapse
2374
- await simulateClick(stdin, 5, 2);
2375
- await simulateClick(stdin, 5, 2);
2688
+ await simulateClick(5, 2);
2689
+ await simulateClick(5, 2);
2376
2690
  // 3. Verify placeholder is restored
2377
2691
  await waitFor(() => {
2378
2692
  expect(stdout.lastFrame()).toMatchSnapshot();
@@ -2424,7 +2738,7 @@ describe('InputPrompt', () => {
2424
2738
  };
2425
2739
  return _jsx(InputPrompt, { ...baseProps, buffer: buffer });
2426
2740
  };
2427
- const { stdin, stdout, unmount, simulateClick } = renderWithProviders(_jsx(TestWrapper, {}), {
2741
+ const { stdout, unmount, simulateClick } = renderWithProviders(_jsx(TestWrapper, {}), {
2428
2742
  mouseEventsEnabled: true,
2429
2743
  useAlternateBuffer: true,
2430
2744
  uiActions,
@@ -2434,8 +2748,8 @@ describe('InputPrompt', () => {
2434
2748
  expect(stdout.lastFrame()).toContain('line1');
2435
2749
  });
2436
2750
  // Simulate double-click WAY to the right on the first line
2437
- await simulateClick(stdin, 100, 2);
2438
- await simulateClick(stdin, 100, 2);
2751
+ await simulateClick(90, 2);
2752
+ await simulateClick(90, 2);
2439
2753
  // Verify it is NOW collapsed
2440
2754
  await waitFor(() => {
2441
2755
  expect(stdout.lastFrame()).toContain(id);
@@ -2833,6 +3147,445 @@ describe('InputPrompt', () => {
2833
3147
  unmount();
2834
3148
  });
2835
3149
  });
3150
+ describe('Ctrl+O paste expansion', () => {
3151
+ const CTRL_O = '\x0f'; // Ctrl+O key sequence
3152
+ it('Ctrl+O triggers paste expansion via keybinding', async () => {
3153
+ const id = '[Pasted Text: 10 lines]';
3154
+ const toggleFn = vi.fn();
3155
+ const buffer = {
3156
+ ...props.buffer,
3157
+ text: id,
3158
+ cursor: [0, 0],
3159
+ pastedContent: {
3160
+ [id]: 'line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline10',
3161
+ },
3162
+ transformationsByLine: [
3163
+ [
3164
+ {
3165
+ logStart: 0,
3166
+ logEnd: id.length,
3167
+ logicalText: id,
3168
+ collapsedText: id,
3169
+ type: 'paste',
3170
+ id,
3171
+ },
3172
+ ],
3173
+ ],
3174
+ expandedPaste: null,
3175
+ getExpandedPasteAtLine: vi.fn().mockReturnValue(null),
3176
+ togglePasteExpansion: toggleFn,
3177
+ };
3178
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props, buffer: buffer }), { uiActions });
3179
+ await act(async () => {
3180
+ stdin.write(CTRL_O);
3181
+ });
3182
+ await waitFor(() => {
3183
+ expect(toggleFn).toHaveBeenCalledWith(id, 0, 0);
3184
+ });
3185
+ unmount();
3186
+ });
3187
+ it.each([
3188
+ {
3189
+ name: 'hint appears on large paste via Ctrl+V',
3190
+ text: 'line1\nline2\nline3\nline4\nline5\nline6',
3191
+ method: 'ctrl-v',
3192
+ expectHint: true,
3193
+ },
3194
+ {
3195
+ name: 'hint does not appear for small pastes via Ctrl+V',
3196
+ text: 'hello',
3197
+ method: 'ctrl-v',
3198
+ expectHint: false,
3199
+ },
3200
+ {
3201
+ name: 'hint appears on large terminal paste event',
3202
+ text: 'line1\nline2\nline3\nline4\nline5\nline6',
3203
+ method: 'terminal-paste',
3204
+ expectHint: true,
3205
+ },
3206
+ ])('$name', async ({ text, method, expectHint }) => {
3207
+ vi.mocked(clipboardy.read).mockResolvedValue(text);
3208
+ vi.mocked(clipboardUtils.clipboardHasImage).mockResolvedValue(false);
3209
+ const emitSpy = vi.spyOn(appEvents, 'emit');
3210
+ const buffer = {
3211
+ ...props.buffer,
3212
+ handleInput: vi.fn().mockReturnValue(true),
3213
+ };
3214
+ // Need kitty protocol enabled for terminal paste events
3215
+ if (method === 'terminal-paste') {
3216
+ mockedUseKittyKeyboardProtocol.mockReturnValue({
3217
+ enabled: true,
3218
+ checking: false,
3219
+ });
3220
+ }
3221
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props, buffer: method === 'terminal-paste' ? buffer : props.buffer }));
3222
+ await act(async () => {
3223
+ if (method === 'ctrl-v') {
3224
+ stdin.write('\x16'); // Ctrl+V
3225
+ }
3226
+ else {
3227
+ stdin.write(`\x1b[200~${text}\x1b[201~`);
3228
+ }
3229
+ });
3230
+ await waitFor(() => {
3231
+ if (expectHint) {
3232
+ expect(emitSpy).toHaveBeenCalledWith(AppEvent.TransientMessage, {
3233
+ message: 'Press Ctrl+O to expand pasted text',
3234
+ type: TransientMessageType.Hint,
3235
+ });
3236
+ }
3237
+ else {
3238
+ // If no hint expected, verify buffer was still updated
3239
+ if (method === 'ctrl-v') {
3240
+ expect(mockBuffer.insert).toHaveBeenCalledWith(text, {
3241
+ paste: true,
3242
+ });
3243
+ }
3244
+ else {
3245
+ expect(buffer.handleInput).toHaveBeenCalled();
3246
+ }
3247
+ }
3248
+ });
3249
+ if (!expectHint) {
3250
+ expect(emitSpy).not.toHaveBeenCalledWith(AppEvent.TransientMessage, expect.any(Object));
3251
+ }
3252
+ emitSpy.mockRestore();
3253
+ unmount();
3254
+ });
3255
+ });
3256
+ describe('tryTogglePasteExpansion', () => {
3257
+ it.each([
3258
+ {
3259
+ name: 'returns false when no pasted content exists',
3260
+ cursor: [0, 0],
3261
+ pastedContent: {},
3262
+ getExpandedPasteAtLine: null,
3263
+ expected: false,
3264
+ },
3265
+ {
3266
+ name: 'expands placeholder under cursor',
3267
+ cursor: [0, 2],
3268
+ pastedContent: { '[Pasted Text: 6 lines]': 'content' },
3269
+ transformations: [
3270
+ {
3271
+ logStart: 0,
3272
+ logEnd: '[Pasted Text: 6 lines]'.length,
3273
+ id: '[Pasted Text: 6 lines]',
3274
+ },
3275
+ ],
3276
+ expected: true,
3277
+ expectedToggle: ['[Pasted Text: 6 lines]', 0, 2],
3278
+ },
3279
+ {
3280
+ name: 'collapses expanded paste when cursor is inside',
3281
+ cursor: [1, 0],
3282
+ pastedContent: { '[Pasted Text: 6 lines]': 'a\nb\nc' },
3283
+ getExpandedPasteAtLine: '[Pasted Text: 6 lines]',
3284
+ expected: true,
3285
+ expectedToggle: ['[Pasted Text: 6 lines]', 1, 0],
3286
+ },
3287
+ {
3288
+ name: 'expands placeholder when cursor is immediately after it',
3289
+ cursor: [0, '[Pasted Text: 6 lines]'.length],
3290
+ pastedContent: { '[Pasted Text: 6 lines]': 'content' },
3291
+ transformations: [
3292
+ {
3293
+ logStart: 0,
3294
+ logEnd: '[Pasted Text: 6 lines]'.length,
3295
+ id: '[Pasted Text: 6 lines]',
3296
+ },
3297
+ ],
3298
+ expected: true,
3299
+ expectedToggle: [
3300
+ '[Pasted Text: 6 lines]',
3301
+ 0,
3302
+ '[Pasted Text: 6 lines]'.length,
3303
+ ],
3304
+ },
3305
+ {
3306
+ name: 'shows hint when cursor is not on placeholder but placeholders exist',
3307
+ cursor: [0, 0],
3308
+ pastedContent: { '[Pasted Text: 6 lines]': 'content' },
3309
+ transformationsByLine: [
3310
+ [],
3311
+ [
3312
+ {
3313
+ logStart: 0,
3314
+ logEnd: '[Pasted Text: 6 lines]'.length,
3315
+ type: 'paste',
3316
+ id: '[Pasted Text: 6 lines]',
3317
+ },
3318
+ ],
3319
+ ],
3320
+ expected: true,
3321
+ expectedHint: 'Move cursor within placeholder to expand',
3322
+ },
3323
+ ])('$name', ({ cursor, pastedContent, transformations, transformationsByLine, getExpandedPasteAtLine, expected, expectedToggle, expectedHint, }) => {
3324
+ const id = '[Pasted Text: 6 lines]';
3325
+ const buffer = {
3326
+ cursor,
3327
+ pastedContent,
3328
+ transformationsByLine: transformationsByLine || [
3329
+ transformations
3330
+ ? transformations.map((t) => ({
3331
+ ...t,
3332
+ logicalText: id,
3333
+ collapsedText: id,
3334
+ type: 'paste',
3335
+ }))
3336
+ : [],
3337
+ ],
3338
+ getExpandedPasteAtLine: vi
3339
+ .fn()
3340
+ .mockReturnValue(getExpandedPasteAtLine),
3341
+ togglePasteExpansion: vi.fn(),
3342
+ };
3343
+ const emitSpy = vi.spyOn(appEvents, 'emit');
3344
+ expect(tryTogglePasteExpansion(buffer)).toBe(expected);
3345
+ if (expectedToggle) {
3346
+ expect(buffer.togglePasteExpansion).toHaveBeenCalledWith(...expectedToggle);
3347
+ }
3348
+ else {
3349
+ expect(buffer.togglePasteExpansion).not.toHaveBeenCalled();
3350
+ }
3351
+ if (expectedHint) {
3352
+ expect(emitSpy).toHaveBeenCalledWith(AppEvent.TransientMessage, {
3353
+ message: expectedHint,
3354
+ type: TransientMessageType.Hint,
3355
+ });
3356
+ }
3357
+ else {
3358
+ expect(emitSpy).not.toHaveBeenCalledWith(AppEvent.TransientMessage, expect.any(Object));
3359
+ }
3360
+ emitSpy.mockRestore();
3361
+ });
3362
+ });
3363
+ describe('History Navigation and Completion Suppression', () => {
3364
+ beforeEach(() => {
3365
+ props.userMessages = ['first message', 'second message'];
3366
+ // Mock useInputHistory to actually call onChange
3367
+ mockedUseInputHistory.mockImplementation(({ onChange, onSubmit }) => ({
3368
+ navigateUp: () => {
3369
+ onChange('second message', 'start');
3370
+ return true;
3371
+ },
3372
+ navigateDown: () => {
3373
+ onChange('first message', 'end');
3374
+ return true;
3375
+ },
3376
+ handleSubmit: vi.fn((val) => onSubmit(val)),
3377
+ }));
3378
+ });
3379
+ it.each([
3380
+ { name: 'Up arrow', key: '\u001B[A', position: 'start' },
3381
+ { name: 'Ctrl+P', key: '\u0010', position: 'start' },
3382
+ ])('should move cursor to $position on $name (older history)', async ({ key, position }) => {
3383
+ const { stdin } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
3384
+ uiActions,
3385
+ });
3386
+ await act(async () => {
3387
+ stdin.write(key);
3388
+ });
3389
+ await waitFor(() => {
3390
+ expect(mockBuffer.setText).toHaveBeenCalledWith('second message', position);
3391
+ });
3392
+ });
3393
+ it.each([
3394
+ { name: 'Down arrow', key: '\u001B[B', position: 'end' },
3395
+ { name: 'Ctrl+N', key: '\u000E', position: 'end' },
3396
+ ])('should move cursor to $position on $name (newer history)', async ({ key, position }) => {
3397
+ const { stdin } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
3398
+ uiActions,
3399
+ });
3400
+ // First go up
3401
+ await act(async () => {
3402
+ stdin.write('\u001B[A');
3403
+ });
3404
+ // Then go down
3405
+ await act(async () => {
3406
+ stdin.write(key);
3407
+ if (key === '\u001B[B') {
3408
+ // Second press to actually navigate history
3409
+ stdin.write(key);
3410
+ }
3411
+ });
3412
+ await waitFor(() => {
3413
+ expect(mockBuffer.setText).toHaveBeenCalledWith('first message', position);
3414
+ });
3415
+ });
3416
+ it('should suppress completion after history navigation', async () => {
3417
+ const { stdin } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
3418
+ uiActions,
3419
+ });
3420
+ await act(async () => {
3421
+ stdin.write('\u001B[A'); // Up arrow
3422
+ });
3423
+ await waitFor(() => {
3424
+ expect(mockedUseCommandCompletion).toHaveBeenLastCalledWith({
3425
+ buffer: mockBuffer,
3426
+ cwd: expect.anything(),
3427
+ slashCommands: expect.anything(),
3428
+ commandContext: expect.anything(),
3429
+ reverseSearchActive: expect.anything(),
3430
+ shellModeActive: expect.anything(),
3431
+ config: expect.anything(),
3432
+ active: false,
3433
+ });
3434
+ });
3435
+ });
3436
+ it('should not render suggestions during history navigation', async () => {
3437
+ // 1. Set up a dynamic mock implementation BEFORE rendering
3438
+ mockedUseCommandCompletion.mockImplementation(({ active }) => ({
3439
+ ...mockCommandCompletion,
3440
+ showSuggestions: active,
3441
+ suggestions: active
3442
+ ? [{ value: 'suggestion', label: 'suggestion' }]
3443
+ : [],
3444
+ }));
3445
+ const { stdout, stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), { uiActions });
3446
+ // 2. Verify suggestions ARE showing initially because active is true by default
3447
+ await waitFor(() => {
3448
+ expect(stdout.lastFrame()).toContain('suggestion');
3449
+ });
3450
+ // 3. Trigger history navigation which should set suppressCompletion to true
3451
+ await act(async () => {
3452
+ stdin.write('\u001B[A');
3453
+ });
3454
+ // 4. Verify that suggestions are NOT in the output frame after navigation
3455
+ await waitFor(() => {
3456
+ expect(stdout.lastFrame()).not.toContain('suggestion');
3457
+ });
3458
+ expect(stdout.lastFrame()).toMatchSnapshot();
3459
+ unmount();
3460
+ });
3461
+ it('should continue to suppress completion after manual cursor movement', async () => {
3462
+ const { stdin } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
3463
+ uiActions,
3464
+ });
3465
+ // Navigate history (suppresses)
3466
+ await act(async () => {
3467
+ stdin.write('\u001B[A');
3468
+ });
3469
+ // Wait for it to be suppressed
3470
+ await waitFor(() => {
3471
+ expect(mockedUseCommandCompletion).toHaveBeenLastCalledWith({
3472
+ buffer: mockBuffer,
3473
+ cwd: expect.anything(),
3474
+ slashCommands: expect.anything(),
3475
+ commandContext: expect.anything(),
3476
+ reverseSearchActive: expect.anything(),
3477
+ shellModeActive: expect.anything(),
3478
+ config: expect.anything(),
3479
+ active: false,
3480
+ });
3481
+ });
3482
+ // Move cursor manually
3483
+ await act(async () => {
3484
+ stdin.write('\u001B[D'); // Left arrow
3485
+ });
3486
+ await waitFor(() => {
3487
+ expect(mockedUseCommandCompletion).toHaveBeenLastCalledWith({
3488
+ buffer: mockBuffer,
3489
+ cwd: expect.anything(),
3490
+ slashCommands: expect.anything(),
3491
+ commandContext: expect.anything(),
3492
+ reverseSearchActive: expect.anything(),
3493
+ shellModeActive: expect.anything(),
3494
+ config: expect.anything(),
3495
+ active: false,
3496
+ });
3497
+ });
3498
+ });
3499
+ it('should re-enable completion after typing', async () => {
3500
+ const { stdin } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
3501
+ uiActions,
3502
+ });
3503
+ // Navigate history (suppresses)
3504
+ await act(async () => {
3505
+ stdin.write('\u001B[A');
3506
+ });
3507
+ // Wait for it to be suppressed
3508
+ await waitFor(() => {
3509
+ expect(mockedUseCommandCompletion).toHaveBeenLastCalledWith(expect.objectContaining({ active: false }));
3510
+ });
3511
+ // Type a character
3512
+ await act(async () => {
3513
+ stdin.write('a');
3514
+ });
3515
+ await waitFor(() => {
3516
+ expect(mockedUseCommandCompletion).toHaveBeenLastCalledWith(expect.objectContaining({ active: true }));
3517
+ });
3518
+ });
3519
+ });
3520
+ describe('shortcuts help visibility', () => {
3521
+ it('opens shortcuts help with ? on empty prompt even when showShortcutsHint is false', async () => {
3522
+ const setShortcutsHelpVisible = vi.fn();
3523
+ const settings = createMockSettings({
3524
+ ui: { showShortcutsHint: false },
3525
+ });
3526
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
3527
+ settings,
3528
+ uiActions: { setShortcutsHelpVisible },
3529
+ });
3530
+ await act(async () => {
3531
+ stdin.write('?');
3532
+ });
3533
+ await waitFor(() => {
3534
+ expect(setShortcutsHelpVisible).toHaveBeenCalledWith(true);
3535
+ });
3536
+ unmount();
3537
+ });
3538
+ it.each([
3539
+ {
3540
+ name: 'terminal paste event occurs',
3541
+ input: '\x1b[200~pasted text\x1b[201~',
3542
+ },
3543
+ {
3544
+ name: 'Ctrl+V (PASTE_CLIPBOARD) is pressed',
3545
+ input: '\x16',
3546
+ setupMocks: () => {
3547
+ vi.mocked(clipboardUtils.clipboardHasImage).mockResolvedValue(false);
3548
+ vi.mocked(clipboardy.read).mockResolvedValue('clipboard text');
3549
+ },
3550
+ },
3551
+ {
3552
+ name: 'mouse right-click paste occurs',
3553
+ input: '\x1b[<2;1;1m',
3554
+ mouseEventsEnabled: true,
3555
+ setupMocks: () => {
3556
+ vi.mocked(clipboardUtils.clipboardHasImage).mockResolvedValue(false);
3557
+ vi.mocked(clipboardy.read).mockResolvedValue('clipboard text');
3558
+ },
3559
+ },
3560
+ {
3561
+ name: 'Ctrl+R hotkey is pressed',
3562
+ input: '\x12',
3563
+ },
3564
+ {
3565
+ name: 'Ctrl+X hotkey is pressed',
3566
+ input: '\x18',
3567
+ },
3568
+ {
3569
+ name: 'F12 hotkey is pressed',
3570
+ input: '\x1b[24~',
3571
+ },
3572
+ ])('should close shortcuts help when a $name', async ({ input, setupMocks, mouseEventsEnabled }) => {
3573
+ setupMocks?.();
3574
+ const setShortcutsHelpVisible = vi.fn();
3575
+ const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
3576
+ uiState: { shortcutsHelpVisible: true },
3577
+ uiActions: { setShortcutsHelpVisible },
3578
+ mouseEventsEnabled,
3579
+ });
3580
+ await act(async () => {
3581
+ stdin.write(input);
3582
+ });
3583
+ await waitFor(() => {
3584
+ expect(setShortcutsHelpVisible).toHaveBeenCalledWith(false);
3585
+ });
3586
+ unmount();
3587
+ });
3588
+ });
2836
3589
  });
2837
3590
  function clean(str) {
2838
3591
  if (!str)