diff --git a/composio/cli/__init__.py b/composio/cli/__init__.py index 0048353df5..a35fb50b0f 100644 --- a/composio/cli/__init__.py +++ b/composio/cli/__init__.py @@ -13,15 +13,27 @@ from composio.cli.logout import _logout from composio.cli.triggers import _triggers from composio.cli.whoami import _whoami +from composio.cli.utils.helpfulcmd import HelpfulCmdBase +class RichGroup(click.Group): + def format_help(self, ctx, formatter): + formatter.write("\n") -@click.group(name="composio") + super().format_help(ctx, formatter) + + formatter.write("\nšŸ“™ Examples:\n\n") + formatter.write(click.style("composio --help", fg='green') + click.style(" # Display help information\n", fg='black')) + formatter.write(click.style("composio add github", fg='green') + click.style(" # Add an integration to your account\n", fg='black')) + formatter.write(click.style("composio login", fg='green') + click.style(" # Log in to your Composio account\n", fg='black')) + + +@click.group(name="composio",cls=RichGroup) +@click.help_option("--help", "-h","-help") def composio() -> None: """ - Composio CLI Tool. + šŸ”— Composio CLI Tool. """ - composio.add_command(_add) composio.add_command(_apps) composio.add_command(_login) diff --git a/composio/cli/actions.py b/composio/cli/actions.py index b5d2e53f2a..95154d8ef9 100755 --- a/composio/cli/actions.py +++ b/composio/cli/actions.py @@ -13,9 +13,17 @@ from composio.cli.context import Context, pass_context from composio.client.enums import App from composio.exceptions import ComposioSDKError +from composio.cli.utils.helpfulcmd import HelpfulCmdBase +class ActionsExamples(HelpfulCmdBase, click.Group): + examples = [ + click.style("composio actions", fg='green') + click.style(" # List all actions\n", fg='black'), + click.style("composio actions --app slack", fg='green') + click.style(" # List all actions for the Slack app\n", fg='black'), + click.style("composio actions --use-case 'get channel messages'", fg='green') + click.style(" # List all actions for the 'get channel messages' use case\n", fg='black'), + ] -@click.group(name="actions", invoke_without_command=True) +@click.group(name="actions", invoke_without_command=True, cls=ActionsExamples) +@click.help_option("--help", "-h", "-help") @click.option( "--app", "apps", diff --git a/composio/cli/add.py b/composio/cli/add.py index 94dc20b1b3..7601979efb 100644 --- a/composio/cli/add.py +++ b/composio/cli/add.py @@ -25,9 +25,17 @@ from composio.constants import DEFAULT_ENTITY_ID from composio.exceptions import ComposioSDKError from composio.utils.url import get_web_url +from composio.cli.utils.helpfulcmd import HelpfulCmd -@click.command(name="add") +class AddIntegrationExamples(HelpfulCmd): + examples = [ + click.style("composio add ", fg='green') + click.style(" # Add a new integration\n", fg='black'), + click.style("composio add --no-browser", fg='green') + click.style(" # Add a new integration without opening the browser\n", fg='black'), + click.style("composio add -i ", fg='green') + click.style(" # Add a new integration using an existing integration ID\n", fg='black'), + ] +@click.command(name="add", cls=AddIntegrationExamples) +@click.help_option("--help", "-h", "-help") @click.argument("name", type=str) @click.option( "--no-browser", diff --git a/composio/cli/apps.py b/composio/cli/apps.py index 56331c6210..ae6fd43eac 100755 --- a/composio/cli/apps.py +++ b/composio/cli/apps.py @@ -15,6 +15,7 @@ from composio.client import ActionModel, AppModel, TriggerModel, enums from composio.client.local_handler import LocalToolHandler from composio.exceptions import ComposioSDKError +from composio.cli.utils.helpfulcmd import HelpfulCmdBase MODULE_TEMPLATE = """\"\"\" @@ -125,8 +126,14 @@ def event(self) -> str: {triggers} """ - -@click.group(name="apps", invoke_without_command=True) +class AppsExamples(HelpfulCmdBase, click.Group): + examples = [ + click.style("composio apps", fg='green') + click.style(" # List all apps\n", fg='black'), + click.style("composio apps --enabled", fg='green') + click.style(" # List only enabled apps\n", fg='black'), + click.style("composio apps update", fg='green') + click.style(" # Update local Apps database\n", fg='black'), + ] +@click.group(name="apps", invoke_without_command=True, cls=AppsExamples) +@click.help_option("--help", "-h", "-help") @click.option( "--enabled", is_flag=True, @@ -152,12 +159,18 @@ def _apps(context: Context, enabled: bool = False) -> None: raise click.ClickException(message=e.message) from e -@_apps.command(name="update") +class UpdateExamples(HelpfulCmdBase, click.Command): + examples = [ + click.style("composio apps update", fg='green') + click.style(" # Update local Apps database\n", fg='black'), + ] + +@_apps.command(name="update", cls=UpdateExamples) @click.option( "--beta", is_flag=True, help="Include beta apps.", ) +@click.help_option("--help", "-h", "-help") @pass_context def _update(context: Context, beta: bool = False) -> None: """Updates local Apps database.""" diff --git a/composio/cli/connections.py b/composio/cli/connections.py index 8c6d440d4f..502b7ffd4e 100644 --- a/composio/cli/connections.py +++ b/composio/cli/connections.py @@ -9,9 +9,17 @@ from composio.cli.context import Context, pass_context from composio.exceptions import ComposioSDKError +from composio.cli.utils.helpfulcmd import HelpfulCmdBase +class ConnectionsExamples(HelpfulCmdBase, click.Group): + examples = [ + click.style("composio connections", fg='green') + click.style(" # List all connections\n", fg='black'), + click.style("composio connections get 123", fg='green') + click.style(" # Get details of connection with ID 123\n", fg='black'), + click.style("composio connections delete 456", fg='green') + click.style(" # Delete connection with ID 456\n", fg='black'), + ] -@click.group(name="connections", invoke_without_command=True) +@click.group(name="connections", invoke_without_command=True, cls=ConnectionsExamples) +@click.help_option("--help", "-h", "-help") @pass_context def _connections(context: Context) -> None: """List composio connections for your account""" @@ -23,8 +31,14 @@ def _connections(context: Context) -> None: print(connection) -@_connections.command(name="get") +class GetExamples(HelpfulCmdBase, click.Command): + examples = [ + click.style("composio connections get 123", fg='green') + click.style(" # Get details of connection with ID 123\n", fg='black'), + ] + +@_connections.command(name="get", cls=GetExamples) @click.argument("id", type=str) +@click.help_option("--help", "-h", "-help") @pass_context def _get(context: Context, id: str) -> None: """Get connection information""" diff --git a/composio/cli/integrations.py b/composio/cli/integrations.py index 27759374be..11a707bc88 100644 --- a/composio/cli/integrations.py +++ b/composio/cli/integrations.py @@ -9,9 +9,18 @@ from composio.cli.context import Context, login_required, pass_context from composio.exceptions import ComposioSDKError +from composio.cli.utils.helpfulcmd import HelpfulCmdBase +class IntegrationsExamples(HelpfulCmdBase, click.Group): + examples = [ + click.style("composio integrations", fg='green') + click.style(" # List all integrations\n", fg='black'), + click.style("composio integrations add --name GitHub", fg='green') + click.style(" # Add a new integration named GitHub\n", fg='black'), + click.style("composio integrations remove --id 123", fg='green') + click.style(" # Remove integration with ID 123\n", fg='black'), + click.style("composio integrations update --id 456 --name GitLab", fg='green') + click.style(" # Update integration with ID 456 to name GitLab\n", fg='black'), + ] -@click.group(name="integrations", invoke_without_command=True) +@click.group(name="integrations", invoke_without_command=True, cls=IntegrationsExamples) +@click.help_option("--help", "-h", "-help") @login_required @pass_context def _integrations(context: Context) -> None: diff --git a/composio/cli/login.py b/composio/cli/login.py index 527a30bb6a..54e1fd371a 100644 --- a/composio/cli/login.py +++ b/composio/cli/login.py @@ -8,20 +8,27 @@ import webbrowser import click - from composio.cli.context import Context, pass_context from composio.client import Composio from composio.exceptions import ComposioSDKError from composio.utils.url import get_web_url +from composio.cli.utils.helpfulcmd import HelpfulCmdBase + +class Examples(HelpfulCmdBase, click.Command): + examples = [ + click.style("composio login --help", fg='green') + click.style(" # Display help for login command\n", fg='black'), + click.style("composio login --no-browser", fg='green') + click.style(" # Login without browser interaction\n", fg='black'), + ] -@click.command(name="login") +@click.command(name="login",cls=Examples) @click.option( "--no-browser", is_flag=True, default=False, help="Prevent from opening browser window", ) +@click.help_option("--help", "-h","-help") @pass_context def _login( context: Context, @@ -32,7 +39,9 @@ def _login( # TODO: Abstract away user_data = context.user_data if user_data.api_key is not None: - raise click.ClickException("Already logged in!") + context.console.print("\n[green]āœ” You're already logged in![/green]\n") + context.console.print("> Use [green]'composio logout'[/green] to log out and then login again") + return context.console.print("\n[green]> Authenticating...[/green]\n") try: diff --git a/composio/cli/logout.py b/composio/cli/logout.py index 1497a893be..8ed421daf8 100644 --- a/composio/cli/logout.py +++ b/composio/cli/logout.py @@ -9,9 +9,17 @@ from composio.cli.context import Context, pass_context from composio.exceptions import ComposioSDKError +from composio.cli.utils.helpfulcmd import HelpfulCmdBase +class Examples(HelpfulCmdBase, click.Command): + examples = [ + click.style("composio logout", fg='green') + click.style(" # Logout from the Composio SDK\n", fg='black'), + ] -@click.command(name="logout") + + +@click.command(name="logout",cls=Examples) +@click.help_option("--help", "-h","-help") @pass_context def _logout(context: Context) -> None: """Logout from the Composio SDK""" diff --git a/composio/cli/triggers.py b/composio/cli/triggers.py index 1af0bc61df..c9801d51bb 100644 --- a/composio/cli/triggers.py +++ b/composio/cli/triggers.py @@ -14,8 +14,18 @@ from composio.client.exceptions import NoItemsFound from composio.exceptions import ComposioSDKError - -@click.group(name="triggers", invoke_without_command=True) +from composio.cli.utils.helpfulcmd import HelpfulCmdBase + + +class TriggersExamples(HelpfulCmdBase, click.Group): + examples = [ + click.style("composio triggers", fg='green') + click.style(" # List all triggers\n", fg='black'), + click.style("composio triggers --active", fg='green') + click.style(" # List only active triggers\n", fg='black'), + click.style("composio triggers --id 12345", fg='green') + click.style(" # List trigger with specific ID\n", fg='black'), + click.style("composio triggers --app MyApp", fg='green') + click.style(" # List triggers for a specific app\n", fg='black'), + ] +@click.group(name="triggers", invoke_without_command=True, cls=TriggersExamples) +@click.help_option("--help", "-h", "-help") @click.option( "--active", is_flag=True, @@ -64,8 +74,14 @@ def _triggers( raise click.ClickException(message=e.message) from e -@_triggers.command(name="get") +class GetTriggerExamples(HelpfulCmdBase, click.Command): + examples = [ + click.style("composio triggers get ", fg='green') + click.style(" # Get details of a specific trigger by ID\n", fg='black'), + ] + +@_triggers.command(name="get", cls=GetTriggerExamples) @click.argument("id", type=str) +@click.help_option("--help", "-h", "-help") @pass_context def _get(context: Context, id: str) -> None: """Get a specific trigger information.""" @@ -95,8 +111,15 @@ def _get(context: Context, id: str) -> None: raise click.ClickException(message=e.message) from e -@_triggers.command(name="enable") + +class EnableTriggerExamples(HelpfulCmdBase, click.Command): + examples = [ + click.style("composio triggers enable ", fg='green') + click.style(" # Enable a trigger for an app\n", fg='black'), + ] + +@_triggers.command(name="enable", cls=EnableTriggerExamples) @click.argument("id", type=str) +@click.help_option("--help", "-h", "-help") @pass_context def _enable_trigger(context: Context, id: str) -> None: """Enable a trigger for an app""" @@ -129,9 +152,14 @@ def _enable_trigger(context: Context, id: str) -> None: except ComposioSDKError as e: raise click.ClickException(message=e.message) from e +class DisableTriggerExamples(HelpfulCmdBase, click.Command): + examples = [ + click.style("composio triggers disable ", fg='green') + click.style(" # Disable a trigger for an app\n", fg='black'), + ] -@_triggers.command(name="disable") +@_triggers.command(name="disable", cls=DisableTriggerExamples) @click.argument("id", type=str) +@click.help_option("--help", "-h", "-help") @pass_context def _disable_trigger(context: Context, id: str) -> None: """Disable a trigger for an app""" @@ -146,13 +174,20 @@ def _disable_trigger(context: Context, id: str) -> None: raise click.ClickException(message=e.message) from e + @_triggers.group(name="callbacks") def _callbacks() -> None: """Manage trigger callbacks.""" -@_callbacks.command(name="set") +class SetCallbackExamples(HelpfulCmdBase, click.Command): + examples = [ + click.style("composio triggers callbacks set ", fg='green') + click.style(" # Set callback URL\n", fg='black'), + ] + +@_callbacks.command(name="set", cls=SetCallbackExamples) @click.argument("url", type=str) +@click.help_option("--help", "-h", "-help") @pass_context def _set_callback(context: Context, url: str) -> None: """ @@ -167,7 +202,13 @@ def _set_callback(context: Context, url: str) -> None: raise click.ClickException(message=e.message) -@_callbacks.command(name="get") +class GetCallbackExamples(HelpfulCmdBase, click.Command): + examples = [ + click.style("composio triggers callbacks get", fg='green') + click.style(" # Get callback URL\n", fg='black'), + ] + +@_callbacks.command(name="get", cls=GetCallbackExamples) +@click.help_option("--help", "-h", "-help") @pass_context def _get_callback(context: Context) -> None: """ diff --git a/composio/cli/utils/helpfulcmd.py b/composio/cli/utils/helpfulcmd.py new file mode 100644 index 0000000000..822ab20adc --- /dev/null +++ b/composio/cli/utils/helpfulcmd.py @@ -0,0 +1,66 @@ +import click +import inspect +from composio.cli.context import Context, pass_context +from composio.client import Composio +from click.formatting import HelpFormatter +from composio.exceptions import ComposioSDKError +from composio.utils.url import get_web_url + +from click.core import Context as ClickContext + +class HelpfulCmdBase: + examples = [] + help = None + + def format_help_text(self, ctx: ClickContext, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + if self.help is not None: + # truncate the help text to the first form feed + text = inspect.cleandoc(self.help).partition("\f")[0] + else: + text = "" + + text = "šŸ“„" + text + if getattr(self, 'deprecated', False): + text = "(Deprecated) {text}".format(text=text) + + if text: + formatter.write_paragraph() + formatter.write_text(click.style(text, fg='white')) + + def format_options(self, ctx: ClickContext, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + if '-h' in rv[0] or '-help' in rv[0] or '--help' in rv[0]: + continue + opts.append(rv) + + if opts: + formatter.write(" šŸ”— Options \n\n") + formatter.write_dl(opts) + + def format_examples(self, ctx, formatter): + formatter.write("\nšŸ“™ Examples:\n\n") + for example in self.examples: + formatter.write(example) + + def format_help(self, ctx, formatter): + formatter.write("\n") + self.format_help_text(ctx, formatter) + formatter.write("\n") + self.format_options(ctx, formatter) + self.format_examples(ctx, formatter) + + +class HelpfulCmd(HelpfulCmdBase, click.Command): + examples = [ + click.style("composio login", fg='green') + click.style(" # Login with browser support\n", fg='black'), + click.style("composio login --no-browser", fg='green') + click.style(" # Login without browser interaction\n", fg='black'), + ] + + +class HelpfulGroup(HelpfulCmdBase, click.Group): + pass diff --git a/composio/cli/whoami.py b/composio/cli/whoami.py index 839c934e95..adf5680010 100644 --- a/composio/cli/whoami.py +++ b/composio/cli/whoami.py @@ -9,9 +9,16 @@ from composio.cli.context import Context, pass_context from composio.exceptions import ComposioSDKError +from composio.cli.utils.helpfulcmd import HelpfulCmdBase -@click.command(name="whoami") +class WhoamiExamples(HelpfulCmdBase, click.Command): + examples = [ + click.style("composio whoami", fg='green') + click.style(" # Display your account information\n", fg='black'), + ] + +@click.command(name="whoami", cls=WhoamiExamples) +@click.help_option("--help", "-h", "-help") @pass_context def _whoami(context: Context) -> None: """List your account information"""