Browse Source

next steps

xcad 10 months ago
parent
commit
5ab85326c5
6 changed files with 346 additions and 248 deletions
  1. 98 55
      cli/core/module.py
  2. 163 0
      cli/core/processor.py
  3. 8 7
      cli/core/prompt.py
  4. 17 41
      cli/core/template.py
  5. 46 145
      cli/core/variables.py
  6. 14 0
      cli/modules/compose.py

+ 98 - 55
cli/core/module.py

@@ -16,6 +16,7 @@ from .prompt import PromptHandler
 from .template import Template
 from .variables import VariableGroup, VariableManager
 from .config import ConfigManager
+from .processor import VariableProcessor
 
 logger = logging.getLogger('boilerplates')
 
@@ -34,8 +35,8 @@ class Module(ABC):
     self.files = files
     
     # Initialize ConfigManager and VariableManager with it
-    self.config_manager = ConfigManager()
-    self.variable_manager = VariableManager(vars if vars is not None else [], self.config_manager)
+    self.config = ConfigManager()
+    self.vars = VariableManager(vars if vars is not None else [], self.config)
 
     self.app = Typer()
     self.libraries = LibraryManager()  # Initialize library manager
@@ -49,23 +50,91 @@ class Module(ABC):
       raise ValueError("Module files must be a non-empty list")
     if not all(isinstance(var, VariableGroup) for var in (vars if vars is not None else [])):
       raise ValueError("Module vars must be a list of VariableGroup instances")
-  
-  @property
-  def vars(self) -> List[VariableGroup]:
-    """Backward compatibility property for accessing variable groups."""
-    return self.variable_manager.variable_groups
-  
-  def get_variable_summary(self) -> Dict[str, Any]:
-    """Get a summary of all variables managed by this module."""
-    return self.variable_manager.get_summary()
-  
-  def add_variable_group(self, group: VariableGroup) -> None:
-    """Add a new variable group to this module."""
-    self.variable_manager.add_group(group)
-  
-  def has_variable(self, name: str) -> bool:
-    """Check if this module has a variable with the given name."""
-    return self.variable_manager.has_variable(name)
+
+  def _validate_variables(self, variables: List[str]) -> Tuple[bool, List[str]]:
+    """Validate if all template variables exist in the variable groups.
+    
+    Args:
+        variables: List of variable names to validate
+        
+    Returns:
+        Tuple of (success: bool, missing_variables: List[str])
+    """
+    missing_variables = [var for var in variables if not self.vars.has_variable(var)]
+    success = len(missing_variables) == 0
+    return success, missing_variables
+
+  def _get_variable_defaults_for_template(self, template_vars: List[str]) -> Dict[str, Any]:
+    """Get default values for variables used in a template.
+    
+    Args:
+        template_vars: List of variable names used in the template
+        
+    Returns:
+        Dictionary mapping variable names to their default values
+    """
+    defaults = {}
+    for group in self.vars.variable_groups:
+      for variable in group.vars:
+        if variable.name in template_vars and variable.value is not None:
+          defaults[variable.name] = variable.value
+    return defaults
+
+  def _get_groups_with_template_vars(self, template_vars: List[str]) -> List[VariableGroup]:
+    """Get groups that contain at least one template variable.
+    
+    Args:
+        template_vars: List of variable names used in the template
+        
+    Returns:
+        List of VariableGroup objects that have variables used by the template
+    """
+    result = []
+    for group in self.vars.variable_groups:
+      if any(var.name in template_vars for var in group.vars):
+        result.append(group)
+    return result
+
+  def _resolve_variable_defaults(self, template_vars: List[str], template_defaults: Dict[str, Any] = None) -> Dict[str, Any]:
+    """Resolve variable default values with priority handling.
+    
+    Priority order:
+    1. Module variable defaults (low priority)
+    2. Template's built-in defaults (medium priority)  
+    3. User config defaults (high priority)
+    """
+    if template_defaults is None:
+      template_defaults = {}
+    
+    # Start with module defaults, then override with template and user config
+    defaults = self._get_variable_defaults_for_template(template_vars)
+    defaults.update(template_defaults)
+    defaults.update({var: value for var, value in self.config.get_variable_defaults(self.name).items() if var in template_vars})
+    
+    return defaults
+
+  def _filter_variables_for_template(self, template_vars: List[str]) -> Dict[str, Any]:
+    """Filter the variable groups to only include variables needed by the template."""
+    filtered_vars = {}
+    template_vars_set = set(template_vars)  # Convert to set for O(1) lookup
+    
+    for group in self._get_groups_with_template_vars(template_vars):
+      # Get variables that match template vars and convert to dict format
+      group_vars = {
+        var.name: var.to_dict() for var in group.vars if var.name in template_vars_set
+      }
+      
+      # Only include groups that have variables
+      if group_vars:
+        filtered_vars[group.name] = {
+          'description': group.description,
+          'enabled': group.enabled,
+          'prompt_to_set': getattr(group, 'prompt_to_set', ''),
+          'prompt_to_enable': getattr(group, 'prompt_to_enable', ''),
+          'vars': group_vars
+        }
+    
+    return filtered_vars
 
   def list(self):
     """List all templates in the module."""
