Skip to content

feat: Marketplace integration -- read marketplace.json for plugin discovery + governance - #503

Merged
Daniel Meppiel (danielmeppiel) merged 16 commits into
mainfrom
copilot/feat-marketplace-integration
Mar 31, 2026
Merged

feat: Marketplace integration -- read marketplace.json for plugin discovery + governance#503
Daniel Meppiel (danielmeppiel) merged 16 commits into
mainfrom
copilot/feat-marketplace-integration

Conversation

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Description

APM now reads existing marketplace.json files (both Copilot CLI and Claude Code formats) for plugin discovery, resolves plugins to Git URLs, then applies its full governance layer (lockfile, SHA pinning, audit trail). Collapses a two-tool workflow into one.

New module: src/apm_cli/marketplace/

  • models.py -- Frozen dataclasses (MarketplaceSource, MarketplacePlugin, MarketplaceManifest) + JSON parser for both Copilot CLI and Claude Code formats
  • errors.py -- Actionable error hierarchy with next-step commands in messages
  • registry.py -- CRUD for ~/.apm/marketplaces.json with process-lifetime cache, atomic writes
  • client.py -- GitHub Contents API fetch via AuthResolver.try_with_fallback(unauth_first=True), 1h TTL cache, stale-while-revalidate, auto-detect marketplace.json location
  • resolver.py -- NAME@MARKETPLACE regex detection + resolution for 4 source types (github, url, git-subdir, relative; npm rejected with clear message)

