@google/gemini-cli 0.0.8999999 → 0.0.77777773

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 (465) hide show
  1. package/README.md +105 -62
  2. package/dist/package.json +13 -5
  3. package/dist/src/commands/extensions/examples/context/GEMINI.md +9 -3
  4. package/dist/src/commands/extensions/examples/context/gemini-extension.json +1 -2
  5. package/dist/src/commands/extensions/examples/mcp-server/gemini-extension.json +3 -2
  6. package/dist/src/commands/extensions/examples/mcp-server/package.json +18 -0
  7. package/dist/src/commands/extensions/examples/mcp-server/tsconfig.json +13 -0
  8. package/dist/src/commands/extensions/install.d.ts +2 -2
  9. package/dist/src/commands/extensions/install.js +35 -36
  10. package/dist/src/commands/extensions/install.js.map +1 -1
  11. package/dist/src/commands/extensions/install.test.js +25 -25
  12. package/dist/src/commands/extensions/install.test.js.map +1 -1
  13. package/dist/src/commands/extensions/link.js +2 -2
  14. package/dist/src/commands/extensions/link.js.map +1 -1
  15. package/dist/src/commands/extensions/list.js +1 -1
  16. package/dist/src/commands/extensions/list.js.map +1 -1
  17. package/dist/src/commands/extensions/new.js +28 -8
  18. package/dist/src/commands/extensions/new.js.map +1 -1
  19. package/dist/src/commands/extensions/new.test.js +14 -5
  20. package/dist/src/commands/extensions/new.test.js.map +1 -1
  21. package/dist/src/commands/extensions/uninstall.js +1 -1
  22. package/dist/src/commands/extensions/uninstall.js.map +1 -1
  23. package/dist/src/commands/extensions/uninstall.test.js +4 -1
  24. package/dist/src/commands/extensions/uninstall.test.js.map +1 -1
  25. package/dist/src/commands/extensions/update.js +35 -24
  26. package/dist/src/commands/extensions/update.js.map +1 -1
  27. package/dist/src/commands/mcp/add.js +6 -1
  28. package/dist/src/commands/mcp/add.js.map +1 -1
  29. package/dist/src/commands/mcp/list.js +5 -4
  30. package/dist/src/commands/mcp/list.js.map +1 -1
  31. package/dist/src/config/config.d.ts +7 -4
  32. package/dist/src/config/config.js +125 -39
  33. package/dist/src/config/config.js.map +1 -1
  34. package/dist/src/config/extension.d.ts +44 -18
  35. package/dist/src/config/extension.js +250 -143
  36. package/dist/src/config/extension.js.map +1 -1
  37. package/dist/src/config/extensions/extensionEnablement.d.ts +12 -9
  38. package/dist/src/config/extensions/extensionEnablement.js +36 -9
  39. package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
  40. package/dist/src/config/extensions/extensionEnablement.test.js +74 -1
  41. package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
  42. package/dist/src/config/extensions/github.d.ts +14 -2
  43. package/dist/src/config/extensions/github.js +111 -89
  44. package/dist/src/config/extensions/github.js.map +1 -1
  45. package/dist/src/config/extensions/github.test.js +124 -13
  46. package/dist/src/config/extensions/github.test.js.map +1 -1
  47. package/dist/src/config/extensions/github_fetch.d.ts +7 -0
  48. package/dist/src/config/extensions/github_fetch.js +34 -0
  49. package/dist/src/config/extensions/github_fetch.js.map +1 -0
  50. package/dist/src/config/extensions/update.d.ts +4 -5
  51. package/dist/src/config/extensions/update.js +64 -42
  52. package/dist/src/config/extensions/update.js.map +1 -1
  53. package/dist/src/config/extensions/update.test.js +121 -71
  54. package/dist/src/config/extensions/update.test.js.map +1 -1
  55. package/dist/src/config/keyBindings.js +1 -1
  56. package/dist/src/config/keyBindings.js.map +1 -1
  57. package/dist/src/config/policy.js +2 -2
  58. package/dist/src/config/policy.js.map +1 -1
  59. package/dist/src/config/settings.d.ts +10 -1
  60. package/dist/src/config/settings.js +21 -6
  61. package/dist/src/config/settings.js.map +1 -1
  62. package/dist/src/config/settingsSchema.d.ts +97 -5
  63. package/dist/src/config/settingsSchema.js +96 -4
  64. package/dist/src/config/settingsSchema.js.map +1 -1
  65. package/dist/src/config/settingsSchema.test.js +8 -0
  66. package/dist/src/config/settingsSchema.test.js.map +1 -1
  67. package/dist/src/config/trustedFolders.d.ts +10 -2
  68. package/dist/src/config/trustedFolders.js +41 -16
  69. package/dist/src/config/trustedFolders.js.map +1 -1
  70. package/dist/src/config/trustedFolders.test.js +95 -14
  71. package/dist/src/config/trustedFolders.test.js.map +1 -1
  72. package/dist/src/gemini.js +114 -132
  73. package/dist/src/gemini.js.map +1 -1
  74. package/dist/src/gemini.test.js +95 -14
  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/services/BuiltinCommandLoader.js +7 -0
  80. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  81. package/dist/src/services/BuiltinCommandLoader.test.js +87 -1
  82. package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
  83. package/dist/src/services/FileCommandLoader.d.ts +1 -1
  84. package/dist/src/services/FileCommandLoader.js +4 -4
  85. package/dist/src/services/FileCommandLoader.js.map +1 -1
  86. package/dist/src/services/prompt-processors/shellProcessor.js +1 -1
  87. package/dist/src/services/prompt-processors/shellProcessor.js.map +1 -1
  88. package/dist/src/test-utils/render.d.ts +12 -1
  89. package/dist/src/test-utils/render.js +56 -1
  90. package/dist/src/test-utils/render.js.map +1 -1
  91. package/dist/src/ui/App.js +7 -9
  92. package/dist/src/ui/App.js.map +1 -1
  93. package/dist/src/ui/AppContainer.js +99 -27
  94. package/dist/src/ui/AppContainer.js.map +1 -1
  95. package/dist/src/ui/AppContainer.test.js +300 -7
  96. package/dist/src/ui/AppContainer.test.js.map +1 -1
  97. package/dist/src/ui/IdeIntegrationNudge.js +3 -0
  98. package/dist/src/ui/IdeIntegrationNudge.js.map +1 -1
  99. package/dist/src/ui/auth/AuthDialog.js +8 -1
  100. package/dist/src/ui/auth/AuthDialog.js.map +1 -1
  101. package/dist/src/ui/auth/AuthDialog.test.js +1 -0
  102. package/dist/src/ui/auth/AuthDialog.test.js.map +1 -1
  103. package/dist/src/ui/auth/AuthInProgress.js +2 -2
  104. package/dist/src/ui/auth/AuthInProgress.js.map +1 -1
  105. package/dist/src/ui/commands/chatCommand.js +21 -27
  106. package/dist/src/ui/commands/chatCommand.js.map +1 -1
  107. package/dist/src/ui/commands/extensionsCommand.js +39 -34
  108. package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
  109. package/dist/src/ui/commands/mcpCommand.js +78 -260
  110. package/dist/src/ui/commands/mcpCommand.js.map +1 -1
  111. package/dist/src/ui/commands/memoryCommand.js +23 -5
  112. package/dist/src/ui/commands/memoryCommand.js.map +1 -1
  113. package/dist/src/ui/commands/modelCommand.d.ts +7 -0
  114. package/dist/src/ui/commands/modelCommand.js +16 -0
  115. package/dist/src/ui/commands/modelCommand.js.map +1 -0
  116. package/dist/src/ui/commands/modelCommand.test.js +30 -0
  117. package/dist/src/ui/commands/modelCommand.test.js.map +1 -0
  118. package/dist/src/ui/commands/permissionsCommand.d.ts +7 -0
  119. package/dist/src/ui/commands/permissionsCommand.js +16 -0
  120. package/dist/src/ui/commands/permissionsCommand.js.map +1 -0
  121. package/dist/src/ui/commands/permissionsCommand.test.d.ts +6 -0
  122. package/dist/src/ui/commands/permissionsCommand.test.js +30 -0
  123. package/dist/src/ui/commands/permissionsCommand.test.js.map +1 -0
  124. package/dist/src/ui/commands/profileCommand.d.ts +7 -0
  125. package/dist/src/ui/commands/profileCommand.js +23 -0
  126. package/dist/src/ui/commands/profileCommand.js.map +1 -0
  127. package/dist/src/ui/commands/setupGithubCommand.test.js +2 -1
  128. package/dist/src/ui/commands/setupGithubCommand.test.js.map +1 -1
  129. package/dist/src/ui/commands/toolsCommand.js +10 -24
  130. package/dist/src/ui/commands/toolsCommand.js.map +1 -1
  131. package/dist/src/ui/commands/types.d.ts +8 -6
  132. package/dist/src/ui/commands/types.js.map +1 -1
  133. package/dist/src/ui/components/AnsiOutput.d.ts +1 -0
  134. package/dist/src/ui/components/AnsiOutput.js +5 -5
  135. package/dist/src/ui/components/AnsiOutput.js.map +1 -1
  136. package/dist/src/ui/components/AnsiOutput.test.js +6 -6
  137. package/dist/src/ui/components/AnsiOutput.test.js.map +1 -1
  138. package/dist/src/ui/components/AppHeader.js +2 -5
  139. package/dist/src/ui/components/AppHeader.js.map +1 -1
  140. package/dist/src/ui/components/CliSpinner.d.ts +10 -0
  141. package/dist/src/ui/components/CliSpinner.js +20 -0
  142. package/dist/src/ui/components/CliSpinner.js.map +1 -0
  143. package/dist/src/ui/components/Composer.js +7 -29
  144. package/dist/src/ui/components/Composer.js.map +1 -1
  145. package/dist/src/ui/components/ConsentPrompt.d.ts +13 -0
  146. package/dist/src/ui/components/ConsentPrompt.js +19 -0
  147. package/dist/src/ui/components/ConsentPrompt.js.map +1 -0
  148. package/dist/src/ui/components/ConsentPrompt.test.d.ts +6 -0
  149. package/dist/src/ui/components/ConsentPrompt.test.js +67 -0
  150. package/dist/src/ui/components/ConsentPrompt.test.js.map +1 -0
  151. package/dist/src/ui/components/ContextSummaryDisplay.js +2 -2
  152. package/dist/src/ui/components/ContextSummaryDisplay.js.map +1 -1
  153. package/dist/src/ui/components/ContextUsageDisplay.d.ts +2 -1
  154. package/dist/src/ui/components/ContextUsageDisplay.js +4 -2
  155. package/dist/src/ui/components/ContextUsageDisplay.js.map +1 -1
  156. package/dist/src/ui/components/DebugProfiler.d.ts +18 -0
  157. package/dist/src/ui/components/DebugProfiler.js +158 -12
  158. package/dist/src/ui/components/DebugProfiler.js.map +1 -1
  159. package/dist/src/ui/components/DebugProfiler.test.d.ts +6 -0
  160. package/dist/src/ui/components/DebugProfiler.test.js +140 -0
  161. package/dist/src/ui/components/DebugProfiler.test.js.map +1 -0
  162. package/dist/src/ui/components/DialogManager.d.ts +7 -1
  163. package/dist/src/ui/components/DialogManager.js +18 -9
  164. package/dist/src/ui/components/DialogManager.js.map +1 -1
  165. package/dist/src/ui/components/EditorSettingsDialog.js +11 -2
  166. package/dist/src/ui/components/EditorSettingsDialog.js.map +1 -1
  167. package/dist/src/ui/components/ExitWarning.d.ts +7 -0
  168. package/dist/src/ui/components/ExitWarning.js +9 -0
  169. package/dist/src/ui/components/ExitWarning.js.map +1 -0
  170. package/dist/src/ui/components/FolderTrustDialog.js +3 -0
  171. package/dist/src/ui/components/FolderTrustDialog.js.map +1 -1
  172. package/dist/src/ui/components/FolderTrustDialog.test.js +2 -2
  173. package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
  174. package/dist/src/ui/components/Footer.d.ts +1 -19
  175. package/dist/src/ui/components/Footer.js +35 -17
  176. package/dist/src/ui/components/Footer.js.map +1 -1
  177. package/dist/src/ui/components/GeminiRespondingSpinner.js +2 -2
  178. package/dist/src/ui/components/GeminiRespondingSpinner.js.map +1 -1
  179. package/dist/src/ui/components/Help.js +1 -1
  180. package/dist/src/ui/components/Help.js.map +1 -1
  181. package/dist/src/ui/components/HistoryItemDisplay.d.ts +2 -1
  182. package/dist/src/ui/components/HistoryItemDisplay.js +10 -1
  183. package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
  184. package/dist/src/ui/components/HistoryItemDisplay.test.js +90 -9
  185. package/dist/src/ui/components/HistoryItemDisplay.test.js.map +1 -1
  186. package/dist/src/ui/components/IdeTrustChangeDialog.d.ts +11 -0
  187. package/dist/src/ui/components/IdeTrustChangeDialog.js +32 -0
  188. package/dist/src/ui/components/IdeTrustChangeDialog.js.map +1 -0
  189. package/dist/src/ui/components/IdeTrustChangeDialog.test.d.ts +6 -0
  190. package/dist/src/ui/components/IdeTrustChangeDialog.test.js +57 -0
  191. package/dist/src/ui/components/IdeTrustChangeDialog.test.js.map +1 -0
  192. package/dist/src/ui/components/InputPrompt.d.ts +8 -2
  193. package/dist/src/ui/components/InputPrompt.js +66 -32
  194. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  195. package/dist/src/ui/components/LoadingIndicator.js +1 -1
  196. package/dist/src/ui/components/LoadingIndicator.js.map +1 -1
  197. package/dist/src/ui/components/LoadingIndicator.test.js +4 -0
  198. package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -1
  199. package/dist/src/ui/components/LoopDetectionConfirmation.js +2 -0
  200. package/dist/src/ui/components/LoopDetectionConfirmation.js.map +1 -1
  201. package/dist/src/ui/components/MainContent.js +7 -2
  202. package/dist/src/ui/components/MainContent.js.map +1 -1
  203. package/dist/src/ui/components/ModelDialog.d.ts +11 -0
  204. package/dist/src/ui/components/ModelDialog.js +57 -0
  205. package/dist/src/ui/components/ModelDialog.js.map +1 -0
  206. package/dist/src/ui/components/ModelDialog.test.d.ts +6 -0
  207. package/dist/src/ui/components/ModelDialog.test.js +153 -0
  208. package/dist/src/ui/components/ModelDialog.test.js.map +1 -0
  209. package/dist/src/ui/components/Notifications.js +12 -4
  210. package/dist/src/ui/components/Notifications.js.map +1 -1
  211. package/dist/src/ui/components/PermissionsModifyTrustDialog.d.ts +13 -0
  212. package/dist/src/ui/components/PermissionsModifyTrustDialog.js +48 -0
  213. package/dist/src/ui/components/PermissionsModifyTrustDialog.js.map +1 -0
  214. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.d.ts +6 -0
  215. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +154 -0
  216. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -0
  217. package/dist/src/ui/components/ProQuotaDialog.js +2 -0
  218. package/dist/src/ui/components/ProQuotaDialog.js.map +1 -1
  219. package/dist/src/ui/components/ProQuotaDialog.test.js +2 -0
  220. package/dist/src/ui/components/ProQuotaDialog.test.js.map +1 -1
  221. package/dist/src/ui/components/SettingsDialog.js +6 -3
  222. package/dist/src/ui/components/SettingsDialog.js.map +1 -1
  223. package/dist/src/ui/components/SettingsDialog.test.js +47 -13
  224. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  225. package/dist/src/ui/components/ShellConfirmationDialog.js +3 -0
  226. package/dist/src/ui/components/ShellConfirmationDialog.js.map +1 -1
  227. package/dist/src/ui/components/ThemeDialog.js +2 -0
  228. package/dist/src/ui/components/ThemeDialog.js.map +1 -1
  229. package/dist/src/ui/components/WorkspaceMigrationDialog.d.ts +2 -2
  230. package/dist/src/ui/components/WorkspaceMigrationDialog.js +7 -5
  231. package/dist/src/ui/components/WorkspaceMigrationDialog.js.map +1 -1
  232. package/dist/src/ui/components/messages/CompressionMessage.js +2 -2
  233. package/dist/src/ui/components/messages/CompressionMessage.js.map +1 -1
  234. package/dist/src/ui/components/messages/ToolConfirmationMessage.js +15 -1
  235. package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
  236. package/dist/src/ui/components/messages/ToolGroupMessage.d.ts +1 -1
  237. package/dist/src/ui/components/messages/ToolGroupMessage.js +5 -5
  238. package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
  239. package/dist/src/ui/components/messages/ToolMessage.d.ts +1 -1
  240. package/dist/src/ui/components/messages/ToolMessage.js +28 -5
  241. package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
  242. package/dist/src/ui/components/messages/UserMessage.js +1 -2
  243. package/dist/src/ui/components/messages/UserMessage.js.map +1 -1
  244. package/dist/src/ui/components/shared/BaseSelectionList.d.ts +38 -0
  245. package/dist/src/ui/components/shared/BaseSelectionList.js +72 -0
  246. package/dist/src/ui/components/shared/BaseSelectionList.js.map +1 -0
  247. package/dist/src/ui/components/shared/BaseSelectionList.test.d.ts +6 -0
  248. package/dist/src/ui/components/shared/BaseSelectionList.test.js +376 -0
  249. package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -0
  250. package/dist/src/ui/components/shared/DescriptiveRadioButtonSelect.d.ts +35 -0
  251. package/dist/src/ui/components/shared/DescriptiveRadioButtonSelect.js +13 -0
  252. package/dist/src/ui/components/shared/DescriptiveRadioButtonSelect.js.map +1 -0
  253. package/dist/src/ui/components/shared/DescriptiveRadioButtonSelect.test.d.ts +6 -0
  254. package/dist/src/ui/components/shared/DescriptiveRadioButtonSelect.test.js +79 -0
  255. package/dist/src/ui/components/shared/DescriptiveRadioButtonSelect.test.js.map +1 -0
  256. package/dist/src/ui/components/shared/RadioButtonSelect.d.ts +2 -3
  257. package/dist/src/ui/components/shared/RadioButtonSelect.js +9 -104
  258. package/dist/src/ui/components/shared/RadioButtonSelect.js.map +1 -1
  259. package/dist/src/ui/components/shared/RadioButtonSelect.test.js +115 -92
  260. package/dist/src/ui/components/shared/RadioButtonSelect.test.js.map +1 -1
  261. package/dist/src/ui/components/shared/ScopeSelector.js +4 -1
  262. package/dist/src/ui/components/shared/ScopeSelector.js.map +1 -1
  263. package/dist/src/ui/components/views/ChatList.d.ts +12 -0
  264. package/dist/src/ui/components/views/ChatList.js +17 -0
  265. package/dist/src/ui/components/views/ChatList.js.map +1 -0
  266. package/dist/src/ui/components/views/ChatList.test.d.ts +6 -0
  267. package/dist/src/ui/components/views/ChatList.test.js +42 -0
  268. package/dist/src/ui/components/views/ChatList.test.js.map +1 -0
  269. package/dist/src/ui/components/views/ExtensionsList.test.js +1 -1
  270. package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
  271. package/dist/src/ui/components/views/McpStatus.d.ts +27 -0
  272. package/dist/src/ui/components/views/McpStatus.js +77 -0
  273. package/dist/src/ui/components/views/McpStatus.js.map +1 -0
  274. package/dist/src/ui/components/views/McpStatus.test.d.ts +6 -0
  275. package/dist/src/ui/components/views/McpStatus.test.js +117 -0
  276. package/dist/src/ui/components/views/McpStatus.test.js.map +1 -0
  277. package/dist/src/ui/components/views/ToolsList.d.ts +14 -0
  278. package/dist/src/ui/components/views/ToolsList.js +7 -0
  279. package/dist/src/ui/components/views/ToolsList.js.map +1 -0
  280. package/dist/src/ui/components/views/ToolsList.test.d.ts +6 -0
  281. package/dist/src/ui/components/views/ToolsList.test.js +45 -0
  282. package/dist/src/ui/components/views/ToolsList.test.js.map +1 -0
  283. package/dist/src/ui/contexts/KeypressContext.js +3 -0
  284. package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
  285. package/dist/src/ui/contexts/ShellFocusContext.d.ts +7 -0
  286. package/dist/src/ui/contexts/ShellFocusContext.js +9 -0
  287. package/dist/src/ui/contexts/ShellFocusContext.js.map +1 -0
  288. package/dist/src/ui/contexts/UIActionsContext.d.ts +2 -0
  289. package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
  290. package/dist/src/ui/contexts/UIStateContext.d.ts +11 -2
  291. package/dist/src/ui/contexts/UIStateContext.js +2 -0
  292. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  293. package/dist/src/ui/hooks/shellCommandProcessor.js +5 -6
  294. package/dist/src/ui/hooks/shellCommandProcessor.js.map +1 -1
  295. package/dist/src/ui/hooks/shellCommandProcessor.test.js +19 -32
  296. package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -1
  297. package/dist/src/ui/hooks/slashCommandProcessor.d.ts +8 -5
  298. package/dist/src/ui/hooks/slashCommandProcessor.js +11 -2
  299. package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
  300. package/dist/src/ui/hooks/useExtensionUpdates.d.ts +10 -1
  301. package/dist/src/ui/hooks/useExtensionUpdates.js +140 -38
  302. package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
  303. package/dist/src/ui/hooks/useExtensionUpdates.test.js +163 -84
  304. package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
  305. package/dist/src/ui/hooks/useFlickerDetector.d.ts +14 -0
  306. package/dist/src/ui/hooks/useFlickerDetector.js +37 -0
  307. package/dist/src/ui/hooks/useFlickerDetector.js.map +1 -0
  308. package/dist/src/ui/hooks/useFlickerDetector.test.d.ts +6 -0
  309. package/dist/src/ui/hooks/useFlickerDetector.test.js +102 -0
  310. package/dist/src/ui/hooks/useFlickerDetector.test.js.map +1 -0
  311. package/dist/src/ui/hooks/useFocus.js +10 -0
  312. package/dist/src/ui/hooks/useFocus.js.map +1 -1
  313. package/dist/src/ui/hooks/useFolderTrust.d.ts +2 -1
  314. package/dist/src/ui/hooks/useFolderTrust.js +13 -9
  315. package/dist/src/ui/hooks/useFolderTrust.js.map +1 -1
  316. package/dist/src/ui/hooks/useGeminiStream.js +38 -3
  317. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  318. package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
  319. package/dist/src/ui/hooks/useHistoryManager.js +3 -3
  320. package/dist/src/ui/hooks/useHistoryManager.js.map +1 -1
  321. package/dist/src/ui/hooks/useIdeTrustListener.d.ts +4 -2
  322. package/dist/src/ui/hooks/useIdeTrustListener.js +40 -14
  323. package/dist/src/ui/hooks/useIdeTrustListener.js.map +1 -1
  324. package/dist/src/ui/hooks/useIdeTrustListener.test.d.ts +6 -0
  325. package/dist/src/ui/hooks/useIdeTrustListener.test.js +183 -0
  326. package/dist/src/ui/hooks/useIdeTrustListener.test.js.map +1 -0
  327. package/dist/src/ui/hooks/useModelCommand.d.ts +12 -0
  328. package/dist/src/ui/hooks/useModelCommand.js +21 -0
  329. package/dist/src/ui/hooks/useModelCommand.js.map +1 -0
  330. package/dist/src/ui/hooks/useModelCommand.test.d.ts +6 -0
  331. package/dist/src/ui/hooks/useModelCommand.test.js +35 -0
  332. package/dist/src/ui/hooks/useModelCommand.test.js.map +1 -0
  333. package/dist/src/ui/hooks/usePermissionsModifyTrust.d.ts +17 -0
  334. package/dist/src/ui/hooks/usePermissionsModifyTrust.js +78 -0
  335. package/dist/src/ui/hooks/usePermissionsModifyTrust.js.map +1 -0
  336. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.d.ts +6 -0
  337. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js +182 -0
  338. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js.map +1 -0
  339. package/dist/src/ui/hooks/usePhraseCycler.js +1 -0
  340. package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
  341. package/dist/src/ui/hooks/useQuotaAndFallback.js +3 -17
  342. package/dist/src/ui/hooks/useQuotaAndFallback.js.map +1 -1
  343. package/dist/src/ui/hooks/useQuotaAndFallback.test.js +13 -43
  344. package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -1
  345. package/dist/src/ui/hooks/useSelectionList.d.ts +34 -0
  346. package/dist/src/ui/hooks/useSelectionList.js +272 -0
  347. package/dist/src/ui/hooks/useSelectionList.js.map +1 -0
  348. package/dist/src/ui/hooks/useSelectionList.test.d.ts +6 -0
  349. package/dist/src/ui/hooks/useSelectionList.test.js +725 -0
  350. package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -0
  351. package/dist/src/ui/hooks/useShellHistory.test.js +12 -8
  352. package/dist/src/ui/hooks/useShellHistory.test.js.map +1 -1
  353. package/dist/src/ui/hooks/useSlashCompletion.js +7 -2
  354. package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
  355. package/dist/src/ui/hooks/useSlashCompletion.test.js +33 -0
  356. package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
  357. package/dist/src/ui/hooks/useTerminalSize.js +2 -3
  358. package/dist/src/ui/hooks/useTerminalSize.js.map +1 -1
  359. package/dist/src/ui/hooks/useToolScheduler.test.js +2 -2
  360. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  361. package/dist/src/ui/hooks/useWorkspaceMigration.d.ts +2 -2
  362. package/dist/src/ui/hooks/useWorkspaceMigration.js +13 -8
  363. package/dist/src/ui/hooks/useWorkspaceMigration.js.map +1 -1
  364. package/dist/src/ui/layouts/DefaultAppLayout.d.ts +7 -0
  365. package/dist/src/ui/layouts/DefaultAppLayout.js +16 -0
  366. package/dist/src/ui/layouts/DefaultAppLayout.js.map +1 -0
  367. package/dist/src/ui/layouts/ScreenReaderAppLayout.d.ts +7 -0
  368. package/dist/src/ui/layouts/ScreenReaderAppLayout.js +17 -0
  369. package/dist/src/ui/layouts/ScreenReaderAppLayout.js.map +1 -0
  370. package/dist/src/ui/noninteractive/nonInteractiveUi.js +3 -1
  371. package/dist/src/ui/noninteractive/nonInteractiveUi.js.map +1 -1
  372. package/dist/src/ui/privacy/CloudFreePrivacyNotice.js +5 -5
  373. package/dist/src/ui/privacy/CloudFreePrivacyNotice.js.map +1 -1
  374. package/dist/src/ui/state/extensions.d.ts +47 -1
  375. package/dist/src/ui/state/extensions.js +68 -1
  376. package/dist/src/ui/state/extensions.js.map +1 -1
  377. package/dist/src/ui/themes/theme.js +2 -2
  378. package/dist/src/ui/themes/theme.js.map +1 -1
  379. package/dist/src/ui/types.d.ts +54 -3
  380. package/dist/src/ui/types.js +3 -0
  381. package/dist/src/ui/types.js.map +1 -1
  382. package/dist/src/ui/utils/MarkdownDisplay.js +1 -2
  383. package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
  384. package/dist/src/ui/utils/MarkdownDisplay.test.js +94 -91
  385. package/dist/src/ui/utils/MarkdownDisplay.test.js.map +1 -1
  386. package/dist/src/ui/utils/displayUtils.d.ts +1 -0
  387. package/dist/src/ui/utils/displayUtils.js +4 -1
  388. package/dist/src/ui/utils/displayUtils.js.map +1 -1
  389. package/dist/src/ui/utils/displayUtils.test.js +36 -17
  390. package/dist/src/ui/utils/displayUtils.test.js.map +1 -1
  391. package/dist/src/ui/utils/textUtils.d.ts +1 -0
  392. package/dist/src/ui/utils/textUtils.js +56 -0
  393. package/dist/src/ui/utils/textUtils.js.map +1 -1
  394. package/dist/src/ui/utils/textUtils.test.d.ts +6 -0
  395. package/dist/src/ui/utils/textUtils.test.js +132 -0
  396. package/dist/src/ui/utils/textUtils.test.js.map +1 -0
  397. package/dist/src/ui/utils/ui-sizing.d.ts +7 -0
  398. package/dist/src/ui/utils/ui-sizing.js +23 -0
  399. package/dist/src/ui/utils/ui-sizing.js.map +1 -0
  400. package/dist/src/utils/commentJson.js +95 -13
  401. package/dist/src/utils/commentJson.js.map +1 -1
  402. package/dist/src/utils/commentJson.test.js +161 -0
  403. package/dist/src/utils/commentJson.test.js.map +1 -1
  404. package/dist/src/utils/deepMerge.d.ts +2 -3
  405. package/dist/src/utils/errors.d.ts +8 -3
  406. package/dist/src/utils/errors.js +23 -13
  407. package/dist/src/utils/errors.js.map +1 -1
  408. package/dist/src/utils/errors.test.js +40 -46
  409. package/dist/src/utils/errors.test.js.map +1 -1
  410. package/dist/src/utils/events.d.ts +2 -1
  411. package/dist/src/utils/events.js +1 -0
  412. package/dist/src/utils/events.js.map +1 -1
  413. package/dist/src/utils/handleAutoUpdate.js +4 -1
  414. package/dist/src/utils/handleAutoUpdate.js.map +1 -1
  415. package/dist/src/utils/installationInfo.d.ts +1 -0
  416. package/dist/src/utils/installationInfo.js +2 -0
  417. package/dist/src/utils/installationInfo.js.map +1 -1
  418. package/dist/src/utils/math.d.ts +13 -0
  419. package/dist/src/utils/math.js +14 -0
  420. package/dist/src/utils/math.js.map +1 -0
  421. package/dist/src/utils/relaunch.d.ts +7 -0
  422. package/dist/src/utils/relaunch.js +57 -0
  423. package/dist/src/utils/relaunch.js.map +1 -0
  424. package/dist/src/utils/relaunch.test.d.ts +6 -0
  425. package/dist/src/utils/relaunch.test.js +273 -0
  426. package/dist/src/utils/relaunch.test.js.map +1 -0
  427. package/dist/src/utils/sandbox.js +31 -17
  428. package/dist/src/utils/sandbox.js.map +1 -1
  429. package/dist/src/utils/sessionCleanup.d.ts +22 -0
  430. package/dist/src/utils/sessionCleanup.integration.test.d.ts +6 -0
  431. package/dist/src/utils/sessionCleanup.integration.test.js +182 -0
  432. package/dist/src/utils/sessionCleanup.integration.test.js.map +1 -0
  433. package/dist/src/utils/sessionCleanup.js +214 -0
  434. package/dist/src/utils/sessionCleanup.js.map +1 -0
  435. package/dist/src/utils/sessionCleanup.test.d.ts +6 -0
  436. package/dist/src/utils/sessionCleanup.test.js +1232 -0
  437. package/dist/src/utils/sessionCleanup.test.js.map +1 -0
  438. package/dist/src/utils/sessionUtils.d.ts +37 -0
  439. package/dist/src/utils/sessionUtils.js +71 -0
  440. package/dist/src/utils/sessionUtils.js.map +1 -0
  441. package/dist/src/utils/windowTitle.d.ts +12 -0
  442. package/dist/src/utils/windowTitle.js +19 -0
  443. package/dist/src/utils/windowTitle.js.map +1 -0
  444. package/dist/src/utils/windowTitle.test.d.ts +6 -0
  445. package/dist/src/utils/windowTitle.test.js +49 -0
  446. package/dist/src/utils/windowTitle.test.js.map +1 -0
  447. package/dist/src/validateNonInterActiveAuth.js +6 -7
  448. package/dist/src/validateNonInterActiveAuth.js.map +1 -1
  449. package/dist/src/zed-integration/acp.js +1 -2
  450. package/dist/src/zed-integration/acp.js.map +1 -1
  451. package/dist/src/zed-integration/fileSystemService.d.ts +1 -0
  452. package/dist/src/zed-integration/fileSystemService.js +3 -0
  453. package/dist/src/zed-integration/fileSystemService.js.map +1 -1
  454. package/dist/src/zed-integration/schema.d.ts +70 -70
  455. package/dist/src/zed-integration/zedIntegration.d.ts +9 -3
  456. package/dist/src/zed-integration/zedIntegration.js +23 -28
  457. package/dist/src/zed-integration/zedIntegration.js.map +1 -1
  458. package/dist/tsconfig.tsbuildinfo +1 -1
  459. package/package.json +13 -5
  460. package/dist/src/commands/extensions/examples/mcp-server/example.js +0 -46
  461. package/dist/src/commands/extensions/examples/mcp-server/example.js.map +0 -1
  462. package/dist/src/ui/contexts/FocusContext.d.ts +0 -7
  463. package/dist/src/ui/contexts/FocusContext.js +0 -9
  464. package/dist/src/ui/contexts/FocusContext.js.map +0 -1
  465. /package/dist/src/{commands/extensions/examples/mcp-server/example.d.ts → ui/commands/modelCommand.test.d.ts} +0 -0
