diff --git a/packages/cli/src/ui/components/InputPrompt.test.tsx b/packages/cli/src/ui/components/InputPrompt.test.tsx index c52ebf7d3c..50311fc508 100644 --- a/packages/cli/src/ui/components/InputPrompt.test.tsx +++ b/packages/cli/src/ui/components/InputPrompt.test.tsx @@ -789,6 +789,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -816,6 +817,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -843,6 +845,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -870,6 +873,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -897,6 +901,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -925,6 +930,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -952,6 +958,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -980,6 +987,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -1008,6 +1016,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -1036,6 +1045,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -1064,6 +1074,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -1094,6 +1105,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -1122,6 +1134,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -1152,6 +1165,7 @@ describe('InputPrompt', () => { mockSlashCommands, mockCommandContext, false, + false, expect.any(Object), ); @@ -1161,7 +1175,6 @@ describe('InputPrompt', () => { describe('vim mode', () => { it('should not call buffer.handleInput when vim mode is enabled and vim handles the input', async () => { - props.vimModeEnabled = true; props.vimHandleInput = vi.fn().mockReturnValue(true); // Mock that vim handled it. const { stdin, unmount } = renderWithProviders( , @@ -1177,7 +1190,6 @@ describe('InputPrompt', () => { }); it('should call buffer.handleInput when vim mode is enabled but vim does not handle the input', async () => { - props.vimModeEnabled = true; props.vimHandleInput = vi.fn().mockReturnValue(false); // Mock that vim did NOT handle it. const { stdin, unmount } = renderWithProviders( , diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx index 35ee31e1db..10cf694b7e 100644 --- a/packages/cli/src/ui/components/InputPrompt.tsx +++ b/packages/cli/src/ui/components/InputPrompt.tsx @@ -155,6 +155,7 @@ export const InputPrompt: React.FC = ({ slashCommands, commandContext, reverseSearchActive, + shellModeActive, config, ); diff --git a/packages/cli/src/ui/hooks/useCommandCompletion.test.ts b/packages/cli/src/ui/hooks/useCommandCompletion.test.ts index f9fbeedc3d..a900d0bbac 100644 --- a/packages/cli/src/ui/hooks/useCommandCompletion.test.ts +++ b/packages/cli/src/ui/hooks/useCommandCompletion.test.ts @@ -121,6 +121,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ), ); @@ -146,6 +147,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ); return { completion, textBuffer }; @@ -179,6 +181,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ), ); @@ -207,6 +210,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ), ); @@ -233,6 +237,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ), ); @@ -246,6 +251,56 @@ describe('useCommandCompletion', () => { ); }); }); + + it.each([ + { + shellModeActive: false, + expectedSuggestions: 1, + expectedShowSuggestions: true, + description: + 'should show slash command suggestions when shellModeActive is false', + }, + { + shellModeActive: true, + expectedSuggestions: 0, + expectedShowSuggestions: false, + description: + 'should not show slash command suggestions when shellModeActive is true', + }, + ])( + '$description', + async ({ + shellModeActive, + expectedSuggestions, + expectedShowSuggestions, + }) => { + setupMocks({ + slashSuggestions: [{ label: 'clear', value: 'clear' }], + }); + + const { result } = renderHook(() => { + const textBuffer = useTextBufferForTest('/'); + const completion = useCommandCompletion( + textBuffer, + testDirs, + testRootDir, + [], + mockCommandContext, + false, + shellModeActive, // Parameterized shellModeActive + mockConfig, + ); + return { ...completion, textBuffer }; + }); + + await waitFor(() => { + expect(result.current.suggestions.length).toBe(expectedSuggestions); + expect(result.current.showSuggestions).toBe( + expectedShowSuggestions, + ); + }); + }, + ); }); describe('Navigation', () => { @@ -272,6 +327,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ), ); @@ -293,6 +349,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ), ); @@ -313,6 +370,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ), ); @@ -339,6 +397,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ), ); @@ -368,6 +427,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ), ); @@ -405,6 +465,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ), ); @@ -435,6 +496,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ); return { ...completion, textBuffer }; @@ -465,6 +527,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ); return { ...completion, textBuffer }; @@ -498,6 +561,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ); return { ...completion, textBuffer }; @@ -530,6 +594,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ); return { ...completion, textBuffer }; @@ -562,6 +627,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ); return { ...completion, textBuffer }; @@ -594,6 +660,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ); return { ...completion, textBuffer }; @@ -619,6 +686,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ); return { ...completion, textBuffer }; @@ -644,6 +712,7 @@ describe('useCommandCompletion', () => { [], mockCommandContext, false, + false, mockConfig, ); return { ...completion, textBuffer }; diff --git a/packages/cli/src/ui/hooks/useCommandCompletion.tsx b/packages/cli/src/ui/hooks/useCommandCompletion.tsx index b55eb89724..188c340011 100644 --- a/packages/cli/src/ui/hooks/useCommandCompletion.tsx +++ b/packages/cli/src/ui/hooks/useCommandCompletion.tsx @@ -51,6 +51,7 @@ export function useCommandCompletion( slashCommands: readonly SlashCommand[], commandContext: CommandContext, reverseSearchActive: boolean = false, + shellModeActive: boolean, config?: Config, ): UseCommandCompletionReturn { const { @@ -163,7 +164,7 @@ export function useCommandCompletion( }); const slashCompletionRange = useSlashCompletion({ - enabled: completionMode === CompletionMode.SLASH, + enabled: completionMode === CompletionMode.SLASH && !shellModeActive, query, slashCommands, commandContext, diff --git a/packages/cli/src/ui/hooks/useGeminiStream.test.tsx b/packages/cli/src/ui/hooks/useGeminiStream.test.tsx index 7d26187300..ffce1348bc 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.test.tsx +++ b/packages/cli/src/ui/hooks/useGeminiStream.test.tsx @@ -1207,6 +1207,39 @@ describe('useGeminiStream', () => { ); }); }); + + it('should not call handleSlashCommand is shell mode is active', async () => { + const { result } = renderHook(() => + useGeminiStream( + new MockedGeminiClientClass(mockConfig), + [], + mockAddItem, + mockConfig, + mockLoadedSettings, + () => {}, + mockHandleSlashCommand, + true, + () => 'vscode' as EditorType, + () => {}, + () => Promise.resolve(), + false, + () => {}, + () => {}, + () => {}, + () => {}, + 80, + 24, + ), + ); + + await act(async () => { + await result.current.submitQuery('/about'); + }); + + await waitFor(() => { + expect(mockHandleSlashCommand).not.toHaveBeenCalled(); + }); + }); }); describe('Memory Refresh on save_memory', () => { diff --git a/packages/cli/src/ui/hooks/useGeminiStream.ts b/packages/cli/src/ui/hooks/useGeminiStream.ts index 5ecd615c0c..e391f6c5da 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.ts +++ b/packages/cli/src/ui/hooks/useGeminiStream.ts @@ -327,41 +327,43 @@ export const useGeminiStream = ( onDebugMessage(`User query: '${trimmedQuery}'`); await logger?.logMessage(MessageSenderType.USER, trimmedQuery); - // Handle UI-only commands first - const slashCommandResult = isSlashCommand(trimmedQuery) - ? await handleSlashCommand(trimmedQuery) - : false; + if (!shellModeActive) { + // Handle UI-only commands first + const slashCommandResult = isSlashCommand(trimmedQuery) + ? await handleSlashCommand(trimmedQuery) + : false; - if (slashCommandResult) { - switch (slashCommandResult.type) { - case 'schedule_tool': { - const { toolName, toolArgs } = slashCommandResult; - const toolCallRequest: ToolCallRequestInfo = { - callId: `${toolName}-${Date.now()}-${Math.random().toString(16).slice(2)}`, - name: toolName, - args: toolArgs, - isClientInitiated: true, - prompt_id, - }; - scheduleToolCalls([toolCallRequest], abortSignal); - return { queryToSend: null, shouldProceed: false }; - } - case 'submit_prompt': { - localQueryToSendToGemini = slashCommandResult.content; + if (slashCommandResult) { + switch (slashCommandResult.type) { + case 'schedule_tool': { + const { toolName, toolArgs } = slashCommandResult; + const toolCallRequest: ToolCallRequestInfo = { + callId: `${toolName}-${Date.now()}-${Math.random().toString(16).slice(2)}`, + name: toolName, + args: toolArgs, + isClientInitiated: true, + prompt_id, + }; + scheduleToolCalls([toolCallRequest], abortSignal); + return { queryToSend: null, shouldProceed: false }; + } + case 'submit_prompt': { + localQueryToSendToGemini = slashCommandResult.content; - return { - queryToSend: localQueryToSendToGemini, - shouldProceed: true, - }; - } - case 'handled': { - return { queryToSend: null, shouldProceed: false }; - } - default: { - const unreachable: never = slashCommandResult; - throw new Error( - `Unhandled slash command result type: ${unreachable}`, - ); + return { + queryToSend: localQueryToSendToGemini, + shouldProceed: true, + }; + } + case 'handled': { + return { queryToSend: null, shouldProceed: false }; + } + default: { + const unreachable: never = slashCommandResult; + throw new Error( + `Unhandled slash command result type: ${unreachable}`, + ); + } } } }