Skip to content

Marketplaces

Marketplaces are curated indexes of plugins hosted in a Git repository (typically GitHub or GitLab). Each marketplace contains a marketplace.json file that maps plugin names to source locations. APM resolves these entries to Git URLs, so plugins installed from marketplaces get the same version locking, security scanning, and governance as any other APM dependency.

A marketplace is a repository with a marketplace.json at its root (GitHub or GitLab). The file lists plugins with their source type and location:

{
"name": "Acme Plugins",
"plugins": [
{
"name": "code-review",
"description": "Automated code review agent",
"source": { "type": "github", "repo": "acme/code-review-plugin" }
},
{
"name": "style-guide",
"source": { "type": "url", "url": "https://github.com/acme/style-guide.git" }
},
{
"name": "eslint-rules",
"source": { "type": "git-subdir", "repo": "acme/monorepo", "subdir": "plugins/eslint-rules" }
},
{
"name": "local-tools",
"source": "./tools/local-plugin"
}
]
}

Both Copilot CLI and Claude Code marketplace.json formats are supported. Copilot CLI uses "repository" and "ref" fields; Claude Code uses "source" (string or object). APM normalizes entries from either format into its canonical dependency representation.

TypeDescriptionExample
githubGitHub owner/repo shorthandacme/code-review-plugin
urlFull HTTPS or SSH Git URLhttps://github.com/acme/style-guide.git
git-subdirSubdirectory within a Git repository (repo + subdir)acme/monorepo + plugins/eslint-rules
String sourceSubdirectory within the marketplace repository itself./tools/local-plugin

npm sources are not supported. Copilot CLI format uses "repository" and optional "ref" fields instead of "source".

The install resolver accepts both legacy (Copilot CLI) and current (Claude Code) key names in marketplace.json source objects:

Current keyLegacy aliasNotes
source (discriminator)typeValues: github, git-subdir, url
reporepositoryMust be owner/repo format
shacommitResolved commit SHA
repo (git-subdir)urlMust be owner/repo, not a full URL

Marketplace authors should use the current keys (emitted by apm pack). Legacy aliases are accepted for backward compatibility with older manifests.

Marketplaces can declare a metadata.pluginRoot field to specify the base directory for bare-name sources:

{
"metadata": { "pluginRoot": "./plugins" },
"plugins": [
{ "name": "my-tool", "source": "my-tool" }
]
}

With pluginRoot set to ./plugins, the source "my-tool" resolves to owner/repo/plugins/my-tool. Sources that already contain a path separator (e.g. ./custom/path) are not affected by pluginRoot.

Plugins can declare a version field and a source.ref that points to a specific Git tag or commit:

{
"name": "code-review",
"description": "Automated code review agent",
"version": "2.1.0",
"source": { "type": "github", "repo": "acme/code-review-plugin", "ref": "v2.1.0" }
}

The version field is informational (displayed by apm view and apm outdated). The source.ref determines which Git ref APM checks out during install.

Terminal window
apm marketplace add acme/plugin-marketplace

This registers the marketplace and fetches its marketplace.json. By default APM tracks the main branch.

When --name is not provided, APM resolves the local alias in this order:

  1. name field declared in the marketplace’s marketplace.json (if present and valid)
  2. Repository name (fallback)

This ensures parity with Claude Code install instructions — if a marketplace’s marketplace.json declares "name": "addy-agent-skills", APM registers it under that alias and shows a hint:

[*] Registering marketplace 'addy-agent-skills'...
[+] Marketplace 'addy-agent-skills' registered (1 plugins)
[i] Install plugins with: apm install <plugin>@addy-agent-skills

Use --name to override the alias explicitly.

Options:

  • --name/-n — Override the local alias (defaults to the marketplace.json name field, then repo name)
  • --branch/-b — Branch to track (default: main)
  • --host — Git host FQDN for non-github.com hosts (default: github.com or GITHUB_HOST env var)
