"""Integration tests for the skills system.""" from pathlib import Path import pytest from crewai import Agent, Crew, Task from crewai.crews.utils import _resolve_crew_skills from crewai.skills.loader import ( activate_skill, discover_skills, format_skill_context, ) from crewai.skills.models import INSTRUCTIONS, METADATA from crewai.utilities.prompts import Prompts def _create_skill_dir(parent: Path, name: str, body: str = "Body.") -> Path: """Helper to create a skill directory with SKILL.md.""" skill_dir = parent / name skill_dir.mkdir() (skill_dir / "SKILL.md").write_text( f"---\nname: {name}\ndescription: Skill {name}\n---\n{body}" ) return skill_dir class TestSkillDiscoveryAndActivation: """End-to-end tests for discover + activate workflow.""" def test_discover_and_activate(self, tmp_path: Path) -> None: _create_skill_dir(tmp_path, "my-skill", body="Use this skill.") skills = discover_skills(tmp_path) assert len(skills) == 1 assert skills[0].disclosure_level == METADATA activated = activate_skill(skills[0]) assert activated.disclosure_level == INSTRUCTIONS assert activated.instructions == "Use this skill." context = format_skill_context(activated) assert '' in context assert "Use this skill." in context def test_filter_by_skill_names(self, tmp_path: Path) -> None: _create_skill_dir(tmp_path, "alpha") _create_skill_dir(tmp_path, "beta") _create_skill_dir(tmp_path, "gamma") all_skills = discover_skills(tmp_path) wanted = {"alpha", "gamma"} filtered = [s for s in all_skills if s.name in wanted] assert {s.name for s in filtered} == {"alpha", "gamma"} def test_full_fixture_skill(self) -> None: fixtures = Path(__file__).parent / "fixtures" valid_dir = fixtures / "valid-skill" if not valid_dir.exists(): pytest.skip("Fixture not found") skills = discover_skills(fixtures) valid_skills = [s for s in skills if s.name == "valid-skill"] assert len(valid_skills) == 1 skill = valid_skills[0] assert skill.frontmatter.license == "Apache-2.0" assert skill.frontmatter.allowed_tools == ["web-search", "file-read"] activated = activate_skill(skill) assert "Instructions" in (activated.instructions or "") def test_multiple_search_paths(self, tmp_path: Path) -> None: path_a = tmp_path / "a" path_a.mkdir() _create_skill_dir(path_a, "skill-a") path_b = tmp_path / "b" path_b.mkdir() _create_skill_dir(path_b, "skill-b") all_skills = [] for search_path in [path_a, path_b]: all_skills.extend(discover_skills(search_path)) names = {s.name for s in all_skills} assert names == {"skill-a", "skill-b"} def test_agent_preserves_metadata_for_discovered_skills(self, tmp_path: Path) -> None: _create_skill_dir(tmp_path, "travel", body="Use this skill for travel planning.") discovered = discover_skills(tmp_path) agent = Agent( role="Travel Advisor", goal="Provide personalized travel suggestions.", backstory="An experienced travel consultant.", skills=discovered, ) assert agent.skills is not None assert agent.skills[0].disclosure_level == METADATA assert agent.skills[0].instructions is None result = Prompts(agent=agent, has_tools=False, use_system_prompt=True).task_execution() system = getattr(result, "system", "") or result.prompt assert '' in system assert "Skill travel" in system # METADATA-level skills must not leak full instructions into the prompt assert "Use this skill for travel planning." not in system def test_agent_accepts_inline_skill_string(self) -> None: agent = Agent( role="Reviewer", goal="Review changes.", backstory="An experienced reviewer.", skills=[ "---\n" "name: inline-review\n" "description: Inline review guidance\n" "---\n" "Focus on behavior and missing tests." ], ) assert agent.skills is not None assert [skill.name for skill in agent.skills] == ["inline-review"] assert [skill.disclosure_level for skill in agent.skills] == [INSTRUCTIONS] assert [skill.instructions for skill in agent.skills] == [ "Focus on behavior and missing tests." ] result = Prompts(agent=agent, has_tools=False, use_system_prompt=True).task_execution() system = getattr(result, "system", "") or result.prompt assert '' in system assert "Focus on behavior and missing tests." in system def test_agent_treats_plain_skill_string_as_path(self, tmp_path: Path) -> None: _create_skill_dir(tmp_path, "path-skill", body="Use the path skill.") agent = Agent( role="Reviewer", goal="Review changes.", backstory="An experienced reviewer.", skills=[str(tmp_path)], ) assert agent.skills is not None assert [skill.name for skill in agent.skills] == ["path-skill"] assert [skill.instructions for skill in agent.skills] == ["Use the path skill."] def test_crew_resolves_inline_skill_string(self) -> None: agent = Agent( role="Reviewer", goal="Review changes.", backstory="An experienced reviewer.", ) task = Task( description="Review the diff.", expected_output="Findings.", agent=agent, ) crew = Crew( agents=[agent], tasks=[task], skills=[ "---\n" "name: crew-inline-review\n" "description: Crew-level inline review guidance\n" "---\n" "Apply this to every agent." ], ) skills = _resolve_crew_skills(crew) assert skills is not None assert [skill.name for skill in skills] == ["crew-inline-review"] assert [skill.instructions for skill in skills] == ["Apply this to every agent."] def test_crew_activates_preloaded_metadata_skill(self, tmp_path: Path) -> None: _create_skill_dir( tmp_path, "crew-preloaded", body="Apply this crew-level guidance to every agent.", ) metadata_skill = discover_skills(tmp_path)[0] agent = Agent( role="Reviewer", goal="Review changes.", backstory="An experienced reviewer.", ) task = Task( description="Review the diff.", expected_output="Findings.", agent=agent, ) crew = Crew( agents=[agent], tasks=[task], skills=[metadata_skill], ) skills = _resolve_crew_skills(crew) assert skills is not None assert [skill.name for skill in skills] == ["crew-preloaded"] assert [skill.disclosure_level for skill in skills] == [INSTRUCTIONS] assert [skill.instructions for skill in skills] == [ "Apply this crew-level guidance to every agent." ]