@@ -948,7 +948,19 @@ def download_template_from_github(ai_assistant: str, download_dir: Path, *, scri
948948 }
949949 return zip_path , metadata
950950
951- def download_and_extract_template (project_path : Path , ai_assistant : str , script_type : str , is_current_dir : bool = False , * , verbose : bool = True , tracker : StepTracker | None = None , client : httpx .Client = None , debug : bool = False , github_token : str = None ) -> Path :
951+ def download_and_extract_template (
952+ project_path : Path ,
953+ ai_assistant : str ,
954+ script_type : str ,
955+ is_current_dir : bool = False ,
956+ * ,
957+ skip_legacy_codex_prompts : bool = False ,
958+ verbose : bool = True ,
959+ tracker : StepTracker | None = None ,
960+ client : httpx .Client = None ,
961+ debug : bool = False ,
962+ github_token : str = None ,
963+ ) -> Path :
952964 """Download the latest release and extract it to create a new project.
953965 Returns project_path. Uses tracker if provided (with keys: fetch, download, extract, cleanup)
954966 """
@@ -1019,6 +1031,10 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
10191031 console .print ("[cyan]Found nested directory structure[/cyan]" )
10201032
10211033 for item in source_dir .iterdir ():
1034+ # Codex skills mode should not materialize legacy prompt files
1035+ # from older template archives.
1036+ if skip_legacy_codex_prompts and ai_assistant == "codex" and item .name == ".codex" :
1037+ continue
10221038 dest_path = project_path / item .name
10231039 if item .is_dir ():
10241040 if dest_path .exists ():
@@ -1069,6 +1085,11 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
10691085 elif verbose :
10701086 console .print ("[cyan]Flattened nested directory structure[/cyan]" )
10711087
1088+ if skip_legacy_codex_prompts and ai_assistant == "codex" :
1089+ legacy_codex_dir = project_path / ".codex"
1090+ if legacy_codex_dir .is_dir ():
1091+ shutil .rmtree (legacy_codex_dir , ignore_errors = True )
1092+
10721093 except Exception as e :
10731094 if tracker :
10741095 tracker .error ("extract" , str (e ))
@@ -1994,7 +2015,18 @@ def init(
19942015
19952016 if use_github :
19962017 with httpx .Client (verify = local_ssl_context ) as local_client :
1997- download_and_extract_template (project_path , selected_ai , selected_script , here , verbose = False , tracker = tracker , client = local_client , debug = debug , github_token = github_token )
2018+ download_and_extract_template (
2019+ project_path ,
2020+ selected_ai ,
2021+ selected_script ,
2022+ here ,
2023+ skip_legacy_codex_prompts = (selected_ai == "codex" and ai_skills ),
2024+ verbose = False ,
2025+ tracker = tracker ,
2026+ client = local_client ,
2027+ debug = debug ,
2028+ github_token = github_token ,
2029+ )
19982030 else :
19992031 scaffold_ok = scaffold_from_core_pack (project_path , selected_ai , selected_script , here , tracker = tracker )
20002032 if not scaffold_ok :
@@ -2013,7 +2045,6 @@ def init(
20132045 if not here and project_path .exists ():
20142046 shutil .rmtree (project_path )
20152047 raise typer .Exit (1 )
2016-
20172048 # For generic agent, rename placeholder directory to user-specified path
20182049 if selected_ai == "generic" and ai_commands_dir :
20192050 placeholder_dir = project_path / ".speckit" / "commands"
@@ -2033,16 +2064,25 @@ def init(
20332064 if ai_skills :
20342065 if selected_ai in NATIVE_SKILLS_AGENTS :
20352066 skills_dir = _get_skills_dir (project_path , selected_ai )
2036- if not _has_bundled_skills (project_path , selected_ai ):
2037- raise RuntimeError (
2038- f"Expected bundled agent skills in { skills_dir .relative_to (project_path )} , "
2039- "but none were found. Re-run with an up-to-date template."
2040- )
2041- if tracker :
2042- tracker .start ("ai-skills" )
2043- tracker .complete ("ai-skills" , f"bundled skills → { skills_dir .relative_to (project_path )} " )
2067+ bundled_found = _has_bundled_skills (project_path , selected_ai )
2068+ if bundled_found :
2069+ if tracker :
2070+ tracker .start ("ai-skills" )
2071+ tracker .complete ("ai-skills" , f"bundled skills → { skills_dir .relative_to (project_path )} " )
2072+ else :
2073+ console .print (f"[green]✓[/green] Using bundled agent skills in { skills_dir .relative_to (project_path )} /" )
20442074 else :
2045- console .print (f"[green]✓[/green] Using bundled agent skills in { skills_dir .relative_to (project_path )} /" )
2075+ # Compatibility fallback: convert command templates to skills
2076+ # when an older template archive does not include native skills.
2077+ # This keeps `specify init --here --ai codex --ai-skills` usable
2078+ # in repos that already contain unrelated skills under .agents/skills.
2079+ fallback_ok = install_ai_skills (project_path , selected_ai , tracker = tracker )
2080+ if not fallback_ok :
2081+ raise RuntimeError (
2082+ f"Expected bundled agent skills in { skills_dir .relative_to (project_path )} , "
2083+ "but none were found and fallback conversion failed. "
2084+ "Re-run with an up-to-date template."
2085+ )
20462086 else :
20472087 skills_ok = install_ai_skills (project_path , selected_ai , tracker = tracker )
20482088
0 commit comments