Skip to content

Commit d044286

Browse files
committed
fix: resolve skill placeholders for all SKILL.md agents, not just codex/kimi
1 parent 569d18a commit d044286

2 files changed

Lines changed: 75 additions & 1 deletion

File tree

src/specify_cli/agents.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,8 @@ def render_skill_command(
282282
if not isinstance(frontmatter, dict):
283283
frontmatter = {}
284284

285-
if agent_name in {"codex", "kimi"}:
285+
agent_config = self.AGENT_CONFIGS.get(agent_name, {})
286+
if agent_config.get("extension") == "/SKILL.md":
286287
body = self.resolve_skill_placeholders(
287288
agent_name, frontmatter, body, project_root
288289
)

tests/test_extensions.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,79 @@ def test_codex_skill_registration_resolves_script_placeholders(self, project_dir
13611361
assert "{ARGS}" not in content
13621362
assert '.specify/scripts/bash/setup-plan.sh --json "$ARGUMENTS"' in content
13631363

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

0 commit comments

Comments
 (0)