@google/gemini-cli 0.1.21 → 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 +26 -28
- package/dist/package.json +2 -2
- 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 +1 -0
- package/dist/src/config/config.js +18 -11
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/keyBindings.js +3 -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 +2 -1
- package/dist/src/config/trustedFolders.js +7 -1
- package/dist/src/config/trustedFolders.js.map +1 -1
- 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 +17 -7
- 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 -3
- package/dist/src/nonInteractiveCli.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 +10 -8
- 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/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/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/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 +0 -4
- 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/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 +7 -7
- 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/useFolderTrust.d.ts +2 -2
- package/dist/src/ui/hooks/useFolderTrust.js +23 -5
- package/dist/src/ui/hooks/useFolderTrust.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 -24
- package/dist/src/ui/hooks/useKeypress.js +9 -330
- package/dist/src/ui/hooks/useKeypress.js.map +1 -1
- 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/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 +5 -0
- package/dist/src/ui/utils/platformConstants.js +5 -0
- package/dist/src/ui/utils/platformConstants.js.map +1 -1
- package/dist/src/ui/utils/terminalSetup.js +7 -7
- package/dist/src/ui/utils/terminalSetup.js.map +1 -1
- 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/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/schema.d.ts +322 -322
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -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":"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"}
|
|
@@ -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,7 @@ 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';
|
|
57
58
|
import { useKittyKeyboardProtocol } from './hooks/useKittyKeyboardProtocol.js';
|
|
58
59
|
import { keyMatchers, Command } from './keyMatchers.js';
|
|
59
60
|
import * as fs from 'fs';
|
|
@@ -69,7 +70,10 @@ import { setUpdateHandler } from '../utils/handleAutoUpdate.js';
|
|
|
69
70
|
import { appEvents, AppEvent } from '../utils/events.js';
|
|
70
71
|
import { isNarrowWidth } from './utils/isNarrowWidth.js';
|
|
71
72
|
const CTRL_EXIT_PROMPT_DURATION_MS = 1000;
|
|
72
|
-
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
|
+
};
|
|
73
77
|
const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
74
78
|
const isFocused = useFocus();
|
|
75
79
|
useBracketedPaste();
|
|
@@ -113,6 +117,7 @@ const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
|
113
117
|
const [editorError, setEditorError] = useState(null);
|
|
114
118
|
const [footerHeight, setFooterHeight] = useState(0);
|
|
115
119
|
const [corgiMode, setCorgiMode] = useState(false);
|
|
120
|
+
const [isTrustedFolderState, setIsTrustedFolder] = useState(config.isTrustedFolder());
|
|
116
121
|
const [currentModel, setCurrentModel] = useState(config.getModel());
|
|
117
122
|
const [shellModeActive, setShellModeActive] = useState(false);
|
|
118
123
|
const [showErrorDetails, setShowErrorDetails] = useState(false);
|
|
@@ -166,7 +171,7 @@ const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
|
166
171
|
.reduce((total, msg) => total + msg.count, 0), [consoleMessages]);
|
|
167
172
|
const { isThemeDialogOpen, openThemeDialog, handleThemeSelect, handleThemeHighlight, } = useThemeCommand(settings, setThemeError, addItem);
|
|
168
173
|
const { isSettingsDialogOpen, openSettingsDialog, closeSettingsDialog } = useSettingsCommand();
|
|
169
|
-
const { isFolderTrustDialogOpen, handleFolderTrustSelect } = useFolderTrust(settings,
|
|
174
|
+
const { isFolderTrustDialogOpen, handleFolderTrustSelect } = useFolderTrust(settings, setIsTrustedFolder);
|
|
170
175
|
const { isAuthDialogOpen, openAuthDialog, handleAuthSelect, isAuthenticating, cancelAuthentication, } = useAuthCommand(settings, setAuthError, config);
|
|
171
176
|
useEffect(() => {
|
|
172
177
|
if (settings.merged.selectedAuthType && !settings.merged.useExternalAuth) {
|
|
@@ -383,7 +388,6 @@ const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
|
383
388
|
pendingHistoryItems.push(...pendingGeminiHistoryItems);
|
|
384
389
|
const { elapsedTime, currentLoadingPhrase } = useLoadingIndicator(streamingState);
|
|
385
390
|
const showAutoAcceptIndicator = useAutoAcceptIndicator({ config });
|
|
386
|
-
const kittyProtocolStatus = useKittyKeyboardProtocol();
|
|
387
391
|
const handleExit = useCallback((pressedOnce, setPressedOnce, timerRef) => {
|
|
388
392
|
if (pressedOnce) {
|
|
389
393
|
if (timerRef.current) {
|
|
@@ -465,8 +469,6 @@ const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
|
465
469
|
]);
|
|
466
470
|
useKeypress(handleGlobalKeypress, {
|
|
467
471
|
isActive: true,
|
|
468
|
-
kittyProtocolEnabled: kittyProtocolStatus.enabled,
|
|
469
|
-
config,
|
|
470
472
|
});
|
|
471
473
|
useEffect(() => {
|
|
472
474
|
if (config) {
|
|
@@ -596,7 +598,7 @@ const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
|
596
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,
|
|
597
599
|
// TODO(taehykim): It seems like references to ids aren't necessary in
|
|
598
600
|
// HistoryItemDisplay. Refactor later. Use a fake id for now.
|
|
599
|
-
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: [
|
|
600
602
|
{ label: 'Yes', value: true },
|
|
601
603
|
{ label: 'No', value: false },
|
|
602
604
|
], onSelect: (value) => {
|
|
@@ -612,7 +614,7 @@ const App = ({ config, settings, startupWarnings = [], version }) => {
|
|
|
612
614
|
? undefined
|
|
613
615
|
: thought, currentLoadingPhrase: config.getAccessibility()?.disableLoadingPhrases
|
|
614
616
|
? undefined
|
|
615
|
-
: 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
|
|
616
|
-
!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 })] })] }) }));
|
|
617
619
|
};
|
|
618
620
|
//# sourceMappingURL=App.js.map
|