@@ -463,175 +463,178 @@ def view_plugin(
463463 typer .echo ()
464464
465465
466- @plugin_app .command ("fetch " )
467- def fetch_repo (
468- url : Optional [str ] = typer .Argument (
466+ @plugin_app .command ("status " )
467+ def plugin_status (
468+ app_type : Optional [str ] = typer .Option (
469469 None ,
470- help = "GitHub URL or owner/repo (e.g., https://github.com/owner/repo or owner/repo)" ,
471- ),
472- save : bool = typer .Option (
473- False ,
474- "--save" ,
475- "-s" ,
476- help = "Save the fetched repo to user config (only used with URL argument)" ,
470+ "--app" ,
471+ "-a" ,
472+ help = f"App type ({ ', ' .join (VALID_APP_TYPES )} ). If not specified, shows status for all apps." ,
477473 ),
478474):
479- """Fetch plugin repos from configured repositories or a specific GitHub URL.
475+ """Show plugin system status for an app, or all apps if none specified."""
476+ from code_assistant_manager .cli .option_utils import resolve_single_app
480477
481- Without URL: Re-fetches info from all configured plugin repositories.
482- With URL: Analyzes a GitHub repository to determine if it's a single plugin
483- or a marketplace with multiple plugins, then optionally saves it.
478+ # If no app specified, show status for all apps
479+ if app_type is None :
480+ typer . echo ( f" \n { Colors . BOLD } Plugin System Status (All Apps): { Colors . RESET } \n " )
484481
485- Examples:
486- cam plugin fetch # Fetch from all configured repos
487- cam plugin fetch owner/repo --save # Fetch and save a new repo
488- cam plugin fetch https://github.com/owner/repo --save
489- """
490- manager = PluginManager ()
482+ # Show common CAM configuration
483+ manager = PluginManager ()
484+ all_repos = manager .get_all_repos ()
485+ configured_marketplaces = {
486+ k : v for k , v in all_repos .items () if v .type == "marketplace"
487+ }
488+ configured_plugins = {k : v for k , v in all_repos .items () if v .type == "plugin" }
491489
492- # If no URL provided, fetch from all configured repos
493- if not url :
494490 typer .echo (
495- f"{ Colors .CYAN } Fetching plugin repos from all configured repositories... { Colors .RESET } "
491+ f"{ Colors .CYAN } Configured Marketplaces (CAM): { Colors .RESET } { len ( configured_marketplaces ) } "
496492 )
493+ for name , repo in sorted (configured_marketplaces .items ()):
494+ if repo .repo_owner and repo .repo_name :
495+ typer .echo (f" • { name } ({ repo .repo_owner } /{ repo .repo_name } )" )
496+ else :
497+ typer .echo (f" • { name } " )
497498
498- all_repos = manager .get_all_repos ()
499- if not all_repos :
499+ if configured_plugins :
500500 typer .echo (
501- f"{ Colors .YELLOW } No plugin repositories configured { Colors .RESET } "
501+ f"\n { Colors .CYAN } Configured Plugins (CAM): { Colors .RESET } { len ( configured_plugins ) } "
502502 )
503- return
503+ for name , repo in sorted (configured_plugins .items ()):
504+ if repo .repo_owner and repo .repo_name :
505+ typer .echo (f" • { name } ({ repo .repo_owner } /{ repo .repo_name } )" )
506+ else :
507+ typer .echo (f" • { name } " )
504508
505- success_count = 0
506- for repo_name , repo in all_repos .items ():
507- if not repo .enabled :
508- typer .echo (f" { Colors .YELLOW } ⊘{ Colors .RESET } { repo_name } (disabled)" )
509- continue
509+ typer .echo (f"\n { Colors .CYAN } { '=' * 50 } { Colors .RESET } \n " )
510510
511- if not repo . repo_owner or not repo . repo_name :
512- typer . echo (
513- f" { Colors . YELLOW } ⊘ { Colors . RESET } { repo_name } (no GitHub info)"
514- )
515- continue
511+ for app in VALID_APP_TYPES :
512+ show_app_info ( app , show_cam_config = False )
513+ if app != VALID_APP_TYPES [ - 1 ]: # Don't add separator after last app
514+ typer . echo ( f" \n { Colors . CYAN } { '=' * 50 } { Colors . RESET } \n " )
515+ return
516516
517- # Fetch repo info
518- from code_assistant_manager .plugins import fetch_repo_info
517+ # Show info for specific app
518+ app = resolve_single_app (app_type , VALID_APP_TYPES , default = "claude" )
519+ show_app_info (app )
519520
520- info = fetch_repo_info (
521- repo .repo_owner , repo .repo_name , repo .repo_branch or "main"
522- )
523521
524- if info :
525- plugin_info = (
526- f"{ info .plugin_count } plugins"
527- if info .type == "marketplace"
528- else "plugin"
529- )
530- typer .echo (
531- f" { Colors .GREEN } ✓{ Colors .RESET } { repo_name } ({ info .type } : { plugin_info } )"
532- )
533- success_count += 1
534- else :
535- typer .echo (
536- f" { Colors .RED } ✗{ Colors .RESET } { repo_name } (failed to fetch)"
537- )
522+ def show_app_info (app : str , show_cam_config : bool = True ):
523+ """Show plugin system information for a specific app."""
524+ handler = get_handler (app )
525+ manager = PluginManager ()
538526
539- typer .echo (
540- f"\n { Colors .GREEN } ✓ Fetched { success_count } /{ len (all_repos )} repositories{ Colors .RESET } "
541- )
542- typer .echo (
543- f"\n { Colors .CYAN } Run 'cam plugin repos' to see all configured repos{ Colors .RESET } "
544- )
545- return
527+ typer .echo (f"\n { Colors .BOLD } { app .capitalize ()} Plugin System:{ Colors .RESET } \n " )
546528
547- typer .echo (f"{ Colors .CYAN } Fetching repository info...{ Colors .RESET } " )
529+ # Show paths
530+ typer .echo (f"{ Colors .CYAN } Configuration:{ Colors .RESET } " )
531+ typer .echo (f" Home: { handler .home_dir } " )
532+ typer .echo (f" Plugins: { handler .user_plugins_dir } " )
533+ typer .echo (f" Marketplaces: { handler .marketplaces_dir } " )
534+ typer .echo (f" Settings: { handler .settings_file } " )
548535
549- # Parse and validate URL
550- parsed = parse_github_url (url )
551- if not parsed :
552- typer .echo (f"{ Colors .RED } ✗ Invalid GitHub URL: { url } { Colors .RESET } " )
553- raise typer .Exit (1 )
536+ # Check status
537+ typer .echo (f"\n { Colors .CYAN } Status:{ Colors .RESET } " )
554538
555- owner , repo , branch = parsed
556- typer .echo (f" Repository: { Colors .BOLD } { owner } /{ repo } { Colors .RESET } " )
539+ home_exists = handler .home_dir .exists ()
540+ status = (
541+ f"{ Colors .GREEN } ✓{ Colors .RESET } "
542+ if home_exists
543+ else f"{ Colors .RED } ✗{ Colors .RESET } "
544+ )
545+ typer .echo (f" { status } Home directory exists" )
557546
558- # Fetch repo info
559- info = fetch_repo_info_from_url (url )
560- if not info :
561- typer .echo (
562- f"{ Colors .RED } ✗ Could not fetch repository info. "
563- f"Make sure the repo has .claude-plugin/marketplace.json{ Colors .RESET } "
564- )
565- raise typer .Exit (1 )
547+ plugins_exists = handler .user_plugins_dir .exists ()
548+ status = (
549+ f"{ Colors .GREEN } ✓{ Colors .RESET } "
550+ if plugins_exists
551+ else f"{ Colors .RED } ✗{ Colors .RESET } "
552+ )
553+ typer .echo (f" { status } Plugins directory exists" )
566554
567- # Display results
568- typer .echo (f"\n { Colors .BOLD } Repository Information:{ Colors .RESET } \n " )
569- typer .echo (f" { Colors .CYAN } Name:{ Colors .RESET } { info .name } " )
570- typer .echo (f" { Colors .CYAN } Type:{ Colors .RESET } { info .type } " )
571- typer .echo (f" { Colors .CYAN } Description:{ Colors .RESET } { info .description or 'N/A' } " )
572- typer .echo (f" { Colors .CYAN } Branch:{ Colors .RESET } { info .branch } " )
555+ cli_path = handler .get_cli_path ()
556+ status = (
557+ f"{ Colors .GREEN } ✓{ Colors .RESET } " if cli_path else f"{ Colors .RED } ✗{ Colors .RESET } "
558+ )
559+ typer .echo (f" { status } { app .capitalize ()} CLI: { cli_path or 'Not found' } " )
573560
574- if info .version :
575- typer .echo (f" { Colors .CYAN } Version:{ Colors .RESET } { info .version } " )
576-
577- if info .type == "marketplace" :
578- typer .echo (f" { Colors .CYAN } Plugin Count:{ Colors .RESET } { info .plugin_count } " )
579- if info .plugins and len (info .plugins ) <= 10 :
580- typer .echo (f"\n { Colors .CYAN } Plugins:{ Colors .RESET } " )
581- for p in info .plugins [:10 ]:
582- typer .echo (f" • { p .get ('name' , 'unknown' )} " )
583- elif info .plugins :
584- typer .echo (f"\n { Colors .CYAN } Plugins:{ Colors .RESET } (showing first 10)" )
585- for p in info .plugins [:10 ]:
586- typer .echo (f" • { p .get ('name' , 'unknown' )} " )
587- typer .echo (f" ... and { len (info .plugins ) - 10 } more" )
588- else :
589- if info .plugin_path :
590- typer .echo (f" { Colors .CYAN } Plugin Path:{ Colors .RESET } { info .plugin_path } " )
591-
592- # Save if requested
593- if save :
594- # Check if already exists
595- existing = manager .get_repo (info .name )
596- if existing :
597- typer .echo (
598- f"\n { Colors .YELLOW } Repository '{ info .name } ' already exists in config.{ Colors .RESET } "
599- )
600- if not typer .confirm ("Overwrite?" ):
601- raise typer .Exit (0 )
602-
603- # Create PluginRepo and save
604- from code_assistant_manager .plugins .models import PluginRepo
605-
606- plugin_repo = PluginRepo (
607- name = info .name ,
608- description = info .description ,
609- repo_owner = info .owner ,
610- repo_name = info .repo ,
611- repo_branch = info .branch ,
612- plugin_path = info .plugin_path ,
613- type = info .type ,
614- enabled = True ,
615- )
616- manager .add_user_repo (plugin_repo )
561+ # Get configured repos from CAM (only show once for all apps)
562+ all_repos = manager .get_all_repos ()
563+ configured_marketplaces = {
564+ k : v for k , v in all_repos .items () if v .type == "marketplace"
565+ }
566+ configured_plugins = {k : v for k , v in all_repos .items () if v .type == "plugin" }
567+
568+ # Show configured marketplaces (from CAM) - only when requested
569+ if show_cam_config and app == VALID_APP_TYPES [0 ]: # Only show global config once
617570 typer .echo (
618- f"\n { Colors .GREEN } ✓ Saved ' { info . name } ' to user config as { info . type } { Colors . RESET } "
571+ f"\n { Colors .CYAN } Configured Marketplaces (CAM): { Colors . RESET } { len ( configured_marketplaces ) } "
619572 )
620- typer .echo (f" Config file: { manager .plugin_repos_file } " )
573+ for name , repo in sorted (configured_marketplaces .items ()):
574+ if repo .repo_owner and repo .repo_name :
575+ typer .echo (f" • { name } ({ repo .repo_owner } /{ repo .repo_name } )" )
576+ else :
577+ typer .echo (f" • { name } " )
621578
622- # Show next steps
623- if info . type == "marketplace" :
579+ # Show configured plugins (from CAM)
580+ if configured_plugins :
624581 typer .echo (
625- f"\n { Colors .CYAN } Next :{ Colors .RESET } cam plugin install { info . name } "
582+ f"\n { Colors .CYAN } Configured Plugins (CAM) :{ Colors .RESET } { len ( configured_plugins ) } "
626583 )
627- else :
628- typer .echo (
629- f"\n { Colors .CYAN } Next:{ Colors .RESET } cam plugin install { info .name } "
584+ for name , repo in sorted (configured_plugins .items ()):
585+ if repo .repo_owner and repo .repo_name :
586+ typer .echo (f" • { name } ({ repo .repo_owner } /{ repo .repo_name } )" )
587+ else :
588+ typer .echo (f" • { name } " )
589+
590+ # Show installed marketplaces (from app)
591+ installed_marketplaces = handler .get_known_marketplaces ()
592+ typer .echo (
593+ f"\n { Colors .CYAN } Installed Marketplaces ({ app .capitalize ()} ):{ Colors .RESET } { len (installed_marketplaces )} "
594+ )
595+ for name , info in sorted (installed_marketplaces .items ()):
596+ source = info .get ("source" , {})
597+ source_url = source .get ("url" , "" )
598+ # Extract repo from URL like https://github.com/owner/repo.git
599+ if "github.com" in source_url :
600+ repo_part = source_url .replace ("https://github.com/" , "" ).replace (
601+ ".git" , ""
630602 )
631- else :
632- typer .echo (
633- f"\n { Colors .CYAN } To save:{ Colors .RESET } cam plugin fetch '{ url } ' --save"
634- )
603+ typer .echo (f" • { name } ({ repo_part } )" )
604+ else :
605+ typer .echo (f" • { name } " )
606+
607+ # Show installed/enabled plugins with details
608+ enabled = handler .get_enabled_plugins ()
609+ enabled_plugins = {k : v for k , v in enabled .items () if v }
610+ disabled_plugins = {k : v for k , v in enabled .items () if not v }
611+
612+ typer .echo (
613+ f"\n { Colors .CYAN } Installed Plugins ({ app .capitalize ()} ):{ Colors .RESET } { len (enabled_plugins )} enabled, { len (disabled_plugins )} disabled"
614+ )
615+
616+ if enabled_plugins :
617+ typer .echo (f"\n { Colors .GREEN } Enabled:{ Colors .RESET } " )
618+ for plugin_key in sorted (enabled_plugins .keys ()):
619+ # Parse plugin key (format: plugin-name@marketplace or just plugin-name)
620+ if "@" in plugin_key :
621+ plugin_name , marketplace = plugin_key .split ("@" , 1 )
622+ typer .echo (
623+ f" { Colors .GREEN } ✓{ Colors .RESET } { plugin_name } ({ marketplace } )"
624+ )
625+ else :
626+ typer .echo (f" { Colors .GREEN } ✓{ Colors .RESET } { plugin_key } " )
627+
628+ if disabled_plugins :
629+ typer .echo (f"\n { Colors .RED } Disabled:{ Colors .RESET } " )
630+ for plugin_key in sorted (disabled_plugins .keys ()):
631+ if "@" in plugin_key :
632+ plugin_name , marketplace = plugin_key .split ("@" , 1 )
633+ typer .echo (
634+ f" { Colors .RED } ✗{ Colors .RESET } { plugin_name } ({ marketplace } )"
635+ )
636+ else :
637+ typer .echo (f" { Colors .RED } ✗{ Colors .RESET } { plugin_key } " )
635638
636639 typer .echo ()
637640
0 commit comments