@google/gemini-cli 0.10.0-preview.3 → 0.11.0-nightly.20251021.e72c00cf
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 +9 -0
- package/dist/google-gemini-cli-0.11.0-nightly.20251020.a96f0659.tgz +0 -0
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/package.json +2 -2
- package/dist/src/commands/extensions/disable.js +8 -5
- package/dist/src/commands/extensions/disable.js.map +1 -1
- package/dist/src/commands/extensions/enable.js +5 -3
- package/dist/src/commands/extensions/enable.js.map +1 -1
- package/dist/src/commands/extensions/examples/mcp-server/example.d.ts +6 -0
- package/dist/src/commands/extensions/examples/mcp-server/example.js +46 -0
- package/dist/src/commands/extensions/examples/mcp-server/example.js.map +1 -0
- package/dist/src/commands/extensions/install.d.ts +1 -0
- package/dist/src/commands/extensions/install.js +18 -4
- package/dist/src/commands/extensions/install.js.map +1 -1
- package/dist/src/commands/extensions/link.js +3 -2
- package/dist/src/commands/extensions/link.js.map +1 -1
- package/dist/src/commands/extensions/list.js +7 -5
- package/dist/src/commands/extensions/list.js.map +1 -1
- package/dist/src/commands/extensions/new.js +14 -20
- package/dist/src/commands/extensions/new.js.map +1 -1
- package/dist/src/commands/extensions/uninstall.js +3 -2
- package/dist/src/commands/extensions/uninstall.js.map +1 -1
- package/dist/src/commands/extensions/update.js +17 -17
- package/dist/src/commands/extensions/update.js.map +1 -1
- package/dist/src/commands/mcp/add.js +7 -4
- package/dist/src/commands/mcp/add.js.map +1 -1
- package/dist/src/commands/mcp/add.test.d.ts +6 -0
- package/dist/src/commands/mcp/add.test.js +244 -0
- package/dist/src/commands/mcp/add.test.js.map +1 -0
- package/dist/src/commands/mcp/list.js +6 -4
- package/dist/src/commands/mcp/list.js.map +1 -1
- package/dist/src/commands/mcp/list.test.d.ts +6 -0
- package/dist/src/commands/mcp/list.test.js +117 -0
- package/dist/src/commands/mcp/list.test.js.map +1 -0
- package/dist/src/commands/mcp/remove.test.d.ts +6 -0
- package/dist/src/commands/mcp/remove.test.js +175 -0
- package/dist/src/commands/mcp/remove.test.js.map +1 -0
- package/dist/src/commands/mcp.test.d.ts +6 -0
- package/dist/src/commands/mcp.test.js +62 -0
- package/dist/src/commands/mcp.test.js.map +1 -0
- package/dist/src/config/auth.js +3 -1
- package/dist/src/config/auth.js.map +1 -1
- package/dist/src/config/auth.test.js +3 -1
- package/dist/src/config/auth.test.js.map +1 -1
- package/dist/src/config/config.d.ts +2 -14
- package/dist/src/config/config.integration.test.d.ts +6 -0
- package/dist/src/config/config.integration.test.js +321 -0
- package/dist/src/config/config.integration.test.js.map +1 -0
- package/dist/src/config/config.js +38 -102
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.d.ts +6 -0
- package/dist/src/config/config.test.js +1961 -0
- package/dist/src/config/config.test.js.map +1 -0
- package/dist/src/config/extension.d.ts +4 -15
- package/dist/src/config/extension.js +84 -97
- package/dist/src/config/extension.js.map +1 -1
- package/dist/src/config/extension.test.d.ts +6 -0
- package/dist/src/config/extension.test.js +1076 -0
- package/dist/src/config/extension.test.js.map +1 -0
- package/dist/src/config/extensions/extensionEnablement.d.ts +1 -1
- package/dist/src/config/extensions/extensionEnablement.js +4 -3
- package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
- package/dist/src/config/extensions/extensionEnablement.test.js +21 -18
- package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
- package/dist/src/config/extensions/github.d.ts +18 -9
- package/dist/src/config/extensions/github.js +106 -29
- package/dist/src/config/extensions/github.js.map +1 -1
- package/dist/src/config/extensions/github.test.js +20 -17
- package/dist/src/config/extensions/github.test.js.map +1 -1
- package/dist/src/config/extensions/update.d.ts +5 -4
- package/dist/src/config/extensions/update.js +10 -6
- package/dist/src/config/extensions/update.js.map +1 -1
- package/dist/src/config/extensions/update.test.js +57 -57
- package/dist/src/config/extensions/update.test.js.map +1 -1
- package/dist/src/config/extensions/variableSchema.d.ts +2 -0
- package/dist/src/config/extensions/variableSchema.js.map +1 -1
- package/dist/src/config/keyBindings.d.ts +2 -1
- package/dist/src/config/keyBindings.js +4 -2
- package/dist/src/config/keyBindings.js.map +1 -1
- package/dist/src/config/policy.d.ts +3 -2
- package/dist/src/config/policy.js +32 -23
- package/dist/src/config/policy.js.map +1 -1
- package/dist/src/config/policy.test.js +60 -36
- package/dist/src/config/policy.test.js.map +1 -1
- package/dist/src/config/sandboxConfig.d.ts +0 -1
- package/dist/src/config/sandboxConfig.js +1 -3
- package/dist/src/config/sandboxConfig.js.map +1 -1
- package/dist/src/config/settings.js +3 -1
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/config/settings.test.d.ts +6 -0
- package/dist/src/config/settings.test.js +1937 -0
- package/dist/src/config/settings.test.js.map +1 -0
- package/dist/src/config/settingsSchema.d.ts +1 -1
- package/dist/src/config/settingsSchema.js +1 -1
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/config/settingsSchema.test.js +1 -1
- package/dist/src/config/settingsSchema.test.js.map +1 -1
- package/dist/src/gemini.js +30 -24
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +42 -11
- 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/nonInteractiveCli.js +92 -4
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/nonInteractiveCli.test.d.ts +6 -0
- package/dist/src/nonInteractiveCli.test.js +711 -0
- package/dist/src/nonInteractiveCli.test.js.map +1 -0
- package/dist/src/services/FileCommandLoader.test.d.ts +6 -0
- package/dist/src/services/FileCommandLoader.test.js +971 -0
- package/dist/src/services/FileCommandLoader.test.js.map +1 -0
- package/dist/src/services/prompt-processors/argumentProcessor.test.d.ts +6 -0
- package/dist/src/services/prompt-processors/argumentProcessor.test.js +40 -0
- package/dist/src/services/prompt-processors/argumentProcessor.test.js.map +1 -0
- package/dist/src/services/prompt-processors/atFileProcessor.js +3 -2
- package/dist/src/services/prompt-processors/atFileProcessor.js.map +1 -1
- package/dist/src/services/prompt-processors/shellProcessor.test.d.ts +6 -0
- package/dist/src/services/prompt-processors/shellProcessor.test.js +482 -0
- package/dist/src/services/prompt-processors/shellProcessor.test.js.map +1 -0
- package/dist/src/test-utils/render.d.ts +2 -1
- package/dist/src/test-utils/render.js +5 -2
- package/dist/src/test-utils/render.js.map +1 -1
- package/dist/src/ui/App.test.d.ts +6 -0
- package/dist/src/ui/App.test.js +110 -0
- package/dist/src/ui/App.test.js.map +1 -0
- package/dist/src/ui/AppContainer.js +43 -28
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/AppContainer.test.js +35 -9
- package/dist/src/ui/AppContainer.test.js.map +1 -1
- package/dist/src/ui/auth/AuthDialog.d.ts +1 -1
- package/dist/src/ui/auth/AuthDialog.js +3 -1
- package/dist/src/ui/auth/AuthDialog.js.map +1 -1
- package/dist/src/ui/auth/useAuth.d.ts +1 -1
- package/dist/src/ui/auth/useAuth.js +3 -1
- package/dist/src/ui/auth/useAuth.js.map +1 -1
- package/dist/src/ui/commands/aboutCommand.js +1 -1
- package/dist/src/ui/commands/aboutCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/aboutCommand.test.js +130 -0
- package/dist/src/ui/commands/aboutCommand.test.js.map +1 -0
- package/dist/src/ui/commands/authCommand.js +1 -1
- package/dist/src/ui/commands/authCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/authCommand.test.js +30 -0
- package/dist/src/ui/commands/authCommand.test.js.map +1 -0
- package/dist/src/ui/commands/bugCommand.js +1 -1
- package/dist/src/ui/commands/bugCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/bugCommand.test.js +105 -0
- package/dist/src/ui/commands/bugCommand.test.js.map +1 -0
- package/dist/src/ui/commands/chatCommand.js +1 -1
- package/dist/src/ui/commands/chatCommand.js.map +1 -1
- package/dist/src/ui/commands/chatCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/chatCommand.test.js +555 -0
- package/dist/src/ui/commands/chatCommand.test.js.map +1 -0
- package/dist/src/ui/commands/clearCommand.js +1 -1
- package/dist/src/ui/commands/clearCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/clearCommand.test.js +76 -0
- package/dist/src/ui/commands/clearCommand.test.js.map +1 -0
- package/dist/src/ui/commands/compressCommand.js +1 -1
- package/dist/src/ui/commands/compressCommand.js.map +1 -1
- package/dist/src/ui/commands/compressCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/compressCommand.test.js +98 -0
- package/dist/src/ui/commands/compressCommand.test.js.map +1 -0
- package/dist/src/ui/commands/copyCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/copyCommand.test.js +242 -0
- package/dist/src/ui/commands/copyCommand.test.js.map +1 -0
- package/dist/src/ui/commands/corgiCommand.js +1 -1
- package/dist/src/ui/commands/corgiCommand.js.map +1 -1
- package/dist/src/ui/commands/corgiCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/corgiCommand.test.js +28 -0
- package/dist/src/ui/commands/corgiCommand.test.js.map +1 -0
- package/dist/src/ui/commands/directoryCommand.js +1 -1
- package/dist/src/ui/commands/directoryCommand.js.map +1 -1
- package/dist/src/ui/commands/directoryCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/directoryCommand.test.js +144 -0
- package/dist/src/ui/commands/directoryCommand.test.js.map +1 -0
- package/dist/src/ui/commands/docsCommand.js +1 -1
- package/dist/src/ui/commands/docsCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/docsCommand.test.js +72 -0
- package/dist/src/ui/commands/docsCommand.test.js.map +1 -0
- package/dist/src/ui/commands/editorCommand.js +1 -1
- package/dist/src/ui/commands/editorCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/editorCommand.test.js +27 -0
- package/dist/src/ui/commands/editorCommand.test.js.map +1 -0
- package/dist/src/ui/commands/extensionsCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/extensionsCommand.test.js +241 -0
- package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -0
- package/dist/src/ui/commands/helpCommand.js +1 -1
- package/dist/src/ui/commands/helpCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/helpCommand.test.js +42 -0
- package/dist/src/ui/commands/helpCommand.test.js.map +1 -0
- package/dist/src/ui/commands/ideCommand.js +6 -6
- package/dist/src/ui/commands/ideCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/ideCommand.test.js +203 -0
- package/dist/src/ui/commands/ideCommand.test.js.map +1 -0
- package/dist/src/ui/commands/initCommand.js +1 -1
- package/dist/src/ui/commands/initCommand.js.map +1 -1
- package/dist/src/ui/commands/initCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/initCommand.test.js +80 -0
- package/dist/src/ui/commands/initCommand.test.js.map +1 -0
- package/dist/src/ui/commands/mcpCommand.js +98 -88
- package/dist/src/ui/commands/mcpCommand.js.map +1 -1
- package/dist/src/ui/commands/mcpCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/mcpCommand.test.js +148 -0
- package/dist/src/ui/commands/mcpCommand.test.js.map +1 -0
- package/dist/src/ui/commands/memoryCommand.js +6 -6
- package/dist/src/ui/commands/memoryCommand.js.map +1 -1
- package/dist/src/ui/commands/memoryCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/memoryCommand.test.js +266 -0
- package/dist/src/ui/commands/memoryCommand.test.js.map +1 -0
- package/dist/src/ui/commands/privacyCommand.js +1 -1
- package/dist/src/ui/commands/privacyCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/privacyCommand.test.js +32 -0
- package/dist/src/ui/commands/privacyCommand.test.js.map +1 -0
- package/dist/src/ui/commands/quitCommand.js +1 -1
- package/dist/src/ui/commands/quitCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/quitCommand.test.js +50 -0
- package/dist/src/ui/commands/quitCommand.test.js.map +1 -0
- package/dist/src/ui/commands/restoreCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/restoreCommand.test.js +190 -0
- package/dist/src/ui/commands/restoreCommand.test.js.map +1 -0
- package/dist/src/ui/commands/settingsCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/settingsCommand.test.js +30 -0
- package/dist/src/ui/commands/settingsCommand.test.js.map +1 -0
- package/dist/src/ui/commands/setupGithubCommand.test.js +1 -2
- package/dist/src/ui/commands/setupGithubCommand.test.js.map +1 -1
- package/dist/src/ui/commands/statsCommand.js +3 -3
- package/dist/src/ui/commands/statsCommand.js.map +1 -1
- package/dist/src/ui/commands/statsCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/statsCommand.test.js +53 -0
- package/dist/src/ui/commands/statsCommand.test.js.map +1 -0
- package/dist/src/ui/commands/terminalSetupCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/terminalSetupCommand.test.js +66 -0
- package/dist/src/ui/commands/terminalSetupCommand.test.js.map +1 -0
- package/dist/src/ui/commands/themeCommand.js +1 -1
- package/dist/src/ui/commands/themeCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/themeCommand.test.js +32 -0
- package/dist/src/ui/commands/themeCommand.test.js.map +1 -0
- package/dist/src/ui/commands/toolsCommand.js +1 -1
- package/dist/src/ui/commands/toolsCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/toolsCommand.test.js +100 -0
- package/dist/src/ui/commands/toolsCommand.test.js.map +1 -0
- package/dist/src/ui/commands/vimCommand.js +1 -1
- package/dist/src/ui/components/Composer.js +5 -3
- package/dist/src/ui/components/Composer.js.map +1 -1
- package/dist/src/ui/components/Composer.test.js +16 -1
- package/dist/src/ui/components/Composer.test.js.map +1 -1
- package/dist/src/ui/components/ContextSummaryDisplay.d.ts +0 -1
- package/dist/src/ui/components/ContextSummaryDisplay.js +2 -12
- package/dist/src/ui/components/ContextSummaryDisplay.js.map +1 -1
- package/dist/src/ui/components/ContextSummaryDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/ContextSummaryDisplay.test.js +66 -0
- package/dist/src/ui/components/ContextSummaryDisplay.test.js.map +1 -0
- package/dist/src/ui/components/DialogManager.js +1 -5
- package/dist/src/ui/components/DialogManager.js.map +1 -1
- package/dist/src/ui/components/EditorSettingsDialog.js +1 -1
- package/dist/src/ui/components/EditorSettingsDialog.js.map +1 -1
- package/dist/src/ui/components/FolderTrustDialog.test.js +7 -3
- package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
- package/dist/src/ui/components/Footer.js +1 -1
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/Footer.test.d.ts +6 -0
- package/dist/src/ui/components/Footer.test.js +231 -0
- package/dist/src/ui/components/Footer.test.js.map +1 -0
- package/dist/src/ui/components/InputPrompt.d.ts +4 -0
- package/dist/src/ui/components/InputPrompt.js +53 -4
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.test.d.ts +6 -0
- package/dist/src/ui/components/InputPrompt.test.js +1716 -0
- package/dist/src/ui/components/InputPrompt.test.js.map +1 -0
- package/dist/src/ui/components/ModelStatsDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/ModelStatsDisplay.test.js +285 -0
- package/dist/src/ui/components/ModelStatsDisplay.test.js.map +1 -0
- package/dist/src/ui/components/PermissionsModifyTrustDialog.js +22 -18
- package/dist/src/ui/components/PermissionsModifyTrustDialog.js.map +1 -1
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +10 -2
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
- package/dist/src/ui/components/QueuedMessageDisplay.js +3 -3
- package/dist/src/ui/components/QueuedMessageDisplay.js.map +1 -1
- package/dist/src/ui/components/QueuedMessageDisplay.test.js +4 -0
- package/dist/src/ui/components/QueuedMessageDisplay.test.js.map +1 -1
- package/dist/src/ui/components/RawMarkdownIndicator.d.ts +7 -0
- package/dist/src/ui/components/RawMarkdownIndicator.js +8 -0
- package/dist/src/ui/components/RawMarkdownIndicator.js.map +1 -0
- package/dist/src/ui/components/SessionSummaryDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/SessionSummaryDisplay.test.js +74 -0
- package/dist/src/ui/components/SessionSummaryDisplay.test.js.map +1 -0
- package/dist/src/ui/components/SettingsDialog.js +8 -8
- package/dist/src/ui/components/SettingsDialog.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.test.js +189 -76
- package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
- package/dist/src/ui/components/StatsDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/StatsDisplay.test.js +351 -0
- package/dist/src/ui/components/StatsDisplay.test.js.map +1 -0
- package/dist/src/ui/components/ThemeDialog.d.ts +4 -2
- package/dist/src/ui/components/ThemeDialog.js +3 -3
- package/dist/src/ui/components/ThemeDialog.js.map +1 -1
- package/dist/src/ui/components/ThemeDialog.test.js +13 -0
- package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
- package/dist/src/ui/components/ToolStatsDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/ToolStatsDisplay.test.js +227 -0
- package/dist/src/ui/components/ToolStatsDisplay.test.js.map +1 -0
- package/dist/src/ui/components/messages/GeminiMessage.js +3 -1
- package/dist/src/ui/components/messages/GeminiMessage.js.map +1 -1
- package/dist/src/ui/components/messages/GeminiMessage.test.d.ts +6 -0
- package/dist/src/ui/components/messages/GeminiMessage.test.js +35 -0
- package/dist/src/ui/components/messages/GeminiMessage.test.js.map +1 -0
- package/dist/src/ui/components/messages/GeminiMessageContent.js +3 -1
- package/dist/src/ui/components/messages/GeminiMessageContent.js.map +1 -1
- package/dist/src/ui/components/messages/Todo.d.ts +7 -0
- package/dist/src/ui/components/messages/Todo.js +59 -0
- package/dist/src/ui/components/messages/Todo.js.map +1 -0
- package/dist/src/ui/components/messages/Todo.test.d.ts +6 -0
- package/dist/src/ui/components/messages/Todo.test.js +113 -0
- package/dist/src/ui/components/messages/Todo.test.js.map +1 -0
- package/dist/src/ui/components/messages/ToolGroupMessage.js +1 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessage.js +8 -3
- package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessage.test.js +2 -2
- package/dist/src/ui/components/messages/ToolMessage.test.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.d.ts +6 -0
- package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.js +30 -0
- package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.js.map +1 -0
- package/dist/src/ui/components/messages/UserShellMessage.js +1 -1
- package/dist/src/ui/components/messages/UserShellMessage.js.map +1 -1
- package/dist/src/ui/components/shared/BaseSelectionList.test.js +1 -1
- package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
- package/dist/src/ui/components/shared/text-buffer.test.d.ts +6 -0
- package/dist/src/ui/components/shared/text-buffer.test.js +1554 -0
- package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -0
- package/dist/src/ui/components/shared/vim-buffer-actions.test.d.ts +6 -0
- package/dist/src/ui/components/shared/vim-buffer-actions.test.js +951 -0
- package/dist/src/ui/components/shared/vim-buffer-actions.test.js.map +1 -0
- package/dist/src/ui/components/views/ExtensionsList.js +3 -4
- package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
- package/dist/src/ui/components/views/ExtensionsList.test.js +2 -9
- package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
- package/dist/src/ui/components/views/McpStatus.d.ts +0 -1
- package/dist/src/ui/components/views/McpStatus.js +4 -4
- package/dist/src/ui/components/views/McpStatus.js.map +1 -1
- package/dist/src/ui/components/views/McpStatus.test.js +0 -5
- package/dist/src/ui/components/views/McpStatus.test.js.map +1 -1
- package/dist/src/ui/components/views/ToolsList.test.js +4 -4
- package/dist/src/ui/components/views/ToolsList.test.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.d.ts +1 -0
- package/dist/src/ui/contexts/KeypressContext.js +176 -50
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.test.js +413 -14
- package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
- package/dist/src/ui/contexts/SessionContext.test.d.ts +6 -0
- package/dist/src/ui/contexts/SessionContext.test.js +177 -0
- package/dist/src/ui/contexts/SessionContext.test.js.map +1 -0
- package/dist/src/ui/contexts/UIActionsContext.d.ts +5 -4
- package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
- package/dist/src/ui/contexts/UIStateContext.d.ts +3 -3
- package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.test.d.ts +6 -0
- package/dist/src/ui/hooks/slashCommandProcessor.test.js +779 -0
- package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -0
- package/dist/src/ui/hooks/useAtCompletion.js +2 -2
- package/dist/src/ui/hooks/useAtCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useAtCompletion.test.d.ts +6 -0
- package/dist/src/ui/hooks/useAtCompletion.test.js +385 -0
- package/dist/src/ui/hooks/useAtCompletion.test.js.map +1 -0
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +0 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
- package/dist/src/ui/hooks/useCommandCompletion.d.ts +1 -1
- package/dist/src/ui/hooks/useCommandCompletion.js +5 -3
- package/dist/src/ui/hooks/useCommandCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useCommandCompletion.test.d.ts +6 -0
- package/dist/src/ui/hooks/useCommandCompletion.test.js +375 -0
- package/dist/src/ui/hooks/useCommandCompletion.test.js.map +1 -0
- package/dist/src/ui/hooks/useConsoleMessages.test.d.ts +6 -0
- package/dist/src/ui/hooks/useConsoleMessages.test.js +110 -0
- package/dist/src/ui/hooks/useConsoleMessages.test.js.map +1 -0
- package/dist/src/ui/hooks/useExtensionUpdates.d.ts +2 -1
- package/dist/src/ui/hooks/useExtensionUpdates.js +5 -3
- package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionUpdates.test.js +22 -13
- package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
- package/dist/src/ui/hooks/useFocus.test.d.ts +6 -0
- package/dist/src/ui/hooks/useFocus.test.js +115 -0
- package/dist/src/ui/hooks/useFocus.test.js.map +1 -0
- package/dist/src/ui/hooks/useFolderTrust.test.d.ts +6 -0
- package/dist/src/ui/hooks/useFolderTrust.test.js +164 -0
- package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -0
- package/dist/src/ui/hooks/useGeminiStream.js +33 -31
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.test.d.ts +6 -0
- package/dist/src/ui/hooks/useGeminiStream.test.js +1936 -0
- package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -0
- package/dist/src/ui/hooks/useKeypress.test.d.ts +6 -0
- package/dist/src/ui/hooks/useKeypress.test.js +234 -0
- package/dist/src/ui/hooks/useKeypress.test.js.map +1 -0
- package/dist/src/ui/hooks/useLoadingIndicator.test.js +5 -0
- package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
- package/dist/src/ui/hooks/useMessageQueue.d.ts +1 -0
- package/dist/src/ui/hooks/useMessageQueue.js +14 -0
- package/dist/src/ui/hooks/useMessageQueue.js.map +1 -1
- package/dist/src/ui/hooks/useMessageQueue.test.js +121 -0
- package/dist/src/ui/hooks/useMessageQueue.test.js.map +1 -1
- package/dist/src/ui/hooks/usePhraseCycler.d.ts +1 -0
- package/dist/src/ui/hooks/usePhraseCycler.js +156 -5
- package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
- package/dist/src/ui/hooks/usePhraseCycler.test.d.ts +6 -0
- package/dist/src/ui/hooks/usePhraseCycler.test.js +155 -0
- package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -0
- package/dist/src/ui/hooks/useThemeCommand.d.ts +2 -1
- package/dist/src/ui/hooks/useThemeCommand.js +6 -0
- package/dist/src/ui/hooks/useThemeCommand.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +3 -0
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/hooks/vim.test.d.ts +6 -0
- package/dist/src/ui/hooks/vim.test.js +1389 -0
- package/dist/src/ui/hooks/vim.test.js.map +1 -0
- package/dist/src/ui/keyMatchers.test.js +9 -3
- package/dist/src/ui/keyMatchers.test.js.map +1 -1
- package/dist/src/ui/themes/theme.test.d.ts +6 -0
- package/dist/src/ui/themes/theme.test.js +85 -0
- package/dist/src/ui/themes/theme.test.js.map +1 -0
- package/dist/src/ui/types.d.ts +0 -1
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/ui/utils/CodeColorizer.d.ts +1 -1
- package/dist/src/ui/utils/CodeColorizer.js +4 -2
- package/dist/src/ui/utils/CodeColorizer.js.map +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.d.ts +1 -0
- package/dist/src/ui/utils/MarkdownDisplay.js +8 -1
- package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
- package/dist/src/ui/utils/commandUtils.js +18 -2
- package/dist/src/ui/utils/commandUtils.js.map +1 -1
- package/dist/src/ui/utils/commandUtils.test.js +61 -6
- package/dist/src/ui/utils/commandUtils.test.js.map +1 -1
- package/dist/src/ui/utils/computeStats.js +5 -2
- package/dist/src/ui/utils/computeStats.js.map +1 -1
- package/dist/src/ui/utils/computeStats.test.d.ts +6 -0
- package/dist/src/ui/utils/computeStats.test.js +262 -0
- package/dist/src/ui/utils/computeStats.test.js.map +1 -0
- package/dist/src/ui/utils/updateCheck.d.ts +2 -1
- package/dist/src/ui/utils/updateCheck.js +4 -1
- package/dist/src/ui/utils/updateCheck.js.map +1 -1
- package/dist/src/ui/utils/updateCheck.test.js +25 -10
- package/dist/src/ui/utils/updateCheck.test.js.map +1 -1
- package/dist/src/utils/cleanup.test.d.ts +6 -0
- package/dist/src/utils/cleanup.test.js +49 -0
- package/dist/src/utils/cleanup.test.js.map +1 -0
- package/dist/src/utils/errors.d.ts +1 -0
- package/dist/src/utils/errors.js +66 -5
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/handleAutoUpdate.test.d.ts +6 -0
- package/dist/src/utils/handleAutoUpdate.test.js +225 -0
- package/dist/src/utils/handleAutoUpdate.test.js.map +1 -0
- package/dist/src/utils/sandbox-macos-permissive-open.sb +3 -1
- package/dist/src/utils/startupWarnings.test.d.ts +6 -0
- package/dist/src/utils/startupWarnings.test.js +61 -0
- package/dist/src/utils/startupWarnings.test.js.map +1 -0
- package/dist/src/validateNonInterActiveAuth.js +2 -2
- package/dist/src/validateNonInterActiveAuth.js.map +1 -1
- package/dist/src/validateNonInterActiveAuth.test.d.ts +6 -0
- package/dist/src/validateNonInterActiveAuth.test.js +336 -0
- package/dist/src/validateNonInterActiveAuth.test.js.map +1 -0
- package/dist/src/zed-integration/zedIntegration.js +1 -3
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/dist/google-gemini-cli-0.10.0-preview.2.tgz +0 -0
- package/dist/src/ui/components/WorkspaceMigrationDialog.d.ts +0 -11
- package/dist/src/ui/components/WorkspaceMigrationDialog.js +0 -44
- package/dist/src/ui/components/WorkspaceMigrationDialog.js.map +0 -1
- package/dist/src/ui/hooks/useWorkspaceMigration.d.ts +0 -13
- package/dist/src/ui/hooks/useWorkspaceMigration.js +0 -59
- package/dist/src/ui/hooks/useWorkspaceMigration.js.map +0 -1
|
@@ -0,0 +1,1961 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
7
|
+
import * as os from 'node:os';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { DEFAULT_GEMINI_MODEL, DEFAULT_GEMINI_MODEL_AUTO, OutputFormat, SHELL_TOOL_NAME, WRITE_FILE_TOOL_NAME, EDIT_TOOL_NAME, } from '@google/gemini-cli-core';
|
|
10
|
+
import { loadCliConfig, parseArguments } from './config.js';
|
|
11
|
+
import * as ServerConfig from '@google/gemini-cli-core';
|
|
12
|
+
import { isWorkspaceTrusted } from './trustedFolders.js';
|
|
13
|
+
vi.mock('./trustedFolders.js', () => ({
|
|
14
|
+
isWorkspaceTrusted: vi
|
|
15
|
+
.fn()
|
|
16
|
+
.mockReturnValue({ isTrusted: true, source: 'file' }), // Default to trusted
|
|
17
|
+
}));
|
|
18
|
+
vi.mock('./sandboxConfig.js', () => ({
|
|
19
|
+
loadSandboxConfig: vi.fn().mockResolvedValue(undefined),
|
|
20
|
+
}));
|
|
21
|
+
vi.mock('fs', async (importOriginal) => {
|
|
22
|
+
const actualFs = await importOriginal();
|
|
23
|
+
const pathMod = await import('node:path');
|
|
24
|
+
const mockHome = '/mock/home/user';
|
|
25
|
+
const MOCK_CWD1 = process.cwd();
|
|
26
|
+
const MOCK_CWD2 = pathMod.resolve(pathMod.sep, 'home', 'user', 'project');
|
|
27
|
+
const mockPaths = new Set([
|
|
28
|
+
MOCK_CWD1,
|
|
29
|
+
MOCK_CWD2,
|
|
30
|
+
pathMod.resolve(pathMod.sep, 'cli', 'path1'),
|
|
31
|
+
pathMod.resolve(pathMod.sep, 'settings', 'path1'),
|
|
32
|
+
pathMod.join(mockHome, 'settings', 'path2'),
|
|
33
|
+
pathMod.join(MOCK_CWD2, 'cli', 'path2'),
|
|
34
|
+
pathMod.join(MOCK_CWD2, 'settings', 'path3'),
|
|
35
|
+
]);
|
|
36
|
+
return {
|
|
37
|
+
...actualFs,
|
|
38
|
+
mkdirSync: vi.fn(),
|
|
39
|
+
writeFileSync: vi.fn(),
|
|
40
|
+
existsSync: vi.fn((p) => mockPaths.has(p.toString())),
|
|
41
|
+
statSync: vi.fn((p) => {
|
|
42
|
+
if (mockPaths.has(p.toString())) {
|
|
43
|
+
return { isDirectory: () => true };
|
|
44
|
+
}
|
|
45
|
+
return actualFs.statSync(p);
|
|
46
|
+
}),
|
|
47
|
+
realpathSync: vi.fn((p) => p),
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
vi.mock('os', async (importOriginal) => {
|
|
51
|
+
const actualOs = await importOriginal();
|
|
52
|
+
return {
|
|
53
|
+
...actualOs,
|
|
54
|
+
homedir: vi.fn(() => '/mock/home/user'),
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
vi.mock('open', () => ({
|
|
58
|
+
default: vi.fn(),
|
|
59
|
+
}));
|
|
60
|
+
vi.mock('read-package-up', () => ({
|
|
61
|
+
readPackageUp: vi.fn(() => Promise.resolve({ packageJson: { version: 'test-version' } })),
|
|
62
|
+
}));
|
|
63
|
+
vi.mock('@google/gemini-cli-core', async () => {
|
|
64
|
+
const actualServer = await vi.importActual('@google/gemini-cli-core');
|
|
65
|
+
return {
|
|
66
|
+
...actualServer,
|
|
67
|
+
IdeClient: {
|
|
68
|
+
getInstance: vi.fn().mockResolvedValue({
|
|
69
|
+
getConnectionStatus: vi.fn(),
|
|
70
|
+
initialize: vi.fn(),
|
|
71
|
+
shutdown: vi.fn(),
|
|
72
|
+
}),
|
|
73
|
+
},
|
|
74
|
+
loadEnvironment: vi.fn(),
|
|
75
|
+
loadServerHierarchicalMemory: vi.fn((cwd, dirs, debug, fileService, extensionPaths, _maxDirs) => Promise.resolve({
|
|
76
|
+
memoryContent: extensionPaths?.join(',') || '',
|
|
77
|
+
fileCount: extensionPaths?.length || 0,
|
|
78
|
+
})),
|
|
79
|
+
DEFAULT_MEMORY_FILE_FILTERING_OPTIONS: {
|
|
80
|
+
respectGitIgnore: false,
|
|
81
|
+
respectGeminiIgnore: true,
|
|
82
|
+
},
|
|
83
|
+
DEFAULT_FILE_FILTERING_OPTIONS: {
|
|
84
|
+
respectGitIgnore: true,
|
|
85
|
+
respectGeminiIgnore: true,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
describe('parseArguments', () => {
|
|
90
|
+
const originalArgv = process.argv;
|
|
91
|
+
afterEach(() => {
|
|
92
|
+
process.argv = originalArgv;
|
|
93
|
+
});
|
|
94
|
+
it('should throw an error when both --prompt and --prompt-interactive are used together', async () => {
|
|
95
|
+
process.argv = [
|
|
96
|
+
'node',
|
|
97
|
+
'script.js',
|
|
98
|
+
'--prompt',
|
|
99
|
+
'test prompt',
|
|
100
|
+
'--prompt-interactive',
|
|
101
|
+
'interactive prompt',
|
|
102
|
+
];
|
|
103
|
+
const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
|
|
104
|
+
throw new Error('process.exit called');
|
|
105
|
+
});
|
|
106
|
+
const mockConsoleError = vi
|
|
107
|
+
.spyOn(console, 'error')
|
|
108
|
+
.mockImplementation(() => { });
|
|
109
|
+
await expect(parseArguments({})).rejects.toThrow('process.exit called');
|
|
110
|
+
expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Cannot use both --prompt (-p) and --prompt-interactive (-i) together'));
|
|
111
|
+
mockExit.mockRestore();
|
|
112
|
+
mockConsoleError.mockRestore();
|
|
113
|
+
});
|
|
114
|
+
it('should throw an error when using short flags -p and -i together', async () => {
|
|
115
|
+
process.argv = [
|
|
116
|
+
'node',
|
|
117
|
+
'script.js',
|
|
118
|
+
'-p',
|
|
119
|
+
'test prompt',
|
|
120
|
+
'-i',
|
|
121
|
+
'interactive prompt',
|
|
122
|
+
];
|
|
123
|
+
const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
|
|
124
|
+
throw new Error('process.exit called');
|
|
125
|
+
});
|
|
126
|
+
const mockConsoleError = vi
|
|
127
|
+
.spyOn(console, 'error')
|
|
128
|
+
.mockImplementation(() => { });
|
|
129
|
+
await expect(parseArguments({})).rejects.toThrow('process.exit called');
|
|
130
|
+
expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Cannot use both --prompt (-p) and --prompt-interactive (-i) together'));
|
|
131
|
+
mockExit.mockRestore();
|
|
132
|
+
mockConsoleError.mockRestore();
|
|
133
|
+
});
|
|
134
|
+
it('should allow --prompt without --prompt-interactive', async () => {
|
|
135
|
+
process.argv = ['node', 'script.js', '--prompt', 'test prompt'];
|
|
136
|
+
const argv = await parseArguments({});
|
|
137
|
+
expect(argv.prompt).toBe('test prompt');
|
|
138
|
+
expect(argv.promptInteractive).toBeUndefined();
|
|
139
|
+
});
|
|
140
|
+
it('should allow --prompt-interactive without --prompt', async () => {
|
|
141
|
+
process.argv = [
|
|
142
|
+
'node',
|
|
143
|
+
'script.js',
|
|
144
|
+
'--prompt-interactive',
|
|
145
|
+
'interactive prompt',
|
|
146
|
+
];
|
|
147
|
+
const argv = await parseArguments({});
|
|
148
|
+
expect(argv.promptInteractive).toBe('interactive prompt');
|
|
149
|
+
expect(argv.prompt).toBeUndefined();
|
|
150
|
+
});
|
|
151
|
+
it('should allow -i flag as alias for --prompt-interactive', async () => {
|
|
152
|
+
process.argv = ['node', 'script.js', '-i', 'interactive prompt'];
|
|
153
|
+
const argv = await parseArguments({});
|
|
154
|
+
expect(argv.promptInteractive).toBe('interactive prompt');
|
|
155
|
+
expect(argv.prompt).toBeUndefined();
|
|
156
|
+
});
|
|
157
|
+
it('should convert positional query argument to prompt by default', async () => {
|
|
158
|
+
process.argv = ['node', 'script.js', 'Hi Gemini'];
|
|
159
|
+
const argv = await parseArguments({});
|
|
160
|
+
expect(argv.query).toBe('Hi Gemini');
|
|
161
|
+
expect(argv.prompt).toBe('Hi Gemini');
|
|
162
|
+
expect(argv.promptInteractive).toBeUndefined();
|
|
163
|
+
});
|
|
164
|
+
it('should map @path to prompt (one-shot) when it starts with @', async () => {
|
|
165
|
+
process.argv = ['node', 'script.js', '@path ./file.md'];
|
|
166
|
+
const argv = await parseArguments({});
|
|
167
|
+
expect(argv.query).toBe('@path ./file.md');
|
|
168
|
+
expect(argv.prompt).toBe('@path ./file.md');
|
|
169
|
+
expect(argv.promptInteractive).toBeUndefined();
|
|
170
|
+
});
|
|
171
|
+
it('should map @path to prompt even when config flags are present', async () => {
|
|
172
|
+
// @path queries should now go to one-shot mode regardless of other flags
|
|
173
|
+
process.argv = [
|
|
174
|
+
'node',
|
|
175
|
+
'script.js',
|
|
176
|
+
'@path',
|
|
177
|
+
'./file.md',
|
|
178
|
+
'--model',
|
|
179
|
+
'gemini-1.5-pro',
|
|
180
|
+
];
|
|
181
|
+
const argv = await parseArguments({});
|
|
182
|
+
expect(argv.query).toBe('@path ./file.md');
|
|
183
|
+
expect(argv.prompt).toBe('@path ./file.md'); // Should map to one-shot
|
|
184
|
+
expect(argv.promptInteractive).toBeUndefined();
|
|
185
|
+
expect(argv.model).toBe('gemini-1.5-pro');
|
|
186
|
+
});
|
|
187
|
+
it('maps unquoted positional @path + arg to prompt (one-shot)', async () => {
|
|
188
|
+
// Simulate: gemini @path ./file.md
|
|
189
|
+
process.argv = ['node', 'script.js', '@path', './file.md'];
|
|
190
|
+
const argv = await parseArguments({});
|
|
191
|
+
// After normalization, query is a single string
|
|
192
|
+
expect(argv.query).toBe('@path ./file.md');
|
|
193
|
+
// And it's mapped to one-shot prompt when no -p/-i flags are set
|
|
194
|
+
expect(argv.prompt).toBe('@path ./file.md');
|
|
195
|
+
expect(argv.promptInteractive).toBeUndefined();
|
|
196
|
+
});
|
|
197
|
+
it('should handle multiple @path arguments in a single command (one-shot)', async () => {
|
|
198
|
+
// Simulate: gemini @path ./file1.md @path ./file2.md
|
|
199
|
+
process.argv = [
|
|
200
|
+
'node',
|
|
201
|
+
'script.js',
|
|
202
|
+
'@path',
|
|
203
|
+
'./file1.md',
|
|
204
|
+
'@path',
|
|
205
|
+
'./file2.md',
|
|
206
|
+
];
|
|
207
|
+
const argv = await parseArguments({});
|
|
208
|
+
// After normalization, all arguments are joined with spaces
|
|
209
|
+
expect(argv.query).toBe('@path ./file1.md @path ./file2.md');
|
|
210
|
+
// And it's mapped to one-shot prompt
|
|
211
|
+
expect(argv.prompt).toBe('@path ./file1.md @path ./file2.md');
|
|
212
|
+
expect(argv.promptInteractive).toBeUndefined();
|
|
213
|
+
});
|
|
214
|
+
it('should handle mixed quoted and unquoted @path arguments (one-shot)', async () => {
|
|
215
|
+
// Simulate: gemini "@path ./file1.md" @path ./file2.md "additional text"
|
|
216
|
+
process.argv = [
|
|
217
|
+
'node',
|
|
218
|
+
'script.js',
|
|
219
|
+
'@path ./file1.md',
|
|
220
|
+
'@path',
|
|
221
|
+
'./file2.md',
|
|
222
|
+
'additional text',
|
|
223
|
+
];
|
|
224
|
+
const argv = await parseArguments({});
|
|
225
|
+
// After normalization, all arguments are joined with spaces
|
|
226
|
+
expect(argv.query).toBe('@path ./file1.md @path ./file2.md additional text');
|
|
227
|
+
// And it's mapped to one-shot prompt
|
|
228
|
+
expect(argv.prompt).toBe('@path ./file1.md @path ./file2.md additional text');
|
|
229
|
+
expect(argv.promptInteractive).toBeUndefined();
|
|
230
|
+
});
|
|
231
|
+
it('should map @path to prompt with ambient flags (debug)', async () => {
|
|
232
|
+
// Ambient flags like debug should NOT affect routing
|
|
233
|
+
process.argv = ['node', 'script.js', '@path', './file.md', '--debug'];
|
|
234
|
+
const argv = await parseArguments({});
|
|
235
|
+
expect(argv.query).toBe('@path ./file.md');
|
|
236
|
+
expect(argv.prompt).toBe('@path ./file.md'); // Should map to one-shot
|
|
237
|
+
expect(argv.promptInteractive).toBeUndefined();
|
|
238
|
+
expect(argv.debug).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
it('should map any @command to prompt (one-shot)', async () => {
|
|
241
|
+
// Test that all @commands now go to one-shot mode
|
|
242
|
+
const testCases = [
|
|
243
|
+
'@path ./file.md',
|
|
244
|
+
'@include src/',
|
|
245
|
+
'@search pattern',
|
|
246
|
+
'@web query',
|
|
247
|
+
'@git status',
|
|
248
|
+
];
|
|
249
|
+
for (const testQuery of testCases) {
|
|
250
|
+
process.argv = ['node', 'script.js', testQuery];
|
|
251
|
+
const argv = await parseArguments({});
|
|
252
|
+
expect(argv.query).toBe(testQuery);
|
|
253
|
+
expect(argv.prompt).toBe(testQuery);
|
|
254
|
+
expect(argv.promptInteractive).toBeUndefined();
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
it('should handle @command with leading whitespace', async () => {
|
|
258
|
+
// Test that trim() + routing handles leading whitespace correctly
|
|
259
|
+
process.argv = ['node', 'script.js', ' @path ./file.md'];
|
|
260
|
+
const argv = await parseArguments({});
|
|
261
|
+
expect(argv.query).toBe(' @path ./file.md');
|
|
262
|
+
expect(argv.prompt).toBe(' @path ./file.md');
|
|
263
|
+
expect(argv.promptInteractive).toBeUndefined();
|
|
264
|
+
});
|
|
265
|
+
it('should throw an error when both --yolo and --approval-mode are used together', async () => {
|
|
266
|
+
process.argv = [
|
|
267
|
+
'node',
|
|
268
|
+
'script.js',
|
|
269
|
+
'--yolo',
|
|
270
|
+
'--approval-mode',
|
|
271
|
+
'default',
|
|
272
|
+
];
|
|
273
|
+
const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
|
|
274
|
+
throw new Error('process.exit called');
|
|
275
|
+
});
|
|
276
|
+
const mockConsoleError = vi
|
|
277
|
+
.spyOn(console, 'error')
|
|
278
|
+
.mockImplementation(() => { });
|
|
279
|
+
await expect(parseArguments({})).rejects.toThrow('process.exit called');
|
|
280
|
+
expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Cannot use both --yolo (-y) and --approval-mode together. Use --approval-mode=yolo instead.'));
|
|
281
|
+
mockExit.mockRestore();
|
|
282
|
+
mockConsoleError.mockRestore();
|
|
283
|
+
});
|
|
284
|
+
it('should throw an error when using short flags -y and --approval-mode together', async () => {
|
|
285
|
+
process.argv = ['node', 'script.js', '-y', '--approval-mode', 'yolo'];
|
|
286
|
+
const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
|
|
287
|
+
throw new Error('process.exit called');
|
|
288
|
+
});
|
|
289
|
+
const mockConsoleError = vi
|
|
290
|
+
.spyOn(console, 'error')
|
|
291
|
+
.mockImplementation(() => { });
|
|
292
|
+
await expect(parseArguments({})).rejects.toThrow('process.exit called');
|
|
293
|
+
expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Cannot use both --yolo (-y) and --approval-mode together. Use --approval-mode=yolo instead.'));
|
|
294
|
+
mockExit.mockRestore();
|
|
295
|
+
mockConsoleError.mockRestore();
|
|
296
|
+
});
|
|
297
|
+
it('should allow --approval-mode without --yolo', async () => {
|
|
298
|
+
process.argv = ['node', 'script.js', '--approval-mode', 'auto_edit'];
|
|
299
|
+
const argv = await parseArguments({});
|
|
300
|
+
expect(argv.approvalMode).toBe('auto_edit');
|
|
301
|
+
expect(argv.yolo).toBe(false);
|
|
302
|
+
});
|
|
303
|
+
it('should allow --yolo without --approval-mode', async () => {
|
|
304
|
+
process.argv = ['node', 'script.js', '--yolo'];
|
|
305
|
+
const argv = await parseArguments({});
|
|
306
|
+
expect(argv.yolo).toBe(true);
|
|
307
|
+
expect(argv.approvalMode).toBeUndefined();
|
|
308
|
+
});
|
|
309
|
+
it('should reject invalid --approval-mode values', async () => {
|
|
310
|
+
process.argv = ['node', 'script.js', '--approval-mode', 'invalid'];
|
|
311
|
+
const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
|
|
312
|
+
throw new Error('process.exit called');
|
|
313
|
+
});
|
|
314
|
+
const mockConsoleError = vi
|
|
315
|
+
.spyOn(console, 'error')
|
|
316
|
+
.mockImplementation(() => { });
|
|
317
|
+
await expect(parseArguments({})).rejects.toThrow('process.exit called');
|
|
318
|
+
expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Invalid values:'));
|
|
319
|
+
mockExit.mockRestore();
|
|
320
|
+
mockConsoleError.mockRestore();
|
|
321
|
+
});
|
|
322
|
+
it('should support comma-separated values for --allowed-tools', async () => {
|
|
323
|
+
process.argv = [
|
|
324
|
+
'node',
|
|
325
|
+
'script.js',
|
|
326
|
+
'--allowed-tools',
|
|
327
|
+
'read_file,ShellTool(git status)',
|
|
328
|
+
];
|
|
329
|
+
const argv = await parseArguments({});
|
|
330
|
+
expect(argv.allowedTools).toEqual(['read_file', 'ShellTool(git status)']);
|
|
331
|
+
});
|
|
332
|
+
it('should support comma-separated values for --allowed-mcp-server-names', async () => {
|
|
333
|
+
process.argv = [
|
|
334
|
+
'node',
|
|
335
|
+
'script.js',
|
|
336
|
+
'--allowed-mcp-server-names',
|
|
337
|
+
'server1,server2',
|
|
338
|
+
];
|
|
339
|
+
const argv = await parseArguments({});
|
|
340
|
+
expect(argv.allowedMcpServerNames).toEqual(['server1', 'server2']);
|
|
341
|
+
});
|
|
342
|
+
it('should support comma-separated values for --extensions', async () => {
|
|
343
|
+
process.argv = ['node', 'script.js', '--extensions', 'ext1,ext2'];
|
|
344
|
+
const argv = await parseArguments({});
|
|
345
|
+
expect(argv.extensions).toEqual(['ext1', 'ext2']);
|
|
346
|
+
});
|
|
347
|
+
it('should correctly parse positional arguments when flags with arguments are present', async () => {
|
|
348
|
+
process.argv = [
|
|
349
|
+
'node',
|
|
350
|
+
'script.js',
|
|
351
|
+
'--model',
|
|
352
|
+
'test-model-string',
|
|
353
|
+
'my-positional-arg',
|
|
354
|
+
];
|
|
355
|
+
const argv = await parseArguments({});
|
|
356
|
+
expect(argv.model).toBe('test-model-string');
|
|
357
|
+
expect(argv.query).toBe('my-positional-arg');
|
|
358
|
+
});
|
|
359
|
+
it('should handle long positional prompts with multiple flags', async () => {
|
|
360
|
+
process.argv = [
|
|
361
|
+
'node',
|
|
362
|
+
'script.js',
|
|
363
|
+
'-e',
|
|
364
|
+
'none',
|
|
365
|
+
'--approval-mode=auto_edit',
|
|
366
|
+
'--allowed-tools=ShellTool',
|
|
367
|
+
'--allowed-tools=ShellTool(whoami)',
|
|
368
|
+
'--allowed-tools=ShellTool(wc)',
|
|
369
|
+
'Use whoami to write a poem in file poem.md about my username in pig latin and use wc to tell me how many lines are in the poem you wrote.',
|
|
370
|
+
];
|
|
371
|
+
const argv = await parseArguments({});
|
|
372
|
+
expect(argv.extensions).toEqual(['none']);
|
|
373
|
+
expect(argv.approvalMode).toBe('auto_edit');
|
|
374
|
+
expect(argv.allowedTools).toEqual([
|
|
375
|
+
'ShellTool',
|
|
376
|
+
'ShellTool(whoami)',
|
|
377
|
+
'ShellTool(wc)',
|
|
378
|
+
]);
|
|
379
|
+
expect(argv.query).toBe('Use whoami to write a poem in file poem.md about my username in pig latin and use wc to tell me how many lines are in the poem you wrote.');
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
describe('loadCliConfig', () => {
|
|
383
|
+
const originalArgv = process.argv;
|
|
384
|
+
beforeEach(() => {
|
|
385
|
+
vi.resetAllMocks();
|
|
386
|
+
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
|
387
|
+
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
|
388
|
+
});
|
|
389
|
+
afterEach(() => {
|
|
390
|
+
process.argv = originalArgv;
|
|
391
|
+
vi.unstubAllEnvs();
|
|
392
|
+
vi.restoreAllMocks();
|
|
393
|
+
});
|
|
394
|
+
describe('Proxy configuration', () => {
|
|
395
|
+
const originalProxyEnv = {};
|
|
396
|
+
const proxyEnvVars = [
|
|
397
|
+
'HTTP_PROXY',
|
|
398
|
+
'HTTPS_PROXY',
|
|
399
|
+
'http_proxy',
|
|
400
|
+
'https_proxy',
|
|
401
|
+
];
|
|
402
|
+
beforeEach(() => {
|
|
403
|
+
for (const key of proxyEnvVars) {
|
|
404
|
+
originalProxyEnv[key] = process.env[key];
|
|
405
|
+
delete process.env[key];
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
afterEach(() => {
|
|
409
|
+
for (const key of proxyEnvVars) {
|
|
410
|
+
if (originalProxyEnv[key]) {
|
|
411
|
+
process.env[key] = originalProxyEnv[key];
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
delete process.env[key];
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
it(`should leave proxy to empty by default`, async () => {
|
|
419
|
+
process.argv = ['node', 'script.js'];
|
|
420
|
+
const argv = await parseArguments({});
|
|
421
|
+
const settings = {};
|
|
422
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
423
|
+
expect(config.getProxy()).toBeFalsy();
|
|
424
|
+
});
|
|
425
|
+
const proxy_url = 'http://localhost:7890';
|
|
426
|
+
const testCases = [
|
|
427
|
+
{
|
|
428
|
+
input: {
|
|
429
|
+
env_name: 'https_proxy',
|
|
430
|
+
proxy_url,
|
|
431
|
+
},
|
|
432
|
+
expected: proxy_url,
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
input: {
|
|
436
|
+
env_name: 'http_proxy',
|
|
437
|
+
proxy_url,
|
|
438
|
+
},
|
|
439
|
+
expected: proxy_url,
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
input: {
|
|
443
|
+
env_name: 'HTTPS_PROXY',
|
|
444
|
+
proxy_url,
|
|
445
|
+
},
|
|
446
|
+
expected: proxy_url,
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
input: {
|
|
450
|
+
env_name: 'HTTP_PROXY',
|
|
451
|
+
proxy_url,
|
|
452
|
+
},
|
|
453
|
+
expected: proxy_url,
|
|
454
|
+
},
|
|
455
|
+
];
|
|
456
|
+
testCases.forEach(({ input, expected }) => {
|
|
457
|
+
it(`should set proxy to ${expected} according to environment variable [${input.env_name}]`, async () => {
|
|
458
|
+
vi.stubEnv(input.env_name, input.proxy_url);
|
|
459
|
+
process.argv = ['node', 'script.js'];
|
|
460
|
+
const argv = await parseArguments({});
|
|
461
|
+
const settings = {};
|
|
462
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
463
|
+
expect(config.getProxy()).toBe(expected);
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => {
|
|
469
|
+
beforeEach(() => {
|
|
470
|
+
vi.resetAllMocks();
|
|
471
|
+
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
|
472
|
+
// Other common mocks would be reset here.
|
|
473
|
+
});
|
|
474
|
+
afterEach(() => {
|
|
475
|
+
vi.restoreAllMocks();
|
|
476
|
+
});
|
|
477
|
+
it('should pass extension context file paths to loadServerHierarchicalMemory', async () => {
|
|
478
|
+
process.argv = ['node', 'script.js'];
|
|
479
|
+
const settings = {};
|
|
480
|
+
const extensions = [
|
|
481
|
+
{
|
|
482
|
+
path: '/path/to/ext1',
|
|
483
|
+
name: 'ext1',
|
|
484
|
+
version: '1.0.0',
|
|
485
|
+
contextFiles: ['/path/to/ext1/GEMINI.md'],
|
|
486
|
+
isActive: true,
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
path: '/path/to/ext2',
|
|
490
|
+
name: 'ext2',
|
|
491
|
+
version: '1.0.0',
|
|
492
|
+
contextFiles: [],
|
|
493
|
+
isActive: true,
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
path: '/path/to/ext3',
|
|
497
|
+
name: 'ext3',
|
|
498
|
+
version: '1.0.0',
|
|
499
|
+
contextFiles: [
|
|
500
|
+
'/path/to/ext3/context1.md',
|
|
501
|
+
'/path/to/ext3/context2.md',
|
|
502
|
+
],
|
|
503
|
+
isActive: true,
|
|
504
|
+
},
|
|
505
|
+
];
|
|
506
|
+
const argv = await parseArguments({});
|
|
507
|
+
await loadCliConfig(settings, extensions, 'session-id', argv);
|
|
508
|
+
expect(ServerConfig.loadServerHierarchicalMemory).toHaveBeenCalledWith(expect.any(String), [], false, expect.any(Object), extensions, true, 'tree', {
|
|
509
|
+
respectGitIgnore: false,
|
|
510
|
+
respectGeminiIgnore: true,
|
|
511
|
+
}, undefined);
|
|
512
|
+
});
|
|
513
|
+
// NOTE TO FUTURE DEVELOPERS:
|
|
514
|
+
// To re-enable tests for loadHierarchicalGeminiMemory, ensure that:
|
|
515
|
+
// 1. os.homedir() is reliably mocked *before* the config.ts module is loaded
|
|
516
|
+
// and its functions (which use os.homedir()) are called.
|
|
517
|
+
// 2. fs/promises and fs mocks correctly simulate file/directory existence,
|
|
518
|
+
// readability, and content based on paths derived from the mocked os.homedir().
|
|
519
|
+
// 3. Spies on console functions (for logger output) are correctly set up if needed.
|
|
520
|
+
// Example of a previously failing test structure:
|
|
521
|
+
it.skip('should correctly use mocked homedir for global path', async () => {
|
|
522
|
+
// This test is skipped because mockFs and fsPromises are not properly imported/mocked
|
|
523
|
+
// TODO: Fix this test by properly setting up mock-fs and fs/promises mocks
|
|
524
|
+
/*
|
|
525
|
+
const MOCK_GEMINI_DIR_LOCAL = path.join(
|
|
526
|
+
'/mock/home/user',
|
|
527
|
+
ServerConfig.GEMINI_DIR,
|
|
528
|
+
);
|
|
529
|
+
const MOCK_GLOBAL_PATH_LOCAL = path.join(
|
|
530
|
+
MOCK_GEMINI_DIR_LOCAL,
|
|
531
|
+
'GEMINI.md',
|
|
532
|
+
);
|
|
533
|
+
mockFs({
|
|
534
|
+
[MOCK_GLOBAL_PATH_LOCAL]: { type: 'file', content: 'GlobalContentOnly' },
|
|
535
|
+
});
|
|
536
|
+
const memory = await loadHierarchicalGeminiMemory('/some/other/cwd', false);
|
|
537
|
+
expect(memory).toBe('GlobalContentOnly');
|
|
538
|
+
expect(vi.mocked(os.homedir)).toHaveBeenCalled();
|
|
539
|
+
expect(fsPromises.readFile).toHaveBeenCalledWith(
|
|
540
|
+
MOCK_GLOBAL_PATH_LOCAL,
|
|
541
|
+
'utf-8',
|
|
542
|
+
);
|
|
543
|
+
*/
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
describe('mergeMcpServers', () => {
|
|
547
|
+
it('should not modify the original settings object', async () => {
|
|
548
|
+
const settings = {
|
|
549
|
+
mcpServers: {
|
|
550
|
+
'test-server': {
|
|
551
|
+
url: 'http://localhost:8080',
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
};
|
|
555
|
+
const extensions = [
|
|
556
|
+
{
|
|
557
|
+
path: '/path/to/ext1',
|
|
558
|
+
name: 'ext1',
|
|
559
|
+
version: '1.0.0',
|
|
560
|
+
mcpServers: {
|
|
561
|
+
'ext1-server': {
|
|
562
|
+
url: 'http://localhost:8081',
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
contextFiles: [],
|
|
566
|
+
isActive: true,
|
|
567
|
+
},
|
|
568
|
+
];
|
|
569
|
+
const originalSettings = JSON.parse(JSON.stringify(settings));
|
|
570
|
+
process.argv = ['node', 'script.js'];
|
|
571
|
+
const argv = await parseArguments({});
|
|
572
|
+
await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
573
|
+
expect(settings).toEqual(originalSettings);
|
|
574
|
+
});
|
|
575
|
+
});
|
|
576
|
+
describe('mergeExcludeTools', () => {
|
|
577
|
+
const defaultExcludes = [
|
|
578
|
+
SHELL_TOOL_NAME,
|
|
579
|
+
EDIT_TOOL_NAME,
|
|
580
|
+
WRITE_FILE_TOOL_NAME,
|
|
581
|
+
];
|
|
582
|
+
const originalIsTTY = process.stdin.isTTY;
|
|
583
|
+
beforeEach(() => {
|
|
584
|
+
process.stdin.isTTY = true;
|
|
585
|
+
});
|
|
586
|
+
afterEach(() => {
|
|
587
|
+
process.stdin.isTTY = originalIsTTY;
|
|
588
|
+
});
|
|
589
|
+
it('should merge excludeTools from settings and extensions', async () => {
|
|
590
|
+
const settings = { tools: { exclude: ['tool1', 'tool2'] } };
|
|
591
|
+
const extensions = [
|
|
592
|
+
{
|
|
593
|
+
path: '/path/to/ext1',
|
|
594
|
+
name: 'ext1',
|
|
595
|
+
version: '1.0.0',
|
|
596
|
+
excludeTools: ['tool3', 'tool4'],
|
|
597
|
+
contextFiles: [],
|
|
598
|
+
isActive: true,
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
path: '/path/to/ext2',
|
|
602
|
+
name: 'ext2',
|
|
603
|
+
version: '1.0.0',
|
|
604
|
+
excludeTools: ['tool5'],
|
|
605
|
+
contextFiles: [],
|
|
606
|
+
isActive: true,
|
|
607
|
+
},
|
|
608
|
+
];
|
|
609
|
+
process.argv = ['node', 'script.js'];
|
|
610
|
+
const argv = await parseArguments({});
|
|
611
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
612
|
+
expect(config.getExcludeTools()).toEqual(expect.arrayContaining(['tool1', 'tool2', 'tool3', 'tool4', 'tool5']));
|
|
613
|
+
expect(config.getExcludeTools()).toHaveLength(5);
|
|
614
|
+
});
|
|
615
|
+
it('should handle overlapping excludeTools between settings and extensions', async () => {
|
|
616
|
+
const settings = { tools: { exclude: ['tool1', 'tool2'] } };
|
|
617
|
+
const extensions = [
|
|
618
|
+
{
|
|
619
|
+
path: '/path/to/ext1',
|
|
620
|
+
name: 'ext1',
|
|
621
|
+
version: '1.0.0',
|
|
622
|
+
excludeTools: ['tool2', 'tool3'],
|
|
623
|
+
contextFiles: [],
|
|
624
|
+
isActive: true,
|
|
625
|
+
},
|
|
626
|
+
];
|
|
627
|
+
process.argv = ['node', 'script.js'];
|
|
628
|
+
const argv = await parseArguments({});
|
|
629
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
630
|
+
expect(config.getExcludeTools()).toEqual(expect.arrayContaining(['tool1', 'tool2', 'tool3']));
|
|
631
|
+
expect(config.getExcludeTools()).toHaveLength(3);
|
|
632
|
+
});
|
|
633
|
+
it('should handle overlapping excludeTools between extensions', async () => {
|
|
634
|
+
const settings = { tools: { exclude: ['tool1'] } };
|
|
635
|
+
const extensions = [
|
|
636
|
+
{
|
|
637
|
+
path: '/path/to/ext1',
|
|
638
|
+
name: 'ext1',
|
|
639
|
+
version: '1.0.0',
|
|
640
|
+
excludeTools: ['tool2', 'tool3'],
|
|
641
|
+
contextFiles: [],
|
|
642
|
+
isActive: true,
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
path: '/path/to/ext2',
|
|
646
|
+
name: 'ext2',
|
|
647
|
+
version: '1.0.0',
|
|
648
|
+
excludeTools: ['tool3', 'tool4'],
|
|
649
|
+
contextFiles: [],
|
|
650
|
+
isActive: true,
|
|
651
|
+
},
|
|
652
|
+
];
|
|
653
|
+
process.argv = ['node', 'script.js'];
|
|
654
|
+
const argv = await parseArguments({});
|
|
655
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
656
|
+
expect(config.getExcludeTools()).toEqual(expect.arrayContaining(['tool1', 'tool2', 'tool3', 'tool4']));
|
|
657
|
+
expect(config.getExcludeTools()).toHaveLength(4);
|
|
658
|
+
});
|
|
659
|
+
it('should return an empty array when no excludeTools are specified and it is interactive', async () => {
|
|
660
|
+
process.stdin.isTTY = true;
|
|
661
|
+
const settings = {};
|
|
662
|
+
const extensions = [];
|
|
663
|
+
process.argv = ['node', 'script.js'];
|
|
664
|
+
const argv = await parseArguments({});
|
|
665
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
666
|
+
expect(config.getExcludeTools()).toEqual([]);
|
|
667
|
+
});
|
|
668
|
+
it('should return default excludes when no excludeTools are specified and it is not interactive', async () => {
|
|
669
|
+
process.stdin.isTTY = false;
|
|
670
|
+
const settings = {};
|
|
671
|
+
const extensions = [];
|
|
672
|
+
process.argv = ['node', 'script.js', '-p', 'test'];
|
|
673
|
+
const argv = await parseArguments({});
|
|
674
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
675
|
+
expect(config.getExcludeTools()).toEqual(defaultExcludes);
|
|
676
|
+
});
|
|
677
|
+
it('should handle settings with excludeTools but no extensions', async () => {
|
|
678
|
+
process.argv = ['node', 'script.js'];
|
|
679
|
+
const argv = await parseArguments({});
|
|
680
|
+
const settings = { tools: { exclude: ['tool1', 'tool2'] } };
|
|
681
|
+
const extensions = [];
|
|
682
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
683
|
+
expect(config.getExcludeTools()).toEqual(expect.arrayContaining(['tool1', 'tool2']));
|
|
684
|
+
expect(config.getExcludeTools()).toHaveLength(2);
|
|
685
|
+
});
|
|
686
|
+
it('should handle extensions with excludeTools but no settings', async () => {
|
|
687
|
+
const settings = {};
|
|
688
|
+
const extensions = [
|
|
689
|
+
{
|
|
690
|
+
path: '/path/to/ext',
|
|
691
|
+
name: 'ext1',
|
|
692
|
+
version: '1.0.0',
|
|
693
|
+
excludeTools: ['tool1', 'tool2'],
|
|
694
|
+
contextFiles: [],
|
|
695
|
+
isActive: true,
|
|
696
|
+
},
|
|
697
|
+
];
|
|
698
|
+
process.argv = ['node', 'script.js'];
|
|
699
|
+
const argv = await parseArguments({});
|
|
700
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
701
|
+
expect(config.getExcludeTools()).toEqual(expect.arrayContaining(['tool1', 'tool2']));
|
|
702
|
+
expect(config.getExcludeTools()).toHaveLength(2);
|
|
703
|
+
});
|
|
704
|
+
it('should not modify the original settings object', async () => {
|
|
705
|
+
const settings = { tools: { exclude: ['tool1'] } };
|
|
706
|
+
const extensions = [
|
|
707
|
+
{
|
|
708
|
+
path: '/path/to/ext',
|
|
709
|
+
name: 'ext1',
|
|
710
|
+
version: '1.0.0',
|
|
711
|
+
excludeTools: ['tool2'],
|
|
712
|
+
contextFiles: [],
|
|
713
|
+
isActive: true,
|
|
714
|
+
},
|
|
715
|
+
];
|
|
716
|
+
const originalSettings = JSON.parse(JSON.stringify(settings));
|
|
717
|
+
process.argv = ['node', 'script.js'];
|
|
718
|
+
const argv = await parseArguments({});
|
|
719
|
+
await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
720
|
+
expect(settings).toEqual(originalSettings);
|
|
721
|
+
});
|
|
722
|
+
});
|
|
723
|
+
describe('Approval mode tool exclusion logic', () => {
|
|
724
|
+
const originalIsTTY = process.stdin.isTTY;
|
|
725
|
+
beforeEach(() => {
|
|
726
|
+
process.stdin.isTTY = false; // Ensure non-interactive mode
|
|
727
|
+
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
|
728
|
+
isTrusted: true,
|
|
729
|
+
source: undefined,
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
afterEach(() => {
|
|
733
|
+
process.stdin.isTTY = originalIsTTY;
|
|
734
|
+
});
|
|
735
|
+
it('should exclude all interactive tools in non-interactive mode with default approval mode', async () => {
|
|
736
|
+
process.argv = ['node', 'script.js', '-p', 'test'];
|
|
737
|
+
const argv = await parseArguments({});
|
|
738
|
+
const settings = {};
|
|
739
|
+
const extensions = [];
|
|
740
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
741
|
+
const excludedTools = config.getExcludeTools();
|
|
742
|
+
expect(excludedTools).toContain(SHELL_TOOL_NAME);
|
|
743
|
+
expect(excludedTools).toContain(EDIT_TOOL_NAME);
|
|
744
|
+
expect(excludedTools).toContain(WRITE_FILE_TOOL_NAME);
|
|
745
|
+
});
|
|
746
|
+
it('should exclude all interactive tools in non-interactive mode with explicit default approval mode', async () => {
|
|
747
|
+
process.argv = [
|
|
748
|
+
'node',
|
|
749
|
+
'script.js',
|
|
750
|
+
'--approval-mode',
|
|
751
|
+
'default',
|
|
752
|
+
'-p',
|
|
753
|
+
'test',
|
|
754
|
+
];
|
|
755
|
+
const argv = await parseArguments({});
|
|
756
|
+
const settings = {};
|
|
757
|
+
const extensions = [];
|
|
758
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
759
|
+
const excludedTools = config.getExcludeTools();
|
|
760
|
+
expect(excludedTools).toContain(SHELL_TOOL_NAME);
|
|
761
|
+
expect(excludedTools).toContain(EDIT_TOOL_NAME);
|
|
762
|
+
expect(excludedTools).toContain(WRITE_FILE_TOOL_NAME);
|
|
763
|
+
});
|
|
764
|
+
it('should exclude only shell tools in non-interactive mode with auto_edit approval mode', async () => {
|
|
765
|
+
process.argv = [
|
|
766
|
+
'node',
|
|
767
|
+
'script.js',
|
|
768
|
+
'--approval-mode',
|
|
769
|
+
'auto_edit',
|
|
770
|
+
'-p',
|
|
771
|
+
'test',
|
|
772
|
+
];
|
|
773
|
+
const argv = await parseArguments({});
|
|
774
|
+
const settings = {};
|
|
775
|
+
const extensions = [];
|
|
776
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
777
|
+
const excludedTools = config.getExcludeTools();
|
|
778
|
+
expect(excludedTools).toContain(SHELL_TOOL_NAME);
|
|
779
|
+
expect(excludedTools).not.toContain(EDIT_TOOL_NAME);
|
|
780
|
+
expect(excludedTools).not.toContain(WRITE_FILE_TOOL_NAME);
|
|
781
|
+
});
|
|
782
|
+
it('should exclude no interactive tools in non-interactive mode with yolo approval mode', async () => {
|
|
783
|
+
process.argv = [
|
|
784
|
+
'node',
|
|
785
|
+
'script.js',
|
|
786
|
+
'--approval-mode',
|
|
787
|
+
'yolo',
|
|
788
|
+
'-p',
|
|
789
|
+
'test',
|
|
790
|
+
];
|
|
791
|
+
const argv = await parseArguments({});
|
|
792
|
+
const settings = {};
|
|
793
|
+
const extensions = [];
|
|
794
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
795
|
+
const excludedTools = config.getExcludeTools();
|
|
796
|
+
expect(excludedTools).not.toContain(SHELL_TOOL_NAME);
|
|
797
|
+
expect(excludedTools).not.toContain(EDIT_TOOL_NAME);
|
|
798
|
+
expect(excludedTools).not.toContain(WRITE_FILE_TOOL_NAME);
|
|
799
|
+
});
|
|
800
|
+
it('should exclude no interactive tools in non-interactive mode with legacy yolo flag', async () => {
|
|
801
|
+
process.argv = ['node', 'script.js', '--yolo', '-p', 'test'];
|
|
802
|
+
const argv = await parseArguments({});
|
|
803
|
+
const settings = {};
|
|
804
|
+
const extensions = [];
|
|
805
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
806
|
+
const excludedTools = config.getExcludeTools();
|
|
807
|
+
expect(excludedTools).not.toContain(SHELL_TOOL_NAME);
|
|
808
|
+
expect(excludedTools).not.toContain(EDIT_TOOL_NAME);
|
|
809
|
+
expect(excludedTools).not.toContain(WRITE_FILE_TOOL_NAME);
|
|
810
|
+
});
|
|
811
|
+
it('should not exclude interactive tools in interactive mode regardless of approval mode', async () => {
|
|
812
|
+
process.stdin.isTTY = true; // Interactive mode
|
|
813
|
+
const testCases = [
|
|
814
|
+
{ args: ['node', 'script.js'] }, // default
|
|
815
|
+
{ args: ['node', 'script.js', '--approval-mode', 'default'] },
|
|
816
|
+
{ args: ['node', 'script.js', '--approval-mode', 'auto_edit'] },
|
|
817
|
+
{ args: ['node', 'script.js', '--approval-mode', 'yolo'] },
|
|
818
|
+
{ args: ['node', 'script.js', '--yolo'] },
|
|
819
|
+
];
|
|
820
|
+
for (const testCase of testCases) {
|
|
821
|
+
process.argv = testCase.args;
|
|
822
|
+
const argv = await parseArguments({});
|
|
823
|
+
const settings = {};
|
|
824
|
+
const extensions = [];
|
|
825
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
826
|
+
const excludedTools = config.getExcludeTools();
|
|
827
|
+
expect(excludedTools).not.toContain(SHELL_TOOL_NAME);
|
|
828
|
+
expect(excludedTools).not.toContain(EDIT_TOOL_NAME);
|
|
829
|
+
expect(excludedTools).not.toContain(WRITE_FILE_TOOL_NAME);
|
|
830
|
+
}
|
|
831
|
+
});
|
|
832
|
+
it('should merge approval mode exclusions with settings exclusions in auto_edit mode', async () => {
|
|
833
|
+
process.argv = [
|
|
834
|
+
'node',
|
|
835
|
+
'script.js',
|
|
836
|
+
'--approval-mode',
|
|
837
|
+
'auto_edit',
|
|
838
|
+
'-p',
|
|
839
|
+
'test',
|
|
840
|
+
];
|
|
841
|
+
const argv = await parseArguments({});
|
|
842
|
+
const settings = { tools: { exclude: ['custom_tool'] } };
|
|
843
|
+
const extensions = [];
|
|
844
|
+
const config = await loadCliConfig(settings, extensions, 'test-session', argv);
|
|
845
|
+
const excludedTools = config.getExcludeTools();
|
|
846
|
+
expect(excludedTools).toContain('custom_tool'); // From settings
|
|
847
|
+
expect(excludedTools).toContain(SHELL_TOOL_NAME); // From approval mode
|
|
848
|
+
expect(excludedTools).not.toContain(EDIT_TOOL_NAME); // Should be allowed in auto_edit
|
|
849
|
+
expect(excludedTools).not.toContain(WRITE_FILE_TOOL_NAME); // Should be allowed in auto_edit
|
|
850
|
+
});
|
|
851
|
+
it('should throw an error for invalid approval mode values in loadCliConfig', async () => {
|
|
852
|
+
// Create a mock argv with an invalid approval mode that bypasses argument parsing validation
|
|
853
|
+
const invalidArgv = {
|
|
854
|
+
approvalMode: 'invalid_mode',
|
|
855
|
+
promptInteractive: '',
|
|
856
|
+
prompt: '',
|
|
857
|
+
yolo: false,
|
|
858
|
+
};
|
|
859
|
+
const settings = {};
|
|
860
|
+
const extensions = [];
|
|
861
|
+
await expect(loadCliConfig(settings, extensions, 'test-session', invalidArgv)).rejects.toThrow('Invalid approval mode: invalid_mode. Valid values are: yolo, auto_edit, default');
|
|
862
|
+
});
|
|
863
|
+
});
|
|
864
|
+
describe('loadCliConfig with allowed-mcp-server-names', () => {
|
|
865
|
+
const originalArgv = process.argv;
|
|
866
|
+
beforeEach(() => {
|
|
867
|
+
vi.resetAllMocks();
|
|
868
|
+
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
|
869
|
+
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
|
870
|
+
});
|
|
871
|
+
afterEach(() => {
|
|
872
|
+
process.argv = originalArgv;
|
|
873
|
+
vi.unstubAllEnvs();
|
|
874
|
+
vi.restoreAllMocks();
|
|
875
|
+
});
|
|
876
|
+
const baseSettings = {
|
|
877
|
+
mcpServers: {
|
|
878
|
+
server1: { url: 'http://localhost:8080' },
|
|
879
|
+
server2: { url: 'http://localhost:8081' },
|
|
880
|
+
server3: { url: 'http://localhost:8082' },
|
|
881
|
+
},
|
|
882
|
+
};
|
|
883
|
+
it('should allow all MCP servers if the flag is not provided', async () => {
|
|
884
|
+
process.argv = ['node', 'script.js'];
|
|
885
|
+
const argv = await parseArguments({});
|
|
886
|
+
const config = await loadCliConfig(baseSettings, [], 'test-session', argv);
|
|
887
|
+
expect(config.getMcpServers()).toEqual(baseSettings.mcpServers);
|
|
888
|
+
});
|
|
889
|
+
it('should allow only the specified MCP server', async () => {
|
|
890
|
+
process.argv = [
|
|
891
|
+
'node',
|
|
892
|
+
'script.js',
|
|
893
|
+
'--allowed-mcp-server-names',
|
|
894
|
+
'server1',
|
|
895
|
+
];
|
|
896
|
+
const argv = await parseArguments({});
|
|
897
|
+
const config = await loadCliConfig(baseSettings, [], 'test-session', argv);
|
|
898
|
+
expect(config.getMcpServers()).toEqual({
|
|
899
|
+
server1: { url: 'http://localhost:8080' },
|
|
900
|
+
});
|
|
901
|
+
});
|
|
902
|
+
it('should allow multiple specified MCP servers', async () => {
|
|
903
|
+
process.argv = [
|
|
904
|
+
'node',
|
|
905
|
+
'script.js',
|
|
906
|
+
'--allowed-mcp-server-names',
|
|
907
|
+
'server1',
|
|
908
|
+
'--allowed-mcp-server-names',
|
|
909
|
+
'server3',
|
|
910
|
+
];
|
|
911
|
+
const argv = await parseArguments({});
|
|
912
|
+
const config = await loadCliConfig(baseSettings, [], 'test-session', argv);
|
|
913
|
+
expect(config.getMcpServers()).toEqual({
|
|
914
|
+
server1: { url: 'http://localhost:8080' },
|
|
915
|
+
server3: { url: 'http://localhost:8082' },
|
|
916
|
+
});
|
|
917
|
+
});
|
|
918
|
+
it('should handle server names that do not exist', async () => {
|
|
919
|
+
process.argv = [
|
|
920
|
+
'node',
|
|
921
|
+
'script.js',
|
|
922
|
+
'--allowed-mcp-server-names',
|
|
923
|
+
'server1',
|
|
924
|
+
'--allowed-mcp-server-names',
|
|
925
|
+
'server4',
|
|
926
|
+
];
|
|
927
|
+
const argv = await parseArguments({});
|
|
928
|
+
const config = await loadCliConfig(baseSettings, [], 'test-session', argv);
|
|
929
|
+
expect(config.getMcpServers()).toEqual({
|
|
930
|
+
server1: { url: 'http://localhost:8080' },
|
|
931
|
+
});
|
|
932
|
+
});
|
|
933
|
+
it('should allow no MCP servers if the flag is provided but empty', async () => {
|
|
934
|
+
process.argv = ['node', 'script.js', '--allowed-mcp-server-names', ''];
|
|
935
|
+
const argv = await parseArguments({});
|
|
936
|
+
const config = await loadCliConfig(baseSettings, [], 'test-session', argv);
|
|
937
|
+
expect(config.getMcpServers()).toEqual({});
|
|
938
|
+
});
|
|
939
|
+
it('should read allowMCPServers from settings', async () => {
|
|
940
|
+
process.argv = ['node', 'script.js'];
|
|
941
|
+
const argv = await parseArguments({});
|
|
942
|
+
const settings = {
|
|
943
|
+
...baseSettings,
|
|
944
|
+
mcp: { allowed: ['server1', 'server2'] },
|
|
945
|
+
};
|
|
946
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
947
|
+
expect(config.getMcpServers()).toEqual({
|
|
948
|
+
server1: { url: 'http://localhost:8080' },
|
|
949
|
+
server2: { url: 'http://localhost:8081' },
|
|
950
|
+
});
|
|
951
|
+
});
|
|
952
|
+
it('should read excludeMCPServers from settings', async () => {
|
|
953
|
+
process.argv = ['node', 'script.js'];
|
|
954
|
+
const argv = await parseArguments({});
|
|
955
|
+
const settings = {
|
|
956
|
+
...baseSettings,
|
|
957
|
+
mcp: { excluded: ['server1', 'server2'] },
|
|
958
|
+
};
|
|
959
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
960
|
+
expect(config.getMcpServers()).toEqual({
|
|
961
|
+
server3: { url: 'http://localhost:8082' },
|
|
962
|
+
});
|
|
963
|
+
});
|
|
964
|
+
it('should override allowMCPServers with excludeMCPServers if overlapping', async () => {
|
|
965
|
+
process.argv = ['node', 'script.js'];
|
|
966
|
+
const argv = await parseArguments({});
|
|
967
|
+
const settings = {
|
|
968
|
+
...baseSettings,
|
|
969
|
+
mcp: {
|
|
970
|
+
excluded: ['server1'],
|
|
971
|
+
allowed: ['server1', 'server2'],
|
|
972
|
+
},
|
|
973
|
+
};
|
|
974
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
975
|
+
expect(config.getMcpServers()).toEqual({
|
|
976
|
+
server2: { url: 'http://localhost:8081' },
|
|
977
|
+
});
|
|
978
|
+
});
|
|
979
|
+
it('should prioritize mcp server flag if set', async () => {
|
|
980
|
+
process.argv = [
|
|
981
|
+
'node',
|
|
982
|
+
'script.js',
|
|
983
|
+
'--allowed-mcp-server-names',
|
|
984
|
+
'server1',
|
|
985
|
+
];
|
|
986
|
+
const argv = await parseArguments({});
|
|
987
|
+
const settings = {
|
|
988
|
+
...baseSettings,
|
|
989
|
+
mcp: {
|
|
990
|
+
excluded: ['server1'],
|
|
991
|
+
allowed: ['server2'],
|
|
992
|
+
},
|
|
993
|
+
};
|
|
994
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
995
|
+
expect(config.getMcpServers()).toEqual({
|
|
996
|
+
server1: { url: 'http://localhost:8080' },
|
|
997
|
+
});
|
|
998
|
+
});
|
|
999
|
+
it('should prioritize CLI flag over both allowed and excluded settings', async () => {
|
|
1000
|
+
process.argv = [
|
|
1001
|
+
'node',
|
|
1002
|
+
'script.js',
|
|
1003
|
+
'--allowed-mcp-server-names',
|
|
1004
|
+
'server2',
|
|
1005
|
+
'--allowed-mcp-server-names',
|
|
1006
|
+
'server3',
|
|
1007
|
+
];
|
|
1008
|
+
const argv = await parseArguments({});
|
|
1009
|
+
const settings = {
|
|
1010
|
+
...baseSettings,
|
|
1011
|
+
mcp: {
|
|
1012
|
+
allowed: ['server1', 'server2'], // Should be ignored
|
|
1013
|
+
excluded: ['server3'], // Should be ignored
|
|
1014
|
+
},
|
|
1015
|
+
};
|
|
1016
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1017
|
+
expect(config.getMcpServers()).toEqual({
|
|
1018
|
+
server2: { url: 'http://localhost:8081' },
|
|
1019
|
+
server3: { url: 'http://localhost:8082' },
|
|
1020
|
+
});
|
|
1021
|
+
});
|
|
1022
|
+
});
|
|
1023
|
+
describe('loadCliConfig model selection', () => {
|
|
1024
|
+
it('selects a model from settings.json if provided', async () => {
|
|
1025
|
+
process.argv = ['node', 'script.js'];
|
|
1026
|
+
const argv = await parseArguments({});
|
|
1027
|
+
const config = await loadCliConfig({
|
|
1028
|
+
model: {
|
|
1029
|
+
name: 'gemini-9001-ultra',
|
|
1030
|
+
},
|
|
1031
|
+
}, [], 'test-session', argv);
|
|
1032
|
+
expect(config.getModel()).toBe('gemini-9001-ultra');
|
|
1033
|
+
});
|
|
1034
|
+
it('uses the default gemini model if nothing is set', async () => {
|
|
1035
|
+
process.argv = ['node', 'script.js']; // No model set.
|
|
1036
|
+
const argv = await parseArguments({});
|
|
1037
|
+
const config = await loadCliConfig({
|
|
1038
|
+
// No model set.
|
|
1039
|
+
}, [], 'test-session', argv);
|
|
1040
|
+
expect(config.getModel()).toBe('auto');
|
|
1041
|
+
});
|
|
1042
|
+
it('always prefers model from argv', async () => {
|
|
1043
|
+
process.argv = ['node', 'script.js', '--model', 'gemini-8675309-ultra'];
|
|
1044
|
+
const argv = await parseArguments({});
|
|
1045
|
+
const config = await loadCliConfig({
|
|
1046
|
+
model: {
|
|
1047
|
+
name: 'gemini-9001-ultra',
|
|
1048
|
+
},
|
|
1049
|
+
}, [], 'test-session', argv);
|
|
1050
|
+
expect(config.getModel()).toBe('gemini-8675309-ultra');
|
|
1051
|
+
});
|
|
1052
|
+
it('selects the model from argv if provided', async () => {
|
|
1053
|
+
process.argv = ['node', 'script.js', '--model', 'gemini-8675309-ultra'];
|
|
1054
|
+
const argv = await parseArguments({});
|
|
1055
|
+
const config = await loadCliConfig({
|
|
1056
|
+
// No model provided via settings.
|
|
1057
|
+
}, [], 'test-session', argv);
|
|
1058
|
+
expect(config.getModel()).toBe('gemini-8675309-ultra');
|
|
1059
|
+
});
|
|
1060
|
+
});
|
|
1061
|
+
describe('loadCliConfig model selection with model router', () => {
|
|
1062
|
+
it('should use auto model when useModelRouter is true and no model is provided', async () => {
|
|
1063
|
+
process.argv = ['node', 'script.js'];
|
|
1064
|
+
const argv = await parseArguments({});
|
|
1065
|
+
const config = await loadCliConfig({
|
|
1066
|
+
experimental: {
|
|
1067
|
+
useModelRouter: true,
|
|
1068
|
+
},
|
|
1069
|
+
}, [], 'test-session', argv);
|
|
1070
|
+
expect(config.getModel()).toBe(DEFAULT_GEMINI_MODEL_AUTO);
|
|
1071
|
+
});
|
|
1072
|
+
it('should use default model when useModelRouter is false and no model is provided', async () => {
|
|
1073
|
+
process.argv = ['node', 'script.js'];
|
|
1074
|
+
const argv = await parseArguments({});
|
|
1075
|
+
const config = await loadCliConfig({
|
|
1076
|
+
experimental: {
|
|
1077
|
+
useModelRouter: false,
|
|
1078
|
+
},
|
|
1079
|
+
}, [], 'test-session', argv);
|
|
1080
|
+
expect(config.getModel()).toBe(DEFAULT_GEMINI_MODEL);
|
|
1081
|
+
});
|
|
1082
|
+
it('should prioritize argv over useModelRouter', async () => {
|
|
1083
|
+
process.argv = ['node', 'script.js', '--model', 'gemini-from-argv'];
|
|
1084
|
+
const argv = await parseArguments({});
|
|
1085
|
+
const config = await loadCliConfig({
|
|
1086
|
+
experimental: {
|
|
1087
|
+
useModelRouter: true,
|
|
1088
|
+
},
|
|
1089
|
+
}, [], 'test-session', argv);
|
|
1090
|
+
expect(config.getModel()).toBe('gemini-from-argv');
|
|
1091
|
+
});
|
|
1092
|
+
it('should prioritize settings over useModelRouter', async () => {
|
|
1093
|
+
process.argv = ['node', 'script.js'];
|
|
1094
|
+
const argv = await parseArguments({});
|
|
1095
|
+
const config = await loadCliConfig({
|
|
1096
|
+
experimental: {
|
|
1097
|
+
useModelRouter: true,
|
|
1098
|
+
},
|
|
1099
|
+
model: {
|
|
1100
|
+
name: 'gemini-from-settings',
|
|
1101
|
+
},
|
|
1102
|
+
}, [], 'test-session', argv);
|
|
1103
|
+
expect(config.getModel()).toBe('gemini-from-settings');
|
|
1104
|
+
});
|
|
1105
|
+
it('should prioritize environment variable over useModelRouter', async () => {
|
|
1106
|
+
process.argv = ['node', 'script.js'];
|
|
1107
|
+
vi.stubEnv('GEMINI_MODEL', 'gemini-from-env');
|
|
1108
|
+
const argv = await parseArguments({});
|
|
1109
|
+
const config = await loadCliConfig({
|
|
1110
|
+
experimental: {
|
|
1111
|
+
useModelRouter: true,
|
|
1112
|
+
},
|
|
1113
|
+
}, [], 'test-session', argv);
|
|
1114
|
+
expect(config.getModel()).toBe('gemini-from-env');
|
|
1115
|
+
});
|
|
1116
|
+
});
|
|
1117
|
+
describe('loadCliConfig folderTrust', () => {
|
|
1118
|
+
const originalArgv = process.argv;
|
|
1119
|
+
beforeEach(() => {
|
|
1120
|
+
vi.resetAllMocks();
|
|
1121
|
+
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
|
1122
|
+
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
|
1123
|
+
});
|
|
1124
|
+
afterEach(() => {
|
|
1125
|
+
process.argv = originalArgv;
|
|
1126
|
+
vi.unstubAllEnvs();
|
|
1127
|
+
vi.restoreAllMocks();
|
|
1128
|
+
});
|
|
1129
|
+
it('should be false when folderTrust is false', async () => {
|
|
1130
|
+
process.argv = ['node', 'script.js'];
|
|
1131
|
+
const settings = {
|
|
1132
|
+
security: {
|
|
1133
|
+
folderTrust: {
|
|
1134
|
+
enabled: false,
|
|
1135
|
+
},
|
|
1136
|
+
},
|
|
1137
|
+
};
|
|
1138
|
+
const argv = await parseArguments({});
|
|
1139
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1140
|
+
expect(config.getFolderTrust()).toBe(false);
|
|
1141
|
+
});
|
|
1142
|
+
it('should be true when folderTrust is true', async () => {
|
|
1143
|
+
process.argv = ['node', 'script.js'];
|
|
1144
|
+
const argv = await parseArguments({});
|
|
1145
|
+
const settings = {
|
|
1146
|
+
security: {
|
|
1147
|
+
folderTrust: {
|
|
1148
|
+
enabled: true,
|
|
1149
|
+
},
|
|
1150
|
+
},
|
|
1151
|
+
};
|
|
1152
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1153
|
+
expect(config.getFolderTrust()).toBe(true);
|
|
1154
|
+
});
|
|
1155
|
+
it('should be false by default', async () => {
|
|
1156
|
+
process.argv = ['node', 'script.js'];
|
|
1157
|
+
const argv = await parseArguments({});
|
|
1158
|
+
const settings = {};
|
|
1159
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1160
|
+
expect(config.getFolderTrust()).toBe(false);
|
|
1161
|
+
});
|
|
1162
|
+
});
|
|
1163
|
+
describe('loadCliConfig with includeDirectories', () => {
|
|
1164
|
+
const originalArgv = process.argv;
|
|
1165
|
+
beforeEach(() => {
|
|
1166
|
+
vi.resetAllMocks();
|
|
1167
|
+
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
|
1168
|
+
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
|
1169
|
+
vi.spyOn(process, 'cwd').mockReturnValue(path.resolve(path.sep, 'home', 'user', 'project'));
|
|
1170
|
+
});
|
|
1171
|
+
afterEach(() => {
|
|
1172
|
+
process.argv = originalArgv;
|
|
1173
|
+
vi.unstubAllEnvs();
|
|
1174
|
+
vi.restoreAllMocks();
|
|
1175
|
+
});
|
|
1176
|
+
it('should combine and resolve paths from settings and CLI arguments', async () => {
|
|
1177
|
+
const mockCwd = path.resolve(path.sep, 'home', 'user', 'project');
|
|
1178
|
+
process.argv = [
|
|
1179
|
+
'node',
|
|
1180
|
+
'script.js',
|
|
1181
|
+
'--include-directories',
|
|
1182
|
+
`${path.resolve(path.sep, 'cli', 'path1')},${path.join(mockCwd, 'cli', 'path2')}`,
|
|
1183
|
+
];
|
|
1184
|
+
const argv = await parseArguments({});
|
|
1185
|
+
const settings = {
|
|
1186
|
+
context: {
|
|
1187
|
+
includeDirectories: [
|
|
1188
|
+
path.resolve(path.sep, 'settings', 'path1'),
|
|
1189
|
+
path.join(os.homedir(), 'settings', 'path2'),
|
|
1190
|
+
path.join(mockCwd, 'settings', 'path3'),
|
|
1191
|
+
],
|
|
1192
|
+
},
|
|
1193
|
+
};
|
|
1194
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1195
|
+
const expected = [
|
|
1196
|
+
mockCwd,
|
|
1197
|
+
path.resolve(path.sep, 'cli', 'path1'),
|
|
1198
|
+
path.join(mockCwd, 'cli', 'path2'),
|
|
1199
|
+
path.resolve(path.sep, 'settings', 'path1'),
|
|
1200
|
+
path.join(os.homedir(), 'settings', 'path2'),
|
|
1201
|
+
path.join(mockCwd, 'settings', 'path3'),
|
|
1202
|
+
];
|
|
1203
|
+
expect(config.getWorkspaceContext().getDirectories()).toEqual(expect.arrayContaining(expected));
|
|
1204
|
+
expect(config.getWorkspaceContext().getDirectories()).toHaveLength(expected.length);
|
|
1205
|
+
});
|
|
1206
|
+
});
|
|
1207
|
+
describe('loadCliConfig chatCompression', () => {
|
|
1208
|
+
const originalArgv = process.argv;
|
|
1209
|
+
beforeEach(() => {
|
|
1210
|
+
vi.resetAllMocks();
|
|
1211
|
+
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
|
1212
|
+
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
|
1213
|
+
});
|
|
1214
|
+
afterEach(() => {
|
|
1215
|
+
process.argv = originalArgv;
|
|
1216
|
+
vi.unstubAllEnvs();
|
|
1217
|
+
vi.restoreAllMocks();
|
|
1218
|
+
});
|
|
1219
|
+
it('should pass chatCompression settings to the core config', async () => {
|
|
1220
|
+
process.argv = ['node', 'script.js'];
|
|
1221
|
+
const argv = await parseArguments({});
|
|
1222
|
+
const settings = {
|
|
1223
|
+
model: {
|
|
1224
|
+
chatCompression: {
|
|
1225
|
+
contextPercentageThreshold: 0.5,
|
|
1226
|
+
},
|
|
1227
|
+
},
|
|
1228
|
+
};
|
|
1229
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1230
|
+
expect(config.getChatCompression()).toEqual({
|
|
1231
|
+
contextPercentageThreshold: 0.5,
|
|
1232
|
+
});
|
|
1233
|
+
});
|
|
1234
|
+
it('should have undefined chatCompression if not in settings', async () => {
|
|
1235
|
+
process.argv = ['node', 'script.js'];
|
|
1236
|
+
const argv = await parseArguments({});
|
|
1237
|
+
const settings = {};
|
|
1238
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1239
|
+
expect(config.getChatCompression()).toBeUndefined();
|
|
1240
|
+
});
|
|
1241
|
+
});
|
|
1242
|
+
describe('loadCliConfig useRipgrep', () => {
|
|
1243
|
+
const originalArgv = process.argv;
|
|
1244
|
+
beforeEach(() => {
|
|
1245
|
+
vi.resetAllMocks();
|
|
1246
|
+
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
|
1247
|
+
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
|
1248
|
+
});
|
|
1249
|
+
afterEach(() => {
|
|
1250
|
+
process.argv = originalArgv;
|
|
1251
|
+
vi.unstubAllEnvs();
|
|
1252
|
+
vi.restoreAllMocks();
|
|
1253
|
+
});
|
|
1254
|
+
it('should be true by default when useRipgrep is not set in settings', async () => {
|
|
1255
|
+
process.argv = ['node', 'script.js'];
|
|
1256
|
+
const argv = await parseArguments({});
|
|
1257
|
+
const settings = {};
|
|
1258
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1259
|
+
expect(config.getUseRipgrep()).toBe(true);
|
|
1260
|
+
});
|
|
1261
|
+
it('should be false when useRipgrep is set to false in settings', async () => {
|
|
1262
|
+
process.argv = ['node', 'script.js'];
|
|
1263
|
+
const argv = await parseArguments({});
|
|
1264
|
+
const settings = { tools: { useRipgrep: false } };
|
|
1265
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1266
|
+
expect(config.getUseRipgrep()).toBe(false);
|
|
1267
|
+
});
|
|
1268
|
+
it('should be true when useRipgrep is explicitly set to true in settings', async () => {
|
|
1269
|
+
process.argv = ['node', 'script.js'];
|
|
1270
|
+
const argv = await parseArguments({});
|
|
1271
|
+
const settings = { tools: { useRipgrep: true } };
|
|
1272
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1273
|
+
expect(config.getUseRipgrep()).toBe(true);
|
|
1274
|
+
});
|
|
1275
|
+
describe('loadCliConfig useModelRouter', () => {
|
|
1276
|
+
it('should be true by default when useModelRouter is not set in settings', async () => {
|
|
1277
|
+
process.argv = ['node', 'script.js'];
|
|
1278
|
+
const argv = await parseArguments({});
|
|
1279
|
+
const settings = {};
|
|
1280
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1281
|
+
expect(config.getUseModelRouter()).toBe(true);
|
|
1282
|
+
});
|
|
1283
|
+
it('should be true when useModelRouter is set to true in settings', async () => {
|
|
1284
|
+
process.argv = ['node', 'script.js'];
|
|
1285
|
+
const argv = await parseArguments({});
|
|
1286
|
+
const settings = { experimental: { useModelRouter: true } };
|
|
1287
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1288
|
+
expect(config.getUseModelRouter()).toBe(true);
|
|
1289
|
+
});
|
|
1290
|
+
it('should be false when useModelRouter is explicitly set to false in settings', async () => {
|
|
1291
|
+
process.argv = ['node', 'script.js'];
|
|
1292
|
+
const argv = await parseArguments({});
|
|
1293
|
+
const settings = { experimental: { useModelRouter: false } };
|
|
1294
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1295
|
+
expect(config.getUseModelRouter()).toBe(false);
|
|
1296
|
+
});
|
|
1297
|
+
});
|
|
1298
|
+
});
|
|
1299
|
+
describe('screenReader configuration', () => {
|
|
1300
|
+
const originalArgv = process.argv;
|
|
1301
|
+
beforeEach(() => {
|
|
1302
|
+
vi.resetAllMocks();
|
|
1303
|
+
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
|
1304
|
+
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
|
1305
|
+
});
|
|
1306
|
+
afterEach(() => {
|
|
1307
|
+
process.argv = originalArgv;
|
|
1308
|
+
vi.unstubAllEnvs();
|
|
1309
|
+
vi.restoreAllMocks();
|
|
1310
|
+
});
|
|
1311
|
+
it('should use screenReader value from settings if CLI flag is not present (settings true)', async () => {
|
|
1312
|
+
process.argv = ['node', 'script.js'];
|
|
1313
|
+
const argv = await parseArguments({});
|
|
1314
|
+
const settings = {
|
|
1315
|
+
ui: { accessibility: { screenReader: true } },
|
|
1316
|
+
};
|
|
1317
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1318
|
+
expect(config.getScreenReader()).toBe(true);
|
|
1319
|
+
});
|
|
1320
|
+
it('should use screenReader value from settings if CLI flag is not present (settings false)', async () => {
|
|
1321
|
+
process.argv = ['node', 'script.js'];
|
|
1322
|
+
const argv = await parseArguments({});
|
|
1323
|
+
const settings = {
|
|
1324
|
+
ui: { accessibility: { screenReader: false } },
|
|
1325
|
+
};
|
|
1326
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1327
|
+
expect(config.getScreenReader()).toBe(false);
|
|
1328
|
+
});
|
|
1329
|
+
it('should prioritize --screen-reader CLI flag (true) over settings (false)', async () => {
|
|
1330
|
+
process.argv = ['node', 'script.js', '--screen-reader'];
|
|
1331
|
+
const argv = await parseArguments({});
|
|
1332
|
+
const settings = {
|
|
1333
|
+
ui: { accessibility: { screenReader: false } },
|
|
1334
|
+
};
|
|
1335
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1336
|
+
expect(config.getScreenReader()).toBe(true);
|
|
1337
|
+
});
|
|
1338
|
+
it('should be false by default when no flag or setting is present', async () => {
|
|
1339
|
+
process.argv = ['node', 'script.js'];
|
|
1340
|
+
const argv = await parseArguments({});
|
|
1341
|
+
const settings = {};
|
|
1342
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1343
|
+
expect(config.getScreenReader()).toBe(false);
|
|
1344
|
+
});
|
|
1345
|
+
});
|
|
1346
|
+
describe('loadCliConfig tool exclusions', () => {
|
|
1347
|
+
const originalArgv = process.argv;
|
|
1348
|
+
const originalIsTTY = process.stdin.isTTY;
|
|
1349
|
+
beforeEach(() => {
|
|
1350
|
+
vi.resetAllMocks();
|
|
1351
|
+
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
|
1352
|
+
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
|
1353
|
+
process.stdin.isTTY = true;
|
|
1354
|
+
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
|
1355
|
+
isTrusted: true,
|
|
1356
|
+
source: undefined,
|
|
1357
|
+
});
|
|
1358
|
+
});
|
|
1359
|
+
afterEach(() => {
|
|
1360
|
+
process.argv = originalArgv;
|
|
1361
|
+
process.stdin.isTTY = originalIsTTY;
|
|
1362
|
+
vi.unstubAllEnvs();
|
|
1363
|
+
vi.restoreAllMocks();
|
|
1364
|
+
});
|
|
1365
|
+
it('should not exclude interactive tools in interactive mode without YOLO', async () => {
|
|
1366
|
+
process.stdin.isTTY = true;
|
|
1367
|
+
process.argv = ['node', 'script.js'];
|
|
1368
|
+
const argv = await parseArguments({});
|
|
1369
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1370
|
+
expect(config.getExcludeTools()).not.toContain('run_shell_command');
|
|
1371
|
+
expect(config.getExcludeTools()).not.toContain('replace');
|
|
1372
|
+
expect(config.getExcludeTools()).not.toContain('write_file');
|
|
1373
|
+
});
|
|
1374
|
+
it('should not exclude interactive tools in interactive mode with YOLO', async () => {
|
|
1375
|
+
process.stdin.isTTY = true;
|
|
1376
|
+
process.argv = ['node', 'script.js', '--yolo'];
|
|
1377
|
+
const argv = await parseArguments({});
|
|
1378
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1379
|
+
expect(config.getExcludeTools()).not.toContain('run_shell_command');
|
|
1380
|
+
expect(config.getExcludeTools()).not.toContain('replace');
|
|
1381
|
+
expect(config.getExcludeTools()).not.toContain('write_file');
|
|
1382
|
+
});
|
|
1383
|
+
it('should exclude interactive tools in non-interactive mode without YOLO', async () => {
|
|
1384
|
+
process.stdin.isTTY = false;
|
|
1385
|
+
process.argv = ['node', 'script.js', '-p', 'test'];
|
|
1386
|
+
const argv = await parseArguments({});
|
|
1387
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1388
|
+
expect(config.getExcludeTools()).toContain('run_shell_command');
|
|
1389
|
+
expect(config.getExcludeTools()).toContain('replace');
|
|
1390
|
+
expect(config.getExcludeTools()).toContain('write_file');
|
|
1391
|
+
});
|
|
1392
|
+
it('should not exclude interactive tools in non-interactive mode with YOLO', async () => {
|
|
1393
|
+
process.stdin.isTTY = false;
|
|
1394
|
+
process.argv = ['node', 'script.js', '-p', 'test', '--yolo'];
|
|
1395
|
+
const argv = await parseArguments({});
|
|
1396
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1397
|
+
expect(config.getExcludeTools()).not.toContain('run_shell_command');
|
|
1398
|
+
expect(config.getExcludeTools()).not.toContain('replace');
|
|
1399
|
+
expect(config.getExcludeTools()).not.toContain('write_file');
|
|
1400
|
+
});
|
|
1401
|
+
it('should not exclude shell tool in non-interactive mode when --allowed-tools="ShellTool" is set', async () => {
|
|
1402
|
+
process.stdin.isTTY = false;
|
|
1403
|
+
process.argv = [
|
|
1404
|
+
'node',
|
|
1405
|
+
'script.js',
|
|
1406
|
+
'-p',
|
|
1407
|
+
'test',
|
|
1408
|
+
'--allowed-tools',
|
|
1409
|
+
'ShellTool',
|
|
1410
|
+
];
|
|
1411
|
+
const argv = await parseArguments({});
|
|
1412
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1413
|
+
expect(config.getExcludeTools()).not.toContain(SHELL_TOOL_NAME);
|
|
1414
|
+
});
|
|
1415
|
+
it('should not exclude shell tool in non-interactive mode when --allowed-tools="run_shell_command" is set', async () => {
|
|
1416
|
+
process.stdin.isTTY = false;
|
|
1417
|
+
process.argv = [
|
|
1418
|
+
'node',
|
|
1419
|
+
'script.js',
|
|
1420
|
+
'-p',
|
|
1421
|
+
'test',
|
|
1422
|
+
'--allowed-tools',
|
|
1423
|
+
'run_shell_command',
|
|
1424
|
+
];
|
|
1425
|
+
const argv = await parseArguments({});
|
|
1426
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1427
|
+
expect(config.getExcludeTools()).not.toContain(SHELL_TOOL_NAME);
|
|
1428
|
+
});
|
|
1429
|
+
it('should not exclude shell tool in non-interactive mode when --allowed-tools="ShellTool(wc)" is set', async () => {
|
|
1430
|
+
process.stdin.isTTY = false;
|
|
1431
|
+
process.argv = [
|
|
1432
|
+
'node',
|
|
1433
|
+
'script.js',
|
|
1434
|
+
'-p',
|
|
1435
|
+
'test',
|
|
1436
|
+
'--allowed-tools',
|
|
1437
|
+
'ShellTool(wc)',
|
|
1438
|
+
];
|
|
1439
|
+
const argv = await parseArguments({});
|
|
1440
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1441
|
+
expect(config.getExcludeTools()).not.toContain(SHELL_TOOL_NAME);
|
|
1442
|
+
});
|
|
1443
|
+
});
|
|
1444
|
+
describe('loadCliConfig interactive', () => {
|
|
1445
|
+
const originalArgv = process.argv;
|
|
1446
|
+
const originalIsTTY = process.stdin.isTTY;
|
|
1447
|
+
beforeEach(() => {
|
|
1448
|
+
vi.resetAllMocks();
|
|
1449
|
+
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
|
1450
|
+
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
|
1451
|
+
process.stdin.isTTY = true;
|
|
1452
|
+
});
|
|
1453
|
+
afterEach(() => {
|
|
1454
|
+
process.argv = originalArgv;
|
|
1455
|
+
process.stdin.isTTY = originalIsTTY;
|
|
1456
|
+
vi.unstubAllEnvs();
|
|
1457
|
+
vi.restoreAllMocks();
|
|
1458
|
+
});
|
|
1459
|
+
it('should be interactive if isTTY and no prompt', async () => {
|
|
1460
|
+
process.stdin.isTTY = true;
|
|
1461
|
+
process.argv = ['node', 'script.js'];
|
|
1462
|
+
const argv = await parseArguments({});
|
|
1463
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1464
|
+
expect(config.isInteractive()).toBe(true);
|
|
1465
|
+
});
|
|
1466
|
+
it('should be interactive if prompt-interactive is set', async () => {
|
|
1467
|
+
process.stdin.isTTY = false;
|
|
1468
|
+
process.argv = ['node', 'script.js', '--prompt-interactive', 'test'];
|
|
1469
|
+
const argv = await parseArguments({});
|
|
1470
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1471
|
+
expect(config.isInteractive()).toBe(true);
|
|
1472
|
+
});
|
|
1473
|
+
it('should not be interactive if not isTTY and no prompt', async () => {
|
|
1474
|
+
process.stdin.isTTY = false;
|
|
1475
|
+
process.argv = ['node', 'script.js'];
|
|
1476
|
+
const argv = await parseArguments({});
|
|
1477
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1478
|
+
expect(config.isInteractive()).toBe(false);
|
|
1479
|
+
});
|
|
1480
|
+
it('should not be interactive if prompt is set', async () => {
|
|
1481
|
+
process.stdin.isTTY = true;
|
|
1482
|
+
process.argv = ['node', 'script.js', '--prompt', 'test'];
|
|
1483
|
+
const argv = await parseArguments({});
|
|
1484
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1485
|
+
expect(config.isInteractive()).toBe(false);
|
|
1486
|
+
});
|
|
1487
|
+
it('should not be interactive if positional prompt words are provided with other flags', async () => {
|
|
1488
|
+
process.stdin.isTTY = true;
|
|
1489
|
+
process.argv = ['node', 'script.js', '--model', 'gemini-1.5-pro', 'Hello'];
|
|
1490
|
+
const argv = await parseArguments({});
|
|
1491
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1492
|
+
expect(config.isInteractive()).toBe(false);
|
|
1493
|
+
});
|
|
1494
|
+
it('should not be interactive if positional prompt words are provided with multiple flags', async () => {
|
|
1495
|
+
process.stdin.isTTY = true;
|
|
1496
|
+
process.argv = [
|
|
1497
|
+
'node',
|
|
1498
|
+
'script.js',
|
|
1499
|
+
'--model',
|
|
1500
|
+
'gemini-1.5-pro',
|
|
1501
|
+
'--yolo',
|
|
1502
|
+
'Hello world',
|
|
1503
|
+
];
|
|
1504
|
+
const argv = await parseArguments({});
|
|
1505
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1506
|
+
expect(config.isInteractive()).toBe(false);
|
|
1507
|
+
// Verify the question is preserved for one-shot execution
|
|
1508
|
+
expect(argv.prompt).toBe('Hello world');
|
|
1509
|
+
expect(argv.promptInteractive).toBeUndefined();
|
|
1510
|
+
});
|
|
1511
|
+
it('should not be interactive if positional prompt words are provided with extensions flag', async () => {
|
|
1512
|
+
process.stdin.isTTY = true;
|
|
1513
|
+
process.argv = ['node', 'script.js', '-e', 'none', 'hello'];
|
|
1514
|
+
const argv = await parseArguments({});
|
|
1515
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1516
|
+
expect(config.isInteractive()).toBe(false);
|
|
1517
|
+
expect(argv.query).toBe('hello');
|
|
1518
|
+
expect(argv.extensions).toEqual(['none']);
|
|
1519
|
+
});
|
|
1520
|
+
it('should handle multiple positional words correctly', async () => {
|
|
1521
|
+
process.stdin.isTTY = true;
|
|
1522
|
+
process.argv = ['node', 'script.js', 'hello world how are you'];
|
|
1523
|
+
const argv = await parseArguments({});
|
|
1524
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1525
|
+
expect(config.isInteractive()).toBe(false);
|
|
1526
|
+
expect(argv.query).toBe('hello world how are you');
|
|
1527
|
+
expect(argv.prompt).toBe('hello world how are you');
|
|
1528
|
+
});
|
|
1529
|
+
it('should handle multiple positional words with flags', async () => {
|
|
1530
|
+
process.stdin.isTTY = true;
|
|
1531
|
+
process.argv = [
|
|
1532
|
+
'node',
|
|
1533
|
+
'script.js',
|
|
1534
|
+
'--model',
|
|
1535
|
+
'gemini-1.5-pro',
|
|
1536
|
+
'write',
|
|
1537
|
+
'a',
|
|
1538
|
+
'function',
|
|
1539
|
+
'to',
|
|
1540
|
+
'sort',
|
|
1541
|
+
'array',
|
|
1542
|
+
];
|
|
1543
|
+
const argv = await parseArguments({});
|
|
1544
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1545
|
+
expect(config.isInteractive()).toBe(false);
|
|
1546
|
+
expect(argv.query).toBe('write a function to sort array');
|
|
1547
|
+
expect(argv.model).toBe('gemini-1.5-pro');
|
|
1548
|
+
});
|
|
1549
|
+
it('should handle empty positional arguments', async () => {
|
|
1550
|
+
process.stdin.isTTY = true;
|
|
1551
|
+
process.argv = ['node', 'script.js', ''];
|
|
1552
|
+
const argv = await parseArguments({});
|
|
1553
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1554
|
+
expect(config.isInteractive()).toBe(true);
|
|
1555
|
+
expect(argv.query).toBeUndefined();
|
|
1556
|
+
});
|
|
1557
|
+
it('should handle extensions flag with positional arguments correctly', async () => {
|
|
1558
|
+
process.stdin.isTTY = true;
|
|
1559
|
+
process.argv = [
|
|
1560
|
+
'node',
|
|
1561
|
+
'script.js',
|
|
1562
|
+
'-e',
|
|
1563
|
+
'none',
|
|
1564
|
+
'hello',
|
|
1565
|
+
'world',
|
|
1566
|
+
'how',
|
|
1567
|
+
'are',
|
|
1568
|
+
'you',
|
|
1569
|
+
];
|
|
1570
|
+
const argv = await parseArguments({});
|
|
1571
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1572
|
+
expect(config.isInteractive()).toBe(false);
|
|
1573
|
+
expect(argv.query).toBe('hello world how are you');
|
|
1574
|
+
expect(argv.extensions).toEqual(['none']);
|
|
1575
|
+
});
|
|
1576
|
+
it('should be interactive if no positional prompt words are provided with flags', async () => {
|
|
1577
|
+
process.stdin.isTTY = true;
|
|
1578
|
+
process.argv = ['node', 'script.js', '--model', 'gemini-1.5-pro'];
|
|
1579
|
+
const argv = await parseArguments({});
|
|
1580
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1581
|
+
expect(config.isInteractive()).toBe(true);
|
|
1582
|
+
});
|
|
1583
|
+
});
|
|
1584
|
+
describe('loadCliConfig approval mode', () => {
|
|
1585
|
+
const originalArgv = process.argv;
|
|
1586
|
+
beforeEach(() => {
|
|
1587
|
+
vi.resetAllMocks();
|
|
1588
|
+
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
|
1589
|
+
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
|
1590
|
+
process.argv = ['node', 'script.js']; // Reset argv for each test
|
|
1591
|
+
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
|
1592
|
+
isTrusted: true,
|
|
1593
|
+
source: undefined,
|
|
1594
|
+
});
|
|
1595
|
+
});
|
|
1596
|
+
afterEach(() => {
|
|
1597
|
+
process.argv = originalArgv;
|
|
1598
|
+
vi.unstubAllEnvs();
|
|
1599
|
+
vi.restoreAllMocks();
|
|
1600
|
+
});
|
|
1601
|
+
it('should default to DEFAULT approval mode when no flags are set', async () => {
|
|
1602
|
+
process.argv = ['node', 'script.js'];
|
|
1603
|
+
const argv = await parseArguments({});
|
|
1604
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1605
|
+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT);
|
|
1606
|
+
});
|
|
1607
|
+
it('should set YOLO approval mode when --yolo flag is used', async () => {
|
|
1608
|
+
process.argv = ['node', 'script.js', '--yolo'];
|
|
1609
|
+
const argv = await parseArguments({});
|
|
1610
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1611
|
+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO);
|
|
1612
|
+
});
|
|
1613
|
+
it('should set YOLO approval mode when -y flag is used', async () => {
|
|
1614
|
+
process.argv = ['node', 'script.js', '-y'];
|
|
1615
|
+
const argv = await parseArguments({});
|
|
1616
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1617
|
+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO);
|
|
1618
|
+
});
|
|
1619
|
+
it('should set DEFAULT approval mode when --approval-mode=default', async () => {
|
|
1620
|
+
process.argv = ['node', 'script.js', '--approval-mode', 'default'];
|
|
1621
|
+
const argv = await parseArguments({});
|
|
1622
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1623
|
+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT);
|
|
1624
|
+
});
|
|
1625
|
+
it('should set AUTO_EDIT approval mode when --approval-mode=auto_edit', async () => {
|
|
1626
|
+
process.argv = ['node', 'script.js', '--approval-mode', 'auto_edit'];
|
|
1627
|
+
const argv = await parseArguments({});
|
|
1628
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1629
|
+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.AUTO_EDIT);
|
|
1630
|
+
});
|
|
1631
|
+
it('should set YOLO approval mode when --approval-mode=yolo', async () => {
|
|
1632
|
+
process.argv = ['node', 'script.js', '--approval-mode', 'yolo'];
|
|
1633
|
+
const argv = await parseArguments({});
|
|
1634
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1635
|
+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO);
|
|
1636
|
+
});
|
|
1637
|
+
it('should prioritize --approval-mode over --yolo when both would be valid (but validation prevents this)', async () => {
|
|
1638
|
+
// Note: This test documents the intended behavior, but in practice the validation
|
|
1639
|
+
// prevents both flags from being used together
|
|
1640
|
+
process.argv = ['node', 'script.js', '--approval-mode', 'default'];
|
|
1641
|
+
const argv = await parseArguments({});
|
|
1642
|
+
// Manually set yolo to true to simulate what would happen if validation didn't prevent it
|
|
1643
|
+
argv.yolo = true;
|
|
1644
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1645
|
+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT);
|
|
1646
|
+
});
|
|
1647
|
+
it('should fall back to --yolo behavior when --approval-mode is not set', async () => {
|
|
1648
|
+
process.argv = ['node', 'script.js', '--yolo'];
|
|
1649
|
+
const argv = await parseArguments({});
|
|
1650
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1651
|
+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO);
|
|
1652
|
+
});
|
|
1653
|
+
// --- Untrusted Folder Scenarios ---
|
|
1654
|
+
describe('when folder is NOT trusted', () => {
|
|
1655
|
+
beforeEach(() => {
|
|
1656
|
+
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
|
1657
|
+
isTrusted: false,
|
|
1658
|
+
source: 'file',
|
|
1659
|
+
});
|
|
1660
|
+
});
|
|
1661
|
+
it('should override --approval-mode=yolo to DEFAULT', async () => {
|
|
1662
|
+
process.argv = ['node', 'script.js', '--approval-mode', 'yolo'];
|
|
1663
|
+
const argv = await parseArguments({});
|
|
1664
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1665
|
+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT);
|
|
1666
|
+
});
|
|
1667
|
+
it('should override --approval-mode=auto_edit to DEFAULT', async () => {
|
|
1668
|
+
process.argv = ['node', 'script.js', '--approval-mode', 'auto_edit'];
|
|
1669
|
+
const argv = await parseArguments({});
|
|
1670
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1671
|
+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT);
|
|
1672
|
+
});
|
|
1673
|
+
it('should override --yolo flag to DEFAULT', async () => {
|
|
1674
|
+
process.argv = ['node', 'script.js', '--yolo'];
|
|
1675
|
+
const argv = await parseArguments({});
|
|
1676
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1677
|
+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT);
|
|
1678
|
+
});
|
|
1679
|
+
it('should remain DEFAULT when --approval-mode=default', async () => {
|
|
1680
|
+
process.argv = ['node', 'script.js', '--approval-mode', 'default'];
|
|
1681
|
+
const argv = await parseArguments({});
|
|
1682
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1683
|
+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.DEFAULT);
|
|
1684
|
+
});
|
|
1685
|
+
});
|
|
1686
|
+
});
|
|
1687
|
+
describe('loadCliConfig fileFiltering', () => {
|
|
1688
|
+
const originalArgv = process.argv;
|
|
1689
|
+
beforeEach(() => {
|
|
1690
|
+
vi.resetAllMocks();
|
|
1691
|
+
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
|
1692
|
+
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
|
1693
|
+
process.argv = ['node', 'script.js']; // Reset argv for each test
|
|
1694
|
+
});
|
|
1695
|
+
afterEach(() => {
|
|
1696
|
+
process.argv = originalArgv;
|
|
1697
|
+
vi.unstubAllEnvs();
|
|
1698
|
+
vi.restoreAllMocks();
|
|
1699
|
+
});
|
|
1700
|
+
const testCases = [
|
|
1701
|
+
{
|
|
1702
|
+
property: 'disableFuzzySearch',
|
|
1703
|
+
getter: (c) => c.getFileFilteringDisableFuzzySearch(),
|
|
1704
|
+
value: true,
|
|
1705
|
+
},
|
|
1706
|
+
{
|
|
1707
|
+
property: 'disableFuzzySearch',
|
|
1708
|
+
getter: (c) => c.getFileFilteringDisableFuzzySearch(),
|
|
1709
|
+
value: false,
|
|
1710
|
+
},
|
|
1711
|
+
{
|
|
1712
|
+
property: 'respectGitIgnore',
|
|
1713
|
+
getter: (c) => c.getFileFilteringRespectGitIgnore(),
|
|
1714
|
+
value: true,
|
|
1715
|
+
},
|
|
1716
|
+
{
|
|
1717
|
+
property: 'respectGitIgnore',
|
|
1718
|
+
getter: (c) => c.getFileFilteringRespectGitIgnore(),
|
|
1719
|
+
value: false,
|
|
1720
|
+
},
|
|
1721
|
+
{
|
|
1722
|
+
property: 'respectGeminiIgnore',
|
|
1723
|
+
getter: (c) => c.getFileFilteringRespectGeminiIgnore(),
|
|
1724
|
+
value: true,
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
1727
|
+
property: 'respectGeminiIgnore',
|
|
1728
|
+
getter: (c) => c.getFileFilteringRespectGeminiIgnore(),
|
|
1729
|
+
value: false,
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
property: 'enableRecursiveFileSearch',
|
|
1733
|
+
getter: (c) => c.getEnableRecursiveFileSearch(),
|
|
1734
|
+
value: true,
|
|
1735
|
+
},
|
|
1736
|
+
{
|
|
1737
|
+
property: 'enableRecursiveFileSearch',
|
|
1738
|
+
getter: (c) => c.getEnableRecursiveFileSearch(),
|
|
1739
|
+
value: false,
|
|
1740
|
+
},
|
|
1741
|
+
];
|
|
1742
|
+
it.each(testCases)('should pass $property from settings to config when $value', async ({ property, getter, value }) => {
|
|
1743
|
+
const settings = {
|
|
1744
|
+
context: {
|
|
1745
|
+
fileFiltering: { [property]: value },
|
|
1746
|
+
},
|
|
1747
|
+
};
|
|
1748
|
+
const argv = await parseArguments(settings);
|
|
1749
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1750
|
+
expect(getter(config)).toBe(value);
|
|
1751
|
+
});
|
|
1752
|
+
});
|
|
1753
|
+
describe('Output format', () => {
|
|
1754
|
+
it('should default to TEXT', async () => {
|
|
1755
|
+
process.argv = ['node', 'script.js'];
|
|
1756
|
+
const argv = await parseArguments({});
|
|
1757
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1758
|
+
expect(config.getOutputFormat()).toBe(OutputFormat.TEXT);
|
|
1759
|
+
});
|
|
1760
|
+
it('should use the format from settings', async () => {
|
|
1761
|
+
process.argv = ['node', 'script.js'];
|
|
1762
|
+
const argv = await parseArguments({});
|
|
1763
|
+
const config = await loadCliConfig({ output: { format: OutputFormat.JSON } }, [], 'test-session', argv);
|
|
1764
|
+
expect(config.getOutputFormat()).toBe(OutputFormat.JSON);
|
|
1765
|
+
});
|
|
1766
|
+
it('should prioritize the format from argv', async () => {
|
|
1767
|
+
process.argv = ['node', 'script.js', '--output-format', 'json'];
|
|
1768
|
+
const argv = await parseArguments({});
|
|
1769
|
+
const config = await loadCliConfig({ output: { format: OutputFormat.JSON } }, [], 'test-session', argv);
|
|
1770
|
+
expect(config.getOutputFormat()).toBe(OutputFormat.JSON);
|
|
1771
|
+
});
|
|
1772
|
+
it('should accept stream-json as a valid output format', async () => {
|
|
1773
|
+
process.argv = ['node', 'script.js', '--output-format', 'stream-json'];
|
|
1774
|
+
const argv = await parseArguments({});
|
|
1775
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1776
|
+
expect(config.getOutputFormat()).toBe(OutputFormat.STREAM_JSON);
|
|
1777
|
+
});
|
|
1778
|
+
it('should error on invalid --output-format argument', async () => {
|
|
1779
|
+
process.argv = ['node', 'script.js', '--output-format', 'yaml'];
|
|
1780
|
+
const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
|
|
1781
|
+
throw new Error('process.exit called');
|
|
1782
|
+
});
|
|
1783
|
+
const mockConsoleError = vi
|
|
1784
|
+
.spyOn(console, 'error')
|
|
1785
|
+
.mockImplementation(() => { });
|
|
1786
|
+
await expect(parseArguments({})).rejects.toThrow('process.exit called');
|
|
1787
|
+
expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Invalid values:'));
|
|
1788
|
+
mockExit.mockRestore();
|
|
1789
|
+
mockConsoleError.mockRestore();
|
|
1790
|
+
});
|
|
1791
|
+
});
|
|
1792
|
+
describe('parseArguments with positional prompt', () => {
|
|
1793
|
+
const originalArgv = process.argv;
|
|
1794
|
+
afterEach(() => {
|
|
1795
|
+
process.argv = originalArgv;
|
|
1796
|
+
});
|
|
1797
|
+
it('should throw an error when both a positional prompt and the --prompt flag are used', async () => {
|
|
1798
|
+
process.argv = [
|
|
1799
|
+
'node',
|
|
1800
|
+
'script.js',
|
|
1801
|
+
'positional',
|
|
1802
|
+
'prompt',
|
|
1803
|
+
'--prompt',
|
|
1804
|
+
'test prompt',
|
|
1805
|
+
];
|
|
1806
|
+
const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
|
|
1807
|
+
throw new Error('process.exit called');
|
|
1808
|
+
});
|
|
1809
|
+
const mockConsoleError = vi
|
|
1810
|
+
.spyOn(console, 'error')
|
|
1811
|
+
.mockImplementation(() => { });
|
|
1812
|
+
await expect(parseArguments({})).rejects.toThrow('process.exit called');
|
|
1813
|
+
expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('Cannot use both a positional prompt and the --prompt (-p) flag together'));
|
|
1814
|
+
mockExit.mockRestore();
|
|
1815
|
+
mockConsoleError.mockRestore();
|
|
1816
|
+
});
|
|
1817
|
+
it('should correctly parse a positional prompt to query field', async () => {
|
|
1818
|
+
process.argv = ['node', 'script.js', 'positional', 'prompt'];
|
|
1819
|
+
const argv = await parseArguments({});
|
|
1820
|
+
expect(argv.query).toBe('positional prompt');
|
|
1821
|
+
// Since no explicit prompt flags are set and query doesn't start with @, should map to prompt (one-shot)
|
|
1822
|
+
expect(argv.prompt).toBe('positional prompt');
|
|
1823
|
+
expect(argv.promptInteractive).toBeUndefined();
|
|
1824
|
+
});
|
|
1825
|
+
it('should have correct positional argument description', async () => {
|
|
1826
|
+
// Test that the positional argument has the expected description
|
|
1827
|
+
const yargsInstance = await import('./config.js');
|
|
1828
|
+
// This test verifies that the positional 'query' argument is properly configured
|
|
1829
|
+
// with the description: "Positional prompt. Defaults to one-shot; use -i/--prompt-interactive for interactive."
|
|
1830
|
+
process.argv = ['node', 'script.js', 'test', 'query'];
|
|
1831
|
+
const argv = await yargsInstance.parseArguments({});
|
|
1832
|
+
expect(argv.query).toBe('test query');
|
|
1833
|
+
});
|
|
1834
|
+
it('should correctly parse a prompt from the --prompt flag', async () => {
|
|
1835
|
+
process.argv = ['node', 'script.js', '--prompt', 'test prompt'];
|
|
1836
|
+
const argv = await parseArguments({});
|
|
1837
|
+
expect(argv.prompt).toBe('test prompt');
|
|
1838
|
+
});
|
|
1839
|
+
});
|
|
1840
|
+
describe('Telemetry configuration via environment variables', () => {
|
|
1841
|
+
it('should prioritize GEMINI_TELEMETRY_ENABLED over settings', async () => {
|
|
1842
|
+
vi.stubEnv('GEMINI_TELEMETRY_ENABLED', 'true');
|
|
1843
|
+
process.argv = ['node', 'script.js'];
|
|
1844
|
+
const argv = await parseArguments({});
|
|
1845
|
+
const settings = { telemetry: { enabled: false } };
|
|
1846
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1847
|
+
expect(config.getTelemetryEnabled()).toBe(true);
|
|
1848
|
+
});
|
|
1849
|
+
it('should prioritize GEMINI_TELEMETRY_TARGET over settings', async () => {
|
|
1850
|
+
vi.stubEnv('GEMINI_TELEMETRY_TARGET', 'gcp');
|
|
1851
|
+
process.argv = ['node', 'script.js'];
|
|
1852
|
+
const argv = await parseArguments({});
|
|
1853
|
+
const settings = {
|
|
1854
|
+
telemetry: { target: ServerConfig.TelemetryTarget.LOCAL },
|
|
1855
|
+
};
|
|
1856
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1857
|
+
expect(config.getTelemetryTarget()).toBe('gcp');
|
|
1858
|
+
});
|
|
1859
|
+
it('should throw when GEMINI_TELEMETRY_TARGET is invalid', async () => {
|
|
1860
|
+
vi.stubEnv('GEMINI_TELEMETRY_TARGET', 'bogus');
|
|
1861
|
+
process.argv = ['node', 'script.js'];
|
|
1862
|
+
const argv = await parseArguments({});
|
|
1863
|
+
const settings = {
|
|
1864
|
+
telemetry: { target: ServerConfig.TelemetryTarget.GCP },
|
|
1865
|
+
};
|
|
1866
|
+
await expect(loadCliConfig(settings, [], 'test-session', argv)).rejects.toThrow(/Invalid telemetry configuration: .*Invalid telemetry target/i);
|
|
1867
|
+
vi.unstubAllEnvs();
|
|
1868
|
+
});
|
|
1869
|
+
it('should prioritize GEMINI_TELEMETRY_OTLP_ENDPOINT over settings and default env var', async () => {
|
|
1870
|
+
vi.stubEnv('OTEL_EXPORTER_OTLP_ENDPOINT', 'http://default.env.com');
|
|
1871
|
+
vi.stubEnv('GEMINI_TELEMETRY_OTLP_ENDPOINT', 'http://gemini.env.com');
|
|
1872
|
+
process.argv = ['node', 'script.js'];
|
|
1873
|
+
const argv = await parseArguments({});
|
|
1874
|
+
const settings = {
|
|
1875
|
+
telemetry: { otlpEndpoint: 'http://settings.com' },
|
|
1876
|
+
};
|
|
1877
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1878
|
+
expect(config.getTelemetryOtlpEndpoint()).toBe('http://gemini.env.com');
|
|
1879
|
+
});
|
|
1880
|
+
it('should prioritize GEMINI_TELEMETRY_OTLP_PROTOCOL over settings', async () => {
|
|
1881
|
+
vi.stubEnv('GEMINI_TELEMETRY_OTLP_PROTOCOL', 'http');
|
|
1882
|
+
process.argv = ['node', 'script.js'];
|
|
1883
|
+
const argv = await parseArguments({});
|
|
1884
|
+
const settings = { telemetry: { otlpProtocol: 'grpc' } };
|
|
1885
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1886
|
+
expect(config.getTelemetryOtlpProtocol()).toBe('http');
|
|
1887
|
+
});
|
|
1888
|
+
it('should prioritize GEMINI_TELEMETRY_LOG_PROMPTS over settings', async () => {
|
|
1889
|
+
vi.stubEnv('GEMINI_TELEMETRY_LOG_PROMPTS', 'false');
|
|
1890
|
+
process.argv = ['node', 'script.js'];
|
|
1891
|
+
const argv = await parseArguments({});
|
|
1892
|
+
const settings = { telemetry: { logPrompts: true } };
|
|
1893
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1894
|
+
expect(config.getTelemetryLogPromptsEnabled()).toBe(false);
|
|
1895
|
+
});
|
|
1896
|
+
it('should prioritize GEMINI_TELEMETRY_OUTFILE over settings', async () => {
|
|
1897
|
+
vi.stubEnv('GEMINI_TELEMETRY_OUTFILE', '/gemini/env/telemetry.log');
|
|
1898
|
+
process.argv = ['node', 'script.js'];
|
|
1899
|
+
const argv = await parseArguments({});
|
|
1900
|
+
const settings = {
|
|
1901
|
+
telemetry: { outfile: '/settings/telemetry.log' },
|
|
1902
|
+
};
|
|
1903
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1904
|
+
expect(config.getTelemetryOutfile()).toBe('/gemini/env/telemetry.log');
|
|
1905
|
+
});
|
|
1906
|
+
it('should prioritize GEMINI_TELEMETRY_USE_COLLECTOR over settings', async () => {
|
|
1907
|
+
vi.stubEnv('GEMINI_TELEMETRY_USE_COLLECTOR', 'true');
|
|
1908
|
+
process.argv = ['node', 'script.js'];
|
|
1909
|
+
const argv = await parseArguments({});
|
|
1910
|
+
const settings = { telemetry: { useCollector: false } };
|
|
1911
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1912
|
+
expect(config.getTelemetryUseCollector()).toBe(true);
|
|
1913
|
+
});
|
|
1914
|
+
it('should use settings value when GEMINI_TELEMETRY_ENABLED is not set', async () => {
|
|
1915
|
+
vi.stubEnv('GEMINI_TELEMETRY_ENABLED', undefined);
|
|
1916
|
+
process.argv = ['node', 'script.js'];
|
|
1917
|
+
const argv = await parseArguments({});
|
|
1918
|
+
const settings = { telemetry: { enabled: true } };
|
|
1919
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1920
|
+
expect(config.getTelemetryEnabled()).toBe(true);
|
|
1921
|
+
});
|
|
1922
|
+
it('should use settings value when GEMINI_TELEMETRY_TARGET is not set', async () => {
|
|
1923
|
+
vi.stubEnv('GEMINI_TELEMETRY_TARGET', undefined);
|
|
1924
|
+
process.argv = ['node', 'script.js'];
|
|
1925
|
+
const argv = await parseArguments({});
|
|
1926
|
+
const settings = {
|
|
1927
|
+
telemetry: { target: ServerConfig.TelemetryTarget.LOCAL },
|
|
1928
|
+
};
|
|
1929
|
+
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
|
1930
|
+
expect(config.getTelemetryTarget()).toBe('local');
|
|
1931
|
+
});
|
|
1932
|
+
it("should treat GEMINI_TELEMETRY_ENABLED='1' as true", async () => {
|
|
1933
|
+
vi.stubEnv('GEMINI_TELEMETRY_ENABLED', '1');
|
|
1934
|
+
process.argv = ['node', 'script.js'];
|
|
1935
|
+
const argv = await parseArguments({});
|
|
1936
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1937
|
+
expect(config.getTelemetryEnabled()).toBe(true);
|
|
1938
|
+
});
|
|
1939
|
+
it("should treat GEMINI_TELEMETRY_ENABLED='0' as false", async () => {
|
|
1940
|
+
vi.stubEnv('GEMINI_TELEMETRY_ENABLED', '0');
|
|
1941
|
+
process.argv = ['node', 'script.js'];
|
|
1942
|
+
const argv = await parseArguments({});
|
|
1943
|
+
const config = await loadCliConfig({ telemetry: { enabled: true } }, [], 'test-session', argv);
|
|
1944
|
+
expect(config.getTelemetryEnabled()).toBe(false);
|
|
1945
|
+
});
|
|
1946
|
+
it("should treat GEMINI_TELEMETRY_LOG_PROMPTS='1' as true", async () => {
|
|
1947
|
+
vi.stubEnv('GEMINI_TELEMETRY_LOG_PROMPTS', '1');
|
|
1948
|
+
process.argv = ['node', 'script.js'];
|
|
1949
|
+
const argv = await parseArguments({});
|
|
1950
|
+
const config = await loadCliConfig({}, [], 'test-session', argv);
|
|
1951
|
+
expect(config.getTelemetryLogPromptsEnabled()).toBe(true);
|
|
1952
|
+
});
|
|
1953
|
+
it("should treat GEMINI_TELEMETRY_LOG_PROMPTS='false' as false", async () => {
|
|
1954
|
+
vi.stubEnv('GEMINI_TELEMETRY_LOG_PROMPTS', 'false');
|
|
1955
|
+
process.argv = ['node', 'script.js'];
|
|
1956
|
+
const argv = await parseArguments({});
|
|
1957
|
+
const config = await loadCliConfig({ telemetry: { logPrompts: true } }, [], 'test-session', argv);
|
|
1958
|
+
expect(config.getTelemetryLogPromptsEnabled()).toBe(false);
|
|
1959
|
+
});
|
|
1960
|
+
});
|
|
1961
|
+
//# sourceMappingURL=config.test.js.map
|