diff --git a/packages/cli/src/ui/hooks/useCommandCompletion.test.ts b/packages/cli/src/ui/hooks/useCommandCompletion.test.ts index 659b99db0d9..8f74457bc07 100644 --- a/packages/cli/src/ui/hooks/useCommandCompletion.test.ts +++ b/packages/cli/src/ui/hooks/useCommandCompletion.test.ts @@ -244,6 +244,33 @@ describe('useCommandCompletion', () => { ); }); }); + + it('should enable AT completion when @ appears after a slash command', async () => { + // Regression test for #2518: typing @ after a custom slash command + // should trigger file search, not remain in SLASH mode. + const text = '/qc:create-issue @readme'; + + renderHook(() => + useCommandCompletion( + useTextBufferForTest(text), + testDirs, + testRootDir, + [], + mockCommandContext, + false, + mockConfig, + ), + ); + + await waitFor(() => { + expect(useAtCompletion).toHaveBeenLastCalledWith( + expect.objectContaining({ + enabled: true, + pattern: 'readme', + }), + ); + }); + }); }); describe('Navigation', () => { diff --git a/packages/cli/src/ui/hooks/useCommandCompletion.tsx b/packages/cli/src/ui/hooks/useCommandCompletion.tsx index cb5d9f276b2..4f263e79440 100644 --- a/packages/cli/src/ui/hooks/useCommandCompletion.tsx +++ b/packages/cli/src/ui/hooks/useCommandCompletion.tsx @@ -74,15 +74,10 @@ export function useCommandCompletion( const { completionMode, query, completionStart, completionEnd } = useMemo(() => { const currentLine = buffer.lines[cursorRow] || ''; - if (cursorRow === 0 && isSlashCommand(currentLine.trim())) { - return { - completionMode: CompletionMode.SLASH, - query: currentLine, - completionStart: 0, - completionEnd: currentLine.length, - }; - } + // Check for @ file reference first — it takes priority over slash + // command completion so that typing `@` after a slash command on the + // same line still triggers file search (see #2518). const codePoints = toCodePoints(currentLine); for (let i = cursorCol - 1; i >= 0; i--) { const char = codePoints[i]; @@ -121,6 +116,15 @@ export function useCommandCompletion( } } + if (cursorRow === 0 && isSlashCommand(currentLine.trim())) { + return { + completionMode: CompletionMode.SLASH, + query: currentLine, + completionStart: 0, + completionEnd: currentLine.length, + }; + } + return { completionMode: CompletionMode.IDLE, query: null,