@@ -110,8 +179,7 @@ class Module(ABC):
     
     # Find variable groups used by this template
     template_var_groups = [
-      group.name for group in self.variable_manager.variable_groups
-      if any(var.name in template.vars for var in group.vars)
+      group.name for group in self._get_groups_with_template_vars(template.vars)
     ]
     
     if template_var_groups:
@@ -130,51 +198,26 @@ class Module(ABC):
     """Generate a new template with complex variable prompting logic"""
     logger.info(f"Generating template '{id}' from module '{self.name}'")
     
-    # Step 1: Find template by ID
-    logger.debug(f"Step 1: Finding template by ID: {id}")
+    # Find template by ID
     template = self.libraries.find_by_id(module_name=self.name, files=self.files, template_id=id)
     if not template:
       logger.error(f"Template '{id}' not found")
       print(f"Template '{id}' not found.")
       return
-    
-    logger.debug(f"Template found: {template.name} with {len(template.vars)} variables")
-    
-    # Step 2: Validate if the variables in the template are valid ones
-    logger.debug(f"Step 2: Validating template variables: {template.vars}")
-    success, missing = self.variable_manager.validate_template_variables(template.vars)
+
+    # Validate if the variables in the template are valid ones
+    success, missing = self._validate_variables(template.vars)
     if not success:
       logger.error(f"Template '{id}' has invalid variables: {missing}")
       print(f"Template '{id}' has invalid variables: {missing}")
       return
     
-    logger.debug("All template variables are valid")
-    
-    # Step 3: Disable variables not found in template
-    logger.debug(f"Step 3: Disabling variables not used by template")
-    self.variable_manager.disable_variables_not_in_template(template.vars)
-    logger.debug("Unused variables disabled")
-
-    # Step 4: Resolve variable defaults with priority (module -> template -> user config)
-    logger.debug(f"Step 4: Resolving variable defaults with priority")
-    resolved_defaults = self.variable_manager.resolve_variable_defaults(
-      self.name, 
-      template.vars, 
-      template.var_defaults
-    )
-    logger.debug(f"Resolved defaults: {resolved_defaults}")
-    
-    # Step 5: Match template vars with vars of the module (only enabled ones)
-    logger.debug(f"Step 5: Filtering variables for template")
-    filtered_vars = self.variable_manager.filter_variables_for_template(template.vars)
-    logger.debug(f"Filtered variables: {list(filtered_vars.keys())}")
-    
-    # Step 6: Execute complex group-based prompting logic
-    logger.debug(f"Step 6: Starting complex prompting logic")
+    # Process variables using dedicated processor
     try:
-      prompt = PromptHandler(filtered_vars, resolved_defaults)
-      final_variable_values = prompt()
-      logger.debug(f"Prompting completed with values: {final_variable_values}")
+      processor = VariableProcessor(self.vars, self.config, self.name)
+      final_variable_values = processor.process_variables_for_template(template)
+      logger.debug(f"Variable processing completed with {len(final_variable_values)} variables")
+      
     except KeyboardInterrupt:
       logger.info("Template generation cancelled by user")
       print("\n[red]Template generation cancelled.[/red]")

