xcad 10 meses atrás
pai
commit
9a5e808618
4 arquivos alterados com 68 adições e 88 exclusões
  1. 27 30
      cli/__main__.py
  2. 3 0
      cli/core/library.py
  3. 36 55
      cli/core/module.py
  4. 2 3
      cli/core/template.py

+ 27 - 30
cli/__main__.py

@@ -4,69 +4,66 @@ Main entry point for the Boilerplates CLI application.
 This file serves as the primary executable when running the CLI.
 This file serves as the primary executable when running the CLI.
 """
 """
 import importlib
 import importlib
-import logging
 import pkgutil
 import pkgutil
 import sys
 import sys
 from pathlib import Path
 from pathlib import Path
-from typer import Typer, Option, Context
+from typer import Typer, Context
+from rich.console import Console
 import cli.modules
 import cli.modules
 from cli.core.registry import registry
 from cli.core.registry import registry
+from cli.core.exceptions import BoilerplateError
 
 
 app = Typer(no_args_is_help=True)
 app = Typer(no_args_is_help=True)
-
-# Set up logging
-logging.basicConfig(
-    level=logging.CRITICAL,
-    format='[%(levelname)s] %(message)s',
-    stream=sys.stdout
-)
-logger = logging.getLogger('boilerplates')
+console = Console()
 
 
 @app.callback()
 @app.callback()
-def main(
-    ctx: Context,
-    debug: bool = Option(False, "--debug", help="Enable debug logging")
-):
+def main(ctx: Context):
   """Main CLI application for managing boilerplates."""
   """Main CLI application for managing boilerplates."""
-  # Enable debug logging if requested
-  if debug:
-    logging.getLogger('boilerplates').setLevel(logging.DEBUG)
-    logger.debug("Debug logging enabled")
-  
-  logger.debug("Starting boilerplates CLI application")
+  pass
 
 
 def init_app():
 def init_app():
   """Initialize the application by discovering and registering modules."""
   """Initialize the application by discovering and registering modules."""
   try:
   try:
     # Auto-discover and import all modules
     # Auto-discover and import all modules
     modules_path = Path(cli.modules.__file__).parent
     modules_path = Path(cli.modules.__file__).parent
-    logger.debug(f"Discovering modules in: {modules_path}")
     
     
     for finder, name, ispkg in pkgutil.iter_modules([str(modules_path)]):
     for finder, name, ispkg in pkgutil.iter_modules([str(modules_path)]):
       if not ispkg and not name.startswith('_') and name != 'base':
       if not ispkg and not name.startswith('_') and name != 'base':
         try:
         try:
-          logger.debug(f"Importing module: {name}")
           importlib.import_module(f"cli.modules.{name}")
           importlib.import_module(f"cli.modules.{name}")
         except ImportError as e:
         except ImportError as e:
-          logger.warning(f"Could not import {name}: {e}")
+          # Silently skip modules that can't be imported
+          pass
     
     
     # Register modules with app
     # Register modules with app
-    logger.debug(f"Registering {len(registry.create_instances())} modules")
     for module in registry.create_instances():
     for module in registry.create_instances():
       try:
       try:
-        logger.debug(f"Registering module: {module.__class__.__name__}")
         module.register_cli(app)
         module.register_cli(app)
       except Exception as e:
       except Exception as e:
-        logger.error(f"Error registering {module.__class__.__name__}: {e}")
+        console.print(f"[yellow]Warning:[/yellow] Error registering {module.__class__.__name__}: {e}")
     
     
   except Exception as e:
   except Exception as e:
-    logger.error(f"Application initialization error: {e}")
-    exit(1)
+    console.print(f"[bold red]Application initialization error:[/bold red] {e}")
+    sys.exit(1)
 
 
 def run():
 def run():
   """Run the CLI application."""
   """Run the CLI application."""
-  init_app()
-  app()
+  try:
+    init_app()
+    app()
+  except BoilerplateError as e:
+    # Handle our custom exceptions cleanly without stack trace
+    console.print(f"[bold red]Error:[/bold red] {e}")
+    sys.exit(1)
+  except KeyboardInterrupt:
+    # Handle Ctrl+C gracefully
+    console.print("\n[yellow]Operation cancelled by user[/yellow]")
+    sys.exit(130)
+  except Exception as e:
+    # Handle unexpected errors - show simplified message
+    console.print(f"[bold red]Unexpected error:[/bold red] {e}")
+    console.print("[dim]Run with --debug for more details[/dim]")
+    sys.exit(1)
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
   run()
   run()

