Skip to content
Open
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
5 changes: 5 additions & 0 deletions src/apm_cli/bundle/plugin_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ def _collect_bare_skill(
# Derive a slug: prefer virtual_path (e.g. "frontend-design"), else last
# segment of repo_url (e.g. "my-skill" from "owner/my-skill")
slug = (getattr(dep, "virtual_path", "") or "").strip("/")
# Strip leading "skills/" to avoid double nesting (skills/skills/…)
# when virtual_path already contains the skills/ prefix.
# Also strip any extra leading slashes (e.g. "skills//foo" → "foo").
if slug.startswith("skills/"):
slug = slug[len("skills/"):].lstrip("/")
if not slug:
slug = dep.repo_url.rsplit("/", 1)[-1] if dep.repo_url else "skill"
for f in sorted(install_path.iterdir()):
Expand Down
39 changes: 38 additions & 1 deletion tests/unit/test_plugin_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,44 @@ def test_virtual_path_used_as_slug(self, tmp_path):
)
out: list = []
_collect_bare_skill(tmp_path, dep, out)
assert any(r.startswith("skills/frontend-design/") for _, r in out)
rel_paths = [r for _, r in out]
assert rel_paths == ["skills/frontend-design/SKILL.md"]

def test_virtual_path_with_skills_prefix_no_double_nesting(self, tmp_path):
"""virtual_path starting with skills/ should not produce skills/skills/."""
from apm_cli.bundle.plugin_exporter import _collect_bare_skill

(tmp_path / "SKILL.md").write_text("# JS/TS Jest")
dep = LockedDependency(
repo_url="github/awesome-copilot",
resolved_commit="abc123",
depth=1,
virtual_path="skills/javascript-typescript-jest",
is_virtual=True,
)
out: list = []
_collect_bare_skill(tmp_path, dep, out)
rel_paths = [r for _, r in out]
# Should be skills/javascript-typescript-jest/SKILL.md, NOT skills/skills/...
assert rel_paths == ["skills/javascript-typescript-jest/SKILL.md"]

def test_virtual_path_with_skills_double_slash_no_leading_slash(self, tmp_path):
"""virtual_path like 'skills//foo' should produce skills/foo/..., not skills//foo/..."""
from apm_cli.bundle.plugin_exporter import _collect_bare_skill

(tmp_path / "SKILL.md").write_text("# Foo")
dep = LockedDependency(
repo_url="github/awesome-copilot",
resolved_commit="abc123",
depth=1,
virtual_path="skills//foo",
is_virtual=True,
)
out: list = []
_collect_bare_skill(tmp_path, dep, out)
rel_paths = [r for _, r in out]
assert rel_paths == ["skills/foo/SKILL.md"]
assert not any("//" in r for r in rel_paths)

def test_skips_when_no_skill_md(self, tmp_path):
"""No SKILL.md at root means nothing collected."""
Expand Down