@@ -3,18 +3,20 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import { GEMINI_DIR, Storage, ClearcutLogger, Config, ExtensionInstallEvent, ExtensionUninstallEvent, } from '@google/gemini-cli-core';
6
+ import { GEMINI_DIR, Storage, Config, ExtensionInstallEvent, ExtensionUninstallEvent, ExtensionUpdateEvent, ExtensionDisableEvent, ExtensionEnableEvent, logExtensionEnable, logExtensionInstallEvent, logExtensionUninstall, logExtensionUpdateEvent, logExtensionDisable, } from '@google/gemini-cli-core';
7
7
  import * as fs from 'node:fs';
8
8
  import * as path from 'node:path';
9
9
  import * as os from 'node:os';
10
10
  import { SettingScope, loadSettings } from '../config/settings.js';
11
11
  import { getErrorMessage } from '../utils/errors.js';
12
- import { recursivelyHydrateStrings } from './extensions/variables.js';
12
+ import { recursivelyHydrateStrings, } from './extensions/variables.js';
13
13
  import { isWorkspaceTrusted } from './trustedFolders.js';
14
14
  import { resolveEnvVarsInObject } from '../utils/envVarResolver.js';
15
15
  import { randomUUID } from 'node:crypto';
16
16
  import { cloneFromGit, downloadFromGitHubRelease, } from './extensions/github.js';
