From 6db74da972f046e5e6950093be745ec033770020 Mon Sep 17 00:00:00 2001 From: sjh9714 <163989462+sjh9714@users.noreply.github.com> Date: Tue, 16 Jun 2026 05:15:08 +0900 Subject: [PATCH] fix: write Codex dev skills as files --- src/specify_cli/agents.py | 2 +- src/specify_cli/extensions.py | 5 +++-- tests/test_extensions.py | 36 +++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/specify_cli/agents.py b/src/specify_cli/agents.py index 3c06418014..1df548b821 100644 --- a/src/specify_cli/agents.py +++ b/src/specify_cli/agents.py @@ -733,7 +733,7 @@ def _write_registered_output( link_outputs: bool, ) -> None: """Write a rendered agent artifact, optionally as a dev-mode symlink.""" - if not link_outputs: + if not link_outputs or (agent_name == "codex" and extension == "/SKILL.md"): dest_file.write_text(content, encoding="utf-8") return diff --git a/src/specify_cli/extensions.py b/src/specify_cli/extensions.py index db53b7997f..795d4d0634 100644 --- a/src/specify_cli/extensions.py +++ b/src/specify_cli/extensions.py @@ -1012,13 +1012,14 @@ def _register_extension_skills( skill_file = skill_subdir / "SKILL.md" cache_root = extension_dir / ".specify-dev" / "extension-skills" cache_file = cache_root / skill_name / "SKILL.md" + use_dev_symlink = link_outputs and selected_ai != "codex" CommandRegistrar._ensure_inside(cache_file, cache_root) if skill_file.exists() or skill_file.is_symlink(): # Do not overwrite user-customized skills, but allow dev-mode # symlinks that point back to this extension's generated cache # to be refreshed on a subsequent dev install. if not ( - link_outputs + use_dev_symlink and self._is_expected_dev_symlink(skill_file, cache_file) ): continue @@ -1089,7 +1090,7 @@ def _register_extension_skills( skill_content ) - if link_outputs: + if use_dev_symlink: try: cache_file.parent.mkdir(parents=True, exist_ok=True) cache_file.write_text(skill_content, encoding="utf-8") diff --git a/tests/test_extensions.py b/tests/test_extensions.py index 1d05e1c2c4..cdfdad67c8 100644 --- a/tests/test_extensions.py +++ b/tests/test_extensions.py @@ -4619,6 +4619,42 @@ def test_add_dev_links_copilot_agent_when_supported( else: assert not agent_file.is_symlink() + def test_add_dev_writes_codex_skills_as_files(self, extension_dir, project_dir): + """Codex dev skills should be written as files so Codex can load them.""" + from typer.testing import CliRunner + from unittest.mock import patch + from specify_cli import app + + init_options = project_dir / ".specify" / "init-options.json" + init_options.write_text( + json.dumps({"ai": "codex", "ai_skills": True}), encoding="utf-8" + ) + + runner = CliRunner() + with patch.object(Path, "cwd", return_value=project_dir): + result = runner.invoke( + app, + ["extension", "add", str(extension_dir), "--dev"], + catch_exceptions=True, + ) + + assert result.exit_code == 0, result.output + + skill_file = ( + project_dir + / ".agents" + / "skills" + / "speckit-test-ext-hello" + / "SKILL.md" + ) + assert skill_file.exists() + assert not skill_file.is_symlink() + + content = skill_file.read_text(encoding="utf-8") + assert "name: speckit-test-ext-hello" in content + assert "metadata:" in content + assert "source: test-ext:commands/hello.md" in content + def test_add_dev_falls_back_to_copy_when_windows_symlinks_unavailable( self, extension_dir, project_dir, monkeypatch ):