fss-link 1.0.40 → 1.0.45

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 (428) hide show
  1. package/dist/commands/mcp/add.test.ts +122 -0
  2. package/dist/commands/mcp/add.ts +222 -0
  3. package/dist/commands/mcp/list.test.ts +154 -0
  4. package/dist/commands/mcp/list.ts +139 -0
  5. package/dist/commands/mcp/remove.test.ts +69 -0
  6. package/dist/commands/mcp/remove.ts +60 -0
  7. package/dist/commands/mcp.test.ts +55 -0
  8. package/dist/commands/mcp.ts +27 -0
  9. package/dist/config/apiValidation.test.ts +118 -0
  10. package/dist/config/auth.test.ts +79 -0
  11. package/dist/config/auth.ts +100 -0
  12. package/dist/config/config.integration.test.ts +407 -0
  13. package/dist/config/config.test.ts +1952 -0
  14. package/dist/config/config.ts +690 -0
  15. package/dist/config/database.test.ts +96 -0
  16. package/dist/config/database.ts +752 -0
  17. package/dist/config/extension.test.ts +236 -0
  18. package/dist/config/extension.ts +180 -0
  19. package/dist/config/keyBindings.test.ts +62 -0
  20. package/dist/config/keyBindings.ts +184 -0
  21. package/dist/config/modelManager.ts +275 -0
  22. package/dist/config/providerManager.ts +244 -0
  23. package/dist/config/providerPersistence.test.ts +377 -0
  24. package/dist/config/providerPersistence.ts +105 -0
  25. package/dist/config/sandboxConfig.ts +107 -0
  26. package/dist/config/settings.test.ts +1424 -0
  27. package/dist/config/settings.ts +517 -0
  28. package/dist/config/settingsSchema.test.ts +252 -0
  29. package/dist/config/settingsSchema.ts +728 -0
  30. package/dist/config/trustedFolders.test.ts +208 -0
  31. package/dist/config/trustedFolders.ts +167 -0
  32. package/dist/gemini.test.tsx +252 -0
  33. package/dist/gemini.tsx +357 -0
  34. package/dist/generated/git-commit.ts +10 -0
  35. package/dist/index.ts +21 -0
  36. package/dist/nonInteractiveCli.test.ts +276 -0
  37. package/dist/nonInteractiveCli.ts +143 -0
  38. package/dist/package.json +87 -87
  39. package/dist/patches/is-in-ci.ts +17 -0
  40. package/dist/services/BuiltinCommandLoader.test.ts +127 -0
  41. package/dist/services/BuiltinCommandLoader.ts +95 -0
  42. package/dist/services/CommandService.test.ts +352 -0
  43. package/dist/services/CommandService.ts +103 -0
  44. package/dist/services/FileCommandLoader.test.ts +1002 -0
  45. package/dist/services/FileCommandLoader.ts +289 -0
  46. package/dist/services/McpPromptLoader.ts +231 -0
  47. package/dist/services/SearchEngineConfigProvider.ts +100 -0
  48. package/dist/services/prompt-processors/argumentProcessor.test.ts +41 -0
  49. package/dist/services/prompt-processors/argumentProcessor.ts +23 -0
  50. package/dist/services/prompt-processors/shellProcessor.test.ts +709 -0
  51. package/dist/services/prompt-processors/shellProcessor.ts +248 -0
  52. package/dist/services/prompt-processors/types.ts +44 -0
  53. package/dist/services/types.ts +24 -0
  54. package/dist/src/config/apiValidation.test.d.ts +6 -0
  55. package/dist/src/config/apiValidation.test.js +99 -0
  56. package/dist/src/config/apiValidation.test.js.map +1 -0
  57. package/dist/src/config/database.d.ts +32 -0
  58. package/dist/src/config/database.js +281 -2
  59. package/dist/src/config/database.js.map +1 -1
  60. package/dist/src/config/database.test.d.ts +6 -0
  61. package/dist/src/config/database.test.js +80 -0
  62. package/dist/src/config/database.test.js.map +1 -0
  63. package/dist/src/config/providerManager.d.ts +74 -0
  64. package/dist/src/config/providerManager.js +203 -0
  65. package/dist/src/config/providerManager.js.map +1 -0
  66. package/dist/src/config/providerPersistence.d.ts +75 -0
  67. package/dist/src/config/providerPersistence.js +55 -0
  68. package/dist/src/config/providerPersistence.js.map +1 -0
  69. package/dist/src/config/providerPersistence.test.d.ts +6 -0
  70. package/dist/src/config/providerPersistence.test.js +283 -0
  71. package/dist/src/config/providerPersistence.test.js.map +1 -0
  72. package/dist/src/config/settingsSchema.d.ts +9 -0
  73. package/dist/src/config/settingsSchema.js +9 -0
  74. package/dist/src/config/settingsSchema.js.map +1 -1
  75. package/dist/src/generated/git-commit.d.ts +1 -1
  76. package/dist/src/generated/git-commit.js +1 -1
  77. package/dist/src/services/BuiltinCommandLoader.js +2 -0
  78. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  79. package/dist/src/ui/App.js +14 -2
  80. package/dist/src/ui/App.js.map +1 -1
  81. package/dist/src/ui/commands/contextCommand.d.ts +7 -0
  82. package/dist/src/ui/commands/contextCommand.js +115 -0
  83. package/dist/src/ui/commands/contextCommand.js.map +1 -0
  84. package/dist/src/ui/components/ContextUsageDisplay.d.ts +3 -1
  85. package/dist/src/ui/components/ContextUsageDisplay.js +43 -3
  86. package/dist/src/ui/components/ContextUsageDisplay.js.map +1 -1
  87. package/dist/src/ui/components/Footer.d.ts +1 -0
  88. package/dist/src/ui/components/Footer.js +2 -2
  89. package/dist/src/ui/components/Footer.js.map +1 -1
  90. package/dist/src/ui/components/GeminiKeyDialog.d.ts +11 -0
  91. package/dist/src/ui/components/GeminiKeyDialog.js +156 -0
  92. package/dist/src/ui/components/GeminiKeyDialog.js.map +1 -0
  93. package/dist/src/ui/components/OpenAIEndpointDialog.d.ts +19 -0
  94. package/dist/src/ui/components/OpenAIEndpointDialog.js +163 -0
  95. package/dist/src/ui/components/OpenAIEndpointDialog.js.map +1 -0
  96. package/dist/src/ui/components/WelcomeBackDialog.d.ts +36 -0
  97. package/dist/src/ui/components/WelcomeBackDialog.js +109 -0
  98. package/dist/src/ui/components/WelcomeBackDialog.js.map +1 -0
  99. package/dist/src/ui/hooks/useWelcomeBack.d.ts +52 -0
  100. package/dist/src/ui/hooks/useWelcomeBack.js +214 -0
  101. package/dist/src/ui/hooks/useWelcomeBack.js.map +1 -0
  102. package/dist/src/zed-integration/schema.d.ts +1516 -1516
  103. package/dist/test-setup.ts +12 -0
  104. package/dist/test-utils/customMatchers.ts +65 -0
  105. package/dist/test-utils/mockCommandContext.test.ts +62 -0
  106. package/dist/test-utils/mockCommandContext.ts +105 -0
  107. package/dist/test-utils/render.tsx +18 -0
  108. package/dist/tsconfig.tsbuildinfo +1 -1
  109. package/dist/ui/App.test.tsx +2181 -0
  110. package/dist/ui/App.tsx +1344 -0
  111. package/dist/ui/IdeIntegrationNudge.tsx +98 -0
  112. package/dist/ui/__snapshots__/App.test.tsx.snap +124 -0
  113. package/dist/ui/colors.ts +56 -0
  114. package/dist/ui/commands/aboutCommand.test.ts +153 -0
  115. package/dist/ui/commands/aboutCommand.ts +49 -0
  116. package/dist/ui/commands/authCommand.test.ts +36 -0
  117. package/dist/ui/commands/authCommand.ts +17 -0
  118. package/dist/ui/commands/bugCommand.test.ts +114 -0
  119. package/dist/ui/commands/bugCommand.ts +92 -0
  120. package/dist/ui/commands/chatCommand.test.ts +414 -0
  121. package/dist/ui/commands/chatCommand.ts +280 -0
  122. package/dist/ui/commands/clearCommand.test.ts +100 -0
  123. package/dist/ui/commands/clearCommand.ts +29 -0
  124. package/dist/ui/commands/compressCommand.test.ts +129 -0
  125. package/dist/ui/commands/compressCommand.ts +78 -0
  126. package/dist/ui/commands/contextCommand.ts +132 -0
  127. package/dist/ui/commands/copyCommand.test.ts +296 -0
  128. package/dist/ui/commands/copyCommand.ts +67 -0
  129. package/dist/ui/commands/corgiCommand.test.ts +34 -0
  130. package/dist/ui/commands/corgiCommand.ts +16 -0
  131. package/dist/ui/commands/directoryCommand.test.tsx +185 -0
  132. package/dist/ui/commands/directoryCommand.tsx +179 -0
  133. package/dist/ui/commands/docsCommand.test.ts +99 -0
  134. package/dist/ui/commands/docsCommand.ts +42 -0
  135. package/dist/ui/commands/editorCommand.test.ts +30 -0
  136. package/dist/ui/commands/editorCommand.ts +21 -0
  137. package/dist/ui/commands/extensionsCommand.test.ts +67 -0
  138. package/dist/ui/commands/extensionsCommand.ts +46 -0
  139. package/dist/ui/commands/helpCommand.test.ts +52 -0
  140. package/dist/ui/commands/helpCommand.ts +23 -0
  141. package/dist/ui/commands/ideCommand.test.ts +255 -0
  142. package/dist/ui/commands/ideCommand.ts +283 -0
  143. package/dist/ui/commands/initCommand.test.ts +127 -0
  144. package/dist/ui/commands/initCommand.ts +117 -0
  145. package/dist/ui/commands/mcpCommand.test.ts +1057 -0
  146. package/dist/ui/commands/mcpCommand.ts +531 -0
  147. package/dist/ui/commands/memoryCommand.test.ts +344 -0
  148. package/dist/ui/commands/memoryCommand.ts +305 -0
  149. package/dist/ui/commands/privacyCommand.test.ts +38 -0
  150. package/dist/ui/commands/privacyCommand.ts +17 -0
  151. package/dist/ui/commands/quitCommand.test.ts +55 -0
  152. package/dist/ui/commands/quitCommand.ts +36 -0
  153. package/dist/ui/commands/restoreCommand.test.ts +250 -0
  154. package/dist/ui/commands/restoreCommand.ts +157 -0
  155. package/dist/ui/commands/searchEngineSetupCommand.ts +18 -0
  156. package/dist/ui/commands/settingsCommand.test.ts +36 -0
  157. package/dist/ui/commands/settingsCommand.ts +17 -0
  158. package/dist/ui/commands/setupGithubCommand.test.ts +238 -0
  159. package/dist/ui/commands/setupGithubCommand.ts +212 -0
  160. package/dist/ui/commands/speakCommand.ts +175 -0
  161. package/dist/ui/commands/statsCommand.test.ts +78 -0
  162. package/dist/ui/commands/statsCommand.ts +70 -0
  163. package/dist/ui/commands/terminalSetupCommand.test.ts +85 -0
  164. package/dist/ui/commands/terminalSetupCommand.ts +45 -0
  165. package/dist/ui/commands/themeCommand.test.ts +38 -0
  166. package/dist/ui/commands/themeCommand.ts +17 -0
  167. package/dist/ui/commands/toolsCommand.test.ts +105 -0
  168. package/dist/ui/commands/toolsCommand.ts +71 -0
  169. package/dist/ui/commands/ttsCommand.ts +143 -0
  170. package/dist/ui/commands/types.ts +204 -0
  171. package/dist/ui/commands/vimCommand.ts +25 -0
  172. package/dist/ui/commands/voiceCommand.ts +125 -0
  173. package/dist/ui/components/AboutBox.tsx +133 -0
  174. package/dist/ui/components/AsciiArt.ts +54 -0
  175. package/dist/ui/components/AuthDialog.test.tsx +334 -0
  176. package/dist/ui/components/AuthDialog.tsx +289 -0
  177. package/dist/ui/components/AuthInProgress.tsx +62 -0
  178. package/dist/ui/components/AutoAcceptIndicator.tsx +47 -0
  179. package/dist/ui/components/ConsoleSummaryDisplay.tsx +35 -0
  180. package/dist/ui/components/ContextSummaryDisplay.test.tsx +85 -0
  181. package/dist/ui/components/ContextSummaryDisplay.tsx +120 -0
  182. package/dist/ui/components/ContextUsageDisplay.tsx +77 -0
  183. package/dist/ui/components/DebugProfiler.tsx +36 -0
  184. package/dist/ui/components/DetailedMessagesDisplay.tsx +82 -0
  185. package/dist/ui/components/EditorSettingsDialog.tsx +172 -0
  186. package/dist/ui/components/FolderTrustDialog.test.tsx +36 -0
  187. package/dist/ui/components/FolderTrustDialog.tsx +74 -0
  188. package/dist/ui/components/Footer.test.tsx +159 -0
  189. package/dist/ui/components/Footer.tsx +158 -0
  190. package/dist/ui/components/GeminiKeyDialog.tsx +252 -0
  191. package/dist/ui/components/GeminiRespondingSpinner.tsx +34 -0
  192. package/dist/ui/components/Header.test.tsx +44 -0
  193. package/dist/ui/components/Header.tsx +70 -0
  194. package/dist/ui/components/Help.tsx +174 -0
  195. package/dist/ui/components/HistoryItemDisplay.test.tsx +125 -0
  196. package/dist/ui/components/HistoryItemDisplay.tsx +98 -0
  197. package/dist/ui/components/InputPrompt.test.tsx +1467 -0
  198. package/dist/ui/components/InputPrompt.tsx +641 -0
  199. package/dist/ui/components/LMStudioModelPrompt.tsx +215 -0
  200. package/dist/ui/components/LoadingIndicator.test.tsx +296 -0
  201. package/dist/ui/components/LoadingIndicator.tsx +82 -0
  202. package/dist/ui/components/MemoryUsageDisplay.tsx +36 -0
  203. package/dist/ui/components/ModelStatsDisplay.test.tsx +252 -0
  204. package/dist/ui/components/ModelStatsDisplay.tsx +197 -0
  205. package/dist/ui/components/OllamaModelPrompt.tsx +206 -0
  206. package/dist/ui/components/OpenAIEndpointDialog.tsx +261 -0
  207. package/dist/ui/components/OpenAIKeyPrompt.test.tsx +64 -0
  208. package/dist/ui/components/OpenAIKeyPrompt.tsx +197 -0
  209. package/dist/ui/components/PrepareLabel.tsx +48 -0
  210. package/dist/ui/components/SearchEngineConfigDialog.tsx +280 -0
  211. package/dist/ui/components/SessionSummaryDisplay.test.tsx +75 -0
  212. package/dist/ui/components/SessionSummaryDisplay.tsx +18 -0
  213. package/dist/ui/components/SettingsDialog.test.tsx +865 -0
  214. package/dist/ui/components/SettingsDialog.tsx +753 -0
  215. package/dist/ui/components/ShellConfirmationDialog.test.tsx +53 -0
  216. package/dist/ui/components/ShellConfirmationDialog.tsx +103 -0
  217. package/dist/ui/components/ShellModeIndicator.tsx +18 -0
  218. package/dist/ui/components/ShowMoreLines.tsx +40 -0
  219. package/dist/ui/components/StatsDisplay.test.tsx +401 -0
  220. package/dist/ui/components/StatsDisplay.tsx +273 -0
  221. package/dist/ui/components/SuggestionsDisplay.tsx +102 -0
  222. package/dist/ui/components/ThemeDialog.tsx +310 -0
  223. package/dist/ui/components/Tips.tsx +45 -0
  224. package/dist/ui/components/TodoDisplay.test.tsx +97 -0
  225. package/dist/ui/components/TodoDisplay.tsx +72 -0
  226. package/dist/ui/components/ToolStatsDisplay.test.tsx +180 -0
  227. package/dist/ui/components/ToolStatsDisplay.tsx +208 -0
  228. package/dist/ui/components/UpdateNotification.tsx +23 -0
  229. package/dist/ui/components/WelcomeBackDialog.tsx +290 -0
  230. package/dist/ui/components/__snapshots__/IDEContextDetailDisplay.test.tsx.snap +24 -0
  231. package/dist/ui/components/__snapshots__/ModelStatsDisplay.test.tsx.snap +121 -0
  232. package/dist/ui/components/__snapshots__/SessionSummaryDisplay.test.tsx.snap +30 -0
  233. package/dist/ui/components/__snapshots__/ShellConfirmationDialog.test.tsx.snap +21 -0
  234. package/dist/ui/components/__snapshots__/StatsDisplay.test.tsx.snap +264 -0
  235. package/dist/ui/components/__snapshots__/ToolStatsDisplay.test.tsx.snap +91 -0
  236. package/dist/ui/components/messages/CompressionMessage.tsx +49 -0
  237. package/dist/ui/components/messages/DiffRenderer.test.tsx +365 -0
  238. package/dist/ui/components/messages/DiffRenderer.tsx +358 -0
  239. package/dist/ui/components/messages/ErrorMessage.tsx +31 -0
  240. package/dist/ui/components/messages/GeminiMessage.tsx +43 -0
  241. package/dist/ui/components/messages/GeminiMessageContent.tsx +43 -0
  242. package/dist/ui/components/messages/InfoMessage.tsx +32 -0
  243. package/dist/ui/components/messages/ToolConfirmationMessage.test.tsx +58 -0
  244. package/dist/ui/components/messages/ToolConfirmationMessage.tsx +297 -0
  245. package/dist/ui/components/messages/ToolGroupMessage.tsx +126 -0
  246. package/dist/ui/components/messages/ToolMessage.test.tsx +183 -0
  247. package/dist/ui/components/messages/ToolMessage.tsx +296 -0
  248. package/dist/ui/components/messages/UserMessage.tsx +43 -0
  249. package/dist/ui/components/messages/UserShellMessage.tsx +25 -0
  250. package/dist/ui/components/shared/MaxSizedBox.test.tsx +425 -0
  251. package/dist/ui/components/shared/MaxSizedBox.tsx +624 -0
  252. package/dist/ui/components/shared/RadioButtonSelect.test.tsx +181 -0
  253. package/dist/ui/components/shared/RadioButtonSelect.tsx +234 -0
  254. package/dist/ui/components/shared/__snapshots__/RadioButtonSelect.test.tsx.snap +47 -0
  255. package/dist/ui/components/shared/text-buffer.test.ts +1728 -0
  256. package/dist/ui/components/shared/text-buffer.ts +2227 -0
  257. package/dist/ui/components/shared/vim-buffer-actions.test.ts +1119 -0
  258. package/dist/ui/components/shared/vim-buffer-actions.ts +814 -0
  259. package/dist/ui/constants.ts +17 -0
  260. package/dist/ui/contexts/KeypressContext.test.tsx +391 -0
  261. package/dist/ui/contexts/KeypressContext.tsx +440 -0
  262. package/dist/ui/contexts/OverflowContext.tsx +87 -0
  263. package/dist/ui/contexts/SessionContext.test.tsx +132 -0
  264. package/dist/ui/contexts/SessionContext.tsx +143 -0
  265. package/dist/ui/contexts/SettingsContext.tsx +20 -0
  266. package/dist/ui/contexts/StreamingContext.tsx +22 -0
  267. package/dist/ui/contexts/VimModeContext.tsx +79 -0
  268. package/dist/ui/editors/editorSettingsManager.ts +66 -0
  269. package/dist/ui/hooks/atCommandProcessor.test.ts +1102 -0
  270. package/dist/ui/hooks/atCommandProcessor.ts +485 -0
  271. package/dist/ui/hooks/shellCommandProcessor.test.ts +481 -0
  272. package/dist/ui/hooks/shellCommandProcessor.ts +314 -0
  273. package/dist/ui/hooks/slashCommandProcessor.test.ts +1044 -0
  274. package/dist/ui/hooks/slashCommandProcessor.ts +595 -0
  275. package/dist/ui/hooks/useAtCompletion.test.ts +497 -0
  276. package/dist/ui/hooks/useAtCompletion.ts +244 -0
  277. package/dist/ui/hooks/useAuthCommand.ts +129 -0
  278. package/dist/ui/hooks/useAutoAcceptIndicator.test.ts +300 -0
  279. package/dist/ui/hooks/useAutoAcceptIndicator.ts +52 -0
  280. package/dist/ui/hooks/useBracketedPaste.ts +37 -0
  281. package/dist/ui/hooks/useCommandCompletion.test.ts +518 -0
  282. package/dist/ui/hooks/useCommandCompletion.tsx +238 -0
  283. package/dist/ui/hooks/useCompletion.ts +128 -0
  284. package/dist/ui/hooks/useConsoleMessages.test.ts +147 -0
  285. package/dist/ui/hooks/useConsoleMessages.ts +110 -0
  286. package/dist/ui/hooks/useEditorSettings.test.ts +283 -0
  287. package/dist/ui/hooks/useEditorSettings.ts +75 -0
  288. package/dist/ui/hooks/useFocus.test.ts +119 -0
  289. package/dist/ui/hooks/useFocus.ts +48 -0
  290. package/dist/ui/hooks/useFolderTrust.test.ts +159 -0
  291. package/dist/ui/hooks/useFolderTrust.ts +72 -0
  292. package/dist/ui/hooks/useGeminiStream.test.tsx +1998 -0
  293. package/dist/ui/hooks/useGeminiStream.ts +1017 -0
  294. package/dist/ui/hooks/useGitBranchName.test.ts +280 -0
  295. package/dist/ui/hooks/useGitBranchName.ts +79 -0
  296. package/dist/ui/hooks/useHistoryManager.test.ts +202 -0
  297. package/dist/ui/hooks/useHistoryManager.ts +111 -0
  298. package/dist/ui/hooks/useInputHistory.test.ts +261 -0
  299. package/dist/ui/hooks/useInputHistory.ts +111 -0
  300. package/dist/ui/hooks/useKeypress.test.ts +280 -0
  301. package/dist/ui/hooks/useKeypress.ts +39 -0
  302. package/dist/ui/hooks/useKittyKeyboardProtocol.ts +31 -0
  303. package/dist/ui/hooks/useLoadingIndicator.test.ts +139 -0
  304. package/dist/ui/hooks/useLoadingIndicator.ts +57 -0
  305. package/dist/ui/hooks/useLogger.ts +32 -0
  306. package/dist/ui/hooks/useMessageQueue.test.ts +226 -0
  307. package/dist/ui/hooks/useMessageQueue.ts +69 -0
  308. package/dist/ui/hooks/usePhraseCycler.test.ts +145 -0
  309. package/dist/ui/hooks/usePhraseCycler.ts +198 -0
  310. package/dist/ui/hooks/usePrivacySettings.test.ts +242 -0
  311. package/dist/ui/hooks/usePrivacySettings.ts +150 -0
  312. package/dist/ui/hooks/useReactToolScheduler.ts +309 -0
  313. package/dist/ui/hooks/useRefreshMemoryCommand.ts +7 -0
  314. package/dist/ui/hooks/useReverseSearchCompletion.test.tsx +260 -0
  315. package/dist/ui/hooks/useReverseSearchCompletion.tsx +95 -0
  316. package/dist/ui/hooks/useSettingsCommand.ts +25 -0
  317. package/dist/ui/hooks/useShellHistory.test.ts +219 -0
  318. package/dist/ui/hooks/useShellHistory.ts +133 -0
  319. package/dist/ui/hooks/useShowMemoryCommand.ts +75 -0
  320. package/dist/ui/hooks/useSlashCompletion.test.ts +434 -0
  321. package/dist/ui/hooks/useSlashCompletion.ts +187 -0
  322. package/dist/ui/hooks/useStateAndRef.ts +36 -0
  323. package/dist/ui/hooks/useTerminalSize.ts +32 -0
  324. package/dist/ui/hooks/useThemeCommand.ts +110 -0
  325. package/dist/ui/hooks/useTimer.test.ts +120 -0
  326. package/dist/ui/hooks/useTimer.ts +65 -0
  327. package/dist/ui/hooks/useToolScheduler.test.ts +1123 -0
  328. package/dist/ui/hooks/useWelcomeBack.ts +253 -0
  329. package/dist/ui/hooks/vim.test.ts +1691 -0
  330. package/dist/ui/hooks/vim.ts +784 -0
  331. package/dist/ui/keyMatchers.test.ts +337 -0
  332. package/dist/ui/keyMatchers.ts +105 -0
  333. package/dist/ui/privacy/CloudFreePrivacyNotice.tsx +117 -0
  334. package/dist/ui/privacy/CloudPaidPrivacyNotice.tsx +59 -0
  335. package/dist/ui/privacy/GeminiPrivacyNotice.tsx +62 -0
  336. package/dist/ui/privacy/PrivacyNotice.tsx +42 -0
  337. package/dist/ui/semantic-colors.ts +26 -0
  338. package/dist/ui/themes/ansi-light.ts +150 -0
  339. package/dist/ui/themes/ansi.ts +159 -0
  340. package/dist/ui/themes/atom-one-dark.ts +147 -0
  341. package/dist/ui/themes/ayu-light.ts +139 -0
  342. package/dist/ui/themes/ayu.ts +113 -0
  343. package/dist/ui/themes/color-utils.test.ts +221 -0
  344. package/dist/ui/themes/color-utils.ts +231 -0
  345. package/dist/ui/themes/default-light.ts +108 -0
  346. package/dist/ui/themes/default.ts +151 -0
  347. package/dist/ui/themes/dracula.ts +124 -0
  348. package/dist/ui/themes/fss-code-dark.ts +156 -0
  349. package/dist/ui/themes/fss-dark.ts +113 -0
  350. package/dist/ui/themes/fss-light.ts +139 -0
  351. package/dist/ui/themes/github-dark.ts +147 -0
  352. package/dist/ui/themes/github-light.ts +149 -0
  353. package/dist/ui/themes/googlecode.ts +146 -0
  354. package/dist/ui/themes/no-color.ts +125 -0
  355. package/dist/ui/themes/qwen-dark.ts +118 -0
  356. package/dist/ui/themes/qwen-light.ts +144 -0
  357. package/dist/ui/themes/semantic-tokens.ts +127 -0
  358. package/dist/ui/themes/shades-of-purple.ts +352 -0
  359. package/dist/ui/themes/theme-manager.test.ts +99 -0
  360. package/dist/ui/themes/theme-manager.ts +257 -0
  361. package/dist/ui/themes/theme.test.ts +97 -0
  362. package/dist/ui/themes/theme.ts +451 -0
  363. package/dist/ui/themes/xcode.ts +154 -0
  364. package/dist/ui/types.ts +255 -0
  365. package/dist/ui/utils/CodeColorizer.tsx +217 -0
  366. package/dist/ui/utils/ConsolePatcher.ts +71 -0
  367. package/dist/ui/utils/InlineMarkdownRenderer.tsx +173 -0
  368. package/dist/ui/utils/MarkdownDisplay.test.tsx +244 -0
  369. package/dist/ui/utils/MarkdownDisplay.tsx +415 -0
  370. package/dist/ui/utils/TableRenderer.tsx +159 -0
  371. package/dist/ui/utils/__snapshots__/MarkdownDisplay.test.tsx.snap +93 -0
  372. package/dist/ui/utils/clipboardUtils.test.ts +76 -0
  373. package/dist/ui/utils/clipboardUtils.ts +149 -0
  374. package/dist/ui/utils/commandUtils.test.ts +384 -0
  375. package/dist/ui/utils/commandUtils.ts +106 -0
  376. package/dist/ui/utils/computeStats.test.ts +292 -0
  377. package/dist/ui/utils/computeStats.ts +86 -0
  378. package/dist/ui/utils/displayUtils.test.ts +58 -0
  379. package/dist/ui/utils/displayUtils.ts +32 -0
  380. package/dist/ui/utils/formatters.test.ts +72 -0
  381. package/dist/ui/utils/formatters.ts +63 -0
  382. package/dist/ui/utils/isNarrowWidth.ts +9 -0
  383. package/dist/ui/utils/kittyProtocolDetector.ts +105 -0
  384. package/dist/ui/utils/markdownUtilities.test.ts +50 -0
  385. package/dist/ui/utils/markdownUtilities.ts +125 -0
  386. package/dist/ui/utils/platformConstants.ts +52 -0
  387. package/dist/ui/utils/terminalSetup.ts +342 -0
  388. package/dist/ui/utils/textUtils.ts +40 -0
  389. package/dist/ui/utils/updateCheck.test.ts +163 -0
  390. package/dist/ui/utils/updateCheck.ts +100 -0
  391. package/dist/utils/checks.ts +28 -0
  392. package/dist/utils/cleanup.test.ts +68 -0
  393. package/dist/utils/cleanup.ts +36 -0
  394. package/dist/utils/dialogScopeUtils.ts +64 -0
  395. package/dist/utils/events.ts +14 -0
  396. package/dist/utils/gitUtils.test.ts +149 -0
  397. package/dist/utils/gitUtils.ts +116 -0
  398. package/dist/utils/handleAutoUpdate.test.ts +272 -0
  399. package/dist/utils/handleAutoUpdate.ts +145 -0
  400. package/dist/utils/installationInfo.test.ts +315 -0
  401. package/dist/utils/installationInfo.ts +176 -0
  402. package/dist/utils/package.ts +38 -0
  403. package/dist/utils/readStdin.ts +51 -0
  404. package/dist/utils/resolvePath.ts +21 -0
  405. package/dist/utils/sandbox-macos-permissive-closed.sb +32 -0
  406. package/dist/utils/sandbox-macos-permissive-open.sb +25 -0
  407. package/dist/utils/sandbox-macos-permissive-proxied.sb +37 -0
  408. package/dist/utils/sandbox-macos-restrictive-closed.sb +93 -0
  409. package/dist/utils/sandbox-macos-restrictive-open.sb +96 -0
  410. package/dist/utils/sandbox-macos-restrictive-proxied.sb +98 -0
  411. package/dist/utils/sandbox.ts +962 -0
  412. package/dist/utils/settingsUtils.test.ts +797 -0
  413. package/dist/utils/settingsUtils.ts +489 -0
  414. package/dist/utils/spawnWrapper.ts +9 -0
  415. package/dist/utils/startupWarnings.test.ts +83 -0
  416. package/dist/utils/startupWarnings.ts +40 -0
  417. package/dist/utils/updateEventEmitter.ts +13 -0
  418. package/dist/utils/userStartupWarnings.test.ts +87 -0
  419. package/dist/utils/userStartupWarnings.ts +69 -0
  420. package/dist/utils/version.ts +12 -0
  421. package/dist/validateNonInterActiveAuth.test.ts +260 -0
  422. package/dist/validateNonInterActiveAuth.ts +51 -0
  423. package/dist/vitest.config.ts +37 -0
  424. package/dist/zed-integration/acp.ts +366 -0
  425. package/dist/zed-integration/fileSystemService.ts +47 -0
  426. package/dist/zed-integration/schema.ts +466 -0
  427. package/dist/zed-integration/zedIntegration.ts +944 -0
  428. package/package.json +2 -2
