@google/gemini-cli 0.10.0-preview.2 → 0.11.0-nightly.20251020.a96f0659

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 (417) hide show
  1. package/README.md +9 -0
  2. package/dist/google-gemini-cli-0.11.0-nightly.20251015.203bad7c.tgz +0 -0
  3. package/dist/package.json +2 -2
  4. package/dist/src/commands/extensions/examples/mcp-server/example.d.ts +6 -0
  5. package/dist/src/commands/extensions/examples/mcp-server/example.js +46 -0
  6. package/dist/src/commands/extensions/examples/mcp-server/example.js.map +1 -0
  7. package/dist/src/commands/extensions/install.d.ts +1 -0
  8. package/dist/src/commands/extensions/install.js +15 -2
  9. package/dist/src/commands/extensions/install.js.map +1 -1
  10. package/dist/src/commands/extensions/list.js +3 -2
  11. package/dist/src/commands/extensions/list.js.map +1 -1
  12. package/dist/src/commands/extensions/update.js +2 -2
  13. package/dist/src/commands/extensions/update.js.map +1 -1
  14. package/dist/src/commands/mcp/add.test.d.ts +6 -0
  15. package/dist/src/commands/mcp/add.test.js +234 -0
  16. package/dist/src/commands/mcp/add.test.js.map +1 -0
  17. package/dist/src/commands/mcp/list.js +5 -3
  18. package/dist/src/commands/mcp/list.js.map +1 -1
  19. package/dist/src/commands/mcp/list.test.d.ts +6 -0
  20. package/dist/src/commands/mcp/list.test.js +117 -0
  21. package/dist/src/commands/mcp/list.test.js.map +1 -0
  22. package/dist/src/commands/mcp/remove.test.d.ts +6 -0
  23. package/dist/src/commands/mcp/remove.test.js +175 -0
  24. package/dist/src/commands/mcp/remove.test.js.map +1 -0
  25. package/dist/src/commands/mcp.test.d.ts +6 -0
  26. package/dist/src/commands/mcp.test.js +62 -0
  27. package/dist/src/commands/mcp.test.js.map +1 -0
  28. package/dist/src/config/auth.js +3 -1
  29. package/dist/src/config/auth.js.map +1 -1
  30. package/dist/src/config/auth.test.js +3 -1
  31. package/dist/src/config/auth.test.js.map +1 -1
  32. package/dist/src/config/config.d.ts +0 -11
  33. package/dist/src/config/config.integration.test.d.ts +6 -0
  34. package/dist/src/config/config.integration.test.js +351 -0
  35. package/dist/src/config/config.integration.test.js.map +1 -0
  36. package/dist/src/config/config.js +17 -74
  37. package/dist/src/config/config.js.map +1 -1
  38. package/dist/src/config/config.test.d.ts +6 -0
  39. package/dist/src/config/config.test.js +2001 -0
  40. package/dist/src/config/config.test.js.map +1 -0
  41. package/dist/src/config/extension.d.ts +1 -4
  42. package/dist/src/config/extension.js +64 -69
  43. package/dist/src/config/extension.js.map +1 -1
  44. package/dist/src/config/extension.test.d.ts +6 -0
  45. package/dist/src/config/extension.test.js +1176 -0
  46. package/dist/src/config/extension.test.js.map +1 -0
  47. package/dist/src/config/extensions/extensionEnablement.d.ts +1 -1
  48. package/dist/src/config/extensions/extensionEnablement.js +4 -3
  49. package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
  50. package/dist/src/config/extensions/extensionEnablement.test.js +21 -18
  51. package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
  52. package/dist/src/config/extensions/github.d.ts +15 -7
  53. package/dist/src/config/extensions/github.js +95 -21
  54. package/dist/src/config/extensions/github.js.map +1 -1
  55. package/dist/src/config/extensions/github.test.js +14 -12
  56. package/dist/src/config/extensions/github.test.js.map +1 -1
  57. package/dist/src/config/extensions/update.test.js +9 -9
  58. package/dist/src/config/extensions/update.test.js.map +1 -1
  59. package/dist/src/config/keyBindings.d.ts +2 -1
  60. package/dist/src/config/keyBindings.js +4 -2
  61. package/dist/src/config/keyBindings.js.map +1 -1
  62. package/dist/src/config/policy.js +13 -14
  63. package/dist/src/config/policy.js.map +1 -1
  64. package/dist/src/config/policy.test.js +3 -3
  65. package/dist/src/config/policy.test.js.map +1 -1
  66. package/dist/src/config/sandboxConfig.d.ts +0 -1
  67. package/dist/src/config/sandboxConfig.js +1 -3
  68. package/dist/src/config/sandboxConfig.js.map +1 -1
  69. package/dist/src/config/settings.test.d.ts +6 -0
  70. package/dist/src/config/settings.test.js +1937 -0
  71. package/dist/src/config/settings.test.js.map +1 -0
  72. package/dist/src/gemini.js +12 -10
  73. package/dist/src/gemini.js.map +1 -1
  74. package/dist/src/gemini.test.js +34 -11
  75. package/dist/src/gemini.test.js.map +1 -1
  76. package/dist/src/generated/git-commit.d.ts +2 -2
  77. package/dist/src/generated/git-commit.js +2 -2
  78. package/dist/src/generated/git-commit.js.map +1 -1
  79. package/dist/src/nonInteractiveCli.js +91 -3
  80. package/dist/src/nonInteractiveCli.js.map +1 -1
  81. package/dist/src/nonInteractiveCli.test.d.ts +6 -0
  82. package/dist/src/nonInteractiveCli.test.js +711 -0
  83. package/dist/src/nonInteractiveCli.test.js.map +1 -0
  84. package/dist/src/services/FileCommandLoader.test.d.ts +6 -0
  85. package/dist/src/services/FileCommandLoader.test.js +971 -0
  86. package/dist/src/services/FileCommandLoader.test.js.map +1 -0
  87. package/dist/src/services/prompt-processors/argumentProcessor.test.d.ts +6 -0
  88. package/dist/src/services/prompt-processors/argumentProcessor.test.js +40 -0
  89. package/dist/src/services/prompt-processors/argumentProcessor.test.js.map +1 -0
  90. package/dist/src/services/prompt-processors/shellProcessor.test.d.ts +6 -0
  91. package/dist/src/services/prompt-processors/shellProcessor.test.js +482 -0
  92. package/dist/src/services/prompt-processors/shellProcessor.test.js.map +1 -0
  93. package/dist/src/test-utils/render.d.ts +2 -1
  94. package/dist/src/test-utils/render.js +5 -2
  95. package/dist/src/test-utils/render.js.map +1 -1
  96. package/dist/src/ui/App.test.d.ts +6 -0
  97. package/dist/src/ui/App.test.js +110 -0
  98. package/dist/src/ui/App.test.js.map +1 -0
  99. package/dist/src/ui/AppContainer.js +39 -26
  100. package/dist/src/ui/AppContainer.js.map +1 -1
  101. package/dist/src/ui/AppContainer.test.js +35 -9
  102. package/dist/src/ui/AppContainer.test.js.map +1 -1
  103. package/dist/src/ui/auth/AuthDialog.d.ts +1 -1
  104. package/dist/src/ui/auth/AuthDialog.js +3 -1
  105. package/dist/src/ui/auth/AuthDialog.js.map +1 -1
  106. package/dist/src/ui/auth/useAuth.d.ts +1 -1
  107. package/dist/src/ui/auth/useAuth.js +3 -1
  108. package/dist/src/ui/auth/useAuth.js.map +1 -1
  109. package/dist/src/ui/commands/aboutCommand.js +1 -1
  110. package/dist/src/ui/commands/aboutCommand.test.d.ts +6 -0
  111. package/dist/src/ui/commands/aboutCommand.test.js +130 -0
  112. package/dist/src/ui/commands/aboutCommand.test.js.map +1 -0
  113. package/dist/src/ui/commands/authCommand.js +1 -1
  114. package/dist/src/ui/commands/authCommand.test.d.ts +6 -0
  115. package/dist/src/ui/commands/authCommand.test.js +30 -0
  116. package/dist/src/ui/commands/authCommand.test.js.map +1 -0
  117. package/dist/src/ui/commands/bugCommand.js +1 -1
  118. package/dist/src/ui/commands/bugCommand.test.d.ts +6 -0
  119. package/dist/src/ui/commands/bugCommand.test.js +105 -0
  120. package/dist/src/ui/commands/bugCommand.test.js.map +1 -0
  121. package/dist/src/ui/commands/chatCommand.js +1 -1
  122. package/dist/src/ui/commands/chatCommand.js.map +1 -1
  123. package/dist/src/ui/commands/chatCommand.test.d.ts +6 -0
  124. package/dist/src/ui/commands/chatCommand.test.js +555 -0
  125. package/dist/src/ui/commands/chatCommand.test.js.map +1 -0
  126. package/dist/src/ui/commands/clearCommand.js +1 -1
  127. package/dist/src/ui/commands/clearCommand.test.d.ts +6 -0
  128. package/dist/src/ui/commands/clearCommand.test.js +76 -0
  129. package/dist/src/ui/commands/clearCommand.test.js.map +1 -0
  130. package/dist/src/ui/commands/compressCommand.js +1 -1
  131. package/dist/src/ui/commands/compressCommand.js.map +1 -1
  132. package/dist/src/ui/commands/compressCommand.test.d.ts +6 -0
  133. package/dist/src/ui/commands/compressCommand.test.js +98 -0
  134. package/dist/src/ui/commands/compressCommand.test.js.map +1 -0
  135. package/dist/src/ui/commands/copyCommand.test.d.ts +6 -0
  136. package/dist/src/ui/commands/copyCommand.test.js +242 -0
  137. package/dist/src/ui/commands/copyCommand.test.js.map +1 -0
  138. package/dist/src/ui/commands/corgiCommand.js +1 -1
  139. package/dist/src/ui/commands/corgiCommand.js.map +1 -1
  140. package/dist/src/ui/commands/corgiCommand.test.d.ts +6 -0
  141. package/dist/src/ui/commands/corgiCommand.test.js +28 -0
  142. package/dist/src/ui/commands/corgiCommand.test.js.map +1 -0
  143. package/dist/src/ui/commands/directoryCommand.test.d.ts +6 -0
  144. package/dist/src/ui/commands/directoryCommand.test.js +145 -0
  145. package/dist/src/ui/commands/directoryCommand.test.js.map +1 -0
  146. package/dist/src/ui/commands/docsCommand.js +1 -1
  147. package/dist/src/ui/commands/docsCommand.test.d.ts +6 -0
  148. package/dist/src/ui/commands/docsCommand.test.js +72 -0
  149. package/dist/src/ui/commands/docsCommand.test.js.map +1 -0
  150. package/dist/src/ui/commands/editorCommand.js +1 -1
  151. package/dist/src/ui/commands/editorCommand.test.d.ts +6 -0
  152. package/dist/src/ui/commands/editorCommand.test.js +27 -0
  153. package/dist/src/ui/commands/editorCommand.test.js.map +1 -0
  154. package/dist/src/ui/commands/extensionsCommand.test.d.ts +6 -0
  155. package/dist/src/ui/commands/extensionsCommand.test.js +241 -0
  156. package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -0
  157. package/dist/src/ui/commands/helpCommand.js +1 -1
  158. package/dist/src/ui/commands/helpCommand.test.d.ts +6 -0
  159. package/dist/src/ui/commands/helpCommand.test.js +42 -0
  160. package/dist/src/ui/commands/helpCommand.test.js.map +1 -0
  161. package/dist/src/ui/commands/ideCommand.js +6 -6
  162. package/dist/src/ui/commands/ideCommand.test.d.ts +6 -0
  163. package/dist/src/ui/commands/ideCommand.test.js +203 -0
  164. package/dist/src/ui/commands/ideCommand.test.js.map +1 -0
  165. package/dist/src/ui/commands/initCommand.js +1 -1
  166. package/dist/src/ui/commands/initCommand.js.map +1 -1
  167. package/dist/src/ui/commands/initCommand.test.d.ts +6 -0
  168. package/dist/src/ui/commands/initCommand.test.js +80 -0
  169. package/dist/src/ui/commands/initCommand.test.js.map +1 -0
  170. package/dist/src/ui/commands/mcpCommand.js +98 -88
  171. package/dist/src/ui/commands/mcpCommand.js.map +1 -1
  172. package/dist/src/ui/commands/mcpCommand.test.d.ts +6 -0
  173. package/dist/src/ui/commands/mcpCommand.test.js +148 -0
  174. package/dist/src/ui/commands/mcpCommand.test.js.map +1 -0
  175. package/dist/src/ui/commands/memoryCommand.js +5 -5
  176. package/dist/src/ui/commands/memoryCommand.js.map +1 -1
  177. package/dist/src/ui/commands/memoryCommand.test.d.ts +6 -0
  178. package/dist/src/ui/commands/memoryCommand.test.js +266 -0
  179. package/dist/src/ui/commands/memoryCommand.test.js.map +1 -0
  180. package/dist/src/ui/commands/privacyCommand.js +1 -1
  181. package/dist/src/ui/commands/privacyCommand.test.d.ts +6 -0
  182. package/dist/src/ui/commands/privacyCommand.test.js +32 -0
  183. package/dist/src/ui/commands/privacyCommand.test.js.map +1 -0
  184. package/dist/src/ui/commands/quitCommand.js +1 -1
  185. package/dist/src/ui/commands/quitCommand.test.d.ts +6 -0
  186. package/dist/src/ui/commands/quitCommand.test.js +50 -0
  187. package/dist/src/ui/commands/quitCommand.test.js.map +1 -0
  188. package/dist/src/ui/commands/restoreCommand.test.d.ts +6 -0
  189. package/dist/src/ui/commands/restoreCommand.test.js +190 -0
  190. package/dist/src/ui/commands/restoreCommand.test.js.map +1 -0
  191. package/dist/src/ui/commands/settingsCommand.test.d.ts +6 -0
  192. package/dist/src/ui/commands/settingsCommand.test.js +30 -0
  193. package/dist/src/ui/commands/settingsCommand.test.js.map +1 -0
  194. package/dist/src/ui/commands/setupGithubCommand.test.js +1 -2
  195. package/dist/src/ui/commands/setupGithubCommand.test.js.map +1 -1
  196. package/dist/src/ui/commands/statsCommand.js +3 -3
  197. package/dist/src/ui/commands/statsCommand.js.map +1 -1
  198. package/dist/src/ui/commands/statsCommand.test.d.ts +6 -0
  199. package/dist/src/ui/commands/statsCommand.test.js +53 -0
  200. package/dist/src/ui/commands/statsCommand.test.js.map +1 -0
  201. package/dist/src/ui/commands/terminalSetupCommand.test.d.ts +6 -0
  202. package/dist/src/ui/commands/terminalSetupCommand.test.js +66 -0
  203. package/dist/src/ui/commands/terminalSetupCommand.test.js.map +1 -0
  204. package/dist/src/ui/commands/themeCommand.js +1 -1
  205. package/dist/src/ui/commands/themeCommand.test.d.ts +6 -0
  206. package/dist/src/ui/commands/themeCommand.test.js +32 -0
  207. package/dist/src/ui/commands/themeCommand.test.js.map +1 -0
  208. package/dist/src/ui/commands/toolsCommand.js +1 -1
  209. package/dist/src/ui/commands/toolsCommand.test.d.ts +6 -0
  210. package/dist/src/ui/commands/toolsCommand.test.js +100 -0
  211. package/dist/src/ui/commands/toolsCommand.test.js.map +1 -0
  212. package/dist/src/ui/commands/vimCommand.js +1 -1
  213. package/dist/src/ui/components/Composer.js +5 -3
  214. package/dist/src/ui/components/Composer.js.map +1 -1
  215. package/dist/src/ui/components/Composer.test.js +16 -1
  216. package/dist/src/ui/components/Composer.test.js.map +1 -1
  217. package/dist/src/ui/components/ContextSummaryDisplay.d.ts +0 -1
  218. package/dist/src/ui/components/ContextSummaryDisplay.js +2 -12
  219. package/dist/src/ui/components/ContextSummaryDisplay.js.map +1 -1
  220. package/dist/src/ui/components/ContextSummaryDisplay.test.d.ts +6 -0
  221. package/dist/src/ui/components/ContextSummaryDisplay.test.js +66 -0
  222. package/dist/src/ui/components/ContextSummaryDisplay.test.js.map +1 -0
  223. package/dist/src/ui/components/DialogManager.js +1 -5
  224. package/dist/src/ui/components/DialogManager.js.map +1 -1
  225. package/dist/src/ui/components/EditorSettingsDialog.js +1 -1
  226. package/dist/src/ui/components/EditorSettingsDialog.js.map +1 -1
  227. package/dist/src/ui/components/FolderTrustDialog.test.js +7 -3
  228. package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
  229. package/dist/src/ui/components/Footer.js +1 -1
  230. package/dist/src/ui/components/Footer.js.map +1 -1
  231. package/dist/src/ui/components/Footer.test.d.ts +6 -0
  232. package/dist/src/ui/components/Footer.test.js +231 -0
  233. package/dist/src/ui/components/Footer.test.js.map +1 -0
  234. package/dist/src/ui/components/InputPrompt.d.ts +4 -0
  235. package/dist/src/ui/components/InputPrompt.js +53 -4
  236. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  237. package/dist/src/ui/components/InputPrompt.test.d.ts +6 -0
  238. package/dist/src/ui/components/InputPrompt.test.js +1716 -0
  239. package/dist/src/ui/components/InputPrompt.test.js.map +1 -0
  240. package/dist/src/ui/components/ModelStatsDisplay.test.d.ts +6 -0
  241. package/dist/src/ui/components/ModelStatsDisplay.test.js +285 -0
  242. package/dist/src/ui/components/ModelStatsDisplay.test.js.map +1 -0
  243. package/dist/src/ui/components/PermissionsModifyTrustDialog.js +22 -18
  244. package/dist/src/ui/components/PermissionsModifyTrustDialog.js.map +1 -1
  245. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +10 -2
  246. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
  247. package/dist/src/ui/components/QueuedMessageDisplay.js +3 -3
  248. package/dist/src/ui/components/QueuedMessageDisplay.js.map +1 -1
  249. package/dist/src/ui/components/QueuedMessageDisplay.test.js +4 -0
  250. package/dist/src/ui/components/QueuedMessageDisplay.test.js.map +1 -1
  251. package/dist/src/ui/components/RawMarkdownIndicator.d.ts +7 -0
  252. package/dist/src/ui/components/RawMarkdownIndicator.js +8 -0
  253. package/dist/src/ui/components/RawMarkdownIndicator.js.map +1 -0
  254. package/dist/src/ui/components/SessionSummaryDisplay.test.d.ts +6 -0
  255. package/dist/src/ui/components/SessionSummaryDisplay.test.js +74 -0
  256. package/dist/src/ui/components/SessionSummaryDisplay.test.js.map +1 -0
  257. package/dist/src/ui/components/SettingsDialog.js +8 -8
  258. package/dist/src/ui/components/SettingsDialog.js.map +1 -1
  259. package/dist/src/ui/components/SettingsDialog.test.js +188 -56
  260. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  261. package/dist/src/ui/components/StatsDisplay.test.d.ts +6 -0
  262. package/dist/src/ui/components/StatsDisplay.test.js +351 -0
  263. package/dist/src/ui/components/StatsDisplay.test.js.map +1 -0
  264. package/dist/src/ui/components/ThemeDialog.d.ts +4 -2
  265. package/dist/src/ui/components/ThemeDialog.js +3 -3
  266. package/dist/src/ui/components/ThemeDialog.js.map +1 -1
  267. package/dist/src/ui/components/ThemeDialog.test.js +13 -0
  268. package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
  269. package/dist/src/ui/components/ToolStatsDisplay.test.d.ts +6 -0
  270. package/dist/src/ui/components/ToolStatsDisplay.test.js +227 -0
  271. package/dist/src/ui/components/ToolStatsDisplay.test.js.map +1 -0
  272. package/dist/src/ui/components/messages/GeminiMessage.js +3 -1
  273. package/dist/src/ui/components/messages/GeminiMessage.js.map +1 -1
  274. package/dist/src/ui/components/messages/GeminiMessage.test.d.ts +6 -0
  275. package/dist/src/ui/components/messages/GeminiMessage.test.js +35 -0
  276. package/dist/src/ui/components/messages/GeminiMessage.test.js.map +1 -0
  277. package/dist/src/ui/components/messages/GeminiMessageContent.js +3 -1
  278. package/dist/src/ui/components/messages/GeminiMessageContent.js.map +1 -1
  279. package/dist/src/ui/components/messages/Todo.d.ts +7 -0
  280. package/dist/src/ui/components/messages/Todo.js +59 -0
  281. package/dist/src/ui/components/messages/Todo.js.map +1 -0
  282. package/dist/src/ui/components/messages/Todo.test.d.ts +6 -0
  283. package/dist/src/ui/components/messages/Todo.test.js +113 -0
  284. package/dist/src/ui/components/messages/Todo.test.js.map +1 -0
  285. package/dist/src/ui/components/messages/ToolGroupMessage.js +1 -1
  286. package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
  287. package/dist/src/ui/components/messages/ToolMessage.js +8 -3
  288. package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
  289. package/dist/src/ui/components/messages/ToolMessage.test.js +2 -2
  290. package/dist/src/ui/components/messages/ToolMessage.test.js.map +1 -1
  291. package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.d.ts +6 -0
  292. package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.js +30 -0
  293. package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.js.map +1 -0
  294. package/dist/src/ui/components/messages/UserShellMessage.js +1 -1
  295. package/dist/src/ui/components/messages/UserShellMessage.js.map +1 -1
  296. package/dist/src/ui/components/shared/BaseSelectionList.test.js +1 -1
  297. package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
  298. package/dist/src/ui/components/shared/text-buffer.test.d.ts +6 -0
  299. package/dist/src/ui/components/shared/text-buffer.test.js +1554 -0
  300. package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -0
  301. package/dist/src/ui/components/shared/vim-buffer-actions.test.d.ts +6 -0
  302. package/dist/src/ui/components/shared/vim-buffer-actions.test.js +951 -0
  303. package/dist/src/ui/components/shared/vim-buffer-actions.test.js.map +1 -0
  304. package/dist/src/ui/components/views/McpStatus.d.ts +0 -1
  305. package/dist/src/ui/components/views/McpStatus.js +2 -2
  306. package/dist/src/ui/components/views/McpStatus.js.map +1 -1
  307. package/dist/src/ui/components/views/McpStatus.test.js +0 -5
  308. package/dist/src/ui/components/views/McpStatus.test.js.map +1 -1
  309. package/dist/src/ui/components/views/ToolsList.test.js +4 -4
  310. package/dist/src/ui/components/views/ToolsList.test.js.map +1 -1
  311. package/dist/src/ui/contexts/KeypressContext.d.ts +1 -0
  312. package/dist/src/ui/contexts/KeypressContext.js +176 -50
  313. package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
  314. package/dist/src/ui/contexts/KeypressContext.test.js +413 -14
  315. package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
  316. package/dist/src/ui/contexts/SessionContext.test.d.ts +6 -0
  317. package/dist/src/ui/contexts/SessionContext.test.js +177 -0
  318. package/dist/src/ui/contexts/SessionContext.test.js.map +1 -0
  319. package/dist/src/ui/contexts/UIActionsContext.d.ts +5 -4
  320. package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
  321. package/dist/src/ui/contexts/UIStateContext.d.ts +3 -3
  322. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  323. package/dist/src/ui/hooks/slashCommandProcessor.test.d.ts +6 -0
  324. package/dist/src/ui/hooks/slashCommandProcessor.test.js +779 -0
  325. package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -0
  326. package/dist/src/ui/hooks/useAtCompletion.js +2 -2
  327. package/dist/src/ui/hooks/useAtCompletion.js.map +1 -1
  328. package/dist/src/ui/hooks/useAtCompletion.test.d.ts +6 -0
  329. package/dist/src/ui/hooks/useAtCompletion.test.js +385 -0
  330. package/dist/src/ui/hooks/useAtCompletion.test.js.map +1 -0
  331. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +0 -1
  332. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
  333. package/dist/src/ui/hooks/useCommandCompletion.d.ts +1 -1
  334. package/dist/src/ui/hooks/useCommandCompletion.js +5 -3
  335. package/dist/src/ui/hooks/useCommandCompletion.js.map +1 -1
  336. package/dist/src/ui/hooks/useCommandCompletion.test.d.ts +6 -0
  337. package/dist/src/ui/hooks/useCommandCompletion.test.js +375 -0
  338. package/dist/src/ui/hooks/useCommandCompletion.test.js.map +1 -0
  339. package/dist/src/ui/hooks/useConsoleMessages.test.d.ts +6 -0
  340. package/dist/src/ui/hooks/useConsoleMessages.test.js +110 -0
  341. package/dist/src/ui/hooks/useConsoleMessages.test.js.map +1 -0
  342. package/dist/src/ui/hooks/useExtensionUpdates.test.js +3 -3
  343. package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
  344. package/dist/src/ui/hooks/useFocus.test.d.ts +6 -0
  345. package/dist/src/ui/hooks/useFocus.test.js +115 -0
  346. package/dist/src/ui/hooks/useFocus.test.js.map +1 -0
  347. package/dist/src/ui/hooks/useFolderTrust.test.d.ts +6 -0
  348. package/dist/src/ui/hooks/useFolderTrust.test.js +164 -0
  349. package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -0
  350. package/dist/src/ui/hooks/useGeminiStream.js +33 -31
  351. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  352. package/dist/src/ui/hooks/useGeminiStream.test.d.ts +6 -0
  353. package/dist/src/ui/hooks/useGeminiStream.test.js +1936 -0
  354. package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -0
  355. package/dist/src/ui/hooks/useKeypress.test.d.ts +6 -0
  356. package/dist/src/ui/hooks/useKeypress.test.js +234 -0
  357. package/dist/src/ui/hooks/useKeypress.test.js.map +1 -0
  358. package/dist/src/ui/hooks/useLoadingIndicator.test.js +5 -0
  359. package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
  360. package/dist/src/ui/hooks/useMessageQueue.d.ts +1 -0
  361. package/dist/src/ui/hooks/useMessageQueue.js +14 -0
  362. package/dist/src/ui/hooks/useMessageQueue.js.map +1 -1
  363. package/dist/src/ui/hooks/useMessageQueue.test.js +121 -0
  364. package/dist/src/ui/hooks/useMessageQueue.test.js.map +1 -1
  365. package/dist/src/ui/hooks/usePhraseCycler.d.ts +1 -0
  366. package/dist/src/ui/hooks/usePhraseCycler.js +156 -5
  367. package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
  368. package/dist/src/ui/hooks/usePhraseCycler.test.d.ts +6 -0
  369. package/dist/src/ui/hooks/usePhraseCycler.test.js +155 -0
  370. package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -0
  371. package/dist/src/ui/hooks/useThemeCommand.d.ts +2 -1
  372. package/dist/src/ui/hooks/useThemeCommand.js +6 -0
  373. package/dist/src/ui/hooks/useThemeCommand.js.map +1 -1
  374. package/dist/src/ui/hooks/useToolScheduler.test.js +3 -0
  375. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  376. package/dist/src/ui/hooks/vim.test.d.ts +6 -0
  377. package/dist/src/ui/hooks/vim.test.js +1389 -0
  378. package/dist/src/ui/hooks/vim.test.js.map +1 -0
  379. package/dist/src/ui/keyMatchers.test.js +9 -3
  380. package/dist/src/ui/keyMatchers.test.js.map +1 -1
  381. package/dist/src/ui/themes/theme.test.d.ts +6 -0
  382. package/dist/src/ui/themes/theme.test.js +85 -0
  383. package/dist/src/ui/themes/theme.test.js.map +1 -0
  384. package/dist/src/ui/types.d.ts +0 -1
  385. package/dist/src/ui/types.js.map +1 -1
  386. package/dist/src/ui/utils/CodeColorizer.d.ts +1 -1
  387. package/dist/src/ui/utils/CodeColorizer.js +4 -2
  388. package/dist/src/ui/utils/CodeColorizer.js.map +1 -1
  389. package/dist/src/ui/utils/MarkdownDisplay.d.ts +1 -0
  390. package/dist/src/ui/utils/MarkdownDisplay.js +8 -1
  391. package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
  392. package/dist/src/ui/utils/commandUtils.js +18 -2
  393. package/dist/src/ui/utils/commandUtils.js.map +1 -1
  394. package/dist/src/ui/utils/commandUtils.test.js +61 -6
  395. package/dist/src/ui/utils/commandUtils.test.js.map +1 -1
  396. package/dist/src/ui/utils/updateCheck.d.ts +2 -1
  397. package/dist/src/ui/utils/updateCheck.js +4 -1
  398. package/dist/src/ui/utils/updateCheck.js.map +1 -1
  399. package/dist/src/ui/utils/updateCheck.test.js +25 -10
  400. package/dist/src/ui/utils/updateCheck.test.js.map +1 -1
  401. package/dist/src/utils/errors.d.ts +1 -0
  402. package/dist/src/utils/errors.js +66 -5
  403. package/dist/src/utils/errors.js.map +1 -1
  404. package/dist/src/validateNonInterActiveAuth.test.d.ts +6 -0
  405. package/dist/src/validateNonInterActiveAuth.test.js +336 -0
  406. package/dist/src/validateNonInterActiveAuth.test.js.map +1 -0
  407. package/dist/src/zed-integration/zedIntegration.js +1 -2
  408. package/dist/src/zed-integration/zedIntegration.js.map +1 -1
  409. package/dist/tsconfig.tsbuildinfo +1 -1
  410. package/package.json +3 -3
  411. package/dist/google-gemini-cli-0.10.0-preview.1.tgz +0 -0
  412. package/dist/src/ui/components/WorkspaceMigrationDialog.d.ts +0 -11
  413. package/dist/src/ui/components/WorkspaceMigrationDialog.js +0 -44
  414. package/dist/src/ui/components/WorkspaceMigrationDialog.js.map +0 -1
  415. package/dist/src/ui/hooks/useWorkspaceMigration.d.ts +0 -13
  416. package/dist/src/ui/hooks/useWorkspaceMigration.js +0 -59
  417. package/dist/src/ui/hooks/useWorkspaceMigration.js.map +0 -1
