@google/gemini-cli 0.24.0-nightly.20260103.30f5c4af4 → 0.25.0-nightly.20260112.15891721a
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 +1 -1
- package/dist/google-gemini-cli-0.25.0-nightly.20260107.59a18e710.tgz +0 -0
- package/dist/package.json +3 -3
- package/dist/src/commands/extensions/configure.d.ts +13 -0
- package/dist/src/commands/extensions/configure.js +124 -0
- package/dist/src/commands/extensions/configure.js.map +1 -0
- package/dist/src/commands/extensions/configure.test.d.ts +1 -0
- package/dist/src/commands/extensions/configure.test.js +197 -0
- package/dist/src/commands/extensions/configure.test.js.map +1 -0
- package/dist/src/commands/extensions/examples/hooks/gemini-extension.json +4 -0
- package/dist/src/commands/extensions/examples/hooks/hooks/hooks.json +14 -0
- package/dist/src/commands/extensions/examples/hooks/scripts/on-start.js +8 -0
- package/dist/src/commands/extensions/examples/mcp-server/README.md +35 -0
- package/dist/src/commands/extensions/examples/mcp-server/example.js +36 -22
- package/dist/src/commands/extensions/examples/mcp-server/gemini-extension.json +1 -1
- package/dist/src/commands/extensions/examples/mcp-server/package.json +0 -7
- package/dist/src/commands/extensions/examples/skills/gemini-extension.json +4 -0
- package/dist/src/commands/extensions/examples/skills/skills/greeter/SKILL.md +7 -0
- package/dist/src/commands/extensions/utils.d.ts +1 -0
- package/dist/src/commands/extensions/utils.js +5 -1
- package/dist/src/commands/extensions/utils.js.map +1 -1
- package/dist/src/commands/extensions.js +2 -2
- package/dist/src/commands/extensions.js.map +1 -1
- package/dist/src/commands/hooks/migrate.js +1 -1
- package/dist/src/commands/hooks/migrate.js.map +1 -1
- package/dist/src/commands/hooks/migrate.test.js +1 -1
- package/dist/src/commands/hooks/migrate.test.js.map +1 -1
- package/dist/src/commands/skills/disable.d.ts +14 -0
- package/dist/src/commands/skills/disable.js +45 -0
- package/dist/src/commands/skills/disable.js.map +1 -0
- package/dist/src/commands/skills/disable.test.d.ts +6 -0
- package/dist/src/commands/skills/disable.test.js +80 -0
- package/dist/src/commands/skills/disable.test.js.map +1 -0
- package/dist/src/commands/skills/enable.d.ts +12 -0
- package/dist/src/commands/skills/enable.js +35 -0
- package/dist/src/commands/skills/enable.js.map +1 -0
- package/dist/src/commands/skills/enable.test.d.ts +6 -0
- package/dist/src/commands/skills/enable.test.js +107 -0
- package/dist/src/commands/skills/enable.test.js.map +1 -0
- package/dist/src/commands/skills/list.d.ts +10 -0
- package/dist/src/commands/skills/list.js +60 -0
- package/dist/src/commands/skills/list.js.map +1 -0
- package/dist/src/commands/skills/list.test.d.ts +6 -0
- package/dist/src/commands/skills/list.test.js +136 -0
- package/dist/src/commands/skills/list.test.js.map +1 -0
- package/dist/src/commands/{extensions/settings.d.ts → skills.d.ts} +2 -2
- package/dist/src/commands/skills.js +26 -0
- package/dist/src/commands/skills.js.map +1 -0
- package/dist/src/commands/skills.test.d.ts +6 -0
- package/dist/src/commands/skills.test.js +49 -0
- package/dist/src/commands/skills.test.js.map +1 -0
- package/dist/src/config/config.js +61 -23
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +220 -1
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/extension-manager-scope.test.js +144 -0
- package/dist/src/config/extension-manager-scope.test.js.map +1 -0
- package/dist/src/config/extension-manager-skills.test.js +146 -0
- package/dist/src/config/extension-manager-skills.test.js.map +1 -0
- package/dist/src/config/extension-manager.js +80 -28
- package/dist/src/config/extension-manager.js.map +1 -1
- package/dist/src/config/extension.test.js +81 -10
- package/dist/src/config/extension.test.js.map +1 -1
- package/dist/src/config/extensions/consent.d.ts +5 -3
- package/dist/src/config/extensions/consent.js +30 -7
- package/dist/src/config/extensions/consent.js.map +1 -1
- package/dist/src/config/extensions/consent.test.js +97 -3
- package/dist/src/config/extensions/consent.test.js.map +1 -1
- package/dist/src/config/extensions/extensionSettings.d.ts +5 -3
- package/dist/src/config/extensions/extensionSettings.js +32 -12
- package/dist/src/config/extensions/extensionSettings.js.map +1 -1
- package/dist/src/config/extensions/extensionSettings.test.js +8 -8
- package/dist/src/config/extensions/extensionSettings.test.js.map +1 -1
- package/dist/src/config/extensions/extensionUpdates.test.d.ts +6 -0
- package/dist/src/config/extensions/extensionUpdates.test.js +227 -0
- package/dist/src/config/extensions/extensionUpdates.test.js.map +1 -0
- package/dist/src/config/extensions/storage.js +2 -2
- package/dist/src/config/extensions/storage.js.map +1 -1
- package/dist/src/config/settings.d.ts +6 -7
- package/dist/src/config/settings.js +61 -238
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/config/settings.test.js +228 -706
- package/dist/src/config/settings.test.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +119 -15
- package/dist/src/config/settingsSchema.js +123 -15
- 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/settings_validation_warning.test.js +10 -5
- package/dist/src/config/settings_validation_warning.test.js.map +1 -1
- package/dist/src/config/trustedFolders.js +1 -2
- package/dist/src/config/trustedFolders.js.map +1 -1
- package/dist/src/config/trustedFolders.test.js +7 -0
- package/dist/src/config/trustedFolders.test.js.map +1 -1
- package/dist/src/gemini.js +67 -60
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +36 -83
- package/dist/src/gemini.test.js.map +1 -1
- package/dist/src/gemini_cleanup.test.js +2 -0
- package/dist/src/gemini_cleanup.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/nonInteractiveCli.js +24 -0
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/nonInteractiveCli.test.js +46 -1
- package/dist/src/nonInteractiveCli.test.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.d.ts +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +45 -6
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.test.js +58 -1
- package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
- package/dist/src/services/prompt-processors/shellProcessor.js +13 -11
- package/dist/src/services/prompt-processors/shellProcessor.js.map +1 -1
- package/dist/src/services/prompt-processors/shellProcessor.test.js +93 -61
- package/dist/src/services/prompt-processors/shellProcessor.test.js.map +1 -1
- package/dist/src/test-utils/render.js +2 -2
- package/dist/src/test-utils/render.js.map +1 -1
- package/dist/src/ui/App.test.js +2 -2
- package/dist/src/ui/App.test.js.map +1 -1
- package/dist/src/ui/AppContainer.js +47 -22
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/AppContainer.test.js +16 -0
- package/dist/src/ui/AppContainer.test.js.map +1 -1
- package/dist/src/ui/commands/agentsCommand.d.ts +7 -0
- package/dist/src/ui/commands/agentsCommand.js +75 -0
- package/dist/src/ui/commands/agentsCommand.js.map +1 -0
- package/dist/src/ui/commands/agentsCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/agentsCommand.test.js +91 -0
- package/dist/src/ui/commands/agentsCommand.test.js.map +1 -0
- package/dist/src/ui/commands/bugCommand.js +27 -4
- package/dist/src/ui/commands/bugCommand.js.map +1 -1
- package/dist/src/ui/commands/bugCommand.test.js +70 -2
- package/dist/src/ui/commands/bugCommand.test.js.map +1 -1
- package/dist/src/ui/commands/chatCommand.d.ts +1 -2
- package/dist/src/ui/commands/chatCommand.js +38 -30
- package/dist/src/ui/commands/chatCommand.js.map +1 -1
- package/dist/src/ui/commands/chatCommand.test.js +79 -52
- package/dist/src/ui/commands/chatCommand.test.js.map +1 -1
- package/dist/src/ui/commands/clearCommand.js +12 -8
- package/dist/src/ui/commands/clearCommand.js.map +1 -1
- package/dist/src/ui/commands/clearCommand.test.js +4 -0
- package/dist/src/ui/commands/clearCommand.test.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.js +70 -2
- package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.test.js +70 -3
- package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -1
- package/dist/src/ui/commands/hooksCommand.js +153 -18
- package/dist/src/ui/commands/hooksCommand.js.map +1 -1
- package/dist/src/ui/commands/hooksCommand.test.js +186 -15
- package/dist/src/ui/commands/hooksCommand.test.js.map +1 -1
- package/dist/src/ui/commands/mcpCommand.test.js +10 -2
- package/dist/src/ui/commands/mcpCommand.test.js.map +1 -1
- package/dist/src/ui/commands/skillsCommand.d.ts +1 -1
- package/dist/src/ui/commands/skillsCommand.js +104 -32
- package/dist/src/ui/commands/skillsCommand.js.map +1 -1
- package/dist/src/ui/commands/skillsCommand.test.d.ts +1 -1
- package/dist/src/ui/commands/skillsCommand.test.js +230 -8
- package/dist/src/ui/commands/skillsCommand.test.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +2 -1
- package/dist/src/ui/commands/types.js +1 -0
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/components/Composer.js +4 -6
- package/dist/src/ui/components/Composer.js.map +1 -1
- package/dist/src/ui/components/Composer.test.js +13 -0
- package/dist/src/ui/components/Composer.test.js.map +1 -1
- package/dist/src/ui/components/ContextSummaryDisplay.test.js +54 -26
- package/dist/src/ui/components/ContextSummaryDisplay.test.js.map +1 -1
- package/dist/src/ui/components/DialogManager.js +1 -1
- package/dist/src/ui/components/DialogManager.js.map +1 -1
- package/dist/src/ui/components/FolderTrustDialog.js +19 -14
- package/dist/src/ui/components/FolderTrustDialog.js.map +1 -1
- package/dist/src/ui/components/FolderTrustDialog.test.js +12 -2
- package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
- package/dist/src/ui/components/Footer.js +4 -4
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js +2 -1
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.test.js +21 -0
- package/dist/src/ui/components/HistoryItemDisplay.test.js.map +1 -1
- package/dist/src/ui/components/HookStatusDisplay.d.ts +12 -0
- package/dist/src/ui/components/HookStatusDisplay.js +20 -0
- package/dist/src/ui/components/HookStatusDisplay.js.map +1 -0
- package/dist/src/ui/components/HookStatusDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/HookStatusDisplay.test.js +51 -0
- package/dist/src/ui/components/HookStatusDisplay.test.js.map +1 -0
- package/dist/src/ui/components/InputPrompt.js +14 -6
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.test.js +19 -2
- package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
- package/dist/src/ui/components/MainContent.js +6 -1
- package/dist/src/ui/components/MainContent.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.js +7 -3
- package/dist/src/ui/components/ModelDialog.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.test.js +16 -3
- package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
- package/dist/src/ui/components/Notifications.js +2 -3
- package/dist/src/ui/components/Notifications.js.map +1 -1
- package/dist/src/ui/components/Notifications.test.js +1 -0
- package/dist/src/ui/components/Notifications.test.js.map +1 -1
- package/dist/src/ui/components/ProQuotaDialog.d.ts +1 -3
- package/dist/src/ui/components/ProQuotaDialog.js +8 -21
- package/dist/src/ui/components/ProQuotaDialog.js.map +1 -1
- package/dist/src/ui/components/ProQuotaDialog.test.js +31 -11
- package/dist/src/ui/components/ProQuotaDialog.test.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.js +33 -13
- package/dist/src/ui/components/SettingsDialog.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.test.js +15 -1
- package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
- package/dist/src/ui/components/StatusDisplay.d.ts +11 -0
- package/dist/src/ui/components/StatusDisplay.js +40 -0
- package/dist/src/ui/components/StatusDisplay.js.map +1 -0
- package/dist/src/ui/components/StatusDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/StatusDisplay.test.js +144 -0
- package/dist/src/ui/components/StatusDisplay.test.js.map +1 -0
- package/dist/src/ui/components/SuggestionsDisplay.js +7 -2
- package/dist/src/ui/components/SuggestionsDisplay.js.map +1 -1
- package/dist/src/ui/components/ThemeDialog.test.js +1 -1
- package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessage.test.js +1 -0
- package/dist/src/ui/components/messages/ToolMessage.test.js.map +1 -1
- package/dist/src/ui/components/views/AgentsStatus.d.ts +13 -0
- package/dist/src/ui/components/views/AgentsStatus.js +23 -0
- package/dist/src/ui/components/views/AgentsStatus.js.map +1 -0
- package/dist/src/ui/components/views/ExtensionsList.js +2 -1
- package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
- package/dist/src/ui/components/views/ExtensionsList.test.js +12 -1
- package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
- package/dist/src/ui/components/views/HooksList.js +25 -16
- package/dist/src/ui/components/views/HooksList.js.map +1 -1
- package/dist/src/ui/components/views/SkillsList.js +9 -7
- package/dist/src/ui/components/views/SkillsList.js.map +1 -1
- package/dist/src/ui/components/views/SkillsList.test.js +37 -4
- package/dist/src/ui/components/views/SkillsList.test.js.map +1 -1
- package/dist/src/ui/constants/tips.js +0 -1
- package/dist/src/ui/constants/tips.js.map +1 -1
- package/dist/src/ui/constants.d.ts +2 -0
- package/dist/src/ui/constants.js +2 -0
- package/dist/src/ui/constants.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.d.ts +1 -0
- package/dist/src/ui/contexts/KeypressContext.js +56 -6
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.test.js +99 -8
- package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
- package/dist/src/ui/contexts/UIStateContext.d.ts +3 -1
- package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.js +16 -2
- package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.test.js +9 -3
- package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor_agents.test.d.ts +6 -0
- package/dist/src/ui/hooks/atCommandProcessor_agents.test.js +183 -0
- package/dist/src/ui/hooks/atCommandProcessor_agents.test.js.map +1 -0
- package/dist/src/ui/hooks/slashCommandProcessor.test.js +1 -0
- package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/useAtCompletion.js +31 -0
- package/dist/src/ui/hooks/useAtCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useAtCompletion_agents.test.d.ts +6 -0
- package/dist/src/ui/hooks/useAtCompletion_agents.test.js +85 -0
- package/dist/src/ui/hooks/useAtCompletion_agents.test.js.map +1 -0
- package/dist/src/ui/hooks/useCommandCompletion.js +12 -8
- package/dist/src/ui/hooks/useCommandCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useCommandCompletion.test.js +81 -0
- package/dist/src/ui/hooks/useCommandCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionUpdates.test.js +7 -0
- package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
- package/dist/src/ui/hooks/useFolderTrust.js +14 -19
- package/dist/src/ui/hooks/useFolderTrust.js.map +1 -1
- package/dist/src/ui/hooks/useFolderTrust.test.js +30 -22
- package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +29 -0
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.test.js +82 -0
- package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
- package/dist/src/ui/hooks/useHookDisplayState.d.ts +7 -0
- package/dist/src/ui/hooks/useHookDisplayState.js +83 -0
- package/dist/src/ui/hooks/useHookDisplayState.js.map +1 -0
- package/dist/src/ui/hooks/useHookDisplayState.test.d.ts +6 -0
- package/dist/src/ui/hooks/useHookDisplayState.test.js +180 -0
- package/dist/src/ui/hooks/useHookDisplayState.test.js.map +1 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js +10 -3
- package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js.map +1 -1
- package/dist/src/ui/hooks/useQuotaAndFallback.js +15 -10
- package/dist/src/ui/hooks/useQuotaAndFallback.js.map +1 -1
- package/dist/src/ui/hooks/useQuotaAndFallback.test.js +13 -8
- package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -1
- package/dist/src/ui/hooks/useRewind.d.ts +14 -0
- package/dist/src/ui/hooks/useRewind.js +31 -0
- package/dist/src/ui/hooks/useRewind.js.map +1 -0
- package/dist/src/ui/hooks/useRewind.test.d.ts +6 -0
- package/dist/src/ui/hooks/useRewind.test.js +100 -0
- package/dist/src/ui/hooks/useRewind.test.js.map +1 -0
- package/dist/src/ui/hooks/useToolScheduler.test.js +11 -2
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/themes/theme-manager.js +2 -3
- package/dist/src/ui/themes/theme-manager.js.map +1 -1
- package/dist/src/ui/themes/theme-manager.test.js +7 -0
- package/dist/src/ui/themes/theme-manager.test.js.map +1 -1
- package/dist/src/ui/types.d.ts +15 -8
- package/dist/src/ui/types.js +1 -0
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/ui/utils/CodeColorizer.test.js +1 -1
- package/dist/src/ui/utils/CodeColorizer.test.js.map +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.test.js +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.test.js.map +1 -1
- package/dist/src/ui/utils/commandUtils.js +12 -7
- package/dist/src/ui/utils/commandUtils.js.map +1 -1
- package/dist/src/ui/utils/commandUtils.test.js +24 -0
- package/dist/src/ui/utils/commandUtils.test.js.map +1 -1
- package/dist/src/ui/utils/directoryUtils.js +4 -4
- package/dist/src/ui/utils/directoryUtils.js.map +1 -1
- package/dist/src/ui/utils/directoryUtils.test.js +1 -0
- package/dist/src/ui/utils/directoryUtils.test.js.map +1 -1
- package/dist/src/ui/utils/historyExportUtils.d.ts +21 -0
- package/dist/src/ui/utils/historyExportUtils.js +59 -0
- package/dist/src/ui/utils/historyExportUtils.js.map +1 -0
- package/dist/src/ui/utils/rewindFileOps.d.ts +47 -0
- package/dist/src/ui/utils/rewindFileOps.js +190 -0
- package/dist/src/ui/utils/rewindFileOps.js.map +1 -0
- package/dist/src/ui/utils/rewindFileOps.test.d.ts +6 -0
- package/dist/src/ui/utils/rewindFileOps.test.js +375 -0
- package/dist/src/ui/utils/rewindFileOps.test.js.map +1 -0
- package/dist/src/ui/utils/terminalCapabilityManager.d.ts +4 -8
- package/dist/src/ui/utils/terminalCapabilityManager.js +43 -72
- package/dist/src/ui/utils/terminalCapabilityManager.js.map +1 -1
- package/dist/src/ui/utils/terminalCapabilityManager.test.js +20 -6
- package/dist/src/ui/utils/terminalCapabilityManager.test.js.map +1 -1
- package/dist/src/ui/utils/terminalSetup.d.ts +1 -1
- package/dist/src/ui/utils/terminalSetup.js +19 -4
- package/dist/src/ui/utils/terminalSetup.js.map +1 -1
- package/dist/src/ui/utils/terminalSetup.test.js +7 -0
- package/dist/src/ui/utils/terminalSetup.test.js.map +1 -1
- package/dist/src/ui/utils/textUtils.js +9 -1
- package/dist/src/ui/utils/textUtils.js.map +1 -1
- package/dist/src/ui/utils/textUtils.test.js +12 -1
- package/dist/src/ui/utils/textUtils.test.js.map +1 -1
- package/dist/src/ui/utils/ui-sizing.js +1 -1
- package/dist/src/ui/utils/ui-sizing.js.map +1 -1
- package/dist/src/ui/utils/ui-sizing.test.js +2 -2
- package/dist/src/ui/utils/ui-sizing.test.js.map +1 -1
- package/dist/src/utils/activityLogger.d.ts +47 -0
- package/dist/src/utils/activityLogger.js +297 -0
- package/dist/src/utils/activityLogger.js.map +1 -0
- package/dist/src/utils/cleanup.d.ts +5 -0
- package/dist/src/utils/cleanup.js +17 -0
- package/dist/src/utils/cleanup.js.map +1 -1
- package/dist/src/utils/cleanup.test.js +21 -31
- package/dist/src/utils/cleanup.test.js.map +1 -1
- package/dist/src/utils/resolvePath.js +3 -3
- package/dist/src/utils/resolvePath.js.map +1 -1
- package/dist/src/utils/resolvePath.test.js +3 -0
- package/dist/src/utils/resolvePath.test.js.map +1 -1
- package/dist/src/utils/sandbox.js +17 -10
- package/dist/src/utils/sandbox.js.map +1 -1
- package/dist/src/utils/sandbox.test.js +17 -5
- package/dist/src/utils/sandbox.test.js.map +1 -1
- package/dist/src/utils/settingsUtils.js +0 -5
- package/dist/src/utils/settingsUtils.js.map +1 -1
- package/dist/src/utils/skillSettings.d.ts +33 -0
- package/dist/src/utils/skillSettings.js +101 -0
- package/dist/src/utils/skillSettings.js.map +1 -0
- package/dist/src/utils/skillUtils.d.ts +16 -0
- package/dist/src/utils/skillUtils.js +51 -0
- package/dist/src/utils/skillUtils.js.map +1 -0
- package/dist/src/utils/userStartupWarnings.d.ts +2 -1
- package/dist/src/utils/userStartupWarnings.js +17 -7
- package/dist/src/utils/userStartupWarnings.js.map +1 -1
- package/dist/src/utils/userStartupWarnings.test.js +37 -6
- package/dist/src/utils/userStartupWarnings.test.js.map +1 -1
- package/dist/src/zed-integration/zedIntegration.js +1 -1
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/src/zed-integration/zedIntegration.test.js +12 -0
- package/dist/src/zed-integration/zedIntegration.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/dist/google-gemini-cli-0.24.0-nightly.20251227.37be16243.tgz +0 -0
- package/dist/src/commands/extensions/examples/mcp-server/example.js.map +0 -1
- package/dist/src/commands/extensions/examples/mcp-server/example.test.js +0 -111
- package/dist/src/commands/extensions/examples/mcp-server/example.test.js.map +0 -1
- package/dist/src/commands/extensions/examples/mcp-server/example.test.ts +0 -135
- package/dist/src/commands/extensions/examples/mcp-server/example.ts +0 -60
- package/dist/src/commands/extensions/examples/mcp-server/tsconfig.json +0 -13
- package/dist/src/commands/extensions/settings.js +0 -111
- package/dist/src/commands/extensions/settings.js.map +0 -1
- package/dist/src/ui/hooks/useBracketedPaste.d.ts +0 -12
- package/dist/src/ui/hooks/useBracketedPaste.js +0 -31
- package/dist/src/ui/hooks/useBracketedPaste.js.map +0 -1
- package/dist/src/ui/utils/bracketedPaste.d.ts +0 -7
- package/dist/src/ui/utils/bracketedPaste.js +0 -15
- package/dist/src/ui/utils/bracketedPaste.js.map +0 -1
- /package/dist/src/{commands/extensions/examples/mcp-server/example.d.ts → config/extension-manager-scope.test.d.ts} +0 -0
- /package/dist/src/{commands/extensions/examples/mcp-server/example.test.d.ts → config/extension-manager-skills.test.d.ts} +0 -0
|
@@ -29,18 +29,25 @@ vi.mock('./trustedFolders.js', () => ({
|
|
|
29
29
|
.fn()
|
|
30
30
|
.mockReturnValue({ isTrusted: true, source: 'file' }),
|
|
31
31
|
}));
|
|
32
|
+
vi.mock('./settingsSchema.js', async (importOriginal) => {
|
|
33
|
+
const actual = await importOriginal();
|
|
34
|
+
return {
|
|
35
|
+
...actual,
|
|
36
|
+
getSettingsSchema: vi.fn(actual.getSettingsSchema),
|
|
37
|
+
};
|
|
38
|
+
});
|
|
32
39
|
// NOW import everything else, including the (now effectively re-exported) settings.js
|
|
33
|
-
import
|
|
40
|
+
import * as pathActual from 'node:path'; // Restored for MOCK_WORKSPACE_SETTINGS_PATH
|
|
34
41
|
import { describe, it, expect, vi, beforeEach, afterEach, } from 'vitest';
|
|
35
42
|
import * as fs from 'node:fs'; // fs will be mocked separately
|
|
36
43
|
import stripJsonComments from 'strip-json-comments'; // Will be mocked separately
|
|
37
44
|
import { isWorkspaceTrusted } from './trustedFolders.js';
|
|
38
45
|
// These imports will get the versions from the vi.mock('./settings.js', ...) factory.
|
|
39
46
|
import { loadSettings, USER_SETTINGS_PATH, // This IS the mocked path.
|
|
40
|
-
getSystemSettingsPath, getSystemDefaultsPath,
|
|
47
|
+
getSystemSettingsPath, getSystemDefaultsPath, saveSettings, getDefaultsFromSchema, } from './settings.js';
|
|
41
48
|
import { FatalConfigError, GEMINI_DIR } from '@google/gemini-cli-core';
|
|
42
|
-
import { ExtensionManager } from './extension-manager.js';
|
|
43
49
|
import { updateSettingsFilePreservingFormat } from '../utils/commentJson.js';
|
|
50
|
+
import { getSettingsSchema, MergeStrategy, } from './settingsSchema.js';
|
|
44
51
|
const MOCK_WORKSPACE_DIR = '/mock/workspace';
|
|
45
52
|
// Use the (mocked) GEMINI_DIR for consistency
|
|
46
53
|
const MOCK_WORKSPACE_SETTINGS_PATH = pathActual.join(MOCK_WORKSPACE_DIR, GEMINI_DIR, 'settings.json');
|
|
@@ -60,6 +67,7 @@ vi.mock('fs', async (importOriginal) => {
|
|
|
60
67
|
vi.mock('./extension.js');
|
|
61
68
|
const mockCoreEvents = vi.hoisted(() => ({
|
|
62
69
|
emitFeedback: vi.fn(),
|
|
70
|
+
emitSettingsChanged: vi.fn(),
|
|
63
71
|
}));
|
|
64
72
|
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
|
65
73
|
const actual = await importOriginal();
|
|
@@ -97,13 +105,6 @@ describe('Settings Loading and Merging', () => {
|
|
|
97
105
|
vi.restoreAllMocks();
|
|
98
106
|
});
|
|
99
107
|
describe('loadSettings', () => {
|
|
100
|
-
it('should load empty settings if no files exist', () => {
|
|
101
|
-
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
102
|
-
expect(settings.system.settings).toEqual({});
|
|
103
|
-
expect(settings.user.settings).toEqual({});
|
|
104
|
-
expect(settings.workspace.settings).toEqual({});
|
|
105
|
-
expect(settings.merged).toEqual({});
|
|
106
|
-
});
|
|
107
108
|
it.each([
|
|
108
109
|
{
|
|
109
110
|
scope: 'system',
|
|
@@ -139,7 +140,7 @@ describe('Settings Loading and Merging', () => {
|
|
|
139
140
|
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
140
141
|
expect(fs.readFileSync).toHaveBeenCalledWith(path, 'utf-8');
|
|
141
142
|
expect(settings[scope].settings).toEqual(content);
|
|
142
|
-
expect(settings.merged).
|
|
143
|
+
expect(settings.merged).toMatchObject(content);
|
|
143
144
|
});
|
|
144
145
|
it('should merge system, user and workspace settings, with system taking precedence over workspace, and workspace over user', () => {
|
|
145
146
|
mockFsExistsSync.mockImplementation((p) => p === getSystemSettingsPath() ||
|
|
@@ -193,7 +194,7 @@ describe('Settings Loading and Merging', () => {
|
|
|
193
194
|
expect(settings.system.settings).toEqual(systemSettingsContent);
|
|
194
195
|
expect(settings.user.settings).toEqual(userSettingsContent);
|
|
195
196
|
expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
|
|
196
|
-
expect(settings.merged).
|
|
197
|
+
expect(settings.merged).toMatchObject({
|
|
197
198
|
ui: {
|
|
198
199
|
theme: 'system-theme',
|
|
199
200
|
},
|
|
@@ -210,137 +211,26 @@ describe('Settings Loading and Merging', () => {
|
|
|
210
211
|
},
|
|
211
212
|
});
|
|
212
213
|
});
|
|
213
|
-
it('should
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
contextFileName: 'LEGACY_CONTEXT.md',
|
|
219
|
-
model: 'gemini-2.5-pro',
|
|
220
|
-
mcpServers: {
|
|
221
|
-
'legacy-server-1': {
|
|
222
|
-
command: 'npm',
|
|
223
|
-
args: ['run', 'start:server1'],
|
|
224
|
-
description: 'Legacy Server 1',
|
|
225
|
-
},
|
|
226
|
-
'legacy-server-2': {
|
|
227
|
-
command: 'node',
|
|
228
|
-
args: ['server2.js'],
|
|
229
|
-
description: 'Legacy Server 2',
|
|
230
|
-
},
|
|
231
|
-
},
|
|
232
|
-
allowMCPServers: ['legacy-server-1'],
|
|
233
|
-
someUnrecognizedSetting: 'should-be-preserved',
|
|
234
|
-
};
|
|
235
|
-
fs.readFileSync.mockImplementation((p) => {
|
|
236
|
-
if (p === USER_SETTINGS_PATH)
|
|
237
|
-
return JSON.stringify(legacySettingsContent);
|
|
238
|
-
return '{}';
|
|
239
|
-
});
|
|
240
|
-
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
241
|
-
expect(settings.merged).toEqual({
|
|
242
|
-
ui: {
|
|
243
|
-
theme: 'legacy-dark',
|
|
244
|
-
},
|
|
245
|
-
general: {
|
|
246
|
-
vimMode: true,
|
|
247
|
-
},
|
|
214
|
+
it('should merge all settings files with the correct precedence', () => {
|
|
215
|
+
// Mock schema to test defaults application
|
|
216
|
+
const mockSchema = {
|
|
217
|
+
ui: { type: 'object', default: {}, properties: {} },
|
|
218
|
+
tools: { type: 'object', default: {}, properties: {} },
|
|
248
219
|
context: {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
description: 'Legacy Server 1',
|
|
259
|
-
},
|
|
260
|
-
'legacy-server-2': {
|
|
261
|
-
command: 'node',
|
|
262
|
-
args: ['server2.js'],
|
|
263
|
-
description: 'Legacy Server 2',
|
|
264
|
-
},
|
|
265
|
-
},
|
|
266
|
-
mcp: {
|
|
267
|
-
allowed: ['legacy-server-1'],
|
|
268
|
-
},
|
|
269
|
-
someUnrecognizedSetting: 'should-be-preserved',
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
it('should rewrite allowedTools to tools.allowed during migration', () => {
|
|
273
|
-
mockFsExistsSync.mockImplementation((p) => p === USER_SETTINGS_PATH);
|
|
274
|
-
const legacySettingsContent = {
|
|
275
|
-
allowedTools: ['fs', 'shell'],
|
|
276
|
-
};
|
|
277
|
-
fs.readFileSync.mockImplementation((p) => {
|
|
278
|
-
if (p === USER_SETTINGS_PATH)
|
|
279
|
-
return JSON.stringify(legacySettingsContent);
|
|
280
|
-
return '{}';
|
|
281
|
-
});
|
|
282
|
-
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
283
|
-
expect(settings.merged.tools?.allowed).toEqual(['fs', 'shell']);
|
|
284
|
-
expect(settings.merged['allowedTools']).toBeUndefined();
|
|
285
|
-
});
|
|
286
|
-
it('should allow V2 settings to override V1 settings when both are present (zombie setting fix)', () => {
|
|
287
|
-
mockFsExistsSync.mockImplementation((p) => p === USER_SETTINGS_PATH);
|
|
288
|
-
const mixedSettingsContent = {
|
|
289
|
-
// V1 setting (migrates to ui.accessibility.screenReader = true)
|
|
290
|
-
accessibility: {
|
|
291
|
-
screenReader: true,
|
|
292
|
-
},
|
|
293
|
-
// V2 setting (explicitly set to false)
|
|
294
|
-
ui: {
|
|
295
|
-
accessibility: {
|
|
296
|
-
screenReader: false,
|
|
220
|
+
type: 'object',
|
|
221
|
+
default: {},
|
|
222
|
+
properties: {
|
|
223
|
+
discoveryMaxDirs: { type: 'number', default: 200 },
|
|
224
|
+
includeDirectories: {
|
|
225
|
+
type: 'array',
|
|
226
|
+
default: [],
|
|
227
|
+
mergeStrategy: MergeStrategy.CONCAT,
|
|
228
|
+
},
|
|
297
229
|
},
|
|
298
230
|
},
|
|
231
|
+
mcpServers: { type: 'object', default: {} },
|
|
299
232
|
};
|
|
300
|
-
|
|
301
|
-
if (p === USER_SETTINGS_PATH)
|
|
302
|
-
return JSON.stringify(mixedSettingsContent);
|
|
303
|
-
return '{}';
|
|
304
|
-
});
|
|
305
|
-
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
306
|
-
// We expect the V2 setting (false) to win, NOT the migrated V1 setting (true)
|
|
307
|
-
expect(settings.merged.ui?.accessibility?.screenReader).toBe(false);
|
|
308
|
-
});
|
|
309
|
-
it('should correctly merge and migrate legacy array properties from multiple scopes', () => {
|
|
310
|
-
mockFsExistsSync.mockReturnValue(true);
|
|
311
|
-
const legacyUserSettings = {
|
|
312
|
-
includeDirectories: ['/user/dir'],
|
|
313
|
-
excludeTools: ['user-tool'],
|
|
314
|
-
excludedProjectEnvVars: ['USER_VAR'],
|
|
315
|
-
};
|
|
316
|
-
const legacyWorkspaceSettings = {
|
|
317
|
-
includeDirectories: ['/workspace/dir'],
|
|
318
|
-
excludeTools: ['workspace-tool'],
|
|
319
|
-
excludedProjectEnvVars: ['WORKSPACE_VAR', 'USER_VAR'],
|
|
320
|
-
};
|
|
321
|
-
fs.readFileSync.mockImplementation((p) => {
|
|
322
|
-
if (p === USER_SETTINGS_PATH)
|
|
323
|
-
return JSON.stringify(legacyUserSettings);
|
|
324
|
-
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
|
|
325
|
-
return JSON.stringify(legacyWorkspaceSettings);
|
|
326
|
-
return '{}';
|
|
327
|
-
});
|
|
328
|
-
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
329
|
-
// Verify includeDirectories are concatenated
|
|
330
|
-
expect(settings.merged.context?.includeDirectories).toEqual([
|
|
331
|
-
'/user/dir',
|
|
332
|
-
'/workspace/dir',
|
|
333
|
-
]);
|
|
334
|
-
// Verify excludeTools are concatenated and de-duped
|
|
335
|
-
expect(settings.merged.tools?.exclude).toEqual([
|
|
336
|
-
'user-tool',
|
|
337
|
-
'workspace-tool',
|
|
338
|
-
]);
|
|
339
|
-
// Verify excludedProjectEnvVars are concatenated and de-duped
|
|
340
|
-
expect(settings.merged.advanced?.excludedEnvVars).toEqual(expect.arrayContaining(['USER_VAR', 'WORKSPACE_VAR']));
|
|
341
|
-
expect(settings.merged.advanced?.excludedEnvVars).toHaveLength(2);
|
|
342
|
-
});
|
|
343
|
-
it('should merge all settings files with the correct precedence', () => {
|
|
233
|
+
getSettingsSchema.mockReturnValue(mockSchema);
|
|
344
234
|
mockFsExistsSync.mockReturnValue(true);
|
|
345
235
|
const systemDefaultsContent = {
|
|
346
236
|
ui: {
|
|
@@ -399,7 +289,7 @@ describe('Settings Loading and Merging', () => {
|
|
|
399
289
|
expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
|
|
400
290
|
expect(settings.merged).toEqual({
|
|
401
291
|
context: {
|
|
402
|
-
|
|
292
|
+
discoveryMaxDirs: 200,
|
|
403
293
|
includeDirectories: [
|
|
404
294
|
'/system/defaults/dir',
|
|
405
295
|
'/user/dir1',
|
|
@@ -407,14 +297,12 @@ describe('Settings Loading and Merging', () => {
|
|
|
407
297
|
'/workspace/dir',
|
|
408
298
|
'/system/dir',
|
|
409
299
|
],
|
|
300
|
+
fileName: 'WORKSPACE_CONTEXT.md',
|
|
410
301
|
},
|
|
302
|
+
mcpServers: {},
|
|
303
|
+
ui: { theme: 'system-theme' },
|
|
304
|
+
tools: { sandbox: false },
|
|
411
305
|
telemetry: false,
|
|
412
|
-
tools: {
|
|
413
|
-
sandbox: false,
|
|
414
|
-
},
|
|
415
|
-
ui: {
|
|
416
|
-
theme: 'system-theme',
|
|
417
|
-
},
|
|
418
306
|
});
|
|
419
307
|
});
|
|
420
308
|
it('should use folderTrust from workspace settings when trusted', () => {
|
|
@@ -533,7 +421,7 @@ describe('Settings Loading and Merging', () => {
|
|
|
533
421
|
},
|
|
534
422
|
expected: {
|
|
535
423
|
key: 'advanced.excludedEnvVars',
|
|
536
|
-
value: ['DEBUG', 'NODE_ENV', 'CUSTOM_VAR'],
|
|
424
|
+
value: ['DEBUG', 'DEBUG_MODE', 'NODE_ENV', 'CUSTOM_VAR'],
|
|
537
425
|
},
|
|
538
426
|
},
|
|
539
427
|
{
|
|
@@ -544,7 +432,7 @@ describe('Settings Loading and Merging', () => {
|
|
|
544
432
|
},
|
|
545
433
|
expected: {
|
|
546
434
|
key: 'advanced.excludedEnvVars',
|
|
547
|
-
value: ['WORKSPACE_DEBUG', 'WORKSPACE_VAR'],
|
|
435
|
+
value: ['DEBUG', 'DEBUG_MODE', 'WORKSPACE_DEBUG', 'WORKSPACE_VAR'],
|
|
548
436
|
},
|
|
549
437
|
},
|
|
550
438
|
])('should handle $description correctly', ({ path, content, expected }) => {
|
|
@@ -591,6 +479,7 @@ describe('Settings Loading and Merging', () => {
|
|
|
591
479
|
]);
|
|
592
480
|
expect(settings.merged.advanced?.excludedEnvVars).toEqual([
|
|
593
481
|
'DEBUG',
|
|
482
|
+
'DEBUG_MODE',
|
|
594
483
|
'NODE_ENV',
|
|
595
484
|
'USER_VAR',
|
|
596
485
|
'WORKSPACE_DEBUG',
|
|
@@ -653,8 +542,8 @@ describe('Settings Loading and Merging', () => {
|
|
|
653
542
|
fs.readFileSync.mockReturnValue('{}');
|
|
654
543
|
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
655
544
|
expect(settings.merged.telemetry).toBeUndefined();
|
|
656
|
-
expect(settings.merged.ui).
|
|
657
|
-
expect(settings.merged.mcpServers).
|
|
545
|
+
expect(settings.merged.ui).toBeDefined();
|
|
546
|
+
expect(settings.merged.mcpServers).toEqual({});
|
|
658
547
|
});
|
|
659
548
|
it('should merge MCP servers correctly, with workspace taking precedence', () => {
|
|
660
549
|
mockFsExistsSync.mockImplementation((p) => p === USER_SETTINGS_PATH || p === MOCK_WORKSPACE_SETTINGS_PATH);
|
|
@@ -762,7 +651,7 @@ describe('Settings Loading and Merging', () => {
|
|
|
762
651
|
mockFsExistsSync.mockReturnValue(false); // No settings files exist
|
|
763
652
|
fs.readFileSync.mockReturnValue('{}');
|
|
764
653
|
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
765
|
-
expect(settings.merged.mcpServers).
|
|
654
|
+
expect(settings.merged.mcpServers).toEqual({});
|
|
766
655
|
});
|
|
767
656
|
it('should merge MCP servers from system, user, and workspace with system taking precedence', () => {
|
|
768
657
|
mockFsExistsSync.mockReturnValue(true);
|
|
@@ -880,10 +769,10 @@ describe('Settings Loading and Merging', () => {
|
|
|
880
769
|
expected: 0.8,
|
|
881
770
|
},
|
|
882
771
|
{
|
|
883
|
-
description: 'should be
|
|
772
|
+
description: 'should be default if not in any settings file',
|
|
884
773
|
userContent: {},
|
|
885
774
|
workspaceContent: {},
|
|
886
|
-
expected:
|
|
775
|
+
expected: 0.5,
|
|
887
776
|
},
|
|
888
777
|
])('$description', ({ userContent, workspaceContent, expected }) => {
|
|
889
778
|
mockFsExistsSync.mockReturnValue(true);
|
|
@@ -1241,7 +1130,7 @@ describe('Settings Loading and Merging', () => {
|
|
|
1241
1130
|
expect(fs.readFileSync).toHaveBeenCalledWith(MOCK_ENV_SYSTEM_SETTINGS_PATH, 'utf-8');
|
|
1242
1131
|
expect(settings.system.path).toBe(MOCK_ENV_SYSTEM_SETTINGS_PATH);
|
|
1243
1132
|
expect(settings.system.settings).toEqual(systemSettingsContent);
|
|
1244
|
-
expect(settings.merged).
|
|
1133
|
+
expect(settings.merged).toMatchObject({
|
|
1245
1134
|
...systemSettingsContent,
|
|
1246
1135
|
});
|
|
1247
1136
|
});
|
|
@@ -1317,8 +1206,9 @@ describe('Settings Loading and Merging', () => {
|
|
|
1317
1206
|
'DEBUG',
|
|
1318
1207
|
]);
|
|
1319
1208
|
expect(settings.merged.advanced?.excludedEnvVars).toEqual([
|
|
1320
|
-
'NODE_ENV',
|
|
1321
1209
|
'DEBUG',
|
|
1210
|
+
'DEBUG_MODE',
|
|
1211
|
+
'NODE_ENV',
|
|
1322
1212
|
]);
|
|
1323
1213
|
});
|
|
1324
1214
|
it('should merge excludedProjectEnvVars with workspace taking precedence', () => {
|
|
@@ -1350,6 +1240,7 @@ describe('Settings Loading and Merging', () => {
|
|
|
1350
1240
|
]);
|
|
1351
1241
|
expect(settings.merged.advanced?.excludedEnvVars).toEqual([
|
|
1352
1242
|
'DEBUG',
|
|
1243
|
+
'DEBUG_MODE',
|
|
1353
1244
|
'NODE_ENV',
|
|
1354
1245
|
'USER_VAR',
|
|
1355
1246
|
'WORKSPACE_DEBUG',
|
|
@@ -1357,557 +1248,6 @@ describe('Settings Loading and Merging', () => {
|
|
|
1357
1248
|
]);
|
|
1358
1249
|
});
|
|
1359
1250
|
});
|
|
1360
|
-
describe('with workspace trust', () => {
|
|
1361
|
-
it('should merge workspace settings when workspace is trusted', () => {
|
|
1362
|
-
mockFsExistsSync.mockReturnValue(true);
|
|
1363
|
-
const userSettingsContent = {
|
|
1364
|
-
ui: { theme: 'dark' },
|
|
1365
|
-
tools: { sandbox: false },
|
|
1366
|
-
};
|
|
1367
|
-
const workspaceSettingsContent = {
|
|
1368
|
-
tools: { sandbox: true },
|
|
1369
|
-
context: { fileName: 'WORKSPACE.md' },
|
|
1370
|
-
};
|
|
1371
|
-
fs.readFileSync.mockImplementation((p) => {
|
|
1372
|
-
if (p === USER_SETTINGS_PATH)
|
|
1373
|
-
return JSON.stringify(userSettingsContent);
|
|
1374
|
-
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
|
|
1375
|
-
return JSON.stringify(workspaceSettingsContent);
|
|
1376
|
-
return '{}';
|
|
1377
|
-
});
|
|
1378
|
-
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
1379
|
-
expect(settings.merged.tools?.sandbox).toBe(true);
|
|
1380
|
-
expect(settings.merged.context?.fileName).toBe('WORKSPACE.md');
|
|
1381
|
-
expect(settings.merged.ui?.theme).toBe('dark');
|
|
1382
|
-
});
|
|
1383
|
-
it('should NOT merge workspace settings when workspace is not trusted', () => {
|
|
1384
|
-
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
|
1385
|
-
isTrusted: false,
|
|
1386
|
-
source: 'file',
|
|
1387
|
-
});
|
|
1388
|
-
mockFsExistsSync.mockReturnValue(true);
|
|
1389
|
-
const userSettingsContent = {
|
|
1390
|
-
ui: { theme: 'dark' },
|
|
1391
|
-
tools: { sandbox: false },
|
|
1392
|
-
context: { fileName: 'USER.md' },
|
|
1393
|
-
};
|
|
1394
|
-
const workspaceSettingsContent = {
|
|
1395
|
-
tools: { sandbox: true },
|
|
1396
|
-
context: { fileName: 'WORKSPACE.md' },
|
|
1397
|
-
};
|
|
1398
|
-
fs.readFileSync.mockImplementation((p) => {
|
|
1399
|
-
if (p === USER_SETTINGS_PATH)
|
|
1400
|
-
return JSON.stringify(userSettingsContent);
|
|
1401
|
-
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
|
|
1402
|
-
return JSON.stringify(workspaceSettingsContent);
|
|
1403
|
-
return '{}';
|
|
1404
|
-
});
|
|
1405
|
-
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
1406
|
-
expect(settings.merged.tools?.sandbox).toBe(false); // User setting
|
|
1407
|
-
expect(settings.merged.context?.fileName).toBe('USER.md'); // User setting
|
|
1408
|
-
expect(settings.merged.ui?.theme).toBe('dark'); // User setting
|
|
1409
|
-
});
|
|
1410
|
-
});
|
|
1411
|
-
describe('migrateSettingsToV1', () => {
|
|
1412
|
-
it('should handle an empty object', () => {
|
|
1413
|
-
const v2Settings = {};
|
|
1414
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1415
|
-
expect(v1Settings).toEqual({});
|
|
1416
|
-
});
|
|
1417
|
-
it('should migrate a simple v2 settings object to v1', () => {
|
|
1418
|
-
const v2Settings = {
|
|
1419
|
-
general: {
|
|
1420
|
-
preferredEditor: 'vscode',
|
|
1421
|
-
vimMode: true,
|
|
1422
|
-
},
|
|
1423
|
-
ui: {
|
|
1424
|
-
theme: 'dark',
|
|
1425
|
-
},
|
|
1426
|
-
};
|
|
1427
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1428
|
-
expect(v1Settings).toEqual({
|
|
1429
|
-
preferredEditor: 'vscode',
|
|
1430
|
-
vimMode: true,
|
|
1431
|
-
theme: 'dark',
|
|
1432
|
-
});
|
|
1433
|
-
});
|
|
1434
|
-
it('should handle nested properties correctly', () => {
|
|
1435
|
-
const v2Settings = {
|
|
1436
|
-
security: {
|
|
1437
|
-
folderTrust: {
|
|
1438
|
-
enabled: true,
|
|
1439
|
-
},
|
|
1440
|
-
auth: {
|
|
1441
|
-
selectedType: 'oauth',
|
|
1442
|
-
},
|
|
1443
|
-
},
|
|
1444
|
-
advanced: {
|
|
1445
|
-
autoConfigureMemory: true,
|
|
1446
|
-
},
|
|
1447
|
-
};
|
|
1448
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1449
|
-
expect(v1Settings).toEqual({
|
|
1450
|
-
folderTrust: true,
|
|
1451
|
-
selectedAuthType: 'oauth',
|
|
1452
|
-
autoConfigureMaxOldSpaceSize: true,
|
|
1453
|
-
});
|
|
1454
|
-
});
|
|
1455
|
-
it('should preserve mcpServers at the top level', () => {
|
|
1456
|
-
const v2Settings = {
|
|
1457
|
-
general: {
|
|
1458
|
-
preferredEditor: 'vscode',
|
|
1459
|
-
},
|
|
1460
|
-
mcpServers: {
|
|
1461
|
-
'my-server': {
|
|
1462
|
-
command: 'npm start',
|
|
1463
|
-
},
|
|
1464
|
-
},
|
|
1465
|
-
};
|
|
1466
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1467
|
-
expect(v1Settings).toEqual({
|
|
1468
|
-
preferredEditor: 'vscode',
|
|
1469
|
-
mcpServers: {
|
|
1470
|
-
'my-server': {
|
|
1471
|
-
command: 'npm start',
|
|
1472
|
-
},
|
|
1473
|
-
},
|
|
1474
|
-
});
|
|
1475
|
-
});
|
|
1476
|
-
it('should carry over unrecognized top-level properties', () => {
|
|
1477
|
-
const v2Settings = {
|
|
1478
|
-
general: {
|
|
1479
|
-
vimMode: false,
|
|
1480
|
-
},
|
|
1481
|
-
unrecognized: 'value',
|
|
1482
|
-
another: {
|
|
1483
|
-
nested: true,
|
|
1484
|
-
},
|
|
1485
|
-
};
|
|
1486
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1487
|
-
expect(v1Settings).toEqual({
|
|
1488
|
-
vimMode: false,
|
|
1489
|
-
unrecognized: 'value',
|
|
1490
|
-
another: {
|
|
1491
|
-
nested: true,
|
|
1492
|
-
},
|
|
1493
|
-
});
|
|
1494
|
-
});
|
|
1495
|
-
it('should handle a complex object with mixed properties', () => {
|
|
1496
|
-
const v2Settings = {
|
|
1497
|
-
general: {
|
|
1498
|
-
disableAutoUpdate: true,
|
|
1499
|
-
},
|
|
1500
|
-
ui: {
|
|
1501
|
-
hideBanner: true,
|
|
1502
|
-
customThemes: {
|
|
1503
|
-
myTheme: {},
|
|
1504
|
-
},
|
|
1505
|
-
},
|
|
1506
|
-
model: {
|
|
1507
|
-
name: 'gemini-pro',
|
|
1508
|
-
},
|
|
1509
|
-
mcpServers: {
|
|
1510
|
-
'server-1': {
|
|
1511
|
-
command: 'node server.js',
|
|
1512
|
-
},
|
|
1513
|
-
},
|
|
1514
|
-
unrecognized: {
|
|
1515
|
-
should: 'be-preserved',
|
|
1516
|
-
},
|
|
1517
|
-
};
|
|
1518
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1519
|
-
expect(v1Settings).toEqual({
|
|
1520
|
-
disableAutoUpdate: true,
|
|
1521
|
-
hideBanner: true,
|
|
1522
|
-
customThemes: {
|
|
1523
|
-
myTheme: {},
|
|
1524
|
-
},
|
|
1525
|
-
model: 'gemini-pro',
|
|
1526
|
-
mcpServers: {
|
|
1527
|
-
'server-1': {
|
|
1528
|
-
command: 'node server.js',
|
|
1529
|
-
},
|
|
1530
|
-
},
|
|
1531
|
-
unrecognized: {
|
|
1532
|
-
should: 'be-preserved',
|
|
1533
|
-
},
|
|
1534
|
-
});
|
|
1535
|
-
});
|
|
1536
|
-
it('should not migrate a v1 settings object', () => {
|
|
1537
|
-
const v1Settings = {
|
|
1538
|
-
preferredEditor: 'vscode',
|
|
1539
|
-
vimMode: true,
|
|
1540
|
-
theme: 'dark',
|
|
1541
|
-
};
|
|
1542
|
-
const migratedSettings = migrateSettingsToV1(v1Settings);
|
|
1543
|
-
expect(migratedSettings).toEqual({
|
|
1544
|
-
preferredEditor: 'vscode',
|
|
1545
|
-
vimMode: true,
|
|
1546
|
-
theme: 'dark',
|
|
1547
|
-
});
|
|
1548
|
-
});
|
|
1549
|
-
it('should migrate a full v2 settings object to v1', () => {
|
|
1550
|
-
const v2Settings = {
|
|
1551
|
-
general: {
|
|
1552
|
-
preferredEditor: 'code',
|
|
1553
|
-
vimMode: true,
|
|
1554
|
-
},
|
|
1555
|
-
ui: {
|
|
1556
|
-
theme: 'dark',
|
|
1557
|
-
},
|
|
1558
|
-
privacy: {
|
|
1559
|
-
usageStatisticsEnabled: false,
|
|
1560
|
-
},
|
|
1561
|
-
model: {
|
|
1562
|
-
name: 'gemini-2.5-pro',
|
|
1563
|
-
},
|
|
1564
|
-
context: {
|
|
1565
|
-
fileName: 'CONTEXT.md',
|
|
1566
|
-
includeDirectories: ['/src'],
|
|
1567
|
-
},
|
|
1568
|
-
tools: {
|
|
1569
|
-
sandbox: true,
|
|
1570
|
-
exclude: ['toolA'],
|
|
1571
|
-
},
|
|
1572
|
-
mcp: {
|
|
1573
|
-
allowed: ['server1'],
|
|
1574
|
-
},
|
|
1575
|
-
security: {
|
|
1576
|
-
folderTrust: {
|
|
1577
|
-
enabled: true,
|
|
1578
|
-
},
|
|
1579
|
-
},
|
|
1580
|
-
advanced: {
|
|
1581
|
-
dnsResolutionOrder: 'ipv4first',
|
|
1582
|
-
excludedEnvVars: ['SECRET'],
|
|
1583
|
-
},
|
|
1584
|
-
mcpServers: {
|
|
1585
|
-
'my-server': {
|
|
1586
|
-
command: 'npm start',
|
|
1587
|
-
},
|
|
1588
|
-
},
|
|
1589
|
-
unrecognizedTopLevel: {
|
|
1590
|
-
value: 'should be preserved',
|
|
1591
|
-
},
|
|
1592
|
-
};
|
|
1593
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1594
|
-
expect(v1Settings).toEqual({
|
|
1595
|
-
preferredEditor: 'code',
|
|
1596
|
-
vimMode: true,
|
|
1597
|
-
theme: 'dark',
|
|
1598
|
-
usageStatisticsEnabled: false,
|
|
1599
|
-
model: 'gemini-2.5-pro',
|
|
1600
|
-
contextFileName: 'CONTEXT.md',
|
|
1601
|
-
includeDirectories: ['/src'],
|
|
1602
|
-
sandbox: true,
|
|
1603
|
-
excludeTools: ['toolA'],
|
|
1604
|
-
allowMCPServers: ['server1'],
|
|
1605
|
-
folderTrust: true,
|
|
1606
|
-
dnsResolutionOrder: 'ipv4first',
|
|
1607
|
-
excludedProjectEnvVars: ['SECRET'],
|
|
1608
|
-
mcpServers: {
|
|
1609
|
-
'my-server': {
|
|
1610
|
-
command: 'npm start',
|
|
1611
|
-
},
|
|
1612
|
-
},
|
|
1613
|
-
unrecognizedTopLevel: {
|
|
1614
|
-
value: 'should be preserved',
|
|
1615
|
-
},
|
|
1616
|
-
});
|
|
1617
|
-
});
|
|
1618
|
-
it('should handle partial v2 settings', () => {
|
|
1619
|
-
const v2Settings = {
|
|
1620
|
-
general: {
|
|
1621
|
-
vimMode: false,
|
|
1622
|
-
},
|
|
1623
|
-
ui: {},
|
|
1624
|
-
model: {
|
|
1625
|
-
name: 'gemini-2.5-pro',
|
|
1626
|
-
},
|
|
1627
|
-
unrecognized: 'value',
|
|
1628
|
-
};
|
|
1629
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1630
|
-
expect(v1Settings).toEqual({
|
|
1631
|
-
vimMode: false,
|
|
1632
|
-
model: 'gemini-2.5-pro',
|
|
1633
|
-
unrecognized: 'value',
|
|
1634
|
-
});
|
|
1635
|
-
});
|
|
1636
|
-
it('should handle settings with different data types', () => {
|
|
1637
|
-
const v2Settings = {
|
|
1638
|
-
general: {
|
|
1639
|
-
vimMode: false,
|
|
1640
|
-
},
|
|
1641
|
-
model: {
|
|
1642
|
-
maxSessionTurns: -1,
|
|
1643
|
-
},
|
|
1644
|
-
context: {
|
|
1645
|
-
includeDirectories: [],
|
|
1646
|
-
},
|
|
1647
|
-
security: {
|
|
1648
|
-
folderTrust: {
|
|
1649
|
-
enabled: undefined,
|
|
1650
|
-
},
|
|
1651
|
-
},
|
|
1652
|
-
};
|
|
1653
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1654
|
-
expect(v1Settings).toEqual({
|
|
1655
|
-
vimMode: false,
|
|
1656
|
-
maxSessionTurns: -1,
|
|
1657
|
-
includeDirectories: [],
|
|
1658
|
-
security: {
|
|
1659
|
-
folderTrust: {
|
|
1660
|
-
enabled: undefined,
|
|
1661
|
-
},
|
|
1662
|
-
},
|
|
1663
|
-
});
|
|
1664
|
-
});
|
|
1665
|
-
it('should preserve unrecognized top-level keys', () => {
|
|
1666
|
-
const v2Settings = {
|
|
1667
|
-
general: {
|
|
1668
|
-
vimMode: true,
|
|
1669
|
-
},
|
|
1670
|
-
customTopLevel: {
|
|
1671
|
-
a: 1,
|
|
1672
|
-
b: [2],
|
|
1673
|
-
},
|
|
1674
|
-
anotherOne: 'hello',
|
|
1675
|
-
};
|
|
1676
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1677
|
-
expect(v1Settings).toEqual({
|
|
1678
|
-
vimMode: true,
|
|
1679
|
-
customTopLevel: {
|
|
1680
|
-
a: 1,
|
|
1681
|
-
b: [2],
|
|
1682
|
-
},
|
|
1683
|
-
anotherOne: 'hello',
|
|
1684
|
-
});
|
|
1685
|
-
});
|
|
1686
|
-
it('should handle an empty v2 settings object', () => {
|
|
1687
|
-
const v2Settings = {};
|
|
1688
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1689
|
-
expect(v1Settings).toEqual({});
|
|
1690
|
-
});
|
|
1691
|
-
it('should correctly handle mcpServers at the top level', () => {
|
|
1692
|
-
const v2Settings = {
|
|
1693
|
-
mcpServers: {
|
|
1694
|
-
serverA: { command: 'a' },
|
|
1695
|
-
},
|
|
1696
|
-
mcp: {
|
|
1697
|
-
allowed: ['serverA'],
|
|
1698
|
-
},
|
|
1699
|
-
};
|
|
1700
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1701
|
-
expect(v1Settings).toEqual({
|
|
1702
|
-
mcpServers: {
|
|
1703
|
-
serverA: { command: 'a' },
|
|
1704
|
-
},
|
|
1705
|
-
allowMCPServers: ['serverA'],
|
|
1706
|
-
});
|
|
1707
|
-
});
|
|
1708
|
-
it('should correctly migrate customWittyPhrases', () => {
|
|
1709
|
-
const v2Settings = {
|
|
1710
|
-
ui: {
|
|
1711
|
-
customWittyPhrases: ['test phrase'],
|
|
1712
|
-
},
|
|
1713
|
-
};
|
|
1714
|
-
const v1Settings = migrateSettingsToV1(v2Settings);
|
|
1715
|
-
expect(v1Settings).toEqual({
|
|
1716
|
-
customWittyPhrases: ['test phrase'],
|
|
1717
|
-
});
|
|
1718
|
-
});
|
|
1719
|
-
});
|
|
1720
|
-
describe('loadEnvironment', () => {
|
|
1721
|
-
function setup({ isFolderTrustEnabled = true, isWorkspaceTrustedValue = true, }) {
|
|
1722
|
-
delete process.env['TESTTEST']; // reset
|
|
1723
|
-
const geminiEnvPath = path.resolve(path.join(GEMINI_DIR, '.env'));
|
|
1724
|
-
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
|
1725
|
-
isTrusted: isWorkspaceTrustedValue,
|
|
1726
|
-
source: 'file',
|
|
1727
|
-
});
|
|
1728
|
-
mockFsExistsSync.mockImplementation((p) => [USER_SETTINGS_PATH, geminiEnvPath].includes(p.toString()));
|
|
1729
|
-
const userSettingsContent = {
|
|
1730
|
-
ui: {
|
|
1731
|
-
theme: 'dark',
|
|
1732
|
-
},
|
|
1733
|
-
security: {
|
|
1734
|
-
folderTrust: {
|
|
1735
|
-
enabled: isFolderTrustEnabled,
|
|
1736
|
-
},
|
|
1737
|
-
},
|
|
1738
|
-
context: {
|
|
1739
|
-
fileName: 'USER_CONTEXT.md',
|
|
1740
|
-
},
|
|
1741
|
-
};
|
|
1742
|
-
fs.readFileSync.mockImplementation((p) => {
|
|
1743
|
-
if (p === USER_SETTINGS_PATH)
|
|
1744
|
-
return JSON.stringify(userSettingsContent);
|
|
1745
|
-
if (p === geminiEnvPath)
|
|
1746
|
-
return 'TESTTEST=1234';
|
|
1747
|
-
return '{}';
|
|
1748
|
-
});
|
|
1749
|
-
}
|
|
1750
|
-
it('sets environment variables from .env files', () => {
|
|
1751
|
-
setup({ isFolderTrustEnabled: false, isWorkspaceTrustedValue: true });
|
|
1752
|
-
loadEnvironment(loadSettings(MOCK_WORKSPACE_DIR).merged);
|
|
1753
|
-
expect(process.env['TESTTEST']).toEqual('1234');
|
|
1754
|
-
});
|
|
1755
|
-
it('does not load env files from untrusted spaces', () => {
|
|
1756
|
-
setup({ isFolderTrustEnabled: true, isWorkspaceTrustedValue: false });
|
|
1757
|
-
loadEnvironment(loadSettings(MOCK_WORKSPACE_DIR).merged);
|
|
1758
|
-
expect(process.env['TESTTEST']).not.toEqual('1234');
|
|
1759
|
-
});
|
|
1760
|
-
});
|
|
1761
|
-
describe('needsMigration', () => {
|
|
1762
|
-
it('should return false for an empty object', () => {
|
|
1763
|
-
expect(needsMigration({})).toBe(false);
|
|
1764
|
-
});
|
|
1765
|
-
it('should return false for settings that are already in V2 format', () => {
|
|
1766
|
-
const v2Settings = {
|
|
1767
|
-
ui: {
|
|
1768
|
-
theme: 'dark',
|
|
1769
|
-
},
|
|
1770
|
-
tools: {
|
|
1771
|
-
sandbox: true,
|
|
1772
|
-
},
|
|
1773
|
-
};
|
|
1774
|
-
expect(needsMigration(v2Settings)).toBe(false);
|
|
1775
|
-
});
|
|
1776
|
-
it('should return true for settings with a V1 key that needs to be moved', () => {
|
|
1777
|
-
const v1Settings = {
|
|
1778
|
-
theme: 'dark', // v1 key
|
|
1779
|
-
};
|
|
1780
|
-
expect(needsMigration(v1Settings)).toBe(true);
|
|
1781
|
-
});
|
|
1782
|
-
it('should return true for settings with a mix of V1 and V2 keys', () => {
|
|
1783
|
-
const mixedSettings = {
|
|
1784
|
-
theme: 'dark', // v1 key
|
|
1785
|
-
tools: {
|
|
1786
|
-
sandbox: true, // v2 key
|
|
1787
|
-
},
|
|
1788
|
-
};
|
|
1789
|
-
expect(needsMigration(mixedSettings)).toBe(true);
|
|
1790
|
-
});
|
|
1791
|
-
it('should return false for settings with only V1 keys that are the same in V2', () => {
|
|
1792
|
-
const v1Settings = {
|
|
1793
|
-
mcpServers: {},
|
|
1794
|
-
telemetry: {},
|
|
1795
|
-
extensions: [],
|
|
1796
|
-
};
|
|
1797
|
-
expect(needsMigration(v1Settings)).toBe(false);
|
|
1798
|
-
});
|
|
1799
|
-
it('should return true for settings with a mix of V1 keys that are the same in V2 and V1 keys that need moving', () => {
|
|
1800
|
-
const v1Settings = {
|
|
1801
|
-
mcpServers: {}, // same in v2
|
|
1802
|
-
theme: 'dark', // needs moving
|
|
1803
|
-
};
|
|
1804
|
-
expect(needsMigration(v1Settings)).toBe(true);
|
|
1805
|
-
});
|
|
1806
|
-
it('should return false for settings with unrecognized keys', () => {
|
|
1807
|
-
const settings = {
|
|
1808
|
-
someUnrecognizedKey: 'value',
|
|
1809
|
-
};
|
|
1810
|
-
expect(needsMigration(settings)).toBe(false);
|
|
1811
|
-
});
|
|
1812
|
-
it('should return false for settings with v2 keys and unrecognized keys', () => {
|
|
1813
|
-
const settings = {
|
|
1814
|
-
ui: { theme: 'dark' },
|
|
1815
|
-
someUnrecognizedKey: 'value',
|
|
1816
|
-
};
|
|
1817
|
-
expect(needsMigration(settings)).toBe(false);
|
|
1818
|
-
});
|
|
1819
|
-
});
|
|
1820
|
-
describe('migrateDeprecatedSettings', () => {
|
|
1821
|
-
let mockFsExistsSync;
|
|
1822
|
-
let mockFsReadFileSync;
|
|
1823
|
-
beforeEach(() => {
|
|
1824
|
-
vi.resetAllMocks();
|
|
1825
|
-
mockFsExistsSync = vi.mocked(fs.existsSync);
|
|
1826
|
-
mockFsExistsSync.mockReturnValue(true);
|
|
1827
|
-
mockFsReadFileSync = vi.mocked(fs.readFileSync);
|
|
1828
|
-
mockFsReadFileSync.mockReturnValue('{}');
|
|
1829
|
-
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
|
1830
|
-
isTrusted: true,
|
|
1831
|
-
source: undefined,
|
|
1832
|
-
});
|
|
1833
|
-
});
|
|
1834
|
-
afterEach(() => {
|
|
1835
|
-
vi.restoreAllMocks();
|
|
1836
|
-
});
|
|
1837
|
-
it('should migrate disabled extensions from user and workspace settings', () => {
|
|
1838
|
-
const userSettingsContent = {
|
|
1839
|
-
extensions: {
|
|
1840
|
-
disabled: ['user-ext-1', 'shared-ext'],
|
|
1841
|
-
},
|
|
1842
|
-
};
|
|
1843
|
-
const workspaceSettingsContent = {
|
|
1844
|
-
extensions: {
|
|
1845
|
-
disabled: ['workspace-ext-1', 'shared-ext'],
|
|
1846
|
-
},
|
|
1847
|
-
};
|
|
1848
|
-
mockFsReadFileSync.mockImplementation((p) => {
|
|
1849
|
-
if (p === USER_SETTINGS_PATH)
|
|
1850
|
-
return JSON.stringify(userSettingsContent);
|
|
1851
|
-
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
|
|
1852
|
-
return JSON.stringify(workspaceSettingsContent);
|
|
1853
|
-
return '{}';
|
|
1854
|
-
});
|
|
1855
|
-
const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
1856
|
-
const setValueSpy = vi.spyOn(loadedSettings, 'setValue');
|
|
1857
|
-
const extensionManager = new ExtensionManager({
|
|
1858
|
-
settings: loadedSettings.merged,
|
|
1859
|
-
workspaceDir: MOCK_WORKSPACE_DIR,
|
|
1860
|
-
requestConsent: vi.fn(),
|
|
1861
|
-
requestSetting: vi.fn(),
|
|
1862
|
-
});
|
|
1863
|
-
const mockDisableExtension = vi.spyOn(extensionManager, 'disableExtension');
|
|
1864
|
-
mockDisableExtension.mockImplementation(async () => { });
|
|
1865
|
-
migrateDeprecatedSettings(loadedSettings, extensionManager);
|
|
1866
|
-
// Check user settings migration
|
|
1867
|
-
expect(mockDisableExtension).toHaveBeenCalledWith('user-ext-1', SettingScope.User);
|
|
1868
|
-
expect(mockDisableExtension).toHaveBeenCalledWith('shared-ext', SettingScope.User);
|
|
1869
|
-
// Check workspace settings migration
|
|
1870
|
-
expect(mockDisableExtension).toHaveBeenCalledWith('workspace-ext-1', SettingScope.Workspace);
|
|
1871
|
-
expect(mockDisableExtension).toHaveBeenCalledWith('shared-ext', SettingScope.Workspace);
|
|
1872
|
-
// Check that setValue was called to remove the deprecated setting
|
|
1873
|
-
expect(setValueSpy).toHaveBeenCalledWith(SettingScope.User, 'extensions', {
|
|
1874
|
-
disabled: undefined,
|
|
1875
|
-
});
|
|
1876
|
-
expect(setValueSpy).toHaveBeenCalledWith(SettingScope.Workspace, 'extensions', {
|
|
1877
|
-
disabled: undefined,
|
|
1878
|
-
});
|
|
1879
|
-
});
|
|
1880
|
-
it('should not do anything if there are no deprecated settings', () => {
|
|
1881
|
-
const userSettingsContent = {
|
|
1882
|
-
extensions: {
|
|
1883
|
-
enabled: ['user-ext-1'],
|
|
1884
|
-
},
|
|
1885
|
-
};
|
|
1886
|
-
const workspaceSettingsContent = {
|
|
1887
|
-
someOtherSetting: 'value',
|
|
1888
|
-
};
|
|
1889
|
-
mockFsReadFileSync.mockImplementation((p) => {
|
|
1890
|
-
if (p === USER_SETTINGS_PATH)
|
|
1891
|
-
return JSON.stringify(userSettingsContent);
|
|
1892
|
-
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
|
|
1893
|
-
return JSON.stringify(workspaceSettingsContent);
|
|
1894
|
-
return '{}';
|
|
1895
|
-
});
|
|
1896
|
-
const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
1897
|
-
const setValueSpy = vi.spyOn(loadedSettings, 'setValue');
|
|
1898
|
-
const extensionManager = new ExtensionManager({
|
|
1899
|
-
settings: loadedSettings.merged,
|
|
1900
|
-
workspaceDir: MOCK_WORKSPACE_DIR,
|
|
1901
|
-
requestConsent: vi.fn(),
|
|
1902
|
-
requestSetting: vi.fn(),
|
|
1903
|
-
});
|
|
1904
|
-
const mockDisableExtension = vi.spyOn(extensionManager, 'disableExtension');
|
|
1905
|
-
mockDisableExtension.mockImplementation(async () => { });
|
|
1906
|
-
migrateDeprecatedSettings(loadedSettings, extensionManager);
|
|
1907
|
-
expect(mockDisableExtension).not.toHaveBeenCalled();
|
|
1908
|
-
expect(setValueSpy).not.toHaveBeenCalled();
|
|
1909
|
-
});
|
|
1910
|
-
});
|
|
1911
1251
|
describe('saveSettings', () => {
|
|
1912
1252
|
it('should save settings using updateSettingsFilePreservingFormat', () => {
|
|
1913
1253
|
const mockUpdateSettings = vi.mocked(updateSettingsFilePreservingFormat);
|
|
@@ -1951,5 +1291,187 @@ describe('Settings Loading and Merging', () => {
|
|
|
1951
1291
|
expect(mockCoreEvents.emitFeedback).toHaveBeenCalledWith('error', 'There was an error saving your latest settings changes.', error);
|
|
1952
1292
|
});
|
|
1953
1293
|
});
|
|
1294
|
+
describe('LoadedSettings and remote admin settings', () => {
|
|
1295
|
+
it('should prioritize remote admin settings over file-based admin settings', () => {
|
|
1296
|
+
mockFsExistsSync.mockReturnValue(true);
|
|
1297
|
+
const systemSettingsContent = {
|
|
1298
|
+
admin: {
|
|
1299
|
+
// These should be ignored
|
|
1300
|
+
secureModeEnabled: true,
|
|
1301
|
+
mcp: { enabled: false },
|
|
1302
|
+
extensions: { enabled: false },
|
|
1303
|
+
},
|
|
1304
|
+
// A non-admin setting to ensure it's still processed
|
|
1305
|
+
ui: { theme: 'system-theme' },
|
|
1306
|
+
};
|
|
1307
|
+
fs.readFileSync.mockImplementation((p) => {
|
|
1308
|
+
if (p === getSystemSettingsPath()) {
|
|
1309
|
+
return JSON.stringify(systemSettingsContent);
|
|
1310
|
+
}
|
|
1311
|
+
return '{}';
|
|
1312
|
+
});
|
|
1313
|
+
const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
1314
|
+
// 1. Verify that on initial load, file-based admin settings are ignored
|
|
1315
|
+
// and schema defaults are used instead.
|
|
1316
|
+
expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false); // default: false
|
|
1317
|
+
expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true); // default: true
|
|
1318
|
+
expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true); // default: true
|
|
1319
|
+
expect(loadedSettings.merged.ui?.theme).toBe('system-theme'); // non-admin setting should be loaded
|
|
1320
|
+
// 2. Now, set remote admin settings.
|
|
1321
|
+
loadedSettings.setRemoteAdminSettings({
|
|
1322
|
+
secureModeEnabled: true,
|
|
1323
|
+
mcpSetting: { mcpEnabled: false },
|
|
1324
|
+
cliFeatureSetting: { extensionsSetting: { extensionsEnabled: false } },
|
|
1325
|
+
});
|
|
1326
|
+
// 3. Verify that remote admin settings take precedence.
|
|
1327
|
+
expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(true);
|
|
1328
|
+
expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(false);
|
|
1329
|
+
expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(false);
|
|
1330
|
+
// non-admin setting should remain unchanged
|
|
1331
|
+
expect(loadedSettings.merged.ui?.theme).toBe('system-theme');
|
|
1332
|
+
});
|
|
1333
|
+
it('should set remote admin settings and recompute merged settings', () => {
|
|
1334
|
+
mockFsExistsSync.mockReturnValue(true);
|
|
1335
|
+
const systemSettingsContent = {
|
|
1336
|
+
admin: {
|
|
1337
|
+
secureModeEnabled: false,
|
|
1338
|
+
mcp: { enabled: false },
|
|
1339
|
+
extensions: { enabled: false },
|
|
1340
|
+
},
|
|
1341
|
+
ui: { theme: 'initial-theme' },
|
|
1342
|
+
};
|
|
1343
|
+
fs.readFileSync.mockImplementation((p) => {
|
|
1344
|
+
if (p === getSystemSettingsPath()) {
|
|
1345
|
+
return JSON.stringify(systemSettingsContent);
|
|
1346
|
+
}
|
|
1347
|
+
return '{}';
|
|
1348
|
+
});
|
|
1349
|
+
const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
1350
|
+
// Ensure initial state from defaults (as file-based admin settings are ignored)
|
|
1351
|
+
expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false);
|
|
1352
|
+
expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true);
|
|
1353
|
+
expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true);
|
|
1354
|
+
expect(loadedSettings.merged.ui?.theme).toBe('initial-theme');
|
|
1355
|
+
const newRemoteSettings = {
|
|
1356
|
+
secureModeEnabled: true,
|
|
1357
|
+
mcpSetting: { mcpEnabled: false },
|
|
1358
|
+
cliFeatureSetting: { extensionsSetting: { extensionsEnabled: false } },
|
|
1359
|
+
};
|
|
1360
|
+
loadedSettings.setRemoteAdminSettings(newRemoteSettings);
|
|
1361
|
+
// Verify that remote admin settings are applied
|
|
1362
|
+
expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(true);
|
|
1363
|
+
expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(false);
|
|
1364
|
+
expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(false);
|
|
1365
|
+
// Non-admin settings should remain untouched
|
|
1366
|
+
expect(loadedSettings.merged.ui?.theme).toBe('initial-theme');
|
|
1367
|
+
// Verify that calling setRemoteAdminSettings with partial data overwrites previous remote settings
|
|
1368
|
+
// and missing properties revert to schema defaults.
|
|
1369
|
+
loadedSettings.setRemoteAdminSettings({ secureModeEnabled: false });
|
|
1370
|
+
expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false);
|
|
1371
|
+
expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true); // Reverts to default: true
|
|
1372
|
+
expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true); // Reverts to default: true
|
|
1373
|
+
});
|
|
1374
|
+
it('should correctly handle undefined remote admin settings', () => {
|
|
1375
|
+
mockFsExistsSync.mockReturnValue(true);
|
|
1376
|
+
const systemSettingsContent = {
|
|
1377
|
+
ui: { theme: 'initial-theme' },
|
|
1378
|
+
};
|
|
1379
|
+
fs.readFileSync.mockImplementation((p) => {
|
|
1380
|
+
if (p === getSystemSettingsPath()) {
|
|
1381
|
+
return JSON.stringify(systemSettingsContent);
|
|
1382
|
+
}
|
|
1383
|
+
return '{}';
|
|
1384
|
+
});
|
|
1385
|
+
const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
1386
|
+
// Should have default admin settings
|
|
1387
|
+
expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false);
|
|
1388
|
+
expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true);
|
|
1389
|
+
expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true);
|
|
1390
|
+
loadedSettings.setRemoteAdminSettings({}); // Set empty remote settings
|
|
1391
|
+
// Admin settings should revert to defaults because there are no remote overrides
|
|
1392
|
+
expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false);
|
|
1393
|
+
expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true);
|
|
1394
|
+
expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true);
|
|
1395
|
+
});
|
|
1396
|
+
it('should correctly handle missing properties in remote admin settings', () => {
|
|
1397
|
+
mockFsExistsSync.mockReturnValue(true);
|
|
1398
|
+
const systemSettingsContent = {
|
|
1399
|
+
admin: {
|
|
1400
|
+
secureModeEnabled: true,
|
|
1401
|
+
},
|
|
1402
|
+
};
|
|
1403
|
+
fs.readFileSync.mockImplementation((p) => {
|
|
1404
|
+
if (p === getSystemSettingsPath()) {
|
|
1405
|
+
return JSON.stringify(systemSettingsContent);
|
|
1406
|
+
}
|
|
1407
|
+
return '{}';
|
|
1408
|
+
});
|
|
1409
|
+
const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
|
|
1410
|
+
// Ensure initial state from defaults (as file-based admin settings are ignored)
|
|
1411
|
+
expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false);
|
|
1412
|
+
expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true);
|
|
1413
|
+
expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true);
|
|
1414
|
+
// Set remote settings with only secureModeEnabled
|
|
1415
|
+
loadedSettings.setRemoteAdminSettings({
|
|
1416
|
+
secureModeEnabled: true,
|
|
1417
|
+
});
|
|
1418
|
+
// Verify secureModeEnabled is updated, others remain defaults
|
|
1419
|
+
expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(true);
|
|
1420
|
+
expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true);
|
|
1421
|
+
expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true);
|
|
1422
|
+
// Set remote settings with only mcpSetting.mcpEnabled
|
|
1423
|
+
loadedSettings.setRemoteAdminSettings({
|
|
1424
|
+
mcpSetting: { mcpEnabled: false },
|
|
1425
|
+
});
|
|
1426
|
+
// Verify mcpEnabled is updated, others remain defaults (secureModeEnabled reverts to default:false)
|
|
1427
|
+
expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false);
|
|
1428
|
+
expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(false);
|
|
1429
|
+
expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true);
|
|
1430
|
+
// Set remote settings with only cliFeatureSetting.extensionsSetting.extensionsEnabled
|
|
1431
|
+
loadedSettings.setRemoteAdminSettings({
|
|
1432
|
+
cliFeatureSetting: { extensionsSetting: { extensionsEnabled: false } },
|
|
1433
|
+
});
|
|
1434
|
+
// Verify extensionsEnabled is updated, others remain defaults
|
|
1435
|
+
expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false);
|
|
1436
|
+
expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true);
|
|
1437
|
+
expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(false);
|
|
1438
|
+
});
|
|
1439
|
+
});
|
|
1440
|
+
describe('getDefaultsFromSchema', () => {
|
|
1441
|
+
it('should extract defaults from a schema', () => {
|
|
1442
|
+
const mockSchema = {
|
|
1443
|
+
prop1: {
|
|
1444
|
+
type: 'string',
|
|
1445
|
+
default: 'default1',
|
|
1446
|
+
label: 'Prop 1',
|
|
1447
|
+
category: 'General',
|
|
1448
|
+
requiresRestart: false,
|
|
1449
|
+
},
|
|
1450
|
+
nested: {
|
|
1451
|
+
type: 'object',
|
|
1452
|
+
label: 'Nested',
|
|
1453
|
+
category: 'General',
|
|
1454
|
+
requiresRestart: false,
|
|
1455
|
+
default: {},
|
|
1456
|
+
properties: {
|
|
1457
|
+
prop2: {
|
|
1458
|
+
type: 'number',
|
|
1459
|
+
default: 42,
|
|
1460
|
+
label: 'Prop 2',
|
|
1461
|
+
category: 'General',
|
|
1462
|
+
requiresRestart: false,
|
|
1463
|
+
},
|
|
1464
|
+
},
|
|
1465
|
+
},
|
|
1466
|
+
};
|
|
1467
|
+
const defaults = getDefaultsFromSchema(mockSchema);
|
|
1468
|
+
expect(defaults).toEqual({
|
|
1469
|
+
prop1: 'default1',
|
|
1470
|
+
nested: {
|
|
1471
|
+
prop2: 42,
|
|
1472
|
+
},
|
|
1473
|
+
});
|
|
1474
|
+
});
|
|
1475
|
+
});
|
|
1954
1476
|
});
|
|
1955
1477
|
//# sourceMappingURL=settings.test.js.map
|