Skip to content

Implement plugin management system and CLI commands#83

Open
SebastienDegodez wants to merge 1 commit intomicrosoft:mainfrom
SebastienDegodez:feature/plugins
Open

Implement plugin management system and CLI commands#83
SebastienDegodez wants to merge 1 commit intomicrosoft:mainfrom
SebastienDegodez:feature/plugins

Conversation

@SebastienDegodez
Copy link

Introduce a plugin management system along with CLI commands for installing and managing plugins from various marketplaces. This enhancement aims to streamline plugin integration and improve user experience.

Copilot AI review requested due to automatic review settings February 9, 2026 23:22
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a first-cut plugin marketplace + resolver layer and exposes it via new apm plugin CLI commands, with docs and fixtures to support plugin installation/integration workflows.

Changes:

  • Introduces MarketplaceManager/PluginResolver for resolving plugin-id@marketplace into a repository URL.
  • Adds apm plugin install (tracks plugins in apm.yml) and apm plugin list (marketplace browsing) commands.
  • Extends apm install to also install entries from apm.yml: plugins, and adds docs + test fixtures around plugin primitives.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 30 comments.

Show a summary per file
File Description
src/apm_cli/plugin/marketplace.py Adds marketplace fetching/parsing and plugin listing/lookup.
src/apm_cli/plugin/resolver.py Adds parsing/resolution for plugin-id@marketplace specs.
src/apm_cli/commands/plugin.py Adds Click CLI group/commands for plugin install/list and apm.yml tracking.
src/apm_cli/cli.py Loads plugins: from apm.yml and installs them alongside APM deps.
src/apm_cli/models/apm_package.py Adds a placeholder get_plugins() method.
tests/unit/test_plugin_system.py Unit tests for marketplace parsing and resolver behavior.
tests/integration/test_plugin_integration.py Integration-style tests around primitive integration using a mock plugin fixture.
tests/fixtures/mock-plugin/** Adds a mock plugin repo layout with .apm/* primitives for tests.
docs/plugins.md New plugin system guide.
docs/index.md Adds link to plugin guide.
docs/cli-reference.md Documents new apm plugin commands.

Comment on lines +13 to +14
@click.group(help="Manage APM plugins from Claude Code and GitHub marketplaces")
def plugin():
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

The click group help string for apm plugin doesn’t follow the repo’s CLI help convention of including a contextual emoji, which makes help output inconsistent with other commands. Please update the group help= to include an appropriate emoji (and keep it consistent with docs/cli-reference.md).

Copilot generated this review using guidance from repository custom instructions.
import tempfile
from pathlib import Path
from unittest.mock import patch, MagicMock
import requests
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

Import of 'requests' is not used.

Suggested change
import requests

Copilot uses AI. Check for mistakes.
@SebastienDegodez SebastienDegodez marked this pull request as draft February 11, 2026 11:12
@SebastienDegodez SebastienDegodez force-pushed the feature/plugins branch 2 times, most recently from 9b10d45 to a4f6f1c Compare February 11, 2026 21:33
@SebastienDegodez SebastienDegodez marked this pull request as ready for review February 11, 2026 21:40
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 22 comments.

Comment on lines 713 to 716
shutil.rmtree(repo_dir)
console.print(
f"{STATUS_SYMBOLS.get('success', '✓')} Plugin '{plugin_id}' uninstalled"
)
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

console is used here but is not defined in this scope, so uninstalling a plugin will raise NameError after removing the directory. Use console = _get_console() (or _rich_success) before printing the uninstall confirmation.

Copilot uses AI. Check for mistakes.
@SebastienDegodez SebastienDegodez force-pushed the feature/plugins branch 2 times, most recently from a74b217 to 61e8ebb Compare February 11, 2026 22:42
Signed-off-by: SebastienDegodez <sebastien.degodez@gmail.com>
danielmeppiel added a commit that referenced this pull request Feb 15, 2026
@danielmeppiel
Copy link
Collaborator

@SebastienDegodez — thank you for this. The plugin marketplace ecosystem is an industry standard now and APM needs to support it. This review is about routing it through APM's existing architecture, not building alongside it.

Decision: Request changes


APM's detection pattern (how it works today)

APM already handles five foreign formats, all the same way:

apm install owner/repo
        ↓
   clone to apm_modules/
        ↓
   look at what's inside:
     ├─ apm.yml only           → APM_PACKAGE
     ├─ SKILL.md only          → CLAUDE_SKILL (synthesize apm.yml)
     ├─ apm.yml + SKILL.md     → HYBRID
     ├─ .collection.yml        → COLLECTION (download items, generate .apm/)
     └─ single .prompt.md file → VIRTUAL_FILE (wrap in package)

No flags. No separate commands. apm install figures it out. The user never needs to know or care what format the source uses.

What plugin support should look like

The same pattern, extended:

apm install anthropics/claude-code-plugins/commit-commands
        ↓
   clone to apm_modules/
        ↓
   look at what's inside:
     ├─ apm.yml       → APM_PACKAGE (existing)
     ├─ SKILL.md      → CLAUDE_SKILL (existing)
     ├─ plugin.json   → MARKETPLACE_PLUGIN ← new detection
     └─ ...

When the downloader finds plugin.json (and no apm.yml), it does what it already does for SKILL.md:

  1. Parse plugin.json for metadata (name, description, version)
  2. Map the plugin's agents/, skills/, commands/ into .apm/ subdirectories
  3. Synthesize apm.yml
  4. Set PackageType.MARKETPLACE_PLUGIN
  5. From here on it's a normal dependency — lock file, version pinning, transitive resolution, conflict detection, everything works

The user experience is just:

apm install anthropics/claude-code-plugins/commit-commands

Or in apm.yml:

dependencies:
  apm:
    - company/coding-standards#v2.0
    - anthropics/claude-code-plugins/commit-commands#v1.2.0

That's it. No apm plugin subcommand. No plugins: section. No @claude syntax. Just apm install, same as everything else.

The marketplace discovery question

The useful part of your PR — MarketplaceManager resolving marketplace.json manifests — answers a real question: "How do I find the repo URL for a plugin I saw in the Claude marketplace?"

But that's a browsing concern, not an install concern. Once a user knows the repo, it's just apm install owner/repo/plugin-name. The mapping from marketplace name to repo URL is what the marketplace UI already does (ClaudePluginHub, /plugin in Claude Code, etc.).

If we want APM to help with discovery later, it can be a lightweight convenience:

apm browse claude    # opens marketplace URL in browser

But that's a separate, smaller feature — not a prerequisite for plugin support.

What to keep from your PR

Keep & relocate Cut
Plugin dataclass + from_claude_format() / from_github_format() → becomes plugin_parser.py (like collection_parser.py) apm plugin CLI subcommand group (all 5 commands)
plugin.json.apm/ structure mapping logic Separate plugins: section in apm.yml
Test fixtures (mock-plugin structure) PluginInstaller (duplicate of existing install pipeline)
Docs explaining plugin format support MarketplaceManager / PluginResolver (defer to Phase 2)
Separate plugin primitive discovery phase

Implementation plan

Phase 1 — plugin.json as detected format (core value, small surface area)

  1. Add MARKETPLACE_PLUGIN to PackageType enum
  2. Write plugin_parser.py in src/apm_cli/deps/ — parses plugin.json, maps agents/skills/commands to .apm/ structure, synthesizes apm.yml
  3. Extend GitHubPackageDownloader detection: after checking for apm.yml and SKILL.md, check for plugin.json
  4. Extend validate_virtual_package_exists() to check for plugin.json in subdirectory packages
  5. scan_dependency_primitives() already handles the rest — no changes needed

Result: apm install owner/repo works for plugin repos. Lock file, version pinning, transitive deps, conflict detection — all free.

Phase 2 — Marketplace source resolution (optional, later)

Your MarketplaceManager code becomes a convenience layer that maps short names to repo URLs. Could be a standalone apm browse or a resolver that feeds into apm install. This is additive and doesn't block Phase 1.

Why this matters architecturally

APM currently has one install pipeline, one manifest format, one discovery system, one lock file. Every foreign format normalizes into that single model. This is the project's most important architectural invariant — it's what makes APM "the npm for agent primitives" rather than "a wrapper around five different tools."

PR #83 as written breaks that invariant by creating a parallel pipeline. The plugin ecosystem deserves full support — but through the existing architecture, not alongside it.

Your plugin.json parsing code and test fixtures are genuinely useful. I'd welcome a V2 that integrates them as a format adapter in the existing pipeline. Happy to pair on scoping that.

@SebastienDegodez
Copy link
Author

Hello @danielmeppiel , sorry I'm in holidays.

Can you take this Pull Request or you can wait one moment ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments