@google/gemini-cli 0.1.20 → 0.1.22
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 +27 -29
- package/dist/package.json +4 -4
- package/dist/src/commands/mcp/add.js +21 -10
- package/dist/src/commands/mcp/add.js.map +1 -1
- package/dist/src/commands/mcp/remove.js +2 -2
- package/dist/src/commands/mcp/remove.js.map +1 -1
- package/dist/src/config/auth.js +4 -3
- package/dist/src/config/auth.js.map +1 -1
- package/dist/src/config/auth.test.d.ts +6 -0
- package/dist/src/config/auth.test.js +57 -0
- package/dist/src/config/auth.test.js.map +1 -0
- package/dist/src/config/config.d.ts +2 -1
- package/dist/src/config/config.js +28 -18
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/keyBindings.js +7 -2
- package/dist/src/config/keyBindings.js.map +1 -1
- package/dist/src/config/keyBindings.test.d.ts +6 -0
- package/dist/src/config/keyBindings.test.js +51 -0
- package/dist/src/config/keyBindings.test.js.map +1 -0
- package/dist/src/config/sandboxConfig.js +3 -3
- package/dist/src/config/sandboxConfig.js.map +1 -1
- package/dist/src/config/settings.js +7 -7
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/config/settingsSchema.test.d.ts +6 -0
- package/dist/src/config/settingsSchema.test.js +195 -0
- package/dist/src/config/settingsSchema.test.js.map +1 -0
- package/dist/src/config/trustedFolders.d.ts +37 -0
- package/dist/src/config/trustedFolders.js +118 -0
- package/dist/src/config/trustedFolders.js.map +1 -0
- package/dist/src/config/trustedFolders.test.d.ts +6 -0
- package/dist/src/config/trustedFolders.test.js +160 -0
- package/dist/src/config/trustedFolders.test.js.map +1 -0
- package/dist/src/gemini.js +23 -10
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.d.ts +6 -0
- package/dist/src/gemini.test.js +193 -0
- package/dist/src/gemini.test.js.map +1 -0
- package/dist/src/generated/git-commit.d.ts +2 -1
- package/dist/src/generated/git-commit.js +2 -1
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/nonInteractiveCli.js +1 -4
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +2 -0
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.test.d.ts +6 -0
- package/dist/src/services/BuiltinCommandLoader.test.js +108 -0
- package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -0
- package/dist/src/services/CommandService.test.d.ts +6 -0
- package/dist/src/services/CommandService.test.js +232 -0
- package/dist/src/services/CommandService.test.js.map +1 -0
- package/dist/src/services/FileCommandLoader.js +10 -8
- package/dist/src/services/FileCommandLoader.js.map +1 -1
- package/dist/src/services/McpPromptLoader.js +3 -3
- package/dist/src/services/McpPromptLoader.js.map +1 -1
- package/dist/src/services/prompt-processors/argumentProcessor.d.ts +2 -7
- package/dist/src/services/prompt-processors/argumentProcessor.js +2 -10
- package/dist/src/services/prompt-processors/argumentProcessor.js.map +1 -1
- package/dist/src/services/prompt-processors/shellProcessor.d.ts +16 -13
- package/dist/src/services/prompt-processors/shellProcessor.js +133 -40
- package/dist/src/services/prompt-processors/shellProcessor.js.map +1 -1
- package/dist/src/services/prompt-processors/types.d.ts +2 -0
- package/dist/src/services/prompt-processors/types.js +2 -0
- package/dist/src/services/prompt-processors/types.js.map +1 -1
- package/dist/src/test-utils/customMatchers.d.ts +14 -0
- package/dist/src/test-utils/customMatchers.js +46 -0
- package/dist/src/test-utils/customMatchers.js.map +1 -0
- package/dist/src/test-utils/mockCommandContext.d.ts +18 -0
- package/dist/src/test-utils/mockCommandContext.js +86 -0
- package/dist/src/test-utils/mockCommandContext.js.map +1 -0
- package/dist/src/test-utils/mockCommandContext.test.d.ts +6 -0
- package/dist/src/test-utils/mockCommandContext.test.js +51 -0
- package/dist/src/test-utils/mockCommandContext.test.js.map +1 -0
- package/dist/src/test-utils/render.d.ts +8 -0
- package/dist/src/test-utils/render.js +10 -0
- package/dist/src/test-utils/render.js.map +1 -0
- package/dist/src/ui/App.js +14 -6
- package/dist/src/ui/App.js.map +1 -1
- package/dist/src/ui/IdeIntegrationNudge.js +3 -3
- package/dist/src/ui/IdeIntegrationNudge.js.map +1 -1
- package/dist/src/ui/commands/aboutCommand.js +8 -5
- package/dist/src/ui/commands/aboutCommand.js.map +1 -1
- package/dist/src/ui/commands/bugCommand.js +6 -4
- package/dist/src/ui/commands/bugCommand.js.map +1 -1
- package/dist/src/ui/commands/chatCommand.js +8 -6
- package/dist/src/ui/commands/chatCommand.js.map +1 -1
- package/dist/src/ui/commands/directoryCommand.js +2 -4
- package/dist/src/ui/commands/directoryCommand.js.map +1 -1
- package/dist/src/ui/commands/docsCommand.js +1 -1
- package/dist/src/ui/commands/docsCommand.js.map +1 -1
- package/dist/src/ui/commands/ideCommand.js +2 -2
- package/dist/src/ui/commands/ideCommand.js.map +1 -1
- package/dist/src/ui/commands/mcpCommand.js +4 -0
- package/dist/src/ui/commands/mcpCommand.js.map +1 -1
- package/dist/src/ui/commands/setupGithubCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/setupGithubCommand.test.js +74 -0
- package/dist/src/ui/commands/setupGithubCommand.test.js.map +1 -0
- package/dist/src/ui/commands/terminalSetupCommand.d.ts +13 -0
- package/dist/src/ui/commands/terminalSetupCommand.js +41 -0
- package/dist/src/ui/commands/terminalSetupCommand.js.map +1 -0
- package/dist/src/ui/commands/types.d.ts +1 -0
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/components/AboutBox.d.ts +1 -0
- package/dist/src/ui/components/AboutBox.js +1 -1
- package/dist/src/ui/components/AboutBox.js.map +1 -1
- package/dist/src/ui/components/AuthDialog.js +8 -8
- package/dist/src/ui/components/AuthDialog.js.map +1 -1
- package/dist/src/ui/components/AuthDialog.test.d.ts +6 -0
- package/dist/src/ui/components/AuthDialog.test.js +242 -0
- package/dist/src/ui/components/AuthDialog.test.js.map +1 -0
- package/dist/src/ui/components/ContextSummaryDisplay.js +1 -1
- package/dist/src/ui/components/FolderTrustDialog.test.d.ts +6 -0
- package/dist/src/ui/components/FolderTrustDialog.test.js +26 -0
- package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -0
- package/dist/src/ui/components/Footer.d.ts +1 -0
- package/dist/src/ui/components/Footer.js +3 -2
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/Header.test.d.ts +6 -0
- package/dist/src/ui/components/Header.test.js +37 -0
- package/dist/src/ui/components/Header.test.js.map +1 -0
- package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/HistoryItemDisplay.test.js +91 -0
- package/dist/src/ui/components/HistoryItemDisplay.test.js.map +1 -0
- package/dist/src/ui/components/InputPrompt.js +3 -1
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/LoadingIndicator.test.d.ts +6 -0
- package/dist/src/ui/components/LoadingIndicator.test.js +190 -0
- package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -0
- package/dist/src/ui/components/SettingsDialog.test.d.ts +6 -0
- package/dist/src/ui/components/SettingsDialog.test.js +555 -0
- package/dist/src/ui/components/SettingsDialog.test.js.map +1 -0
- package/dist/src/ui/components/ShellConfirmationDialog.js +2 -1
- package/dist/src/ui/components/ShellConfirmationDialog.js.map +1 -1
- package/dist/src/ui/components/ShellConfirmationDialog.test.d.ts +6 -0
- package/dist/src/ui/components/ShellConfirmationDialog.test.js +40 -0
- package/dist/src/ui/components/ShellConfirmationDialog.test.js.map +1 -0
- package/dist/src/ui/components/StatsDisplay.js +1 -1
- package/dist/src/ui/components/StatsDisplay.js.map +1 -1
- package/dist/src/ui/components/messages/DiffRenderer.js +10 -1
- package/dist/src/ui/components/messages/DiffRenderer.js.map +1 -1
- package/dist/src/ui/components/messages/DiffRenderer.test.d.ts +6 -0
- package/dist/src/ui/components/messages/DiffRenderer.test.js +239 -0
- package/dist/src/ui/components/messages/DiffRenderer.test.js.map +1 -0
- package/dist/src/ui/components/messages/InfoMessage.js +2 -1
- package/dist/src/ui/components/messages/InfoMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js +2 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.test.d.ts +6 -0
- package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js +37 -0
- package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js.map +1 -0
- package/dist/src/ui/components/messages/ToolMessage.test.d.ts +6 -0
- package/dist/src/ui/components/messages/ToolMessage.test.js +118 -0
- package/dist/src/ui/components/messages/ToolMessage.test.js.map +1 -0
- package/dist/src/ui/components/shared/MaxSizedBox.js +1 -1
- package/dist/src/ui/components/shared/MaxSizedBox.js.map +1 -1
- package/dist/src/ui/components/shared/MaxSizedBox.test.d.ts +6 -0
- package/dist/src/ui/components/shared/MaxSizedBox.test.js +154 -0
- package/dist/src/ui/components/shared/MaxSizedBox.test.js.map +1 -0
- package/dist/src/ui/components/shared/RadioButtonSelect.js +1 -1
- package/dist/src/ui/components/shared/RadioButtonSelect.js.map +1 -1
- package/dist/src/ui/components/shared/RadioButtonSelect.test.d.ts +6 -0
- package/dist/src/ui/components/shared/RadioButtonSelect.test.js +111 -0
- package/dist/src/ui/components/shared/RadioButtonSelect.test.js.map +1 -0
- package/dist/src/ui/components/shared/text-buffer.js +32 -10
- package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
- package/dist/src/ui/components/shared/vim-buffer-actions.js +2 -1
- package/dist/src/ui/components/shared/vim-buffer-actions.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.d.ts +30 -0
- package/dist/src/ui/contexts/KeypressContext.js +310 -0
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -0
- package/dist/src/ui/contexts/KeypressContext.test.d.ts +6 -0
- package/dist/src/ui/contexts/KeypressContext.test.js +220 -0
- package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -0
- package/dist/src/ui/contexts/SessionContext.d.ts +1 -0
- package/dist/src/ui/contexts/SessionContext.js +2 -1
- package/dist/src/ui/contexts/SessionContext.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.js +9 -9
- package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.test.d.ts +6 -0
- package/dist/src/ui/hooks/atCommandProcessor.test.js +802 -0
- package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -0
- package/dist/src/ui/hooks/shellCommandProcessor.test.d.ts +6 -0
- package/dist/src/ui/hooks/shellCommandProcessor.test.js +328 -0
- package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -0
- package/dist/src/ui/hooks/slashCommandProcessor.js +74 -53
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useAtCompletion.js +3 -5
- package/dist/src/ui/hooks/useAtCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.d.ts +6 -0
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +191 -0
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -0
- package/dist/src/ui/hooks/useEditorSettings.test.d.ts +6 -0
- package/dist/src/ui/hooks/useEditorSettings.test.js +164 -0
- package/dist/src/ui/hooks/useEditorSettings.test.js.map +1 -0
- package/dist/src/ui/hooks/useFocus.d.ts +4 -0
- package/dist/src/ui/hooks/useFocus.js +4 -4
- package/dist/src/ui/hooks/useFocus.js.map +1 -1
- package/dist/src/ui/hooks/useFolderTrust.d.ts +3 -2
- package/dist/src/ui/hooks/useFolderTrust.js +43 -10
- package/dist/src/ui/hooks/useFolderTrust.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +1 -2
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useGitBranchName.test.d.ts +6 -0
- package/dist/src/ui/hooks/useGitBranchName.test.js +175 -0
- package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -0
- package/dist/src/ui/hooks/useHistoryManager.test.d.ts +6 -0
- package/dist/src/ui/hooks/useHistoryManager.test.js +171 -0
- package/dist/src/ui/hooks/useHistoryManager.test.js.map +1 -0
- package/dist/src/ui/hooks/useInputHistory.test.d.ts +6 -0
- package/dist/src/ui/hooks/useInputHistory.test.js +207 -0
- package/dist/src/ui/hooks/useInputHistory.test.js.map +1 -0
- package/dist/src/ui/hooks/useKeypress.d.ts +4 -16
- package/dist/src/ui/hooks/useKeypress.js +8 -140
- package/dist/src/ui/hooks/useKeypress.js.map +1 -1
- package/dist/src/ui/hooks/useKittyKeyboardProtocol.d.ts +15 -0
- package/dist/src/ui/hooks/useKittyKeyboardProtocol.js +20 -0
- package/dist/src/ui/hooks/useKittyKeyboardProtocol.js.map +1 -0
- package/dist/src/ui/hooks/useLoadingIndicator.test.d.ts +6 -0
- package/dist/src/ui/hooks/useLoadingIndicator.test.js +91 -0
- package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -0
- package/dist/src/ui/hooks/useReverseSearchCompletion.test.d.ts +6 -0
- package/dist/src/ui/hooks/useReverseSearchCompletion.test.js +163 -0
- package/dist/src/ui/hooks/useReverseSearchCompletion.test.js.map +1 -0
- package/dist/src/ui/hooks/useShellHistory.test.d.ts +6 -0
- package/dist/src/ui/hooks/useShellHistory.test.js +162 -0
- package/dist/src/ui/hooks/useShellHistory.test.js.map +1 -0
- package/dist/src/ui/hooks/useSlashCompletion.test.d.ts +6 -0
- package/dist/src/ui/hooks/useSlashCompletion.test.js +272 -0
- package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -0
- package/dist/src/ui/hooks/useThemeCommand.js +1 -1
- package/dist/src/ui/hooks/useThemeCommand.js.map +1 -1
- package/dist/src/ui/hooks/useTimer.test.d.ts +6 -0
- package/dist/src/ui/hooks/useTimer.test.js +90 -0
- package/dist/src/ui/hooks/useTimer.test.js.map +1 -0
- package/dist/src/ui/hooks/useToolScheduler.test.d.ts +6 -0
- package/dist/src/ui/hooks/useToolScheduler.test.js +841 -0
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -0
- package/dist/src/ui/keyMatchers.test.d.ts +6 -0
- package/dist/src/ui/keyMatchers.test.js +276 -0
- package/dist/src/ui/keyMatchers.test.js.map +1 -0
- package/dist/src/ui/themes/color-utils.test.d.ts +6 -0
- package/dist/src/ui/themes/color-utils.test.js +197 -0
- package/dist/src/ui/themes/color-utils.test.js.map +1 -0
- package/dist/src/ui/themes/theme-manager.js +1 -1
- package/dist/src/ui/themes/theme-manager.js.map +1 -1
- package/dist/src/ui/themes/theme-manager.test.d.ts +6 -0
- package/dist/src/ui/themes/theme-manager.test.js +83 -0
- package/dist/src/ui/themes/theme-manager.test.js.map +1 -0
- package/dist/src/ui/types.d.ts +2 -0
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/ui/utils/CodeColorizer.js +1 -1
- package/dist/src/ui/utils/CodeColorizer.js.map +1 -1
- package/dist/src/ui/utils/InlineMarkdownRenderer.js +8 -1
- package/dist/src/ui/utils/InlineMarkdownRenderer.js.map +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.test.d.ts +6 -0
- package/dist/src/ui/utils/MarkdownDisplay.test.js +151 -0
- package/dist/src/ui/utils/MarkdownDisplay.test.js.map +1 -0
- package/dist/src/ui/utils/clipboardUtils.test.d.ts +6 -0
- package/dist/src/ui/utils/clipboardUtils.test.js +65 -0
- package/dist/src/ui/utils/clipboardUtils.test.js.map +1 -0
- package/dist/src/ui/utils/commandUtils.test.d.ts +6 -0
- package/dist/src/ui/utils/commandUtils.test.js +294 -0
- package/dist/src/ui/utils/commandUtils.test.js.map +1 -0
- package/dist/src/ui/utils/displayUtils.test.d.ts +6 -0
- package/dist/src/ui/utils/displayUtils.test.js +42 -0
- package/dist/src/ui/utils/displayUtils.test.js.map +1 -0
- package/dist/src/ui/utils/formatters.test.d.ts +6 -0
- package/dist/src/ui/utils/formatters.test.js +56 -0
- package/dist/src/ui/utils/formatters.test.js.map +1 -0
- package/dist/src/ui/utils/kittyProtocolDetector.d.ts +13 -0
- package/dist/src/ui/utils/kittyProtocolDetector.js +88 -0
- package/dist/src/ui/utils/kittyProtocolDetector.js.map +1 -0
- package/dist/src/ui/utils/markdownUtilities.test.d.ts +6 -0
- package/dist/src/ui/utils/markdownUtilities.test.js +42 -0
- package/dist/src/ui/utils/markdownUtilities.test.js.map +1 -0
- package/dist/src/ui/utils/platformConstants.d.ts +43 -0
- package/dist/src/ui/utils/platformConstants.js +44 -0
- package/dist/src/ui/utils/platformConstants.js.map +1 -0
- package/dist/src/ui/utils/terminalSetup.d.ts +30 -0
- package/dist/src/ui/utils/terminalSetup.js +281 -0
- package/dist/src/ui/utils/terminalSetup.js.map +1 -0
- package/dist/src/ui/utils/updateCheck.js +1 -1
- package/dist/src/ui/utils/updateCheck.js.map +1 -1
- package/dist/src/ui/utils/updateCheck.test.d.ts +6 -0
- package/dist/src/ui/utils/updateCheck.test.js +145 -0
- package/dist/src/ui/utils/updateCheck.test.js.map +1 -0
- package/dist/src/utils/checks.d.ts +19 -0
- package/dist/src/utils/checks.js +24 -0
- package/dist/src/utils/checks.js.map +1 -0
- package/dist/src/utils/gitUtils.test.d.ts +6 -0
- package/dist/src/utils/gitUtils.test.js +113 -0
- package/dist/src/utils/gitUtils.test.js.map +1 -0
- package/dist/src/utils/installationInfo.test.d.ts +6 -0
- package/dist/src/utils/installationInfo.test.js +242 -0
- package/dist/src/utils/installationInfo.test.js.map +1 -0
- package/dist/src/utils/readStdin.js +10 -0
- package/dist/src/utils/readStdin.js.map +1 -1
- package/dist/src/utils/sandbox.js +62 -60
- package/dist/src/utils/sandbox.js.map +1 -1
- package/dist/src/utils/settingsUtils.test.d.ts +6 -0
- package/dist/src/utils/settingsUtils.test.js +514 -0
- package/dist/src/utils/settingsUtils.test.js.map +1 -0
- package/dist/src/utils/userStartupWarnings.test.d.ts +6 -0
- package/dist/src/utils/userStartupWarnings.test.js +67 -0
- package/dist/src/utils/userStartupWarnings.test.js.map +1 -0
- package/dist/src/utils/version.js +1 -1
- package/dist/src/utils/version.js.map +1 -1
- package/dist/src/validateNonInterActiveAuth.js +3 -3
- package/dist/src/validateNonInterActiveAuth.js.map +1 -1
- package/dist/src/zed-integration/acp.d.ts +63 -0
- package/dist/src/{acp → zed-integration}/acp.js +76 -44
- package/dist/src/zed-integration/acp.js.map +1 -0
- package/dist/src/zed-integration/schema.d.ts +11679 -0
- package/dist/src/zed-integration/schema.js +305 -0
- package/dist/src/zed-integration/schema.js.map +1 -0
- package/dist/src/zed-integration/zedIntegration.d.ts +10 -0
- package/dist/src/{acp/acpPeer.js → zed-integration/zedIntegration.js} +333 -187
- package/dist/src/zed-integration/zedIntegration.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/dist/src/acp/acp.d.ts +0 -208
- package/dist/src/acp/acp.js.map +0 -1
- package/dist/src/acp/acpPeer.d.ts +0 -8
- package/dist/src/acp/acpPeer.js.map +0 -1
- package/dist/src/ui/utils/errorParsing.d.ts +0 -7
- package/dist/src/ui/utils/errorParsing.js +0 -90
- package/dist/src/ui/utils/errorParsing.js.map +0 -1
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { checkCommandPermissions, ShellExecutionService, } from '@google/gemini-cli-core';
|
|
6
|
+
import { ApprovalMode, checkCommandPermissions, escapeShellArg, getShellConfiguration, ShellExecutionService, } from '@google/gemini-cli-core';
|
|
7
|
+
import { SHELL_INJECTION_TRIGGER, SHORTHAND_ARGS_PLACEHOLDER, } from './types.js';
|
|
7
8
|
export class ConfirmationRequiredError extends Error {
|
|
8
9
|
commandsToConfirm;
|
|
9
10
|
constructor(message, commandsToConfirm) {
|
|
@@ -13,65 +14,157 @@ export class ConfirmationRequiredError extends Error {
|
|
|
13
14
|
}
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
17
|
+
* Handles prompt interpolation, including shell command execution (`!{...}`)
|
|
18
|
+
* and context-aware argument injection (`{{args}}`).
|
|
18
19
|
*
|
|
19
|
-
* This processor ensures that
|
|
20
|
-
*
|
|
20
|
+
* This processor ensures that:
|
|
21
|
+
* 1. `{{args}}` outside `!{...}` are replaced with raw input.
|
|
22
|
+
* 2. `{{args}}` inside `!{...}` are replaced with shell-escaped input.
|
|
23
|
+
* 3. Shell commands are executed securely after argument substitution.
|
|
24
|
+
* 4. Parsing correctly handles nested braces.
|
|
21
25
|
*/
|
|
22
26
|
export class ShellProcessor {
|
|
23
27
|
commandName;
|
|
24
|
-
/**
|
|
25
|
-
* A regular expression to find all instances of `!{...}`. The inner
|
|
26
|
-
* capture group extracts the command itself.
|
|
27
|
-
*/
|
|
28
|
-
static SHELL_INJECTION_REGEX = /!\{([^}]*)\}/g;
|
|
29
|
-
/**
|
|
30
|
-
* @param commandName The name of the custom command being executed, used
|
|
31
|
-
* for logging and error messages.
|
|
32
|
-
*/
|
|
33
28
|
constructor(commandName) {
|
|
34
29
|
this.commandName = commandName;
|
|
35
30
|
}
|
|
36
31
|
async process(prompt, context) {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
32
|
+
const userArgsRaw = context.invocation?.args || '';
|
|
33
|
+
if (!prompt.includes(SHELL_INJECTION_TRIGGER)) {
|
|
34
|
+
return prompt.replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsRaw);
|
|
35
|
+
}
|
|
36
|
+
const config = context.services.config;
|
|
37
|
+
if (!config) {
|
|
38
|
+
throw new Error(`Security configuration not loaded. Cannot verify shell command permissions for '${this.commandName}'. Aborting.`);
|
|
39
|
+
}
|
|
40
|
+
const { sessionShellAllowlist } = context.session;
|
|
41
|
+
const injections = this.extractInjections(prompt);
|
|
42
|
+
// If extractInjections found no closed blocks (and didn't throw), treat as raw.
|
|
43
|
+
if (injections.length === 0) {
|
|
44
|
+
return prompt.replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsRaw);
|
|
46
45
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
const { shell } = getShellConfiguration();
|
|
47
|
+
const userArgsEscaped = escapeShellArg(userArgsRaw, shell);
|
|
48
|
+
const resolvedInjections = injections.map((injection) => {
|
|
49
|
+
if (injection.command === '') {
|
|
50
|
+
return injection;
|
|
51
|
+
}
|
|
52
|
+
// Replace {{args}} inside the command string with the escaped version.
|
|
53
|
+
const resolvedCommand = injection.command.replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsEscaped);
|
|
54
|
+
return { ...injection, resolvedCommand };
|
|
55
|
+
});
|
|
56
|
+
const commandsToConfirm = new Set();
|
|
57
|
+
for (const injection of resolvedInjections) {
|
|
58
|
+
const command = injection.resolvedCommand;
|
|
59
|
+
if (!command)
|
|
60
|
+
continue;
|
|
61
|
+
// Security check on the final, escaped command string.
|
|
50
62
|
const { allAllowed, disallowedCommands, blockReason, isHardDenial } = checkCommandPermissions(command, config, sessionShellAllowlist);
|
|
51
63
|
if (!allAllowed) {
|
|
52
|
-
// If it's a hard denial, this is a non-recoverable security error.
|
|
53
64
|
if (isHardDenial) {
|
|
54
|
-
throw new Error(`${this.commandName} cannot be run.
|
|
65
|
+
throw new Error(`${this.commandName} cannot be run. Blocked command: "${command}". Reason: ${blockReason || 'Blocked by configuration.'}`);
|
|
66
|
+
}
|
|
67
|
+
// If not a hard denial, respect YOLO mode and auto-approve.
|
|
68
|
+
if (config.getApprovalMode() !== ApprovalMode.YOLO) {
|
|
69
|
+
disallowedCommands.forEach((uc) => commandsToConfirm.add(uc));
|
|
55
70
|
}
|
|
56
|
-
// Add each soft denial disallowed command to the set for confirmation.
|
|
57
|
-
disallowedCommands.forEach((uc) => commandsToConfirm.add(uc));
|
|
58
71
|
}
|
|
59
|
-
commandsToExecute.push({ fullMatch: match[0], command });
|
|
60
72
|
}
|
|
61
|
-
//
|
|
62
|
-
// pipeline and trigger the UI flow.
|
|
73
|
+
// Handle confirmation requirements.
|
|
63
74
|
if (commandsToConfirm.size > 0) {
|
|
64
75
|
throw new ConfirmationRequiredError('Shell command confirmation required', Array.from(commandsToConfirm));
|
|
65
76
|
}
|
|
66
|
-
|
|
67
|
-
let
|
|
68
|
-
for (const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
let processedPrompt = '';
|
|
78
|
+
let lastIndex = 0;
|
|
79
|
+
for (const injection of resolvedInjections) {
|
|
80
|
+
// Append the text segment BEFORE the injection, substituting {{args}} with RAW input.
|
|
81
|
+
const segment = prompt.substring(lastIndex, injection.startIndex);
|
|
82
|
+
processedPrompt += segment.replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsRaw);
|
|
83
|
+
// Execute the resolved command (which already has ESCAPED input).
|
|
84
|
+
if (injection.resolvedCommand) {
|
|
85
|
+
const { result } = ShellExecutionService.execute(injection.resolvedCommand, config.getTargetDir(), () => { }, new AbortController().signal);
|
|
86
|
+
const executionResult = await result;
|
|
87
|
+
// Handle Spawn Errors
|
|
88
|
+
if (executionResult.error && !executionResult.aborted) {
|
|
89
|
+
throw new Error(`Failed to start shell command in '${this.commandName}': ${executionResult.error.message}. Command: ${injection.resolvedCommand}`);
|
|
90
|
+
}
|
|
91
|
+
// Append the output, making stderr explicit for the model.
|
|
92
|
+
if (executionResult.stdout) {
|
|
93
|
+
processedPrompt += executionResult.stdout;
|
|
94
|
+
}
|
|
95
|
+
if (executionResult.stderr) {
|
|
96
|
+
if (executionResult.stdout) {
|
|
97
|
+
processedPrompt += '\n';
|
|
98
|
+
}
|
|
99
|
+
processedPrompt += `--- STDERR ---\n${executionResult.stderr}`;
|
|
100
|
+
}
|
|
101
|
+
// Append a status message if the command did not succeed.
|
|
102
|
+
if (executionResult.aborted) {
|
|
103
|
+
processedPrompt += `\n[Shell command '${injection.resolvedCommand}' aborted]`;
|
|
104
|
+
}
|
|
105
|
+
else if (executionResult.exitCode !== 0 &&
|
|
106
|
+
executionResult.exitCode !== null) {
|
|
107
|
+
processedPrompt += `\n[Shell command '${injection.resolvedCommand}' exited with code ${executionResult.exitCode}]`;
|
|
108
|
+
}
|
|
109
|
+
else if (executionResult.signal !== null) {
|
|
110
|
+
processedPrompt += `\n[Shell command '${injection.resolvedCommand}' terminated by signal ${executionResult.signal}]`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
lastIndex = injection.endIndex;
|
|
73
114
|
}
|
|
115
|
+
// Append the remaining text AFTER the last injection, substituting {{args}} with RAW input.
|
|
116
|
+
const finalSegment = prompt.substring(lastIndex);
|
|
117
|
+
processedPrompt += finalSegment.replaceAll(SHORTHAND_ARGS_PLACEHOLDER, userArgsRaw);
|
|
74
118
|
return processedPrompt;
|
|
75
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Iteratively parses the prompt string to extract shell injections (!{...}),
|
|
122
|
+
* correctly handling nested braces within the command.
|
|
123
|
+
*
|
|
124
|
+
* @param prompt The prompt string to parse.
|
|
125
|
+
* @returns An array of extracted ShellInjection objects.
|
|
126
|
+
* @throws Error if an unclosed injection (`!{`) is found.
|
|
127
|
+
*/
|
|
128
|
+
extractInjections(prompt) {
|
|
129
|
+
const injections = [];
|
|
130
|
+
let index = 0;
|
|
131
|
+
while (index < prompt.length) {
|
|
132
|
+
const startIndex = prompt.indexOf(SHELL_INJECTION_TRIGGER, index);
|
|
133
|
+
if (startIndex === -1) {
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
let currentIndex = startIndex + SHELL_INJECTION_TRIGGER.length;
|
|
137
|
+
let braceCount = 1;
|
|
138
|
+
let foundEnd = false;
|
|
139
|
+
while (currentIndex < prompt.length) {
|
|
140
|
+
const char = prompt[currentIndex];
|
|
141
|
+
// We count literal braces. This parser does not interpret shell quoting/escaping.
|
|
142
|
+
if (char === '{') {
|
|
143
|
+
braceCount++;
|
|
144
|
+
}
|
|
145
|
+
else if (char === '}') {
|
|
146
|
+
braceCount--;
|
|
147
|
+
if (braceCount === 0) {
|
|
148
|
+
const commandContent = prompt.substring(startIndex + SHELL_INJECTION_TRIGGER.length, currentIndex);
|
|
149
|
+
const endIndex = currentIndex + 1;
|
|
150
|
+
injections.push({
|
|
151
|
+
command: commandContent.trim(),
|
|
152
|
+
startIndex,
|
|
153
|
+
endIndex,
|
|
154
|
+
});
|
|
155
|
+
index = endIndex;
|
|
156
|
+
foundEnd = true;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
currentIndex++;
|
|
161
|
+
}
|
|
162
|
+
// Check if the inner loop finished without finding the closing brace.
|
|
163
|
+
if (!foundEnd) {
|
|
164
|
+
throw new Error(`Invalid syntax in command '${this.commandName}': Unclosed shell injection starting at index ${startIndex} ('!{'). Ensure braces are balanced.`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return injections;
|
|
168
|
+
}
|
|
76
169
|
}
|
|
77
170
|
//# sourceMappingURL=shellProcessor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shellProcessor.js","sourceRoot":"","sources":["../../../../src/services/prompt-processors/shellProcessor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"shellProcessor.js","sourceRoot":"","sources":["../../../../src/services/prompt-processors/shellProcessor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,YAAY,EACZ,uBAAuB,EACvB,cAAc,EACd,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAEL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AAEpB,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAGzC;IAFT,YACE,OAAe,EACR,iBAA2B;QAElC,KAAK,CAAC,OAAO,CAAC,CAAC;QAFR,sBAAiB,GAAjB,iBAAiB,CAAU;QAGlC,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;AAgBD;;;;;;;;;GASG;AACH,MAAM,OAAO,cAAc;IACI;IAA7B,YAA6B,WAAmB;QAAnB,gBAAW,GAAX,WAAW,CAAQ;IAAG,CAAC;IAEpD,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,OAAuB;QACnD,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC;QAEnD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,CAAC;YAC9C,OAAO,MAAM,CAAC,UAAU,CAAC,0BAA0B,EAAE,WAAW,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;QACvC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,mFAAmF,IAAI,CAAC,WAAW,cAAc,CAClH,CAAC;QACJ,CAAC;QACD,MAAM,EAAE,qBAAqB,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;QAElD,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAClD,gFAAgF;QAChF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAC,UAAU,CAAC,0BAA0B,EAAE,WAAW,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,GAAG,qBAAqB,EAAE,CAAC;QAC1C,MAAM,eAAe,GAAG,cAAc,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAE3D,MAAM,kBAAkB,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE;YACtD,IAAI,SAAS,CAAC,OAAO,KAAK,EAAE,EAAE,CAAC;gBAC7B,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,uEAAuE;YACvE,MAAM,eAAe,GAAG,SAAS,CAAC,OAAO,CAAC,UAAU,CAClD,0BAA0B,EAC1B,eAAe,CAChB,CAAC;YACF,OAAO,EAAE,GAAG,SAAS,EAAE,eAAe,EAAE,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAU,CAAC;QAC5C,KAAK,MAAM,SAAS,IAAI,kBAAkB,EAAE,CAAC;YAC3C,MAAM,OAAO,GAAG,SAAS,CAAC,eAAe,CAAC;YAE1C,IAAI,CAAC,OAAO;gBAAE,SAAS;YAEvB,uDAAuD;YACvD,MAAM,EAAE,UAAU,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,GACjE,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,qBAAqB,CAAC,CAAC;YAElE,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,IAAI,YAAY,EAAE,CAAC;oBACjB,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,WAAW,qCAAqC,OAAO,cAAc,WAAW,IAAI,2BAA2B,EAAE,CAC1H,CAAC;gBACJ,CAAC;gBAED,4DAA4D;gBAC5D,IAAI,MAAM,CAAC,eAAe,EAAE,KAAK,YAAY,CAAC,IAAI,EAAE,CAAC;oBACnD,kBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;gBAChE,CAAC;YACH,CAAC;QACH,CAAC;QAED,oCAAoC;QACpC,IAAI,iBAAiB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,yBAAyB,CACjC,qCAAqC,EACrC,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAC9B,CAAC;QACJ,CAAC;QAED,IAAI,eAAe,GAAG,EAAE,CAAC;QACzB,IAAI,SAAS,GAAG,CAAC,CAAC;QAElB,KAAK,MAAM,SAAS,IAAI,kBAAkB,EAAE,CAAC;YAC3C,sFAAsF;YACtF,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;YAClE,eAAe,IAAI,OAAO,CAAC,UAAU,CACnC,0BAA0B,EAC1B,WAAW,CACZ,CAAC;YAEF,kEAAkE;YAClE,IAAI,SAAS,CAAC,eAAe,EAAE,CAAC;gBAC9B,MAAM,EAAE,MAAM,EAAE,GAAG,qBAAqB,CAAC,OAAO,CAC9C,SAAS,CAAC,eAAe,EACzB,MAAM,CAAC,YAAY,EAAE,EACrB,GAAG,EAAE,GAAE,CAAC,EACR,IAAI,eAAe,EAAE,CAAC,MAAM,CAC7B,CAAC;gBAEF,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC;gBAErC,sBAAsB;gBACtB,IAAI,eAAe,CAAC,KAAK,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC;oBACtD,MAAM,IAAI,KAAK,CACb,qCAAqC,IAAI,CAAC,WAAW,MAAM,eAAe,CAAC,KAAK,CAAC,OAAO,cAAc,SAAS,CAAC,eAAe,EAAE,CAClI,CAAC;gBACJ,CAAC;gBAED,2DAA2D;gBAC3D,IAAI,eAAe,CAAC,MAAM,EAAE,CAAC;oBAC3B,eAAe,IAAI,eAAe,CAAC,MAAM,CAAC;gBAC5C,CAAC;gBACD,IAAI,eAAe,CAAC,MAAM,EAAE,CAAC;oBAC3B,IAAI,eAAe,CAAC,MAAM,EAAE,CAAC;wBAC3B,eAAe,IAAI,IAAI,CAAC;oBAC1B,CAAC;oBACD,eAAe,IAAI,mBAAmB,eAAe,CAAC,MAAM,EAAE,CAAC;gBACjE,CAAC;gBAED,0DAA0D;gBAC1D,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC;oBAC5B,eAAe,IAAI,qBAAqB,SAAS,CAAC,eAAe,YAAY,CAAC;gBAChF,CAAC;qBAAM,IACL,eAAe,CAAC,QAAQ,KAAK,CAAC;oBAC9B,eAAe,CAAC,QAAQ,KAAK,IAAI,EACjC,CAAC;oBACD,eAAe,IAAI,qBAAqB,SAAS,CAAC,eAAe,sBAAsB,eAAe,CAAC,QAAQ,GAAG,CAAC;gBACrH,CAAC;qBAAM,IAAI,eAAe,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;oBAC3C,eAAe,IAAI,qBAAqB,SAAS,CAAC,eAAe,0BAA0B,eAAe,CAAC,MAAM,GAAG,CAAC;gBACvH,CAAC;YACH,CAAC;YAED,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC;QACjC,CAAC;QAED,4FAA4F;QAC5F,MAAM,YAAY,GAAG,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACjD,eAAe,IAAI,YAAY,CAAC,UAAU,CACxC,0BAA0B,EAC1B,WAAW,CACZ,CAAC;QAEF,OAAO,eAAe,CAAC;IACzB,CAAC;IAED;;;;;;;OAOG;IACK,iBAAiB,CAAC,MAAc;QACtC,MAAM,UAAU,GAAqB,EAAE,CAAC;QACxC,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,OAAO,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;YAC7B,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;YAElE,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;gBACtB,MAAM;YACR,CAAC;YAED,IAAI,YAAY,GAAG,UAAU,GAAG,uBAAuB,CAAC,MAAM,CAAC;YAC/D,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,IAAI,QAAQ,GAAG,KAAK,CAAC;YAErB,OAAO,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;gBACpC,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;gBAElC,kFAAkF;gBAClF,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;oBACjB,UAAU,EAAE,CAAC;gBACf,CAAC;qBAAM,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;oBACxB,UAAU,EAAE,CAAC;oBACb,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;wBACrB,MAAM,cAAc,GAAG,MAAM,CAAC,SAAS,CACrC,UAAU,GAAG,uBAAuB,CAAC,MAAM,EAC3C,YAAY,CACb,CAAC;wBACF,MAAM,QAAQ,GAAG,YAAY,GAAG,CAAC,CAAC;wBAElC,UAAU,CAAC,IAAI,CAAC;4BACd,OAAO,EAAE,cAAc,CAAC,IAAI,EAAE;4BAC9B,UAAU;4BACV,QAAQ;yBACT,CAAC,CAAC;wBAEH,KAAK,GAAG,QAAQ,CAAC;wBACjB,QAAQ,GAAG,IAAI,CAAC;wBAChB,MAAM;oBACR,CAAC;gBACH,CAAC;gBACD,YAAY,EAAE,CAAC;YACjB,CAAC;YAED,sEAAsE;YACtE,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CACb,8BAA8B,IAAI,CAAC,WAAW,iDAAiD,UAAU,sCAAsC,CAChJ,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,UAAU,CAAC;IACpB,CAAC;CACF"}
|
|
@@ -30,6 +30,8 @@ export interface IPromptProcessor {
|
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* The placeholder string for shorthand argument injection in custom commands.
|
|
33
|
+
* When used outside of !{...}, arguments are injected raw.
|
|
34
|
+
* When used inside !{...}, arguments are shell-escaped.
|
|
33
35
|
*/
|
|
34
36
|
export declare const SHORTHAND_ARGS_PLACEHOLDER = "{{args}}";
|
|
35
37
|
/**
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* The placeholder string for shorthand argument injection in custom commands.
|
|
8
|
+
* When used outside of !{...}, arguments are injected raw.
|
|
9
|
+
* When used inside !{...}, arguments are shell-escaped.
|
|
8
10
|
*/
|
|
9
11
|
export const SHORTHAND_ARGS_PLACEHOLDER = '{{args}}';
|
|
10
12
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/services/prompt-processors/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA6BH
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/services/prompt-processors/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA6BH;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,UAAU,CAAC;AAErD;;GAEG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
declare module 'vitest' {
|
|
7
|
+
interface Assertion<T> {
|
|
8
|
+
toHaveOnlyValidCharacters(): T;
|
|
9
|
+
}
|
|
10
|
+
interface AsymmetricMatchersContaining {
|
|
11
|
+
toHaveOnlyValidCharacters(): void;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/// <reference types="vitest/globals" />
|
|
7
|
+
/**
|
|
8
|
+
* @license
|
|
9
|
+
* Copyright 2025 Google LLC
|
|
10
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
11
|
+
*/
|
|
12
|
+
import { expect } from 'vitest';
|
|
13
|
+
// RegExp to detect invalid characters: backspace, and ANSI escape codes
|
|
14
|
+
// eslint-disable-next-line no-control-regex
|
|
15
|
+
const invalidCharsRegex = /[\b\x1b]/;
|
|
16
|
+
function toHaveOnlyValidCharacters(buffer) {
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
const { isNot } = this;
|
|
19
|
+
let pass = true;
|
|
20
|
+
const invalidLines = [];
|
|
21
|
+
for (let i = 0; i < buffer.lines.length; i++) {
|
|
22
|
+
const line = buffer.lines[i];
|
|
23
|
+
if (line.includes('\n')) {
|
|
24
|
+
pass = false;
|
|
25
|
+
invalidLines.push({ line: i, content: line });
|
|
26
|
+
break; // Fail fast on newlines
|
|
27
|
+
}
|
|
28
|
+
if (invalidCharsRegex.test(line)) {
|
|
29
|
+
pass = false;
|
|
30
|
+
invalidLines.push({ line: i, content: line });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
pass,
|
|
35
|
+
message: () => `Expected buffer ${isNot ? 'not ' : ''}to have only valid characters, but found invalid characters in lines:\n${invalidLines
|
|
36
|
+
.map((l) => ` [${l.line}]: "${l.content}"`) /* This line was changed */
|
|
37
|
+
.join('\n')}`,
|
|
38
|
+
actual: buffer.lines,
|
|
39
|
+
expected: 'Lines with no line breaks, backspaces, or escape codes.',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
expect.extend({
|
|
43
|
+
toHaveOnlyValidCharacters,
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
});
|
|
46
|
+
//# sourceMappingURL=customMatchers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customMatchers.js","sourceRoot":"","sources":["../../../src/test-utils/customMatchers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wCAAwC;AAExC;;;;GAIG;AAEH,OAAO,EAAa,MAAM,EAAE,MAAM,QAAQ,CAAC;AAG3C,wEAAwE;AACxE,4CAA4C;AAC5C,MAAM,iBAAiB,GAAG,UAAU,CAAC;AAErC,SAAS,yBAAyB,CAAkB,MAAkB;IACpE,8DAA8D;IAC9D,MAAM,EAAE,KAAK,EAAE,GAAG,IAAW,CAAC;IAC9B,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,MAAM,YAAY,GAA6C,EAAE,CAAC;IAElE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,IAAI,GAAG,KAAK,CAAC;YACb,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9C,MAAM,CAAC,wBAAwB;QACjC,CAAC;QACD,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,IAAI,GAAG,KAAK,CAAC;YACb,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CACZ,mBAAmB,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,0EAA0E,YAAY;aACzH,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,2BAA2B;aACvE,IAAI,CAAC,IAAI,CAAC,EAAE;QACjB,MAAM,EAAE,MAAM,CAAC,KAAK;QACpB,QAAQ,EAAE,yDAAyD;KACpE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,CAAC;IACZ,yBAAyB;IACzB,8DAA8D;CACxD,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { CommandContext } from '../ui/commands/types.js';
|
|
7
|
+
type DeepPartial<T> = T extends object ? {
|
|
8
|
+
[P in keyof T]?: DeepPartial<T[P]>;
|
|
9
|
+
} : T;
|
|
10
|
+
/**
|
|
11
|
+
* Creates a deep, fully-typed mock of the CommandContext for use in tests.
|
|
12
|
+
* All functions are pre-mocked with `vi.fn()`.
|
|
13
|
+
*
|
|
14
|
+
* @param overrides - A deep partial object to override any default mock values.
|
|
15
|
+
* @returns A complete, mocked CommandContext object.
|
|
16
|
+
*/
|
|
17
|
+
export declare const createMockCommandContext: (overrides?: DeepPartial<CommandContext>) => CommandContext;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { vi } from 'vitest';
|
|
7
|
+
/**
|
|
8
|
+
* Creates a deep, fully-typed mock of the CommandContext for use in tests.
|
|
9
|
+
* All functions are pre-mocked with `vi.fn()`.
|
|
10
|
+
*
|
|
11
|
+
* @param overrides - A deep partial object to override any default mock values.
|
|
12
|
+
* @returns A complete, mocked CommandContext object.
|
|
13
|
+
*/
|
|
14
|
+
export const createMockCommandContext = (overrides = {}) => {
|
|
15
|
+
const defaultMocks = {
|
|
16
|
+
invocation: {
|
|
17
|
+
raw: '',
|
|
18
|
+
name: '',
|
|
19
|
+
args: '',
|
|
20
|
+
},
|
|
21
|
+
services: {
|
|
22
|
+
config: null,
|
|
23
|
+
settings: { merged: {} },
|
|
24
|
+
git: undefined,
|
|
25
|
+
logger: {
|
|
26
|
+
log: vi.fn(),
|
|
27
|
+
logMessage: vi.fn(),
|
|
28
|
+
saveCheckpoint: vi.fn(),
|
|
29
|
+
loadCheckpoint: vi.fn().mockResolvedValue([]),
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
}, // Cast because Logger is a class.
|
|
32
|
+
},
|
|
33
|
+
ui: {
|
|
34
|
+
addItem: vi.fn(),
|
|
35
|
+
clear: vi.fn(),
|
|
36
|
+
setDebugMessage: vi.fn(),
|
|
37
|
+
pendingItem: null,
|
|
38
|
+
setPendingItem: vi.fn(),
|
|
39
|
+
loadHistory: vi.fn(),
|
|
40
|
+
toggleCorgiMode: vi.fn(),
|
|
41
|
+
toggleVimEnabled: vi.fn(),
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
},
|
|
44
|
+
session: {
|
|
45
|
+
sessionShellAllowlist: new Set(),
|
|
46
|
+
stats: {
|
|
47
|
+
sessionStartTime: new Date(),
|
|
48
|
+
lastPromptTokenCount: 0,
|
|
49
|
+
metrics: {
|
|
50
|
+
models: {},
|
|
51
|
+
tools: {
|
|
52
|
+
totalCalls: 0,
|
|
53
|
+
totalSuccess: 0,
|
|
54
|
+
totalFail: 0,
|
|
55
|
+
totalDurationMs: 0,
|
|
56
|
+
totalDecisions: { accept: 0, reject: 0, modify: 0 },
|
|
57
|
+
byName: {},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
+
const merge = (target, source) => {
|
|
65
|
+
const output = { ...target };
|
|
66
|
+
for (const key in source) {
|
|
67
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
68
|
+
const sourceValue = source[key];
|
|
69
|
+
const targetValue = output[key];
|
|
70
|
+
if (
|
|
71
|
+
// We only want to recursivlty merge plain objects
|
|
72
|
+
Object.prototype.toString.call(sourceValue) === '[object Object]' &&
|
|
73
|
+
Object.prototype.toString.call(targetValue) === '[object Object]') {
|
|
74
|
+
output[key] = merge(targetValue, sourceValue);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// If not, we do a direct assignment. This preserves Date objects and others.
|
|
78
|
+
output[key] = sourceValue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return output;
|
|
83
|
+
};
|
|
84
|
+
return merge(defaultMocks, overrides);
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=mockCommandContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mockCommandContext.js","sourceRoot":"","sources":["../../../src/test-utils/mockCommandContext.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { vi, describe, it, expect } from 'vitest';
|
|
7
|
+
import { createMockCommandContext } from './mockCommandContext.js';
|
|
8
|
+
describe('createMockCommandContext', () => {
|
|
9
|
+
it('should return a valid CommandContext object with default mocks', () => {
|
|
10
|
+
const context = createMockCommandContext();
|
|
11
|
+
// Just a few spot checks to ensure the structure is correct
|
|
12
|
+
// and functions are mocks.
|
|
13
|
+
expect(context).toBeDefined();
|
|
14
|
+
expect(context.ui.addItem).toBeInstanceOf(Function);
|
|
15
|
+
expect(vi.isMockFunction(context.ui.addItem)).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
it('should apply top-level overrides correctly', () => {
|
|
18
|
+
const mockClear = vi.fn();
|
|
19
|
+
const overrides = {
|
|
20
|
+
ui: {
|
|
21
|
+
clear: mockClear,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
const context = createMockCommandContext(overrides);
|
|
25
|
+
// Call the function to see if the override was used
|
|
26
|
+
context.ui.clear();
|
|
27
|
+
// Assert that our specific mock was called, not the default
|
|
28
|
+
expect(mockClear).toHaveBeenCalled();
|
|
29
|
+
// And that other defaults are still in place
|
|
30
|
+
expect(vi.isMockFunction(context.ui.addItem)).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
it('should apply deeply nested overrides correctly', () => {
|
|
33
|
+
// This is the most important test for factory's logic.
|
|
34
|
+
const mockConfig = {
|
|
35
|
+
getProjectRoot: () => '/test/project',
|
|
36
|
+
getModel: () => 'gemini-pro',
|
|
37
|
+
};
|
|
38
|
+
const overrides = {
|
|
39
|
+
services: {
|
|
40
|
+
config: mockConfig,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
const context = createMockCommandContext(overrides);
|
|
44
|
+
expect(context.services.config).toBeDefined();
|
|
45
|
+
expect(context.services.config?.getModel()).toBe('gemini-pro');
|
|
46
|
+
expect(context.services.config?.getProjectRoot()).toBe('/test/project');
|
|
47
|
+
// Verify a default property on the same nested object is still there
|
|
48
|
+
expect(context.services.logger).toBeDefined();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=mockCommandContext.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mockCommandContext.test.js","sourceRoot":"","sources":["../../../src/test-utils/mockCommandContext.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAEnE,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,OAAO,GAAG,wBAAwB,EAAE,CAAC;QAE3C,4DAA4D;QAC5D,2BAA2B;QAC3B,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9B,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACpD,MAAM,CAAC,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1B,MAAM,SAAS,GAAG;YAChB,EAAE,EAAE;gBACF,KAAK,EAAE,SAAS;aACjB;SACF,CAAC;QAEF,MAAM,OAAO,GAAG,wBAAwB,CAAC,SAAS,CAAC,CAAC;QAEpD,oDAAoD;QACpD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;QAEnB,4DAA4D;QAC5D,MAAM,CAAC,SAAS,CAAC,CAAC,gBAAgB,EAAE,CAAC;QACrC,6CAA6C;QAC7C,MAAM,CAAC,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,uDAAuD;QACvD,MAAM,UAAU,GAAG;YACjB,cAAc,EAAE,GAAG,EAAE,CAAC,eAAe;YACrC,QAAQ,EAAE,GAAG,EAAE,CAAC,YAAY;SAC7B,CAAC;QAEF,MAAM,SAAS,GAAG;YAChB,QAAQ,EAAE;gBACR,MAAM,EAAE,UAAU;aACnB;SACF,CAAC;QAEF,MAAM,OAAO,GAAG,wBAAwB,CAAC,SAAS,CAAC,CAAC;QAEpD,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9C,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/D,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAExE,qEAAqE;QACrE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { render } from 'ink-testing-library';
|
|
7
|
+
import React from 'react';
|
|
8
|
+
export declare const renderWithProviders: (component: React.ReactElement) => ReturnType<typeof render>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2025 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
import { render } from 'ink-testing-library';
|
|
8
|
+
import { KeypressProvider } from '../ui/contexts/KeypressContext.js';
|
|
9
|
+
export const renderWithProviders = (component) => render(_jsx(KeypressProvider, { kittyProtocolEnabled: true, children: component }));
|
|
10
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/test-utils/render.tsx"],"names":[],"mappings":";AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAErE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,SAA6B,EACF,EAAE,CAC7B,MAAM,CACJ,KAAC,gBAAgB,IAAC,oBAAoB,EAAE,IAAI,YACzC,SAAS,GACO,CACpB,CAAC"}
|
package/dist/src/ui/App.js
CHANGED
|
@@ -54,6 +54,8 @@ import { useTextBuffer } from './components/shared/text-buffer.js';
|
|
|
54
54
|
import { useVimMode, VimModeProvider } from './contexts/VimModeContext.js';
|
|
55
55
|
import { useVim } from './hooks/vim.js';
|
|
56
56
|
import { useKeypress } from './hooks/useKeypress.js';
|
|
57
|
+
import { KeypressProvider } from './contexts/KeypressContext.js';
|
|
58
|
+
import { useKittyKeyboardProtocol } from './hooks/useKittyKeyboardProtocol.js';
|
|
57
59
|
import { keyMatchers, Command } from './keyMatchers.js';
|
|
58
60
|
import * as fs from 'fs';
|
|
59
61
|
import { UpdateNotification } from './components/UpdateNotification.js';
|
|
@@ -68,7 +70,10 @@ import { setUpdateHandler } from '../utils/handleAutoUpdate.js';
|
|
|
68
70
|
import { appEvents, AppEvent } from '../utils/events.js';
|
|
69
71
|
import { isNarrowWidth } from './utils/isNarrowWidth.js';
|
|
70
72
|
const CTRL_EXIT_PROMPT_DURATION_MS = 1000;
|
|
71
|
-
export const AppWrapper = (props) =>
|
|
73
|
+
export const AppWrapper = (props) => {
|
|
74
|
+
const kittyProtocolStatus = useKittyKeyboardProtocol();
|
|
75
|
+
return (_jsx(KeypressProvider, { kittyProtocolEnabled: kittyProtocolStatus.enabled, config: props.config, children: _jsx(SessionStatsProvider, { children: _jsx(VimModeProvider, { settings: props.settings, children: _jsx(App, { ...props }) }) }) }));
|
|
76
|
+
};
|
|
72
77
|
const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
73
78
|
const isFocused = useFocus();
|
|
74
79
|
useBracketedPaste();
|
|
@@ -112,6 +117,7 @@ const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
|
112
117
|
const [editorError, setEditorError] = useState(null);
|
|
113
118
|
const [footerHeight, setFooterHeight] = useState(0);
|
|
114
119
|
const [corgiMode, setCorgiMode] = useState(false);
|
|
120
|
+
const [isTrustedFolderState, setIsTrustedFolder] = useState(config.isTrustedFolder());
|
|
115
121
|
const [currentModel, setCurrentModel] = useState(config.getModel());
|
|
116
122
|
const [shellModeActive, setShellModeActive] = useState(false);
|
|
117
123
|
const [showErrorDetails, setShowErrorDetails] = useState(false);
|
|
@@ -165,7 +171,7 @@ const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
|
165
171
|
.reduce((total, msg) => total + msg.count, 0), [consoleMessages]);
|
|
166
172
|
const { isThemeDialogOpen, openThemeDialog, handleThemeSelect, handleThemeHighlight, } = useThemeCommand(settings, setThemeError, addItem);
|
|
167
173
|
const { isSettingsDialogOpen, openSettingsDialog, closeSettingsDialog } = useSettingsCommand();
|
|
168
|
-
const { isFolderTrustDialogOpen, handleFolderTrustSelect } = useFolderTrust(settings);
|
|
174
|
+
const { isFolderTrustDialogOpen, handleFolderTrustSelect } = useFolderTrust(settings, setIsTrustedFolder);
|
|
169
175
|
const { isAuthDialogOpen, openAuthDialog, handleAuthSelect, isAuthenticating, cancelAuthentication, } = useAuthCommand(settings, setAuthError, config);
|
|
170
176
|
useEffect(() => {
|
|
171
177
|
if (settings.merged.selectedAuthType && !settings.merged.useExternalAuth) {
|
|
@@ -461,7 +467,9 @@ const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
|
461
467
|
isAuthenticating,
|
|
462
468
|
cancelOngoingRequest,
|
|
463
469
|
]);
|
|
464
|
-
useKeypress(handleGlobalKeypress, {
|
|
470
|
+
useKeypress(handleGlobalKeypress, {
|
|
471
|
+
isActive: true,
|
|
472
|
+
});
|
|
465
473
|
useEffect(() => {
|
|
466
474
|
if (config) {
|
|
467
475
|
setGeminiMdFileCount(config.getGeminiMdFileCount());
|
|
@@ -590,7 +598,7 @@ const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
|
590
598
|
], children: (item) => item }, staticKey), _jsx(OverflowProvider, { children: _jsxs(Box, { ref: pendingHistoryItemRef, flexDirection: "column", children: [pendingHistoryItems.map((item, i) => (_jsx(HistoryItemDisplay, { availableTerminalHeight: constrainHeight ? availableTerminalHeight : undefined, terminalWidth: mainAreaWidth,
|
|
591
599
|
// TODO(taehykim): It seems like references to ids aren't necessary in
|
|
592
600
|
// HistoryItemDisplay. Refactor later. Use a fake id for now.
|
|
593
|
-
item: { ...item, id: 0 }, isPending: true, config: config, isFocused: !isEditorDialogOpen }, i))), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) }), _jsxs(Box, { flexDirection: "column", ref: mainControlsRef, children: [updateInfo && _jsx(UpdateNotification, { message: updateInfo.message }), startupWarnings.length > 0 && (_jsx(Box, { borderStyle: "round", borderColor: Colors.AccentYellow, paddingX: 1, marginY: 1, flexDirection: "column", children: startupWarnings.map((warning, index) => (_jsx(Text, { color: Colors.AccentYellow, children: warning }, index))) })), shouldShowIdePrompt && currentIDE ? (_jsx(IdeIntegrationNudge, { ide: currentIDE, onComplete: handleIdePromptComplete })) : isFolderTrustDialogOpen ? (_jsx(FolderTrustDialog, { onSelect: handleFolderTrustSelect })) : shellConfirmationRequest ? (_jsx(ShellConfirmationDialog, { request: shellConfirmationRequest })) : confirmationRequest ? (_jsxs(Box, { flexDirection: "column", children: [confirmationRequest.prompt, _jsx(Box, { paddingY: 1, children: _jsx(RadioButtonSelect, { items: [
|
|
601
|
+
item: { ...item, id: 0 }, isPending: true, config: config, isFocused: !isEditorDialogOpen }, i))), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) }), _jsxs(Box, { flexDirection: "column", ref: mainControlsRef, children: [updateInfo && _jsx(UpdateNotification, { message: updateInfo.message }), startupWarnings.length > 0 && (_jsx(Box, { borderStyle: "round", borderColor: Colors.AccentYellow, paddingX: 1, marginY: 1, flexDirection: "column", children: startupWarnings.map((warning, index) => (_jsx(Text, { color: Colors.AccentYellow, children: warning }, index))) })), shouldShowIdePrompt && currentIDE ? (_jsx(IdeIntegrationNudge, { ide: currentIDE, onComplete: handleIdePromptComplete })) : isFolderTrustDialogOpen ? (_jsx(FolderTrustDialog, { onSelect: handleFolderTrustSelect })) : shellConfirmationRequest ? (_jsx(ShellConfirmationDialog, { request: shellConfirmationRequest })) : confirmationRequest ? (_jsxs(Box, { flexDirection: "column", children: [confirmationRequest.prompt, _jsx(Box, { paddingY: 1, children: _jsx(RadioButtonSelect, { isFocused: !!confirmationRequest, items: [
|
|
594
602
|
{ label: 'Yes', value: true },
|
|
595
603
|
{ label: 'No', value: false },
|
|
596
604
|
], onSelect: (value) => {
|
|
@@ -606,7 +614,7 @@ const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
|
606
614
|
? undefined
|
|
607
615
|
: thought, currentLoadingPhrase: config.getAccessibility()?.disableLoadingPhrases
|
|
608
616
|
? undefined
|
|
609
|
-
: currentLoadingPhrase, elapsedTime: elapsedTime }), _jsxs(Box, { marginTop: 1, justifyContent: "space-between", width: "100%", flexDirection: isNarrow ? 'column' : 'row', alignItems: isNarrow ? 'flex-start' : 'center', children: [_jsxs(Box, { children: [process.env
|
|
610
|
-
!shellModeActive && (_jsx(AutoAcceptIndicator, { approvalMode: showAutoAcceptIndicator })), shellModeActive && _jsx(ShellModeIndicator, {})] })] }), showErrorDetails && (_jsx(OverflowProvider, { children: _jsxs(Box, { flexDirection: "column", children: [_jsx(DetailedMessagesDisplay, { messages: filteredConsoleMessages, maxHeight: constrainHeight ? debugConsoleMaxHeight : undefined, width: inputWidth }), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) })), isInputActive && (_jsx(InputPrompt, { buffer: buffer, inputWidth: inputWidth, suggestionsWidth: suggestionsWidth, onSubmit: handleFinalSubmit, userMessages: userMessages, onClearScreen: handleClearScreen, config: config, slashCommands: slashCommands, commandContext: commandContext, shellModeActive: shellModeActive, setShellModeActive: setShellModeActive, onEscapePromptChange: handleEscapePromptChange, focus: isFocused, vimHandleInput: vimHandleInput, placeholder: placeholder }))] })), initError && streamingState !== StreamingState.Responding && (_jsx(Box, { borderStyle: "round", borderColor: Colors.AccentRed, paddingX: 1, marginBottom: 1, children: history.find((item) => item.type === 'error' && item.text?.includes(initError))?.text ? (_jsx(Text, { color: Colors.AccentRed, children: history.find((item) => item.type === 'error' && item.text?.includes(initError))?.text })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { color: Colors.AccentRed, children: ["Initialization Error: ", initError] }), _jsxs(Text, { color: Colors.AccentRed, children: [' ', "Please check API key and configuration."] })] })) })), _jsx(Footer, { model: currentModel, targetDir: config.getTargetDir(), debugMode: config.getDebugMode(), branchName: branchName, debugMessage: debugMessage, corgiMode: corgiMode, errorCount: errorCount, showErrorDetails: showErrorDetails, showMemoryUsage: config.getDebugMode() || settings.merged.showMemoryUsage || false, promptTokenCount: sessionStats.lastPromptTokenCount, nightly: nightly, vimMode: vimModeEnabled ? vimMode : undefined })] })] }) }));
|
|
617
|
+
: currentLoadingPhrase, elapsedTime: elapsedTime }), _jsxs(Box, { marginTop: 1, justifyContent: "space-between", width: "100%", flexDirection: isNarrow ? 'column' : 'row', alignItems: isNarrow ? 'flex-start' : 'center', children: [_jsxs(Box, { children: [process.env['GEMINI_SYSTEM_MD'] && (_jsx(Text, { color: Colors.AccentRed, children: "|\u2310\u25A0_\u25A0| " })), ctrlCPressedOnce ? (_jsx(Text, { color: Colors.AccentYellow, children: "Press Ctrl+C again to exit." })) : ctrlDPressedOnce ? (_jsx(Text, { color: Colors.AccentYellow, children: "Press Ctrl+D again to exit." })) : showEscapePrompt ? (_jsx(Text, { color: Colors.Gray, children: "Press Esc again to clear." })) : (_jsx(ContextSummaryDisplay, { ideContext: ideContextState, geminiMdFileCount: geminiMdFileCount, contextFileNames: contextFileNames, mcpServers: config.getMcpServers(), blockedMcpServers: config.getBlockedMcpServers(), showToolDescriptions: showToolDescriptions }))] }), _jsxs(Box, { paddingTop: isNarrow ? 1 : 0, children: [showAutoAcceptIndicator !== ApprovalMode.DEFAULT &&
|
|
618
|
+
!shellModeActive && (_jsx(AutoAcceptIndicator, { approvalMode: showAutoAcceptIndicator })), shellModeActive && _jsx(ShellModeIndicator, {})] })] }), showErrorDetails && (_jsx(OverflowProvider, { children: _jsxs(Box, { flexDirection: "column", children: [_jsx(DetailedMessagesDisplay, { messages: filteredConsoleMessages, maxHeight: constrainHeight ? debugConsoleMaxHeight : undefined, width: inputWidth }), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) })), isInputActive && (_jsx(InputPrompt, { buffer: buffer, inputWidth: inputWidth, suggestionsWidth: suggestionsWidth, onSubmit: handleFinalSubmit, userMessages: userMessages, onClearScreen: handleClearScreen, config: config, slashCommands: slashCommands, commandContext: commandContext, shellModeActive: shellModeActive, setShellModeActive: setShellModeActive, onEscapePromptChange: handleEscapePromptChange, focus: isFocused, vimHandleInput: vimHandleInput, placeholder: placeholder }))] })), initError && streamingState !== StreamingState.Responding && (_jsx(Box, { borderStyle: "round", borderColor: Colors.AccentRed, paddingX: 1, marginBottom: 1, children: history.find((item) => item.type === 'error' && item.text?.includes(initError))?.text ? (_jsx(Text, { color: Colors.AccentRed, children: history.find((item) => item.type === 'error' && item.text?.includes(initError))?.text })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { color: Colors.AccentRed, children: ["Initialization Error: ", initError] }), _jsxs(Text, { color: Colors.AccentRed, children: [' ', "Please check API key and configuration."] })] })) })), _jsx(Footer, { model: currentModel, targetDir: config.getTargetDir(), debugMode: config.getDebugMode(), branchName: branchName, debugMessage: debugMessage, corgiMode: corgiMode, errorCount: errorCount, showErrorDetails: showErrorDetails, showMemoryUsage: config.getDebugMode() || settings.merged.showMemoryUsage || false, promptTokenCount: sessionStats.lastPromptTokenCount, nightly: nightly, vimMode: vimModeEnabled ? vimMode : undefined, isTrustedFolder: isTrustedFolderState })] })] }) }));
|
|
611
619
|
};
|
|
612
620
|
//# sourceMappingURL=App.js.map
|