From 06382a93cddd5a8c77bb7c12faddfb46853e64be Mon Sep 17 00:00:00 2001 From: Michel Belleau Date: Sun, 5 Apr 2026 14:46:57 -0400 Subject: [PATCH] fix(skills): respect local skill dir in skill manager --- tests/tools/test_skill_manager_tool.py | 31 ++++++++++++++++++++++++++ tools/skill_manager_tool.py | 16 +++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/tests/tools/test_skill_manager_tool.py b/tests/tools/test_skill_manager_tool.py index a20d23fcbd82..a2f6ab1ebe79 100644 --- a/tests/tools/test_skill_manager_tool.py +++ b/tests/tools/test_skill_manager_tool.py @@ -219,6 +219,37 @@ def test_create_rejects_category_traversal(self, tmp_path): assert "Invalid category '../escape'" in result["error"] assert not (tmp_path / "escape").exists() + +class TestFindSkill: + def test_find_skill_respects_patched_skills_dir(self, tmp_path): + with patch("tools.skill_manager_tool.SKILLS_DIR", tmp_path): + _create_skill("my-skill", VALID_SKILL_CONTENT) + result = _find_skill("my-skill") + + assert result is not None + assert result["path"] == tmp_path / "my-skill" + + def test_find_skill_includes_external_dirs(self, tmp_path, monkeypatch): + local_skills = tmp_path / "local-skills" + external_skills = tmp_path / "external-skills" + local_skills.mkdir() + external_skills.mkdir() + (external_skills / "ext-skill").mkdir() + (external_skills / "ext-skill" / "SKILL.md").write_text( + VALID_SKILL_CONTENT, + encoding="utf-8", + ) + + with patch("tools.skill_manager_tool.SKILLS_DIR", local_skills): + monkeypatch.setattr( + "agent.skill_utils.get_external_skills_dirs", + lambda: [external_skills], + ) + result = _find_skill("ext-skill") + + assert result is not None + assert result["path"] == external_skills / "ext-skill" + def test_create_rejects_absolute_category(self, tmp_path): skills_dir = tmp_path / "skills" skills_dir.mkdir() diff --git a/tools/skill_manager_tool.py b/tools/skill_manager_tool.py index b8d8d62232e7..221f039029aa 100644 --- a/tools/skill_manager_tool.py +++ b/tools/skill_manager_tool.py @@ -209,8 +209,20 @@ def _find_skill(name: str) -> Optional[Dict[str, Any]]: external dirs configured via skills.external_dirs. Returns {"path": Path} or None. """ - from agent.skill_utils import get_all_skills_dirs - for skills_dir in get_all_skills_dirs(): + # Use the module-level SKILLS_DIR first so tests and callers that + # monkeypatch it get the expected local-first behavior. Then append any + # configured external dirs. + all_dirs = [SKILLS_DIR] + try: + from agent.skill_utils import get_external_skills_dirs + + for ext_dir in get_external_skills_dirs(): + if ext_dir not in all_dirs: + all_dirs.append(ext_dir) + except Exception: + pass + + for skills_dir in all_dirs: if not skills_dir.exists(): continue for skill_md in skills_dir.rglob("SKILL.md"):