File size: 2,359 Bytes
597fb95
 
 
3422d54
597fb95
3422d54
 
 
597fb95
3422d54
 
 
597fb95
3422d54
 
 
 
 
597fb95
3422d54
 
 
 
597fb95
3422d54
 
 
 
597fb95
3422d54
 
 
597fb95
 
3422d54
597fb95
3422d54
 
597fb95
 
 
 
 
 
 
 
 
 
 
 
 
3422d54
597fb95
 
 
 
 
 
 
 
 
 
2b086b8
597fb95
3422d54
 
 
 
 
 
 
597fb95
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import importlib
import pkgutil
import inspect
from abc import ABC, abstractmethod
from typing import List, Dict, Type

class SlashCommand(ABC):
    """Base class for all slash commands."""

    @property
    @abstractmethod
    def name(self) -> str:
        """The command string, e.g., '/help'."""
        pass

    @property
    @abstractmethod
    def description(self) -> str:
        """Brief summary of what the command does."""
        pass

    @property
    def requires_permission(self) -> bool:
        """True if this command needs explicit user approval before running."""
        return False

    @abstractmethod
    async def execute(self, app, args: List[str]):
        """The logic to run when the command is invoked."""
        pass

class CommandManager:
    """Registry and executor for slash commands."""

    def __init__(self):
        self.commands: Dict[str, SlashCommand] = {}

    def register_command(self, cmd: SlashCommand):
        """Manually register a command instance."""
        self.commands[cmd.name.lower()] = cmd

    def auto_discover(self, package_path: str):
        """
        Dynamically discover and register SlashCommand classes in a package.
        e.g., auto_discover('cli_textual.plugins.commands')
        """
        try:
            package = importlib.import_module(package_path)
            for _, name, is_pkg in pkgutil.iter_modules(package.__path__):
                full_module_name = f"{package_path}.{name}"
                module = importlib.import_module(full_module_name)

                for _, obj in inspect.getmembers(module):
                    if (inspect.isclass(obj) and 
                        issubclass(obj, SlashCommand) and 
                        obj is not SlashCommand):
                        # Instantiate and register
                        instance = obj()
                        self.register_command(instance)
        except Exception as e:
            print(f"Error during command discovery: {e}")

    def get_command(self, name: str) -> SlashCommand | None:
        return self.commands.get(name.lower())

    def get_all_help(self) -> str:
        help_text = "### Commands\n"
        for name in sorted(self.commands.keys()):
            cmd = self.commands[name]
            help_text += f"- {name.ljust(15)} {cmd.description}\n"
        return help_text