+ 163 - 0
cli/core/processor.py

@@ -0,0 +1,163 @@
+from typing import Any, Dict, List
+import logging
+from .variables import VariableManager
+from .config import ConfigManager
+from .template import Template
+from .prompt import PromptHandler
+
+logger = logging.getLogger('boilerplates')
+
+
+class VariableProcessor:
+  """
+  Handles the complete variable population pipeline for template generation.
+  
+  This class implements a clean, step-by-step approach to:
+  1. Filter variables relevant to the template
+  2. Resolve defaults with proper priority handling  
+  3. Prompt users for missing values
+  4. Return final variable values for template rendering
+  """
+  
+  def __init__(self, vars_manager: VariableManager, config_manager: ConfigManager, module_name: str = None):
+    """Initialize the processor with required managers."""
+    self.vars = vars_manager
+    self.config = config_manager
+    self.module_name = module_name
+    self.logger = logging.getLogger('boilerplates')
+  
+  def process_variables_for_template(self, template: Template) -> Dict[str, Any]:
+    """
+    Execute the complete variable processing pipeline.
+    
+    Args:
+        template: Template object containing variables and defaults
+        
+    Returns:
+        Dictionary of final variable values ready for template rendering
+    """
+    self.logger.debug("Starting variable processing pipeline")
+    
+    # Step 1: Filter and prepare variables for this template
+    self._prepare_variables_for_template(template)
+    
+    # Step 2: Resolve defaults in priority order
+    resolved_defaults = self._resolve_defaults(template)
+    
+    # Step 3: Handle user interaction and prompting
+    final_values = self._prompt_for_values(template, resolved_defaults)
+    
+    self.logger.debug(f"Variable processing completed with {len(final_values)} variables")
+    return final_values
+  
+  def _prepare_variables_for_template(self, template: Template) -> None:
+    """
+    Step 1: Disable variables not needed by the template.
+    
+    This optimizes the user experience by only showing relevant variables.
+    """
+    self.logger.debug(f"Filtering variables for template with {len(template.vars)} required variables")
+    
+    disabled_count = 0
+    for var_group in self.vars.get_all_groups():
+      for var in var_group.get_all_vars():
+        if var.name not in template.vars:
+          var.disable()
+          disabled_count += 1
+    
+    self.logger.debug(f"Disabled {disabled_count} variables not needed by template")
+  
+  def _resolve_defaults(self, template: Template) -> Dict[str, Any]:
+    """
+    Step 2: Resolve variable defaults with proper priority handling.
+    
+    Priority order (low to high):
+    1. Module variable defaults
+    2. Template built-in defaults 
+    3. User configuration defaults
+    
+    Returns:
+        Dictionary of resolved default values
+    """
+    self.logger.debug("Resolving variable defaults with priority handling")
+    defaults = {}
+    
+    # Priority 1: Module variable defaults (lowest priority)
+    for group in self.vars.get_all_groups():
+      for var in group.get_all_vars():
+        if var.name in template.vars and var.value is not None:
+          defaults[var.name] = var.value
+          self.logger.debug(f"Set module default for '{var.name}': {var.value}")
+    
+    # Priority 2: Template defaults (medium priority)
+    for var_name, default_value in template.var_defaults.items():
+      if var_name in template.vars:
+        defaults[var_name] = default_value
+        self.logger.debug(f"Set template default for '{var_name}': {default_value}")
+    
+    # Priority 3: User config defaults (highest priority)
+    user_defaults = self.config.get_variable_defaults(self.module_name or "unknown")
+    for var_name, default_value in user_defaults.items():
+      if var_name in template.vars:
+        defaults[var_name] = default_value
+        self.logger.debug(f"Set user config default for '{var_name}': {default_value}")
+    
+    self.logger.debug(f"Resolved {len(defaults)} default values")
+    return defaults
+  
+  def _prompt_for_values(self, template: Template, defaults: Dict[str, Any]) -> Dict[str, Any]:
+    """
+    Step 3: Handle user prompting for variable values.
+    
+    Args:
+        template: Template object
+        defaults: Resolved default values
+        
+    Returns:
+        Dictionary of final variable values
+    """
+    self.logger.debug("Starting user prompting phase")
+    
+    # Filter variable groups to only include those needed by the template
+    filtered_groups = self._filter_variables_for_template(template.vars)
+    
+    # Create and execute prompt handler
+    prompt_handler = PromptHandler(filtered_groups, defaults)
+    final_values = prompt_handler()
+    
+    self.logger.debug(f"User prompting completed with {len(final_values)} final values")
+    return final_values
+  
+  def _filter_variables_for_template(self, template_vars: List[str]) -> Dict[str, Any]:
+    """
+    Filter variable groups to only include variables needed by the template.
+    
+    This is adapted from the existing method in the Module class.
+    """
+    filtered_vars = {}
+    template_vars_set = set(template_vars)  # Convert to set for O(1) lookup
+    
+    for group in self.vars.get_all_groups():
+      # Only process enabled groups
+      if not group.enabled:
+        continue
+        
+      # Get variables that match template vars and are enabled
+      group_vars = {
+        var.name: var.to_dict() 
+        for var in group.vars 
+        if var.name in template_vars_set and var.enabled
+      }
+      
+      # Only include groups that have variables
+      if group_vars:
+        filtered_vars[group.name] = {
+          'description': group.description,
+          'enabled': group.enabled,
+          'prompt_to_set': getattr(group, 'prompt_to_set', ''),
+          'prompt_to_enable': getattr(group, 'prompt_to_enable', ''),
+          'vars': group_vars
+        }
+    
+    self.logger.debug(f"Filtered to {len(filtered_vars)} variable groups for template")
+    return filtered_vars