Terminal window
# Register with a custom name on a specific branch
apm marketplace add acme/plugin-marketplace --name acme-plugins --branch release
# Register from a GitHub Enterprise host (two equivalent forms)
apm marketplace add acme/plugin-marketplace --host ghes.corp.example.com
apm marketplace add ghes.corp.example.com/acme/plugin-marketplace

gitlab.com is recognized as GitLab automatically. For self-managed GitLab, set GITLAB_HOST to that instance’s FQDN, or list several hosts in APM_GITLAB_HOSTS (comma-separated). See Authentication for host configuration and tokens.

APM fetches marketplace.json via the GitLab REST v4 raw-file API when the host classifies as GitLab (not the GitHub Contents API). Candidate paths are unchanged: repository root, .github/plugin/marketplace.json, and .claude-plugin/marketplace.json.

Terminal window
# GitLab.com (FQDN in the argument or via --host)
apm marketplace add mygroup/plugin-marketplace --host gitlab.com
apm marketplace add gitlab.com/mygroup/plugin-marketplace
# Self-managed GitLab (set GITLAB_HOST or APM_GITLAB_HOSTS so APM classifies the instance as GitLab, then use FQDN in the path or --host)
apm marketplace add git.company.com/mygroup/plugin-marketplace
apm marketplace add mygroup/plugin-marketplace --host git.company.com
Terminal window
apm marketplace list

Shows all registered marketplaces with their source repository and branch.

View all plugins available in a specific marketplace:

Terminal window
apm marketplace browse acme-plugins

Search plugins by name or description in a specific marketplace using QUERY@MARKETPLACE:

Terminal window
apm search "code review@skills"

Options:

  • --limit — Maximum results to return (default: 20)
Terminal window
apm search "linting@awesome-copilot" --limit 5

The @MARKETPLACE scope is required — this avoids name collisions when different marketplaces contain plugins with the same name. To see everything in a marketplace, use apm marketplace browse <name> instead.

Use the NAME@MARKETPLACE syntax to install a plugin from a specific marketplace:

Terminal window
# Install using the source ref from the marketplace entry
apm install code-review@acme-plugins
# Install with a specific git ref override
apm install code-review@acme-plugins#v2.0.0
# Install from a specific branch
apm install code-review@acme-plugins#main

The # separator carries a raw git ref that overrides the source.ref from the marketplace entry. Without #, APM uses the ref defined in the marketplace manifest.

APM resolves the plugin name against the marketplace index, fetches the underlying Git repository using the resolved ref, and installs it as a standard APM dependency. The resolved source appears in apm.yml and apm.lock.yaml just like any direct dependency.

On GitLab-class hosts (gitlab.com and self-managed instances when APM classifies the host as GitLab), monorepo layouts—plugins whose marketplace.json sources point at a subdirectory inside the marketplace repository—work with this syntax; you do not need a manual git: + path: dependency entry for apm install. When you declare Git dependencies yourself in apm.yml, nested paths may still need the object form with explicit git: and path:—see the Dependencies guide.

For full apm install options, see CLI Commands.

Show metadata for a marketplace plugin:

Terminal window
apm view code-review@acme-plugins

Displays the plugin’s name, version, description, source, and tags.

Marketplace-resolved plugins are tracked in apm.lock.yaml with full provenance:

apm_modules:
acme/code-review-plugin:
resolved: https://github.com/acme/code-review-plugin#main
commit: abc123def456789
discovered_via: acme-plugins
marketplace_plugin_name: code-review

The discovered_via field records which marketplace was used for discovery. marketplace_plugin_name stores the original plugin name from the index. The resolved URL and commit pin the exact version, so builds remain reproducible regardless of marketplace availability.

APM caches marketplace indexes locally with a 1-hour TTL. Within that window, commands like search and browse use the cached index. After expiry, APM fetches a fresh copy from the network. If the network request fails, APM falls back to the expired cache (stale-if-error) so commands still work offline.

Force a cache refresh:

Terminal window
# Refresh a specific marketplace
apm marketplace update acme-plugins
# Refresh all registered marketplaces
apm marketplace update

