@@ -605,6 +605,32 @@ pub fn run_with_discovery(task_name: &str, args: Vec<String>) -> Result<()> {
605605 bail ! ( "No tasks defined in {} or subdirectories" , root. display( ) ) ;
606606 }
607607
608+ let discovered = select_discovered_task ( & discovery, task_name) ?;
609+ if let Some ( discovered) = discovered {
610+ return run ( TaskRunOpts {
611+ config : discovered. config_path . clone ( ) ,
612+ delegate_to_hub : false ,
613+ hub_host : std:: net:: IpAddr :: from ( [ 127 , 0 , 0 , 1 ] ) ,
614+ hub_port : 9050 ,
615+ name : discovered. task . name . clone ( ) ,
616+ args,
617+ } ) ;
618+ }
619+
620+ // List available tasks in error message
621+ let available: Vec < _ > = discovery. tasks . iter ( ) . map ( task_reference) . collect ( ) ;
622+ bail ! (
623+ "task '{}' not found.\n Available tasks: {}" ,
624+ task_name,
625+ available. join( ", " )
626+ ) ;
627+ }
628+
629+ fn select_discovered_task < ' a > (
630+ discovery : & ' a discover:: DiscoveryResult ,
631+ task_name : & str ,
632+ ) -> Result < Option < & ' a discover:: DiscoveredTask > > {
633+ let mut scoped_not_found: Option < ( String , String , Vec < String > ) > = None ;
608634 if let Some ( ( scope, scoped_task) ) = parse_scoped_selector ( task_name) {
609635 let scope_exists = discovery. tasks . iter ( ) . any ( |d| d. matches_scope ( & scope) ) ;
610636 if scope_exists {
@@ -640,14 +666,7 @@ pub fn run_with_discovery(task_name: &str, args: Vec<String>) -> Result<()> {
640666 } ;
641667
642668 if let Some ( discovered) = selected {
643- return run ( TaskRunOpts {
644- config : discovered. config_path . clone ( ) ,
645- delegate_to_hub : false ,
646- hub_host : std:: net:: IpAddr :: from ( [ 127 , 0 , 0 , 1 ] ) ,
647- hub_port : 9050 ,
648- name : discovered. task . name . clone ( ) ,
649- args,
650- } ) ;
669+ return Ok ( Some ( discovered) ) ;
651670 }
652671
653672 let scoped_available: Vec < String > = discovery
@@ -656,16 +675,7 @@ pub fn run_with_discovery(task_name: &str, args: Vec<String>) -> Result<()> {
656675 . filter ( |d| d. matches_scope ( & scope) )
657676 . map ( task_reference)
658677 . collect ( ) ;
659- bail ! (
660- "task '{}' not found in scope '{}'.\n Available in scope: {}" ,
661- scoped_task,
662- scope,
663- if scoped_available. is_empty( ) {
664- "(none)" . to_string( )
665- } else {
666- scoped_available. join( ", " )
667- }
668- ) ;
678+ scoped_not_found = Some ( ( scope, scoped_task, scoped_available) ) ;
669679 }
670680 }
671681
@@ -706,23 +716,23 @@ pub fn run_with_discovery(task_name: &str, args: Vec<String>) -> Result<()> {
706716 } ;
707717
708718 if let Some ( discovered) = discovered {
709- return run ( TaskRunOpts {
710- config : discovered. config_path . clone ( ) ,
711- delegate_to_hub : false ,
712- hub_host : std:: net:: IpAddr :: from ( [ 127 , 0 , 0 , 1 ] ) ,
713- hub_port : 9050 ,
714- name : discovered. task . name . clone ( ) ,
715- args,
716- } ) ;
719+ return Ok ( Some ( discovered) ) ;
717720 }
718721
719- // List available tasks in error message
720- let available: Vec < _ > = discovery. tasks . iter ( ) . map ( task_reference) . collect ( ) ;
721- bail ! (
722- "task '{}' not found.\n Available tasks: {}" ,
723- task_name,
724- available. join( ", " )
725- ) ;
722+ if let Some ( ( scope, scoped_task, scoped_available) ) = scoped_not_found {
723+ bail ! (
724+ "task '{}' not found in scope '{}'.\n Available in scope: {}" ,
725+ scoped_task,
726+ scope,
727+ if scoped_available. is_empty( ) {
728+ "(none)" . to_string( )
729+ } else {
730+ scoped_available. join( ", " )
731+ }
732+ ) ;
733+ }
734+
735+ Ok ( None )
726736}
727737
728738fn parse_scoped_selector ( selector : & str ) -> Option < ( String , String ) > {
@@ -3172,6 +3182,23 @@ mod tests {
31723182 assert_eq ! ( selected. scope, "root" ) ;
31733183 }
31743184
3185+ #[ test]
3186+ fn select_discovered_task_allows_exact_names_with_scope_delimiters ( ) {
3187+ let scoped = discovered_task ( "mobile" , "mobile" , "run" ) ;
3188+ let exact = discovered_task ( "root" , "" , "mobile:dev" ) ;
3189+ let discovery = discover:: DiscoveryResult {
3190+ tasks : vec ! [ scoped, exact] ,
3191+ root_config : None ,
3192+ root_cfg : None ,
3193+ } ;
3194+
3195+ let selected = select_discovered_task ( & discovery, "mobile:dev" )
3196+ . expect ( "selection should succeed" )
3197+ . expect ( "exact task should resolve" ) ;
3198+ assert_eq ! ( selected. scope, "root" ) ;
3199+ assert_eq ! ( selected. task. name, "mobile:dev" ) ;
3200+ }
3201+
31753202 #[ test]
31763203 fn format_discovered_task_lines_prefixes_scope ( ) {
31773204 let entries = vec ! [ discovered_task( "mobile" , "mobile" , "dev" ) ] ;
0 commit comments