toast-cli 3.0.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
toast/__init__.py ADDED
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import click
4
+ import importlib
5
+ import inspect
6
+ import os
7
+ import pkgutil
8
+ import sys
9
+ from typing import List, Type
10
+ from toast.helpers import CustomHelpGroup
11
+
12
+ def discover_and_load_plugins(plugins_package_name: str = "toast.plugins") -> List[Type]:
13
+ """
14
+ Dynamically discover and load all plugin classes from the plugins package.
15
+
16
+ Args:
17
+ plugins_package_name: Name of the plugins package to search
18
+
19
+ Returns:
20
+ List of plugin classes that extend BasePlugin
21
+ """
22
+ from toast.plugins.base_plugin import BasePlugin
23
+
24
+ discovered_plugins = []
25
+
26
+ try:
27
+ # Import the plugins package
28
+ plugins_package = importlib.import_module(plugins_package_name)
29
+ plugins_path = os.path.dirname(plugins_package.__file__)
30
+
31
+ # Discover all modules in the plugins package
32
+ for _, name, is_pkg in pkgutil.iter_modules([plugins_path]):
33
+ # Skip the base_plugin module and __init__.py
34
+ if name == "base_plugin" or name == "__init__" or name == "utils":
35
+ continue
36
+
37
+ # Import the module
38
+ module_name = f"{plugins_package_name}.{name}"
39
+ try:
40
+ module = importlib.import_module(module_name)
41
+
42
+ # Find all classes in the module that are subclasses of BasePlugin
43
+ for item_name, item in inspect.getmembers(module, inspect.isclass):
44
+ if (issubclass(item, BasePlugin) and
45
+ item is not BasePlugin and
46
+ item.__module__ == module_name):
47
+ discovered_plugins.append(item)
48
+ except ImportError as e:
49
+ click.echo(f"Error loading plugin module {module_name}: {e}", err=True)
50
+
51
+ except ImportError as e:
52
+ click.echo(f"Error loading plugins package {plugins_package_name}: {e}", err=True)
53
+
54
+ return discovered_plugins
55
+
56
+
57
+ @click.group(cls=CustomHelpGroup)
58
+ def toast_cli():
59
+ """
60
+ Toast command-line tool with dynamically loaded plugins.
61
+ """
62
+ pass
63
+
64
+ def main():
65
+ # Discover and load all plugins
66
+ plugins = discover_and_load_plugins()
67
+
68
+ if not plugins:
69
+ click.echo("No plugins were discovered", err=True)
70
+ sys.exit(1)
71
+
72
+ # Register each plugin with the CLI
73
+ for plugin_class in plugins:
74
+ try:
75
+ plugin_class.register(toast_cli)
76
+ except Exception as e:
77
+ click.echo(f"Error registering plugin {plugin_class.__name__}: {e}", err=True)
78
+
79
+ # Run the CLI
80
+ toast_cli()
81
+
82
+ if __name__ == "__main__":
83
+ main()
toast/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from toast import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
toast/helpers.py ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import click
4
+ import os
5
+
6
+ def display_logo():
7
+ """Display the toast.sh ASCII logo"""
8
+ logo = """
9
+ _ _ _
10
+ | |_ ___ __ _ ___| |_ ___| |__
11
+ | __/ _ \ / _' / __| __| / __| '_ \\
12
+ | || (_) | (_| \__ \ |_ _\__ \ | | |
13
+ \__\___/ \__,_|___/\__(-)___/_| |_| v{0}
14
+ """.format(get_version())
15
+ click.echo(logo)
16
+ click.echo("=" * 80)
17
+
18
+ def get_version():
19
+ """Get the version from the VERSION file"""
20
+ try:
21
+ version_file = os.path.join(os.path.dirname(__file__), "..", "VERSION")
22
+ if os.path.exists(version_file):
23
+ with open(version_file, "r") as f:
24
+ version = f.read().strip()
25
+ return version
26
+ return "3.0.0"
27
+ except Exception:
28
+ return "3.0.0"
29
+
30
+ class CustomHelpCommand(click.Command):
31
+ def get_help(self, ctx):
32
+ display_logo()
33
+ return super().get_help(ctx)
34
+
35
+ class CustomHelpGroup(click.Group):
36
+ def get_help(self, ctx):
37
+ display_logo()
38
+ return super().get_help(ctx)
@@ -0,0 +1 @@
1
+ # Make the plugins directory a Python package
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import click
4
+ import subprocess
5
+ from toast.plugins.base_plugin import BasePlugin
6
+
7
+ class AmPlugin(BasePlugin):
8
+ """Plugin for 'am' command - shows AWS caller identity."""
9
+
10
+ name = "am"
11
+ help = "Show AWS caller identity"
12
+
13
+ @classmethod
14
+ def execute(cls, **kwargs):
15
+ try:
16
+ result = subprocess.run(["aws", "sts", "get-caller-identity"], capture_output=True, text=True)
17
+ if result.returncode == 0:
18
+ formatted_json = subprocess.run(["jq", "-C", "."], input=result.stdout, capture_output=True, text=True)
19
+ click.echo(formatted_json.stdout)
20
+ else:
21
+ click.echo("Error fetching AWS caller identity.")
22
+ except Exception as e:
23
+ click.echo(f"Error fetching AWS caller identity: {e}")
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import click
4
+
5
+ class BasePlugin:
6
+ """Base class for all plugins."""
7
+
8
+ name = None # Command name
9
+ help = None # Command help text
10
+
11
+ @classmethod
12
+ def register(cls, cli_group):
13
+ """Register the plugin with the CLI group."""
14
+ if not cls.name:
15
+ raise ValueError(f"Plugin {cls.__name__} must define a name")
16
+
17
+ # Use regular Command class to avoid showing logo for subcommands
18
+ @cli_group.command(name=cls.name, help=cls.help, cls=click.Command)
19
+ @cls.get_arguments
20
+ def command(**kwargs):
21
+ return cls.execute(**kwargs)
22
+
23
+ @classmethod
24
+ def get_arguments(cls, func):
25
+ """Define command arguments. Override in subclass."""
26
+ return func
27
+
28
+ @classmethod
29
+ def execute(cls, **kwargs):
30
+ """Execute the command. Override in subclass."""
31
+ raise NotImplementedError(f"Plugin {cls.__name__} must implement execute method")
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import click
4
+ import subprocess
5
+ import os
6
+ from toast.plugins.base_plugin import BasePlugin
7
+ from toast.plugins.utils import select_from_list
8
+
9
+ class CdwPlugin(BasePlugin):
10
+ """Plugin for 'cdw' command - helps navigate to workspace directories."""
11
+
12
+ name = "cdw"
13
+ help = "Navigate to a workspace directory"
14
+
15
+ @classmethod
16
+ def execute(cls, **kwargs):
17
+ workspace_dir = os.path.expanduser("~/workspace")
18
+ if not os.path.exists(workspace_dir):
19
+ os.makedirs(workspace_dir)
20
+
21
+ result = subprocess.run(["find", workspace_dir, "-mindepth", "1", "-maxdepth", "2", "-type", "d"], capture_output=True, text=True)
22
+ directories = sorted(result.stdout.splitlines())
23
+
24
+ if not directories:
25
+ click.echo("No directories found in ~/workspace.")
26
+ return
27
+
28
+ selected_dir = select_from_list(directories, "Select a directory")
29
+
30
+ if selected_dir:
31
+ click.echo(selected_dir)
32
+ else:
33
+ click.echo("No directory selected.", err=True)
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import click
4
+ import subprocess
5
+ from toast.plugins.base_plugin import BasePlugin
6
+ from toast.plugins.utils import select_from_list
7
+
8
+ class CtxPlugin(BasePlugin):
9
+ """Plugin for 'ctx' command - manages Kubernetes contexts."""
10
+
11
+ name = "ctx"
12
+ help = "Manage Kubernetes contexts"
13
+
14
+ @classmethod
15
+ def execute(cls, **kwargs):
16
+ result = subprocess.run(["kubectl", "config", "get-contexts", "-o=name"], capture_output=True, text=True)
17
+ if result.returncode != 0:
18
+ click.echo("Error fetching Kubernetes contexts. Is kubectl configured correctly?")
19
+ return
20
+
21
+ contexts = sorted(result.stdout.splitlines())
22
+ contexts.append("[New...]")
23
+ if len(contexts) > 1:
24
+ contexts.append("[Del...]")
25
+
26
+ selected_ctx = select_from_list(contexts, "Select a Kubernetes context")
27
+
28
+ if selected_ctx == "[New...]":
29
+ result = subprocess.run(["aws", "eks", "list-clusters", "--query", "clusters", "--output", "text"], capture_output=True, text=True)
30
+ if result.returncode != 0:
31
+ click.echo("Error fetching EKS clusters.")
32
+ return
33
+
34
+ clusters = sorted(result.stdout.split())
35
+ if not clusters:
36
+ click.echo("No EKS clusters found.")
37
+ return
38
+
39
+ selected_cluster = select_from_list(clusters, "Select an EKS cluster")
40
+
41
+ if selected_cluster:
42
+ subprocess.run(["aws", "eks", "update-kubeconfig", "--name", selected_cluster, "--alias", selected_cluster])
43
+ click.echo(f"Updated kubeconfig for {selected_cluster}")
44
+ else:
45
+ click.echo("No cluster selected.")
46
+ elif selected_ctx == "[Del...]":
47
+ delete_contexts = [ctx for ctx in contexts if ctx not in ("[New...]", "[Del...]")]
48
+ delete_contexts.append("[All...]")
49
+ selected_to_delete = select_from_list(delete_contexts, "Select a context to delete")
50
+ if selected_to_delete == "[All...]":
51
+ subprocess.run(["kubectl", "config", "unset", "contexts"])
52
+ click.echo("Deleted all Kubernetes contexts.")
53
+ elif selected_to_delete:
54
+ subprocess.run(["kubectl", "config", "delete-context", selected_to_delete])
55
+ click.echo(f"Deleted Kubernetes context: {selected_to_delete}")
56
+ else:
57
+ click.echo("No context selected for deletion.")
58
+ elif selected_ctx:
59
+ subprocess.run(["kubectl", "config", "use-context", selected_ctx])
60
+ click.echo(f"Switched to Kubernetes context: {selected_ctx}")
61
+ else:
62
+ click.echo("No context selected.")
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import click
4
+ import subprocess
5
+ import os
6
+ import dotenv
7
+ from pathlib import Path
8
+ from toast.plugins.base_plugin import BasePlugin
9
+ from toast.plugins.utils import select_from_list
10
+
11
+ class EnvPlugin(BasePlugin):
12
+ """Plugin for 'env' command - sets environment."""
13
+
14
+ name = "env"
15
+ help = "Set environment with AWS profile"
16
+
17
+ @classmethod
18
+ def get_arguments(cls, func):
19
+ func = click.argument('env_name', required=False)(func)
20
+ return func
21
+
22
+ @classmethod
23
+ def execute(cls, env_name=None, **kwargs):
24
+ # Try to get AWS_ENV_PATH from .env file
25
+ dotenv_path = Path('.env')
26
+ env_path = None
27
+
28
+ if dotenv_path.exists():
29
+ dotenv.load_dotenv(dotenv_path)
30
+ env_path = os.environ.get("AWS_ENV_PATH")
31
+
32
+ # If AWS_ENV_PATH is not set, create it
33
+ if not env_path:
34
+ # Get username using whoami command
35
+ result = subprocess.run(["whoami"], capture_output=True, text=True)
36
+ default_username = result.stdout.strip()
37
+
38
+ # Create github.com directory if it doesn't exist
39
+ github_dir = os.path.expanduser("~/workspace/github.com")
40
+ if not os.path.exists(github_dir):
41
+ os.makedirs(github_dir, exist_ok=True)
42
+ click.echo(f"Created directory: {github_dir}")
43
+
44
+ # Ask user to input username or use provided env_name as username
45
+ if not env_name:
46
+ click.echo(f"Enter GitHub username (default: {default_username}):")
47
+ input_username = input().strip()
48
+ username = input_username if input_username else default_username
49
+ else:
50
+ # Check if env_name might be a profile name rather than username
51
+ potential_path = os.path.expanduser(f"~/workspace/github.com/{default_username}/keys/env/{env_name}")
52
+ if os.path.exists(potential_path):
53
+ # env_name is likely a profile name, use default username
54
+ username = default_username
55
+ else:
56
+ # env_name is likely a username
57
+ username = env_name
58
+
59
+ # Set AWS_ENV_PATH
60
+ env_path = os.path.expanduser(f"~/workspace/github.com/{username}/keys/env")
61
+
62
+ # Create directory if it doesn't exist
63
+ env_dir = os.path.dirname(env_path)
64
+ if not os.path.exists(env_dir):
65
+ os.makedirs(env_dir, exist_ok=True)
66
+ click.echo(f"Created directory: {env_dir}")
67
+
68
+ # Update .env file
69
+ with open(dotenv_path, 'a+') as f:
70
+ f.seek(0)
71
+ content = f.read()
72
+ if "AWS_ENV_PATH" not in content:
73
+ if content and not content.endswith('\n'):
74
+ f.write('\n')
75
+ f.write(f"AWS_ENV_PATH={env_path}\n")
76
+
77
+ # Export the environment variable
78
+ os.environ["AWS_ENV_PATH"] = env_path
79
+
80
+ # List available profiles if env_path exists
81
+ if os.path.exists(env_path):
82
+ try:
83
+ profiles = [f for f in os.listdir(env_path) if os.path.isfile(os.path.join(env_path, f))]
84
+
85
+ if not profiles:
86
+ click.echo(f"No profiles found in {env_path}")
87
+ return
88
+
89
+ if not env_name or env_name not in profiles:
90
+ selected_profile = select_from_list(profiles, "Select an AWS profile")
91
+ if selected_profile:
92
+ env_name = selected_profile
93
+ else:
94
+ click.echo("No profile selected.")
95
+ return
96
+
97
+ # Load the selected profile and environment variables
98
+ profile_path = os.path.join(env_path, env_name)
99
+ aws_access_key_id = None
100
+ aws_secret_access_key = None
101
+ aws_region = None
102
+
103
+ if os.path.exists(profile_path):
104
+ with open(profile_path, 'r') as f:
105
+ for line in f:
106
+ if '=' in line:
107
+ key, value = line.strip().split('=', 1)
108
+ os.environ[key] = value
109
+
110
+ # Capture AWS credentials for config file
111
+ if key == "AWS_ACCESS_KEY_ID":
112
+ aws_access_key_id = value
113
+ elif key == "AWS_SECRET_ACCESS_KEY":
114
+ aws_secret_access_key = value
115
+ elif key == "AWS_REGION":
116
+ aws_region = value
117
+
118
+ # Ensure AWS CLI config directory exists
119
+ aws_config_dir = os.path.expanduser("~/.aws")
120
+ if not os.path.exists(aws_config_dir):
121
+ os.makedirs(aws_config_dir, exist_ok=True)
122
+
123
+ # Update AWS config and credentials files if we have the necessary info
124
+ if aws_access_key_id and aws_secret_access_key:
125
+ # Update credentials file for both profile name and default
126
+ credentials_file = os.path.join(aws_config_dir, "credentials")
127
+
128
+ # Update the named profile
129
+ cls._update_aws_file(credentials_file, env_name, {
130
+ "aws_access_key_id": aws_access_key_id,
131
+ "aws_secret_access_key": aws_secret_access_key
132
+ })
133
+
134
+ # Also update default profile with the same credentials
135
+ cls._update_aws_file(credentials_file, "default", {
136
+ "aws_access_key_id": aws_access_key_id,
137
+ "aws_secret_access_key": aws_secret_access_key
138
+ })
139
+
140
+ click.echo(f"Updated default AWS profile with {env_name} credentials")
141
+
142
+ # Update config file for both profile name and default
143
+ config_file = os.path.join(aws_config_dir, "config")
144
+ config_entries = {}
145
+ if aws_region:
146
+ config_entries["region"] = aws_region
147
+
148
+ if config_entries:
149
+ # Update the named profile
150
+ cls._update_aws_file(config_file, f"profile {env_name}", config_entries)
151
+
152
+ # Also update default profile
153
+ cls._update_aws_file(config_file, "default", config_entries)
154
+
155
+ # Set the AWS_PROFILE environment variable
156
+ os.environ["AWS_PROFILE"] = env_name
157
+ click.echo(f"Set AWS_PROFILE={env_name}")
158
+
159
+ click.echo(f"Set environment to {env_name}")
160
+
161
+ # Display region if available
162
+ if "AWS_REGION" in os.environ:
163
+ click.echo(f"AWS Region: {os.environ['AWS_REGION']}")
164
+
165
+ # Display profile information using AWS CLI with colored output
166
+ try:
167
+ result = subprocess.run(["aws", "sts", "get-caller-identity"], capture_output=True, text=True)
168
+ if result.returncode == 0:
169
+ formatted_json = subprocess.run(["jq", "-C", "."], input=result.stdout, capture_output=True, text=True)
170
+ click.echo(formatted_json.stdout)
171
+ else:
172
+ click.echo("Error fetching AWS caller identity.")
173
+ except Exception as e:
174
+ click.echo(f"Error fetching AWS identity: {e}")
175
+
176
+ except Exception as e:
177
+ click.echo(f"Error setting environment: {e}")
178
+ else:
179
+ click.echo(f"Environment path does not exist: {env_path}")
180
+
181
+ @staticmethod
182
+ def _update_aws_file(file_path, section_name, entries):
183
+ """Update AWS credentials or config file with given section and entries"""
184
+ config_content = ""
185
+ section_exists = False
186
+
187
+ # Read existing content if file exists
188
+ if os.path.exists(file_path):
189
+ with open(file_path, 'r') as f:
190
+ config_content = f.read()
191
+
192
+ # Parse sections
193
+ sections = {}
194
+ current_section = None
195
+ for line in config_content.splitlines():
196
+ line = line.strip()
197
+ if line.startswith('[') and line.endswith(']'):
198
+ current_section = line[1:-1]
199
+ sections[current_section] = []
200
+ elif current_section is not None:
201
+ sections[current_section].append(line)
202
+
203
+ # Update or add section
204
+ sections[section_name] = [f"{key} = {value}" for key, value in entries.items()]
205
+
206
+ # Write updated content
207
+ with open(file_path, 'w') as f:
208
+ for section, lines in sections.items():
209
+ f.write(f"[{section}]\n")
210
+ for line in lines:
211
+ f.write(f"{line}\n")
212
+ f.write("\n") # Empty line between sections
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import click
4
+ import os
5
+ import subprocess
6
+ import re
7
+ from toast.plugins.base_plugin import BasePlugin
8
+
9
+
10
+ class GitPlugin(BasePlugin):
11
+ """Plugin for 'git' command - handles Git repository operations."""
12
+
13
+ name = "git"
14
+ help = "Manage Git repositories"
15
+
16
+ @classmethod
17
+ def get_arguments(cls, func):
18
+ func = click.argument("command", required=True)(func)
19
+ func = click.argument("repo_name", required=True)(func)
20
+ func = click.option(
21
+ "--target", "-t", help="Target directory name for clone operation"
22
+ )(func)
23
+ return func
24
+
25
+ @classmethod
26
+ def execute(cls, command, repo_name, target=None, **kwargs):
27
+ # Get the current path
28
+ current_path = os.getcwd()
29
+
30
+ # Check if the current path matches the expected pattern
31
+ pattern = r"^.*/workspace/github.com/([^/]+)$"
32
+ match = re.match(pattern, current_path)
33
+
34
+ if not match:
35
+ click.echo(
36
+ "Error: Current directory must be in ~/workspace/github.com/{username} format"
37
+ )
38
+ return
39
+
40
+ # Extract username from the path
41
+ username = match.group(1)
42
+
43
+ if command == "clone" or command == "cl":
44
+ # Determine the target directory name
45
+ target_dir = target if target else repo_name
46
+
47
+ # Construct the repository URL
48
+ repo_url = f"https://github.com/{username}/{repo_name}.git"
49
+
50
+ # Target path in the current directory
51
+ target_path = os.path.join(current_path, target_dir)
52
+
53
+ # Check if the target directory already exists
54
+ if os.path.exists(target_path):
55
+ click.echo(f"Error: Target directory '{target_dir}' already exists")
56
+ return
57
+
58
+ # Clone the repository
59
+ click.echo(f"Cloning {repo_url} into {target_path}...")
60
+ try:
61
+ result = subprocess.run(
62
+ ["git", "clone", repo_url, target_path],
63
+ capture_output=True,
64
+ text=True,
65
+ )
66
+
67
+ if result.returncode == 0:
68
+ click.echo(f"Successfully cloned {repo_name} to {target_path}")
69
+ else:
70
+ click.echo(f"Error cloning repository: {result.stderr}")
71
+ except Exception as e:
72
+ click.echo(f"Error executing git command: {e}")
73
+
74
+ elif command == "rm":
75
+ # Path to the repository
76
+ repo_path = os.path.join(current_path, repo_name)
77
+
78
+ # Check if the repository exists
79
+ if not os.path.exists(repo_path):
80
+ click.echo(f"Error: Repository directory '{repo_name}' does not exist")
81
+ return
82
+
83
+ try:
84
+ # Remove the repository
85
+ subprocess.run(["rm", "-rf", repo_path], check=True)
86
+ click.echo(f"Successfully removed {repo_path}")
87
+ except Exception as e:
88
+ click.echo(f"Error removing repository: {e}")
89
+
90
+ else:
91
+ click.echo(f"Unknown command: {command}")
92
+ click.echo("Available commands: clone, rm")
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import click
4
+ import subprocess
5
+ from toast.plugins.base_plugin import BasePlugin
6
+ from toast.plugins.utils import select_from_list
7
+
8
+ class RegionPlugin(BasePlugin):
9
+ """Plugin for 'region' command - sets AWS region."""
10
+
11
+ name = "region"
12
+ help = "Set AWS region"
13
+
14
+ @classmethod
15
+ def execute(cls, **kwargs):
16
+ try:
17
+ result = subprocess.run(["aws", "ec2", "describe-regions", "--query", "Regions[].RegionName", "--output", "text"], capture_output=True, text=True)
18
+ regions = sorted(result.stdout.split())
19
+ if not regions:
20
+ click.echo("No regions found.")
21
+ return
22
+
23
+ selected_region = select_from_list(regions, "Select AWS Region")
24
+
25
+ if selected_region:
26
+ subprocess.run(["aws", "configure", "set", "default.region", selected_region])
27
+ subprocess.run(["aws", "configure", "set", "default.output", "json"])
28
+ click.echo(f"Set AWS region to {selected_region}")
29
+ else:
30
+ click.echo("No region selected.")
31
+ except Exception as e:
32
+ click.echo(f"Error fetching AWS regions: {e}")
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import click
4
+ from toast.plugins.base_plugin import BasePlugin
5
+
6
+ class SsmPlugin(BasePlugin):
7
+ """Plugin for 'ssm' command - runs SSM commands."""
8
+
9
+ name = "ssm"
10
+ help = "Run AWS SSM commands"
11
+
12
+ @classmethod
13
+ def execute(cls, **kwargs):
14
+ click.echo("Running SSM command")
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import click
4
+ from toast.plugins.base_plugin import BasePlugin
5
+
6
+ class UpdatePlugin(BasePlugin):
7
+ """Plugin for 'update' command - updates CLI tool."""
8
+
9
+ name = "update"
10
+ help = "Update CLI tool"
11
+
12
+ @classmethod
13
+ def execute(cls, **kwargs):
14
+ click.echo("Updating CLI tool")
toast/plugins/utils.py ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import subprocess
4
+ import click
5
+
6
+ def select_from_list(options, prompt="Select an option"):
7
+ try:
8
+ fzf_proc = subprocess.run(["fzf", "--height=15", "--reverse", "--border", "--prompt", prompt+": "], input="\n".join(options), capture_output=True, text=True)
9
+ return fzf_proc.stdout.strip()
10
+ except Exception as e:
11
+ click.echo(f"Error selecting from list: {e}")
12
+ return None