Install hook (install.py)

  • Pre-parse intercept before DependencyReference.parse() detects NAME@MARKETPLACE via ^[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+$ (no /, no :)
  • Resolves to canonical owner/repo#ref, replaces package variable, stores provenance
  • Backward-compatible grammar extension -- these inputs previously raised ValueError

CLI commands (commands/marketplace.py + cli.py)

  • apm marketplace add/list/browse/update/remove
  • apm search QUERY (top-level, across all registered marketplaces)

Lockfile provenance (lockfile.py)

  • Two optional fields on LockedDependency: discovered_via, marketplace_plugin_name
  • Backward compatible -- None by default, omitted from YAML when unset
# Register a marketplace
apm marketplace add acme-org/plugin-marketplace
# Browse and install
apm marketplace browse acme-plugins
apm install security-checks@acme-plugins
# -> Resolves to acme-org/security-plugin#v1.3.0, full governance applies

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Maintenance / refactor

Testing

  • Tested locally
  • All existing tests pass
  • Added tests for new functionality (if applicable)

114 unit tests across 8 files: models, resolver (20 regex positive/negative cases + 4 source types), client (cache TTL, stale-while-revalidate, auto-detect), registry (CRUD + persistence), CLI commands (CliRunner), lockfile provenance (round-trip + backward compat), install integration, error hierarchy. Full suite: 3295 passed.

CopilotAIand others added 5 commits March 31, 2026 04:55
Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12a9b016-7930-41b8-a340-c64f11486b71
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
- Add marketplace/ package: models, errors, registry, client, resolver
- Add marketplace CLI commands: add, list, browse, update, remove, search
- Add lockfile provenance fields: discovered_via, marketplace_plugin_name
- Add install hook for NAME@MARKETPLACE syntax pre-parse intercept
- Wire marketplace commands in cli.py
Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12a9b016-7930-41b8-a340-c64f11486b71
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
- Create guides/marketplaces.md covering marketplace concepts,
registration, browsing, search, install syntax, provenance tracking,
and cache behavior
- Add apm marketplace and apm search command sections to cli-commands.md
- Update apm install arguments to include NAME@MARKETPLACE syntax
- Update plugins.md Finding Plugins section with marketplace cross-refs
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
…mplementation
- Use array-based plugins format matching models.py parser expectations
- Use discovered_via and marketplace_plugin_name matching lockfile.py fields
- Document both Copilot CLI (repository/ref) and Claude Code (source) formats
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
- git-subdir uses separate repo and subdir fields
- Relative string sources resolve to marketplace repo subdirectory
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
@danielmeppielDaniel Meppiel (danielmeppiel) added the enhancement Deprecated: use type/feature. Kept for issue history; will be removed in milestone 0.10.0. label Mar 31, 2026
CopilotAIand others added 2 commits March 31, 2026 05:21
- 114 unit tests across 8 test files covering all marketplace modules
- New marketplace guide at docs/src/content/docs/guides/marketplaces.md
- Updated CLI reference with marketplace and search commands
- Updated plugins guide with marketplace integration section
- CHANGELOG entry for marketplace feature
Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12a9b016-7930-41b8-a340-c64f11486b71
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
- Use List[MarketplacePlugin] from typing instead of lowercase generic
- Eliminate duplicated condition in install.py marketplace intercept
- Restructure control flow for clarity
Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12a9b016-7930-41b8-a340-c64f11486b71
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
CopilotAI changed the title [WIP] Add marketplace integration to read marketplace.json for pluginsfeat: Marketplace integration -- read marketplace.json for plugin discovery + governanceMar 31, 2026
@danielmeppiel
Daniel Meppiel (danielmeppiel) marked this pull request as ready for review March 31, 2026 06:51
CopilotAI review requested due to automatic review settings March 31, 2026 06:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a first-class "marketplace" feature to APM: it can discover plugins from marketplace.json (Copilot CLI + Claude Code formats), resolve them into Git dependencies, and then apply the existing governance flow (locking, SHA pinning, provenance).

Changes:

  • Introduces src/apm_cli/marketplace/ (models/parser, registry persistence, GitHub fetch + caching, and resolver for NAME@MARKETPLACE).
  • Adds CLI surface area: apm marketplace add/list/browse/update/remove and top-level apm search.
  • Extends install + lockfile to track marketplace provenance (discovered_via, marketplace_plugin_name) and documents the workflow.

Reviewed changes

Copilot reviewed 23 out of 24 changed files in this pull request and generated 12 comments.

Show a summary per file
FileDescription
tests/unit/marketplace/init.pyAdds marketplace unit test package.
tests/unit/marketplace/test_lockfile_provenance.pyTests new lockfile provenance fields serialization/back-compat.
tests/unit/marketplace/test_marketplace_client.pyTests marketplace fetch/caching behavior and auto-detection logic.
tests/unit/marketplace/test_marketplace_commands.pyTests new CLI commands using Click CliRunner.
tests/unit/marketplace/test_marketplace_errors.pyTests actionable marketplace error messages.
tests/unit/marketplace/test_marketplace_install_integration.pyTests marketplace ref detection and _ValidationOutcome provenance field presence.
tests/unit/marketplace/test_marketplace_models.pyTests dataclasses and parsing for both marketplace.json formats.
tests/unit/marketplace/test_marketplace_registry.pyTests registry CRUD + persistence and corrupted file handling.
tests/unit/marketplace/test_marketplace_resolver.pyTests NAME@MARKETPLACE regex and source-type resolution.
src/apm_cli/cli.pyRegisters marketplace command group and top-level search.
src/apm_cli/commands/install.pyAdds marketplace pre-parse intercept; threads provenance into the install engine and lockfile generation.
src/apm_cli/commands/marketplace.pyImplements apm marketplace ... and apm search commands.
src/apm_cli/core/command_logger.pyExtends _ValidationOutcome to carry marketplace_provenance.
src/apm_cli/deps/lockfile.pyAdds provenance fields to LockedDependency + (de)serialization.
src/apm_cli/marketplace/init.pyExposes marketplace public API symbols.
src/apm_cli/marketplace/client.pyImplements GitHub Contents API fetch, TTL cache, and path auto-detection.
src/apm_cli/marketplace/errors.pyAdds marketplace-specific exception hierarchy with next-step guidance.
src/apm_cli/marketplace/models.pyAdds frozen dataclasses + parser for Copilot/Claude marketplace.json.
src/apm_cli/marketplace/registry.pyAdds persistent registry in ~/.apm/marketplaces.json with caching + atomic writes.
src/apm_cli/marketplace/resolver.pyAdds NAME@MARKETPLACE parsing and plugin source-to-canonical resolution.
docs/src/content/docs/guides/marketplaces.mdNew guide page describing marketplace concepts and workflows.
docs/src/content/docs/guides/plugins.mdUpdates plugin discovery guidance to include marketplaces.
docs/src/content/docs/reference/cli-commands.mdDocuments new commands and NAME@MARKETPLACE install syntax.
CHANGELOG.mdAdds Unreleased "Added" entries describing the feature.

Comment threadsrc/apm_cli/commands/install.py
Comment threadsrc/apm_cli/commands/install.py Outdated
Comment threadsrc/apm_cli/marketplace/resolver.py Outdated
Comment threadsrc/apm_cli/marketplace/resolver.py Outdated
Comment threadsrc/apm_cli/marketplace/client.py
Comment threadsrc/apm_cli/marketplace/models.py Outdated
Comment threadsrc/apm_cli/marketplace/client.py Outdated
Comment threadsrc/apm_cli/commands/marketplace.py
Comment threaddocs/src/content/docs/reference/cli-commands.md
Comment threaddocs/src/content/docs/guides/marketplaces.md Outdated
- Narrow except Exception to except ImportError for lazy marketplace import (comment #1)
- Fix provenance key mismatch: use dep identity instead of canonical for lockfile lookup (comment #2)
- Include subdir in git-subdir source resolution with path traversal validation (comment #3)
- Include relative path in relative source resolution with traversal validation (comment #4)
- Sanitize marketplace name in cache file paths to prevent path traversal (comment #5)
- Fix docs: stale-if-error, not stale-while-revalidate (comment #6)
- Consolidate CHANGELOG entries into single line with (#503) (comment #7)
- Remove unused _SUPPORTED_SOURCE_TYPES set (comment #8)
- Let auth errors propagate in _auto_detect_path instead of swallowing (comment #9)
- Validate marketplace --name against [a-zA-Z0-9._-]+ charset (comment #10)
- Fix doc examples to use identifier-compatible names (comments #11, #12)
- Update tests to match corrected resolver behavior, add traversal tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel

Copy link
Copy Markdown
Collaborator

Addressed all 12 review comments in commit e814a88:

Code fixes:

  1. install.py L148 -- Narrowed except Exception to except ImportError so only missing marketplace module is silenced; real errors propagate.
  2. install.py L2262 -- Fixed provenance key mismatch: provenance dict is now keyed by identity (owner/repo) instead of canonical (owner/repo#ref), matching how lockfile.dependencies is keyed.
  3. resolver.py L90 -- _resolve_git_subdir_source() now includes subdir in the resolved string (e.g., owner/repo/packages/plugin-a#ref), with path traversal validation rejecting `.." segments.
  4. resolver.py L98 -- _resolve_relative_source() now includes the relative path as a virtual subdir (e.g., owner/repo/plugins/my-plugin), with path traversal validation.
  5. client.py L48 -- Added _sanitize_cache_name() that strips non-[a-zA-Z0-9._-] characters from marketplace names before use in file paths, preventing directory traversal.
  6. marketplaces.md L128 -- Updated docs: "stale-while-revalidate" changed to "stale-if-error" to accurately describe the blocking fetch + expired-cache fallback behavior.
  7. CHANGELOG.md L15 -- Consolidated 3 entries into a single line with (#503) per Keep a Changelog convention.
  8. models.py L109 -- Removed unused _SUPPORTED_SOURCE_TYPES set.
  9. client.py L173 -- Removed try/except from _auto_detect_path() entirely. Since _fetch_file() already returns None for 404s, any MarketplaceFetchError is a non-404 failure (auth, network) and should propagate.
  10. marketplace.py L57 -- Added name validation in add command: display names must match [a-zA-Z0-9._-]+ for compatibility with plugin@marketplace syntax.
  11. cli-commands.md L871 -- Changed example from --name "Acme Plugins" to --name acme-plugins.
  12. marketplaces.md L65 -- Same fix as fix: pass env dict to setup_runtime_environment to preserve CI tokens #11.

Tests: Updated resolver test expectations to match corrected behavior, added path traversal rejection tests. All 3314 unit tests pass.

Bug #1 - Format incompatibility with awesome-copilot marketplace:
- Parser now accepts 'source' key (Copilot CLI) as type discriminator
fallback when 'type' key is absent, normalizing to 'type' for resolvers
- GitHub source resolver now accepts 'path' field (Copilot CLI) as
virtual subdirectory, same as 'subdir' in git-subdir sources
- Path traversal validation applied to 'path' field
- Fixes: 8 of 62 plugins in awesome-copilot that use github source
objects with 'source'+'path' keys instead of 'type'+'subdir'
Bug #2 - Lockfile provenance never written:
- Root cause: install passed raw marketplace refs (NAME@MARKETPLACE)
as only_packages, but DependencyReference.parse() can't parse those,
so identity filtering removed all deps -> 'already installed'
- Fix: use validated_packages (canonical owner/repo strings) instead
of raw click argument for only_pkgs
Both bugs verified fixed via E2E tests against real marketplaces:
- github/awesome-copilot (62 plugins)
- anthropics/skills (3 plugins)
- microsoft/azure-skills (1 plugin)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel

Copy link
Copy Markdown
Collaborator

E2E Testing Against Real Marketplaces (commit ac1afbb)

Tested all marketplace commands against 3 real-world marketplaces:

MarketplacePluginsFormatPath
github/awesome-copilot62Copilot CLI.github/plugin/marketplace.json
anthropics/skills3Claude Code.claude-plugin/marketplace.json
microsoft/azure-skills1Claude Code.claude-plugin/marketplace.json

Results: 25/25 PASS (after fixes)

PhaseTestsStatus
Registration (add/list/name validation)6/6All PASS
Browse & Search (cross-marketplace)6/6All PASS
Install (NAME@MARKETPLACE)5/5All PASS (after 2 bug fixes)
Update & Remove4/4All PASS
Error Cases3/3All PASS

Bugs Found & Fixed

Bug #1 (P1) — Copilot CLI format incompatibility: awesome-copilot uses "source": "github" (not "type": "github") and "path" (not "subdir") in its source dicts. Parser now handles both key names with normalization. Affects 8/62 plugins that use external github sources.

Bug #2 (P2) — Provenance never written: apm install NAME@MARKETPLACE passed raw marketplace refs as only_packages, which DependencyReference.parse() couldn't parse, causing identity filter to remove all deps (deps_to_install empty → "already installed"). Fix: use validated_packages (canonical strings) instead of raw click args. Lockfile now correctly shows discovered_via and marketplace_plugin_name.

Verified Post-Fix

# apm.lock.yaml now contains:discovered_via: awesome-copilotmarketplace_plugin_name: azure

Full report: WIP/e2e-marketplace-test-report.md

@danielmeppiel

Copy link
Copy Markdown
Collaborator

E2E Marketplace Testing Report

Ran 25 test scenarios against 3 official marketplaces using the branch binary built from source.

Marketplaces Tested

MarketplaceRepoFormatPlugins
Awesome Copilotgithub/awesome-copilotCopilot CLI ("source" key)62 plugins
Anthropic Skillsanthropics/skillsClaude Code (relative sources)3 plugins
Azure Skillsmicrosoft/azure-skillsClaude Code (relative sources)1 plugin

Commands Tested & Results

Phase 1: Setup

#CommandResult
1apm initPASS
2apm marketplace list (empty state)PASS

Phase 2: Registration (apm marketplace add)

#CommandResult
3apm marketplace add awesome-copilot github/awesome-copilotPASS -- auto-detected .github/plugin/marketplace.json
4apm marketplace add skills anthropics/skillsPASS -- auto-detected .claude-plugin/marketplace.json
5apm marketplace add azure-skills microsoft/azure-skillsPASS -- auto-detected .claude-plugin/marketplace.json
6apm marketplace list (3 registered)PASS
7apm marketplace add awesome-copilot github/awesome-copilot (duplicate)PASS -- clear error
8apm marketplace add bad ../traversal (invalid name)PASS -- rejected

Phase 3: Browse & Search (apm marketplace browse/search)

#CommandResult
9apm marketplace browse awesome-copilotPASS -- 62 plugins listed
10apm marketplace browse skillsPASS -- 3 plugins listed
11apm marketplace browse azure-skillsPASS -- 1 plugin listed
12apm marketplace search azure (cross-marketplace)PASS -- results from multiple marketplaces
13apm marketplace search nonexistent-xyzPASS -- empty results, no crash
14apm marketplace browse unknownPASS -- clear error message

Phase 4: Install (apm install NAME@MARKETPLACE)

#CommandResult
15apm install azure@azure-skillsPASS -- installed, provenance in lockfile
16apm install sequential-thinking@skillsPASS -- installed from Anthropic
17apm install azure@awesome-copilotPASS -- Copilot CLI format resolved correctly
18apm install unknown@skillsPASS -- clear "not found" error
19apm install thing@nonexistentPASS -- clear "marketplace not registered" error

Phase 5: Update & Remove (apm marketplace remove)

#CommandResult
20apm marketplace remove azure-skillsPASS
21apm marketplace list (2 remaining)PASS
22apm marketplace remove azure-skills (already removed)PASS -- clear error
23apm marketplace remove skills && apm marketplace remove awesome-copilotPASS

Phase 6: Lockfile & Provenance Verification

#CommandResult
24Verify apm.lock contains discovered_via and marketplace_plugin_namePASS
25Verify provenance keyed by dependency identity (not canonical)PASS

Summary: 25/25 PASS

Two bugs were discovered during initial testing and fixed in commit ac1afbb:

  1. Copilot CLI format compatibility -- awesome-copilot uses "source": "github" + "path" keys instead of "type" + "subdir". Parser now handles both formats.
  2. Provenance not written to lockfile -- raw NAME@MARKETPLACE args could not be parsed by DependencyReference.parse(), causing identity mismatch. Fixed by using validated canonical package strings.

Both fixes have unit tests. Full suite: 3320 tests passing.

Search now requires QUERY@MARKETPLACE (e.g. apm search security@skills)
to eliminate name collisions across marketplaces. Added search_marketplace()
client function for single-marketplace search.
- Rejects bare queries without @ — clear error with usage example
- Validates marketplace exists before searching
- Updated docs/guides/marketplaces.md with new syntax
- 7 test cases: format validation, unknown marketplace, results, no results
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align all documentation with QUERY@MARKETPLACE search format.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace 3 ad-hoc '..' in x.split('/') checks in marketplace/resolver.py
with validate_path_segments() from utils/path_security.py. Add
defense-in-depth validate_path_segments() call to _sanitize_cache_name()
in client.py.
This ensures marketplace code uses the same cross-platform path safety
utilities (backslash normalization, single-dot rejection) as the rest
of APM.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Directs contributors to use validate_path_segments() and
ensure_path_within() from utils/path_security.py instead of
ad-hoc traversal checks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel
Daniel Meppiel (danielmeppiel) merged commit 6bf1b65 into mainMar 31, 2026
8 checks passed
@danielmeppiel
Daniel Meppiel (danielmeppiel) deleted the copilot/feat-marketplace-integration branch March 31, 2026 11:33
Sergio Sisternes (sergio-sisternes-epam) pushed a commit that referenced this pull request May 19, 2026
…covery + governance (#503)
* Initial plan
* Initial plan for marketplace integration
Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12a9b016-7930-41b8-a340-c64f11486b71
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
* feat: marketplace integration core implementation
- Add marketplace/ package: models, errors, registry, client, resolver
- Add marketplace CLI commands: add, list, browse, update, remove, search
- Add lockfile provenance fields: discovered_via, marketplace_plugin_name
- Add install hook for NAME@MARKETPLACE syntax pre-parse intercept
- Wire marketplace commands in cli.py
Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12a9b016-7930-41b8-a340-c64f11486b71
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
* docs: add marketplace integration guide and CLI reference
- Create guides/marketplaces.md covering marketplace concepts,
registration, browsing, search, install syntax, provenance tracking,
and cache behavior
- Add apm marketplace and apm search command sections to cli-commands.md
- Update apm install arguments to include NAME@MARKETPLACE syntax
- Update plugins.md Finding Plugins section with marketplace cross-refs
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
* docs: fix marketplace.json format and lockfile field names to match implementation
- Use array-based plugins format matching models.py parser expectations
- Use discovered_via and marketplace_plugin_name matching lockfile.py fields
- Document both Copilot CLI (repository/ref) and Claude Code (source) formats
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
* docs: fix git-subdir and relative source descriptions to match resolver
- git-subdir uses separate repo and subdir fields
- Relative string sources resolve to marketplace repo subdirectory
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
* feat: add marketplace unit tests and docs
- 114 unit tests across 8 test files covering all marketplace modules
- New marketplace guide at docs/src/content/docs/guides/marketplaces.md
- Updated CLI reference with marketplace and search commands
- Updated plugins guide with marketplace integration section
- CHANGELOG entry for marketplace feature
Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12a9b016-7930-41b8-a340-c64f11486b71
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
* refactor: address code review feedback
- Use List[MarketplacePlugin] from typing instead of lowercase generic
- Eliminate duplicated condition in install.py marketplace intercept
- Restructure control flow for clarity
Agent-Logs-Url: https://github.com/microsoft/apm/sessions/12a9b016-7930-41b8-a340-c64f11486b71
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
* fix: address all 12 PR review comments on marketplace integration
- Narrow except Exception to except ImportError for lazy marketplace import (comment #1)
- Fix provenance key mismatch: use dep identity instead of canonical for lockfile lookup (comment #2)
- Include subdir in git-subdir source resolution with path traversal validation (comment #3)
- Include relative path in relative source resolution with traversal validation (comment #4)
- Sanitize marketplace name in cache file paths to prevent path traversal (comment #5)
- Fix docs: stale-if-error, not stale-while-revalidate (comment #6)
- Consolidate CHANGELOG entries into single line with (#503) (comment #7)
- Remove unused _SUPPORTED_SOURCE_TYPES set (comment #8)
- Let auth errors propagate in _auto_detect_path instead of swallowing (comment #9)
- Validate marketplace --name against [a-zA-Z0-9._-]+ charset (comment #10)
- Fix doc examples to use identifier-compatible names (comments #11, #12)
- Update tests to match corrected resolver behavior, add traversal tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: Copilot CLI format compatibility and marketplace provenance bugs
Bug #1 - Format incompatibility with awesome-copilot marketplace:
- Parser now accepts 'source' key (Copilot CLI) as type discriminator
fallback when 'type' key is absent, normalizing to 'type' for resolvers
- GitHub source resolver now accepts 'path' field (Copilot CLI) as
virtual subdirectory, same as 'subdir' in git-subdir sources
- Path traversal validation applied to 'path' field
- Fixes: 8 of 62 plugins in awesome-copilot that use github source
objects with 'source'+'path' keys instead of 'type'+'subdir'
Bug #2 - Lockfile provenance never written:
- Root cause: install passed raw marketplace refs (NAME@MARKETPLACE)
as only_packages, but DependencyReference.parse() can't parse those,
so identity filtering removed all deps -> 'already installed'
- Fix: use validated_packages (canonical owner/repo strings) instead
of raw click argument for only_pkgs
Both bugs verified fixed via E2E tests against real marketplaces:
- github/awesome-copilot (62 plugins)
- anthropics/skills (3 plugins)
- microsoft/azure-skills (1 plugin)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: scope marketplace search to QUERY@MARKETPLACE format
Search now requires QUERY@MARKETPLACE (e.g. apm search security@skills)
to eliminate name collisions across marketplaces. Added search_marketplace()
client function for single-marketplace search.
- Rejects bare queries without @ — clear error with usage example
- Validates marketplace exists before searching
- Updated docs/guides/marketplaces.md with new syntax
- 7 test cases: format validation, unknown marketplace, results, no results
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: update CLI reference and plugins guide for scoped search syntax
Align all documentation with QUERY@MARKETPLACE search format.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor: use centralized path_security for marketplace traversal checks
Replace 3 ad-hoc '..' in x.split('/') checks in marketplace/resolver.py
with validate_path_segments() from utils/path_security.py. Add
defense-in-depth validate_path_segments() call to _sanitize_cache_name()
in client.py.
This ensures marketplace code uses the same cross-platform path safety
utilities (backslash normalization, single-dot rejection) as the rest
of APM.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: add path safety rule to copilot-instructions.md
Directs contributors to use validate_path_segments() and
ensure_path_within() from utils/path_security.py instead of
ad-hoc traversal checks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
Co-authored-by: danielmeppiel <dmeppiel@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancementDeprecated: use type/feature. Kept for issue history; will be removed in milestone 0.10.0.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Marketplace integration -- read marketplace.json for plugin discovery + governance

3 participants

@danielmeppiel