+ 8 - 7
cli/core/prompt.py

@@ -77,8 +77,14 @@ class PromptHandler:
     # Step 2: Determine if group should be enabled
     group_enabled = self._determine_group_enabled_status(group_name, variables, vars_without_defaults)
     
+    # Always set default values for variables in this group, even if user doesn't want to configure them
+    vars_with_defaults = self._get_variables_with_defaults(variables)
+    for var_name in vars_with_defaults:
+      default_value = self.resolved_defaults.get(var_name)
+      self.final_values[var_name] = default_value
+    
     if not group_enabled:
-      logger.debug(f"Group {group_name} disabled by user")
+      logger.debug(f"Group {group_name} disabled by user, but defaults have been applied")
       return
       
     # Step 3: Prompt for required variables (those without defaults)
@@ -164,12 +170,7 @@ class PromptHandler:
   def _handle_variables_with_defaults(self, group_name: str, vars_with_defaults: List[str], variables: Dict[str, Any]):
     """Handle variables that have default values."""
     
-    # First, set all default values
-    for var_name in vars_with_defaults:
-      default_value = self.resolved_defaults.get(var_name)
-      self.final_values[var_name] = default_value
-    
-    # Ask if user wants to customize any of these values
+    # Ask if user wants to customize any of these values (defaults already set earlier)
     try:
       want_to_customize = Confirm.ask(f"[yellow]Do you want to customize any {group_name} variables?[/yellow]", default=False)
     except (EOFError, KeyboardInterrupt):

+ 17 - 41
cli/core/template.py

@@ -1,5 +1,7 @@
 from pathlib import Path
 from typing import Any, Dict, Set, Tuple
+import logging
+import re
 from jinja2 import Environment, BaseLoader, meta, nodes
 import frontmatter
 
@@ -7,6 +9,16 @@ import frontmatter
 class Template:
   """Data class for template information extracted from frontmatter."""
   
+  @staticmethod
+  def _create_jinja_env() -> Environment:
+    """Create standardized Jinja2 environment for consistent template processing."""
+    return Environment(
+      loader=BaseLoader(),
+      trim_blocks=True,           # Remove first newline after block tags
+      lstrip_blocks=True,         # Strip leading whitespace from block tags  
+      keep_trailing_newline=False # Remove trailing newlines
+    )
+  
   def __init__(self, file_path: Path, frontmatter_data: Dict[str, Any], content: str):
     self.file_path = file_path
     self.content = content
@@ -56,10 +68,6 @@ class Template:
   def _parse_template_variables(self, template_content: str) -> Tuple[Set[str], Dict[str, Any]]:
     """Parse Jinja2 template to extract variables and their default values.
     
-    Analyzes template content to find:
-    1. All undeclared variables (using AST analysis)
-    2. Default values from | default() filters (using AST traversal)
-    
     Examples:
         {{ app_name | default('my-app') }} → vars={'app_name'}, defaults={'app_name': 'my-app'}
         {{ port | default(8080) }} → vars={'port'}, defaults={'port': 8080}
@@ -69,13 +77,7 @@ class Template:
         Tuple of (all_variable_names, variable_defaults)
     """
     try:
-      # Use consistent Jinja2 environment configuration
-      env = Environment(
-        loader=BaseLoader(),
-        trim_blocks=True,           # Remove first newline after block tags
-        lstrip_blocks=True,         # Strip leading whitespace from block tags  
-        keep_trailing_newline=False # Remove trailing newlines
-      )
+      env = self._create_jinja_env()
       ast = env.parse(template_content)
       
       # Extract all undeclared variables
@@ -95,13 +97,6 @@ class Template:
     except Exception:
       return set(), {}
 
-  @staticmethod
-  def _parse_frontmatter(file_path: Path) -> Tuple[Dict[str, Any], str]:
-    """Parse frontmatter and content from a file."""
-    with open(file_path, 'r', encoding='utf-8') as f:
-      post = frontmatter.load(f)
-    return post.metadata, post.content
-
   def to_dict(self) -> Dict[str, Any]:
     """Convert to dictionary for display."""
     return {
@@ -122,37 +117,18 @@ class Template:
     }
 
   def render(self, variable_values: Dict[str, Any]) -> str:
-    """Render the template with the provided variable values.
-    
-    Args:
-        variable_values: Dictionary of variable names to their values
-        
-    Returns:
-        Rendered template content as string
-    """
-    import logging
-    import re
-    
+    """Render the template with the provided variable values."""
     logger = logging.getLogger('boilerplates')
     
     try:
-      # Configure Jinja2 environment to handle whitespace and blank lines
-      env = Environment(
-        loader=BaseLoader(),
-        trim_blocks=True,           # Remove first newline after block tags
-        lstrip_blocks=True,         # Strip leading whitespace from block tags
-        keep_trailing_newline=False # Remove trailing newlines
-      )
+      env = self._create_jinja_env()
       jinja_template = env.from_string(self.content)
       rendered_content = jinja_template.render(**variable_values)
       
-      # Additional post-processing to remove multiple consecutive blank lines
-      # Replace multiple consecutive newlines with single newlines
+      # Clean up excessive blank lines and whitespace
       rendered_content = re.sub(r'\n\s*\n\s*\n+', '\n\n', rendered_content)
-      # Remove leading/trailing whitespace
-      rendered_content = rendered_content.strip()
+      return rendered_content.strip()
       
-      return rendered_content
     except Exception as e:
       logger.error(f"Jinja2 template rendering failed: {e}")
       raise ValueError(f"Failed to render template: {e}")

+ 46 - 145
cli/core/variables.py

@@ -12,6 +12,21 @@ class Variable:
     self.type = var_type  # e.g., string, integer, boolean, choice
     self.options = options if options is not None else []  # For choice type
     self.enabled = enabled  # Whether this variable is enabled (default: True)
+  
+  def disable(self) -> None:
+    """Disable this variable."""
+    self.enabled = False
+
+  def to_dict(self) -> Dict[str, Any]:
+    """Convert Variable to dictionary for compatibility with PromptHandler."""
+    return {
+      'name': self.name,
+      'description': self.description,
+      'value': self.value,
+      'type': self.type,
+      'options': self.options,
+      'enabled': self.enabled
+    }
 
 
 class VariableGroup():
@@ -25,32 +40,16 @@ class VariableGroup():
     self.prompt_to_set = ""  # Custom prompt message
     self.prompt_to_enable = ""  # Custom prompt message when asking to enable this group
   
-  def is_enabled(self) -> bool:
-    """Check if this variable group is enabled."""
-    return self.enabled
-  
-  def enable(self) -> None:
-    """Enable this variable group."""
-    self.enabled = True
-  
   def disable(self) -> None:
