@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.
- package/README.md +105 -62
- package/dist/package.json +13 -5
- package/dist/src/commands/extensions/examples/context/GEMINI.md +9 -3
- package/dist/src/commands/extensions/examples/context/gemini-extension.json +1 -2
- package/dist/src/commands/extensions/examples/mcp-server/gemini-extension.json +3 -2
- package/dist/src/commands/extensions/examples/mcp-server/package.json +18 -0
- package/dist/src/commands/extensions/examples/mcp-server/tsconfig.json +13 -0
- package/dist/src/commands/extensions/install.d.ts +2 -2
- package/dist/src/commands/extensions/install.js +35 -36
- package/dist/src/commands/extensions/install.js.map +1 -1
- package/dist/src/commands/extensions/install.test.js +25 -25
- package/dist/src/commands/extensions/install.test.js.map +1 -1
- package/dist/src/commands/extensions/link.js +2 -2
- package/dist/src/commands/extensions/link.js.map +1 -1
- package/dist/src/commands/extensions/list.js +1 -1
- package/dist/src/commands/extensions/list.js.map +1 -1
- package/dist/src/commands/extensions/new.js +28 -8
- package/dist/src/commands/extensions/new.js.map +1 -1
- package/dist/src/commands/extensions/new.test.js +14 -5
- package/dist/src/commands/extensions/new.test.js.map +1 -1
- package/dist/src/commands/extensions/uninstall.js +1 -1
- package/dist/src/commands/extensions/uninstall.js.map +1 -1
- package/dist/src/commands/extensions/uninstall.test.js +4 -1
- package/dist/src/commands/extensions/uninstall.test.js.map +1 -1
- package/dist/src/commands/extensions/update.js +35 -24
- package/dist/src/commands/extensions/update.js.map +1 -1
- package/dist/src/commands/mcp/add.js +6 -1
- package/dist/src/commands/mcp/add.js.map +1 -1
- package/dist/src/commands/mcp/list.js +5 -4
- package/dist/src/commands/mcp/list.js.map +1 -1
- package/dist/src/config/config.d.ts +7 -4
- package/dist/src/config/config.js +125 -39
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/extension.d.ts +44 -18
- package/dist/src/config/extension.js +250 -143
- package/dist/src/config/extension.js.map +1 -1
- package/dist/src/config/extensions/extensionEnablement.d.ts +12 -9
- package/dist/src/config/extensions/extensionEnablement.js +36 -9
- package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
- package/dist/src/config/extensions/extensionEnablement.test.js +74 -1
- package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
- package/dist/src/config/extensions/github.d.ts +14 -2
- package/dist/src/config/extensions/github.js +111 -89
- package/dist/src/config/extensions/github.js.map +1 -1
- package/dist/src/config/extensions/github.test.js +124 -13
- package/dist/src/config/extensions/github.test.js.map +1 -1
- package/dist/src/config/extensions/github_fetch.d.ts +7 -0
- package/dist/src/config/extensions/github_fetch.js +34 -0
- package/dist/src/config/extensions/github_fetch.js.map +1 -0
- package/dist/src/config/extensions/update.d.ts +4 -5
- package/dist/src/config/extensions/update.js +64 -42
- package/dist/src/config/extensions/update.js.map +1 -1
- package/dist/src/config/extensions/update.test.js +121 -71
- package/dist/src/config/extensions/update.test.js.map +1 -1
- package/dist/src/config/keyBindings.js +1 -1
- package/dist/src/config/keyBindings.js.map +1 -1
- package/dist/src/config/policy.js +2 -2
- package/dist/src/config/policy.js.map +1 -1
- package/dist/src/config/settings.d.ts +10 -1
- package/dist/src/config/settings.js +21 -6
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +97 -5
- package/dist/src/config/settingsSchema.js +96 -4
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/config/settingsSchema.test.js +8 -0
- package/dist/src/config/settingsSchema.test.js.map +1 -1
- package/dist/src/config/trustedFolders.d.ts +10 -2
- package/dist/src/config/trustedFolders.js +41 -16
- package/dist/src/config/trustedFolders.js.map +1 -1
- package/dist/src/config/trustedFolders.test.js +95 -14
- package/dist/src/config/trustedFolders.test.js.map +1 -1
- package/dist/src/gemini.js +114 -132
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +95 -14
- package/dist/src/gemini.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +7 -0
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.test.js +87 -1
- package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
- package/dist/src/services/FileCommandLoader.d.ts +1 -1
- package/dist/src/services/FileCommandLoader.js +4 -4
- package/dist/src/services/FileCommandLoader.js.map +1 -1
- package/dist/src/services/prompt-processors/shellProcessor.js +1 -1
- package/dist/src/services/prompt-processors/shellProcessor.js.map +1 -1
- package/dist/src/test-utils/render.d.ts +12 -1
- package/dist/src/test-utils/render.js +56 -1
- package/dist/src/test-utils/render.js.map +1 -1
- package/dist/src/ui/App.js +7 -9
- package/dist/src/ui/App.js.map +1 -1
- package/dist/src/ui/AppContainer.js +99 -27
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/AppContainer.test.js +300 -7
- package/dist/src/ui/AppContainer.test.js.map +1 -1
- package/dist/src/ui/IdeIntegrationNudge.js +3 -0
- package/dist/src/ui/IdeIntegrationNudge.js.map +1 -1
- package/dist/src/ui/auth/AuthDialog.js +8 -1
- package/dist/src/ui/auth/AuthDialog.js.map +1 -1
- package/dist/src/ui/auth/AuthDialog.test.js +1 -0
- package/dist/src/ui/auth/AuthDialog.test.js.map +1 -1
- package/dist/src/ui/auth/AuthInProgress.js +2 -2
- package/dist/src/ui/auth/AuthInProgress.js.map +1 -1
- package/dist/src/ui/commands/chatCommand.js +21 -27
- package/dist/src/ui/commands/chatCommand.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.js +39 -34
- package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
- package/dist/src/ui/commands/mcpCommand.js +78 -260
- package/dist/src/ui/commands/mcpCommand.js.map +1 -1
- package/dist/src/ui/commands/memoryCommand.js +23 -5
- package/dist/src/ui/commands/memoryCommand.js.map +1 -1
- package/dist/src/ui/commands/modelCommand.d.ts +7 -0
- package/dist/src/ui/commands/modelCommand.js +16 -0
- package/dist/src/ui/commands/modelCommand.js.map +1 -0
- package/dist/src/ui/commands/modelCommand.test.js +30 -0
- package/dist/src/ui/commands/modelCommand.test.js.map +1 -0
- package/dist/src/ui/commands/permissionsCommand.d.ts +7 -0
- package/dist/src/ui/commands/permissionsCommand.js +16 -0
- package/dist/src/ui/commands/permissionsCommand.js.map +1 -0
- package/dist/src/ui/commands/permissionsCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/permissionsCommand.test.js +30 -0
- package/dist/src/ui/commands/permissionsCommand.test.js.map +1 -0
- package/dist/src/ui/commands/profileCommand.d.ts +7 -0
- package/dist/src/ui/commands/profileCommand.js +23 -0
- package/dist/src/ui/commands/profileCommand.js.map +1 -0
- package/dist/src/ui/commands/setupGithubCommand.test.js +2 -1
- package/dist/src/ui/commands/setupGithubCommand.test.js.map +1 -1
- package/dist/src/ui/commands/toolsCommand.js +10 -24
- package/dist/src/ui/commands/toolsCommand.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +8 -6
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/components/AnsiOutput.d.ts +1 -0
- package/dist/src/ui/components/AnsiOutput.js +5 -5
- package/dist/src/ui/components/AnsiOutput.js.map +1 -1
- package/dist/src/ui/components/AnsiOutput.test.js +6 -6
- package/dist/src/ui/components/AnsiOutput.test.js.map +1 -1
- package/dist/src/ui/components/AppHeader.js +2 -5
- package/dist/src/ui/components/AppHeader.js.map +1 -1
- package/dist/src/ui/components/CliSpinner.d.ts +10 -0
- package/dist/src/ui/components/CliSpinner.js +20 -0
- package/dist/src/ui/components/CliSpinner.js.map +1 -0
- package/dist/src/ui/components/Composer.js +7 -29
- package/dist/src/ui/components/Composer.js.map +1 -1
- package/dist/src/ui/components/ConsentPrompt.d.ts +13 -0
- package/dist/src/ui/components/ConsentPrompt.js +19 -0
- package/dist/src/ui/components/ConsentPrompt.js.map +1 -0
- package/dist/src/ui/components/ConsentPrompt.test.d.ts +6 -0
- package/dist/src/ui/components/ConsentPrompt.test.js +67 -0
- package/dist/src/ui/components/ConsentPrompt.test.js.map +1 -0
- package/dist/src/ui/components/ContextSummaryDisplay.js +2 -2
- package/dist/src/ui/components/ContextSummaryDisplay.js.map +1 -1
- package/dist/src/ui/components/ContextUsageDisplay.d.ts +2 -1
- package/dist/src/ui/components/ContextUsageDisplay.js +4 -2
- package/dist/src/ui/components/ContextUsageDisplay.js.map +1 -1
- package/dist/src/ui/components/DebugProfiler.d.ts +18 -0
- package/dist/src/ui/components/DebugProfiler.js +158 -12
- package/dist/src/ui/components/DebugProfiler.js.map +1 -1
- package/dist/src/ui/components/DebugProfiler.test.d.ts +6 -0
- package/dist/src/ui/components/DebugProfiler.test.js +140 -0
- package/dist/src/ui/components/DebugProfiler.test.js.map +1 -0
- package/dist/src/ui/components/DialogManager.d.ts +7 -1
- package/dist/src/ui/components/DialogManager.js +18 -9
- package/dist/src/ui/components/DialogManager.js.map +1 -1
- package/dist/src/ui/components/EditorSettingsDialog.js +11 -2
- package/dist/src/ui/components/EditorSettingsDialog.js.map +1 -1
- package/dist/src/ui/components/ExitWarning.d.ts +7 -0
- package/dist/src/ui/components/ExitWarning.js +9 -0
- package/dist/src/ui/components/ExitWarning.js.map +1 -0
- package/dist/src/ui/components/FolderTrustDialog.js +3 -0
- package/dist/src/ui/components/FolderTrustDialog.js.map +1 -1
- package/dist/src/ui/components/FolderTrustDialog.test.js +2 -2
- package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
- package/dist/src/ui/components/Footer.d.ts +1 -19
- package/dist/src/ui/components/Footer.js +35 -17
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/GeminiRespondingSpinner.js +2 -2
- package/dist/src/ui/components/GeminiRespondingSpinner.js.map +1 -1
- package/dist/src/ui/components/Help.js +1 -1
- package/dist/src/ui/components/Help.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.d.ts +2 -1
- package/dist/src/ui/components/HistoryItemDisplay.js +10 -1
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.test.js +90 -9
- package/dist/src/ui/components/HistoryItemDisplay.test.js.map +1 -1
- package/dist/src/ui/components/IdeTrustChangeDialog.d.ts +11 -0
- package/dist/src/ui/components/IdeTrustChangeDialog.js +32 -0
- package/dist/src/ui/components/IdeTrustChangeDialog.js.map +1 -0
- package/dist/src/ui/components/IdeTrustChangeDialog.test.d.ts +6 -0
- package/dist/src/ui/components/IdeTrustChangeDialog.test.js +57 -0
- package/dist/src/ui/components/IdeTrustChangeDialog.test.js.map +1 -0
- package/dist/src/ui/components/InputPrompt.d.ts +8 -2
- package/dist/src/ui/components/InputPrompt.js +66 -32
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/LoadingIndicator.js +1 -1
- package/dist/src/ui/components/LoadingIndicator.js.map +1 -1
- package/dist/src/ui/components/LoadingIndicator.test.js +4 -0
- package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -1
- package/dist/src/ui/components/LoopDetectionConfirmation.js +2 -0
- package/dist/src/ui/components/LoopDetectionConfirmation.js.map +1 -1
- package/dist/src/ui/components/MainContent.js +7 -2
- package/dist/src/ui/components/MainContent.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.d.ts +11 -0
- package/dist/src/ui/components/ModelDialog.js +57 -0
- package/dist/src/ui/components/ModelDialog.js.map +1 -0
- package/dist/src/ui/components/ModelDialog.test.d.ts +6 -0
- package/dist/src/ui/components/ModelDialog.test.js +153 -0
- package/dist/src/ui/components/ModelDialog.test.js.map +1 -0
- package/dist/src/ui/components/Notifications.js +12 -4
- package/dist/src/ui/components/Notifications.js.map +1 -1
- package/dist/src/ui/components/PermissionsModifyTrustDialog.d.ts +13 -0
- package/dist/src/ui/components/PermissionsModifyTrustDialog.js +48 -0
- package/dist/src/ui/components/PermissionsModifyTrustDialog.js.map +1 -0
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.d.ts +6 -0
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +154 -0
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -0
- package/dist/src/ui/components/ProQuotaDialog.js +2 -0
- package/dist/src/ui/components/ProQuotaDialog.js.map +1 -1
- package/dist/src/ui/components/ProQuotaDialog.test.js +2 -0
- package/dist/src/ui/components/ProQuotaDialog.test.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.js +6 -3
- package/dist/src/ui/components/SettingsDialog.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.test.js +47 -13
- package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
- package/dist/src/ui/components/ShellConfirmationDialog.js +3 -0
- package/dist/src/ui/components/ShellConfirmationDialog.js.map +1 -1
- package/dist/src/ui/components/ThemeDialog.js +2 -0
- package/dist/src/ui/components/ThemeDialog.js.map +1 -1
- package/dist/src/ui/components/WorkspaceMigrationDialog.d.ts +2 -2
- package/dist/src/ui/components/WorkspaceMigrationDialog.js +7 -5
- package/dist/src/ui/components/WorkspaceMigrationDialog.js.map +1 -1
- package/dist/src/ui/components/messages/CompressionMessage.js +2 -2
- package/dist/src/ui/components/messages/CompressionMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js +15 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.d.ts +1 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.js +5 -5
- package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessage.d.ts +1 -1
- package/dist/src/ui/components/messages/ToolMessage.js +28 -5
- package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
- package/dist/src/ui/components/messages/UserMessage.js +1 -2
- package/dist/src/ui/components/messages/UserMessage.js.map +1 -1
- package/dist/src/ui/components/shared/BaseSelectionList.d.ts +38 -0
- package/dist/src/ui/components/shared/BaseSelectionList.js +72 -0
- package/dist/src/ui/components/shared/BaseSelectionList.js.map +1 -0
- package/dist/src/ui/components/shared/BaseSelectionList.test.d.ts +6 -0
- package/dist/src/ui/components/shared/BaseSelectionList.test.js +376 -0
- package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -0
- package/dist/src/ui/components/shared/DescriptiveRadioButtonSelect.d.ts +35 -0
- package/dist/src/ui/components/shared/DescriptiveRadioButtonSelect.js +13 -0
- package/dist/src/ui/components/shared/DescriptiveRadioButtonSelect.js.map +1 -0
- package/dist/src/ui/components/shared/DescriptiveRadioButtonSelect.test.d.ts +6 -0
- package/dist/src/ui/components/shared/DescriptiveRadioButtonSelect.test.js +79 -0
- package/dist/src/ui/components/shared/DescriptiveRadioButtonSelect.test.js.map +1 -0
- package/dist/src/ui/components/shared/RadioButtonSelect.d.ts +2 -3
- package/dist/src/ui/components/shared/RadioButtonSelect.js +9 -104
- package/dist/src/ui/components/shared/RadioButtonSelect.js.map +1 -1
- package/dist/src/ui/components/shared/RadioButtonSelect.test.js +115 -92
- package/dist/src/ui/components/shared/RadioButtonSelect.test.js.map +1 -1
- package/dist/src/ui/components/shared/ScopeSelector.js +4 -1
- package/dist/src/ui/components/shared/ScopeSelector.js.map +1 -1
- package/dist/src/ui/components/views/ChatList.d.ts +12 -0
- package/dist/src/ui/components/views/ChatList.js +17 -0
- package/dist/src/ui/components/views/ChatList.js.map +1 -0
- package/dist/src/ui/components/views/ChatList.test.d.ts +6 -0
- package/dist/src/ui/components/views/ChatList.test.js +42 -0
- package/dist/src/ui/components/views/ChatList.test.js.map +1 -0
- package/dist/src/ui/components/views/ExtensionsList.test.js +1 -1
- package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
- package/dist/src/ui/components/views/McpStatus.d.ts +27 -0
- package/dist/src/ui/components/views/McpStatus.js +77 -0
- package/dist/src/ui/components/views/McpStatus.js.map +1 -0
- package/dist/src/ui/components/views/McpStatus.test.d.ts +6 -0
- package/dist/src/ui/components/views/McpStatus.test.js +117 -0
- package/dist/src/ui/components/views/McpStatus.test.js.map +1 -0
- package/dist/src/ui/components/views/ToolsList.d.ts +14 -0
- package/dist/src/ui/components/views/ToolsList.js +7 -0
- package/dist/src/ui/components/views/ToolsList.js.map +1 -0
- package/dist/src/ui/components/views/ToolsList.test.d.ts +6 -0
- package/dist/src/ui/components/views/ToolsList.test.js +45 -0
- package/dist/src/ui/components/views/ToolsList.test.js.map +1 -0
- package/dist/src/ui/contexts/KeypressContext.js +3 -0
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/contexts/ShellFocusContext.d.ts +7 -0
- package/dist/src/ui/contexts/ShellFocusContext.js +9 -0
- package/dist/src/ui/contexts/ShellFocusContext.js.map +1 -0
- package/dist/src/ui/contexts/UIActionsContext.d.ts +2 -0
- package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
- package/dist/src/ui/contexts/UIStateContext.d.ts +11 -2
- package/dist/src/ui/contexts/UIStateContext.js +2 -0
- package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
- package/dist/src/ui/hooks/shellCommandProcessor.js +5 -6
- package/dist/src/ui/hooks/shellCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/shellCommandProcessor.test.js +19 -32
- package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.d.ts +8 -5
- package/dist/src/ui/hooks/slashCommandProcessor.js +11 -2
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionUpdates.d.ts +10 -1
- package/dist/src/ui/hooks/useExtensionUpdates.js +140 -38
- package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionUpdates.test.js +163 -84
- package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
- package/dist/src/ui/hooks/useFlickerDetector.d.ts +14 -0
- package/dist/src/ui/hooks/useFlickerDetector.js +37 -0
- package/dist/src/ui/hooks/useFlickerDetector.js.map +1 -0
- package/dist/src/ui/hooks/useFlickerDetector.test.d.ts +6 -0
- package/dist/src/ui/hooks/useFlickerDetector.test.js +102 -0
- package/dist/src/ui/hooks/useFlickerDetector.test.js.map +1 -0
- package/dist/src/ui/hooks/useFocus.js +10 -0
- package/dist/src/ui/hooks/useFocus.js.map +1 -1
- package/dist/src/ui/hooks/useFolderTrust.d.ts +2 -1
- package/dist/src/ui/hooks/useFolderTrust.js +13 -9
- package/dist/src/ui/hooks/useFolderTrust.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +38 -3
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
- package/dist/src/ui/hooks/useHistoryManager.js +3 -3
- package/dist/src/ui/hooks/useHistoryManager.js.map +1 -1
- package/dist/src/ui/hooks/useIdeTrustListener.d.ts +4 -2
- package/dist/src/ui/hooks/useIdeTrustListener.js +40 -14
- package/dist/src/ui/hooks/useIdeTrustListener.js.map +1 -1
- package/dist/src/ui/hooks/useIdeTrustListener.test.d.ts +6 -0
- package/dist/src/ui/hooks/useIdeTrustListener.test.js +183 -0
- package/dist/src/ui/hooks/useIdeTrustListener.test.js.map +1 -0
- package/dist/src/ui/hooks/useModelCommand.d.ts +12 -0
- package/dist/src/ui/hooks/useModelCommand.js +21 -0
- package/dist/src/ui/hooks/useModelCommand.js.map +1 -0
- package/dist/src/ui/hooks/useModelCommand.test.d.ts +6 -0
- package/dist/src/ui/hooks/useModelCommand.test.js +35 -0
- package/dist/src/ui/hooks/useModelCommand.test.js.map +1 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.d.ts +17 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.js +78 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.js.map +1 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.test.d.ts +6 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js +182 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js.map +1 -0
- package/dist/src/ui/hooks/usePhraseCycler.js +1 -0
- package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
- package/dist/src/ui/hooks/useQuotaAndFallback.js +3 -17
- package/dist/src/ui/hooks/useQuotaAndFallback.js.map +1 -1
- package/dist/src/ui/hooks/useQuotaAndFallback.test.js +13 -43
- package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -1
- package/dist/src/ui/hooks/useSelectionList.d.ts +34 -0
- package/dist/src/ui/hooks/useSelectionList.js +272 -0
- package/dist/src/ui/hooks/useSelectionList.js.map +1 -0
- package/dist/src/ui/hooks/useSelectionList.test.d.ts +6 -0
- package/dist/src/ui/hooks/useSelectionList.test.js +725 -0
- package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -0
- package/dist/src/ui/hooks/useShellHistory.test.js +12 -8
- package/dist/src/ui/hooks/useShellHistory.test.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.js +7 -2
- package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js +33 -0
- package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useTerminalSize.js +2 -3
- package/dist/src/ui/hooks/useTerminalSize.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +2 -2
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/hooks/useWorkspaceMigration.d.ts +2 -2
- package/dist/src/ui/hooks/useWorkspaceMigration.js +13 -8
- package/dist/src/ui/hooks/useWorkspaceMigration.js.map +1 -1
- package/dist/src/ui/layouts/DefaultAppLayout.d.ts +7 -0
- package/dist/src/ui/layouts/DefaultAppLayout.js +16 -0
- package/dist/src/ui/layouts/DefaultAppLayout.js.map +1 -0
- package/dist/src/ui/layouts/ScreenReaderAppLayout.d.ts +7 -0
- package/dist/src/ui/layouts/ScreenReaderAppLayout.js +17 -0
- package/dist/src/ui/layouts/ScreenReaderAppLayout.js.map +1 -0
- package/dist/src/ui/noninteractive/nonInteractiveUi.js +3 -1
- package/dist/src/ui/noninteractive/nonInteractiveUi.js.map +1 -1
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.js +5 -5
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.js.map +1 -1
- package/dist/src/ui/state/extensions.d.ts +47 -1
- package/dist/src/ui/state/extensions.js +68 -1
- package/dist/src/ui/state/extensions.js.map +1 -1
- package/dist/src/ui/themes/theme.js +2 -2
- package/dist/src/ui/themes/theme.js.map +1 -1
- package/dist/src/ui/types.d.ts +54 -3
- package/dist/src/ui/types.js +3 -0
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.js +1 -2
- package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.test.js +94 -91
- package/dist/src/ui/utils/MarkdownDisplay.test.js.map +1 -1
- package/dist/src/ui/utils/displayUtils.d.ts +1 -0
- package/dist/src/ui/utils/displayUtils.js +4 -1
- package/dist/src/ui/utils/displayUtils.js.map +1 -1
- package/dist/src/ui/utils/displayUtils.test.js +36 -17
- package/dist/src/ui/utils/displayUtils.test.js.map +1 -1
- package/dist/src/ui/utils/textUtils.d.ts +1 -0
- package/dist/src/ui/utils/textUtils.js +56 -0
- package/dist/src/ui/utils/textUtils.js.map +1 -1
- package/dist/src/ui/utils/textUtils.test.d.ts +6 -0
- package/dist/src/ui/utils/textUtils.test.js +132 -0
- package/dist/src/ui/utils/textUtils.test.js.map +1 -0
- package/dist/src/ui/utils/ui-sizing.d.ts +7 -0
- package/dist/src/ui/utils/ui-sizing.js +23 -0
- package/dist/src/ui/utils/ui-sizing.js.map +1 -0
- package/dist/src/utils/commentJson.js +95 -13
- package/dist/src/utils/commentJson.js.map +1 -1
- package/dist/src/utils/commentJson.test.js +161 -0
- package/dist/src/utils/commentJson.test.js.map +1 -1
- package/dist/src/utils/deepMerge.d.ts +2 -3
- package/dist/src/utils/errors.d.ts +8 -3
- package/dist/src/utils/errors.js +23 -13
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/errors.test.js +40 -46
- package/dist/src/utils/errors.test.js.map +1 -1
- package/dist/src/utils/events.d.ts +2 -1
- package/dist/src/utils/events.js +1 -0
- package/dist/src/utils/events.js.map +1 -1
- package/dist/src/utils/handleAutoUpdate.js +4 -1
- package/dist/src/utils/handleAutoUpdate.js.map +1 -1
- package/dist/src/utils/installationInfo.d.ts +1 -0
- package/dist/src/utils/installationInfo.js +2 -0
- package/dist/src/utils/installationInfo.js.map +1 -1
- package/dist/src/utils/math.d.ts +13 -0
- package/dist/src/utils/math.js +14 -0
- package/dist/src/utils/math.js.map +1 -0
- package/dist/src/utils/relaunch.d.ts +7 -0
- package/dist/src/utils/relaunch.js +57 -0
- package/dist/src/utils/relaunch.js.map +1 -0
- package/dist/src/utils/relaunch.test.d.ts +6 -0
- package/dist/src/utils/relaunch.test.js +273 -0
- package/dist/src/utils/relaunch.test.js.map +1 -0
- package/dist/src/utils/sandbox.js +31 -17
- package/dist/src/utils/sandbox.js.map +1 -1
- package/dist/src/utils/sessionCleanup.d.ts +22 -0
- package/dist/src/utils/sessionCleanup.integration.test.d.ts +6 -0
- package/dist/src/utils/sessionCleanup.integration.test.js +182 -0
- package/dist/src/utils/sessionCleanup.integration.test.js.map +1 -0
- package/dist/src/utils/sessionCleanup.js +214 -0
- package/dist/src/utils/sessionCleanup.js.map +1 -0
- package/dist/src/utils/sessionCleanup.test.d.ts +6 -0
- package/dist/src/utils/sessionCleanup.test.js +1232 -0
- package/dist/src/utils/sessionCleanup.test.js.map +1 -0
- package/dist/src/utils/sessionUtils.d.ts +37 -0
- package/dist/src/utils/sessionUtils.js +71 -0
- package/dist/src/utils/sessionUtils.js.map +1 -0
- package/dist/src/utils/windowTitle.d.ts +12 -0
- package/dist/src/utils/windowTitle.js +19 -0
- package/dist/src/utils/windowTitle.js.map +1 -0
- package/dist/src/utils/windowTitle.test.d.ts +6 -0
- package/dist/src/utils/windowTitle.test.js +49 -0
- package/dist/src/utils/windowTitle.test.js.map +1 -0
- package/dist/src/validateNonInterActiveAuth.js +6 -7
- package/dist/src/validateNonInterActiveAuth.js.map +1 -1
- package/dist/src/zed-integration/acp.js +1 -2
- package/dist/src/zed-integration/acp.js.map +1 -1
- package/dist/src/zed-integration/fileSystemService.d.ts +1 -0
- package/dist/src/zed-integration/fileSystemService.js +3 -0
- package/dist/src/zed-integration/fileSystemService.js.map +1 -1
- package/dist/src/zed-integration/schema.d.ts +70 -70
- package/dist/src/zed-integration/zedIntegration.d.ts +9 -3
- package/dist/src/zed-integration/zedIntegration.js +23 -28
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -5
- package/dist/src/commands/extensions/examples/mcp-server/example.js +0 -46
- package/dist/src/commands/extensions/examples/mcp-server/example.js.map +0 -1
- package/dist/src/ui/contexts/FocusContext.d.ts +0 -7
- package/dist/src/ui/contexts/FocusContext.js +0 -9
- package/dist/src/ui/contexts/FocusContext.js.map +0 -1
- /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,
|
|
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
|
|
60
|
+
await installOrUpdateExtension(installMetadata, requestConsent);
|
|
59
61
|
}
|
|
60
62
|
catch (_) {
|
|
61
|
-
failedInstallNames.push(extension.
|
|
63
|
+
failedInstallNames.push(extension.name);
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
return failedInstallNames;
|
|
65
67
|
}
|
|
66
|
-
function
|
|
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
|
-
|
|
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 (
|
|
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.
|
|
89
|
-
|
|
90
|
-
uniqueExtensions.set(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.
|
|
100
|
-
uniqueExtensions.set(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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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:
|
|
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,
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
|
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()
|
|
264
|
+
resolve(['y', ''].includes(answer.trim().toLowerCase()));
|
|
249
265
|
});
|
|
250
266
|
});
|
|
251
267
|
}
|
|
252
|
-
|
|
253
|
-
|
|
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
|
|
273
|
-
|
|
274
|
-
installMetadata.
|
|
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 =
|
|
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
|
-
|
|
301
|
-
|
|
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
|
-
|
|
323
|
-
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
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
|
-
|
|
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
|
-
|
|
404
|
+
const source = mcpServer.httpUrl ??
|
|
405
|
+
`${mcpServer.command || ''}${mcpServer.args ? ' ' + mcpServer.args.join(' ') : ''}`;
|
|
406
|
+
output.push(` * ${key} (${isLocal ? 'local' : 'remote'}): ${source}`);
|
|
355
407
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
372
|
-
|
|
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
|
-
|
|
378
|
-
return null;
|
|
379
|
-
}
|
|
462
|
+
validateName(config.name);
|
|
380
463
|
return config;
|
|
381
464
|
}
|
|
382
|
-
catch (
|
|
383
|
-
|
|
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.
|
|
390
|
-
extensionIdentifier.toLowerCase() ||
|
|
471
|
+
const extensionName = installedExtensions.find((installed) => installed.name.toLowerCase() === extensionIdentifier.toLowerCase() ||
|
|
391
472
|
installed.installMetadata?.source.toLowerCase() ===
|
|
392
|
-
extensionIdentifier.toLowerCase())?.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
515
|
+
if (extension.mcpServers) {
|
|
421
516
|
output += `\n MCP servers:`;
|
|
422
|
-
Object.keys(extension.
|
|
517
|
+
Object.keys(extension.mcpServers).forEach((key) => {
|
|
423
518
|
output += `\n ${key}`;
|
|
424
519
|
});
|
|
425
520
|
}
|
|
426
|
-
if (extension.
|
|
521
|
+
if (extension.excludeTools) {
|
|
427
522
|
output += `\n Excluded tools:`;
|
|
428
|
-
extension.
|
|
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
|
|
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
|