@@ -0,0 +1,711 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { executeToolCall, ToolErrorType, shutdownTelemetry, GeminiEventType, OutputFormat, uiTelemetryService, FatalInputError, } from '@google/gemini-cli-core';
7
+ import { runNonInteractive } from './nonInteractiveCli.js';
8
+ import { vi } from 'vitest';
9
+ // Mock core modules
10
+ vi.mock('./ui/hooks/atCommandProcessor.js');
11
+ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
12
+ const original = await importOriginal();
13
+ class MockChatRecordingService {
14
+ initialize = vi.fn();
15
+ recordMessage = vi.fn();
16
+ recordMessageTokens = vi.fn();
17
+ recordToolCalls = vi.fn();
18
+ }
19
+ return {
20
+ ...original,
21
+ executeToolCall: vi.fn(),
22
+ shutdownTelemetry: vi.fn(),
23
+ isTelemetrySdkInitialized: vi.fn().mockReturnValue(true),
24
+ ChatRecordingService: MockChatRecordingService,
25
+ uiTelemetryService: {
26
+ getMetrics: vi.fn(),
27
+ },
28
+ };
29
+ });
30
+ const mockGetCommands = vi.hoisted(() => vi.fn());
31
+ const mockCommandServiceCreate = vi.hoisted(() => vi.fn());
32
+ vi.mock('./services/CommandService.js', () => ({
33
+ CommandService: {
34
+ create: mockCommandServiceCreate,
35
+ },
36
+ }));
37
+ describe('runNonInteractive', () => {
38
+ let mockConfig;
39
+ let mockSettings;
40
+ let mockToolRegistry;
41
+ let mockCoreExecuteToolCall;
42
+ let mockShutdownTelemetry;
43
+ let consoleErrorSpy;
44
+ let processStdoutSpy;
45
+ let mockGeminiClient;
46
+ beforeEach(async () => {
47
+ mockCoreExecuteToolCall = vi.mocked(executeToolCall);
48
+ mockShutdownTelemetry = vi.mocked(shutdownTelemetry);
49
+ mockCommandServiceCreate.mockResolvedValue({
50
+ getCommands: mockGetCommands,
51
+ });
52
+ consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
53
+ processStdoutSpy = vi
54
+ .spyOn(process.stdout, 'write')
55
+ .mockImplementation(() => true);
56
+ vi.spyOn(process, 'exit').mockImplementation((code) => {
57
+ throw new Error(`process.exit(${code}) called`);
58
+ });
59
+ mockToolRegistry = {
60
+ getTool: vi.fn(),
61
+ getFunctionDeclarations: vi.fn().mockReturnValue([]),
62
+ };
63
+ mockGeminiClient = {
64
+ sendMessageStream: vi.fn(),
65
+ getChatRecordingService: vi.fn(() => ({
66
+ initialize: vi.fn(),
67
+ recordMessage: vi.fn(),
68
+ recordMessageTokens: vi.fn(),
69
+ recordToolCalls: vi.fn(),
70
+ })),
71
+ };
72
+ mockConfig = {
73
+ initialize: vi.fn().mockResolvedValue(undefined),
74
+ getGeminiClient: vi.fn().mockReturnValue(mockGeminiClient),
75
+ getToolRegistry: vi.fn().mockReturnValue(mockToolRegistry),
76
+ getMaxSessionTurns: vi.fn().mockReturnValue(10),
77
+ getSessionId: vi.fn().mockReturnValue('test-session-id'),
78
+ getProjectRoot: vi.fn().mockReturnValue('/test/project'),
79
+ storage: {
80
+ getProjectTempDir: vi.fn().mockReturnValue('/test/project/.gemini/tmp'),
81
+ },
82
+ getIdeMode: vi.fn().mockReturnValue(false),
83
+ getContentGeneratorConfig: vi.fn().mockReturnValue({}),
84
+ getDebugMode: vi.fn().mockReturnValue(false),
85
+ getOutputFormat: vi.fn().mockReturnValue('text'),
86
+ getFolderTrust: vi.fn().mockReturnValue(false),
87
+ isTrustedFolder: vi.fn().mockReturnValue(false),
88
+ };
89
+ mockSettings = {
90
+ system: { path: '', settings: {} },
91
+ systemDefaults: { path: '', settings: {} },
92
+ user: { path: '', settings: {} },
93
+ workspace: { path: '', settings: {} },
94
+ errors: [],
95
+ setValue: vi.fn(),
96
+ merged: {
97
+ security: {
98
+ auth: {
99
+ enforcedType: undefined,
100
+ },
101
+ },
102
+ },
103
+ isTrusted: true,
104
+ migratedInMemorScopes: new Set(),
105
+ forScope: vi.fn(),
106
+ computeMergedSettings: vi.fn(),
107
+ };
108
+ const { handleAtCommand } = await import('./ui/hooks/atCommandProcessor.js');
109
+ vi.mocked(handleAtCommand).mockImplementation(async ({ query }) => ({
110
+ processedQuery: [{ text: query }],
111
+ shouldProceed: true,
112
+ }));
113
+ });
114
+ afterEach(() => {
115
+ vi.restoreAllMocks();
116
+ });
117
+ async function* createStreamFromEvents(events) {
118
+ for (const event of events) {
119
+ yield event;
120
+ }
121
+ }
122
+ it('should process input and write text output', async () => {
123
+ const events = [
124
+ { type: GeminiEventType.Content, value: 'Hello' },
125
+ { type: GeminiEventType.Content, value: ' World' },
126
+ {
127
+ type: GeminiEventType.Finished,
128
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
129
+ },
130
+ ];
131
+ mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
132
+ await runNonInteractive(mockConfig, mockSettings, 'Test input', 'prompt-id-1');
133
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith([{ text: 'Test input' }], expect.any(AbortSignal), 'prompt-id-1');
134
+ expect(processStdoutSpy).toHaveBeenCalledWith('Hello');
135
+ expect(processStdoutSpy).toHaveBeenCalledWith(' World');
136
+ expect(processStdoutSpy).toHaveBeenCalledWith('\n');
137
+ expect(mockShutdownTelemetry).toHaveBeenCalled();
138
+ });
139
+ it('should handle a single tool call and respond', async () => {
140
+ const toolCallEvent = {
141
+ type: GeminiEventType.ToolCallRequest,
142
+ value: {
143
+ callId: 'tool-1',
144
+ name: 'testTool',
145
+ args: { arg1: 'value1' },
146
+ isClientInitiated: false,
147
+ prompt_id: 'prompt-id-2',
148
+ },
149
+ };
150
+ const toolResponse = [{ text: 'Tool response' }];
151
+ mockCoreExecuteToolCall.mockResolvedValue({
152
+ status: 'success',
153
+ request: {
154
+ callId: 'tool-1',
155
+ name: 'testTool',
156
+ args: { arg1: 'value1' },
157
+ isClientInitiated: false,
158
+ prompt_id: 'prompt-id-2',
159
+ },
160
+ tool: {},
161
+ invocation: {},
162
+ response: {
163
+ responseParts: toolResponse,
164
+ callId: 'tool-1',
165
+ error: undefined,
166
+ errorType: undefined,
167
+ contentLength: undefined,
168
+ },
169
+ });
170
+ const firstCallEvents = [toolCallEvent];
171
+ const secondCallEvents = [
172
+ { type: GeminiEventType.Content, value: 'Final answer' },
173
+ {
174
+ type: GeminiEventType.Finished,
175
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
176
+ },
177
+ ];
178
+ mockGeminiClient.sendMessageStream
179
+ .mockReturnValueOnce(createStreamFromEvents(firstCallEvents))
180
+ .mockReturnValueOnce(createStreamFromEvents(secondCallEvents));
181
+ await runNonInteractive(mockConfig, mockSettings, 'Use a tool', 'prompt-id-2');
182
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
183
+ expect(mockCoreExecuteToolCall).toHaveBeenCalledWith(mockConfig, expect.objectContaining({ name: 'testTool' }), expect.any(AbortSignal));
184
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenNthCalledWith(2, [{ text: 'Tool response' }], expect.any(AbortSignal), 'prompt-id-2');
185
+ expect(processStdoutSpy).toHaveBeenCalledWith('Final answer');
186
+ expect(processStdoutSpy).toHaveBeenCalledWith('\n');
187
+ });
188
+ it('should handle error during tool execution and should send error back to the model', async () => {
189
+ const toolCallEvent = {
190
+ type: GeminiEventType.ToolCallRequest,
191
+ value: {
192
+ callId: 'tool-1',
193
+ name: 'errorTool',
194
+ args: {},
195
+ isClientInitiated: false,
196
+ prompt_id: 'prompt-id-3',
197
+ },
198
+ };
199
+ mockCoreExecuteToolCall.mockResolvedValue({
200
+ status: 'error',
201
+ request: {
202
+ callId: 'tool-1',
203
+ name: 'errorTool',
204
+ args: {},
205
+ isClientInitiated: false,
206
+ prompt_id: 'prompt-id-3',
207
+ },
208
+ tool: {},
209
+ response: {
210
+ callId: 'tool-1',
211
+ error: new Error('Execution failed'),
212
+ errorType: ToolErrorType.EXECUTION_FAILED,
213
+ responseParts: [
214
+ {
215
+ functionResponse: {
216
+ name: 'errorTool',
217
+ response: {
218
+ output: 'Error: Execution failed',
219
+ },
220
+ },
221
+ },
222
+ ],
223
+ resultDisplay: 'Execution failed',
224
+ contentLength: undefined,
225
+ },
226
+ });
227
+ const finalResponse = [
228
+ {
229
+ type: GeminiEventType.Content,
230
+ value: 'Sorry, let me try again.',
231
+ },
232
+ {
233
+ type: GeminiEventType.Finished,
234
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
235
+ },
236
+ ];
237
+ mockGeminiClient.sendMessageStream
238
+ .mockReturnValueOnce(createStreamFromEvents([toolCallEvent]))
239
+ .mockReturnValueOnce(createStreamFromEvents(finalResponse));
240
+ await runNonInteractive(mockConfig, mockSettings, 'Trigger tool error', 'prompt-id-3');
241
+ expect(mockCoreExecuteToolCall).toHaveBeenCalled();
242
+ expect(consoleErrorSpy).toHaveBeenCalledWith('Error executing tool errorTool: Execution failed');
243
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
244
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenNthCalledWith(2, [
245
+ {
246
+ functionResponse: {
247
+ name: 'errorTool',
248
+ response: {
249
+ output: 'Error: Execution failed',
250
+ },
251
+ },
252
+ },
253
+ ], expect.any(AbortSignal), 'prompt-id-3');
254
+ expect(processStdoutSpy).toHaveBeenCalledWith('Sorry, let me try again.');
255
+ });
256
+ it('should exit with error if sendMessageStream throws initially', async () => {
257
+ const apiError = new Error('API connection failed');
258
+ mockGeminiClient.sendMessageStream.mockImplementation(() => {
259
+ throw apiError;
260
+ });
261
+ await expect(runNonInteractive(mockConfig, mockSettings, 'Initial fail', 'prompt-id-4')).rejects.toThrow(apiError);
262
+ });
263
+ it('should not exit if a tool is not found, and should send error back to model', async () => {
264
+ const toolCallEvent = {
265
+ type: GeminiEventType.ToolCallRequest,
266
+ value: {
267
+ callId: 'tool-1',
268
+ name: 'nonexistentTool',
269
+ args: {},
270
+ isClientInitiated: false,
271
+ prompt_id: 'prompt-id-5',
272
+ },
273
+ };
274
+ mockCoreExecuteToolCall.mockResolvedValue({
275
+ status: 'error',
276
+ request: {
277
+ callId: 'tool-1',
278
+ name: 'nonexistentTool',
279
+ args: {},
280
+ isClientInitiated: false,
281
+ prompt_id: 'prompt-id-5',
282
+ },
283
+ response: {
284
+ callId: 'tool-1',
285
+ error: new Error('Tool "nonexistentTool" not found in registry.'),
286
+ resultDisplay: 'Tool "nonexistentTool" not found in registry.',
287
+ responseParts: [],
288
+ errorType: undefined,
289
+ contentLength: undefined,
290
+ },
291
+ });
292
+ const finalResponse = [
293
+ {
294
+ type: GeminiEventType.Content,
295
+ value: "Sorry, I can't find that tool.",
296
+ },
297
+ {
298
+ type: GeminiEventType.Finished,
299
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
300
+ },
301
+ ];
302
+ mockGeminiClient.sendMessageStream
303
+ .mockReturnValueOnce(createStreamFromEvents([toolCallEvent]))
304
+ .mockReturnValueOnce(createStreamFromEvents(finalResponse));
305
+ await runNonInteractive(mockConfig, mockSettings, 'Trigger tool not found', 'prompt-id-5');
306
+ expect(mockCoreExecuteToolCall).toHaveBeenCalled();
307
+ expect(consoleErrorSpy).toHaveBeenCalledWith('Error executing tool nonexistentTool: Tool "nonexistentTool" not found in registry.');
308
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
309
+ expect(processStdoutSpy).toHaveBeenCalledWith("Sorry, I can't find that tool.");
310
+ });
311
+ it('should exit when max session turns are exceeded', async () => {
312
+ vi.mocked(mockConfig.getMaxSessionTurns).mockReturnValue(0);
313
+ await expect(runNonInteractive(mockConfig, mockSettings, 'Trigger loop', 'prompt-id-6')).rejects.toThrow('process.exit(53) called');
314
+ });
315
+ it('should preprocess @include commands before sending to the model', async () => {
316
+ // 1. Mock the imported atCommandProcessor
317
+ const { handleAtCommand } = await import('./ui/hooks/atCommandProcessor.js');
318
+ const mockHandleAtCommand = vi.mocked(handleAtCommand);
319
+ // 2. Define the raw input and the expected processed output
320
+ const rawInput = 'Summarize @file.txt';
321
+ const processedParts = [
322
+ { text: 'Summarize @file.txt' },
323
+ { text: '\n--- Content from referenced files ---\n' },
324
+ { text: 'This is the content of the file.' },
325
+ { text: '\n--- End of content ---' },
326
+ ];
327
+ // 3. Setup the mock to return the processed parts
328
+ mockHandleAtCommand.mockResolvedValue({
329
+ processedQuery: processedParts,
330
+ shouldProceed: true,
331
+ });
332
+ // Mock a simple stream response from the Gemini client
333
+ const events = [
334
+ { type: GeminiEventType.Content, value: 'Summary complete.' },
335
+ {
336
+ type: GeminiEventType.Finished,
337
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
338
+ },
339
+ ];
340
+ mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
341
+ // 4. Run the non-interactive mode with the raw input
342
+ await runNonInteractive(mockConfig, mockSettings, rawInput, 'prompt-id-7');
343
+ // 5. Assert that sendMessageStream was called with the PROCESSED parts, not the raw input
344
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith(processedParts, expect.any(AbortSignal), 'prompt-id-7');
345
+ // 6. Assert the final output is correct
346
+ expect(processStdoutSpy).toHaveBeenCalledWith('Summary complete.');
347
+ });
348
+ it('should process input and write JSON output with stats', async () => {
349
+ const events = [
350
+ { type: GeminiEventType.Content, value: 'Hello World' },
351
+ {
352
+ type: GeminiEventType.Finished,
353
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
354
+ },
355
+ ];
356
+ mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
357
+ vi.mocked(mockConfig.getOutputFormat).mockReturnValue(OutputFormat.JSON);
358
+ const mockMetrics = {
359
+ models: {},
360
+ tools: {
361
+ totalCalls: 0,
362
+ totalSuccess: 0,
363
+ totalFail: 0,
364
+ totalDurationMs: 0,
365
+ totalDecisions: {
366
+ accept: 0,
367
+ reject: 0,
368
+ modify: 0,
369
+ auto_accept: 0,
370
+ },
371
+ byName: {},
372
+ },
373
+ files: {
374
+ totalLinesAdded: 0,
375
+ totalLinesRemoved: 0,
376
+ },
377
+ };
378
+ vi.mocked(uiTelemetryService.getMetrics).mockReturnValue(mockMetrics);
379
+ await runNonInteractive(mockConfig, mockSettings, 'Test input', 'prompt-id-1');
380
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith([{ text: 'Test input' }], expect.any(AbortSignal), 'prompt-id-1');
381
+ expect(processStdoutSpy).toHaveBeenCalledWith(JSON.stringify({ response: 'Hello World', stats: mockMetrics }, null, 2));
382
+ });
383
+ it('should write JSON output with stats for tool-only commands (no text response)', async () => {
384
+ // Test the scenario where a command completes successfully with only tool calls
385
+ // but no text response - this would have caught the original bug
386
+ const toolCallEvent = {
387
+ type: GeminiEventType.ToolCallRequest,
388
+ value: {
389
+ callId: 'tool-1',
390
+ name: 'testTool',
391
+ args: { arg1: 'value1' },
392
+ isClientInitiated: false,
393
+ prompt_id: 'prompt-id-tool-only',
394
+ },
395
+ };
396
+ const toolResponse = [{ text: 'Tool executed successfully' }];
397
+ mockCoreExecuteToolCall.mockResolvedValue({
398
+ status: 'success',
399
+ request: {
400
+ callId: 'tool-1',
401
+ name: 'testTool',
402
+ args: { arg1: 'value1' },
403
+ isClientInitiated: false,
404
+ prompt_id: 'prompt-id-tool-only',
405
+ },
406
+ tool: {},
407
+ invocation: {},
408
+ response: {
409
+ responseParts: toolResponse,
410
+ callId: 'tool-1',
411
+ error: undefined,
412
+ errorType: undefined,
413
+ contentLength: undefined,
414
+ },
415
+ });
416
+ // First call returns only tool call, no content
417
+ const firstCallEvents = [
418
+ toolCallEvent,
419
+ {
420
+ type: GeminiEventType.Finished,
421
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 5 } },
422
+ },
423
+ ];
424
+ // Second call returns no content (tool-only completion)
425
+ const secondCallEvents = [
426
+ {
427
+ type: GeminiEventType.Finished,
428
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 3 } },
429
+ },
430
+ ];
431
+ mockGeminiClient.sendMessageStream
432
+ .mockReturnValueOnce(createStreamFromEvents(firstCallEvents))
433
+ .mockReturnValueOnce(createStreamFromEvents(secondCallEvents));
434
+ vi.mocked(mockConfig.getOutputFormat).mockReturnValue(OutputFormat.JSON);
435
+ const mockMetrics = {
436
+ models: {},
437
+ tools: {
438
+ totalCalls: 1,
439
+ totalSuccess: 1,
440
+ totalFail: 0,
441
+ totalDurationMs: 100,
442
+ totalDecisions: {
443
+ accept: 1,
444
+ reject: 0,
445
+ modify: 0,
446
+ auto_accept: 0,
447
+ },
448
+ byName: {
449
+ testTool: {
450
+ count: 1,
451
+ success: 1,
452
+ fail: 0,
453
+ durationMs: 100,
454
+ decisions: {
455
+ accept: 1,
456
+ reject: 0,
457
+ modify: 0,
458
+ auto_accept: 0,
459
+ },
460
+ },
461
+ },
462
+ },
463
+ files: {
464
+ totalLinesAdded: 0,
465
+ totalLinesRemoved: 0,
466
+ },
467
+ };
468
+ vi.mocked(uiTelemetryService.getMetrics).mockReturnValue(mockMetrics);
469
+ await runNonInteractive(mockConfig, mockSettings, 'Execute tool only', 'prompt-id-tool-only');
470
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
471
+ expect(mockCoreExecuteToolCall).toHaveBeenCalledWith(mockConfig, expect.objectContaining({ name: 'testTool' }), expect.any(AbortSignal));
472
+ // This should output JSON with empty response but include stats
473
+ expect(processStdoutSpy).toHaveBeenCalledWith(JSON.stringify({ response: '', stats: mockMetrics }, null, 2));
474
+ });
475
+ it('should write JSON output with stats for empty response commands', async () => {
476
+ // Test the scenario where a command completes but produces no content at all
477
+ const events = [
478
+ {
479
+ type: GeminiEventType.Finished,
480
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 1 } },
481
+ },
482
+ ];
483
+ mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
484
+ vi.mocked(mockConfig.getOutputFormat).mockReturnValue(OutputFormat.JSON);
485
+ const mockMetrics = {
486
+ models: {},
487
+ tools: {
488
+ totalCalls: 0,
489
+ totalSuccess: 0,
490
+ totalFail: 0,
491
+ totalDurationMs: 0,
492
+ totalDecisions: {
493
+ accept: 0,
494
+ reject: 0,
495
+ modify: 0,
496
+ auto_accept: 0,
497
+ },
498
+ byName: {},
499
+ },
500
+ files: {
501
+ totalLinesAdded: 0,
502
+ totalLinesRemoved: 0,
503
+ },
504
+ };
505
+ vi.mocked(uiTelemetryService.getMetrics).mockReturnValue(mockMetrics);
506
+ await runNonInteractive(mockConfig, mockSettings, 'Empty response test', 'prompt-id-empty');
507
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith([{ text: 'Empty response test' }], expect.any(AbortSignal), 'prompt-id-empty');
508
+ // This should output JSON with empty response but include stats
509
+ expect(processStdoutSpy).toHaveBeenCalledWith(JSON.stringify({ response: '', stats: mockMetrics }, null, 2));
510
+ });
511
+ it('should handle errors in JSON format', async () => {
512
+ vi.mocked(mockConfig.getOutputFormat).mockReturnValue(OutputFormat.JSON);
513
+ const testError = new Error('Invalid input provided');
514
+ mockGeminiClient.sendMessageStream.mockImplementation(() => {
515
+ throw testError;
516
+ });
517
+ // Mock console.error to capture JSON error output
518
+ const consoleErrorJsonSpy = vi
519
+ .spyOn(console, 'error')
520
+ .mockImplementation(() => { });
521
+ let thrownError = null;
522
+ try {
523
+ await runNonInteractive(mockConfig, mockSettings, 'Test input', 'prompt-id-error');
524
+ // Should not reach here
525
+ expect.fail('Expected process.exit to be called');
526
+ }
527
+ catch (error) {
528
+ thrownError = error;
529
+ }
530
+ // Should throw because of mocked process.exit
531
+ expect(thrownError?.message).toBe('process.exit(1) called');
532
+ expect(consoleErrorJsonSpy).toHaveBeenCalledWith(JSON.stringify({
533
+ error: {
534
+ type: 'Error',
535
+ message: 'Invalid input provided',
536
+ code: 1,
537
+ },
538
+ }, null, 2));
539
+ });
540
+ it('should handle FatalInputError with custom exit code in JSON format', async () => {
541
+ vi.mocked(mockConfig.getOutputFormat).mockReturnValue(OutputFormat.JSON);
542
+ const fatalError = new FatalInputError('Invalid command syntax provided');
543
+ mockGeminiClient.sendMessageStream.mockImplementation(() => {
544
+ throw fatalError;
545
+ });
546
+ // Mock console.error to capture JSON error output
547
+ const consoleErrorJsonSpy = vi
548
+ .spyOn(console, 'error')
549
+ .mockImplementation(() => { });
550
+ let thrownError = null;
551
+ try {
552
+ await runNonInteractive(mockConfig, mockSettings, 'Invalid syntax', 'prompt-id-fatal');
553
+ // Should not reach here
554
+ expect.fail('Expected process.exit to be called');
555
+ }
556
+ catch (error) {
557
+ thrownError = error;
558
+ }
559
+ // Should throw because of mocked process.exit with custom exit code
560
+ expect(thrownError?.message).toBe('process.exit(42) called');
561
+ expect(consoleErrorJsonSpy).toHaveBeenCalledWith(JSON.stringify({
562
+ error: {
563
+ type: 'FatalInputError',
564
+ message: 'Invalid command syntax provided',
565
+ code: 42,
566
+ },
567
+ }, null, 2));
568
+ });
569
+ it('should execute a slash command that returns a prompt', async () => {
570
+ const mockCommand = {
571
+ name: 'testcommand',
572
+ description: 'a test command',
573
+ action: vi.fn().mockResolvedValue({
574
+ type: 'submit_prompt',
575
+ content: [{ text: 'Prompt from command' }],
576
+ }),
577
+ };
578
+ mockGetCommands.mockReturnValue([mockCommand]);
579
+ const events = [
580
+ { type: GeminiEventType.Content, value: 'Response from command' },
581
+ {
582
+ type: GeminiEventType.Finished,
583
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 5 } },
584
+ },
585
+ ];
586
+ mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
587
+ await runNonInteractive(mockConfig, mockSettings, '/testcommand', 'prompt-id-slash');
588
+ // Ensure the prompt sent to the model is from the command, not the raw input
589
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith([{ text: 'Prompt from command' }], expect.any(AbortSignal), 'prompt-id-slash');
590
+ expect(processStdoutSpy).toHaveBeenCalledWith('Response from command');
591
+ });
592
+ it('should throw FatalInputError if a command requires confirmation', async () => {
593
+ const mockCommand = {
594
+ name: 'confirm',
595
+ description: 'a command that needs confirmation',
596
+ action: vi.fn().mockResolvedValue({
597
+ type: 'confirm_shell_commands',
598
+ commands: ['rm -rf /'],
599
+ }),
600
+ };
601
+ mockGetCommands.mockReturnValue([mockCommand]);
602
+ await expect(runNonInteractive(mockConfig, mockSettings, '/confirm', 'prompt-id-confirm')).rejects.toThrow('Exiting due to a confirmation prompt requested by the command.');
603
+ });
604
+ it('should treat an unknown slash command as a regular prompt', async () => {
605
+ // No commands are mocked, so any slash command is "unknown"
606
+ mockGetCommands.mockReturnValue([]);
607
+ const events = [
608
+ { type: GeminiEventType.Content, value: 'Response to unknown' },
609
+ {
610
+ type: GeminiEventType.Finished,
611
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 5 } },
612
+ },
613
+ ];
614
+ mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
615
+ await runNonInteractive(mockConfig, mockSettings, '/unknowncommand', 'prompt-id-unknown');
616
+ // Ensure the raw input is sent to the model
617
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith([{ text: '/unknowncommand' }], expect.any(AbortSignal), 'prompt-id-unknown');
618
+ expect(processStdoutSpy).toHaveBeenCalledWith('Response to unknown');
619
+ });
620
+ it('should throw for unhandled command result types', async () => {
621
+ const mockCommand = {
622
+ name: 'noaction',
623
+ description: 'unhandled type',
624
+ action: vi.fn().mockResolvedValue({
625
+ type: 'unhandled',
626
+ }),
627
+ };
628
+ mockGetCommands.mockReturnValue([mockCommand]);
629
+ await expect(runNonInteractive(mockConfig, mockSettings, '/noaction', 'prompt-id-unhandled')).rejects.toThrow('Exiting due to command result that is not supported in non-interactive mode.');
630
+ });
631
+ it('should pass arguments to the slash command action', async () => {
632
+ const mockAction = vi.fn().mockResolvedValue({
633
+ type: 'submit_prompt',
634
+ content: [{ text: 'Prompt from command' }],
635
+ });
636
+ const mockCommand = {
637
+ name: 'testargs',
638
+ description: 'a test command',
639
+ action: mockAction,
640
+ };
641
+ mockGetCommands.mockReturnValue([mockCommand]);
642
+ const events = [
643
+ { type: GeminiEventType.Content, value: 'Acknowledged' },
644
+ {
645
+ type: GeminiEventType.Finished,
646
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 1 } },
647
+ },
648
+ ];
649
+ mockGeminiClient.sendMessageStream.mockReturnValue(createStreamFromEvents(events));
650
+ await runNonInteractive(mockConfig, mockSettings, '/testargs arg1 arg2', 'prompt-id-args');
651
+ expect(mockAction).toHaveBeenCalledWith(expect.any(Object), 'arg1 arg2');
652
+ expect(processStdoutSpy).toHaveBeenCalledWith('Acknowledged');
653
+ });
654
+ it('should allow a normally-excluded tool when --allowed-tools is set', async () => {
655
+ // By default, ShellTool is excluded in non-interactive mode.
656
+ // This test ensures that --allowed-tools overrides this exclusion.
657
+ vi.mocked(mockConfig.getToolRegistry).mockReturnValue({
658
+ getTool: vi.fn().mockReturnValue({
659
+ name: 'ShellTool',
660
+ description: 'A shell tool',
661
+ run: vi.fn(),
662
+ }),
663
+ getFunctionDeclarations: vi.fn().mockReturnValue([{ name: 'ShellTool' }]),
664
+ });
665
+ const toolCallEvent = {
666
+ type: GeminiEventType.ToolCallRequest,
667
+ value: {
668
+ callId: 'tool-shell-1',
669
+ name: 'ShellTool',
670
+ args: { command: 'ls' },
671
+ isClientInitiated: false,
672
+ prompt_id: 'prompt-id-allowed',
673
+ },
674
+ };
675
+ const toolResponse = [{ text: 'file.txt' }];
676
+ mockCoreExecuteToolCall.mockResolvedValue({
677
+ status: 'success',
678
+ request: {
679
+ callId: 'tool-shell-1',
680
+ name: 'ShellTool',
681
+ args: { command: 'ls' },
682
+ isClientInitiated: false,
683
+ prompt_id: 'prompt-id-allowed',
684
+ },
685
+ tool: {},
686
+ invocation: {},
687
+ response: {
688
+ responseParts: toolResponse,
689
+ callId: 'tool-shell-1',
690
+ error: undefined,
691
+ errorType: undefined,
692
+ contentLength: undefined,
693
+ },
694
+ });
695
+ const firstCallEvents = [toolCallEvent];
696
+ const secondCallEvents = [
697
+ { type: GeminiEventType.Content, value: 'file.txt' },
698
+ {
699
+ type: GeminiEventType.Finished,
700
+ value: { reason: undefined, usageMetadata: { totalTokenCount: 10 } },
701
+ },
702
+ ];
703
+ mockGeminiClient.sendMessageStream
704
+ .mockReturnValueOnce(createStreamFromEvents(firstCallEvents))
705
+ .mockReturnValueOnce(createStreamFromEvents(secondCallEvents));
706
+ await runNonInteractive(mockConfig, mockSettings, 'List the files', 'prompt-id-allowed');
707
+ expect(mockCoreExecuteToolCall).toHaveBeenCalledWith(mockConfig, expect.objectContaining({ name: 'ShellTool' }), expect.any(AbortSignal));
708
+ expect(processStdoutSpy).toHaveBeenCalledWith('file.txt');
709
+ });
710
+ });
711
+ //# sourceMappingURL=nonInteractiveCli.test.js.map