Skip to content

Commit 5b41a2c

Browse files
zhujian0805claude
andcommitted
refactor: reduce cyclomatic complexity in 4 high-complexity functions
Based on docs/refactoring/REFACTORING_PLANS.md: 1. browse_marketplace() in cli/plugin_commands.py (CC: 32 → ~6-8) - Extract _resolve_marketplace_repo(), _resolve_from_known_marketplaces() - Extract _filter_plugins(), _display_marketplace_* helpers 2. validate_command() in config.py (CC: 25 → ~8-10) - Extract pattern constants as module-level frozensets - Extract _contains_dangerous_pattern(), _is_safe_executable() - Extract _validate_command_arguments(), _validate_simple_command() 3. validate_config() in config.py (CC: 24 → ~6-8) - Extract _validate_common_config(), _validate_endpoint() - Extract _is_validation_cache_valid(), _cache_validation_result() 4. show() in mcp/server_commands.py (CC: 20 → ~5-6) - Extract _display_optional_field(), _display_author_info() - Extract _display_installation_methods(), _display_tools(), _display_examples() All 455 unit tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent da5482c commit 5b41a2c

3 files changed

Lines changed: 617 additions & 406 deletions

File tree

code_assistant_manager/cli/plugin_commands.py

Lines changed: 157 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,148 @@ def _print_repo(name: str, repo: PluginRepo, is_user: bool = False):
616616
typer.echo()
617617

618618