-    """Disable this variable group."""
+    """Disable this variable group and all its variables."""
     self.enabled = False
-  
-  def get_enabled_variables(self) -> List[Variable]:
-    """Get all enabled variables in this group."""
-    return [var for var in self.vars if var.enabled]
-  
-  def disable_variables_not_in_template(self, template_vars: List[str]) -> None:
-    """Disable all variables that are not found in the template variables.
-    
-    Args:
-        template_vars: List of variable names used in the template
-    """
     for var in self.vars:
-      if var.name not in template_vars:
-        var.enabled = False
-  
+      var.disable()
+
+  def get_all_vars(self) -> List[Variable]:
+    """Get all variables in this group."""
+    return self.vars
+
   @classmethod
   def from_dict(cls, name: str, config: Dict[str, Any]) -> "VariableGroup":
     """Create a VariableGroup from a dictionary configuration."""
@@ -97,19 +96,10 @@ class VariableManager:
       raise ValueError("group must be a VariableGroup instance")
     self.variable_groups.append(group)
   
-  def disable_variables_not_in_template(self, template_vars: List[str]) -> None:
-    """Disable all variables in all groups that are not found in the template variables.
-    
-    Args:
-        template_vars: List of variable names used in the template
-    """
-    for group in self.variable_groups:
-      group.disable_variables_not_in_template(template_vars)
-  
-  def get_all_variable_names(self) -> List[str]:
-    """Get all variable names from all variable groups."""
-    return [var.name for group in self.variable_groups for var in group.vars]
-  
+  def get_all_groups(self) -> List[VariableGroup]:
+    """Get all variable groups."""
+    return self.variable_groups
+
   def has_variable(self, name: str) -> bool:
     """Check if a variable exists in any group."""
     for group in self.variable_groups:
@@ -118,125 +108,36 @@ class VariableManager:
           return True
     return False
   
-  def validate_template_variables(self, template_vars: List[str]) -> Tuple[bool, List[str]]:
-    """Validate if all template variables exist in the variable groups.
-    
-    Args:
-        template_vars: List of variable names used in the template
-        
-    Returns:
-        Tuple of (success: bool, missing_variables: List[str])
-    """
-    all_variables = self.get_all_variable_names()
-    missing_variables = [var for var in template_vars if var not in all_variables]
-    success = len(missing_variables) == 0
-    return success, missing_variables
-  
-  def filter_variables_for_template(self, template_vars: List[str]) -> Dict[str, Any]:
-    """Filter the variable groups to only include variables needed by the template.
+  def get_variable_value(self, name: str, group_name: str = None) -> Any:
+    """Get the value of a variable by name.
     
     Args:
-        template_vars: List of variable names used in the template
+        name: Variable name to find
+        group_name: Optional group name to search within
         
     Returns:
-        Dictionary with filtered variable groups and their variables, including group metadata
+        Variable value if found, None otherwise
     """
-    filtered_vars = {}
-    
     for group in self.variable_groups:
-      group_has_template_vars = False
-      group_vars = {}
-      
-      for variable in group.vars:
-        if variable.name in template_vars:
-          group_has_template_vars = True
-          group_vars[variable.name] = {
-            'name': variable.name,
-            'description': variable.description,
-            'value': variable.value,
-            'type': variable.type,
-            'options': getattr(variable, 'options', []),
-            'enabled': variable.enabled
-          }
-      
-      # Only include groups that have variables used by the template
-      if group_has_template_vars:
-        filtered_vars[group.name] = {
-          'description': group.description,
-          'enabled': group.enabled,
-          'prompt_to_set': getattr(group, 'prompt_to_set', ''),
-          'prompt_to_enable': getattr(group, 'prompt_to_enable', ''),
-          'vars': group_vars
-        }
-    
-    return filtered_vars
-  
-  def get_module_defaults(self, template_vars: List[str]) -> Dict[str, Any]:
-    """Get default values from module variable definitions for template variables.
-    
-    Args:
-        template_vars: List of variable names used in the template
-        
-    Returns:
-        Dictionary mapping variable names to their default values
-    """
-    defaults = {}
-    
-    for group in self.variable_groups:
-      for variable in group.vars:
-        if variable.name in template_vars and variable.value is not None:
-          defaults[variable.name] = variable.value
-    
-    return defaults
-  
-  def resolve_variable_defaults(self, module_name: str, template_vars: List[str], template_defaults: Dict[str, Any] = None) -> Dict[str, Any]:
-    """Resolve variable default values with hardcoded priority handling.
-    
-    Priority order (hardcoded):
-    1. Module variable defaults (low priority)
-    2. Template's built-in defaults from |default() filters (medium priority)
-    3. User config defaults (high priority)
+      if group_name is not None and group.name != group_name:
+        continue
+      for var in group.vars:
+        if var.name == name:
+          return var.value
+    return None
+
+  def get_variables_in_template(self, template_vars: List[str]) -> List[tuple]:
+    """Get all variables that exist in the template vars list.
     
     Args:
-        module_name: Name of the module (for config lookup)
         template_vars: List of variable names used in the template
-        template_defaults: Dictionary of template's built-in default values
         
     Returns:
-        Dictionary of variable names to their resolved default values
+        List of tuples (group_name, variable) for variables found in template
     """
