Skip to content

Commit a71d60a

Browse files
zhujian0805claude
andcommitted
feat: separate marketplace and plugin installation commands
- Add new 'cam plugin marketplace install' command for installing marketplaces to apps - Refactor 'cam plugin install' to handle only plugins (not marketplaces) - Update README with comprehensive plugin command documentation - Add comprehensive tests for marketplace install command (6 test cases) - Improve help messages throughout plugin command system with better context and examples - Create clear separation between config operations (add/remove/list) and app operations (install/update) This resolves confusion where the overloaded 'install' command handled both marketplace and plugin installation. Now: - Marketplace config: cam plugin marketplace add/remove/list - Marketplace app installation: cam plugin marketplace install/update - Plugin installation: cam plugin install 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 61db986 commit a71d60a

File tree

5 files changed

+300
-62
lines changed

5 files changed

+300
-62
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,42 @@ cam skill install <skill> # Install a skill
200200
cam skill fetch # Fetch skills from configured repos
201201
```
202202

203+
### Plugin Subcommands
204+
205+
CAM supports plugin management for compatible assistants (currently Claude and CodeBuddy). Plugins are organized into marketplaces that can be browsed and installed.
206+
207+
#### Marketplace Management (Configuration)
208+
209+
```bash
210+
cam plugin marketplace add <source> # Add marketplace to CAM config
211+
cam plugin marketplace list # List configured marketplaces
212+
cam plugin marketplace remove <name> # Remove marketplace from CAM config
213+
```
214+
215+
#### Marketplace Management (App Installation)
216+
217+
```bash
218+
cam plugin marketplace install <name> # Install marketplace to Claude/CodeBuddy
219+
cam plugin marketplace update [name] # Update installed marketplaces in apps
220+
```
221+
222+
#### Plugin Management
223+
224+
```bash
225+
cam plugin list # List installed/enabled plugins
226+
cam plugin repos # List available plugin repositories
227+
cam plugin add-repo <owner>/<repo> # Add plugin repository to CAM config
228+
cam plugin remove-repo <name> # Remove plugin repository from CAM config
229+
cam plugin install <plugin>[@marketplace] # Install a plugin from marketplace
230+
cam plugin uninstall <plugin> # Uninstall a plugin
231+
cam plugin enable <plugin> # Enable a disabled plugin
232+
cam plugin disable <plugin> # Disable an enabled plugin
233+
cam plugin browse [marketplace] # Browse plugins in marketplaces
234+
cam plugin view <plugin> # View detailed plugin information
235+
cam plugin status # Show plugin system status
236+
cam plugin validate <path> # Validate plugin/marketplace manifest
237+
```
238+
203239
## Architecture Overview
204240

205241
CAM implements industry-standard design patterns for maintainability and extensibility:

code_assistant_manager/cli/plugin_commands.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818

1919
# Combine all plugin subcommands into the main plugin app
2020
plugin_app = typer.Typer(
21-
help="Manage plugins and marketplaces for AI assistants (Claude, CodeBuddy)",
21+
help="Manage plugins and marketplaces for AI assistants (Claude, CodeBuddy).\n\n"
22+
"CAM supports plugin management for compatible assistants. Plugins are distributed\n"
23+
"through marketplaces that can be browsed and installed. Use 'cam plugin marketplace'\n"
24+
"subcommands to manage marketplaces themselves, and 'cam plugin install' for plugins.",
2225
no_args_is_help=True,
2326
)
2427

code_assistant_manager/cli/plugins/plugin_install_commands.py

Lines changed: 16 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def _set_plugin_enabled(handler, plugin: str, enabled: bool) -> bool:
100100
def install_plugin(
101101
plugin: str = typer.Argument(
102102
...,
103-
help="Plugin name, plugin@marketplace, or marketplace name",
103+
help="Plugin name or plugin@marketplace. Examples: 'code-reviewer' or 'code-reviewer@awesome-plugins'",
104104
),
105105
marketplace: Optional[str] = typer.Option(
106106
None,
@@ -115,63 +115,24 @@ def install_plugin(
115115
help=f"App type to install to ({', '.join(VALID_APP_TYPES)})",
116116
),
117117
):
118-
"""Install a plugin from available marketplaces or add a built-in marketplace."""
118+
"""Install a plugin from available marketplaces.
119+
120+
Installs a plugin to Claude or CodeBuddy from configured marketplaces.
121+
The plugin can be specified as:
122+
- plugin-name (searches all configured marketplaces)
123+
- plugin-name@marketplace-name (specifies which marketplace to use)
124+
125+
For marketplace management, use 'cam plugin marketplace install <marketplace>'.
126+
For browsing available plugins, use 'cam plugin browse'.
127+
128+
Examples:
129+
cam plugin install code-reviewer
130+
cam plugin install code-reviewer@awesome-plugins
131+
cam plugin install --marketplace awesome-plugins code-reviewer
132+
"""
119133
app = resolve_single_app(app_type, VALID_APP_TYPES, default="claude")
120134
_check_app_cli(app)
121135
handler = _get_handler(app)
122-
manager = PluginManager()
123-
124-
# Check if it's a configured repo (user repos take precedence over builtin)
125-
configured_repo = manager.get_repo(plugin)
126-
repo_url = None
127-
if configured_repo and configured_repo.repo_owner and configured_repo.repo_name:
128-
repo_url = f"https://github.com/{configured_repo.repo_owner}/{configured_repo.repo_name}"
129-
130-
# Handle marketplace type - just add the marketplace
131-
if configured_repo.type == "marketplace":
132-
typer.echo(f"{Colors.CYAN}Adding marketplace: {plugin}...{Colors.RESET}")
133-
success, msg = handler.marketplace_add(repo_url)
134-
if success:
135-
typer.echo(f"{Colors.GREEN}✓ Marketplace added: {plugin}{Colors.RESET}")
136-
typer.echo(
137-
f"\n{Colors.CYAN}Browse plugins with:{Colors.RESET} cam plugin browse {plugin}"
138-
)
139-
typer.echo(
140-
f"{Colors.CYAN}Install plugins with:{Colors.RESET} cam plugin install <plugin-name>@{plugin}"
141-
)
142-
elif "already installed" in msg.lower():
143-
typer.echo(
144-
f"{Colors.YELLOW}Marketplace '{plugin}' is already installed.{Colors.RESET}"
145-
)
146-
typer.echo(
147-
f"\n{Colors.CYAN}Browse plugins with:{Colors.RESET} cam plugin browse {plugin}"
148-
)
149-
else:
150-
typer.echo(
151-
f"{Colors.RED}✗ Failed to add marketplace: {msg}{Colors.RESET}"
152-
)
153-
raise typer.Exit(1)
154-
return # Exit after handling marketplace
155-
156-
# Handle plugin type - add marketplace first if needed, then install plugin
157-
if configured_repo and configured_repo.type == "plugin" and repo_url:
158-
known_marketplaces = handler.get_known_marketplaces()
159-
marketplace_exists = any(
160-
configured_repo.repo_owner.lower() in name.lower()
161-
or configured_repo.repo_name.lower() in name.lower()
162-
for name in known_marketplaces
163-
)
164-
165-
if not marketplace_exists:
166-
typer.echo(
167-
f"{Colors.CYAN}Adding marketplace for plugin: {plugin}...{Colors.RESET}"
168-
)
169-
success, msg = handler.marketplace_add(repo_url)
170-
if not success and "already installed" not in msg.lower():
171-
typer.echo(
172-
f"{Colors.RED}✗ Failed to add marketplace: {msg}{Colors.RESET}"
173-
)
174-
raise typer.Exit(1)
175136

176137
plugin_ref = f"{plugin}@{marketplace}" if marketplace else plugin
177138
typer.echo(f"{Colors.CYAN}Installing plugin: {plugin_ref}...{Colors.RESET}")

code_assistant_manager/cli/plugins/plugin_marketplace_commands.py

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
# ==================== Marketplace Subcommand ====================
2525

2626
marketplace_app = typer.Typer(
27-
help="Manage installed marketplaces",
27+
help="Manage marketplaces for AI assistant plugins.\n\n"
28+
"Marketplaces are collections of plugins that can be installed to Claude or CodeBuddy.\n"
29+
"Use 'add' to configure new marketplaces, 'install' to add them to your app,\n"
30+
"'update' to refresh from source, and 'list'/'remove' for management.",
2831
no_args_is_help=True,
2932
)
3033

@@ -50,6 +53,9 @@ def marketplace_add(
5053
):
5154
"""Add a marketplace from a URL, path, or GitHub repo, or fetch and add from GitHub.
5255
56+
This adds a marketplace to CAM's configuration. After adding, use
57+
'cam plugin marketplace install <name>' to install it to Claude/CodeBuddy.
58+
5359
Examples:
5460
cam plugin marketplace add https://github.com/owner/repo
5561
cam plugin marketplace add owner/repo --save
@@ -156,7 +162,7 @@ def marketplace_add(
156162
# Show next steps
157163
if info.type == "marketplace":
158164
typer.echo(
159-
f"\n{Colors.CYAN}Next:{Colors.RESET} cam plugin install {info.name}"
165+
f"\n{Colors.CYAN}Next:{Colors.RESET} cam plugin marketplace install {info.name}"
160166
)
161167
else:
162168
typer.echo(
@@ -408,7 +414,86 @@ def marketplace_update(
408414
plugin_app.add_typer(marketplace_app, name="marketplace")
409415

410416

411-
# ==================== Add shortcuts ====================
412-
plugin_app.command(name="ls", hidden=True)(list_plugins)
413-
plugin_app.command(name="i", hidden=True)(install_plugin)
414-
plugin_app.command(name="rm", hidden=True)(uninstall_plugin)
417+
@marketplace_app.command("install")
418+
def marketplace_install(
419+
marketplace: str = typer.Argument(
420+
...,
421+
help="Marketplace name to install (must be configured with 'cam plugin marketplace add' first)",
422+
),
423+
app_type: str = typer.Option(
424+
"claude",
425+
"--app",
426+
"-a",
427+
help=f"App type to install marketplace to ({', '.join(VALID_APP_TYPES)})",
428+
),
429+
):
430+
"""Install a configured marketplace to Claude or CodeBuddy.
431+
432+
This installs a marketplace that has been previously configured in CAM
433+
to the target AI assistant app. The marketplace must already be configured
434+
using 'cam plugin marketplace add' before it can be installed.
435+
436+
After installation, you can browse plugins with 'cam plugin browse <marketplace>'
437+
and install them with 'cam plugin install <plugin>@<marketplace>'.
438+
439+
Examples:
440+
cam plugin marketplace install awesome-claude-code-plugins
441+
cam plugin marketplace install my-marketplace --app codebuddy
442+
"""
443+
from code_assistant_manager.cli.option_utils import resolve_single_app
444+
from code_assistant_manager.plugins import PluginManager
445+
446+
app = resolve_single_app(app_type, VALID_APP_TYPES, default="claude")
447+
handler = get_handler(app)
448+
manager = PluginManager()
449+
450+
# Check if marketplace is configured
451+
configured_repo = manager.get_repo(marketplace)
452+
if not configured_repo:
453+
typer.echo(
454+
f"{Colors.RED}✗ Marketplace '{marketplace}' not found in CAM configuration.{Colors.RESET}"
455+
)
456+
typer.echo(
457+
f"{Colors.YELLOW}Use 'cam plugin add-repo --type marketplace <owner>/<repo>' to add it first.{Colors.RESET}"
458+
)
459+
raise typer.Exit(1)
460+
461+
if configured_repo.type != "marketplace":
462+
typer.echo(
463+
f"{Colors.RED}✗ '{marketplace}' is not a marketplace (type: {configured_repo.type}).{Colors.RESET}"
464+
)
465+
raise typer.Exit(1)
466+
467+
if not configured_repo.repo_owner or not configured_repo.repo_name:
468+
typer.echo(
469+
f"{Colors.RED}✗ Marketplace '{marketplace}' has no GitHub source configured.{Colors.RESET}"
470+
)
471+
raise typer.Exit(1)
472+
473+
repo_url = (
474+
f"https://github.com/{configured_repo.repo_owner}/{configured_repo.repo_name}"
475+
)
476+
477+
typer.echo(f"{Colors.CYAN}Installing marketplace: {marketplace}...{Colors.RESET}")
478+
success, msg = handler.marketplace_add(repo_url)
479+
480+
if success:
481+
typer.echo(
482+
f"{Colors.GREEN}✓ Marketplace installed: {marketplace}{Colors.RESET}"
483+
)
484+
typer.echo(
485+
f"\n{Colors.CYAN}Browse plugins with:{Colors.RESET} cam plugin browse {marketplace}"
486+
)
487+
typer.echo(
488+
f"{Colors.CYAN}Install plugins with:{Colors.RESET} cam plugin install <plugin-name>@{marketplace}"
489+
)
490+
elif "already installed" in msg.lower():
491+
typer.echo(
492+
f"{Colors.YELLOW}Marketplace '{marketplace}' is already installed.{Colors.RESET}"
493+
)
494+
typer.echo(
495+
f"\n{Colors.CYAN}Browse plugins with:{Colors.RESET} cam plugin browse {marketplace}"
496+
)
497+
else:
498+
typer.echo(f"{Colors.RED}✗ Failed to install marketplace: {msg}{Colors.RESET}")
499+
raise typer.Exit(1)

0 commit comments

Comments
 (0)