| 1 | from pathlib import Path |
| 2 | |
| 3 | from helpers import projects, subagents |
| 4 | |
| 5 | |
| 6 | def _write_profile(root: Path, name: str, metadata: str = "") -> Path: |
| 7 | profile = root / name |
| 8 | profile.mkdir(parents=True) |
| 9 | if metadata: |
| 10 | (profile / "agent.yaml").write_text(metadata, encoding="utf-8") |
| 11 | return profile |
| 12 | |
| 13 | |
| 14 | def test_missing_metadata_inherits_and_present_empty_clears(tmp_path: Path) -> None: |
| 15 | bundled_root = tmp_path / "agents" |
| 16 | user_root = tmp_path / "usr-agents" |
| 17 | _write_profile( |
| 18 | bundled_root, |
| 19 | "researcher", |
| 20 | "title: Researcher\ndescription: Source heavy\ncontext: Delegate research\n", |
| 21 | ) |
| 22 | _write_profile(user_root, "researcher", "description: ''\ncontext: ''\n") |
| 23 | |
| 24 | bundled = subagents._load_agent_data_from_dir( |
| 25 | str(bundled_root), "researcher", "default" |
| 26 | ) |
| 27 | user = subagents._load_agent_data_from_dir( |
| 28 | str(user_root), "researcher", "user" |
| 29 | ) |
| 30 | merged = subagents._merge_agent(bundled, user) |
| 31 | |
| 32 | assert merged is not None |
| 33 | assert merged.title == "Researcher" |
| 34 | assert merged.description == "" |
| 35 | assert merged.context == "" |
| 36 | assert merged.origin == ["default", "user"] |
| 37 | |
| 38 | |
| 39 | def test_prompt_only_override_does_not_clear_metadata(tmp_path: Path) -> None: |
| 40 | bundled_root = tmp_path / "agents" |
| 41 | user_root = tmp_path / "usr-agents" |
| 42 | _write_profile( |
| 43 | bundled_root, |
| 44 | "researcher", |
| 45 | "title: Researcher\ndescription: Source heavy\ncontext: Delegate research\n", |
| 46 | ) |
| 47 | profile = _write_profile(user_root, "researcher") |
| 48 | prompts = profile / "prompts" |
| 49 | prompts.mkdir() |
| 50 | (prompts / "agent.system.main.specifics.md").write_text( |
| 51 | "Only this changes.\n", encoding="utf-8" |
| 52 | ) |
| 53 | |
| 54 | merged = subagents._merge_agent( |
| 55 | subagents._load_agent_data_from_dir( |
| 56 | str(bundled_root), "researcher", "default" |
| 57 | ), |
| 58 | subagents._load_agent_data_from_dir( |
| 59 | str(user_root), "researcher", "user" |
| 60 | ), |
| 61 | ) |
| 62 | |
| 63 | assert merged is not None |
| 64 | assert merged.title == "Researcher" |
| 65 | assert merged.description == "Source heavy" |
| 66 | assert merged.context == "Delegate research" |
| 67 | assert merged.prompts == { |
| 68 | "agent.system.main.specifics.md": "Only this changes.\n" |
| 69 | } |
| 70 | |
| 71 | |
| 72 | def test_nonexistent_layer_is_not_an_override(tmp_path: Path) -> None: |
| 73 | assert ( |
| 74 | subagents._load_agent_data_from_dir(str(tmp_path), "missing", "user") |
| 75 | is None |
| 76 | ) |
| 77 | |
| 78 | |
| 79 | def test_bundled_directory_without_definition_is_not_a_profile(tmp_path: Path) -> None: |
| 80 | root = tmp_path / "agents" |
| 81 | _write_profile(root, "_example") |
| 82 | |
| 83 | assert subagents._get_agents_list_from_dir(str(root), "default") == {} |
| 84 | |
| 85 | |
| 86 | def test_default_specifics_uses_only_the_canonical_prompt_path() -> None: |
| 87 | root = Path(__file__).resolve().parents[1] |
| 88 | legacy = root / "agents" / "default" / "agent.system.main.specifics.md" |
| 89 | canonical = ( |
| 90 | root |
| 91 | / "agents" |
| 92 | / "default" |
| 93 | / "prompts" |
| 94 | / "agent.system.main.specifics.md" |
| 95 | ) |
| 96 | |
| 97 | assert not legacy.exists() |
| 98 | assert canonical.is_file() |
| 99 | assert canonical.read_bytes() == b"" |
| 100 | |
| 101 | |
| 102 | def test_available_agents_include_project_profiles_and_project_overrides( |
| 103 | monkeypatch, |
| 104 | ) -> None: |
| 105 | requested = [] |
| 106 | monkeypatch.setattr( |
| 107 | subagents, |
| 108 | "get_agents_dict", |
| 109 | lambda project_name=None: requested.append(project_name) or { |
| 110 | "_example": subagents.SubAgentListItem( |
| 111 | name="_example", enabled=True |
| 112 | ), |
| 113 | "default": subagents.SubAgentListItem( |
| 114 | name="default", enabled=False |
| 115 | ), |
| 116 | "global": subagents.SubAgentListItem(name="global", enabled=True), |
| 117 | "project-only": subagents.SubAgentListItem( |
| 118 | name="project-only", enabled=True |
| 119 | ), |
| 120 | }, |
| 121 | ) |
| 122 | monkeypatch.setattr( |
| 123 | projects, |
| 124 | "load_project_subagents", |
| 125 | lambda _name: { |
| 126 | "default": {"enabled": False}, |
| 127 | "global": {"enabled": False}, |
| 128 | }, |
| 129 | ) |
| 130 | |
| 131 | available = subagents.get_available_agents_dict("demo") |
| 132 | |
| 133 | assert requested == ["demo"] |
| 134 | assert list(available) == ["project-only"] |
| 135 | |
| 136 | |
| 137 | def test_all_agents_list_omits_default_utility_profile( |
| 138 | tmp_path: Path, monkeypatch |
| 139 | ) -> None: |
| 140 | root = tmp_path / "agents" |
| 141 | _write_profile(root, "default", "title: Default\n") |
| 142 | _write_profile(root, "agent0", "title: Agent 0\n") |
| 143 | monkeypatch.setattr(subagents, "get_agents_roots", lambda: [str(root)]) |
| 144 | |
| 145 | assert "default" in subagents._get_agents_list_from_dir(str(root), "default") |
| 146 | assert subagents.get_all_agents_list() == [{"key": "agent0", "label": "Agent 0"}] |