@@ -354,12 +354,64 @@ describe("keypress atoms", () => {
354354 }
355355 store . set ( keyboardHandlerAtom , tabKey )
356356
357- // Should append only 'de' to complete '/mode'
357+ // Should complete to '/mode'
358358 const text = store . get ( textBufferStringAtom )
359359 expect ( text ) . toBe ( "/mode" )
360360 } )
361361
362- it ( "should complete argument by appending only missing part" , ( ) => {
362+ it ( "should complete command even when user types wrong letters" , ( ) => {
363+ // Type '/modl' - typo, but 'model' should still be suggested
364+ const chars = [ "/" , "m" , "o" , "d" , "l" ]
365+ for ( const char of chars ) {
366+ const key : Key = {
367+ name : char ,
368+ sequence : char ,
369+ ctrl : false ,
370+ meta : false ,
371+ shift : false ,
372+ paste : false ,
373+ }
374+ store . set ( keyboardHandlerAtom , key )
375+ }
376+
377+ // Autocomplete should now be visible
378+ expect ( store . get ( showAutocompleteAtom ) ) . toBe ( true )
379+
380+ // Set up autocomplete suggestions
381+ const mockCommand : Command = {
382+ name : "model" ,
383+ description : "Manage models" ,
384+ aliases : [ ] ,
385+ usage : "/model <subcommand>" ,
386+ examples : [ "/model info" ] ,
387+ category : "settings" ,
388+ handler : vi . fn ( ) ,
389+ }
390+ const mockSuggestion : CommandSuggestion = {
391+ command : mockCommand ,
392+ matchScore : 70 ,
393+ highlightedName : "model" ,
394+ }
395+ store . set ( suggestionsAtom , [ mockSuggestion ] )
396+ store . set ( selectedIndexAtom , 0 )
397+
398+ // Press Tab
399+ const tabKey : Key = {
400+ name : "tab" ,
401+ sequence : "\t" ,
402+ ctrl : false ,
403+ meta : false ,
404+ shift : false ,
405+ paste : false ,
406+ }
407+ store . set ( keyboardHandlerAtom , tabKey )
408+
409+ // Should replace '/modl' with '/model' (not '/modlmodel')
410+ const text = store . get ( textBufferStringAtom )
411+ expect ( text ) . toBe ( "/model" )
412+ } )
413+
414+ it ( "should complete argument by replacing partial text" , ( ) => {
363415 // Type '/mode tes' - this will automatically trigger autocomplete
364416 const input = "/mode tes"
365417 for ( const char of input ) {
@@ -398,11 +450,96 @@ describe("keypress atoms", () => {
398450 }
399451 store . set ( keyboardHandlerAtom , tabKey )
400452
401- // Should append only 't ' to complete '/mode test'
453+ // Should replace 'tes' with 'test ' to complete '/mode test'
402454 const text = store . get ( textBufferStringAtom )
403455 expect ( text ) . toBe ( "/mode test" )
404456 } )
405457
458+ it ( "should replace partial argument with full suggestion" , ( ) => {
459+ // Bug fix: Type '/model info gpt' with suggestion 'openai/gpt-5'
460+ // This test verifies the fix where Tab was incorrectly appending instead of replacing
461+ const input = "/model info gpt"
462+ for ( const char of input ) {
463+ const key : Key = {
464+ name : char ,
465+ sequence : char ,
466+ ctrl : false ,
467+ meta : false ,
468+ shift : false ,
469+ paste : false ,
470+ }
471+ store . set ( keyboardHandlerAtom , key )
472+ }
473+
474+ // Set up argument suggestions
475+ const mockArgumentSuggestion : ArgumentSuggestion = {
476+ value : "openai/gpt-5" ,
477+ description : "OpenAI GPT-5 model" ,
478+ matchScore : 90 ,
479+ highlightedValue : "openai/gpt-5" ,
480+ }
481+ store . set ( argumentSuggestionsAtom , [ mockArgumentSuggestion ] )
482+ store . set ( suggestionsAtom , [ ] ) // No command suggestions
483+ store . set ( selectedIndexAtom , 0 )
484+
485+ // Press Tab
486+ const tabKey : Key = {
487+ name : "tab" ,
488+ sequence : "\t" ,
489+ ctrl : false ,
490+ meta : false ,
491+ shift : false ,
492+ paste : false ,
493+ }
494+ store . set ( keyboardHandlerAtom , tabKey )
495+
496+ // Should replace 'gpt' with 'openai/gpt-5' (not append to get 'gptopenai/gpt-5')
497+ const text = store . get ( textBufferStringAtom )
498+ expect ( text ) . toBe ( "/model info openai/gpt-5" )
499+ } )
500+
501+ it ( "should complete argument from empty with trailing space" , ( ) => {
502+ // Type '/model info ' (with trailing space)
503+ const input = "/model info "
504+ for ( const char of input ) {
505+ const key : Key = {
506+ name : char ,
507+ sequence : char ,
508+ ctrl : false ,
509+ meta : false ,
510+ shift : false ,
511+ paste : false ,
512+ }
513+ store . set ( keyboardHandlerAtom , key )
514+ }
515+
516+ // Set up argument suggestions
517+ const mockArgumentSuggestion : ArgumentSuggestion = {
518+ value : "openai/gpt-4" ,
519+ description : "OpenAI GPT-4 model" ,
520+ matchScore : 100 ,
521+ highlightedValue : "openai/gpt-4" ,
522+ }
523+ store . set ( argumentSuggestionsAtom , [ mockArgumentSuggestion ] )
524+ store . set ( suggestionsAtom , [ ] )
525+ store . set ( selectedIndexAtom , 0 )
526+
527+ // Press Tab
528+ const tabKey : Key = {
529+ name : "tab" ,
530+ sequence : "\t" ,
531+ ctrl : false ,
532+ meta : false ,
533+ shift : false ,
534+ paste : false ,
535+ }
536+ store . set ( keyboardHandlerAtom , tabKey )
537+
538+ // Should add the full suggestion value
539+ const text = store . get ( textBufferStringAtom )
540+ expect ( text ) . toBe ( "/model info openai/gpt-4" )
541+ } )
542+
406543 it ( "should handle exact match completion" , ( ) => {
407544 // Type '/help' - this will automatically trigger autocomplete
408545 const input = "/help"
0 commit comments