From b2e866585d4ae4545351afbd831f6fd713a8a633 Mon Sep 17 00:00:00 2001 From: Gal Zahavi <38544478+galz10@users.noreply.github.com> Date: Tue, 13 Jan 2026 14:01:30 -0800 Subject: [PATCH] fix(cli): allow @ file selector on slash command lines (#16370) --- .../cli/src/ui/components/InputPrompt.test.tsx | 6 +++++- packages/cli/src/ui/components/InputPrompt.tsx | 17 +++++++++++------ .../src/ui/hooks/useCommandCompletion.test.tsx | 12 ++++++++++-- .../cli/src/ui/hooks/useCommandCompletion.tsx | 2 ++ 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/ui/components/InputPrompt.test.tsx b/packages/cli/src/ui/components/InputPrompt.test.tsx index ee194dc3cb..68b044071d 100644 --- a/packages/cli/src/ui/components/InputPrompt.test.tsx +++ b/packages/cli/src/ui/components/InputPrompt.test.tsx @@ -26,7 +26,10 @@ import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'; import type { UseShellHistoryReturn } from '../hooks/useShellHistory.js'; import { useShellHistory } from '../hooks/useShellHistory.js'; import type { UseCommandCompletionReturn } from '../hooks/useCommandCompletion.js'; -import { useCommandCompletion } from '../hooks/useCommandCompletion.js'; +import { + useCommandCompletion, + CompletionMode, +} from '../hooks/useCommandCompletion.js'; import type { UseInputHistoryReturn } from '../hooks/useInputHistory.js'; import { useInputHistory } from '../hooks/useInputHistory.js'; import type { UseReverseSearchCompletionReturn } from '../hooks/useReverseSearchCompletion.js'; @@ -214,6 +217,7 @@ describe('InputPrompt', () => { leafCommand: null, }, getCompletedText: vi.fn().mockReturnValue(null), + completionMode: CompletionMode.IDLE, }; mockedUseCommandCompletion.mockReturnValue(mockCommandCompletion); diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx index 2ed22900b2..20d84ca650 100644 --- a/packages/cli/src/ui/components/InputPrompt.tsx +++ b/packages/cli/src/ui/components/InputPrompt.tsx @@ -18,7 +18,10 @@ import chalk from 'chalk'; import stringWidth from 'string-width'; import { useShellHistory } from '../hooks/useShellHistory.js'; import { useReverseSearchCompletion } from '../hooks/useReverseSearchCompletion.js'; -import { useCommandCompletion } from '../hooks/useCommandCompletion.js'; +import { + useCommandCompletion, + CompletionMode, +} from '../hooks/useCommandCompletion.js'; import type { Key } from '../hooks/useKeypress.js'; import { useKeypress } from '../hooks/useKeypress.js'; import { keyMatchers, Command } from '../keyMatchers.js'; @@ -1045,11 +1048,13 @@ export const InputPrompt: React.FC = ({ scrollOffset={activeCompletion.visibleStartIndex} userInput={buffer.text} mode={ - buffer.text.startsWith('/') && - !reverseSearchActive && - !commandSearchActive - ? 'slash' - : 'reverse' + completion.completionMode === CompletionMode.AT + ? 'reverse' + : buffer.text.startsWith('/') && + !reverseSearchActive && + !commandSearchActive + ? 'slash' + : 'reverse' } expandedIndex={expandedSuggestionIndex} /> diff --git a/packages/cli/src/ui/hooks/useCommandCompletion.test.tsx b/packages/cli/src/ui/hooks/useCommandCompletion.test.tsx index 1679782707..e023de786f 100644 --- a/packages/cli/src/ui/hooks/useCommandCompletion.test.tsx +++ b/packages/cli/src/ui/hooks/useCommandCompletion.test.tsx @@ -16,7 +16,10 @@ import { import { act, useEffect } from 'react'; import { renderWithProviders } from '../../test-utils/render.js'; import { waitFor } from '../../test-utils/async.js'; -import { useCommandCompletion } from './useCommandCompletion.js'; +import { + useCommandCompletion, + CompletionMode, +} from './useCommandCompletion.js'; import type { CommandContext } from '../commands/types.js'; import type { Config } from '@google/gemini-cli-core'; import { useTextBuffer } from '../components/shared/text-buffer.js'; @@ -160,6 +163,7 @@ describe('useCommandCompletion', () => { expect(result.current.visibleStartIndex).toBe(0); expect(result.current.showSuggestions).toBe(false); expect(result.current.isLoadingSuggestions).toBe(false); + expect(result.current.completionMode).toBe(CompletionMode.IDLE); }); it('should reset state when completion mode becomes IDLE', async () => { @@ -207,7 +211,7 @@ describe('useCommandCompletion', () => { it('should call useAtCompletion with the correct query for an escaped space', async () => { const text = '@src/a\\ file.txt'; - renderCommandCompletionHook(text); + const { result } = renderCommandCompletionHook(text); await waitFor(() => { expect(useAtCompletion).toHaveBeenLastCalledWith( @@ -216,6 +220,7 @@ describe('useCommandCompletion', () => { pattern: 'src/a\\ file.txt', }), ); + expect(result.current.completionMode).toBe(CompletionMode.AT); }); }); @@ -272,6 +277,9 @@ describe('useCommandCompletion', () => { expect(result.current.showSuggestions).toBe( expectedShowSuggestions, ); + if (!shellModeActive) { + expect(result.current.completionMode).toBe(CompletionMode.SLASH); + } }); }, ); diff --git a/packages/cli/src/ui/hooks/useCommandCompletion.tsx b/packages/cli/src/ui/hooks/useCommandCompletion.tsx index b6c4991648..b5f3264ee7 100644 --- a/packages/cli/src/ui/hooks/useCommandCompletion.tsx +++ b/packages/cli/src/ui/hooks/useCommandCompletion.tsx @@ -55,6 +55,7 @@ export interface UseCommandCompletionReturn { leafCommand: SlashCommand | null; }; getCompletedText: (suggestion: Suggestion) => string | null; + completionMode: CompletionMode; } export function useCommandCompletion( @@ -341,5 +342,6 @@ export function useCommandCompletion( getCommandFromSuggestion: slashCompletionRange.getCommandFromSuggestion, slashCompletionRange, getCompletedText, + completionMode, }; }