-    if template_defaults is None:
-      template_defaults = {}
-    
-    # Priority 1: Start with module variable defaults (low priority)
-    defaults = self.get_module_defaults(template_vars)
-    
-    # Priority 2: Override with template's built-in defaults (medium priority)
-    defaults.update(template_defaults)
-    
-    # Priority 3: Override with user config defaults (high priority)
-    user_config_defaults = self.config_manager.get_variable_defaults(module_name)
-    for var_name in template_vars:
-      if var_name in user_config_defaults:
-        defaults[var_name] = user_config_defaults[var_name]
-    
-    return defaults
-  
-  def get_summary(self) -> Dict[str, Any]:
-    """Get a summary of all variable groups and their contents."""
-    summary = {
-      'total_groups': len(self.variable_groups),
-      'total_variables': len(self.get_all_variable_names()),
-      'groups': []
-    }
-    
+    result = []
     for group in self.variable_groups:
-      group_info = {
-        'name': group.name,
-        'description': group.description,
-        'variable_count': len(group.vars),
-        'variables': [var.name for var in group.vars]
-      }
-      summary['groups'].append(group_info)
-    
-    return summary
+      for var in group.vars:
+        if var.name in template_vars:
+          result.append((group.name, var))
+    return result

+ 14 - 0
cli/modules/compose.py

@@ -32,12 +32,26 @@ class ComposeModule(Module):
       "swarm": {
         "description": "Variables for Docker Swarm deployment",
         "vars": {
+          "swarm": {"description": "Enable Docker Swarm mode", "value": False, "var_type": "boolean"},
+          "swarm_replicas": {"description": "Number of replicas in Swarm", "value": 1, "var_type": "integer"},
           "replica_count": {"description": "Number of replicas in Swarm", "value": 1, "var_type": "integer"}
         }
       },
+      "networking": {
+        "description": "Network and port configuration",
+        "vars": {
+          "service_port": {"description": "Service port mapping", "value": {"http": 8080, "https": 8443}, "var_type": "dict"},
+          "docker_network": {"description": "Docker network name", "value": "bridge"}
+        }
+      },
       "traefik": {
         "description": "Variables for Traefik labels",
         "vars": {
+          "traefik": {"description": "Enable Traefik labels", "value": False, "var_type": "boolean"},
+          "traefik_enable": {"description": "Enable Traefik for this service", "value": True, "var_type": "boolean"},
+          "traefik_host": {"description": "Traefik host rule", "value": "example.com"},
+          "traefik_tls": {"description": "Enable TLS for Traefik", "value": True, "var_type": "boolean"},
+          "traefik_certresolver": {"description": "Traefik certificate resolver", "value": "letsencrypt"},
           "traefik_http_port": {"description": "HTTP port for Traefik", "value": 80, "var_type": "integer"},
           "traefik_https_port": {"description": "HTTPS port for Traefik", "value": 443, "var_type": "integer"},
           "traefik_entrypoints": {"description": "Entry points for Traefik", "value": ["http", "https"], "var_type": "list"}