@@ -15,6 +15,7 @@ import {
1515} from '@codebuff/common/util/agent-name-resolver'
1616import { isDir } from '@codebuff/common/util/file'
1717import { pluralize } from '@codebuff/common/util/string'
18+ import { uniq } from 'lodash'
1819import {
1920 blueBright ,
2021 bold ,
@@ -458,6 +459,84 @@ export class CLI {
458459 process . stdin . on ( 'keypress' , ( str , key ) => this . handleKeyPress ( str , key ) )
459460 }
460461
462+ private calculateAgentNameCompletions (
463+ line : string ,
464+ ) : [ string [ ] , string ] | null {
465+ if ( ! line . includes ( '@' ) ) {
466+ return null
467+ }
468+
469+ const $split = line . split ( '@' )
470+ const atAgentPrefix = `@${ $split [ $split . length - 1 ] } `
471+ const searchTerm = atAgentPrefix . substring ( 1 ) . toLowerCase ( ) // Remove @ prefix
472+
473+ // Get all agent names using functional API
474+ const localAgentInfo = getCachedLocalAgentInfo ( )
475+ const allAgentNames = [
476+ ...new Set (
477+ getAllAgents ( localAgentInfo ) . map ( ( agent ) => agent . displayName ) ,
478+ ) ,
479+ ]
480+
481+ // Filter agent names that match the search term
482+ const matchingAgents = allAgentNames . filter ( ( name ) =>
483+ name . toLowerCase ( ) . startsWith ( searchTerm ) ,
484+ )
485+
486+ if ( matchingAgents . length > 0 ) {
487+ // Return completions with @ prefix
488+ const completions = matchingAgents . map ( ( name ) => `@${ name } ` )
489+ return [ completions , atAgentPrefix ]
490+ }
491+
492+ return null
493+ }
494+
495+ private calculateAtFilenameCompletions (
496+ line : string ,
497+ ) : [ string [ ] , string ] | null {
498+ if ( ! line . includes ( '@' ) ) {
499+ return null
500+ }
501+
502+ const $split = line . split ( '@' )
503+ const atFilePrefix = `@${ $split [ $split . length - 1 ] } `
504+ const searchTerm = atFilePrefix . substring ( 1 ) . toLowerCase ( ) // Remove @ prefix
505+
506+ const client = Client . getInstance ( )
507+ if ( ! client . fileContext ) {
508+ return null
509+ }
510+ const allFiles = this . getAllFilePaths ( client . fileContext . fileTree )
511+ // high priority first
512+ function priority ( filePath : string ) : number {
513+ if ( ! filePath . endsWith ( '/' ) ) {
514+ return 0
515+ }
516+ return 1
517+ }
518+ const matchingPaths = uniq (
519+ allFiles
520+ . map ( ( filePath ) => {
521+ let candidate = null
522+ while ( filePath . includes ( searchTerm ) ) {
523+ candidate = filePath
524+ filePath = path . dirname ( filePath ) + '/'
525+ }
526+ return candidate
527+ } )
528+ . filter ( ( filePath ) : filePath is string => ! ! filePath ) ,
529+ ) . sort ( ( a , b ) => priority ( a ) - priority ( b ) )
530+
531+ if ( matchingPaths . length > 0 ) {
532+ // Return completions with @ prefix
533+ const completions = matchingPaths . map ( ( path ) => `@${ path } ` )
534+ return [ completions , atFilePrefix ]
535+ }
536+
537+ return null
538+ }
539+
461540 private inputCompleter ( line : string ) : [ string [ ] , string ] {
462541 const lastWord = line . split ( ' ' ) . pop ( ) || ''
463542
@@ -483,34 +562,19 @@ export class CLI {
483562 return [ [ ] , line ] // No slash command matches
484563 }
485564
486- // Handle @ prefix for agent name completion
487- if ( line . includes ( '@' ) ) {
488- const $split = line . split ( '@' )
489- const atAgentPrefix = `@${ $split [ $split . length - 1 ] } `
490- const searchTerm = atAgentPrefix . substring ( 1 ) . toLowerCase ( ) // Remove @ prefix
491-
492- // Get all agent names using functional API
493- const localAgentInfo = getCachedLocalAgentInfo ( )
494- const allAgentNames = [
495- ...new Set (
496- getAllAgents ( localAgentInfo ) . map ( ( agent ) => agent . displayName ) ,
497- ) ,
498- ]
499-
500- // Filter agent names that match the search term
501- const matchingAgents = allAgentNames . filter ( ( name ) =>
502- name . toLowerCase ( ) . startsWith ( searchTerm ) ,
503- )
504-
505- if ( matchingAgents . length > 0 ) {
506- // Return completions with @ prefix
507- const completions = matchingAgents . map ( ( name ) => `@${ name } ` )
508- return [ completions , atAgentPrefix ]
509- }
510-
511- // If no agent matches, return empty completions for better UX
512- // Users typing @ likely intend to mention an agent
513- return [ [ ] , atAgentPrefix ]
565+ const agentCompletions = this . calculateAgentNameCompletions ( line )
566+ const atFilenameCompletions = this . calculateAtFilenameCompletions ( line )
567+ const atCompletion : [ string [ ] , string ] = [ [ ] , '' ]
568+ if ( agentCompletions ) {
569+ atCompletion [ 0 ] . push ( ...agentCompletions [ 0 ] )
570+ atCompletion [ 1 ] = agentCompletions [ 1 ]
571+ }
572+ if ( atFilenameCompletions ) {
573+ atCompletion [ 0 ] . push ( ...atFilenameCompletions [ 0 ] )
574+ atCompletion [ 1 ] = atFilenameCompletions [ 1 ]
575+ }
576+ if ( atCompletion [ 1 ] ) {
577+ return atCompletion
514578 }
515579
516580 // Original file path completion logic (unchanged)
0 commit comments