619+
# ==================== Browse Marketplace Helper Functions ====================
620+
621+
622+
def _resolve_marketplace_repo(
623+
manager: PluginManager, handler: ClaudePluginHandler, marketplace: str
624+
) -> tuple[Optional[str], Optional[str], str]:
625+
"""Resolve marketplace name to repo owner/name/branch.
626+
627+
Returns:
628+
Tuple of (repo_owner, repo_name, repo_branch) or (None, None, "main") if not found
629+
"""
630+
repo = manager.get_repo(marketplace)
631+
632+
if repo and repo.repo_owner and repo.repo_name:
633+
return repo.repo_owner, repo.repo_name, repo.repo_branch
634+
635+
# Try Claude's known marketplaces as fallback
636+
return _resolve_from_known_marketplaces(handler, marketplace)
637+
638+
639+
def _resolve_from_known_marketplaces(
640+
handler: ClaudePluginHandler, marketplace: str
641+
) -> tuple[Optional[str], Optional[str], str]:
642+
"""Fallback resolution from Claude's known_marketplaces.json."""
643+
import json
644+
645+
known_file = handler.known_marketplaces_file
646+
if not known_file.exists():
647+
return None, None, "main"
648+
649+
try:
650+
with open(known_file, "r") as f:
651+
known = json.load(f)
652+
653+
if marketplace not in known:
654+
return None, None, "main"
655+
656+
source_url = known[marketplace].get("source", {}).get("url", "")
657+
if "github.com" not in source_url:
658+
return None, None, "main"
659+
660+
parsed = parse_github_url(source_url)
661+
if parsed:
662+
return parsed
663+
except Exception:
664+
pass
665+
666+
return None, None, "main"
667+
668+
669+
def _filter_plugins(
670+
plugins: list[dict],
671+
query: Optional[str] = None,
672+
category: Optional[str] = None,
673+
) -> list[dict]:
674+
"""Filter plugins by query string and/or category."""
675+
result = plugins
676+
677+
if query:
678+
query_lower = query.lower()
679+
result = [
680+
p
681+
for p in result
682+
if query_lower in p.get("name", "").lower()
683+
or query_lower in p.get("description", "").lower()
684+
]
685+
686+
if category:
687+
category_lower = category.lower()
688+
result = [p for p in result if category_lower in p.get("category", "").lower()]
689+
690+
return result
691+
692+
693+
def _display_marketplace_not_found(
694+
manager: PluginManager, handler: ClaudePluginHandler, marketplace: str
695+
) -> None:
696+
"""Display error message when marketplace is not found."""
697+
typer.echo(
698+
f"{Colors.RED}✗ Marketplace '{marketplace}' not found in config or Claude.{Colors.RESET}"
699+
)
700+
typer.echo(f"\n{Colors.CYAN}Available repos:{Colors.RESET}")
701+
for name in manager.get_all_repos():
702+
typer.echo(f" • {name}")
703+
typer.echo(f"\n{Colors.CYAN}Installed marketplaces:{Colors.RESET}")
704+
for name in handler.get_known_marketplaces():
705+
typer.echo(f" • {name}")
706+
707+
708+
def _display_marketplace_header(
709+
info, query: Optional[str], category: Optional[str], total: int
710+
) -> None:
711+
"""Display marketplace info header."""
712+
typer.echo(
713+
f"\n{Colors.BOLD}{info.name}{Colors.RESET} - {info.description or 'No description'}"
714+
)
715+
if info.version:
716+
typer.echo(f"Version: {info.version}")
717+
typer.echo(f"Total plugins: {info.plugin_count}")
718+
if query or category:
719+
typer.echo(f"Matching: {total}")
720+
721+
722+
def _display_plugin(plugin: dict) -> None:
723+
"""Display a single plugin entry."""
724+
name = plugin.get("name", "unknown")
725+
version = plugin.get("version", "")
726+
desc = plugin.get("description", "")
727+
cat = plugin.get("category", "")
728+
729+
version_str = f" v{version}" if version else ""
730+
cat_str = f" [{cat}]" if cat else ""
731+
732+
typer.echo(
733+
f" {Colors.BOLD}{name}{Colors.RESET}{version_str}{Colors.CYAN}{cat_str}{Colors.RESET}"
734+
)
735+
if desc:
736+
if len(desc) > 80:
737+
desc = desc[:77] + "..."
738+
typer.echo(f" {desc}")
739+
740+
741+
def _display_marketplace_footer(info, marketplace: str, total: int, limit: int) -> None:
742+
"""Display marketplace footer with categories and install hint."""
743+
if total > limit:
744+
typer.echo(f"\n ... and {total - limit} more")
745+
746+
categories = {p.get("category") for p in info.plugins if p.get("category")}
747+
if categories:
748+
typer.echo(
749+
f"\n{Colors.CYAN}Categories:{Colors.RESET} {', '.join(sorted(categories))}"
750+
)
751+
752+
typer.echo(
753+
f"\n{Colors.CYAN}Install with:{Colors.RESET} cam plugin install <plugin-name>@{marketplace}"
754+
)
755+
typer.echo()
756+
757+
758+
# ==================== Browse Marketplace Command ====================
759+
760+
619761
@plugin_app.command("browse")
620762
def browse_marketplace(
621763
marketplace: str = typer.Argument(
@@ -646,135 +788,40 @@ def browse_marketplace(
646788
Fetches the marketplace manifest from GitHub and lists all available plugins.
647789
Use --query to search by name/description, --category to filter by category.
648790
"""
791+
from code_assistant_manager.plugins.fetch import fetch_repo_info
792+
649793
manager = PluginManager()
650794
handler = _get_handler()
651795

652-
# Get the marketplace repo config
653-
repo = manager.get_repo(marketplace)
654-
655-
# If not in our config, try to get URL from Claude's known_marketplaces.json
656-
repo_owner = None
657-
repo_name = None
658-
repo_branch = "main"
659-
660-
if repo and repo.repo_owner and repo.repo_name:
661-
repo_owner = repo.repo_owner
662-
repo_name = repo.repo_name
663-
repo_branch = repo.repo_branch
664-
else:
665-
# Try to extract from Claude's known marketplaces
666-
known_file = handler.known_marketplaces_file
667-
if known_file.exists():
668-
import json
669-
670-
try:
671-
with open(known_file, "r") as f:
672-
known = json.load(f)
673-
if marketplace in known:
674-
source_url = known[marketplace].get("source", {}).get("url", "")
675-
# Parse URL like https://github.com/owner/repo.git
676-
if "github.com" in source_url:
677-
from code_assistant_manager.plugins.fetch import (
678-
parse_github_url,
679-
)
680-
681-
parsed = parse_github_url(source_url)
682-
if parsed:
683-
repo_owner, repo_name, repo_branch = parsed
684-
except Exception:
685-
pass
796+
# Resolve marketplace to repo info
797+
repo_owner, repo_name, repo_branch = _resolve_marketplace_repo(
798+
manager, handler, marketplace
799+
)
686800

687801
if not repo_owner or not repo_name:
688-
typer.echo(
689-
f"{Colors.RED}✗ Marketplace '{marketplace}' not found in config or Claude.{Colors.RESET}"
690-
)
691-
typer.echo(f"\n{Colors.CYAN}Available repos:{Colors.RESET}")
692-
for name in manager.get_all_repos():
693-
typer.echo(f" • {name}")
694-
typer.echo(f"\n{Colors.CYAN}Installed marketplaces:{Colors.RESET}")
695-
for name in handler.get_known_marketplaces():
696-
typer.echo(f" • {name}")
802+
_display_marketplace_not_found(manager, handler, marketplace)
697803
raise typer.Exit(1)
698804

805+
# Fetch plugins
699806
typer.echo(f"{Colors.CYAN}Fetching plugins from {marketplace}...{Colors.RESET}")
700-
701-
from code_assistant_manager.plugins.fetch import fetch_repo_info
702-
703807
info = fetch_repo_info(repo_owner, repo_name, repo_branch)
808+
704809
if not info or not info.plugins:
705810
typer.echo(f"{Colors.RED}✗ Could not fetch plugins from repo.{Colors.RESET}")
706811
raise typer.Exit(1)
707812

708-
# Filter plugins
709-
plugins = info.plugins
710-
if query:
711-
query_lower = query.lower()
712-
plugins = [
713-
p
714-
for p in plugins
715-
if query_lower in p.get("name", "").lower()
716-
or query_lower in p.get("description", "").lower()
717-
]
718-
719-
if category:
720-
category_lower = category.lower()
721-
plugins = [
722-
p for p in plugins if category_lower in p.get("category", "").lower()
723-
]
724-
725-
# Display results
813+
# Filter and display
814+
plugins = _filter_plugins(info.plugins, query, category)
726815
total = len(plugins)
727816
plugins = plugins[:limit]
728817

729-
typer.echo(
730-
f"\n{Colors.BOLD}{info.name}{Colors.RESET} - {info.description or 'No description'}"
731-
)
732-
if info.version:
733-
typer.echo(f"Version: {info.version}")
734-
typer.echo(f"Total plugins: {info.plugin_count}")
735-
736-
if query or category:
737-
typer.echo(f"Matching: {total}")
738-
818+
_display_marketplace_header(info, query, category, total)
739819
typer.echo(f"\n{Colors.BOLD}Plugins:{Colors.RESET}\n")
740820

741-
# Get unique categories for reference
742-
categories = set()
743-
for p in info.plugins:
744-
if p.get("category"):
745-
categories.add(p["category"])
746-
747-
for p in plugins:
748-
name = p.get("name", "unknown")
749-
version = p.get("version", "")
750-
desc = p.get("description", "")
751-
cat = p.get("category", "")
821+
for plugin in plugins:
822+
_display_plugin(plugin)
752823

753-
version_str = f" v{version}" if version else ""
754-
cat_str = f" [{cat}]" if cat else ""
755-
756-
typer.echo(
757-
f" {Colors.BOLD}{name}{Colors.RESET}{version_str}{Colors.CYAN}{cat_str}{Colors.RESET}"
758-
)
759-
if desc:
760-
# Truncate long descriptions
761-
if len(desc) > 80:
762-
desc = desc[:77] + "..."
763-
typer.echo(f" {desc}")
764-
765-
if total > limit:
766-
typer.echo(f"\n ... and {total - limit} more")
767-
768-
# Show categories if available
769-
if categories:
770-
typer.echo(
771-
f"\n{Colors.CYAN}Categories:{Colors.RESET} {', '.join(sorted(categories))}"
772-
)
773-
774-
typer.echo(
775-
f"\n{Colors.CYAN}Install with:{Colors.RESET} cam plugin install <plugin-name>@{marketplace}"
776-
)
777-
typer.echo()
824+
_display_marketplace_footer(info, marketplace, total, limit)
778825

779826

780827
@plugin_app.command("fetch")

0 commit comments

Comments
 (0)