============================================================================ list — show what's installed across code agents ============================================================================
(kind entities.Kind)
| 973 | // ============================================================================ |
| 974 | |
| 975 | func entityListCommand(kind entities.Kind) *cobra.Command { |
| 976 | var ( |
| 977 | showRepos bool |
| 978 | appFilter string |
| 979 | ) |
| 980 | cmd := &cobra.Command{ |
| 981 | Use: "list", |
| 982 | Aliases: []string{"ls"}, |
| 983 | Short: "List installed " + string(kind) + "s across code agents", |
| 984 | Long: "List " + string(kind) + `s installed across all supported code agents. |
| 985 | |
| 986 | Scans the installation directories of all supported agents (claude, codex, |
| 987 | gemini, copilot, cursor, and many more) and reports what is installed. |
| 988 | |
| 989 | Use --app to filter to a specific agent. |
| 990 | Use --repos to also display configured repository sources.`, |
| 991 | Example: " cam " + string(kind) + " list\n" + |
| 992 | " cam " + string(kind) + " list --app claude\n" + |
| 993 | " cam " + string(kind) + " list --repos", |
| 994 | Args: cobra.NoArgs, |
| 995 | RunE: func(cmd *cobra.Command, args []string) error { |
| 996 | out := cmd.OutOrStdout() |
| 997 | |
| 998 | apps := entities.AppPathsFor(kind) |
| 999 | if apps == nil { |
| 1000 | fmt.Fprintf(out, "No app paths configured for %ss\n", kind) |
| 1001 | return nil |
| 1002 | } |
| 1003 | |
| 1004 | appsToScan := apps |
| 1005 | if appFilter != "" { |
| 1006 | if dest, ok := apps[appFilter]; ok { |
| 1007 | appsToScan = map[string]string{appFilter: dest} |
| 1008 | } else { |
| 1009 | return fmt.Errorf("unknown app %q (supported: %s)", |
| 1010 | appFilter, strings.Join(entities.SupportedApps(kind), ", ")) |
| 1011 | } |
| 1012 | } |
| 1013 | |
| 1014 | type installedEntry struct { |
| 1015 | name string |
| 1016 | app string |
| 1017 | path string |
| 1018 | } |
| 1019 | var installed []installedEntry |
| 1020 | anyFound := false |
| 1021 | |
| 1022 | sortedApps := make([]string, 0, len(appsToScan)) |
| 1023 | for app := range appsToScan { |
| 1024 | sortedApps = append(sortedApps, app) |
| 1025 | } |
| 1026 | sort.Strings(sortedApps) |
| 1027 | |
| 1028 | // Track Claude-specific plugin listing separately. |
| 1029 | claudeHandled := false |
| 1030 | claudePluginTotal := 0 |
| 1031 | |
| 1032 | for _, app := range sortedApps { |
no test coverage detected