Skip to content

Commit 08718c5

Browse files
committed
Feature: Add plugin view command to show plugin details
New 'cam plugin view' command displays detailed information about a specific plugin: - Plugin name, version, category, and marketplace source - Full description - Source path - Associated skills (if available) - Installation command Usage examples: cam plugin view document-skills cam plugin view documentation-generator@awesome-claude-code-plugins cam plugin view example-skills -a codebuddy The command searches all configured marketplaces and supports: - Quick lookup by plugin name (first match found) - Specific marketplace filtering with @marketplace syntax - Help suggestions when plugin not found
1 parent 23f95d9 commit 08718c5

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed

code_assistant_manager/cli/plugin_commands.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,128 @@ def browse_marketplace(
10021002
_display_marketplace_footer(info, marketplace, total, limit)
10031003

10041004

1005+
@plugin_app.command("view")
1006+
def view_plugin(
1007+
plugin: str = typer.Argument(
1008+
...,
1009+
help="Plugin name to view (e.g., 'document-skills' or 'document-skills@anthropic-agent-skills')",
1010+
),
1011+
app_type: str = typer.Option(
1012+
"claude",
1013+
"--app",
1014+
"-a",
1015+
help=f"App type ({', '.join(VALID_APP_TYPES)})",
1016+
),
1017+
):
1018+
"""View detailed information about a specific plugin.
1019+
1020+
Shows the plugin description, version, category, and source marketplace.
1021+
You can specify just the plugin name or include the marketplace name
1022+
with the @ format (e.g., 'plugin-name@marketplace').
1023+
"""
1024+
from code_assistant_manager.plugins.fetch import fetch_repo_info
1025+
1026+
app = resolve_single_app(app_type, VALID_APP_TYPES, default="claude")
1027+
manager = PluginManager()
1028+
1029+
# Parse plugin name and marketplace if provided
1030+
parts = plugin.split("@")
1031+
plugin_name = parts[0]
1032+
marketplace_filter = parts[1] if len(parts) > 1 else None
1033+
1034+
all_repos = manager.get_all_repos()
1035+
if not all_repos:
1036+
typer.echo(f"{Colors.YELLOW}No plugin repositories configured{Colors.RESET}")
1037+
raise typer.Exit(1)
1038+
1039+
# Search for the plugin
1040+
found_plugin = None
1041+
found_marketplace = None
1042+
1043+
for repo_name, repo in all_repos.items():
1044+
if marketplace_filter and repo_name.lower() != marketplace_filter.lower():
1045+
continue
1046+
1047+
if not repo.repo_owner or not repo.repo_name:
1048+
continue
1049+
1050+
# Fetch repo info
1051+
info = fetch_repo_info(
1052+
repo.repo_owner, repo.repo_name, repo.repo_branch or "main"
1053+
)
1054+
if not info:
1055+
continue
1056+
1057+
if info.type == "marketplace":
1058+
# Search in marketplace plugins
1059+
for p in info.plugins:
1060+
if p.get("name", "").lower() == plugin_name.lower():
1061+
found_plugin = p
1062+
found_marketplace = repo_name
1063+
break
1064+
else:
1065+
# Check if this is the plugin
1066+
if info.name.lower() == plugin_name.lower():
1067+
found_plugin = {
1068+
"name": info.name,
1069+
"version": info.version or "1.0.0",
1070+
"description": info.description or "No description provided",
1071+
"category": "",
1072+
"source": info.plugin_path or "./",
1073+
}
1074+
found_marketplace = repo_name
1075+
break
1076+
1077+
if found_plugin:
1078+
break
1079+
1080+
if not found_plugin:
1081+
typer.echo(
1082+
f"{Colors.RED}✗ Plugin '{plugin}' not found in configured repositories{Colors.RESET}"
1083+
)
1084+
typer.echo()
1085+
typer.echo(f"{Colors.CYAN}Try browsing available plugins:{Colors.RESET}")
1086+
if marketplace_filter:
1087+
typer.echo(f" cam plugin browse {marketplace_filter}")
1088+
else:
1089+
typer.echo(f" cam plugin browse")
1090+
raise typer.Exit(1)
1091+
1092+
# Display plugin details
1093+
typer.echo(f"\n{Colors.BOLD}{found_plugin.get('name', 'Unknown')}{Colors.RESET}")
1094+
1095+
version = found_plugin.get("version")
1096+
if version:
1097+
typer.echo(f"{Colors.CYAN}Version:{Colors.RESET} {version}")
1098+
1099+
category = found_plugin.get("category")
1100+
if category:
1101+
typer.echo(f"{Colors.CYAN}Category:{Colors.RESET} {category}")
1102+
1103+
typer.echo(f"{Colors.CYAN}Marketplace:{Colors.RESET} {found_marketplace}")
1104+
1105+
description = found_plugin.get("description")
1106+
if description:
1107+
typer.echo(f"\n{Colors.CYAN}Description:{Colors.RESET}")
1108+
typer.echo(f" {description}")
1109+
1110+
source = found_plugin.get("source")
1111+
if source:
1112+
typer.echo(f"\n{Colors.CYAN}Source:{Colors.RESET} {source}")
1113+
1114+
skills = found_plugin.get("skills")
1115+
if skills:
1116+
typer.echo(f"\n{Colors.CYAN}Skills:{Colors.RESET}")
1117+
for skill in skills:
1118+
typer.echo(f" • {skill}")
1119+
1120+
typer.echo()
1121+
typer.echo(
1122+
f"{Colors.CYAN}Install:{Colors.RESET} cam plugin install {plugin_name}@{found_marketplace}"
1123+
)
1124+
typer.echo()
1125+
1126+
10051127
@plugin_app.command("fetch")
10061128
def fetch_repo(
10071129
url: Optional[str] = typer.Argument(

0 commit comments

Comments
 (0)