When PROXY_REGISTRY_URL is set, marketplace commands (add, browse, search, update) fetch marketplace.json through the registry proxy (Artifactory Archive Entry Download) first. Only if the proxy does not return the file does APM fall back to the host API: GitHub Contents API for GitHub/GHES, or GitLab REST v4 raw file endpoints for GitLab-classified hosts. When PROXY_REGISTRY_ONLY=1 is also set, that direct host API fallback is blocked entirely, enabling fully air-gapped marketplace discovery for both GitHub- and GitLab-backed indexes.

Terminal window
export PROXY_REGISTRY_URL="https://art.corp.example.com/artifactory/github"
export PROXY_REGISTRY_ONLY=1 # optional: block direct GitHub/GitLab API access
apm marketplace add anthropics/skills # fetches via Artifactory
apm marketplace browse skills # fetches via Artifactory

This builds on the same proxy infrastructure used by apm install. See Registry Proxy & Air-gapped for full configuration, the bypass-prevention contract, and the air-gapped CI playbook.

Remove a registered marketplace:

Terminal window
apm marketplace remove acme-plugins
# Skip confirmation prompt
apm marketplace remove acme-plugins --yes

Removing a marketplace does not uninstall plugins previously installed from it. Those plugins remain pinned in apm.lock.yaml to their resolved Git sources.

Check a marketplace manifest for schema errors and duplicate entries:

Terminal window
apm marketplace validate acme-plugins
# Verbose output
apm marketplace validate acme-plugins --verbose

Catches: missing required fields and duplicate plugin names (case-insensitive).

For full option details, see CLI Commands.

APM caches version-to-ref mappings in ~/.apm/cache/marketplace/version-pins.json. On subsequent installs, APM compares the marketplace ref against the cached pin. If a version’s ref has changed, APM warns:

WARNING: Version 2.0.0 of code-review@acme-plugins ref changed: was 'v2.0.0', now 'deadbeef'. This may indicate a ref swap attack.

This detects marketplace maintainers (or compromised accounts) silently pointing an existing version at different code.

When installing a marketplace plugin, APM checks all other registered marketplaces for plugins with the same name. A match produces a warning:

WARNING: Plugin 'code-review' also found in marketplace 'other-plugins'. Verify you are installing from the intended source.

Shadow detection runs automatically during install — no configuration required.

  • Use commit SHAs as refs — tags and branches can be moved; commit SHAs cannot.
  • Keep plugin names unique across marketplaces — avoids shadow warnings and reduces confusion.
  • Review immutability warnings — a changed ref for an existing version is a strong signal of tampering.

When building a marketplace that tracks packages from a monorepo (multiple packages inside one Git repository), use --subdir to point each entry at its subdirectory:

Terminal window
apm marketplace package add acme/monorepo --subdir plugins/eslint-rules --name eslint-rules
apm marketplace package add acme/monorepo --subdir plugins/formatter --name formatter

Mutable git refs (HEAD, branch names) are automatically resolved to concrete 40-character SHAs before being stored in apm.yml. This ensures supply-chain safety — the entry always pins to an immutable commit.

Default behaviour (no --ref): When neither --version nor --ref is provided, the current HEAD SHA is pinned automatically:

Terminal window
# Resolves HEAD to its current SHA and stores it
apm marketplace package add acme/code-review

Explicit HEAD: Passing --ref HEAD warns that HEAD is mutable, then resolves:

Terminal window
apm marketplace package add acme/code-review --ref HEAD
# [!] 'HEAD' is a mutable ref. Resolving to current SHA for safety.
# [i] Resolved HEAD to abc123def456

Branch names: Branch names that match refs/heads/* on the remote are also resolved:

Terminal window
apm marketplace package add acme/code-review --ref main
# [!] 'main' is a branch (mutable ref). Resolving to current SHA for safety.
# [i] Resolved main to abc123def456

Updating pinned SHAs: Use package set with --ref HEAD to re-pin to the latest commit:

Terminal window
apm marketplace package set code-review --ref HEAD

Tags and concrete SHAs are stored as-is without resolution.

If you want to create and maintain your own marketplace registry, see the Marketplace Authoring Guide.