2828EXCLUDED_PARAMS = ['self' , 'raw' , 'polling' , 'custom_headers' , 'operation_config' ,
2929 'content_version' , 'kwargs' , 'client' , 'no_wait' ]
3030EVENT_FAILED_EXTENSION_LOAD = 'MainLoader.OnFailedExtensionLoad'
31+ # Marker used by CommandIndex.get() to signal top-level tab completion optimization
32+ TOP_LEVEL_COMPLETION_MARKER = '__top_level_completion__'
3133
3234# [Reserved, in case of future usage]
3335# Modules that will always be loaded. They don't expose commands but hook into CLI core.
@@ -225,6 +227,24 @@ def __init__(self, cli_ctx=None):
225227 self .cmd_to_loader_map = {}
226228 self .loaders = []
227229
230+ def _create_stub_commands_for_completion (self , command_names ):
231+ """Create stub commands for top-level tab completion optimization.
232+
233+ Stub commands allow argcomplete to parse command names without loading modules.
234+
235+ :param command_names: List of command names to create stubs for
236+ """
237+ from azure .cli .core .commands import AzCliCommand
238+
239+ def _stub_handler (* _args , ** _kwargs ):
240+ """Stub command handler used only for argument completion."""
241+ return None
242+
243+ for cmd_name in command_names :
244+ if cmd_name not in self .command_table :
245+ # Stub commands only need names for argcomplete parser construction.
246+ self .command_table [cmd_name ] = AzCliCommand (self , cmd_name , _stub_handler )
247+
228248 def _update_command_definitions (self ):
229249 for cmd_name in self .command_table :
230250 loaders = self .cmd_to_loader_map [cmd_name ]
@@ -423,9 +443,16 @@ def _get_extension_suppressions(mod_loaders):
423443 index_result = command_index .get (args )
424444 if index_result :
425445 index_modules , index_extensions = index_result
446+
447+ if index_modules == TOP_LEVEL_COMPLETION_MARKER :
448+ self ._create_stub_commands_for_completion (index_extensions )
449+ _update_command_table_from_extensions ([], ALWAYS_LOADED_EXTENSIONS )
450+ return self .command_table
451+
426452 # Always load modules and extensions, because some of them (like those in
427453 # ALWAYS_LOADED_EXTENSIONS) don't expose a command, but hooks into handlers in CLI core
428454 _update_command_table_from_modules (args , index_modules )
455+
429456 # The index won't contain suppressed extensions
430457 _update_command_table_from_extensions ([], index_extensions )
431458
@@ -473,7 +500,6 @@ def _get_extension_suppressions(mod_loaders):
473500 else :
474501 logger .debug ("No module found from index for '%s'" , args )
475502
476- # No module found from the index. Load all command modules and extensions
477503 logger .debug ("Loading all modules and extensions" )
478504 _update_command_table_from_modules (args )
479505
@@ -662,6 +688,23 @@ def __init__(self, cli_ctx=None):
662688 self .cloud_profile = cli_ctx .cloud .profile
663689 self .cli_ctx = cli_ctx
664690
691+ def _get_top_level_completion_commands (self ):
692+ """Get top-level command names for tab completion optimization.
693+
694+ Returns marker and list of top-level commands (e.g., 'network', 'vm') for creating
695+ stub commands without module loading. Returns None if index is empty, triggering
696+ fallback to full module loading.
697+
698+ :return: tuple of (TOP_LEVEL_COMPLETION_MARKER, list of top-level command names) or None
699+ """
700+ index = self .INDEX .get (self ._COMMAND_INDEX ) or {}
701+ if not index :
702+ logger .debug ("Command index is empty, will fall back to loading all modules" )
703+ return None
704+ top_level_commands = list (index .keys ())
705+ logger .debug ("Top-level completion: %d commands available" , len (top_level_commands ))
706+ return TOP_LEVEL_COMPLETION_MARKER , top_level_commands
707+
665708 def get (self , args ):
666709 """Get the corresponding module and extension list of a command.
667710
@@ -681,6 +724,9 @@ def get(self, args):
681724 # Make sure the top-level command is provided, like `az version`.
682725 # Skip command index for `az` or `az --help`.
683726 if not args or args [0 ].startswith ('-' ):
727+ # For top-level completion (az [tab])
728+ if not args and self .cli_ctx .data .get ('completer_active' ):
729+ return self ._get_top_level_completion_commands ()
684730 return None
685731
686732 # Get the top-level command, like `network` in `network vnet create -h`
0 commit comments