Skip to content

Commit 3a63863

Browse files
committed
Refine preset skill restore helpers
1 parent 691b4dd commit 3a63863

2 files changed

Lines changed: 21 additions & 15 deletions

File tree

src/specify_cli/agents.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,18 @@ def render_frontmatter(fm: dict) -> str:
213213
return f"---\n{yaml_str}---\n"
214214

215215
def _adjust_script_paths(self, frontmatter: dict) -> dict:
216-
"""Adjust script paths from extension-relative to repo-relative.
216+
"""Normalize script paths in frontmatter to generated project locations.
217+
218+
Rewrites known repo-relative and top-level script paths under the
219+
`scripts` and `agent_scripts` keys (for example `../../scripts/`,
220+
`../../templates/`, `../../memory/`, `scripts/`, `templates/`, and
221+
`memory/`) to the `.specify/...` paths used in generated projects.
217222
218223
Args:
219224
frontmatter: Frontmatter dictionary
220225
221226
Returns:
222-
Modified frontmatter with adjusted paths
227+
Modified frontmatter with normalized project paths
223228
"""
224229
frontmatter = deepcopy(frontmatter)
225230

src/specify_cli/presets.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)