feat: Promote sub-skills inside packages to top-level .github/skills/ entries#102
Merged
danielmeppiel merged 2 commits intomicrosoft:mainfrom Feb 24, 2026
Conversation
…tries Sub-skills placed under .apm/skills/<name>/SKILL.md are now promoted to independent top-level entries in .github/skills/<name>/ during install. This allows Copilot to discover them independently, since it only scans direct children of .github/skills/. Closes microsoft#101
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a new feature that promotes sub-skills from .apm/skills/ to top-level .github/skills/ entries during package installation. This addresses a limitation in VS Code's Copilot skill discovery, which only scans direct children of .github/skills/ and doesn't recurse into nested directories. The feature enables packages to bundle multiple independently-discoverable skills within a single installable unit.
Changes:
- Adds sub-skill promotion logic in
_integrate_native_skill()to copy sub-skills from.apm/skills/*to top-level.github/skills/and.claude/skills/entries - Updates
sync_integration()to include promoted sub-skills when determining which skills to preserve during cleanup - Adds comprehensive test coverage with 7 new tests covering promotion, name collisions, Claude integration, and orphan detection
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
src/apm_cli/integration/skill_integrator.py |
Implements sub-skill promotion in _integrate_native_skill() for both GitHub and Claude targets, and updates sync_integration() to preserve promoted sub-skills during cleanup |
tests/unit/integration/test_skill_integrator.py |
Adds 7 comprehensive tests covering sub-skill promotion, multiple sub-skills, invalid directories, name collisions, Claude integration, backward compatibility, and sync preservation |
docs/skills.md |
Documents the new Option 4 (Multi-skill Package) pattern and Sub-skill Promotion behavior with clear examples |
- Exclude .apm/ from parent copy to avoid redundant storage - Extract _promote_sub_skills() helper to eliminate duplication - Assert warning is emitted on sub-skill name collision - Add test for sub-skill name normalization (invalid → valid)
danielmeppiel
approved these changes
Feb 24, 2026
Collaborator
danielmeppiel
left a comment
There was a problem hiding this comment.
Great and needed implementation, tested corner cases locally and it works well, thank you!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🚀 New Feature
Description
Promote sub-skills inside packages to top-level
.github/skills/entries.When a package contains sub-skills under
.apm/skills/<name>/SKILL.md, APM now promotes each one to an independent top-level entry in.github/skills/<name>/during install. This allows Copilot to discover and invoke them independently.Rationale
VS Code's skill discovery contract scans
.github/skills/<name>/SKILL.md— direct children only, no recursion. Today, if a package ships sub-skills (e.g., a monorepo package with both modernization agents and an Azure naming skill), only the parent package'sSKILL.mdis discoverable. The sub-skills end up buried inside the parent tree and are invisible to the IDE.This creates a gap for packages that want to bundle multiple independently-useful skills within a single installable unit. The only workaround today is to split them into separate packages and install each one individually — which adds friction and fights the natural monorepo structure many teams use.
Opinionated design — open to discussion
This PR introduces an opinionated convention: sub-skills live under
.apm/skills/, matching the existing authoring namespace where all other primitives already reside (.apm/instructions/,.apm/agents/,.apm/prompts/). This is a design choice, not the only possible approach. Alternative patterns (e.g., top-levelskills/folder, manifest-driven declarations inapm.yml) could achieve similar results with different tradeoffs.This PR is meant to showcase the usage pattern and validate the concept. The convention and implementation details are subject to discussion.
Changes Made
skill_integrator.py— 56 lines added)test_skill_integrator.py— 7 new tests, 179 lines)docs/skills.md— Option 4 + Sub-skill Promotion section)Implementation details
_integrate_native_skill()— After the existingcopytreeto.github/skills/, scanspackage_path / ".apm" / "skills"for subdirectories containing aSKILL.md. Each qualifying sub-skill is copied to.github/skills/<sub-name>/(and.claude/skills/<sub-name>/when.claude/exists). Name collisions produce a warning and overwrite.sync_integration()— When building theinstalled_skill_namesset for orphan detection, also scans.apm/skills/in installed packages so promoted sub-skills aren't marked as orphaned duringapm deps sync.Key design decisions:
SKILL.mdare promoted — other files/dirs are ignoredif sub_skills_dir.is_dir()— packages without.apm/skills/behave identically to beforeTesting
agent-librarypackage containing.apm/skills/azure-naming/)test_sub_skill_promoted_to_top_leveltest_multiple_sub_skills_promotedtest_sub_skill_without_skill_md_not_promotedtest_sub_skill_name_collision_overwrites_with_warningtest_sub_skill_promoted_to_claude_skillstest_package_without_sub_skills_unchangedtest_sync_integration_preserves_promoted_sub_skillsChecklist
enhancementlabel to this PRFixes #101