@@ -1369,6 +1369,79 @@ def test_codex_skill_registration_resolves_script_placeholders(self, project_dir
13691369 assert "{ARGS}" not in content
13701370 assert '.specify/scripts/bash/setup-plan.sh --json "$ARGUMENTS"' in content
13711371
1372+ @pytest .mark .parametrize ("agent_name,skills_path" , [
1373+ ("codex" , ".agents/skills" ),
1374+ ("kimi" , ".kimi/skills" ),
1375+ ("claude" , ".claude/skills" ),
1376+ ("cursor-agent" , ".cursor/skills" ),
1377+ ("trae" , ".trae/skills" ),
1378+ ("agy" , ".agents/skills" ),
1379+ ])
1380+ def test_all_skill_agents_register_commands_with_resolved_placeholders (
1381+ self , project_dir , temp_dir , agent_name , skills_path
1382+ ):
1383+ """All SKILL.md agents must produce fully resolved SKILL.md files when commands are registered."""
1384+ import yaml
1385+
1386+ ext_dir = temp_dir / f"ext-{ agent_name } "
1387+ ext_dir .mkdir ()
1388+ (ext_dir / "commands" ).mkdir ()
1389+
1390+ manifest_data = {
1391+ "schema_version" : "1.0" ,
1392+ "extension" : {
1393+ "id" : f"ext-{ agent_name } " ,
1394+ "name" : "Scripted Extension" ,
1395+ "version" : "1.0.0" ,
1396+ "description" : "Test" ,
1397+ },
1398+ "requires" : {"speckit_version" : ">=0.1.0" },
1399+ "provides" : {
1400+ "commands" : [
1401+ {
1402+ "name" : f"speckit.ext-{ agent_name } .run" ,
1403+ "file" : "commands/run.md" ,
1404+ "description" : "Scripted command" ,
1405+ }
1406+ ]
1407+ },
1408+ }
1409+ with open (ext_dir / "extension.yml" , "w" ) as f :
1410+ yaml .dump (manifest_data , f )
1411+
1412+ (ext_dir / "commands" / "run.md" ).write_text (
1413+ "---\n "
1414+ "description: Scripted command\n "
1415+ "scripts:\n "
1416+ ' sh: ../../scripts/bash/setup-plan.sh --json "{ARGS}"\n '
1417+ "---\n \n "
1418+ "Run {SCRIPT}\n "
1419+ "Agent is __AGENT__.\n "
1420+ )
1421+
1422+ init_options = project_dir / ".specify" / "init-options.json"
1423+ init_options .parent .mkdir (parents = True , exist_ok = True )
1424+ init_options .write_text (f'{{"ai":"{ agent_name } ","script":"sh"}}' )
1425+
1426+ skills_dir = project_dir
1427+ for part in skills_path .split ("/" ):
1428+ skills_dir = skills_dir / part
1429+ skills_dir .mkdir (parents = True )
1430+
1431+ manifest = ExtensionManifest (ext_dir / "extension.yml" )
1432+ registrar = CommandRegistrar ()
1433+ registrar .register_commands_for_agent (agent_name , manifest , ext_dir , project_dir )
1434+
1435+ skill_dir_name = f"speckit-ext-{ agent_name } -run"
1436+ skill_file = skills_dir / skill_dir_name / "SKILL.md"
1437+ assert skill_file .exists (), f"SKILL.md not created for { agent_name } "
1438+
1439+ content = skill_file .read_text ()
1440+ assert "{SCRIPT}" not in content , f"{{SCRIPT}} not resolved for { agent_name } "
1441+ assert "__AGENT__" not in content , f"__AGENT__ not resolved for { agent_name } "
1442+ assert "{ARGS}" not in content , f"{{ARGS}} not resolved for { agent_name } "
1443+ assert '.specify/scripts/bash/setup-plan.sh' in content
1444+
13721445 def test_codex_skill_alias_frontmatter_matches_alias_name (self , project_dir , temp_dir ):
13731446 """Codex alias skills should render their own matching `name:` frontmatter."""
13741447 import yaml
0 commit comments