CLI Internals
Architecture
Layer 4: Executor

Layer 4: Blueprint Executor

VFS Management & Action Orchestration

The Blueprint Executor is the intelligence layer that analyzes blueprints, manages VFS pre-population, expands forEach actions, and delegates to action handlers.


Core Responsibilities

  1. Blueprint Analysis - Determine what files the blueprint needs
  2. VFS Pre-population - Load required files from disk into VFS
  3. forEach Expansion - Expand loop actions into individual actions
  4. Action Delegation - Route each action to appropriate handler
  5. Error Handling - Fail-fast on any error

Implementation

// src/core/services/execution/blueprint/blueprint-executor.ts
export class BlueprintExecutor {
  private modifierRegistry: ModifierRegistry;
  private actionHandlerRegistry: ActionHandlerRegistry;
  private blueprintAnalyzer: BlueprintAnalyzer;
 
  async executeBlueprint(
    blueprint: Blueprint,
    context: ProjectContext,
    vfs: VirtualFileSystem
  ): Promise<BlueprintExecutionResult> {
    
    // 1. Analyze blueprint
    const analysis = this.blueprintAnalyzer.analyzeBlueprint(blueprint, context);
    
    // 2. Pre-populate VFS
    if (analysis.filesToRead.length > 0) {
      await vfs.initializeWithFiles(analysis.filesToRead);
    }
    
    // 3. Expand forEach actions
    const expandedActions = this.expandForEachActions(blueprint.actions, context);
    
    // 4. Execute all actions
    for (const action of expandedActions) {
      const result = await this.actionHandlerRegistry.handleAction(
        action, context, projectRoot, vfs
      );
      
      if (!result.success) {
        return { success: false, errors: [result.error] };
      }
    }
    
    return { success: true, files: [...], errors: [], warnings: [] };
  }
}

Blueprint Analysis

Purpose

Determine what files a blueprint will need to access before it runs.

Implementation

// src/core/services/project/blueprint-analyzer/blueprint-analyzer.ts
export class BlueprintAnalyzer {
  analyzeBlueprint(blueprint: Blueprint, context: ProjectContext) {
    const filesToRead = new Set<string>();
    
    // 1. Add explicitly declared contextual files
    if (blueprint.contextualFiles) {
      blueprint.contextualFiles.forEach(f => filesToRead.add(f));
    }
    
    // 2. Scan actions for file references
    blueprint.actions.forEach(action => {
      if (action.type === 'ENHANCE_FILE' && action.path) {
        filesToRead.add(action.path);
      }
      
      if (action.type === 'MERGE_JSON' && action.path) {
        filesToRead.add(action.path);
      }
      
      // ... scan other action types
    });
    
    return {
      filesToRead: Array.from(filesToRead),
      requiresVFS: filesToRead.size > 0
    };
  }
}

Result example:

{
  filesToRead: ['package.json', 'tsconfig.json', 'src/app/layout.tsx'],
  requiresVFS: true
}

forEach Expansion

Purpose

Avoid repetitive actions in blueprints.

Example

Blueprint:

{
  type: 'CREATE_FILE',
  forEach: 'module.parameters.components',  // ['button', 'card', 'dialog']
  path: 'components/ui/{{item}}.tsx',
  template: 'component.ejs'
}

Expansion:

private expandForEachActions(actions: BlueprintAction[], context) {
  const expanded = [];
  
  for (const action of actions) {
    if (action.forEach) {
      // Resolve array path
      const items = this.resolveForEachPath(action.forEach, context);
      // items = ['button', 'card', 'dialog']
      
      // Create action for each item
      items.forEach(item => {
        const newAction = {
          ...action,
          path: action.path.replace(/\{\{item\}\}/g, item),
          // Replace {{item}} in all string properties
        };
        expanded.push(newAction);
      });
    } else {
      expanded.push(action);
    }
  }
  
  return expanded;
}

Result:

[
  { type: 'CREATE_FILE', path: 'components/ui/button.tsx', template: 'component.ejs' },
  { type: 'CREATE_FILE', path: 'components/ui/card.tsx', template: 'component.ejs' },
  { type: 'CREATE_FILE', path: 'components/ui/dialog.tsx', template: 'component.ejs' }
]

Next: Layer 5: Action Handlers →