morph-cli 0.1.0

AST-based codebase migration and codemod tool for JavaScript and TypeScript projects.
Documentation
use std::path::{Path, PathBuf};
use anyhow::{Result};
use serde::{Serialize, Deserialize};
use std::fs;
use chrono::{DateTime, Utc};

use crate::core::detection::scanner::Scanner;
use crate::core::registry::RecipeRegistry;
use crate::core::pipeline::executor::{PipelineExecutor};
use crate::core::recipe::TransformMode;
use crate::utils::terminal;
use crate::commands::verify::run_verification;

#[derive(Debug, Serialize, Deserialize)]
pub struct ValidationRepoReport {
    pub timestamp: DateTime<Utc>,
    pub target_path: PathBuf,
    pub total_files: usize,
    pub recipes_detected: usize,
    pub dry_run_modifications: usize,
    pub verification_errors: usize,
    pub verification_warnings: usize,
}

pub fn execute(path: &Path, project_root: &Path) -> Result<()> {
    let spinner = terminal::spinner(format!("Validating repository: {}...", path.display()).as_str());

    // 1. Scan
    let mut scanner = Scanner::new(path.to_path_buf());
    let _scan_result = scanner.scan();
    
    let mut total_files = 0;
    for entry in walkdir::WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
        if entry.file_type().is_file() {
            total_files += 1;
        }
    }

    // 2. Detection (all recipes)
    let registry = RecipeRegistry::new();
    let mut recipes_detected = 0;
    for recipe in registry.all() {
        if let Ok(report) = recipe.detect(path, &indicatif::ProgressBar::hidden()) {
            if !report.analyses.is_empty() {
                recipes_detected += 1;
            }
        }
    }

    // 3. Dry-run Transforms
    let mut executor = PipelineExecutor::new(path.to_path_buf());
    for recipe in registry.all() {
        executor = executor.add_recipe(recipe.metadata().name);
    }
    
    let pipeline_summary = executor.execute(
        path, 
        TransformMode::DryRun, 
        false, // review
        false, // autofix
        &registry, 
        false // verbose
    )?;

    // 4. Verification (Baseline)
    let verify_summary = run_verification(path)?;

    spinner.finish_and_clear();

    let report = ValidationRepoReport {
        timestamp: Utc::now(),
        target_path: path.to_path_buf(),
        total_files,
        recipes_detected,
        dry_run_modifications: pipeline_summary.total_changed_files,
        verification_errors: verify_summary.total_errors,
        verification_warnings: verify_summary.total_warnings,
    };

    // Store report
    let validation_dir = project_root.join(".morph-cli/validation");
    fs::create_dir_all(&validation_dir)?;
    let report_path = validation_dir.join(format!("validation_{}.json", report.timestamp.timestamp()));
    let json = serde_json::to_string_pretty(&report)?;
    fs::write(&report_path, json)?;

    println!();
    println!("{} Repository Validation Summary", terminal::label("".repeat(50).as_str()));
    println!("  Target Path:   {}", path.display());
    println!("  Total Files:   {}", report.total_files);
    println!("  Recipes Active:{}", report.recipes_detected);
    println!("  Dry-run Mods:  {}", report.dry_run_modifications);
    println!("  Baseline Errs: {}", report.verification_errors);
    println!("  Baseline Warns:{}", report.verification_warnings);
    println!("  Report Stored: {}", report_path.display());
    println!();

    Ok(())
}