Skip to content

Commit 6cd2576

Browse files
committed
disable suggestions in shell mode
1 parent b421d21 commit 6cd2576

File tree

2 files changed

+173
-1
lines changed

2 files changed

+173
-1
lines changed

cli/src/state/atoms/__tests__/shell.test.ts

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,165 @@ describe("shell mode - comprehensive tests", () => {
471471
})
472472
})
473473

474+
describe("@ tag (file mention) handling in shell mode", () => {
475+
it("should not trigger file mention autocomplete when typing @ in shell mode", () => {
476+
// Enter shell mode
477+
store.set(toggleShellModeAtom)
478+
expect(store.get(shellModeActiveAtom)).toBe(true)
479+
480+
// Type text with @ symbol
481+
store.set(setTextAtom, "git commit -m 'fix @username'")
482+
483+
// Verify shell mode is still active
484+
expect(store.get(shellModeActiveAtom)).toBe(true)
485+
expect(store.get(inputModeAtom)).toBe("shell")
486+
487+
// The @ should be treated as regular text, not triggering file mentions
488+
expect(store.get(textBufferStringAtom)).toBe("git commit -m 'fix @username'")
489+
})
490+
491+
it("should allow @ symbols in email addresses in shell mode", () => {
492+
// Enter shell mode
493+
store.set(toggleShellModeAtom)
494+
expect(store.get(shellModeActiveAtom)).toBe(true)
495+
496+
// Type command with email
497+
const emailCommand = "git config user.email [email protected]"
498+
store.set(setTextAtom, emailCommand)
499+
500+
// Verify the @ is preserved as normal text
501+
expect(store.get(textBufferStringAtom)).toBe(emailCommand)
502+
expect(store.get(shellModeActiveAtom)).toBe(true)
503+
})
504+
505+
it("should allow multiple @ symbols in shell commands", () => {
506+
// Enter shell mode
507+
store.set(toggleShellModeAtom)
508+
expect(store.get(shellModeActiveAtom)).toBe(true)
509+
510+
// Type command with multiple @ symbols
511+
const command = "echo 'user@host, admin@host, test@domain'"
512+
store.set(setTextAtom, command)
513+
514+
// Verify all @ symbols are preserved
515+
expect(store.get(textBufferStringAtom)).toBe(command)
516+
expect(store.get(shellModeActiveAtom)).toBe(true)
517+
})
518+
519+
it("should allow @ in shell command arguments", () => {
520+
// Enter shell mode
521+
store.set(toggleShellModeAtom)
522+
expect(store.get(shellModeActiveAtom)).toBe(true)
523+
524+
// Type command with @ in various positions
525+
const command = "ssh [email protected] -p 22"
526+
store.set(setTextAtom, command)
527+
528+
// Verify @ is treated as normal text
529+
expect(store.get(textBufferStringAtom)).toBe(command)
530+
})
531+
532+
it("should handle @ at the start of a shell command", () => {
533+
// Enter shell mode
534+
store.set(toggleShellModeAtom)
535+
expect(store.get(shellModeActiveAtom)).toBe(true)
536+
537+
// Type command starting with @
538+
store.set(setTextAtom, "@echo test")
539+
540+
// Verify @ is preserved
541+
expect(store.get(textBufferStringAtom)).toBe("@echo test")
542+
expect(store.get(shellModeActiveAtom)).toBe(true)
543+
})
544+
})
545+
546+
describe("/ command suggestions in shell mode", () => {
547+
it("should not trigger command suggestions when typing / in shell mode", () => {
548+
// Enter shell mode
549+
store.set(toggleShellModeAtom)
550+
expect(store.get(shellModeActiveAtom)).toBe(true)
551+
552+
// Type text with / for paths
553+
store.set(setTextAtom, "cd /home/user/projects")
554+
555+
// Verify shell mode is still active
556+
expect(store.get(shellModeActiveAtom)).toBe(true)
557+
expect(store.get(inputModeAtom)).toBe("shell")
558+
559+
// The / should be treated as regular text (path separator), not triggering commands
560+
expect(store.get(textBufferStringAtom)).toBe("cd /home/user/projects")
561+
})
562+
563+
it("should allow absolute paths with / in shell mode", () => {
564+
// Enter shell mode
565+
store.set(toggleShellModeAtom)
566+
expect(store.get(shellModeActiveAtom)).toBe(true)
567+
568+
// Type command with absolute path
569+
const pathCommand = "ls -la /var/log/"
570+
store.set(setTextAtom, pathCommand)
571+
572+
// Verify the / is preserved as normal text
573+
expect(store.get(textBufferStringAtom)).toBe(pathCommand)
574+
expect(store.get(shellModeActiveAtom)).toBe(true)
575+
})
576+
577+
it("should allow multiple slashes in file paths", () => {
578+
// Enter shell mode
579+
store.set(toggleShellModeAtom)
580+
expect(store.get(shellModeActiveAtom)).toBe(true)
581+
582+
// Type command with multiple slashes
583+
const command = "cat /etc/nginx/nginx.conf"
584+
store.set(setTextAtom, command)
585+
586+
// Verify all / symbols are preserved
587+
expect(store.get(textBufferStringAtom)).toBe(command)
588+
expect(store.get(shellModeActiveAtom)).toBe(true)
589+
})
590+
591+
it("should allow / at the start of a shell command (absolute paths)", () => {
592+
// Enter shell mode
593+
store.set(toggleShellModeAtom)
594+
expect(store.get(shellModeActiveAtom)).toBe(true)
595+
596+
// Type command starting with / (absolute path)
597+
store.set(setTextAtom, "/usr/bin/python3 script.py")
598+
599+
// Verify / is preserved
600+
expect(store.get(textBufferStringAtom)).toBe("/usr/bin/python3 script.py")
601+
expect(store.get(shellModeActiveAtom)).toBe(true)
602+
})
603+
604+
it("should allow URLs with / and @ in shell mode", () => {
605+
// Enter shell mode
606+
store.set(toggleShellModeAtom)
607+
expect(store.get(shellModeActiveAtom)).toBe(true)
608+
609+
// Type command with URL containing both @ and /
610+
const curlCommand = "curl https://[email protected]/api/endpoint"
611+
store.set(setTextAtom, curlCommand)
612+
613+
// Verify both @ and / are preserved
614+
expect(store.get(textBufferStringAtom)).toBe(curlCommand)
615+
expect(store.get(shellModeActiveAtom)).toBe(true)
616+
})
617+
618+
it("should allow git commands with / in branch names", () => {
619+
// Enter shell mode
620+
store.set(toggleShellModeAtom)
621+
expect(store.get(shellModeActiveAtom)).toBe(true)
622+
623+
// Type git command with / in branch name
624+
const gitCommand = "git checkout feature/add-new-feature"
625+
store.set(setTextAtom, gitCommand)
626+
627+
// Verify / is preserved
628+
expect(store.get(textBufferStringAtom)).toBe(gitCommand)
629+
expect(store.get(shellModeActiveAtom)).toBe(true)
630+
})
631+
})
632+
474633
describe("edge cases", () => {
475634
it("should handle empty string command gracefully", async () => {
476635
await store.set(executeShellCommandAtom, "")

cli/src/state/hooks/useCommandInput.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
showAutocompleteMenuAtom,
4343
getSelectedSuggestionAtom,
4444
} from "../atoms/ui.js"
45+
import { shellModeActiveAtom } from "../atoms/shell.js"
4546
import { textBufferStringAtom, textBufferCursorAtom } from "../atoms/textBuffer.js"
4647
import { routerModelsAtom, extensionStateAtom } from "../atoms/extension.js"
4748
import { providerAtom, updateProviderAtom } from "../atoms/config.js"
@@ -150,6 +151,7 @@ export function useCommandInput(): UseCommandInputReturn {
150151
const inputValue = useAtomValue(textBufferStringAtom)
151152
const cursor = useAtomValue(textBufferCursorAtom)
152153
const showAutocomplete = useAtomValue(showAutocompleteAtom)
154+
const isShellMode = useAtomValue(shellModeActiveAtom)
153155
const commandSuggestions = useAtomValue(suggestionsAtom)
154156
const argumentSuggestions = useAtomValue(argumentSuggestionsAtom)
155157
const fileMentionSuggestions = useAtomValue(fileMentionSuggestionsAtom)
@@ -214,6 +216,17 @@ export function useCommandInput(): UseCommandInputReturn {
214216
}, [showAutocompleteAction])
215217

216218
const updateSuggestions = useCallback(async () => {
219+
// In shell mode, disable all autocomplete
220+
// Shell commands use @ (emails, SSH), / (paths), and other special chars
221+
// that shouldn't trigger autocomplete suggestions
222+
if (isShellMode) {
223+
// Clear all suggestion state
224+
clearFileMentionAction()
225+
setSuggestionsAction([])
226+
setArgumentSuggestionsAction([])
227+
return
228+
}
229+
217230
// Calculate cursor position in the text (convert row/col to absolute position)
218231
const lines = inputValue.split("\n")
219232
let cursorPosition = 0
@@ -234,7 +247,6 @@ export function useCommandInput(): UseCommandInputReturn {
234247
return
235248
}
236249

237-
// Clear file mention state if not in context
238250
clearFileMentionAction()
239251

240252
// Fall back to command/argument detection
@@ -286,6 +298,7 @@ export function useCommandInput(): UseCommandInputReturn {
286298
inputValue,
287299
cursor,
288300
cwd,
301+
isShellMode,
289302
setSuggestionsAction,
290303
setArgumentSuggestionsAction,
291304
setFileMentionSuggestionsAction,

0 commit comments

Comments
 (0)