+ 3 - 0
cli/core/library.py

@@ -52,6 +52,9 @@ class Library:
         if file_path.is_file():
         if file_path.is_file():
           # Create Template object using the new class method
           # Create Template object using the new class method
           template = Template.from_file(file_path)
           template = Template.from_file(file_path)
+          # Set module context if not already specified in frontmatter
+          if not template.module:
+            template.module = module_name
           templates.append(template)
           templates.append(template)
 
 
     if sorted:
     if sorted:

+ 36 - 55
cli/core/module.py

@@ -58,12 +58,8 @@ class Module(ABC):
 
 
   def show(self, id: str = Argument(..., help="Template ID")):
   def show(self, id: str = Argument(..., help="Template ID")):
     """Show template details."""
     """Show template details."""
-    logger.debug(f"Showing template: {id}")
-    
     template = self._get_template(id)
     template = self._get_template(id)
-    if not template:
-      return
-    
+
     # Header
     # Header
     version = f" v{template.version}" if template.version else ""
     version = f" v{template.version}" if template.version else ""
     console.print(f"[bold magenta]{template.name} ({template.id}{version})[/bold magenta]")
     console.print(f"[bold magenta]{template.name} ({template.id}{version})[/bold magenta]")
@@ -86,18 +82,15 @@ class Module(ABC):
     
     
     # Content
     # Content
     if template.content:
     if template.content:
-      console.print(f"\n{template.content}")
-  
-  def _get_template(self, template_id: str, raise_on_missing: bool = False):
+      print(f"\n{template.content}")
+
+  def _get_template(self, template_id: str):
     """Get template by ID with unified error handling."""
     """Get template by ID with unified error handling."""
     template = self.libraries.find_by_id(self.name, self.files, template_id)
     template = self.libraries.find_by_id(self.name, self.files, template_id)
     
     
     if not template:
     if not template:
-      logger.error(f"Template '{template_id}' not found")
-      if raise_on_missing:
-        raise TemplateNotFoundError(template_id, self.name)
-      console.print(f"[red]Template '{template_id}' not found[/red]")
-    
+      raise TemplateNotFoundError(template_id, self.name)
+
     return template
     return template
 
 
   def generate(
   def generate(
@@ -106,17 +99,19 @@ class Module(ABC):
     out: Optional[Path] = Option(None, "--out", "-o")
     out: Optional[Path] = Option(None, "--out", "-o")
   ):
   ):
     """Generate from template."""
     """Generate from template."""
-    logger.debug(f"Generating template: {id}")
-    
-    template = self._get_template(id, raise_on_missing=True)
+    template = self._get_template(id)
     
     
     # Validate template (will raise TemplateValidationError if validation fails)
     # Validate template (will raise TemplateValidationError if validation fails)
     self._validate_template(template, id)
     self._validate_template(template, id)
     
     
-    print("TEST")
+    print("TEST SUCCESSFUL")
   
   
   def _validate_template(self, template, template_id: str) -> None:
   def _validate_template(self, template, template_id: str) -> None:
     """Validate template and raise error if validation fails."""
     """Validate template and raise error if validation fails."""
+    from .exceptions import TemplateValidationError
+    
+    validation_errors = []
+    
     # Update template variables with module variable registry
     # Update template variables with module variable registry
     module_variable_registry = getattr(self, 'variables', None)
     module_variable_registry = getattr(self, 'variables', None)
     if module_variable_registry:
     if module_variable_registry:
@@ -125,47 +120,33 @@ class Module(ABC):
       # Validate parent-child relationships in the registry
       # Validate parent-child relationships in the registry
       registry_errors = module_variable_registry.validate_parent_child_relationships()
       registry_errors = module_variable_registry.validate_parent_child_relationships()
       if registry_errors:
       if registry_errors:
-        logger.error(f"Variable registry validation errors: {registry_errors}")
+        validation_errors.extend(registry_errors)
+      
+      # Validate that all template variables are either registered or in template_vars
+      unregistered_vars = []
+      for var_name in template.vars:
+        # Check if variable is registered in module
+        if not module_variable_registry.get_variable(var_name):
+          # Check if it's defined in template's frontmatter variable_metadata (template_vars)
+          if var_name not in template.variable_metadata:
+            unregistered_vars.append(var_name)
+      
+      if unregistered_vars:
+        validation_errors.append(
+          f"Unregistered variables found: {', '.join(unregistered_vars)}. "
+          f"Variables must be either registered in the module or defined in template frontmatter 'variables' section."
+        )
     
     
-    # Validate template with module variables
-    warnings = template.validate(module_variable_registry)
+    # Validate template syntax and structure
+    template_warnings = template.validate(module_variable_registry)
+    
+    # If there are validation errors, fail with TemplateValidationError
+    if validation_errors:
+      raise TemplateValidationError(template_id, validation_errors)
     
     
     # If there are non-critical warnings, log them but don't fail
     # If there are non-critical warnings, log them but don't fail
-    if warnings:
-      logger.warning(f"Template '{template_id}' has validation warnings: {warnings}")
-  
-  def _apply_metadata_to_variables(self, variables: Dict[str, Any], template_metadata: Dict[str, Any]):
-    """Apply metadata from module and template to variables."""
-    # First apply module metadata
-    module_var_metadata = self.metadata.get('variables', {})
-    for var_name, var in variables.items():
-      if var_name in module_var_metadata:
-        meta = module_var_metadata[var_name]
-        if 'hint' in meta and not var.hint:
-          var.hint = meta['hint']
-        if 'description' in meta and not var.description:
-          var.description = meta['description']
-        if 'tip' in meta and not var.tip:
-          var.tip = meta['tip']
-        if 'validation' in meta and not var.validation:
-          var.validation = meta['validation']
-        if 'icon' in meta and not var.icon:
-          var.icon = meta['icon']
-    
-    # Then apply template metadata (overrides module metadata)
-    for var_name, var in variables.items():
-      if var_name in template_metadata:
-        meta = template_metadata[var_name]
-        if 'hint' in meta:
-          var.hint = meta['hint']
-        if 'description' in meta:
-          var.description = meta['description']
-        if 'tip' in meta:
-          var.tip = meta['tip']
-        if 'validation' in meta:
-          var.validation = meta['validation']
-        if 'icon' in meta:
-          var.icon = meta['icon']
+    if template_warnings:
+      logger.warning(f"Template '{template_id}' has validation warnings: {template_warnings}")
   
   
   def register_cli(self, app: Typer):
   def register_cli(self, app: Typer):
     """Register module commands with the main app."""
     """Register module commands with the main app."""

+ 2 - 3
cli/core/template.py

@@ -1,5 +1,5 @@
 from pathlib import Path
 from pathlib import Path
-from typing import Any, Dict, List, Set, Tuple
+from typing import Any, Dict, List, Set, Tuple, Optional
 from dataclasses import dataclass, field
 from dataclasses import dataclass, field
 import logging
 import logging
 import re
 import re
@@ -37,7 +37,6 @@ class Template:
   # Template variable analysis results
   # Template variable analysis results
   vars: Set[str] = field(default_factory=set, init=False)
   vars: Set[str] = field(default_factory=set, init=False)
   var_defaults: Dict[str, Any] = field(default_factory=dict, init=False)
   var_defaults: Dict[str, Any] = field(default_factory=dict, init=False)
-  
   def __post_init__(self):
   def __post_init__(self):
     """Initialize computed properties after dataclass initialization."""
     """Initialize computed properties after dataclass initialization."""
     # Set default name if not provided
     # Set default name if not provided
@@ -180,7 +179,7 @@ class Template:
     # The template parser will have found all variables used
     # The template parser will have found all variables used
     
     
     # Check for missing required frontmatter fields
     # Check for missing required frontmatter fields
-    if not self.name or self.name == self.file_path.parent.name:
+    if not self.name:
       errors.append("Missing 'name' in frontmatter")
       errors.append("Missing 'name' in frontmatter")
     
     
     if not self.description or self.description == 'No description available':
     if not self.description or self.description == 'No description available':