Fix duplicated skills/ directory nesting in plugin pack#726
Fix duplicated skills/ directory nesting in plugin pack#726Ai-chan-0411 wants to merge 2 commits intomicrosoft:mainfrom
Conversation
When a dependency has virtual_path like "skills/javascript-typescript-jest", _collect_bare_skill used the full path as the slug, producing output at skills/skills/javascript-typescript-jest/SKILL.md instead of the expected skills/javascript-typescript-jest/SKILL.md. Strip the leading "skills/" from virtual_path before using it as the slug to prevent this double nesting. Fixes microsoft#719
|
@microsoft-github-policy-service agree |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Fixes incorrect skills/skills/... directory nesting when packing plugins that depend on virtual skills whose virtual_path already includes a skills/ prefix.
Changes:
- Strip a leading
skills/prefix fromvirtual_pathbefore using it as the output slug in_collect_bare_skill - Add a regression unit test to ensure
virtual_path="skills/..."produces a flatskills/...output structure
Show a summary per file
| File | Description |
|---|---|
src/apm_cli/bundle/plugin_exporter.py |
Normalizes virtual_path to avoid double skills/ nesting when building output paths |
tests/unit/test_plugin_exporter.py |
Adds regression coverage for virtual_path values starting with skills/ |
Copilot's findings
- Files reviewed: 2/2 changed files
- Comments generated: 2
| # Strip leading "skills/" to avoid double nesting (skills/skills/…) | ||
| # when virtual_path already contains the skills/ prefix. | ||
| if slug.startswith("skills/"): | ||
| slug = slug[len("skills/"):] |
There was a problem hiding this comment.
slug = slug[len("skills/"):] can leave an unintended leading slash if virtual_path contains multiple slashes (e.g. "skills//foo" becomes "/foo"), producing paths like "skills//foo/...". Consider normalizing by removing the prefix and then stripping leading slashes (or splitting into path segments and dropping an initial "skills" segment) so slug never starts with /.
| slug = slug[len("skills/"):] | |
| slug = slug[len("skills/"):].lstrip("/") |
| 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 any(r.startswith("skills/javascript-typescript-jest/") for r in rel_paths) |
There was a problem hiding this comment.
The test assertion is a bit loose (it only checks the directory prefix). Since this test creates only SKILL.md, asserting the exact expected relative path (e.g. equality against "skills/javascript-typescript-jest/SKILL.md") would make the regression test stricter and easier to interpret. Also, out: list = [] is very generic; if this file uses type hints elsewhere, annotating out with the expected element type would improve readability.
| 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 any(r.startswith("skills/javascript-typescript-jest/") for r in rel_paths) | |
| out: list[tuple[Path, str]] = [] | |
| _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"] |
…ath slug
When virtual_path contains multiple slashes (e.g. "skills//foo"),
the previous slice `slug[len("skills/"):]` would leave a leading
slash, producing an invalid path like "skills//foo/SKILL.md".
Added `.lstrip("/")` to handle this edge case.
Also tightened test assertions in TestCollectBareSkill to use exact
path matching instead of loose prefix checks, and added a dedicated
regression test for the double-slash scenario.
|
Thanks for the Copilot review — both points addressed in f75ad61: 1. Leading slash after slug = slug[len("skills/"):].lstrip("/")2. Loose assertion in assert rel_paths == ["skills/frontend-design/SKILL.md"]Applied the same tightening to All 66 tests pass locally. |
Description
When running
apm pack --format pluginwith a dependency whosevirtual_pathstarts withskills/(e.g.github/awesome-copilot/skills/javascript-typescript-jest), the bare skill collector (_collect_bare_skill) used the fullvirtual_pathas the slug. Since the function already prependsskills/to the output path, this producedskills/skills/javascript-typescript-jest/SKILL.mdinstead of the expectedskills/javascript-typescript-jest/SKILL.md.I ran into this while testing a plugin that depends on
github/awesome-copilot/skills/javascript-typescript-jest— the packed output had the skill files nested one level too deep, which meant the plugin host couldn't discover them.The fix strips the leading
skills/prefix fromvirtual_pathbefore using it as the directory slug, preventing the double nesting. Added a regression test that verifies virtual paths with askills/prefix produce the correct flat structure.Fixes #719
Type of change
Testing