17
17
  import { ExtensionEnablementManager } from './extensions/extensionEnablement.js';
18
+ import chalk from 'chalk';
19
+ import { escapeAnsiCtrlCodes } from '../ui/utils/textUtils.js';
18
20
  export const EXTENSIONS_DIRECTORY_NAME = path.join(GEMINI_DIR, 'extensions');
19
21
  export const EXTENSIONS_CONFIG_FILENAME = 'gemini-extension.json';
20
22
  export const INSTALL_METADATA_FILENAME = '.gemini-extension-install.json';
@@ -47,7 +49,7 @@ export function getWorkspaceExtensions(workspaceDir) {
47
49
  export async function copyExtension(source, destination) {
48
50
  await fs.promises.cp(source, destination, { recursive: true });
49
51
  }
50
- export async function performWorkspaceExtensionMigration(extensions) {
52
+ export async function performWorkspaceExtensionMigration(extensions, requestConsent) {
51
53
  const failedInstallNames = [];
52
54
  for (const extension of extensions) {
53
55
  try {
@@ -55,39 +57,40 @@ export async function performWorkspaceExtensionMigration(extensions) {
55
57
  source: extension.path,
56
58
  type: 'local',
57
59
  };
58
- await installExtension(installMetadata);
60
+ await installOrUpdateExtension(installMetadata, requestConsent);
59
61
  }
60
62
  catch (_) {
61
- failedInstallNames.push(extension.config.name);
63
+ failedInstallNames.push(extension.name);
62
64
  }
63
65
  }
64
66
  return failedInstallNames;
65
67
  }
66
- function getClearcutLogger(cwd) {
68
+ function getTelemetryConfig(cwd) {
69
+ const settings = loadSettings(cwd);
67
70
  const config = new Config({
71
+ telemetry: settings.merged.telemetry,
72
+ interactive: false,
68
73
  sessionId: randomUUID(),
69
74
  targetDir: cwd,
70
75
  cwd,
71
76
  model: '',
72
77
  debugMode: false,
73
78
  });
74
- const logger = ClearcutLogger.getInstance(config);
75
- return logger;
79
+ return config;
76
80
  }
77
- export function loadExtensions(workspaceDir = process.cwd()) {
81
+ export function loadExtensions(extensionEnablementManager, workspaceDir = process.cwd()) {
78
82
  const settings = loadSettings(workspaceDir).merged;
79
83
  const allExtensions = [...loadUserExtensions()];
80
- if ((isWorkspaceTrusted(settings) ?? true) &&
84
+ if (isWorkspaceTrusted(settings).isTrusted &&
81
85
  // Default management setting to true
82
86
  !(settings.experimental?.extensionManagement ?? true)) {
83
87
  allExtensions.push(...getWorkspaceExtensions(workspaceDir));
84
88
  }
85
89
  const uniqueExtensions = new Map();
86
- const manager = new ExtensionEnablementManager(ExtensionStorage.getUserExtensionsDir());
87
90
  for (const extension of allExtensions) {
88
- if (!uniqueExtensions.has(extension.config.name) &&
89
- manager.isEnabled(extension.config.name, workspaceDir)) {
90
- uniqueExtensions.set(extension.config.name, extension);
91
+ if (!uniqueExtensions.has(extension.name) &&
92
+ extensionEnablementManager.isEnabled(extension.name, workspaceDir)) {
93
+ uniqueExtensions.set(extension.name, extension);
91
94
  }
92
95
  }
93
96
  return Array.from(uniqueExtensions.values());
@@ -96,8 +99,8 @@ export function loadUserExtensions() {
96
99
  const userExtensions = loadExtensionsFromDir(os.homedir());
97
100
  const uniqueExtensions = new Map();
98
101
  for (const extension of userExtensions) {
99
- if (!uniqueExtensions.has(extension.config.name)) {
100
- uniqueExtensions.set(extension.config.name, extension);
102
+ if (!uniqueExtensions.has(extension.name)) {
103
+ uniqueExtensions.set(extension.name, extension);
101
104
  }
102
105
  }
103
106
  return Array.from(uniqueExtensions.values());
@@ -128,39 +131,59 @@ export function loadExtension(context) {
128
131
  if (installMetadata?.type === 'link') {
129
132
  effectiveExtensionPath = installMetadata.source;
130
133
  }
131
- const configFilePath = path.join(effectiveExtensionPath, EXTENSIONS_CONFIG_FILENAME);
132
- if (!fs.existsSync(configFilePath)) {
133
- console.error(`Warning: extension directory ${effectiveExtensionPath} does not contain a config file ${configFilePath}.`);
134
- return null;
135
- }
136
134
  try {
137
- const configContent = fs.readFileSync(configFilePath, 'utf-8');
138
- let config = recursivelyHydrateStrings(JSON.parse(configContent), {
139
- extensionPath: extensionDir,
140
- workspacePath: workspaceDir,
141
- '/': path.sep,
142
- pathSeparator: path.sep,
135
+ let config = loadExtensionConfig({
136
+ extensionDir: effectiveExtensionPath,
137
+ workspaceDir,
143
138
  });
144
- if (!config.name || !config.version) {
145
- console.error(`Invalid extension config in ${configFilePath}: missing name or version.`);
146
- return null;
147
- }
148
139
  config = resolveEnvVarsInObject(config);
140
+ if (config.mcpServers) {
141
+ config.mcpServers = Object.fromEntries(Object.entries(config.mcpServers).map(([key, value]) => [
142
+ key,
143
+ filterMcpConfig(value),
144
+ ]));
145
+ }
149
146
  const contextFiles = getContextFileNames(config)
150
147
  .map((contextFileName) => path.join(effectiveExtensionPath, contextFileName))
151
148
  .filter((contextFilePath) => fs.existsSync(contextFilePath));
152
149
  return {
150
+ name: config.name,
151
+ version: config.version,
153
152
  path: effectiveExtensionPath,
154
- config,
155
153
  contextFiles,
156
154
  installMetadata,
155
+ mcpServers: config.mcpServers,
156
+ excludeTools: config.excludeTools,
157
+ isActive: true, // Barring any other signals extensions should be considered Active.
157
158
  };
158
159
  }
159
160
  catch (e) {
160
- console.error(`Warning: error parsing extension config in ${configFilePath}: ${getErrorMessage(e)}`);
161
+ console.error(`Warning: Skipping extension in ${effectiveExtensionPath}: ${getErrorMessage(e)}`);
161
162
  return null;
162
163
  }
163
164
  }
165
+ export function loadExtensionByName(name, workspaceDir = process.cwd()) {
166
+ const userExtensionsDir = ExtensionStorage.getUserExtensionsDir();
167
+ if (!fs.existsSync(userExtensionsDir)) {
168
+ return null;
169
+ }
170
+ for (const subdir of fs.readdirSync(userExtensionsDir)) {
171
+ const extensionDir = path.join(userExtensionsDir, subdir);
172
+ if (!fs.statSync(extensionDir).isDirectory()) {
173
+ continue;
174
+ }
175
+ const extension = loadExtension({ extensionDir, workspaceDir });
176
+ if (extension && extension.name.toLowerCase() === name.toLowerCase()) {
177
+ return extension;
178
+ }
179
+ }
180
+ return null;
181
+ }
182
+ function filterMcpConfig(original) {
183
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
184
+ const { trust, ...rest } = original;
185
+ return Object.freeze(rest);
186
+ }
164
187
  export function loadInstallMetadata(extensionDir) {
165
188
  const metadataFilePath = path.join(extensionDir, INSTALL_METADATA_FILENAME);
166
189
  try {
@@ -188,55 +211,48 @@ function getContextFileNames(config) {
188
211
  * @param enabledExtensionNames The names of explicitly enabled extensions.
189
212
  * @param workspaceDir The current workspace directory.
190
213
  */
191
- export function annotateActiveExtensions(extensions, enabledExtensionNames, workspaceDir) {
192
- const manager = new ExtensionEnablementManager(ExtensionStorage.getUserExtensionsDir());
193
- const annotatedExtensions = [];
194
- if (enabledExtensionNames.length === 0) {
195
- return extensions.map((extension) => ({
196
- name: extension.config.name,
197
- version: extension.config.version,
198
- isActive: manager.isEnabled(extension.config.name, workspaceDir),
199
- path: extension.path,
200
- installMetadata: extension.installMetadata,
201
- }));
202
- }
203
- const lowerCaseEnabledExtensions = new Set(enabledExtensionNames.map((e) => e.trim().toLowerCase()));
204
- if (lowerCaseEnabledExtensions.size === 1 &&
205
- lowerCaseEnabledExtensions.has('none')) {
206
- return extensions.map((extension) => ({
207
- name: extension.config.name,
208
- version: extension.config.version,
209
- isActive: false,
210
- path: extension.path,
211
- installMetadata: extension.installMetadata,
212
- }));
213
- }
214
- const notFoundNames = new Set(lowerCaseEnabledExtensions);
215
- for (const extension of extensions) {
216
- const lowerCaseName = extension.config.name.toLowerCase();
217
- const isActive = lowerCaseEnabledExtensions.has(lowerCaseName);
218
- if (isActive) {
219
- notFoundNames.delete(lowerCaseName);
220
- }
221
- annotatedExtensions.push({
222
- name: extension.config.name,
223
- version: extension.config.version,
224
- isActive,
225
- path: extension.path,
226
- installMetadata: extension.installMetadata,
227
- });
228
- }
229
- for (const requestedName of notFoundNames) {
230
- console.error(`Extension not found: ${requestedName}`);
231
- }
232
- return annotatedExtensions;
214
+ export function annotateActiveExtensions(extensions, workspaceDir, manager) {
215
+ manager.validateExtensionOverrides(extensions);
216
+ return extensions.map((extension) => ({
217
+ ...extension,
218
+ isActive: manager.isEnabled(extension.name, workspaceDir),
219
+ }));
220
+ }
221
+ /**
222
+ * Requests consent from the user to perform an action, by reading a Y/n
223
+ * character from stdin.
224
+ *
225
+ * This should not be called from interactive mode as it will break the CLI.
226
+ *
227
+ * @param consentDescription The description of the thing they will be consenting to.
228
+ * @returns boolean, whether they consented or not.
229
+ */
230
+ export async function requestConsentNonInteractive(consentDescription) {
231
+ console.info(consentDescription);
232
+ const result = await promptForConsentNonInteractive('Do you want to continue? [Y/n]: ');
233
+ return result;
234
+ }
235
+ /**
236
+ * Requests consent from the user to perform an action, in interactive mode.
237
+ *
238
+ * This should not be called from non-interactive mode as it will not work.
239
+ *
240
+ * @param consentDescription The description of the thing they will be consenting to.
241
+ * @param setExtensionUpdateConfirmationRequest A function to actually add a prompt to the UI.
242
+ * @returns boolean, whether they consented or not.
243
+ */
244
+ export async function requestConsentInteractive(consentDescription, addExtensionUpdateConfirmationRequest) {
245
+ return await promptForConsentInteractive(consentDescription + '\n\nDo you want to continue?', addExtensionUpdateConfirmationRequest);
233
246
  }
234
247
  /**
235
- * Asks users a prompt and awaits for a y/n response
248
+ * Asks users a prompt and awaits for a y/n response on stdin.
249
+ *
250
+ * This should not be called from interactive mode as it will break the CLI.
251
+ *
236
252
  * @param prompt A yes/no prompt to ask the user
237
- * @returns Whether or not the user answers 'y' (yes)
253
+ * @returns Whether or not the user answers 'y' (yes). Defaults to 'yes' on enter.
238
254
  */
239
- async function promptForContinuation(prompt) {
255
+ async function promptForConsentNonInteractive(prompt) {
240
256
  const readline = await import('node:readline');
241
257
  const rl = readline.createInterface({
242
258
  input: process.stdin,
@@ -245,17 +261,37 @@ async function promptForContinuation(prompt) {
245
261
  return new Promise((resolve) => {
246
262
  rl.question(prompt, (answer) => {
247
263
  rl.close();
248
- resolve(answer.toLowerCase() === 'y');
264
+ resolve(['y', ''].includes(answer.trim().toLowerCase()));
249
265
  });
250
266
  });
251
267
  }
252
- export async function installExtension(installMetadata, askConsent = false, cwd = process.cwd()) {
253
- const logger = getClearcutLogger(cwd);
268
+ /**
269
+ * Asks users an interactive yes/no prompt.
270
+ *
271
+ * This should not be called from non-interactive mode as it will break the CLI.
272
+ *
273
+ * @param prompt A markdown prompt to ask the user
274
+ * @param setExtensionUpdateConfirmationRequest Function to update the UI state with the confirmation request.
275
+ * @returns Whether or not the user answers yes.
276
+ */
277
+ async function promptForConsentInteractive(prompt, addExtensionUpdateConfirmationRequest) {
278
+ return await new Promise((resolve) => {
279
+ addExtensionUpdateConfirmationRequest({
280
+ prompt,
281
+ onConfirm: (resolvedConfirmed) => {
282
+ resolve(resolvedConfirmed);
283
+ },
284
+ });
285
+ });
286
+ }
287
+ export async function installOrUpdateExtension(installMetadata, requestConsent, cwd = process.cwd(), previousExtensionConfig) {
288
+ const isUpdate = !!previousExtensionConfig;
289
+ const telemetryConfig = getTelemetryConfig(cwd);
254
290
  let newExtensionConfig = null;
255
291
  let localSourcePath;
256
292
  try {
257
293
  const settings = loadSettings(cwd).merged;
258
- if (!isWorkspaceTrusted(settings)) {
294
+ if (!isWorkspaceTrusted(settings).isTrusted) {
259
295
  throw new Error(`Could not install extension from untrusted folder at ${installMetadata.source}`);
260
296
  }
261
297
  const extensionsDir = ExtensionStorage.getUserExtensionsDir();
@@ -269,9 +305,9 @@ export async function installExtension(installMetadata, askConsent = false, cwd
269
305
  installMetadata.type === 'github-release') {
270
306
  tempDir = await ExtensionStorage.createTmpDir();
271
307
  try {
272
- const tagName = await downloadFromGitHubRelease(installMetadata, tempDir);
273
- updateExtensionVersion(tempDir, tagName);
274
- installMetadata.type = 'github-release';
308
+ const result = await downloadFromGitHubRelease(installMetadata, tempDir);
309
+ installMetadata.type = result.type;
310
+ installMetadata.releaseTag = result.tagName;
275
311
  }
276
312
  catch (_error) {
277
313
  await cloneFromGit(installMetadata, tempDir);
@@ -287,22 +323,22 @@ export async function installExtension(installMetadata, askConsent = false, cwd
287
323
  throw new Error(`Unsupported install type: ${installMetadata.type}`);
288
324
  }
289
325
  try {
290
- newExtensionConfig = await loadExtensionConfig({
326
+ newExtensionConfig = loadExtensionConfig({
291
327
  extensionDir: localSourcePath,
292
328
  workspaceDir: cwd,
293
329
  });
294
- if (!newExtensionConfig) {
295
- throw new Error(`Invalid extension at ${installMetadata.source}. Please make sure it has a valid gemini-extension.json file.`);
296
- }
297
330
  const newExtensionName = newExtensionConfig.name;
331
+ if (!isUpdate) {
332
+ const installedExtensions = loadUserExtensions();
333
+ if (installedExtensions.some((installed) => installed.name === newExtensionName)) {
334
+ throw new Error(`Extension "${newExtensionName}" is already installed. Please uninstall it first.`);
335
+ }
336
+ }
337
+ await maybeRequestConsentOrFail(newExtensionConfig, requestConsent, previousExtensionConfig);
298
338
  const extensionStorage = new ExtensionStorage(newExtensionName);
299
339
  const destinationPath = extensionStorage.getExtensionDir();
300
- const installedExtensions = loadUserExtensions();
301
- if (installedExtensions.some((installed) => installed.config.name === newExtensionName)) {
302
- throw new Error(`Extension "${newExtensionName}" is already installed. Please uninstall it first.`);
303
- }
304
- if (askConsent) {
305
- await requestConsent(newExtensionConfig);
340
+ if (isUpdate) {
341
+ await uninstallExtension(newExtensionName, isUpdate, cwd);
306
342
  }
307
343
  await fs.promises.mkdir(destinationPath, { recursive: true });
308
344
  if (installMetadata.type === 'local' ||
@@ -319,132 +355,203 @@ export async function installExtension(installMetadata, askConsent = false, cwd
319
355
  await fs.promises.rm(tempDir, { recursive: true, force: true });
320
356
  }
321
357
  }
322
- logger?.logExtensionInstallEvent(new ExtensionInstallEvent(newExtensionConfig.name, newExtensionConfig.version, installMetadata.source, 'success'));
323
- enableExtension(newExtensionConfig.name, SettingScope.User);
358
+ if (isUpdate) {
359
+ logExtensionUpdateEvent(telemetryConfig, new ExtensionUpdateEvent(newExtensionConfig.name, newExtensionConfig.version, previousExtensionConfig.version, installMetadata.source, 'success'));
360
+ }
361
+ else {
362
+ logExtensionInstallEvent(telemetryConfig, new ExtensionInstallEvent(newExtensionConfig.name, newExtensionConfig.version, installMetadata.source, 'success'));
363
+ enableExtension(newExtensionConfig.name, SettingScope.User);
364
+ }
324
365
  return newExtensionConfig.name;
325
366
  }
326
367
  catch (error) {
327
368
  // Attempt to load config from the source path even if installation fails
328
369
  // to get the name and version for logging.
329
370
  if (!newExtensionConfig && localSourcePath) {
330
- newExtensionConfig = await loadExtensionConfig({
331
- extensionDir: localSourcePath,
332
- workspaceDir: cwd,
333
- });
371
+ try {
372
+ newExtensionConfig = loadExtensionConfig({
373
+ extensionDir: localSourcePath,
374
+ workspaceDir: cwd,
375
+ });
376
+ }
377
+ catch {
378
+ // Ignore error, this is just for logging.
379
+ }
380
+ }
381
+ if (isUpdate) {
382
+ logExtensionUpdateEvent(telemetryConfig, new ExtensionUpdateEvent(newExtensionConfig?.name ?? previousExtensionConfig.name, newExtensionConfig?.version ?? '', previousExtensionConfig.version, installMetadata.source, 'error'));
383
+ }
384
+ else {
385
+ logExtensionInstallEvent(telemetryConfig, new ExtensionInstallEvent(newExtensionConfig?.name ?? '', newExtensionConfig?.version ?? '', installMetadata.source, 'error'));
334
386
  }
335
- logger?.logExtensionInstallEvent(new ExtensionInstallEvent(newExtensionConfig?.name ?? '', newExtensionConfig?.version ?? '', installMetadata.source, 'error'));
336
387
  throw error;
337
388
  }
338
389
  }
339
- async function updateExtensionVersion(extensionDir, extensionVersion) {
340
- const configFilePath = path.join(extensionDir, EXTENSIONS_CONFIG_FILENAME);
341
- if (fs.existsSync(configFilePath)) {
342
- const configContent = await fs.promises.readFile(configFilePath, 'utf-8');
343
- const config = JSON.parse(configContent);
344
- config.version = extensionVersion;
345
- await fs.promises.writeFile(configFilePath, JSON.stringify(config, null, 2));
346
- }
347
- }
348
- async function requestConsent(extensionConfig) {
349
- const mcpServerEntries = Object.entries(extensionConfig.mcpServers || {});
390
+ /**
391
+ * Builds a consent string for installing an extension based on it's
392
+ * extensionConfig.
393
+ */
394
+ function extensionConsentString(extensionConfig) {
395
+ const sanitizedConfig = escapeAnsiCtrlCodes(extensionConfig);
396
+ const output = [];
397
+ const mcpServerEntries = Object.entries(sanitizedConfig.mcpServers || {});
398
+ output.push(`Installing extension "${sanitizedConfig.name}".`);
399
+ output.push('**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**');
350
400
  if (mcpServerEntries.length) {
351
- console.info('This extension will run the following MCP servers: ');
401
+ output.push('This extension will run the following MCP servers:');
352
402
  for (const [key, mcpServer] of mcpServerEntries) {
353
403
  const isLocal = !!mcpServer.command;
354
- console.info(` * ${key} (${isLocal ? 'local' : 'remote'}): ${mcpServer.description}`);
404
+ const source = mcpServer.httpUrl ??
405
+ `${mcpServer.command || ''}${mcpServer.args ? ' ' + mcpServer.args.join(' ') : ''}`;
406
+ output.push(` * ${key} (${isLocal ? 'local' : 'remote'}): ${source}`);
355
407
  }
356
- console.info('The extension will append info to your gemini.md context');
357
- const shouldContinue = await promptForContinuation('Do you want to continue? (y/n): ');
358
- if (!shouldContinue) {
359
- throw new Error('Installation cancelled by user.');
408
+ }
409
+ if (sanitizedConfig.contextFileName) {
410
+ output.push(`This extension will append info to your gemini.md context using ${sanitizedConfig.contextFileName}`);
411
+ }
412
+ if (sanitizedConfig.excludeTools) {
413
+ output.push(`This extension will exclude the following core tools: ${sanitizedConfig.excludeTools}`);
414
+ }
415
+ return output.join('\n');
416
+ }
417
+ /**
418
+ * Requests consent from the user to install an extension (extensionConfig), if
419
+ * there is any difference between the consent string for `extensionConfig` and
420
+ * `previousExtensionConfig`.
421
+ *
422
+ * Always requests consent if previousExtensionConfig is null.
423
+ *
424
+ * Throws if the user does not consent.
425
+ */
426
+ async function maybeRequestConsentOrFail(extensionConfig, requestConsent, previousExtensionConfig) {
427
+ const extensionConsent = extensionConsentString(extensionConfig);
428
+ if (previousExtensionConfig) {
429
+ const previousExtensionConsent = extensionConsentString(previousExtensionConfig);
430
+ if (previousExtensionConsent === extensionConsent) {
431
+ return;
360
432
  }
361
433
  }
434
+ if (!(await requestConsent(extensionConsent))) {
435
+ throw new Error(`Installation cancelled for "${extensionConfig.name}".`);
436
+ }
437
+ }
438
+ export function validateName(name) {
439
+ if (!/^[a-zA-Z0-9-]+$/.test(name)) {
440
+ throw new Error(`Invalid extension name: "${name}". Only letters (a-z, A-Z), numbers (0-9), and dashes (-) are allowed.`);
441
+ }
362
442
  }
363
- export async function loadExtensionConfig(context) {
443
+ export function loadExtensionConfig(context) {
364
444
  const { extensionDir, workspaceDir } = context;
365
445
  const configFilePath = path.join(extensionDir, EXTENSIONS_CONFIG_FILENAME);
366
446
  if (!fs.existsSync(configFilePath)) {
367
- return null;
447
+ throw new Error(`Configuration file not found at ${configFilePath}`);
368
448
  }
369
449
  try {
370
450
  const configContent = fs.readFileSync(configFilePath, 'utf-8');
371
- const config = recursivelyHydrateStrings(JSON.parse(configContent), {
372
- extensionPath: extensionDir,
451
+ const rawConfig = JSON.parse(configContent);
452
+ if (!rawConfig.name || !rawConfig.version) {
453
+ throw new Error(`Invalid configuration in ${configFilePath}: missing ${!rawConfig.name ? '"name"' : '"version"'}`);
454
+ }
455
+ const installDir = new ExtensionStorage(rawConfig.name).getExtensionDir();
456
+ const config = recursivelyHydrateStrings(rawConfig, {
457
+ extensionPath: installDir,
373
458
  workspacePath: workspaceDir,
374
459
  '/': path.sep,
375
460
  pathSeparator: path.sep,
376
461
  });
377
- if (!config.name || !config.version) {
378
- return null;
379
- }
462
+ validateName(config.name);
380
463
  return config;
381
464
  }
382
- catch (_) {
383
- return null;
465
+ catch (e) {
466
+ throw new Error(`Failed to load extension config from ${configFilePath}: ${getErrorMessage(e)}`);
384
467
  }
385
468
  }
386
- export async function uninstallExtension(extensionIdentifier, cwd = process.cwd()) {
387
- const logger = getClearcutLogger(cwd);
469
+ export async function uninstallExtension(extensionIdentifier, isUpdate, cwd = process.cwd()) {
388
470
  const installedExtensions = loadUserExtensions();
389
- const extensionName = installedExtensions.find((installed) => installed.config.name.toLowerCase() ===
390
- extensionIdentifier.toLowerCase() ||
471
+ const extensionName = installedExtensions.find((installed) => installed.name.toLowerCase() === extensionIdentifier.toLowerCase() ||
391
472
  installed.installMetadata?.source.toLowerCase() ===
392
- extensionIdentifier.toLowerCase())?.config.name;
473
+ extensionIdentifier.toLowerCase())?.name;
393
474
  if (!extensionName) {
394
475
  throw new Error(`Extension not found.`);
395
476
  }
396
- const manager = new ExtensionEnablementManager(ExtensionStorage.getUserExtensionsDir());
397
- manager.remove(extensionName);
398
477
  const storage = new ExtensionStorage(extensionName);
399
478
  await fs.promises.rm(storage.getExtensionDir(), {
400
479
  recursive: true,
401
480
  force: true,
402
481
  });
403
- logger?.logExtensionUninstallEvent(new ExtensionUninstallEvent(extensionName, 'success'));
482
+ // The rest of the cleanup below here is only for true uninstalls, not
483
+ // uninstalls related to updates.
484
+ if (isUpdate)
485
+ return;
486
+ const manager = new ExtensionEnablementManager(ExtensionStorage.getUserExtensionsDir(), [extensionName]);
487
+ manager.remove(extensionName);
488
+ const telemetryConfig = getTelemetryConfig(cwd);
489
+ logExtensionUninstall(telemetryConfig, new ExtensionUninstallEvent(extensionName, 'success'));
404
490
  }
405
- export function toOutputString(extension) {
406
- let output = `${extension.config.name} (${extension.config.version})`;
491
+ export function toOutputString(extension, workspaceDir) {
492
+ const manager = new ExtensionEnablementManager(ExtensionStorage.getUserExtensionsDir());
493
+ const userEnabled = manager.isEnabled(extension.name, os.homedir());
494
+ const workspaceEnabled = manager.isEnabled(extension.name, workspaceDir);
495
+ const status = workspaceEnabled ? chalk.green('✓') : chalk.red('✗');
496
+ let output = `${status} ${extension.name} (${extension.version})`;
407
497
  output += `\n Path: ${extension.path}`;
408
498
  if (extension.installMetadata) {
409
499
  output += `\n Source: ${extension.installMetadata.source} (Type: ${extension.installMetadata.type})`;
410
500
  if (extension.installMetadata.ref) {
411
501
  output += `\n Ref: ${extension.installMetadata.ref}`;
412
502
  }
503
+ if (extension.installMetadata.releaseTag) {
504
+ output += `\n Release tag: ${extension.installMetadata.releaseTag}`;
505
+ }
413
506
  }
507
+ output += `\n Enabled (User): ${userEnabled}`;
508
+ output += `\n Enabled (Workspace): ${workspaceEnabled}`;
414
509
  if (extension.contextFiles.length > 0) {
415
510
  output += `\n Context files:`;
416
511
  extension.contextFiles.forEach((contextFile) => {
417
512
  output += `\n ${contextFile}`;
418
513
  });
419
514
  }
420
- if (extension.config.mcpServers) {
515
+ if (extension.mcpServers) {
421
516
  output += `\n MCP servers:`;
422
- Object.keys(extension.config.mcpServers).forEach((key) => {
517
+ Object.keys(extension.mcpServers).forEach((key) => {
423
518
  output += `\n ${key}`;
424
519
  });
425
520
  }
426
- if (extension.config.excludeTools) {
521
+ if (extension.excludeTools) {
427
522
  output += `\n Excluded tools:`;
428
- extension.config.excludeTools.forEach((tool) => {
523
+ extension.excludeTools.forEach((tool) => {
429
524
  output += `\n ${tool}`;
430
525
  });
431
526
  }
432
527
  return output;
433
528
  }
434
529
  export function disableExtension(name, scope, cwd = process.cwd()) {
530
+ const config = getTelemetryConfig(cwd);
435
531
  if (scope === SettingScope.System || scope === SettingScope.SystemDefaults) {
436
532
  throw new Error('System and SystemDefaults scopes are not supported.');
437
533
  }
438
- const manager = new ExtensionEnablementManager(ExtensionStorage.getUserExtensionsDir());
534
+ const extension = loadExtensionByName(name, cwd);
535
+ if (!extension) {
536
+ throw new Error(`Extension with name ${name} does not exist.`);
537
+ }
538
+ const manager = new ExtensionEnablementManager(ExtensionStorage.getUserExtensionsDir(), [name]);
439
539
  const scopePath = scope === SettingScope.Workspace ? cwd : os.homedir();
440
540
  manager.disable(name, true, scopePath);
541
+ logExtensionDisable(config, new ExtensionDisableEvent(name, scope));
441
542
  }
442
543
  export function enableExtension(name, scope, cwd = process.cwd()) {
443
544
  if (scope === SettingScope.System || scope === SettingScope.SystemDefaults) {
444
545
  throw new Error('System and SystemDefaults scopes are not supported.');
445
546
  }
547
+ const extension = loadExtensionByName(name, cwd);
548
+ if (!extension) {
549
+ throw new Error(`Extension with name ${name} does not exist.`);
550
+ }
446
551
  const manager = new ExtensionEnablementManager(ExtensionStorage.getUserExtensionsDir());
447
552
  const scopePath = scope === SettingScope.Workspace ? cwd : os.homedir();
448
553
  manager.enable(name, true, scopePath);
554
+ const config = getTelemetryConfig(cwd);
555
+ logExtensionEnable(config, new ExtensionEnableEvent(name, scope));
449
556
  }
450
557
  //# sourceMappingURL=extension.js.map