apm install cannot install subdirectory (virtual) packages from generic git hosts (e.g. self-hosted GitLab). APM constructs an invalid clone URL by concatenating
the subdirectory path into the repository URL instead of cloning the base repository and extracting the subdirectory.
To Reproduce
- Host a git repository on a non-GitHub, non-ADO server (e.g. GitLab CE at git.example.com)
- Reference a subdirectory package in apm.yml:
dependencies:
apm:
- git: https://git.example.com/my-org/my-group/my-skills.git
path: dist/skill-a
ref: main
- Run
apm install.
Expected behavior
APM clones my-org/my-skills.git and extracts dist/skill-a via sparse-checkout (same as GitHub).
Actual behavior
APM attempts to clone a nonexistent repo at https://git.example.com/my-org/my-skills/dist/skill-a.
Root cause
_detect_virtual_package() in reference.py:542-553:
- GitHub: min_base_segments = 2 (always owner/repo) → correct split
- Generic hosts: min_base_segments = len(path_segments) → all segments absorbed into repo_url, virtual_path never detected
Compounded by a lossy round-trip: parse_from_dict() correctly separates repo/path → str() flattens them into one string → download_package() re-parses via
parse() and loses the boundary.
Suggested fixes
A. Preserve DependencyReference through the pipeline (no str round-trip)
B. Default min_base_segments = 2 for generic hosts with opt-in for deeper paths
C. Encode the repo/path boundary in str() serialization
Workaround
None via configuration. Manual clone + copy required.
Environment (please complete the following information):
- OS: MacOS
- Python Version: 3.14.3
- APM Version: 0.8.0 (af190ac)
- VSCode Version (if relevant): no
Logs
Installing dependencies from apm.yml...
Installing APM dependencies (5)...
Using apm.lock.yaml (2 locked dependencies)
✓ github.com/anthropics/skills/skills/skill-creator
✓ github.com/vercel-labs/agent-browser/skills/agent-browser
Generated apm.lock.yaml with 2 dependencies
Installed 2 APM dependencies
No MCP dependencies found in apm.yml
── Diagnostics ──
✗ 3 packages failed:
└─ ai/grandpa-s-skills/dist/brain-council — Failed to install git.example.com/ai/grandpa-s-skills/dist/brain-council#master: Failed to clone repository
ai/grandpa-s-skills/dist/brain-council using all available methods. For private repositories on git.example.com, configure SSH keys or a git credential helper. APM delegates
authentication to git for non-GitHub/ADO hosts. Last error: Cmd('git') failed due to: exit code(128)
cmdline: git clone -v --depth=1 --branch=master -- https://git.example.com/ai/grandpa-s-skills/dist/brain-council
/var/folders/2_/n6nrjf29389cnm07z_w22_z40000gn/T/tmp46_d2g94
stderr: 'Cloning into '/var/folders/2_/n6nrjf29389cnm07z_w22_z40000gn/T/tmp46_d2g94'...
remote: The project you were looking for could not be found or you don't have permission to view it.
fatal: repository 'https://git.example.com/ai/grandpa-s-skills/dist/brain-council.git/' not found
apm installcannot install subdirectory (virtual) packages from generic git hosts (e.g. self-hosted GitLab). APM constructs an invalid clone URL by concatenatingthe subdirectory path into the repository URL instead of cloning the base repository and extracting the subdirectory.
To Reproduce
apm install.Expected behavior
APM clones my-org/my-skills.git and extracts dist/skill-a via sparse-checkout (same as GitHub).
Actual behavior
APM attempts to clone a nonexistent repo at https://git.example.com/my-org/my-skills/dist/skill-a.
Root cause
_detect_virtual_package() in reference.py:542-553:
Compounded by a lossy round-trip: parse_from_dict() correctly separates repo/path → str() flattens them into one string → download_package() re-parses via
parse() and loses the boundary.
Suggested fixes
A. Preserve DependencyReference through the pipeline (no str round-trip)
B. Default min_base_segments = 2 for generic hosts with opt-in for deeper paths
C. Encode the repo/path boundary in str() serialization
Workaround
None via configuration. Manual clone + copy required.
Environment (please complete the following information):
Logs