@@ -588,25 +588,23 @@ def _get_skills_dir(self) -> Optional[Path]:
588588 return skills_dir
589589
590590 @staticmethod
591- def _skill_names_for_command (cmd_name : str ) -> List [ str ]:
591+ def _skill_names_for_command (cmd_name : str ) -> tuple [ str , str ]:
592592 """Return the modern and legacy skill directory names for a command."""
593593 raw_short_name = cmd_name
594594 if raw_short_name .startswith ("speckit." ):
595595 raw_short_name = raw_short_name [len ("speckit." ):]
596596
597- skill_names = [ f"speckit-{ raw_short_name .replace ('.' , '-' )} " ]
597+ modern_skill_name = f"speckit-{ raw_short_name .replace ('.' , '-' )} "
598598 legacy_skill_name = f"speckit.{ raw_short_name } "
599- if legacy_skill_name not in skill_names :
600- skill_names .append (legacy_skill_name )
599+ return modern_skill_name , legacy_skill_name
601600
602- return skill_names
603-
604- def _find_extension_skill_restore (self , skill_name : str ) -> Optional [Dict [str , Any ]]:
605- """Find the highest-priority installed extension template for a skill."""
601+ def _build_extension_skill_restore_index (self ) -> Dict [str , Dict [str , Any ]]:
602+ """Index extension-backed skill restore data by skill directory name."""
606603 from .extensions import ExtensionManifest , ValidationError
607604
608605 resolver = PresetResolver (self .project_root )
609606 extensions_dir = self .project_root / ".specify" / "extensions"
607+ restore_index : Dict [str , Dict [str , Any ]] = {}
610608
611609 for _priority , ext_id , _metadata in resolver ._get_all_extensions_by_priority ():
612610 ext_dir = extensions_dir / ext_id
@@ -625,8 +623,6 @@ def _find_extension_skill_restore(self, skill_name: str) -> Optional[Dict[str, A
625623 cmd_file_rel = cmd_info .get ("file" )
626624 if not isinstance (cmd_name , str ) or not isinstance (cmd_file_rel , str ):
627625 continue
628- if skill_name not in self ._skill_names_for_command (cmd_name ):
629- continue
630626
631627 cmd_path = Path (cmd_file_rel )
632628 if cmd_path .is_absolute ():
@@ -641,13 +637,17 @@ def _find_extension_skill_restore(self, skill_name: str) -> Optional[Dict[str, A
641637 if not source_file .is_file ():
642638 continue
643639
644- return {
640+ restore_info = {
645641 "command_name" : cmd_name ,
646642 "source_file" : source_file ,
647643 "source" : f"extension:{ manifest .id } " ,
648644 }
645+ modern_skill_name , legacy_skill_name = self ._skill_names_for_command (cmd_name )
646+ restore_index .setdefault (modern_skill_name , restore_info )
647+ if legacy_skill_name != modern_skill_name :
648+ restore_index .setdefault (legacy_skill_name , restore_info )
649649
650- return None
650+ return restore_index
651651
652652 def _register_skills (
653653 self ,
@@ -797,6 +797,7 @@ def _unregister_skills(self, skill_names: List[str], preset_dir: Path) -> None:
797797 init_opts = load_init_options (self .project_root )
798798 selected_ai = init_opts .get ("ai" )
799799 registrar = CommandRegistrar ()
800+ extension_restore_index = self ._build_extension_skill_restore_index ()
800801
801802 for skill_name in skill_names :
802803 # Derive command name from skill name (speckit-specify -> specify)
@@ -851,7 +852,7 @@ def _unregister_skills(self, skill_names: List[str], preset_dir: Path) -> None:
851852 skill_file .write_text (skill_content , encoding = "utf-8" )
852853 continue
853854
854- extension_restore = self . _find_extension_skill_restore (skill_name )
855+ extension_restore = extension_restore_index . get (skill_name )
855856 if extension_restore :
856857 content = extension_restore ["source_file" ].read_text (encoding = "utf-8" )
857858 frontmatter , body = registrar .parse_frontmatter (content )
0 commit comments