@@ -0,0 +1,357 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import React from 'react';
8
+ import { render } from 'ink';
9
+ import { AppWrapper } from './ui/App.js';
10
+ import { loadCliConfig, parseArguments } from './config/config.js';
11
+ import { readStdin } from './utils/readStdin.js';
12
+ import { basename } from 'node:path';
13
+ import v8 from 'node:v8';
14
+ import os from 'node:os';
15
+ import dns from 'node:dns';
16
+ import { spawn } from 'node:child_process';
17
+ import { start_sandbox } from './utils/sandbox.js';
18
+ import {
19
+ DnsResolutionOrder,
20
+ LoadedSettings,
21
+ loadSettings,
22
+ SettingScope,
23
+ } from './config/settings.js';
24
+ import { themeManager } from './ui/themes/theme-manager.js';
25
+ import { getStartupWarnings } from './utils/startupWarnings.js';
26
+ import { getUserStartupWarnings } from './utils/userStartupWarnings.js';
27
+ import { ConsolePatcher } from './ui/utils/ConsolePatcher.js';
28
+ import { runNonInteractive } from './nonInteractiveCli.js';
29
+ import { loadExtensions } from './config/extension.js';
30
+ import { cleanupCheckpoints, registerCleanup } from './utils/cleanup.js';
31
+ import { getCliVersion } from './utils/version.js';
32
+ import {
33
+ Config,
34
+ sessionId,
35
+ logUserPrompt,
36
+ AuthType,
37
+ getOauthClient,
38
+ logIdeConnection,
39
+ IdeConnectionEvent,
40
+ IdeConnectionType,
41
+ } from 'fss-link-core';
42
+ import { validateAuthMethod } from './config/auth.js';
43
+ import { setMaxSizedBoxDebugging } from './ui/components/shared/MaxSizedBox.js';
44
+ import { validateNonInteractiveAuth } from './validateNonInterActiveAuth.js';
45
+ import { detectAndEnableKittyProtocol } from './ui/utils/kittyProtocolDetector.js';
46
+ import { checkForUpdates } from './ui/utils/updateCheck.js';
47
+ import { handleAutoUpdate } from './utils/handleAutoUpdate.js';
48
+ import { appEvents, AppEvent } from './utils/events.js';
49
+ import { SettingsContext } from './ui/contexts/SettingsContext.js';
50
+ import { getModelManager } from './config/modelManager.js';
51
+
52
+ export function validateDnsResolutionOrder(
53
+ order: string | undefined,
54
+ ): DnsResolutionOrder {
55
+ const defaultValue: DnsResolutionOrder = 'ipv4first';
56
+ if (order === undefined) {
57
+ return defaultValue;
58
+ }
59
+ if (order === 'ipv4first' || order === 'verbatim') {
60
+ return order;
61
+ }
62
+ // We don't want to throw here, just warn and use the default.
63
+ console.warn(
64
+ `Invalid value for dnsResolutionOrder in settings: "${order}". Using default "${defaultValue}".`,
65
+ );
66
+ return defaultValue;
67
+ }
68
+
69
+ function getNodeMemoryArgs(config: Config): string[] {
70
+ const totalMemoryMB = os.totalmem() / (1024 * 1024);
71
+ const heapStats = v8.getHeapStatistics();
72
+ const currentMaxOldSpaceSizeMb = Math.floor(
73
+ heapStats.heap_size_limit / 1024 / 1024,
74
+ );
75
+
76
+ // Set target to 50% of total memory
77
+ const targetMaxOldSpaceSizeInMB = Math.floor(totalMemoryMB * 0.5);
78
+ if (config.getDebugMode()) {
79
+ console.debug(
80
+ `Current heap size ${currentMaxOldSpaceSizeMb.toFixed(2)} MB`,
81
+ );
82
+ }
83
+
84
+ if (process.env['GEMINI_CLI_NO_RELAUNCH']) {
85
+ return [];
86
+ }
87
+
88
+ if (targetMaxOldSpaceSizeInMB > currentMaxOldSpaceSizeMb) {
89
+ if (config.getDebugMode()) {
90
+ console.debug(
91
+ `Need to relaunch with more memory: ${targetMaxOldSpaceSizeInMB.toFixed(2)} MB`,
92
+ );
93
+ }
94
+ return [`--max-old-space-size=${targetMaxOldSpaceSizeInMB}`];
95
+ }
96
+
97
+ return [];
98
+ }
99
+
100
+ async function relaunchWithAdditionalArgs(additionalArgs: string[]) {
101
+ const nodeArgs = [...additionalArgs, ...process.argv.slice(1)];
102
+ const newEnv = { ...process.env, GEMINI_CLI_NO_RELAUNCH: 'true' };
103
+
104
+ const child = spawn(process.execPath, nodeArgs, {
105
+ stdio: 'inherit',
106
+ env: newEnv,
107
+ });
108
+
109
+ await new Promise((resolve) => child.on('close', resolve));
110
+ process.exit(0);
111
+ }
112
+ import { runZedIntegration } from './zed-integration/zedIntegration.js';
113
+
114
+ export function setupUnhandledRejectionHandler() {
115
+ let unhandledRejectionOccurred = false;
116
+ process.on('unhandledRejection', (reason, _promise) => {
117
+ const errorMessage = `=========================================
118
+ This is an unexpected error. Please file a bug report using the /bug tool.
119
+ CRITICAL: Unhandled Promise Rejection!
120
+ =========================================
121
+ Reason: ${reason}${
122
+ reason instanceof Error && reason.stack
123
+ ? `
124
+ Stack trace:
125
+ ${reason.stack}`
126
+ : ''
127
+ }`;
128
+ appEvents.emit(AppEvent.LogError, errorMessage);
129
+ if (!unhandledRejectionOccurred) {
130
+ unhandledRejectionOccurred = true;
131
+ appEvents.emit(AppEvent.OpenDebugConsole);
132
+ }
133
+ });
134
+ }
135
+
136
+ export async function main() {
137
+ setupUnhandledRejectionHandler();
138
+ const workspaceRoot = process.cwd();
139
+ const settings = loadSettings(workspaceRoot);
140
+
141
+ await cleanupCheckpoints();
142
+ if (settings.errors.length > 0) {
143
+ for (const error of settings.errors) {
144
+ let errorMessage = `Error in ${error.path}: ${error.message}`;
145
+ if (!process.env['NO_COLOR']) {
146
+ errorMessage = `\x1b[31m${errorMessage}\x1b[0m`;
147
+ }
148
+ console.error(errorMessage);
149
+ console.error(`Please fix ${error.path} and try again.`);
150
+ }
151
+ process.exit(1);
152
+ }
153
+
154
+ const argv = await parseArguments();
155
+ const extensions = loadExtensions(workspaceRoot);
156
+ const config = await loadCliConfig(
157
+ settings.merged,
158
+ extensions,
159
+ sessionId,
160
+ argv,
161
+ );
162
+
163
+ const consolePatcher = new ConsolePatcher({
164
+ stderr: true,
165
+ debugMode: config.getDebugMode(),
166
+ });
167
+ consolePatcher.patch();
168
+ registerCleanup(consolePatcher.cleanup);
169
+
170
+ dns.setDefaultResultOrder(
171
+ validateDnsResolutionOrder(settings.merged.dnsResolutionOrder),
172
+ );
173
+
174
+ if (argv.promptInteractive && !process.stdin.isTTY) {
175
+ console.error(
176
+ 'Error: The --prompt-interactive flag is not supported when piping input from stdin.',
177
+ );
178
+ process.exit(1);
179
+ }
180
+
181
+ if (config.getListExtensions()) {
182
+ console.log('Installed extensions:');
183
+ for (const extension of extensions) {
184
+ console.log(`- ${extension.config.name}`);
185
+ }
186
+ process.exit(0);
187
+ }
188
+
189
+ // Set a default auth type if one isn't set.
190
+ if (!settings.merged.selectedAuthType) {
191
+ if (process.env['CLOUD_SHELL'] === 'true') {
192
+ settings.setValue(
193
+ SettingScope.User,
194
+ 'selectedAuthType',
195
+ AuthType.CLOUD_SHELL,
196
+ );
197
+ }
198
+ }
199
+
200
+ setMaxSizedBoxDebugging(config.getDebugMode());
201
+
202
+ await config.initialize();
203
+
204
+ // Connect Config with ModelManager for persistent model state
205
+ const modelManager = getModelManager();
206
+ config.setModelStateProvider(modelManager);
207
+
208
+ if (config.getIdeMode()) {
209
+ await config.getIdeClient().connect();
210
+ logIdeConnection(config, new IdeConnectionEvent(IdeConnectionType.START));
211
+ }
212
+
213
+ // Load custom themes from settings
214
+ themeManager.loadCustomThemes(settings.merged.customThemes);
215
+
216
+ if (settings.merged.theme) {
217
+ if (!themeManager.setActiveTheme(settings.merged.theme)) {
218
+ // If the theme is not found during initial load, log a warning and continue.
219
+ // The useThemeCommand hook in App.tsx will handle opening the dialog.
220
+ console.warn(`Warning: Theme "${settings.merged.theme}" not found.`);
221
+ }
222
+ }
223
+
224
+ // hop into sandbox if we are outside and sandboxing is enabled
225
+ if (!process.env['SANDBOX']) {
226
+ const memoryArgs = settings.merged.autoConfigureMaxOldSpaceSize
227
+ ? getNodeMemoryArgs(config)
228
+ : [];
229
+ const sandboxConfig = config.getSandbox();
230
+ if (sandboxConfig) {
231
+ if (
232
+ settings.merged.selectedAuthType &&
233
+ !settings.merged.useExternalAuth
234
+ ) {
235
+ // Validate authentication here because the sandbox will interfere with the Oauth2 web redirect.
236
+ try {
237
+ const err = validateAuthMethod(settings.merged.selectedAuthType);
238
+ if (err) {
239
+ throw new Error(err);
240
+ }
241
+ await config.refreshAuth(settings.merged.selectedAuthType);
242
+ } catch (err) {
243
+ console.error('Error authenticating:', err);
244
+ process.exit(1);
245
+ }
246
+ }
247
+ await start_sandbox(sandboxConfig, memoryArgs, config);
248
+ process.exit(0);
249
+ } else {
250
+ // Not in a sandbox and not entering one, so relaunch with additional
251
+ // arguments to control memory usage if needed.
252
+ if (memoryArgs.length > 0) {
253
+ await relaunchWithAdditionalArgs(memoryArgs);
254
+ process.exit(0);
255
+ }
256
+ }
257
+ }
258
+
259
+ if (
260
+ settings.merged.selectedAuthType === AuthType.LOGIN_WITH_GOOGLE &&
261
+ config.isBrowserLaunchSuppressed()
262
+ ) {
263
+ // Do oauth before app renders to make copying the link possible.
264
+ await getOauthClient(settings.merged.selectedAuthType, config);
265
+ }
266
+
267
+ if (config.getExperimentalZedIntegration()) {
268
+ return runZedIntegration(config, settings, extensions, argv);
269
+ }
270
+
271
+ let input = config.getQuestion();
272
+ const startupWarnings = [
273
+ ...(await getStartupWarnings()),
274
+ ...(await getUserStartupWarnings(workspaceRoot)),
275
+ ];
276
+
277
+ // Render UI, passing necessary config values. Check that there is no command line question.
278
+ if (config.isInteractive()) {
279
+ const version = await getCliVersion();
280
+ // Detect and enable Kitty keyboard protocol once at startup
281
+ await detectAndEnableKittyProtocol();
282
+ setWindowTitle(basename(workspaceRoot), settings);
283
+ const instance = render(
284
+ <React.StrictMode>
285
+ <SettingsContext.Provider value={settings}>
286
+ <AppWrapper
287
+ config={config}
288
+ settings={settings}
289
+ startupWarnings={startupWarnings}
290
+ version={version}
291
+ />
292
+ </SettingsContext.Provider>
293
+ </React.StrictMode>,
294
+ { exitOnCtrlC: false },
295
+ );
296
+
297
+ checkForUpdates()
298
+ .then((info) => {
299
+ handleAutoUpdate(info, settings, config.getProjectRoot());
300
+ })
301
+ .catch((err) => {
302
+ // Silently ignore update check errors.
303
+ if (config.getDebugMode()) {
304
+ console.error('Update check failed:', err);
305
+ }
306
+ });
307
+
308
+ registerCleanup(() => instance.unmount());
309
+ return;
310
+ }
311
+ // If not a TTY, read from stdin
312
+ // This is for cases where the user pipes input directly into the command
313
+ if (!process.stdin.isTTY) {
314
+ const stdinData = await readStdin();
315
+ if (stdinData) {
316
+ input = `${stdinData}\n\n${input}`;
317
+ }
318
+ }
319
+ if (!input) {
320
+ console.error('No input provided via stdin.');
321
+ process.exit(1);
322
+ }
323
+
324
+ const prompt_id = Math.random().toString(16).slice(2);
325
+ logUserPrompt(config, {
326
+ 'event.name': 'user_prompt',
327
+ 'event.timestamp': new Date().toISOString(),
328
+ prompt: input,
329
+ prompt_id,
330
+ auth_type: config.getContentGeneratorConfig()?.authType,
331
+ prompt_length: input.length,
332
+ });
333
+
334
+ const nonInteractiveConfig = await validateNonInteractiveAuth(
335
+ settings.merged.selectedAuthType,
336
+ settings.merged.useExternalAuth,
337
+ config,
338
+ );
339
+
340
+ await runNonInteractive(nonInteractiveConfig, input, prompt_id);
341
+ process.exit(0);
342
+ }
343
+
344
+ function setWindowTitle(title: string, settings: LoadedSettings) {
345
+ if (!settings.merged.hideWindowTitle) {
346
+ const windowTitle = (process.env['CLI_TITLE'] || `Qwen - ${title}`).replace(
347
+ // eslint-disable-next-line no-control-regex
348
+ /[\x00-\x1F\x7F]/g,
349
+ '',
350
+ );
351
+ process.stdout.write(`\x1b]2;${windowTitle}\x07`);
352
+
353
+ process.on('exit', () => {
354
+ process.stdout.write(`\x1b]2;\x07`);
355
+ });
356
+ }
357
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ // This file is auto-generated by the build script (scripts/generate-git-commit-info.js)
8
+ // Do not edit this file manually.
9
+ export const GIT_COMMIT_INFO = '696dd3d0';
10
+ export const CLI_VERSION = '1.0.31';
package/dist/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @license
5
+ * Copyright 2025 Google LLC
6
+ * SPDX-License-Identifier: Apache-2.0
7
+ */
8
+
9
+ import './src/gemini.js';
10
+ import { main } from './src/gemini.js';
11
+
12
+ // --- Global Entry Point ---
13
+ main().catch((error) => {
14
+ console.error('An unexpected critical error occurred:');
15
+ if (error instanceof Error) {
16
+ console.error(error.stack);
17
+ } else {
18
+ console.error(String(error));
19
+ }
20
+ process.exit(1);
21
+ });
@@ -0,0 +1,276 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import {
8
+ Config,
9
+ executeToolCall,
10
+ ToolRegistry,
11
+ ToolErrorType,
12
+ shutdownTelemetry,
13
+ GeminiEventType,
14
+ ServerGeminiStreamEvent,
15
+ } from 'fss-link-core';
16
+ import { Part } from '@google/genai';
17
+ import { runNonInteractive } from './nonInteractiveCli.js';
18
+ import { vi } from 'vitest';
19
+
20
+ // Mock core modules
21
+ vi.mock('fss-link-core', async (importOriginal) => {
22
+ const original =
23
+ await importOriginal<typeof import('fss-link-core')>();
24
+ return {
25
+ ...original,
26
+ executeToolCall: vi.fn(),
27
+ shutdownTelemetry: vi.fn(),
28
+ isTelemetrySdkInitialized: vi.fn().mockReturnValue(true),
29
+ };
30
+ });
31
+
32
+ describe('runNonInteractive', () => {
33
+ let mockConfig: Config;
34
+ let mockToolRegistry: ToolRegistry;
35
+ let mockCoreExecuteToolCall: vi.Mock;
36
+ let mockShutdownTelemetry: vi.Mock;
37
+ let consoleErrorSpy: vi.SpyInstance;
38
+ let processExitSpy: vi.SpyInstance;
39
+ let processStdoutSpy: vi.SpyInstance;
40
+ let mockGeminiClient: {
41
+ sendMessageStream: vi.Mock;
42
+ };
43
+
44
+ beforeEach(() => {
45
+ mockCoreExecuteToolCall = vi.mocked(executeToolCall);
46
+ mockShutdownTelemetry = vi.mocked(shutdownTelemetry);
47
+
48
+ consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
49
+ processExitSpy = vi
50
+ .spyOn(process, 'exit')
51
+ .mockImplementation((() => {}) as (code?: number) => never);
52
+ processStdoutSpy = vi
53
+ .spyOn(process.stdout, 'write')
54
+ .mockImplementation(() => true);
55
+
56
+ mockToolRegistry = {
57
+ getTool: vi.fn(),
58
+ getFunctionDeclarations: vi.fn().mockReturnValue([]),
59
+ } as unknown as ToolRegistry;
60
+
61
+ mockGeminiClient = {
62
+ sendMessageStream: vi.fn(),
63
+ };
64
+
65
+ mockConfig = {
66
+ initialize: vi.fn().mockResolvedValue(undefined),
67
+ getGeminiClient: vi.fn().mockReturnValue(mockGeminiClient),
68
+ getToolRegistry: vi.fn().mockReturnValue(mockToolRegistry),
69
+ getMaxSessionTurns: vi.fn().mockReturnValue(10),
70
+ getIdeMode: vi.fn().mockReturnValue(false),
71
+ getFullContext: vi.fn().mockReturnValue(false),
72
+ getContentGeneratorConfig: vi.fn().mockReturnValue({}),
73
+ getDebugMode: vi.fn().mockReturnValue(false),
74
+ } as unknown as Config;
75
+ });
76
+
77
+ afterEach(() => {
78
+ vi.restoreAllMocks();
79
+ });
80
+
81
+ async function* createStreamFromEvents(
82
+ events: ServerGeminiStreamEvent[],
83
+ ): AsyncGenerator<ServerGeminiStreamEvent> {
84
+ for (const event of events) {
85
+ yield event;
86
+ }
87
+ }
88
+
89
+ it('should process input and write text output', async () => {
90
+ const events: ServerGeminiStreamEvent[] = [
91
+ { type: GeminiEventType.Content, value: 'Hello' },
92
+ { type: GeminiEventType.Content, value: ' World' },
93
+ ];
94
+ mockGeminiClient.sendMessageStream.mockReturnValue(
95
+ createStreamFromEvents(events),
96
+ );
97
+
98
+ await runNonInteractive(mockConfig, 'Test input', 'prompt-id-1');
99
+
100
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledWith(
101
+ [{ text: 'Test input' }],
102
+ expect.any(AbortSignal),
103
+ 'prompt-id-1',
104
+ );
105
+ expect(processStdoutSpy).toHaveBeenCalledWith('Hello');
106
+ expect(processStdoutSpy).toHaveBeenCalledWith(' World');
107
+ expect(processStdoutSpy).toHaveBeenCalledWith('\n');
108
+ expect(mockShutdownTelemetry).toHaveBeenCalled();
109
+ });
110
+
111
+ it('should handle a single tool call and respond', async () => {
112
+ const toolCallEvent: ServerGeminiStreamEvent = {
113
+ type: GeminiEventType.ToolCallRequest,
114
+ value: {
115
+ callId: 'tool-1',
116
+ name: 'testTool',
117
+ args: { arg1: 'value1' },
118
+ isClientInitiated: false,
119
+ prompt_id: 'prompt-id-2',
120
+ },
121
+ };
122
+ const toolResponse: Part[] = [{ text: 'Tool response' }];
123
+ mockCoreExecuteToolCall.mockResolvedValue({ responseParts: toolResponse });
124
+
125
+ const firstCallEvents: ServerGeminiStreamEvent[] = [toolCallEvent];
126
+ const secondCallEvents: ServerGeminiStreamEvent[] = [
127
+ { type: GeminiEventType.Content, value: 'Final answer' },
128
+ ];
129
+
130
+ mockGeminiClient.sendMessageStream
131
+ .mockReturnValueOnce(createStreamFromEvents(firstCallEvents))
132
+ .mockReturnValueOnce(createStreamFromEvents(secondCallEvents));
133
+
134
+ await runNonInteractive(mockConfig, 'Use a tool', 'prompt-id-2');
135
+
136
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
137
+ expect(mockCoreExecuteToolCall).toHaveBeenCalledWith(
138
+ mockConfig,
139
+ expect.objectContaining({ name: 'testTool' }),
140
+ expect.any(AbortSignal),
141
+ );
142
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenNthCalledWith(
143
+ 2,
144
+ [{ text: 'Tool response' }],
145
+ expect.any(AbortSignal),
146
+ 'prompt-id-2',
147
+ );
148
+ expect(processStdoutSpy).toHaveBeenCalledWith('Final answer');
149
+ expect(processStdoutSpy).toHaveBeenCalledWith('\n');
150
+ });
151
+
152
+ it('should handle error during tool execution and should send error back to the model', async () => {
153
+ const toolCallEvent: ServerGeminiStreamEvent = {
154
+ type: GeminiEventType.ToolCallRequest,
155
+ value: {
156
+ callId: 'tool-1',
157
+ name: 'errorTool',
158
+ args: {},
159
+ isClientInitiated: false,
160
+ prompt_id: 'prompt-id-3',
161
+ },
162
+ };
163
+ mockCoreExecuteToolCall.mockResolvedValue({
164
+ error: new Error('Execution failed'),
165
+ errorType: ToolErrorType.EXECUTION_FAILED,
166
+ responseParts: {
167
+ functionResponse: {
168
+ name: 'errorTool',
169
+ response: {
170
+ output: 'Error: Execution failed',
171
+ },
172
+ },
173
+ },
174
+ resultDisplay: 'Execution failed',
175
+ });
176
+ const finalResponse: ServerGeminiStreamEvent[] = [
177
+ {
178
+ type: GeminiEventType.Content,
179
+ value: 'Sorry, let me try again.',
180
+ },
181
+ ];
182
+ mockGeminiClient.sendMessageStream
183
+ .mockReturnValueOnce(createStreamFromEvents([toolCallEvent]))
184
+ .mockReturnValueOnce(createStreamFromEvents(finalResponse));
185
+
186
+ await runNonInteractive(mockConfig, 'Trigger tool error', 'prompt-id-3');
187
+
188
+ expect(mockCoreExecuteToolCall).toHaveBeenCalled();
189
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
190
+ 'Error executing tool errorTool: Execution failed',
191
+ );
192
+ expect(processExitSpy).not.toHaveBeenCalled();
193
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
194
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenNthCalledWith(
195
+ 2,
196
+ [
197
+ {
198
+ functionResponse: {
199
+ name: 'errorTool',
200
+ response: {
201
+ output: 'Error: Execution failed',
202
+ },
203
+ },
204
+ },
205
+ ],
206
+ expect.any(AbortSignal),
207
+ 'prompt-id-3',
208
+ );
209
+ expect(processStdoutSpy).toHaveBeenCalledWith('Sorry, let me try again.');
210
+ });
211
+
212
+ it('should exit with error if sendMessageStream throws initially', async () => {
213
+ const apiError = new Error('API connection failed');
214
+ mockGeminiClient.sendMessageStream.mockImplementation(() => {
215
+ throw apiError;
216
+ });
217
+
218
+ await runNonInteractive(mockConfig, 'Initial fail', 'prompt-id-4');
219
+
220
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
221
+ '[API Error: API connection failed]',
222
+ );
223
+ expect(processExitSpy).toHaveBeenCalledWith(1);
224
+ });
225
+
226
+ it('should not exit if a tool is not found, and should send error back to model', async () => {
227
+ const toolCallEvent: ServerGeminiStreamEvent = {
228
+ type: GeminiEventType.ToolCallRequest,
229
+ value: {
230
+ callId: 'tool-1',
231
+ name: 'nonexistentTool',
232
+ args: {},
233
+ isClientInitiated: false,
234
+ prompt_id: 'prompt-id-5',
235
+ },
236
+ };
237
+ mockCoreExecuteToolCall.mockResolvedValue({
238
+ error: new Error('Tool "nonexistentTool" not found in registry.'),
239
+ resultDisplay: 'Tool "nonexistentTool" not found in registry.',
240
+ });
241
+ const finalResponse: ServerGeminiStreamEvent[] = [
242
+ {
243
+ type: GeminiEventType.Content,
244
+ value: "Sorry, I can't find that tool.",
245
+ },
246
+ ];
247
+
248
+ mockGeminiClient.sendMessageStream
249
+ .mockReturnValueOnce(createStreamFromEvents([toolCallEvent]))
250
+ .mockReturnValueOnce(createStreamFromEvents(finalResponse));
251
+
252
+ await runNonInteractive(
253
+ mockConfig,
254
+ 'Trigger tool not found',
255
+ 'prompt-id-5',
256
+ );
257
+
258
+ expect(mockCoreExecuteToolCall).toHaveBeenCalled();
259
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
260
+ 'Error executing tool nonexistentTool: Tool "nonexistentTool" not found in registry.',
261
+ );
262
+ expect(processExitSpy).not.toHaveBeenCalled();
263
+ expect(mockGeminiClient.sendMessageStream).toHaveBeenCalledTimes(2);
264
+ expect(processStdoutSpy).toHaveBeenCalledWith(
265
+ "Sorry, I can't find that tool.",
266
+ );
267
+ });
268
+
269
+ it('should exit when max session turns are exceeded', async () => {
270
+ vi.mocked(mockConfig.getMaxSessionTurns).mockReturnValue(0);
271
+ await runNonInteractive(mockConfig, 'Trigger loop', 'prompt-id-6');
272
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
273
+ '\n Reached max session turns for this session. Increase the number of turns by specifying maxSessionTurns in settings.json.',
274
+ );
275
+ });
276
+ });