Skip to content

Allow plugin component paths to reference shared directories within the marketplace repository #308775

@xAndreiLi

Description

@xAndreiLi

resolveComponentDirs enforces that all component paths (skills, agents, hooks, MCP servers) declared in a plugin manifest must resolve within the plugin's own directory. Any path that traverses outside using ../ is silently dropped by the isEqualOrParentresolved, pluginUri) check.

This is overly restrictive for marketplace repositories that contain multiple plugins. A common repository layout looks like:

my-marketplace/
├── skills/
│   └── shared-skill/
│       └── SKILL.md
├── plugins/
│   ├── plugin-a/
│   │   └── plugin.json    ← wants to reference ../../skills/shared-skill
│   └── plugin-b/
│       └── plugin.json    ← also wants to reference ../../skills/shared-skill

Today, both plugin-a and plugin-b cannot reference ../../skills/shared-skill because it resolves outside their plugin directories. The only workaround is to duplicate the skill into each plugin's own skills/ folder — which defeats the purpose of a shared repository layout.

This is especially problematic for marketplaces that keep skills in a root-level skills/ directory so they can be discovered by npx skills. Authors are forced to maintain duplicate copies of every skill: one at the repository root for npx skills discovery, and one inside each plugin for VS Code to find. This duplication is error-prone, increases maintenance burden, and diverges from the conventions of the broader skills ecosystem.

Proposed Solution

Completed PR

Relax the containment boundary for marketplace plugins from the plugin directory to the repository root. The repository root is already a known, trusted boundary. IAgentPluginRepositoryService.getRepositoryUri() computes it, and plugin installation already validates that plugin directories stay within it.

Concretely:

Add an optional boundaryUri parameter to resolveComponentDirs (defaulting to pluginUri for backward compatibility)
For marketplace-discovered plugins, pass the repository URI as the boundary
Non-marketplace plugins (configured, extension-contributed) continue using the plugin directory as the boundary
This allows marketplace plugins to declare paths like ../../skills/shared-skill that resolve outside the plugin but stay within the repository — while still preventing traversal to arbitrary filesystem locations.

Benefits

Eliminates forced duplication of shared components across plugins in the same repository
Aligns with the npx skills convention of keeping skills at the repository root
Backward compatible — only marketplace plugins get the relaxed boundary; all other plugin types retain the current behavior
Maintains security — paths that escape the repository root are still rejected

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions