# agent/commands.py from dataclasses import dataclass from typing import Callable, Dict, List import os from .memory import Memory @dataclass class Command: name: str description: str needs_project_root: bool handler: Callable[[List[str], Memory], str] class CommandRegistry: def __init__(self, memory: Memory): self.memory = memory self.commands: Dict[str, Command] = {} self._register_defaults() def _register_defaults(self): # /setroot def cmd_setroot(args: List[str], mem: Memory) -> str: if not args: return "Usage: `/setroot `" path = args[0] if not os.path.isdir(path): return f"Le chemin `{path}` n'existe pas ou n'est pas un dossier." mem.set_project_root(path) return f"✅ Chemin du projet défini sur `{path}`." self.register( Command( name="setroot", description="Définit le chemin racine du projet.", needs_project_root=False, handler=cmd_setroot, ) ) # /scan [rel_path] def cmd_scan(args: List[str], mem: Memory) -> str: root = mem.get_project_root() rel = args[0] if args else "." full = os.path.abspath(os.path.join(root, rel)) # sécurité basique if not full.startswith(os.path.abspath(root)): return "❌ Tu ne peux pas sortir du project_root." if not os.path.isdir(full): return f"❌ `{rel}` n'est pas un dossier dans le projet." entries = sorted(os.listdir(full)) if not entries: return f"📁 `{rel}` est vide." lines = [f"📁 Scan de `{rel}` (dans `{root}`):"] for e in entries: p = os.path.join(full, e) if os.path.isdir(p): lines.append(f" 📂 {e}/") else: lines.append(f" 📄 {e}") return "\n".join(lines) self.register( Command( name="scan", description="Liste les fichiers/dossiers du projet.", needs_project_root=True, handler=cmd_scan, ) ) def register(self, cmd: Command): self.commands[cmd.name] = cmd def handle(self, raw_input: str) -> str: """ Parse et exécute une commande du type `/scan src` ou `/setroot /chemin`. """ text = raw_input.strip() if not text.startswith("/"): return "Internal error: not a command." parts = text[1:].split() if not parts: return "Commande vide." name = parts[0] args = parts[1:] cmd = self.commands.get(name) if not cmd: return f"Commande inconnue: `/{name}`.\nCommandes disponibles: {', '.join('/'+n for n in self.commands.keys())}" # dépendance project_root if cmd.needs_project_root and not self.memory.get_project_root(): return ( "❗ Aucun `project_root` défini pour l'instant.\n" "Commence par le définir avec:\n" "`/setroot /chemin/vers/ton/projet`" ) return cmd.handler(args, self.memory)