Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/apm_cli/primitives/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,9 @@ def _extract_primitive_name(file_path: Path) -> str:

# For structured directories like .apm/chatmodes/name.chatmode.md
if (base_idx + 2 < len(path_parts) and
path_parts[base_idx + 1] in ['chatmodes', 'instructions', 'context', 'memory']):
path_parts[base_idx + 1] in ['chatmodes', 'instructions', 'context', 'memory', 'agents']):
basename = file_path.name
# Remove the double extension (.chatmode.md, .instructions.md, etc.)
# Remove the double extension (.chatmode.md, .instructions.md, .agent.md, etc.)
if basename.endswith('.chatmode.md'):
return basename.replace('.chatmode.md', '')
elif basename.endswith('.instructions.md'):
Expand All @@ -201,6 +201,8 @@ def _extract_primitive_name(file_path: Path) -> str:
return basename.replace('.context.md', '')
elif basename.endswith('.memory.md'):
return basename.replace('.memory.md', '')
elif basename.endswith('.agent.md'):
return basename.replace('.agent.md', '')
elif basename.endswith('.md'):
return basename.replace('.md', '')
except (ValueError, IndexError):
Expand Down
24 changes: 9 additions & 15 deletions tests/unit/primitives/test_discovery_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
🤖 Test Improver: automated AI assistant focused on improving test coverage.
"""

import os
import stat
import tempfile
import unittest
from pathlib import Path
Expand All @@ -16,7 +14,6 @@
_is_readable,
_is_under_directory,
_should_skip_directory,
discover_primitives,
find_primitive_files,
get_dependency_declaration_order,
scan_directory_with_source,
Expand Down Expand Up @@ -98,18 +95,17 @@ class TestExtractPrimitiveName(unittest.TestCase):
"""Tests for _extract_primitive_name with various path structures."""

def test_agent_md_in_apm_agents_dir(self):
"""Files in .apm/agents/ fall through to fallback (agents/ not in structured subdirs list).
The fallback strips .md giving 'myagent.agent'."""
"""Files in .apm/agents/ are treated as structured agent primitives.
The '.agent.md' suffix is stripped, yielding just the agent name."""
path = Path("/project/.apm/agents/myagent.agent.md")
name = _extract_primitive_name(path)
# 'agents' is not in the structured subdirs list, so fallback strips .md only
self.assertEqual(name, "myagent.agent")
self.assertEqual(name, "myagent")

def test_agent_md_in_github_agents_dir(self):
"""Files in .github/agents/ also fall through to fallback."""
""".github/agents/ is also treated as a structured agent primitive directory."""
path = Path("/project/.github/agents/reviewer.agent.md")
name = _extract_primitive_name(path)
self.assertEqual(name, "reviewer.agent")
self.assertEqual(name, "reviewer")

def test_instruction_in_structured_dir(self):
path = Path("/project/.apm/instructions/coding-style.instructions.md")
Expand Down Expand Up @@ -210,8 +206,8 @@ def test_no_skill_md_leaves_collection_empty(self):
_discover_local_skill(self.tmp, collection)
self.assertEqual(len(collection.skills), 0)

def test_unreadable_skill_md_warns_and_skips(self):
"""parse error on SKILL.md is caught, printed as warning, skipped."""
def test_parse_error_on_skill_md_warns_and_skips(self):
"""A parse error on SKILL.md is caught, printed as warning, and skipped."""
skill_path = Path(self.tmp) / "SKILL.md"
_write(skill_path, SKILL_CONTENT)
collection = PrimitiveCollection()
Expand Down Expand Up @@ -636,12 +632,10 @@ def test_readable_file_returns_true(self):
def test_unreadable_file_returns_false(self):
path = Path(self.tmp) / "test.md"
path.write_text("content")
os.chmod(path, 0o000)
try:
# Simulate unreadable file by forcing PermissionError when opening.
with patch("apm_cli.primitives.discovery.open", side_effect=PermissionError):
result = _is_readable(path)
self.assertFalse(result)
finally:
os.chmod(path, 0o644)

def test_binary_file_returns_false(self):
path = Path(self.tmp) / "test.md"
Expand Down