@@ -2058,6 +2058,54 @@ def listinstalled():
20582058 return plugins
20592059
20602060
2061+ def find_plugin_candidates (source : SourceDir , depth = 2 ) -> list :
2062+ """Filter through a source and return any candidates that appear to be
2063+ installable plugins with the registered installers."""
2064+ candidates = []
2065+ assert isinstance (source , SourceDir )
2066+ if not source .contents and not source .prepopulated :
2067+ source .populate ()
2068+
2069+ guess = InstInfo (source .name , source .location , None , source_dir = source )
2070+ if guess .get_inst_details ():
2071+ candidates .append (source .name )
2072+ if depth <= 1 :
2073+ return candidates
2074+
2075+ for c in source .contents :
2076+ if not isinstance (c , SourceDir ):
2077+ continue
2078+ candidates .extend (find_plugin_candidates (c , depth = depth - 1 ))
2079+
2080+ return candidates
2081+
2082+
2083+ def available_plugins () -> list :
2084+ """List installable plugins available from the sources list"""
2085+ candidates = []
2086+ # FIXME: update for LoadedSource object
2087+ for source in RECKLESS_SOURCES :
2088+ if source .type == Source .UNKNOWN :
2089+ log .debug (f'confusing source: { source .type } ' )
2090+ continue
2091+ # It takes too many API calls to query for installable plugins accurately.
2092+ if source .type == Source .GITHUB_REPO and not source .local_clone :
2093+ # FIXME: ignoring non-cloned repos for now.
2094+ log .debug (f'unable to search { source .original_source } without a local clone of the repository.' )
2095+ continue
2096+
2097+ if source .local_clone :
2098+ candidates .extend (find_plugin_candidates (source .local_clone ))
2099+ else :
2100+ candidates .extend (find_plugin_candidates (source .content ))
2101+
2102+ # Order and deduplicate results
2103+ candidates = list (set (candidates ))
2104+ candidates .sort ()
2105+ log .info (' ' .join (candidates ))
2106+ return candidates
2107+
2108+
20612109def report_version () -> str :
20622110 """return reckless version"""
20632111 log .info (__VERSION__ )
@@ -2131,6 +2179,10 @@ if __name__ == '__main__':
21312179 search_cmd .add_argument ('targets' , type = str , nargs = '*' )
21322180 search_cmd .set_defaults (func = search )
21332181
2182+ available_cmd = cmd1 .add_parser ('available' , help = 'list plugins available '
2183+ 'from the sources list' )
2184+ available_cmd .set_defaults (func = available_plugins )
2185+
21342186 enable_cmd = cmd1 .add_parser ('enable' , help = 'dynamically enable a plugin '
21352187 'and update config' )
21362188 enable_cmd .add_argument ('targets' , type = str , nargs = '*' )
@@ -2170,7 +2222,7 @@ if __name__ == '__main__':
21702222
21712223 all_parsers = [parser , install_cmd , uninstall_cmd , search_cmd , enable_cmd ,
21722224 disable_cmd , list_parse , source_add , source_rem , help_cmd ,
2173- update , list_cmd ]
2225+ update , list_cmd , available_cmd ]
21742226 for p in all_parsers :
21752227 # This default depends on the .lightning directory
21762228 p .add_argument ('-d' , '--reckless-dir' , action = StoreIdempotent ,
0 commit comments