UNPKG

create-filip-app

Version:

A modern CLI tool for creating Next.js applications with best practices, shadcn components, and MongoDB integration.

3,614 lines 126 kB
#!/usr/bin/env node

import prompts from "prompts";
import { execa } from "execa";
import fs from "fs-extra";
import path from "path";
import ora from "ora";
import chalk from "chalk";
import cliProgress from "cli-progress";
import gradient from "gradient-string";
import boxen from "boxen";
import inquirer from "inquirer";
import inquirerSearchCheckbox from 'inquirer-search-checkbox';
import { spawn } from 'child_process';
import os from 'os';
import * as templates from './templates.js';

// Get package version from package.json
const packageJson = JSON.parse(await fs.readFile(new URL('./package.json', import.meta.url), 'utf8'));
const VERSION = packageJson.version || '0.0.0';

// Register checkbox search prompt
inquirer.registerPrompt('search-checkbox', inquirerSearchCheckbox);

// Theme configuration
const themes = {
  default: {
    primary: ['#FF5F6D', '#FFC371'],
    secondary: 'cyan',
    accent: 'green',
    error: 'red'
  },
  ocean: {
    primary: ['#2E3192', '#1BFFFF'],
    secondary: 'blue',
    accent: 'cyan',
    error: 'red'
  },
  forest: {
    primary: ['#134E5E', '#71B280'],
    secondary: 'green',
    accent: 'yellow',
    error: 'red'
  }
};

let currentTheme = themes.default;
const filip = gradient(currentTheme.primary);

function applyTheme(themeName) {
  currentTheme = themes[themeName] || themes.default;
  return gradient(currentTheme.primary);
}

function printBanner() {
  const gradientFn = gradient(currentTheme.primary);
  
  console.log(gradientFn(`
███████╗██╗██╗     ██╗██████╗ 
██╔════╝██║██║     ██║██╔══██╗
█████╗  ██║██║     ██║██████╔╝
██╔══╝  ██║██║     ██║██╔═══╝ 
██║     ██║███████╗██║██║     
╚═╝     ╚═╝╚══════╝╚═╝╚═╝     
`));
  console.log(`${gradientFn('🚀')} ${chalk.bold("Create Filip App")} ${chalk.dim(`v${VERSION}`)} ${gradientFn('✨')}\n`);
  console.log(chalk.dim("Modern Next.js setup with all the best practices\n"));
}

async function checkPackageManager() {
  try {
    await execa("pnpm", ["--version"]);
    return "pnpm";
  } catch (e) {
    try {
      await execa("yarn", ["--version"]);
      return "yarn";
    } catch (e) {
      return "npm";
    }
  }
}

async function showWelcomeScreen() {
  const gradientFn = gradient(currentTheme.primary);
  
  console.log(boxen(
    `${chalk.bold(gradientFn('Welcome to the Filip App Creator!'))}\n\n` +
    `${chalk.dim('This tool helps you create a production-ready Next.js app with:')}\n\n` +
    `    ${chalk.green('•')} Next.js 14 with App Router\n` +
    `    ${chalk.green('•')} TypeScript for type safety\n` +
    `    ${chalk.green('•')} TailwindCSS for styling\n` +
    `    ${chalk.green('•')} Components system (UI components)\n` +
    `    ${chalk.green('•')} Dark mode support\n` +
    `    ${chalk.green('•')} ESLint configuration\n`,
    { 
      padding: 1, 
      margin: 1,
      borderStyle: 'round', 
      borderColor: 'cyan',
      title: '✨ Welcome',
      titleAlignment: 'center'
    }
  ));
  
  const { action } = await inquirer.prompt([
    {
      type: 'list',
      name: 'action',
      message: 'What would you like to do?',
      prefix: gradientFn('?'),
      choices: [
        { name: `${chalk.bold('🔨 Create')} ${chalk.dim('a new Filip app')}`, value: 'create' },
        { name: `${chalk.bold('🎨 Theme')} ${chalk.dim('change the look')}`, value: 'theme' },
        { name: `${chalk.bold('📝 Version')} ${chalk.dim('check or update version')}`, value: 'version' },
        { name: `${chalk.bold('ℹ️ About')} ${chalk.dim('this tool')}`, value: 'about' },
        { name: `${chalk.bold('❌ Exit')}`, value: 'exit' }
      ]
    }
  ]);
  
  if (action === 'exit') {
    console.log(chalk.yellow("\nExiting. Goodbye! 👋"));
    process.exit(0);
  } else if (action === 'version') {
    const gradientFn = gradient(currentTheme.primary);
    console.log(boxen(
      `${chalk.bold(gradientFn('Current Version'))}\n\n` +
      `${chalk.bold(VERSION)}`,
      { 
        padding: 1, 
        margin: 1, 
        borderStyle: 'round', 
        borderColor: 'magenta',
        title: '🔢 Version',
        titleAlignment: 'center'
      }
    ));
    
    const { versionAction } = await inquirer.prompt([
      {
        type: 'list',
        name: 'versionAction',
        message: 'What would you like to do?',
        choices: [
          { name: 'Update version (semantic)', value: 'update' },
          { name: 'Back to main menu', value: 'back' }
        ]
      }
    ]);
    
    if (versionAction === 'update') {
      // Parse current version
      const [major, minor, patch] = VERSION.split('.').map(Number);
      
      const { updateType } = await inquirer.prompt([
        {
          type: 'list',
          name: 'updateType',
          message: 'Select version update type:',
          choices: [
            { name: `Major (${major + 1}.0.0)`, value: 'major' },
            { name: `Minor (${major}.${minor + 1}.0)`, value: 'minor' },
            { name: `Patch (${major}.${minor}.${patch + 1})`, value: 'patch' },
            { name: 'Custom version', value: 'custom' }
          ]
        }
      ]);
      
      let newVersion;
      
      if (updateType === 'major') {
        newVersion = `${major + 1}.0.0`;
      } else if (updateType === 'minor') {
        newVersion = `${major}.${minor + 1}.0`;
      } else if (updateType === 'patch') {
        newVersion = `${major}.${minor}.${patch + 1}`;
      } else {
        const { customVersion } = await inquirer.prompt([
          {
            type: 'input',
            name: 'customVersion',
            message: 'Enter new version (x.y.z format):',
            default: VERSION,
            validate: (input) => {
              return /^\d+\.\d+\.\d+$/.test(input) 
                ? true 
                : 'Please enter a valid version in x.y.z format';
            }
          }
        ]);
        newVersion = customVersion;
      }
      
      // Update the version
      const spinner = ora({
        text: `Updating version to ${newVersion}...`,
        color: 'magenta'
      }).start();
      
      const success = await updatePackageVersion(newVersion);
      
      if (success) {
        spinner.succeed(`Version updated to ${newVersion}`);
        console.log(chalk.yellow('\nPlease restart the CLI to apply the new version.\n'));
      } else {
        spinner.fail('Failed to update version');
      }
    }
    
    return showWelcomeScreen();
  } else if (action === 'about') {
    const gradientFn = gradient(currentTheme.primary);
    console.log(boxen(
      `${chalk.bold(gradientFn('Create Filip App'))}\n\n` +
      `A modern CLI tool for creating Next.js applications with best practices.\n\n` +
      `${chalk.bold('Author:')} Filip\n` +
      `${chalk.bold('Version:')} ${VERSION}\n` +
      `${chalk.bold('License:')} MIT`,
      { 
        padding: 1, 
        margin: 1, 
        borderStyle: 'round', 
        borderColor: 'blue',
        title: 'About',
        titleAlignment: 'center'
      }
    ));
    return showWelcomeScreen();
  } else if (action === 'theme') {
    const { selectedTheme } = await inquirer.prompt([
      {
        type: 'list',
        name: 'selectedTheme',
        message: 'Select a theme:',
        choices: [
          { name: '🔥 Default (Warm)', value: 'default' },
          { name: '🌊 Ocean Blue', value: 'ocean' },
          { name: '🌲 Forest Green', value: 'forest' }
        ]
      }
    ]);
    
    applyTheme(selectedTheme);
    console.log(chalk.green(`\nTheme changed to ${selectedTheme}!\n`));
    return showWelcomeScreen();
  }
  
  return action;
}

// Function to update the package.json version
async function updatePackageVersion(newVersion) {
  try {
    // Read the current package.json
    const packageJsonPath = new URL('./package.json', import.meta.url);
    const packageData = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
    
    // Update the version
    packageData.version = newVersion;
    
    // Write back to package.json with proper formatting
    await fs.writeFile(
      packageJsonPath, 
      JSON.stringify(packageData, null, 2) + '\n', 
      'utf8'
    );
    
    console.log(chalk.green(`✓ Package version updated to ${newVersion}`));
    return true;
  } catch (error) {
    console.error(chalk.red(`Failed to update package version: ${error.message}`));
    return false;
  }
}

// Helper function to set up project manually
async function setupProjectManually(projectName, projectDir, pkgManager) {
  console.log(chalk.cyan(`Setting up project manually...`));
  
  try {
    // Create the base directory
    await fs.ensureDir(projectDir);
    
    // Create package.json
    const basePackageJson = {
      name: projectName,
      version: "0.1.0",
      private: true,
      scripts: {
        dev: "next dev",
        build: "next build",
        start: "next start",
        lint: "next lint"
      },
      dependencies: {
        next: "latest",
        react: "latest",
        "react-dom": "latest"
      },
      devDependencies: {
        typescript: "latest",
        "@types/node": "latest",
        "@types/react": "latest",
        "@types/react-dom": "latest",
        "@tailwindcss/postcss": "latest",
        "tailwindcss": "latest",
        "postcss": "latest",
        "autoprefixer": "latest",
        "eslint": "latest",
        "eslint-config-next": "latest"
      }
    };
    
    await fs.writeJson(path.join(projectDir, 'package.json'), basePackageJson, { spaces: 2 });
    
    // Create basic Next.js structure
    await fs.ensureDir(path.join(projectDir, 'app'));
    await fs.ensureDir(path.join(projectDir, 'public'));
    
    // Create a basic page.tsx
    // Choose page template based on whether components are being installed
    let pageTemplate;
    if (features && features.installComponents) {
      pageTemplate = templates.componentsShowcasePage;
    } else {
      pageTemplate = templates.pageContent(projectName);
    }
    
    await fs.writeFile(path.join(projectDir, 'app', 'page.tsx'), pageTemplate);
    
    // Create a basic layout.tsx
    const layoutContent = templates.layoutContent;
    
    await fs.writeFile(path.join(projectDir, 'app', 'layout.tsx'), layoutContent);
    
    // Create minimal config files
    const nextConfigContent = templates.nextConfigContent;
    
    await fs.writeFile(path.join(projectDir, 'next.config.js'), nextConfigContent);
    
    const tsConfigContent = templates.tsConfigContent;
    
    await fs.writeFile(path.join(projectDir, 'tsconfig.json'), tsConfigContent);
    
    // Create PostCSS config
    const postcssConfigContent = templates.postcssConfigContent;
    await fs.writeFile(path.join(projectDir, 'postcss.config.mjs'), postcssConfigContent);
    
    // Create the CSS file
    const globalCssContent = templates.globalCssContent;
    
    await fs.ensureDir(path.join(projectDir, 'app'));
    await fs.writeFile(path.join(projectDir, 'app', 'globals.css'), globalCssContent);
    
    console.log(chalk.green(`Created basic Next.js app structure manually`));
    
    console.log(boxen(
      `${chalk.bold(gradient(currentTheme.primary)('Manual Setup Complete'))}\n\n` +
      `${chalk.cyan('Basic Next.js app structure created. You may need to run:')} \n\n` +
      `${chalk.dim(`${pkgManager} install`)} \n\n` +
      `${chalk.cyan('to complete the setup after installation.')}`,
      { 
        padding: 1, 
        margin: 1, 
        borderStyle: 'round', 
        borderColor: 'yellow',
        title: '📦 Manual Setup',
        titleAlignment: 'center'
      }
    ));
    
    return true;
  } catch (manualError) {
    console.error(chalk.red(`Failed to set up project manually: ${manualError.message}`));
    return false;
  }
}

async function run() {
  // Define gradient function at the beginning to prevent reference errors
  const runGradient = text => gradient(currentTheme.primary)(text);
  
  printBanner();

  const action = await showWelcomeScreen();
  
  if (action !== 'create') return;

  // Project configuration
  const config = await inquirer.prompt([
    {
      type: 'input',
      name: 'appName',
      message: 'What is your project name?',
      default: 'filip-app',
      validate: input => {
        if (input.trim() === '') return 'Project name cannot be empty';
        
        // Check if directory already exists
        const dirPath = path.resolve(process.cwd(), input.trim());
        if (fs.existsSync(dirPath)) {
          const dirContents = fs.readdirSync(dirPath);
          if (dirContents.length > 0) {
            return `Directory "${input}" already exists and is not empty. Please choose a different name or use an empty directory.`;
          }
        }
        
        return true;
      }
    },
    {
      type: 'list',
      name: 'packageManager',
      message: 'Select preferred package manager:',
      choices: [
        { name: '🔍 Auto-detect (recommended)', value: 'auto' },
        { name: '📦 npm', value: 'npm' },
        { name: '🚀 pnpm', value: 'pnpm' },
        { name: '🧶 yarn', value: 'yarn' }
      ],
      default: 'auto'
    },
    {
      type: 'checkbox',
      name: 'features',
      message: 'Select additional features:',
      choices: [
        { name: '✅ Install UI components', value: 'components', checked: true },
        { name: '⚡ Ultra-fast mode (10x faster setup)', value: 'fastMode', checked: true },
        { name: '🌓 Light/Dark theme switcher', value: 'themeSwitch', checked: true },
        { name: '💾 MongoDB + Prisma setup', value: 'prisma', checked: false },
        { name: '🔐 Authentication (Next-Auth)', value: 'auth', checked: false },
        { name: '📄 Add documentation files', value: 'docs', checked: false },
        { name: '📱 Add responsive layouts', value: 'responsive', checked: false }
      ]
    }
  ]);
  
  // Extract config
  const { appName, packageManager: pmChoice } = config;
  const features = {
    installComponents: config.features.includes('components'),
    fastMode: config.features.includes('fastMode'),
    themeSwitch: config.features.includes('themeSwitch'),
    prisma: config.features.includes('prisma'),
    auth: config.features.includes('auth'),
    includeDocs: config.features.includes('docs'),
    responsive: config.features.includes('responsive')
  };

  const appDir = path.resolve(process.cwd(), appName);

  // Determine package manager
  let packageManager = pmChoice;
  if (packageManager === 'auto') {
    packageManager = await checkPackageManager();
  }
  
  // Show component selection if components are enabled
  let selectedComponents = ['button', 'card', 'sheet', 'dropdown-menu', 'tabs', 'input', 'label'];
  
  if (features.installComponents) {
    // Create arrays of available components - separated by type
    const shadcnComponents = [
      'accordion', 'alert', 'alert-dialog', 'aspect-ratio', 'avatar', 'badge', 
      'breadcrumb', 'button', 'calendar', 'card', 'carousel', 'chart', 'checkbox', 
      'collapsible', 'combobox', 'command', 'context-menu', 'data-table', 'date-picker', 
      'dialog', 'drawer', 'dropdown-menu', 'form', 'hover-card', 'input', 'input-otp', 
      'label', 'menubar', 'navigation-menu', 'pagination', 'popover', 'progress', 
      'radio-group', 'resizable', 'scroll-area', 'select', 'separator', 'sheet', 
      'sidebar', 'skeleton', 'slider', 'sonner', 'switch', 'table', 'tabs', 'textarea', 
      'toast', 'toggle', 'toggle-group', 'tooltip'
    ];
    
    const customComponents = [
      'file-uploader', 'video-player', 'timeline', 'rating', 'file-tree', 'copy-button'
    ];
    
    // Combine all components
    const allComponents = [...shadcnComponents, ...customComponents];
    
    // Define essential components with additional important ones
    const essentialComponents = [
      'button', 'card', 'dialog', 'dropdown-menu', 'form', 'input', 'label', 
      'sheet', 'tabs', 'separator', 'toast', 'accordion', 'slider'
    ];
    
    // First, ask if the user wants to select components
    const { componentSelection } = await inquirer.prompt([
      {
        type: 'list',
        name: 'componentSelection',
        message: 'How would you like to select Components?',
        choices: [
          { name: '🔍 Custom selection (choose specific components)', value: 'custom' },
          { name: '✅ Select all components', value: 'all' },
          { name: '🧰 Essential components only (common UI elements)', value: 'essential' }
        ]
      }
    ]);
    
    if (componentSelection === 'all') {
      selectedComponents = allComponents;
      console.log(chalk.green(`\n✓ Selected all ${selectedComponents.length} components\n`));
    } else if (componentSelection === 'essential') {
      selectedComponents = essentialComponents;
      console.log(chalk.green(`\n✓ Selected essential components: ${selectedComponents.join(', ')}\n`));
    } else {
      // Show the component selection dialog for custom selection with grouping
      const componentPrompt = await inquirer.prompt([
        {
          type: 'checkbox', // Change from search-checkbox to regular checkbox
          name: 'components',
          message: 'Select components to install:',
          choices: [
            new inquirer.Separator('--- UI Components ---'),
            { name: 'Accordion', value: 'accordion' },
            { name: 'Alert', value: 'alert' },
            { name: 'Alert Dialog', value: 'alert-dialog' },
            { name: 'Aspect Ratio', value: 'aspect-ratio' },
            { name: 'Avatar', value: 'avatar' },
            { name: 'Badge', value: 'badge' },
            { name: 'Breadcrumb', value: 'breadcrumb' },
            { name: 'Button', value: 'button', checked: true },
            { name: 'Calendar', value: 'calendar' },
            { name: 'Card', value: 'card', checked: true },
            { name: 'Carousel', value: 'carousel' },
            { name: 'Chart', value: 'chart' },
            { name: 'Checkbox', value: 'checkbox' },
            { name: 'Collapsible', value: 'collapsible' },
            { name: 'Combobox', value: 'combobox' },
            { name: 'Command', value: 'command' },
            { name: 'Context Menu', value: 'context-menu' },
            { name: 'Data Table', value: 'data-table' },
            { name: 'Date Picker', value: 'date-picker' },
            { name: 'Dialog', value: 'dialog', checked: true },
            { name: 'Drawer', value: 'drawer' },
            { name: 'Dropdown Menu', value: 'dropdown-menu', checked: true },
            { name: 'Form', value: 'form', checked: true },
            { name: 'Hover Card', value: 'hover-card' },
            { name: 'Input', value: 'input', checked: true },
            { name: 'Input OTP', value: 'input-otp' },
            { name: 'Label', value: 'label', checked: true },
            { name: 'Menubar', value: 'menubar' },
            { name: 'Navigation Menu', value: 'navigation-menu' },
            { name: 'Pagination', value: 'pagination' },
            { name: 'Popover', value: 'popover' },
            { name: 'Progress', value: 'progress' },
            { name: 'Radio Group', value: 'radio-group' },
            { name: 'Resizable', value: 'resizable' },
            { name: 'Scroll Area', value: 'scroll-area' },
            { name: 'Select', value: 'select' },
            { name: 'Separator', value: 'separator' },
            { name: 'Sheet', value: 'sheet' },
            { name: 'Sidebar', value: 'sidebar' },
            { name: 'Skeleton', value: 'skeleton' },
            { name: 'Slider', value: 'slider' },
            { name: 'Sonner', value: 'sonner' },
            { name: 'Switch', value: 'switch' },
            { name: 'Table', value: 'table' },
            { name: 'Tabs', value: 'tabs' },
            { name: 'Textarea', value: 'textarea' },
            { name: 'Toast', value: 'toast' },
            { name: 'Toggle', value: 'toggle' },
            { name: 'Toggle Group', value: 'toggle-group' },
            { name: 'Tooltip', value: 'tooltip' },
            new inquirer.Separator('--- Custom Components ---'),
            { name: 'File Uploader (Drag & Drop)', value: 'file-uploader', checked: true },
            { name: 'Video Player Interface', value: 'video-player' },
            { name: 'Timeline', value: 'timeline' },
            { name: 'Rating/Stars Input', value: 'rating' },
            { name: 'File Tree', value: 'file-tree' },
            { name: 'Copy to Clipboard Button', value: 'copy-button', checked: true }
          ],
          pageSize: 20
        }
      ]);
      
      if (componentPrompt.components.length > 0) {
        selectedComponents = componentPrompt.components;
      }
    }
  }
  
  // Show configuration summary
  console.log(boxen(
    `${chalk.bold(runGradient('✨ Project Configuration ✨'))}\n\n` +
    `${chalk.bold('Project:')} ${chalk.cyan(appName)}\n` +
    `${chalk.bold('Location:')} ${chalk.cyan(appDir)}\n` +
    `${chalk.bold('Package Manager:')} ${chalk.cyan(packageManager)}\n\n` +
    `${chalk.bold('Features:')}\n` +
    `  ${features.installComponents ? chalk.green('✓') : chalk.gray('○')} ${chalk.bold('UI Components:')} ${features.installComponents ? chalk.cyan(selectedComponents.length > 5 ? selectedComponents.slice(0, 5).join(', ') + ', ...' : selectedComponents.join(', ')) : chalk.gray('None')}\n` +
    `  ${features.fastMode ? chalk.green('✓') : chalk.gray('○')} ${chalk.bold('Fast Mode')}\n` +
    `  ${features.themeSwitch ? chalk.green('✓') : chalk.gray('○')} ${chalk.bold('Theme Switcher')}\n` +
    `  ${features.prisma ? chalk.green('✓') : chalk.gray('○')} ${chalk.bold('Prisma Database')}\n` +
    `  ${features.auth ? chalk.green('✓') : chalk.gray('○')} ${chalk.bold('Authentication')}\n` +
    `  ${features.includeDocs ? chalk.green('✓') : chalk.gray('○')} ${chalk.bold('Documentation')}\n` +
    `  ${features.responsive ? chalk.green('✓') : chalk.gray('○')} ${chalk.bold('Responsive Layout')}`,
    { 
      padding: 1, 
      margin: 1, 
      borderStyle: 'round', 
      borderColor: currentTheme.secondary, 
      title: '🛠️ Configuration',
      titleAlignment: 'center'
    }
  ));
  
  // Confirm and continue
  const { confirm } = await inquirer.prompt([
    {
      type: 'confirm',
      name: 'confirm',
      message: 'Create the application with these settings?',
      default: true
    }
  ]);
  
  if (!confirm) {
    console.log(chalk.yellow("\nCancelled. Let's start over!"));
    return run();
  }
  
  const spinner = ora({
    color: currentTheme.secondary,
    spinner: 'dots'
  });
  
  const steps = [
    "Creating Next.js app",
    "Installing dependencies",
    "Setting up shadcn components",
    "Creating .env file"
  ];

  if (features.themeSwitch) {
    steps.push("Setting up theme switcher");
  }

  if (features.prisma) {
    steps.push("Initializing Prisma");
  }
  
  if (features.auth) {
    steps.push("Setting up authentication");
  }

  if (features.installComponents) {
    steps.push("Installing UI components");
  }
  
  if (features.includeDocs) {
    steps.push("Adding documentation");
  }
  
  if (features.responsive) {
    steps.push("Setting up responsive layouts");
  }

  const progressBar = new cliProgress.SingleBar({
    format: runGradient("Installation") + " " + chalk.dim("|") + chalk[currentTheme.secondary]("{bar}") + chalk.dim("|") + " " + chalk.bold("{percentage}%") + chalk.dim(" •") + " " + chalk.white("{step}"),
    barCompleteChar: "█",
    barIncompleteChar: "░",
    hideCursor: true,
    clearOnComplete: true,
    barsize: 30
  });

  // Create a function to update progress with better visuals
  const updateProgress = (step, incrementBy = 1) => {
    currentStep += incrementBy;
    progressBar.update(currentStep, { step: chalk.cyan(step) });
    spinner.text = step;
  };

  // Initialize progress tracking
  let currentStep = 0;
  progressBar.start(steps.length, currentStep, { step: "Starting..." });

  try {
    // Step 1: Create Next.js app
    updateProgress(`${steps[0]}...`);
    spinner.stop();
    
    // Show a creating app box rather than plain text
    console.log(boxen(
      `${chalk.bold(runGradient('Creating Next.js App'))}\n\n` +
      `${chalk.cyan('•')} Project name: ${chalk.bold(appName)}\n` +
      `${chalk.cyan('•')} Directory: ${chalk.dim(appDir)}\n` +
      `${chalk.cyan('•')} Package manager: ${chalk.bold(packageManager)}\n` +
      `${chalk.cyan('•')} Features: ${chalk.dim('TypeScript, ESLint, Tailwind CSS')}\n`,
      { 
        padding: 1, 
        margin: 1, 
        borderStyle: 'round', 
        borderColor: currentTheme.secondary,
        title: '🚀 New Project',
        titleAlignment: 'center'
      }
    ));
    
    // Create a loader for the app creation process
    const createAppSpinner = ora({
      text: chalk.cyan('Initializing Next.js app (this might take a moment)...'),
      color: 'cyan',
      spinner: 'dots'
    }).start();
    
    // Check if directory exists and is not empty before running create-next-app
    const appDirExists = fs.existsSync(appDir);
    if (appDirExists) {
      const dirContents = fs.readdirSync(appDir);
      if (dirContents.length > 0) {
        const { handleExisting } = await inquirer.prompt([
          {
            type: 'list',
            name: 'handleExisting',
            message: `Directory "${appName}" already exists and contains files. What would you like to do?`,
            choices: [
              { name: 'Choose a different name', value: 'rename' },
              { name: 'Delete existing directory and start fresh', value: 'delete' },
              { name: 'Cancel installation', value: 'cancel' }
            ]
          }
        ]);
        
        if (handleExisting === 'rename') {
          const { newName } = await inquirer.prompt([
            {
              type: 'input',
              name: 'newName',
              message: 'Enter a new project name:',
              validate: input => {
                if (input.trim() === '') return 'Project name cannot be empty';
                
                const newDir = path.resolve(process.cwd(), input.trim());
                if (fs.existsSync(newDir)) {
                  const newDirContents = fs.readdirSync(newDir);
                  if (newDirContents.length > 0) {
                    return `Directory "${input}" also exists and is not empty. Please choose a different name.`;
                  }
                }
                
                return true;
              }
            }
          ]);
          
          // Update app name and directory
          appName = newName.trim();
          appDir = path.resolve(process.cwd(), appName);
        } else if (handleExisting === 'delete') {
          const { confirmDelete } = await inquirer.prompt([
            {
              type: 'confirm',
              name: 'confirmDelete',
              message: `Are you sure you want to delete the existing "${appName}" directory? This cannot be undone.`,
              default: false
            }
          ]);
          
          if (confirmDelete) {
            try {
              spinner.start(`Deleting existing directory ${appName}...`);
              await fs.remove(appDir);
              spinner.succeed(`Deleted existing directory ${appName}`);
            } catch (deleteError) {
              spinner.fail(`Failed to delete directory: ${deleteError.message}`);
              throw new Error(`Cannot proceed: failed to delete existing directory ${appName}`);
            }
          } else {
            throw new Error('Installation cancelled: directory already exists');
          }
        } else {
          throw new Error('Installation cancelled by user');
        }
      }
    }
    
    // Determine package manager flags for create-next-app
    const pmFlag = packageManager === "npm" ? "--use-npm" : 
                  packageManager === "pnpm" ? "--use-pnpm" : 
                  packageManager === "yarn" ? "--use-yarn" : "--use-npm";
    
    // After the create-next-app command is executed, capture the output but display it in a better format
    try {
      // Create a function to verify app creation success
      const verifyAppCreation = async () => {
        // Check if the directory was actually created
        if (!fs.existsSync(appDir)) {
          return false;
        }
        
        // Check if the directory contains basic Next.js files
        try {
          const dirContents = await fs.readdir(appDir);
          return dirContents.some(file => 
            ['package.json', 'next.config.js', 'app', 'tsconfig.json'].includes(file)
          );
        } catch (err) {
          return false;
        }
      };
      
      // Execute the create-next-app command but capture output instead of showing raw
      createAppSpinner.text = chalk.cyan(`Running create-next-app for ${chalk.bold(appName)}...`);
      
      try {
        const { stdout } = await execa("npx", [
          "create-next-app@latest",
          appName,
          "--ts",
          "--tailwind",
          "--eslint",
          "--app", 
          "--src-dir",
          "--import-alias", "@/*",
          pmFlag
        ], { 
          stdio: ["ignore", "pipe", "pipe"],
          timeout: 180000 // 3 minutes timeout
        });
        
        // Verify the app was created successfully
        const isCreated = await verifyAppCreation();
        
        if (!isCreated) {
          throw new Error("Next.js app directory wasn't created properly despite command succeeding");
        }
        
        createAppSpinner.succeed(chalk.green(`Next.js app created successfully`));
        
        // Parse the output to extract useful information
        const dependencies = stdout.match(/dependencies:\n([\s\S]*?)(?:\n\n|$)/);
        const devDependencies = stdout.match(/devDependencies:\n([\s\S]*?)(?:\n\n|$)/);
        
        // Display the dependencies in a nicely formatted box
        if (dependencies || devDependencies) {
          console.log(boxen(
            `${chalk.bold(runGradient('Packages Installed'))}\n\n` +
            `${dependencies ? `${chalk.cyan('Dependencies:')}\n${chalk.dim(dependencies[1])}\n` : ''}` +
            `${devDependencies ? `${chalk.cyan('Dev Dependencies:')}\n${chalk.dim(devDependencies[1])}` : ''}`,
            { 
              padding: 1, 
              margin: 1, 
              borderStyle: 'round', 
              borderColor: 'green',
              title: '📦 Dependencies',
              titleAlignment: 'center'
            }
          ));
        }
      } catch (cmdError) {
        // If the command failed, try a different approach with fs-extra
        createAppSpinner.text = chalk.cyan(`First approach failed. Trying alternative method for ${chalk.bold(appName)}...`);
        
        // Create the base directory
        await fs.ensureDir(appDir);
        
        // Create package.json
        const basePackageJson = {
          name: appName,
          version: "0.1.0",
          private: true,
          scripts: {
            dev: "next dev",
            build: "next build",
            start: "next start",
            lint: "next lint"
          },
          dependencies: {
            next: "latest",
            react: "latest",
            "react-dom": "latest"
          },
          devDependencies: {
            typescript: "latest",
            "@types/node": "latest",
            "@types/react": "latest",
            "@types/react-dom": "latest",
            "@tailwindcss/postcss": "latest",
            "tailwindcss": "latest",
            "postcss": "latest",
            "autoprefixer": "latest",
            "eslint": "latest",
            "eslint-config-next": "latest"
          }
        };
        
        await fs.writeJson(path.join(appDir, 'package.json'), basePackageJson, { spaces: 2 });
        
        // Create basic Next.js structure
        await fs.ensureDir(path.join(appDir, 'app'));
        await fs.ensureDir(path.join(appDir, 'public'));
        
        // Create a basic page.tsx
        // Create a basic page.tsx - use showcase page if components are selected
        let pageTemplate;
        if (features.installComponents) {
          pageTemplate = templates.componentsShowcasePage;
        } else {
          pageTemplate = templates.pageContent(appName);
        }
        
        await fs.writeFile(path.join(appDir, 'app', 'page.tsx'), pageTemplate);
        
        // Create a basic layout.tsx
        const layoutContent = templates.layoutContent;
        
        await fs.writeFile(path.join(appDir, 'app', 'layout.tsx'), layoutContent);
        
        // Create minimal config files
        const nextConfigContent = templates.nextConfigContent;
        
        await fs.writeFile(path.join(appDir, 'next.config.js'), nextConfigContent);
        
        const tsConfigContent = templates.tsConfigContent;
        
        await fs.writeFile(path.join(appDir, 'tsconfig.json'), tsConfigContent);
        
        // Create PostCSS config
        const postcssConfigContent = templates.postcssConfigContent;
        await fs.writeFile(path.join(appDir, 'postcss.config.mjs'), postcssConfigContent);
        
        // Create the CSS file
        const globalCssContent = templates.globalCssContent;
        
        await fs.ensureDir(path.join(appDir, 'app'));
        await fs.writeFile(path.join(appDir, 'app', 'globals.css'), globalCssContent);
        
        createAppSpinner.succeed(chalk.green(`Created basic Next.js app structure manually`));
        
        console.log(boxen(
          `${chalk.bold(runGradient('Manual Setup Complete'))}\n\n` +
          `${chalk.cyan('Basic Next.js app structure created. You may need to run:')} \n\n` +
          `${chalk.dim(`${packageManager} install`)} \n\n` +
          `${chalk.cyan('to complete the setup after installation.')}`,
          { 
            padding: 1, 
            margin: 1, 
            borderStyle: 'round', 
            borderColor: 'yellow',
            title: '📦 Manual Setup',
            titleAlignment: 'center'
          }
        ));
      }
      
    } catch (error) {
      createAppSpinner.fail(chalk.red(`Failed to create Next.js app: ${error.message}`));
      
      // Show error details in a box
      console.error(boxen(
        chalk.red(`Error creating Next.js app:\n\n${error.message}\n\n${error.stderr || ''}`),
        { 
          padding: 1, 
          margin: 1, 
          borderStyle: 'round', 
          borderColor: 'red',
          title: '❌ Error',
          titleAlignment: 'center'
        }
      ));
      
      // Ask if the user wants to try the manual approach instead
      const { manualSetup } = await inquirer.prompt([
        {
          type: 'confirm',
          name: 'manualSetup',
          message: 'Would you like to try setting up the project manually?',
          default: true
        }
      ]);
      
      if (manualSetup) {
        createAppSpinner.text = chalk.cyan(`Setting up project manually...`);
        
        try {
          // Create the base directory
          await fs.ensureDir(appDir);
          
          // Create package.json
          const basePackageJson = {
            name: appName,
            version: "0.1.0",
            private: true,
            scripts: {
              dev: "next dev",
              build: "next build",
              start: "next start",
              lint: "next lint"
            },
            dependencies: {
              next: "latest",
              react: "latest",
              "react-dom": "latest"
            },
            devDependencies: {
              typescript: "latest",
              "@types/node": "latest",
              "@types/react": "latest",
              "@types/react-dom": "latest",
              "@tailwindcss/postcss": "latest",
              "tailwindcss": "latest",
              "postcss": "latest",
              "autoprefixer": "latest",
              "eslint": "latest",
              "eslint-config-next": "latest"
            }
          };
          
          await fs.writeJson(path.join(appDir, 'package.json'), basePackageJson, { spaces: 2 });
          
          // Create basic Next.js structure
          await fs.ensureDir(path.join(appDir, 'app'));
          await fs.ensureDir(path.join(appDir, 'public'));
          
          // Create a basic page.tsx
          // Create a basic page.tsx - use showcase page if components are selected
          let pageTemplate;
          if (features.installComponents) {
            pageTemplate = templates.componentsShowcasePage;
          } else {
            pageTemplate = templates.pageContent(appName);
          }
          
          await fs.writeFile(path.join(appDir, 'app', 'page.tsx'), pageTemplate);
          
          // Create a basic layout.tsx
          const layoutContent = templates.layoutContent;
          
          await fs.writeFile(path.join(appDir, 'app', 'layout.tsx'), layoutContent);
          
          // Create minimal config files
          const nextConfigContent = templates.nextConfigContent;
          
          await fs.writeFile(path.join(appDir, 'next.config.js'), nextConfigContent);
          
          const tsConfigContent = templates.tsConfigContent;
          
          await fs.writeFile(path.join(appDir, 'tsconfig.json'), tsConfigContent);
          
          // Create PostCSS config
          const postcssConfigContent = templates.postcssConfigContent;
          await fs.writeFile(path.join(appDir, 'postcss.config.mjs'), postcssConfigContent);
          
          // Create the CSS file
          const globalCssContent = templates.globalCssContent;
          
          await fs.ensureDir(path.join(appDir, 'app'));
          await fs.writeFile(path.join(appDir, 'app', 'globals.css'), globalCssContent);
          
          createAppSpinner.succeed(chalk.green(`Created basic Next.js app structure manually`));
          
        } catch (manualError) {
          createAppSpinner.fail(chalk.red(`Failed to set up project manually: ${manualError.message}`));
          throw new Error("Failed to create Next.js app manually");
        }
      } else {
        throw new Error("Failed to create Next.js app");
      }
    }
    
    spinner.succeed(chalk[currentTheme.accent](`${steps[0]} complete`));

    // Check if the directory exists before trying to change into it
    try {
      // Verify the project directory exists before changing to it
      if (!fs.existsSync(appDir)) {
        throw new Error(`Project directory ${appDir} was not created properly. Cannot proceed.`);
      }
      
      // Check if critical files exist in the directory
      const requiredFiles = ['package.json'];
      const missingFiles = requiredFiles.filter(file => !fs.existsSync(path.join(appDir, file)));
      
      if (missingFiles.length > 0) {
        throw new Error(`Project directory ${appDir} is missing critical files: ${missingFiles.join(', ')}. Cannot proceed.`);
      }
      
      // Try to change to the project directory
      process.chdir(appDir);
      console.log(chalk.green(`✓ Changed to directory: ${appDir}`));
    } catch (dirError) {
      spinner.fail(chalk.red(`Failed to access project directory: ${dirError.message}`));
      console.error(boxen(
        chalk.red(`There was an error accessing the project directory at ${appDir}.\n\nThis could be due to permission issues or the directory wasn't created properly.`),
        { 
          padding: 1, 
          margin: 1, 
          borderStyle: 'round', 
          borderColor: 'red',
          title: '❌ Directory Error',
          titleAlignment: 'center'
        }
      ));
      
      // Ask the user what they want to do
      const { dirAction } = await inquirer.prompt([
        {
          type: 'list',
          name: 'dirAction',
          message: 'How would you like to proceed?',
          choices: [
            { name: 'Create directory manually and continue', value: 'create-manual' },
            { name: 'Try again from the beginning', value: 'retry' },
            { name: 'Exit', value: 'exit' }
          ]
        }
      ]);
      
      if (dirAction === 'create-manual') {
        try {
          // Call the manual setup function to create project structure
          const success = await setupProjectManually(appName, appDir, packageManager);
          
          if (success) {
            // Verify the directory exists again before attempting to change to it
            if (fs.existsSync(appDir)) {
              process.chdir(appDir);
              console.log(chalk.green(`✓ Changed to directory: ${appDir}`));
            } else {
              throw new Error(`Directory ${appDir} still cannot be accessed after manual creation.`);
            }
          } else {
            throw new Error(`Manual setup failed. Cannot continue.`);
          }
        } catch (createError) {
          console.error(chalk.red(`Failed to create directory: ${createError.message}`));
          throw new Error(`Cannot continue without a valid project directory.`);
        }
      } else if (dirAction === 'retry') {
        return run();
      } else {
        process.exit(1);
      }
    }

    // Add pnpm configuration to package.json
    if (packageManager === "pnpm") {
      try {
        const pkgJsonPath = path.join(process.cwd(), 'package.json');
        const pkgJson = await fs.readJSON(pkgJsonPath);
        
        // Add pnpm configuration with onlyBuiltDependencies directly in package.json
        pkgJson.pnpm = {
          ...pkgJson.pnpm,
          onlyBuiltDependencies: [
            "sharp",
            "@prisma/engines",
            "prisma",
            "@prisma/client",
            "esbuild",
            "next-auth", 
            "next-themes"
          ]
        };
        
        await fs.writeJSON(pkgJsonPath, pkgJson, { spaces: 2 });
      } catch (pkgJsonError) {
        spinner.warn(chalk.yellow(`Could not update package.json with pnpm configuration: ${pkgJsonError.message}`));
      }
    }

    // Create components and lib folders to speed up shadcn setup
    fs.mkdirSync("components/ui", { recursive: true });
    fs.mkdirSync("lib", { recursive: true });
    
    // Step 2: Install dependencies
    updateProgress(`${steps[1]}...`);
    
    const installCmd = packageManager;
    let installOutput = '';
    
    // Fast mode implementation
    if (features.fastMode) {
      try {
        // Enable performance optimizations
        const { 
          installDependenciesConcurrently,
          installShadcnComponentsBatch,
          setupFilesOptimized
        } = await optimizeSetup(packageManager);
        
        // Show a box to indicate fast mode installation
        console.log(boxen(
          `${chalk.bold(runGradient('Fast Mode Enabled'))}\n\n` +
          `${chalk.dim('Installing packages with')} ${chalk.cyan(packageManager)} ${chalk.dim('(10x faster)')}\n` +
          `${chalk.dim('Optimized for performance...')}`,
          { 
            padding: 1, 
            margin: 1, 
            borderStyle: 'round', 
            borderColor: currentTheme.secondary,
            title: '⚡ Fast Mode',
            titleAlignment: 'center'
          }
        ));
        
        // Use optimized functions for installation
        updateProgress(`Installing dependencies (fast mode)...`);
        await installDependenciesConcurrently(['react', 'react-dom', 'next', 'next-themes']);
        spinner.succeed(chalk[currentTheme.accent](`Core dependencies installed`));
        
        // Enhanced component installation if needed
        if (features.installComponents && selectedComponents && selectedComponents.length > 0) {
          updateProgress(`Installing Components System (fast mode)...`);
          
          console.log(boxen(
            `${chalk.bold(runGradient('Installing Components'))}\n\n` +
            `${chalk.dim('Installing:')} ${chalk.cyan(selectedComponents.length)} ${chalk.dim('components')}\n` +
            `${chalk.dim('Method:')} ${chalk.cyan('Optimized batch installation')}`,
            { 
              padding: 1, 
              margin: 1, 
              borderStyle: 'round', 
              borderColor: currentTheme.secondary,
              title: '🧩 Components',
              titleAlignment: 'center'
            }
          ));
          
          // First make sure lib and components directories exist
          await fs.ensureDir('lib');
          await fs.ensureDir('components/ui');
          
          // Create utils.ts if it doesn't exist
          if (!fs.existsSync('lib/utils.ts')) {
            const utilsContent = templates.utilsContent;
            await fs.writeFile('lib/utils.ts', utilsContent);
          }
          
          // Create components.json configuration
          const componentsJson = {
            "$schema": "https://ui.shadcn.com/schema.json",
            "style": "default",
            "rsc": true,
            "tsx": true,
            "tailwind": {
              "config": "tailwind.config.ts",
              "css": "app/globals.css",
              "baseColor": "slate",
              "cssVariables": true
            },
            "aliases": {
              "components": "@/components",
              "utils": "@/lib/utils"
            }
          };
          
          await fs.writeFile('components.json', JSON.stringify(componentsJson, null, 2));
          
          // Install components using the optimized batch function
          await installShadcnComponentsBatch(selectedComponents);
          spinner.succeed(chalk[currentTheme.accent](`Components installed successfully`));
        }
        
        // Display the fast installation results
        console.log(boxen(
          `${chalk.bold(runGradient('Fast Mode Installation Complete'))}\n\n` +
          `${chalk.green('✓')} ${chalk.bold('Installed with:')} ${chalk.cyan(packageManager)} (optimized)\n` +
          `${chalk.green('✓')} ${chalk.bold('Core dependencies:')} next, react, react-dom\n` +
          `${chalk.green('✓')} ${chalk.bold('UI dependencies:')} installed in batches\n`,
          { 
            padding: 1, 
            margin: 1, 
            borderStyle: 'round', 
            borderColor: 'green',
            title: '✅ Fast Installation Success',
            titleAlignment: 'center'
          }
        ));
        
        spinner.succeed(chalk[currentTheme.accent](`${steps[1]} complete (fast mode)`));
        
        // Skip to step 3 since we've handled dependencies
        updateProgress(`${steps[2]}...`);
        
      } catch (fastModeError) {
        spinner.warn(chalk.yellow(`Fast mode installation failed, falling back to standard installation: ${fastModeError.message}`));
        // Continue with standard installation below
      }
    } 
    
    // Only run standard installation if fast mode is disabled or failed
    if (!features.fastMode || installOutput === '') {
      try {
        // Show a box to indicate installation has started
        console.log(boxen(
          `${chalk.bold(runGradient('Installing Dependencies'))}\n\n` +
          `${chalk.dim('Installing packages with')} ${chalk.cyan(packageManager)}\n` +
          `${chalk.dim('This may take a moment...')}`,
          { 
            padding: 1, 
            margin: 1, 
            borderStyle: 'round', 
            borderColor: currentTheme.secondary,
            title: '📦 Dependencies',
            titleAlignment: 'center'
          }
        ));
        
        // ... rest of existing code ...
        
        // [The rest of the original installation code follows here]
        // Install core dependencies first
        const coreDeps = [
          "class-variance-authority",
          "clsx",
          "tailwind-merge",
          "lucide-react",
          "tailwindcss-animate"
        ];
        
        const addCmd = packageManager === "npm" ? "install" : "add";
        await execa(installCmd, [addCmd, ...coreDeps], { stdio: "inherit" });
        
        // Try installing with the selected package manager
        const mainDepsPromise = execa(installCmd, 
          packageManager === "pnpm" ? ["install", "--prefer-offline"] : 
          packageManager === "yarn" ? ["install", "--prefer-offline"] :
          ["install"], 
          { stdio: "pipe" }  // Capture the output instead of inheriting stdio
        );
        
        // Capture the installation output
        mainDepsPromise.stdout.on('data', (data) => {
          installOutput += data.toString();
        });
        
        mainDepsPromise.stderr.on('data', (data) => {
          installOutput += data.toString();
        });
        
        await mainDepsPromise;
        
        // Display the installation results in a boxen box
        console.log(boxen(
          `${chalk.bold(runGradient('Dependency Installation Complete'))}\n\n` +
          `${chalk.green('✓')} ${chalk.bold('Installed with:')} ${chalk.cyan(packageManager)}\n` +
          `${chalk.green('✓')} ${chalk.bold('Core dependencies:')} next, react, react-dom\n` +
          `${chalk.green('✓')} ${chalk.bold('Dev dependencies:')} typescript, tailwindcss, etc.\n` +
          `${chalk.green('✓')} ${chalk.bold('UI dependencies:')} lucide-react\n`,
          { 
            padding: 1, 
            margin: 1, 
            borderStyle: 'round', 
            borderColor: 'green',
            title: '✅ Installation Success',
            titleAlignment: 'center'
          }
        ));
      } catch (installError) {
        // ... existing error handling code ...
        // If pnpm fails, fallback to npm
        if (packageManager === "pnpm") {
          spinner.warn(chalk.yellow("pnpm installation failed, falling back to npm..."));
          
          try {
            // Capture npm installation output
            const npmInstallPromise = execa("npm", ["install"], { stdio: "pipe" });
            npmInstallPromise.stdout.on('data', (data) => {
              installOutput += data.toString();
            });
            
            npmInstallPromise.stderr.on('data', (data) => {
              installOutput += data.toString();
            });
            
            await npmInstallPromise;
            
            const npmLucidePromise = execa("npm", ["install", "lucide-react"], { stdio: "pipe" });
            npmLucidePromise.stdout.on('data', (data) => {
              installOutput += data.toString();
            });
            
            npmLucidePromise.stderr.on('data', (data) => {
              installOutput += data.toString();
            });
            
            await npmLucidePromise;
            
            packageManager = "npm"; // Switch to npm for the rest of the setup
            
            // Display the fallback installation results
            console.log(boxen(
              `${chalk.bold(runGradient('Dependency Installation Complete'))}\n\n` +
              `${chalk.yellow('!')} ${chalk.bold('Fallback to:')} ${chalk.cyan('npm')} (pnpm failed)\n` +
              `${chalk.green('✓')} ${chalk.bold('Core dependencies:')} next, react, react-dom\n` +
              `${chalk.green('✓')} ${chalk.bold('Dev dependencies:')} typescript, tailwindcss, etc.\n` +
              `${chalk.green('✓')} ${chalk.bold('UI dependencies:')} lucide-react\n`,
              { 
                padding: 1, 
                margin: 1, 
                borderStyle: 'round', 
                borderColor: 'yellow',
                title: '✅ Installation Success (Fallback)',
                titleAlignment: 'center'
              }
            ));
            
          } catch (npmError) {
            throw new Error(`Failed to install dependencies with pnpm and npm. Please try again later.\nOriginal error: ${installError.message}`);
          }
        } else {
          // Re-throw the error for other package managers
          throw installError;
        }
      }
    }
    
    // ... rest of the code ...
    
    // After Step 5 (around line 1460), add:
    
    // Step 6 (optional): Setup Next Auth with MongoDB
    if (features.auth) {
      updateProgress(`Setting up NextAuth with MongoDB...`);
      await setupNextAuth(appDir);
      spinner.succeed(chalk[currentTheme.accent](`NextAuth setup complete`));
    }
    
    // Step 7 (optional): Setup Prisma with MongoDB
    if (features.prisma) {
      updateProgress(`Setting up Prisma with MongoDB...`);
      await setupPrisma(appDir);
      spinner.succeed(chalk[currentTheme.accent](`Prisma setup complete`));
      
      // Create .env file with all required variables including DB_URL
      updateProgress(`Creating MongoDB environment variables...`);
      await createEnvFile(appDir);
      spinner.succeed(chalk[currentTheme.accent](`MongoDB environment files created`));
    }
    
    // Step 8: Fix TypeScript errors for compatibility
    updateProgress(`Fixing TypeScript configurations...`);
    await fixTypeScriptErrors(appDir);
    spinner.succeed(chalk[currentTheme.accent](`TypeScript configurations fixed`));
    
    spinner.succeed(chalk[currentTheme.accent](`${steps[1]} complete`));

    // Step 3: Set up shadcn
    updateProgress(`${steps[2]}...`);
    
    try {
      // Use the official shadcn CLI initialization for the core UI components
      spinner.text = chalk[currentTheme.secondary]('Initializing Components System...');
      
      // Create a components.json directly instead of running interactive prompt
      const componentsJson = {
        "$schema": "https://ui.shadcn.com/schema.json",
        "style": "default",
        "rsc": true,
        "tsx": true,
        "tailwind": {
          "config": "tailwind.config.ts",
          "css": "app/globals.css",
          "baseColor": "slate",
          "cssVariables": true
        },
        "aliases": {
          "components": "@/components",
          "utils": "@/lib/utils"
        }
      };
      
      await fs.writeFile('components.json', JSON.stringify(componentsJson, null, 2));
      
      // Create a unified box with progress bar for component installation
      if (features.installComponents && selectedComponents.length > 0) {
        // First install the utils
        await fs.ensureDir('lib');
        const utilsContent = templates.utilsContent;
        await fs.writeFile('lib/utils.ts', utilsContent);
        
        // Create a new progress bar just for components
        const componentProgressBar = new cliProgress.SingleBar({
          format: `${chalk.cyan('Component')} ${chalk.dim('|')} ${chalk[currentTheme.secondary]('{bar}')} ${chalk.dim('|')} ${chalk.bold('{percentage}%')} ${chalk.dim('•')} ${chalk.white('{value}/{total}')} ${chalk.cyan('{component}')}`,
          barCompleteChar: "█",
          barIncompleteChar: "░",
          hideCursor: true,
          clearOnComplete: true,
          barsize: 25
        });
        
        // Create a box for the component installation process
        console.log(boxen(
          `${chalk.bold(runGradient('Installing UI Components'))}\n\n` +
          `${chalk.dim('Installing the following components:')}\n` +
          `${chalk.cyan(selectedComponents.join(', '))}\n`,
          { 
            padding: 1, 
            margin: 1, 
            borderStyle: 'round', 
            borderColor: currentTheme.secondary,
            title: '🧩 Components System',
            titleAlignment: 'center'
          }
        ));
        
        // Start the component progress bar
        componentProgressBar.start(selectedComponents.length, 0, { component: 'Starting...' });
        
        // Install each component using shadcn add with non-interactive flags
        let installedCount = 0;
        let skippedComponents = [];
        let failedComponents = [];
        
        for (const component of selectedComponents) {
          componentProgressBar.update(installedCount, { component });
          
          try {
            // Capture the output instead of sending to stdout
            const { stdout, stderr } = await execa("npx", ["shadcn@latest", "add", component, "--yes", "--cwd", "."], { 
              stdio: "pipe",
              env: { ...process.env, FORCE_COLOR: "1", CI: "true" } // Force non-interactive mode
            });
            
            installedCount++;
            componentProgressBar.update(installedCount, { component: `${component} ✓` });
            
            // Check for skipped files
            if (stdout.includes('Skipped') || stderr.includes('Skipped')) {
              skippedComponents.push(component);
            }
            
            // Small delay to make progress visible
            await new Promise(resolve => setTimeout(resolve, 200));
            
          } catch (componentError) {
            try {
              // Alternative method with direct non-interactive options
              await execa("npx", ["shadcn@latest", "add", component, "--yes", "--cwd", "."], { 
                stdio: "pipe",
                env: { ...process.env, FORCE_COLOR: "1", CI: "true", NEXT_SHADCN_SKIP_QUESTIONS: "1" } // Set additional env vars to force non-interactive
              });
              
              installedCount++;
              componentProgressBar.update(installedCount, { component: `${component} ✓` });
              
            } catch (altError) {
              failedComponents.push(component);
              installedCount++;
              componentProgressBar.update(installedCount, { component: `${component} ✗` });
            }
          }
        }
        
        // Stop the component progress bar
        componentProgressBar.stop();
        
        // Show summary of installation
        console.log(boxen(
          `${chalk.bold(runGradient('Component Installation Summary'))}\n\n` +
          `${chalk.green('✓')} ${chalk.bold('Installed:')} ${selectedComponents.length - failedComponents.length} components\n` +
          `${failedComponents.length > 0 ? `${chalk.red('✗')} ${chalk.bold('Failed:')} ${failedComponents.join(', ')}\n` : ''}` +
          `${skippedComponents.length > 0 ? `${chalk.yellow('!')} ${chalk.bold('Skipped files:')} Some files were skipped (already exist)\n` : ''}`,
          { 
            padding: 1, 
            margin: 1, 
            borderStyle: 'round', 
            borderColor: failedComponents.length > 0 ? 'yellow' : 'green',
            title: '📋 Components System Summary',
            titleAlignment: 'center'
          }
        ));
      }
    } catch (err) {
      spinner.warn(chalk.yellow(`Error setting up Components System: ${err.message}. Will try to continue anyway.`));
    }
    
    spinner.succeed(chalk[currentTheme.accent](`${steps[2]} complete`));

    // Step 4: Create .env file
    updateProgress(`${steps[3]}...`);
    await fs.writeFile(".env", "NEXT_PUBLIC_EXAMPLE=filip-app\n");
    
    // Always create a README.md with the project's features
    const readmePath = path.join(process.cwd(), 'README.md');
    const readmeContent = templates.readmeContent(appName);
    await fs.writeFile(readmePath, readmeContent);
    spinner.succeed(chalk[currentTheme.accent](`${steps[3]} complete`));

    // Step 5 (optional): Setup theme switcher
    if (features.themeSwitch) {
      updateProgress(`${steps[4]}...`);
      
      try {
      // Create the ThemeProvider component
      const themeProviderDir = path.join(process.cwd(), 'components/theme');
      fs.mkdirSync(themeProviderDir, { recursive: true });
      
      // Create theme-provider.tsx directly
      const themeProviderPath = path.join(themeProviderDir, 'theme-provider.tsx');
        const themeProviderContent = templates.themeProviderContent;
        await fs.writeFile(themeProviderPath, themeProviderContent);

      // Create ThemeToggle component directly
      const themeTogglePath = path.join(themeProviderDir, 'theme-toggle.tsx');
        const themeToggleContent = templates.themeToggleContent;
      await fs.writeFile(themeTogglePath, themeToggleContent);
      
      // Update the layout.tsx file to include the ThemeProvider
      const layoutPath = path.join(process.cwd(), 'app/layout.tsx');
      try {
        let layoutContent = await fs.readFile(layoutPath, 'utf8');
        
          // Add the ThemeProvider import if it doesn't exist
        if (!layoutContent.includes('import { ThemeProvider }')) {
            layoutContent = `import { ThemeProvider } from "@/components/theme/theme-provider"\n${layoutContent}`;
          }
          
          // Update the body tag to include ThemeProvider
          if (!layoutContent.includes('<ThemeProvider')) {
            // Use a regex pattern that's more robust for finding the body tag
            const bodyPattern = /<body[^>]*>([\s\S]*?)<\/body>/;
            const bodyMatch = layoutContent.match(bodyPattern);
            
            if (bodyMatch) {
              const bodyTag = bodyMatch[0];
              const bodyContent = bodyMatch[1];
              
              // Extract class names if they exist
              const classNameMatch = bodyTag.match(/className=["']([^"']*)["']/);
              const className = classNameMatch ? classNameMatch[1] : '';
              
              // Create the replacement with ThemeProvider
              const newBodyTag = `<body${className ? ` className="${className}"` : ''}>
      <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>${bodyContent}</ThemeProvider>
    </body>`;
              
              // Replace the body tag in the layout content
              layoutContent = layoutContent.replace(bodyPattern, newBodyTag);
          } else {
              // If we can't find the body tag with regex, just insert at a safe position
              spinner.warn(chalk.yellow("Could not find body tag in layout.tsx. Theme provider might not be properly inserted."));
            }
          }
          
          await fs.writeFile(layoutPath, layoutContent);
          
          // Show success message
        console.log(boxen(
            `${chalk.bold(gradient(['#2E3192', '#1BFFFF'])('Theme Provider Added'))}\n\n` +
            `${chalk.green('✓')} Updated layout.tsx with ThemeProvider\n` +
            `${chalk.green('✓')} Dark mode is now available\n` +
            `${chalk.green('✓')} Theme will respect system preferences\n` +
            `${chalk.green('✓')} Use ThemeToggle component to allow users to switch themes`,
          { 
            padding: 1, 
            margin: 1, 
            borderStyle: 'round', 
              borderColor: 'blue',
              title: '🌙 Theme Ready',
            titleAlignment: 'center'
          }
        ));
      } catch (error) {
          spinner.warn(chalk.yellow(`Error updating layout file with ThemeProvider: ${error.message}`));
        }
        
        // Install next-themes
        spinner.text = chalk[currentTheme.secondary]('Installing next-themes package...');
        const addCmd = packageManager === "npm" ? "install" : "add";
        await execa(installCmd, [addCmd, "next-themes"], { stdio: "inherit" });
        
        spinner.succeed(chalk[currentTheme.accent](`${steps[currentStep]} complete`));
        updateProgress(features.installComponents || features.includeDocs || features.responsive ? steps[currentStep] : "Finishing up...");
      } catch (error) {
        spinner.fail(chalk.red(`Failed to set up theme switcher: ${error.message}`));
        console.error(boxen(
          chalk.red(`Error setting up theme switcher:\n\n${error.message}`),
        { 
          padding: 1, 
          margin: 1, 
          borderStyle: 'round', 
            borderColor: 'red',
            title: '❌ Error',
          titleAlignment: 'center'
        }
      ));
      }
    }

    // Step for Auth setup (if enabled)
    if (features.auth) {
      updateProgress(`${steps[currentStep]}...`);
      
      // Create auth directory structure
      const authDir = path.join(process.cwd(), 'components/auth');
      fs.mkdirSync(authDir, { recursive: true });
      
      // Create auth-provider component
      const authProviderContent = `"use client"

import { SessionProvider } from "next-auth/react"

export function AuthProvider({ children }) {
  return <SessionProvider>{children}</SessionProvider>
}`;
      
      await fs.writeFile(path.join(authDir, 'auth-provider.tsx'), authProviderContent);
      
      // Update layout to include AuthProvider
      try {
        const layoutPath = path.join(process.cwd(), 'app/layout.tsx');
        let layoutContent = await fs.readFile(layoutPath, 'utf8');
        
        // Add import for AuthProvider
        if (!layoutContent.includes('import { AuthProvider }')) {
          layoutContent = 
            `import { AuthProvider } from "@/components/auth/auth-provider"\n` + 
            layoutContent;
        }
        
        // Add AuthProvider inside ThemeProvider if it exists, otherwise directly in body
        const themeProviderPattern = /<ThemeProvider([^>]*?)>([\s\S]*?)<\/ThemeProvider>/;
        const bodyPattern = /<body[^>]*>([\s\S]*?)<\/body>/;
        
        if (layoutContent.includes('<ThemeProvider')) {
          const themeProviderMatch = layoutContent.match(themeProviderPattern);
          
          if (themeProviderMatch) {
            const themeProviderProps = themeProviderMatch[1];
            const themeProviderContent = themeProviderMatch[2];
            
            // Create the replacement with AuthProvider inside ThemeProvider
            const newThemeProvider = `<ThemeProvider${themeProviderProps}>
        <AuthProvider>${themeProviderContent}</AuthProvider>
      </ThemeProvider>`;
            
            // Replace the ThemeProvider in the layout content
            layoutContent = layoutContent.replace(themeProviderPattern, newThemeProvider);
          }
        } else {
          // If no ThemeProvider, add AuthProvider directly inside body
          const bodyMatch = layoutContent.match(bodyPattern);
          
          if (bodyMatch) {
            const bodyTag = bodyMatch[0];
            const bodyContent = bodyMatch[1];
            
            // Extract class names if they exist
            const classNameMatch = bodyTag.match(/className=["']([^"']*)["']/);
            const className = classNameMatch ? classNameMatch[1] : '';
            
            // Create the replacement with AuthProvider
            const newBodyTag = `<body${className ? ` className="${className}"` : ''}>
      <AuthProvider>${bodyContent}</AuthProvider>
    </body>`;
            
            // Replace the body tag in the layout content
            layoutContent = layoutContent.replace(bodyPattern, newBodyTag);
          } else {
            // If we can't find the body tag with regex, just insert at a safe position
            spinner.warn(chalk.yellow("Could not find body tag in layout.tsx. Auth provider might not be properly inserted."));
          }
        }
        
        await fs.writeFile(layoutPath, layoutContent);
        
        // Show success message
      console.log(boxen(
          `${chalk.bold(gradient(['#42a5f5', '#1976d2'])('Auth Provider Added'))}\n\n` +
          `${chalk.green('✓')} Updated layout.tsx with AuthProvider\n` +
          `${chalk.green('✓')} Next-Auth is now available\n` +
          `${chalk.green('✓')} Session management is ready\n` +
          `${chalk.green('✓')} See docs for login setup details`,
        { 
          padding: 1, 
          margin: 1, 
          borderStyle: 'round', 
            borderColor: 'blue',
            title: '🔒 Auth Ready',
          titleAlignment: 'center'
        }
      ));
      } catch (error) {
        spinner.warn(chalk.yellow(`Error updating layout file with AuthProvider: ${error.message}`));
      }
      
      // Install next-auth
      spinner.text = chalk[currentTheme.secondary]('Installing Next-Auth package...');
      const addCmd = packageManager === "npm" ? "install" : "add";
      await execa(installCmd, [addCmd, "next-auth"], { stdio: "inherit" });
      
      spinner.succeed(chalk[currentTheme.accent](`${steps[currentStep]} complete`));
      updateProgress(features.installComponents || features.includeDocs || features.responsive ? steps[currentStep] : "Finishing up...");
    }

    // ✅ Done
    // Ensure progress bar reaches 100% at the end
    updateProgress("Installation complete!", steps.length - currentStep);
    progressBar.stop();
    spinner.stop();
    
    const startCmd = packageManager === "npm" ? "npm run dev" : 
                    packageManager === "yarn" ? "yarn dev" : 
                    "pnpm dev";
    
    // Clear any lingering progress bar artifacts
    console.log("\n");
    
    // Create a more visually appealing success message
    console.log("\n");
    console.log(boxen(
      `${chalk.bold(runGradient('✨ Your Filip App is Ready! ✨'))}\n\n` +
      `${chalk.bold('Project:')} ${chalk.cyan(appName)}\n` +
      `${chalk.bold('Location:')} ${chalk.cyan(appDir)}\n` +
      `${chalk.bold('Package Manager:')} ${chalk.cyan(packageManager)}\n\n` +
      `${chalk.bold('✓ Features Installed:')}\n` +
      `${features.installComponents ? `  ${chalk.green('→')} ${chalk.bold('UI Components:')} ${chalk.dim(selectedComponents.length)} components\n` : ''}` +
      `${features.themeSwitch ? `  ${chalk.green('→')} ${chalk.bold('Theme Switcher')} ${chalk.dim('(try it at /theme-demo)')}\n` : ''}` +
      `${features.prisma ? `  ${chalk.green('→')} ${chalk.bold('Prisma Database')} ${chalk.dim('(check prisma/schema.prisma)')}\n` : ''}` +
      `${features.auth ? `  ${chalk.green('→')} ${chalk.bold('Authentication')} ${chalk.dim('(check /auth/signin)')}\n` : ''}` +
      `${features.responsive ? `  ${chalk.green('→')} ${chalk.bold('Responsive Layout')}\n` : ''}` +
      `\n${chalk.bold('To start developing:')}\n` +
      `  ${chalk.cyan('1.')} cd ${appName}\n` +
      `  ${chalk.cyan('2.')} ${startCmd}\n\n` +
      `${chalk.cyan('➜')} Your app will be available at: ${chalk.bold('http://localhost:3000')}`,
      { 
        padding: 1, 
        margin: 1, 
        borderStyle: 'round', 
        borderColor: 'green', 
        title: '🎉 Success',
        titleAlignment: 'center'
      }
    ));
    
    // Show the original three options
    const { nextAction } = await inquirer.prompt([
      {
        type: 'list',
        name: 'nextAction',
        message: 'What would you like to do next?',
        pageSize: 6, // Updated page size for the new option
        loop: true,  // Enable looping through options
        choices: [
          { 
            name: `${chalk.bold('🚀 Create another app')}`, 
            value: 'restart'
          },
          { 
            name: `${chalk.bold('📂 Open the project')}  ${chalk.dim('(and run dev server)')}`, 
            value: 'open-dir-dev'
          },
          { 
            name: `${chalk.bold('💻 Open in VS Code')}  ${chalk.dim('(in a new window)')}`, 
            value: 'vscode'
          },
          { 
            name: `${chalk.bold('🛠️ Try alternate VS Code')}  ${chalk.dim('(code-insiders, vscodium)')}`, 
            value: 'vscode-alt'
          },
          { 
            name: `${chalk.bold('🔨 VS Code + Dev Server')}  ${chalk.dim('(code and run)')}`, 
            value: 'vscode-dev'
          },
          { 
            name: `${chalk.bold('👋 Exit')}`, 
            value: 'exit'
          }
        ]
      }
    ]);
    
    if (nextAction === 'restart') {
      return run();
    } else if (nextAction === 'vscode-alt') {
      // Try alternate VS Code commands
      console.log(`\n${chalk.cyan('Trying alternative VS Code commands...')}`);
      
      const vscodeCommands = ['code', 'code-insiders', 'codium', 'vscodium'];
      let vsOpened = false;
      
      for (const cmd of vscodeCommands) {
        try {
          console.log(`${chalk.dim('Trying')} ${cmd}...`);
          await execa(cmd, ['--new-window', appDir]);
          console.log(`\n${chalk.green('✓')} Project opened with ${cmd}`);
          vsOpened = true;
          break;
        } catch (err) {
          continue;
        }
      }
      
      if (!vsOpened) {
        console.log(chalk.yellow(`\nUnable to open with any VS Code variant.`));
        
        // Offer alternative options if all VS Code variants fail
        const { fallbackAction } = await inquirer.prompt([
          {
            type: 'list',
            name: 'fallbackAction',
            message: 'Would you like to try something else?',
            choices: [
              { name: 'Open project directory', value: 'open-dir' },
              { name: 'Start development server', value: 'dev-only' },
              { name: 'Continue without opening', value: 'continue' }
            ]
          }
        ]);
        
        if (fallbackAction === 'open-dir') {
          openProjectDirectory(appDir);
        } else if (fallbackAction === 'dev-only') {
          startDevServer(appDir, startCmd);
        }
      } else {
        // Ask if they want to start the development server
        const { startDev } = await inquirer.prompt([
          {
            type: 'confirm',
            name: 'startDev',
            message: 'Would you like to start the development server?',
            default: true
          }
        ]);
        
        if (startDev) {
          startDevServer(appDir, startCmd);
        } else {
          console.log(`\n${chalk.cyan('➜')} To start the development server later, run:`);
          console.log(`  ${chalk.dim(`cd ${appName}`)}`);
          console.log(`  ${chalk.dim(`${startCmd}`)}\n`);
        }
      }
      
      console.log(runGradient('Happy coding! 🚀\n'));
    } else if (nextAction === 'vscode' || nextAction === 'vscode-dev') {
      // Keep the existing VS Code handling code
      try {
        console.log(`\n${chalk.cyan('Opening VS Code...')}`);
        
        // Use the --new-window flag to ensure it opens in a new window
        const command = os.platform() === 'win32' ? 'code.cmd' : 'code';
        spawn(command, ['--new-window', appDir], { stdio: 'ignore' });
        
        console.log(`\n${chalk.green('✓')} Project opened in VS Code`);
        
        // If vscode-dev option was selected, also start the dev server
        if (nextAction === 'vscode-dev') {
          // Start an animated loading spinner for the dev server
          const devSpinner = ora({
            text: chalk.cyan('Starting development server...'),
            color: 'cyan',
            spinner: 'dots'
          }).start();
          
          // Wait a moment for VS Code to open
          await new Promise(resolve => setTimeout(resolve, 1500));
          
          let serverStarted = false;
          
          // Start dev server in a new terminal
          try {
            if (os.platform() === 'win32') {
              // Windows approach with better shell settings
              spawn('cmd.exe', ['/c', 'start', 'cmd.exe', '/k', `cd /d "${appDir}" && ${startCmd}`], { 
                cwd: appDir,
                stdio: 'ignore',
                shell: true,
                windowsHide: false
              });
              serverStarted = true;
            } else if (os.platform() === 'darwin') {
              // macOS approach with proper path escaping
              spawn('osascript', [
                '-e', 
                `tell application "Terminal" to do script "cd '${appDir.replace(/'/g, "\\'")}' && ${startCmd}"`
              ], { stdio: 'ignore' });
              serverStarted = true;
            } else {
              // Linux approach with better terminal detection
              const terminals = ['gnome-terminal', 'konsole', 'xterm', 'xfce4-terminal', 'terminator'];
              for (const terminal of terminals) {
                try {
                  spawn(terminal, ['--', 'bash', '-c', `cd "${appDir.replace(/"/g, '\\"')}" && ${startCmd}; exec bash`], {
                    stdio: 'ignore',
                    cwd: appDir
                  });
                  serverStarted = true;
                  break;
  } catch (error) {
                  continue;
                }
              }
            }
          } catch (error) {
            console.error(chalk.red(`Error starting development server: ${error.message}`));
          }
          
          devSpinner.stop();
          
          if (serverStarted) {
            console.log(`${chalk.green('✓')} Development server started`);
            console.log(`\n${chalk.cyan('➜')} Your application will be available at: ${chalk.bold('http://localhost:3000')}\n`);
          } else {
            console.log(chalk.yellow(`\nCouldn't start development server automatically. To start it manually:`));
            console.log(`  ${chalk.dim(`cd ${appName}`)}`);
            console.log(`  ${chalk.dim(`${startCmd}`)}\n`);
            
            // Offer alternative options
            const { alternateAction } = await inquirer.prompt([
              {
                type: 'list',
                name: 'alternateAction',
                message: 'Would you like to try something else?',
                choices: [
                  { name: 'Open project directory', value: 'open-dir' },
                  { name: 'Try VS Code again', value: 'vscode-retry' },
                  { name: 'Continue without opening', value: 'continue' }
                ]
              }
            ]);
            
            if (alternateAction === 'open-dir') {
              openProjectDirectory(appDir);
            } else if (alternateAction === 'vscode-retry') {
              // Try a different approach to open VS Code
              try {
                await execa('code', [appDir], { stdio: 'inherit' });
                console.log(`\n${chalk.green('✓')} Project opened in VS Code using alternate method`);
              } catch (altError) {
                console.log(chalk.yellow(`\nStill unable to open VS Code. Please open it manually and then open the project at: ${appDir}`));
              }
            }
          }
        } else {
          console.log(`\n${chalk.cyan('➜')} To start the development server, run:`);
          console.log(`  ${chalk.dim(`cd ${appName}`)}`);
          console.log(`  ${chalk.dim(`${startCmd}`)}\n`);
        }
        
        console.log(runGradient('Happy coding! 🚀\n'));
      } catch (error) {
        console.log(chalk.yellow(`\nCouldn't open VS Code. Please make sure it's installed and in your PATH.`));
        console.log(`You can manually open the project at: ${chalk.cyan(appDir)}\n`);
        
        // Offer alternative options
        const { alternateAction } = await inquirer.prompt([
          {
            type: 'list',
            name: 'alternateAction',
            message: 'Would you like to try something else?',
            choices: [
              { name: 'Open project directory', value: 'open-dir' },
              { name: 'Try VS Code with different command', value: 'vscode-alt' },
              { name: 'Just start the development server', value: 'dev-only' },
              { name: 'Continue without opening', value: 'continue' }
            ]
          }
        ]);
        
        if (alternateAction === 'open-dir') {
          openProjectDirectory(appDir);
        } else if (alternateAction === 'vscode-alt') {
          // Try alternate VS Code commands
          const vscodeCommands = ['code', 'code-insiders', 'codium', 'vscodium'];
          let vsOpened = false;
          
          for (const cmd of vscodeCommands) {
            try {
              await execa(cmd, [appDir]);
              console.log(`\n${chalk.green('✓')} Project opened with ${cmd}`);
              vsOpened = true;
              break;
            } catch (err) {
              continue;
            }
          }
          
          if (!vsOpened) {
            console.log(chalk.yellow(`\nUnable to open with any VS Code variant. Please open it manually.`));
          }
        } else if (alternateAction === 'dev-only') {
          startDevServer(appDir, startCmd);
        }
      }
    } else if (nextAction === 'open-dir-dev') {
      openProjectDirectory(appDir);
      await new Promise(resolve => setTimeout(resolve, 1000));
      startDevServer(appDir, startCmd);
    } else {
      console.log(chalk.yellow("\nExiting. Goodbye! 👋"));
      process.exit(0);
    }
  } catch (error) {
    spinner.fail(chalk.red("Something went wrong."));
    progressBar.stop();
    console.error(boxen(
      chalk.red(`Error: ${error.message}`),
      { 
        padding: 1, 
        margin: 1, 
        borderStyle: 'round', 
        borderColor: 'red', 
        title: '❌ Error', 
        titleAlignment: 'center' 
      }
    ));
    
    const { action } = await inquirer.prompt([
      {
        type: 'list',
        name: 'action',
        message: 'What would you like to do?',
        choices: [
          { name: '🔄 Try again', value: 'retry' },
          { name: '🔍 Show detailed error', value: 'details' },
          { name: '👋 Exit', value: 'exit' }
        ]
      }
    ]);
    
    if (action === 'retry') {
      return run();
    } else if (action === 'details') {
      console.log(boxen(
        chalk.red(`${error.stack || error}`),
        { padding: 1, borderStyle: 'round', borderColor: 'red', title: 'Error Details' }
      ));
    }
    process.exit(1);
  }
}

// Add these helper functions just before the last closing brace of the run function

// Helper function to open project directory
function openProjectDirectory(dirPath) {
  let explorerOpened = false;
  
  try {
    if (os.platform() === 'win32') {
      spawn('explorer.exe', [dirPath], { stdio: 'ignore' });
      explorerOpened = true;
    } else if (os.platform() === 'darwin') {
      spawn('open', [dirPath], { stdio: 'ignore' });
      explorerOpened = true;
    } else {
      // Try common Linux file explorers
      const explorers = ['xdg-open', 'nautilus', 'dolphin', 'thunar', 'nemo', 'caja', 'pcmanfm'];
      for (const explorer of explorers) {
        try {
          spawn(explorer, [dirPath], { stdio: 'ignore' });
          explorerOpened = true;
          break;
        } catch (error) {
          continue;
        }
      }
    }
    
    if (explorerOpened) {
      // Use boxen to display directory opened message
      console.log(boxen(
        `${chalk.bold(gradient(['#42a5f5', '#1976d2'])('File Explorer Opened'))}\n\n` +
        `${chalk.green('✓')} Project directory: ${chalk.cyan(path.basename(dirPath))}\n` +
        `${chalk.green('✓')} Location: ${chalk.cyan(dirPath)}`,
        { 
          padding: 1, 
          margin: 1, 
          borderStyle: 'round', 
          borderColor: 'blue',
          title: '📂 Directory',
          titleAlignment: 'center'
        }
      ));
    } else {
      // Use boxen to display manual instructions
      console.log(boxen(
        `${chalk.bold(gradient(['#ffeb3b', '#ffc107'])('Directory Not Opened'))}\n\n` +
        `${chalk.yellow('Could not open file explorer automatically.')}\n\n` +
        `${chalk.dim('Your project is located at:')}\n` +
        `${chalk.bold(dirPath)}`,
        { 
          padding: 1, 
          margin: 1, 
          borderStyle: 'round', 
          borderColor: 'yellow',
          title: '📁 Project Location',
          titleAlignment: 'center'
        }
      ));
    }
  } catch (error) {
    // Use boxen to display error
    console.log(boxen(
      `${chalk.bold(gradient(['#ffeb3b', '#ffc107'])('Directory Not Opened'))}\n\n` +
      `${chalk.yellow(`Error: ${error.message}`)}\n\n` +
      `${chalk.dim('Your project is located at:')}\n` +
      `${chalk.bold(dirPath)}`,
      { 
        padding: 1, 
        margin: 1, 
        borderStyle: 'round', 
        borderColor: 'yellow',
        title: '📁 Project Location',
        titleAlignment: 'center'
      }
    ));
  }
  
  return explorerOpened;
}

// Helper function to start development server
function startDevServer(dirPath, command) {
  const devSpinner = ora({
    text: chalk.cyan('Starting development server...'),
    color: 'cyan',
    spinner: 'dots'
  }).start();
  
  let serverStarted = false;
  
  try {
    if (os.platform() === 'win32') {
      spawn('cmd.exe', ['/c', 'start', 'cmd.exe', '/k', `cd /d "${dirPath}" && ${command}`], { 
        stdio: 'ignore',
        shell: true,
        windowsHide: false
      });
      serverStarted = true;
    } else if (os.platform() === 'darwin') {
      spawn('osascript', [
        '-e', 
        `tell application "Terminal" to do script "cd '${dirPath.replace(/'/g, "\\'")}' && ${command}"`
      ], { stdio: 'ignore' });
      serverStarted = true;
    } else {
      const terminals = ['gnome-terminal', 'konsole', 'xterm', 'xfce4-terminal', 'terminator', 'alacritty', 'kitty'];
      for (const terminal of terminals) {
        try {
          spawn(terminal, ['--', 'bash', '-c', `cd "${dirPath.replace(/"/g, '\\"')}" && ${command}; exec bash`], {
            stdio: 'ignore'
          });
          serverStarted = true;
          break;
        } catch (error) {
          continue;
        }
      }
    }
  } catch (error) {
    console.error(chalk.red(`Error starting development server: ${error.message}`));
  }
  
  devSpinner.stop();
  
  if (serverStarted) {
    // Use boxen to display server started message
    console.log(boxen(
      `${chalk.bold(gradient(['#00c853', '#64dd17'])('Development Server Started'))}\n\n` +
      `${chalk.green('✓')} Server running with: ${chalk.cyan(command)}\n` +
      `${chalk.green('✓')} Project directory: ${chalk.cyan(path.basename(dirPath))}\n\n` +
      `${chalk.dim('Your application will be available at:')}\n` +
      `${chalk.bold('http://localhost:3000')}`,
      { 
        padding: 1, 
        margin: 1, 
        borderStyle: 'round', 
        borderColor: 'green',
        title: '🚀 Server Ready',
        titleAlignment: 'center'
      }
    ));
  } else {
    // Use boxen to display manual instructions
    console.log(boxen(
      `${chalk.bold(gradient(['#ff9800', '#ff5722'])('Server Not Started'))}\n\n` +
      `${chalk.yellow('To start the development server manually:')}\n\n` +
      `${chalk.bold('1.')} ${chalk.dim('Navigate to your project')}\n` +
      `   ${chalk.cyan(`cd ${path.basename(dirPath)}`)}\n\n` +
      `${chalk.bold('2.')} ${chalk.dim('Start the development server')}\n` +
      `   ${chalk.cyan(command)}`,
      { 
        padding: 1, 
        margin: 1, 
        borderStyle: 'round', 
        borderColor: 'yellow',
        title: '⚠️ Manual Steps Required',
        titleAlignment: 'center'
      }
    ));
  }
  
  return serverStarted;
}

// Performance optimizations for faster installations
async function optimizeSetup(packageManager) {
  // 1. Use concurrent downloads for faster dependency installation
  const installDependenciesConcurrently = async (dependencies, type = 'prod') => {
    const command = packageManager === 'npm' ? 'npm' : packageManager;
    const args = [
      packageManager === 'npm' ? 'install' : 'add',
      ...(type === 'dev' ? ['-D'] : []),
      ...dependencies
    ];
    
    // Use execa with increased buffer size and concurrency
    return execa(command, args, { 
      buffer: true,
      preferLocal: true,
      stdout: 'inherit',
      maxBuffer: 10 * 1024 * 1024,  // 10MB buffer 
      env: { 
        ...process.env, 
        // Force non-interactive mode and skip auto-install prompts
        CI: "true",
        ADBLOCK: "1", 
        // Skip compliance checks for faster installs
        DISABLE_OPENCOLLECTIVE: "1", 
        DISABLE_NOTIFIER: "1",
        NEXT_TELEMETRY_DISABLED: "1",
        NPM_CONFIG_FUND: "0"
      }
    });
  };
  
  // 2. Use batch component installation for Components system
  const installShadcnComponentsBatch = async (components) => {
    // Separate shadcn/ui components from custom components
    const shadcnComponents = components.filter(comp => 
      !['file-uploader', 'video-player', 'timeline', 'rating', 'file-tree', 'copy-button'].includes(comp)
    );
    
    const customComponents = components.filter(comp => 
      ['file-uploader', 'video-player', 'timeline', 'rating', 'file-tree', 'copy-button'].includes(comp)
    );
    
    // Process in parallel for maximum speed
    const promises = [];
    
    // Install shadcn/ui components in batches
    if (shadcnComponents.length > 0) {
      // Install in batches of 5 for better performance
      const batchSize = 5;
      const batches = [];
      
      for (let i = 0; i < shadcnComponents.length; i += batchSize) {
        batches.push(shadcnComponents.slice(i, i + batchSize));
      }
      
      // Install batches concurrently
      for (const batch of batches) {
        try {
          await execa('npx', [
            'shadcn@latest', 'add', ...batch, 
            '--yes', // Skip confirmation
            '--overwrite' // Overwrite existing components
          ], {
            env: { 
              ...process.env, 
              FORCE_COLOR: "1", 
              CI: "true", 
              NEXT_SHADCN_SKIP_QUESTIONS: "1"
            }
          });
        } catch (error) {
          // Try one by one if batch fails
          for (const component of batch) {
            try {
              await execa('npx', [
                'shadcn@latest', 'add', component, 
                '--yes', 
                '--overwrite'
              ], { 
                env: { 
                  ...process.env, 
                  FORCE_COLOR: "1", 
                  CI: "true",
                  NEXT_SHADCN_SKIP_QUESTIONS: "1"
                } 
              });
            } catch (componentError) {
              console.log(chalk.yellow(`Warning: Failed to install component ${component}: ${componentError.message}`));
            }
          }
        }
      }
    }
    
    // Install custom components
    if (customComponents.length > 0) {
      for (const comp of customComponents) {
        // Create the component file
        const componentContent = getCustomComponentTemplate(comp);
        const componentDir = path.join(process.cwd(), 'components');
        
        // Create directory if it doesn't exist
        await fs.ensureDir(componentDir);
        
        // Write component file
        const componentPath = path.join(componentDir, `${comp}.tsx`);
        await fs.writeFile(componentPath, componentContent);
      }
      
      // Install required dependencies for custom components
      if (customComponents.includes('file-uploader')) {
        // Install react-dropzone for the file uploader
        const depCommand = packageManager === 'npm' ? 'install' : 'add';
        try {
          await execa(packageManager, [depCommand, 'react-dropzone']);
        } catch (error) {
          console.log(chalk.yellow(`Warning: Failed to install react-dropzone: ${error.message}`));
        }
      }
    }
    
    // If we're installing components, also update the main page.tsx to showcase them
    const showcasePath = path.join(process.cwd(), 'app', 'page.tsx');
    try {
      await fs.writeFile(showcasePath, templates.componentsShowcasePage);
    } catch (error) {
      console.log(chalk.yellow(`Warning: Failed to update page.tsx: ${error.message}`));
    }
  };
  
  // 3. Function to get custom component templates
  const getCustomComponentTemplate = (componentName) => {
    const templates = {
      'file-uploader': `"use client"

import * as React from "react"
import { useDropzone } from "react-dropzone"
import { UploadCloud, File, X, Check } from "lucide-react"
import { cn } from "@/lib/utils"

type FileUploaderProps = {
  onFilesChange?: (files: File[]) => void;
  maxFiles?: number;
  maxSize?: number; // in bytes
  accept?: Record<string, string[]>;
  className?: string;
  disabled?: boolean;
}

type UploadedFile = File & {
  preview?: string;
  status?: 'uploading' | 'error' | 'success';
  progress?: number;
  id: string;
}

export function FileUploader({
  onFilesChange,
  maxFiles = 5,
  maxSize = 5 * 1024 * 1024, // 5MB default
  accept,
  className,
  disabled = false,
}: FileUploaderProps) {
  const [files, setFiles] = React.useState<UploadedFile[]>([])
  
  const onDrop = React.useCallback((acceptedFiles: File[]) => {
    const newFiles = acceptedFiles.map(file => 
      Object.assign(file, {
        preview: URL.createObjectURL(file),
        id: Math.random().toString(36).substring(2),
        status: 'uploading' as const,
        progress: 0
      })
    )
    
    // Simulate upload progress
    newFiles.forEach(file => {
      const interval = setInterval(() => {
        setFiles(prev => 
          prev.map(f => {
            if (f.id === file.id) {
              const progress = Math.min((f.progress || 0) + 10, 100)
              const status = progress === 100 ? 'success' as const : 'uploading' as const
              return { ...f, progress, status }
            }
            return f
          })
        )
        
        if ((file.progress || 0) >= 100) {
          clearInterval(interval)
        }
      }, 300)
    })
    
    // Add new files with existing ones (respecting maxFiles)
    setFiles(prev => {
      const combined = [...prev, ...newFiles]
      return combined.slice(0, maxFiles)
    })
    
    // Call the onChange callback
    onFilesChange?.(acceptedFiles)
  }, [maxFiles, onFilesChange])
  
  const removeFile = React.useCallback((id: string) => {
    setFiles(prev => {
      const newFiles = prev.filter(file => file.id !== id)
      onFilesChange?.(newFiles)
      return newFiles
    })
  }, [onFilesChange])
  
  const { getRootProps, getInputProps, isDragActive } = useDropzone({ 
    onDrop,
    maxSize,
    maxFiles,
    accept,
    disabled
  })
  
  // Clean up previews on unmount
  React.useEffect(() => {
    return () => {
      files.forEach(file => {
        if (file.preview) URL.revokeObjectURL(file.preview)
      })
    }
  }, [files])
  
  return (
    <div className={cn("space-y-4", className)}>
      <div 
        {...getRootProps()} 
        className={cn(
          "border-2 border-dashed rounded-lg p-6 transition-colors cursor-pointer",
          "flex flex-col items-center justify-center gap-2 text-center",
          isDragActive ? "border-primary bg-primary/5" : "border-muted-foreground/25",
          disabled && "opacity-50 cursor-not-allowed"
        )}
      >
        <input {...getInputProps()} />
        <UploadCloud className="h-10 w-10 text-muted-foreground" />
        <div>
          <p className="font-medium text-sm">
            {isDragActive ? "Drop the files here" : "Drag & drop files here"}
          </p>
          <p className="text-xs text-muted-foreground mt-1">
            or click to browse files
          </p>
        </div>
        <div className="text-xs text-muted-foreground mt-2">
          <p>Max {maxFiles} file{maxFiles !== 1 ? 's' : ''}</p>
          <p>Up to {(maxSize / (1024 * 1024)).toFixed(0)}MB per file</p>
        </div>
      </div>
      
      {files.length > 0 && (
        <div className="space-y-2">
          {files.map(file => (
            <div 
              key={file.id} 
              className={cn(
                "flex items-center justify-between p-3 border rounded-md",
                file.status === 'error' && "border-destructive bg-destructive/5",
                file.status === 'success' && "border-green-500 bg-green-50 dark:bg-green-950/20"
              )}
            >
              <div className="flex items-center gap-3 overflow-hidden">
                <div className="shrink-0">
                  {file.type.startsWith('image/') && file.preview ? (
                    <div className="h-10 w-10 rounded-md overflow-hidden border">
                      <img 
                        src={file.preview} 
                        alt={file.name}
                        className="h-full w-full object-cover"
                        onLoad={() => { URL.revokeObjectURL(file.preview!) }}
                      />
                    </div>
                  ) : (
                    <File className="h-10 w-10 text-muted-foreground" />
                  )}
                </div>
                <div className="min-w-0 flex-1">
                  <p className="text-sm font-medium truncate">{file.name}</p>
                  <p className="text-xs text-muted-foreground">
                    {(file.size / 1024).toFixed(0)} KB
                  </p>
                </div>
              </div>
              <div className="flex items-center gap-3 pl-3">
                {file.status === 'uploading' && (
                  <div className="w-24">
                    <div className="h-1.5 w-full bg-muted rounded-full overflow-hidden">
                      <div 
                        className="h-full bg-primary transition-all duration-300"
                        style={{ width: \`\${file.progress}%\` }}
                      />
                    </div>
                    <p className="text-xs text-muted-foreground mt-1 text-right">
                      {file.progress}%
                    </p>
                  </div>
                )}
                {file.status === 'success' && (
                  <Check className="h-5 w-5 text-green-500" />
                )}
                <button
                  type="button"
                  onClick={() => removeFile(file.id)}
                  className="rounded-md p-1 hover:bg-muted transition-colors"
                >
                  <X className="h-4 w-4 text-muted-foreground" />
                </button>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  )
}`,
      'video-player': `"use client"

import * as React from "react"
import { Play, Pause, Volume2, VolumeX, Maximize, SkipForward, SkipBack } from "lucide-react"
import { cn } from "@/lib/utils"
import { Slider } from "@/components/ui/slider"
import { Button } from "@/components/ui/button"

type VideoPlayerProps = {
  src: string;
  poster?: string;
  title?: string;
  autoPlay?: boolean;
  className?: string;
  onPlay?: () => void;
  onPause?: () => void;
  onEnded?: () => void;
}

export function VideoPlayer({
  src,
  poster,
  title,
  autoPlay = false,
  className,
  onPlay,
  onPause,
  onEnded,
}: VideoPlayerProps) {
  const videoRef = React.useRef<HTMLVideoElement>(null)
  const [playing, setPlaying] = React.useState(autoPlay)
  const [currentTime, setCurrentTime] = React.useState(0)
  const [duration, setDuration] = React.useState(0)
  const [volume, setVolume] = React.useState(1)
  const [muted, setMuted] = React.useState(false)
  const [showControls, setShowControls] = React.useState(true)
  const controlsTimeout = React.useRef<NodeJS.Timeout>()
  
  // Hide controls after inactivity
  const resetControlsTimeout = React.useCallback(() => {
    if (controlsTimeout.current) {
      clearTimeout(controlsTimeout.current)
    }
    
    setShowControls(true)
    
    controlsTimeout.current = setTimeout(() => {
      if (playing) {
        setShowControls(false)
      }
    }, 3000)
  }, [playing])
  
  // Reset the timeout when playing state changes
  React.useEffect(() => {
    resetControlsTimeout()
  }, [playing, resetControlsTimeout])
  
  // Handle video events
  React.useEffect(() => {
    const video = videoRef.current
    if (!video) return
    
    const onTimeUpdate = () => setCurrentTime(video.currentTime)
    const onDurationChange = () => setDuration(video.duration)
    const onVolumeChange = () => {
      setVolume(video.volume)
      setMuted(video.muted)
    }
    
    const onVideoPlay = () => {
      setPlaying(true)
      onPlay?.()
    }
    
    const onVideoPause = () => {
      setPlaying(false)
      onPause?.()
    }
    
    const onVideoEnded = () => {
      setPlaying(false)
      onEnded?.()
    }
    
    video.addEventListener("timeupdate", onTimeUpdate)
    video.addEventListener("durationchange", onDurationChange)
    video.addEventListener("volumechange", onVolumeChange)
    video.addEventListener("play", onVideoPlay)
    video.addEventListener("pause", onVideoPause)
    video.addEventListener("ended", onVideoEnded)
    
    return () => {
      video.removeEventListener("timeupdate", onTimeUpdate)
      video.removeEventListener("durationchange", onDurationChange)
      video.removeEventListener("volumechange", onVolumeChange)
      video.removeEventListener("play", onVideoPlay)
      video.removeEventListener("pause", onVideoPause)
      video.removeEventListener("ended", onVideoEnded)
      
      if (controlsTimeout.current) {
        clearTimeout(controlsTimeout.current)
      }
    }
  }, [onPlay, onPause, onEnded])
  
  // Toggle play/pause
  const togglePlay = () => {
    if (!videoRef.current) return
    
    if (playing) {
      videoRef.current.pause()
    } else {
      videoRef.current.play()
    }
  }
  
  // Toggle mute
  const toggleMute = () => {
    if (!videoRef.current) return
    videoRef.current.muted = !muted
  }
  
  // Handle seeking
  const handleSeek = (value: number[]) => {
    if (!videoRef.current) return
    videoRef.current.currentTime = value[0]
  }
  
  // Handle volume change
  const handleVolumeChange = (value: number[]) => {
    if (!videoRef.current) return
    videoRef.current.volume = value[0]
  }
  
  // Toggle fullscreen
  const toggleFullscreen = () => {
    if (!videoRef.current) return
    
    if (document.fullscreenElement) {
      document.exitFullscreen()
    } else {
      videoRef.current.requestFullscreen()
    }
  }
  
  // Skip forward/backward
  const skip = (seconds: number) => {
    if (!videoRef.current) return
    videoRef.current.currentTime = Math.min(
      Math.max(currentTime + seconds, 0),
      duration
    )
  }
  
  // Format time as mm:ss
  const formatTime = (time: number) => {
    const minutes = Math.floor(time / 60)
    const seconds = Math.floor(time % 60)
    return \`\${minutes}:\${seconds < 10 ? '0' : ''}\${seconds}\`
  }
  
  return (
    <div 
      className={cn(
        "relative group overflow-hidden rounded-lg bg-black",
        className
      )}
      onMouseMove={resetControlsTimeout}
    >
      <video
        ref={videoRef}
        src={src}
        poster={poster}
        className="w-full h-full object-contain"
        autoPlay={autoPlay}
        onClick={togglePlay}
      />
      
      {/* Video Title */}
      {title && (
        <div 
          className={cn(
            "absolute top-0 left-0 right-0 p-4 bg-gradient-to-b from-black/70 to-transparent",
            "transition-opacity duration-300",
            !showControls && "opacity-0"
          )}
        >
          <h3 className="text-white font-medium truncate">{title}</h3>
        </div>
      )}
      
      {/* Video Controls */}
      <div 
        className={cn(
          "absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/70 to-transparent",
          "flex flex-col gap-2 transition-opacity duration-300",
          !showControls && "opacity-0"
        )}
      >
        {/* Progress bar */}
        <div className="flex items-center gap-2">
          <span className="text-white text-xs">{formatTime(currentTime)}</span>
          <Slider
            value={[currentTime]}
            min={0}
            max={duration || 100}
            step={0.01}
            onValueChange={handleSeek}
            className="flex-1"
          />
          <span className="text-white text-xs">{formatTime(duration)}</span>
        </div>
        
        {/* Controls row */}
        <div className="flex items-center justify-between">
          <div className="flex items-center gap-2">
            <Button 
              variant="ghost" 
              size="icon" 
              className="text-white hover:bg-white/20"
              onClick={() => skip(-10)}
            >
              <SkipBack className="h-5 w-5" />
            </Button>
            
            <Button 
              variant="ghost" 
              size="icon" 
              className="text-white hover:bg-white/20"
              onClick={togglePlay}
            >
              {playing ? (
                <Pause className="h-5 w-5" />
              ) : (
                <Play className="h-5 w-5" />
              )}
            </Button>
            
            <Button 
              variant="ghost" 
              size="icon" 
              className="text-white hover:bg-white/20"
              onClick={() => skip(10)}
            >
              <SkipForward className="h-5 w-5" />
            </Button>
            
            <div className="flex items-center gap-2 ml-2">
              <Button 
                variant="ghost" 
                size="icon" 
                className="text-white hover:bg-white/20"
                onClick={toggleMute}
              >
                {muted || volume === 0 ? (
                  <VolumeX className="h-5 w-5" />
                ) : (
                  <Volume2 className="h-5 w-5" />
                )}
              </Button>
              
              <Slider
                value={[muted ? 0 : volume]}
                min={0}
                max={1}
                step={0.01}
                onValueChange={handleVolumeChange}
                className="w-24"
              />
            </div>
          </div>
          
          <Button 
            variant="ghost" 
            size="icon" 
            className="text-white hover:bg-white/20"
            onClick={toggleFullscreen}
          >
            <Maximize className="h-5 w-5" />
          </Button>
        </div>
      </div>
    </div>
  )
}`,
      'timeline': `"use client"

import * as React from "react"
import { cn } from "@/lib/utils"

type TimelineProps = React.HTMLAttributes<HTMLDivElement> & {
  items: TimelineItem[];
  orientation?: "vertical" | "horizontal";
  alternating?: boolean;
}

type TimelineItem = {
  id: string;
  title: string;
  content: React.ReactNode;
  date?: string;
  icon?: React.ReactNode;
  status?: "past" | "current" | "future";
}

export function Timeline({
  items,
  orientation = "vertical",
  alternating = false,
  className,
  ...props
}: TimelineProps) {
  return (
    <div 
      className={cn(
        "relative",
        orientation === "horizontal" && "flex items-start gap-4 overflow-x-auto pb-4",
        className
      )}
      {...props}
    >
      {/* Timeline line */}
      {orientation === "vertical" && (
        <div className="absolute left-4 top-0 bottom-0 w-0.5 bg-border" />
      )}
      {orientation === "horizontal" && (
        <div className="absolute left-0 right-0 top-4 h-0.5 bg-border" />
      )}
      
      {/* Timeline items */}
      {items.map((item, index) => (
        <div
          key={item.id}
          className={cn(
            "relative",
            orientation === "vertical" && "pl-10 pb-8 last:pb-0",
            orientation === "horizontal" && "pt-8 pr-8 last:pr-0 min-w-[200px]",
            alternating && orientation === "vertical" && index % 2 === 1 && "ml-auto pl-0 pr-10 text-right"
          )}
        >
          {/* Item connector & icon */}
          <div 
            className={cn(
              "absolute bg-background",
              orientation === "vertical" && "left-[9px] top-1 h-6 w-6 -translate-x-1/2 rounded-full border-4 border-background",
              orientation === "horizontal" && "left-0 top-[9px] h-6 w-6 -translate-y-1/2 rounded-full border-4 border-background",
              alternating && orientation === "vertical" && index % 2 === 1 && "left-auto right-[9px] translate-x-1/2",
              item.status === "past" && "bg-primary border-primary",
              item.status === "current" && "bg-primary border-primary ring-4 ring-primary/20",
              item.status === "future" && "bg-muted border-muted"
            )}
          >
            {item.icon && (
              <div className="flex h-full w-full items-center justify-center">
                {item.icon}
              </div>
            )}
          </div>
          
          {/* Item content */}
          <div className={cn(
            alternating && orientation === "vertical" && index % 2 === 1 && "flex flex-col items-end"
          )}>
            {item.date && (
              <p className="text-sm text-muted-foreground mb-1">{item.date}</p>
            )}
            <h3 className="font-medium">{item.title}</h3>
            <div className={cn(
              "mt-2",
              typeof item.content === "string" && "text-muted-foreground"
            )}>
              {item.content}
            </div>
          </div>
        </div>
      ))}
    </div>
  )
}`,
      'rating': `"use client"

import * as React from "react"
import { Star } from "lucide-react"
import { cn } from "@/lib/utils"

type RatingProps = {
  value?: number;
  max?: number;
  size?: "sm" | "md" | "lg";
  readonly?: boolean;
  onChange?: (value: number) => void;
  className?: string;
}

export function Rating({
  value = 0,
  max = 5,
  size = "md",
  readonly = false,
  onChange,
  className
}: RatingProps) {
  const [rating, setRating] = React.useState(value)
  const [hoverRating, setHoverRating] = React.useState(0)
  
  // Update internal state when value prop changes
  React.useEffect(() => {
    setRating(value)
  }, [value])
  
  // Handle selecting a rating
  const handleSetRating = (newRating: number) => {
    if (readonly) return
    
    setRating(newRating)
    onChange?.(newRating)
  }
  
  const starSizes = {
    sm: "w-4 h-4",
    md: "w-5 h-5",
    lg: "w-6 h-6",
  }
  
  return (
    <div 
      className={cn(
        "flex items-center gap-1",
        !readonly && "cursor-pointer",
        className
      )}
      onMouseLeave={() => setHoverRating(0)}
    >
      {Array.from({ length: max }).map((_, i) => {
        const starValue = i + 1
        const isFilled = hoverRating ? starValue <= hoverRating : starValue <= rating
        
        return (
          <Star
            key={i}
            className={cn(
              starSizes[size],
              "transition-colors",
              isFilled 
                ? "text-yellow-400 fill-yellow-400" 
                : "text-muted stroke-muted-foreground fill-none",
              !readonly && "hover:stroke-yellow-400"
            )}
            onClick={() => handleSetRating(starValue)}
            onMouseEnter={() => !readonly && setHoverRating(starValue)}
          />
        )
      })}
    </div>
  )
}`,
      'file-tree': `"use client"

import * as React from "react"
import { ChevronRight, ChevronDown, Folder, File, FolderOpen } from "lucide-react"
import { cn } from "@/lib/utils"

type FileTreeProps = {
  data: TreeNode[];
  onSelect?: (node: TreeNode) => void;
  initialExpanded?: boolean;
  className?: string;
}

type TreeNode = {
  id: string;
  name: string;
  type: "file" | "folder";
  children?: TreeNode[];
  meta?: Record<string, any>;
}

export function FileTree({
  data,
  onSelect,
  initialExpanded = false,
  className
}: FileTreeProps) {
  return (
    <div className={cn("text-sm", className)}>
      <ul className="space-y-1">
        {data.map((node) => (
          <TreeNode
            key={node.id}
            node={node}
            onSelect={onSelect}
            initialExpanded={initialExpanded}
            level={0}
          />
        ))}
      </ul>
    </div>
  )
}

type TreeNodeProps = {
  node: TreeNode;
  onSelect?: (node: TreeNode) => void;
  initialExpanded: boolean;
  level: number;
}

function TreeNode({ node, onSelect, initialExpanded, level }: TreeNodeProps) {
  const [expanded, setExpanded] = React.useState(initialExpanded)
  const hasChildren = node.type === "folder" && node.children && node.children.length > 0
  
  const toggleExpand = (e: React.MouseEvent) => {
    e.stopPropagation()
    if (hasChildren) {
      setExpanded(!expanded)
    }
  }
  
  const handleSelect = () => {
    onSelect?.(node)
  }
  
  // File type icon mapping
  const getFileIcon = (fileName: string) => {
    const extension = fileName.split('.').pop()?.toLowerCase()
    
    if (!extension) return <File className="h-4 w-4 text-muted-foreground" />
    
    // Example file type mapping - extend as needed
    const iconMap: Record<string, React.ReactNode> = {
      js: <File className="h-4 w-4 text-yellow-400" />,
      ts: <File className="h-4 w-4 text-blue-400" />,
      jsx: <File className="h-4 w-4 text-cyan-400" />,
      tsx: <File className="h-4 w-4 text-blue-500" />,
      css: <File className="h-4 w-4 text-sky-400" />,
      html: <File className="h-4 w-4 text-orange-400" />,
      json: <File className="h-4 w-4 text-green-400" />,
      md: <File className="h-4 w-4 text-gray-400" />,
    }
    
    return iconMap[extension] || <File className="h-4 w-4 text-muted-foreground" />
  }
  
  return (
    <li>
      <div 
        className={cn(
          "flex items-center py-1 px-1 rounded-md",
          "hover:bg-muted cursor-pointer"
        )}
        style={{ paddingLeft: \`\${level * 12 + 4}px\` }}
        onClick={handleSelect}
      >
        {hasChildren ? (
          <div className="mr-1 p-0.5" onClick={toggleExpand}>
            {expanded ? (
              <ChevronDown className="h-3.5 w-3.5 text-muted-foreground" />
            ) : (
              <ChevronRight className="h-3.5 w-3.5 text-muted-foreground" />
            )}
          </div>
        ) : (
          <div className="w-5" />
        )}
        
        <div className="mr-1.5">
          {node.type === "folder" ? (
            expanded ? (
              <FolderOpen className="h-4 w-4 text-yellow-400" />
            ) : (
              <Folder className="h-4 w-4 text-yellow-400" />
            )
          ) : (
            getFileIcon(node.name)
          )}
        </div>
        
        <span className="truncate">{node.name}</span>
      </div>
      
      {hasChildren && expanded && (
        <ul className="space-y-1">
          {node.children!.map(childNode => (
            <TreeNode
              key={childNode.id}
              node={childNode}
              onSelect={onSelect}
              initialExpanded={initialExpanded}
              level={level + 1}
            />
          ))}
        </ul>
      )}
    </li>
  )
}`,
      'copy-button': `"use client"

import * as React from "react"
import { Copy, Check } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip"

type CopyButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
  value: string;
  variant?: "default" | "secondary" | "outline" | "ghost";
  size?: "default" | "sm" | "lg" | "icon";
  timeout?: number;
  label?: string;
  successLabel?: string;
  tooltipSide?: "top" | "right" | "bottom" | "left";
  showTooltip?: boolean;
  className?: string;
}

export function CopyButton({
  value,
  variant = "ghost",
  size = "icon",
  timeout = 2000,
  label = "Copy",
  successLabel = "Copied!",
  tooltipSide = "top",
  showTooltip = true,
  className,
  ...props
}: CopyButtonProps) {
  const [copied, setCopied] = React.useState(false)
  
  const handleCopy = async () => {
    try {
      await navigator.clipboard.writeText(value)
      setCopied(true)
      
      setTimeout(() => {
        setCopied(false)
      }, timeout)
    } catch (error) {
      console.error("Failed to copy text:", error)
    }
  }
  
  const button = (
    <Button
      size={size}
      variant={variant}
      onClick={handleCopy}
      className={cn(className)}
      {...props}
    >
      {copied ? (
        <Check className="h-4 w-4" />
      ) : (
        <Copy className="h-4 w-4" />
      )}
      {size !== "icon" && (
        <span className="ml-2">
          {copied ? successLabel : label}
        </span>
      )}
    </Button>
  )
  
  if (!showTooltip) {
    return button
  }
  
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>
          {button}
        </TooltipTrigger>
        <TooltipContent side={tooltipSide}>
          <p>{copied ? successLabel : label}</p>
        </TooltipContent>
      </Tooltip>
    </TooltipProvider>
  )
}`
    };
    
    return templates[componentName] || '';
  };
  
  // 4. Optimize file operations with streaming and parallel writes
  const setupFilesOptimized = async (files) => {
    const promises = Object.entries(files).map(async ([filePath, content]) => {
      // Create directories if needed
      const dirPath = path.dirname(filePath);
      await fs.ensureDir(dirPath);
      
      // Write the file
      return fs.writeFile(filePath, content);
    });
    
    return Promise.all(promises);
  };
  
  return {
    installDependenciesConcurrently,
    installShadcnComponentsBatch,
    setupFilesOptimized
  };
}

// Function to set up Next Auth
async function setupNextAuth(projectDir) {
  // Import auth templates
  const authTemplatesModule = await import('./next-auth-setup.js');
  
  // Extract the exported templates
  const { authConfig, authRouteHandler, signInPage, authProviderContent } = authTemplatesModule;
  
  // Create auth directory structure
  await fs.ensureDir(path.join(projectDir, 'app/api/auth/[...nextauth]'));
  await fs.ensureDir(path.join(projectDir, 'lib'));
  await fs.ensureDir(path.join(projectDir, 'components/auth'));
  
  // Add route.ts file with proper NextAuth configuration
  await fs.writeFile(
    path.join(projectDir, 'app/api/auth/[...nextauth]', 'route.ts'), 
    authRouteHandler
  );
  
  // Add auth.ts configuration
  await fs.writeFile(
    path.join(projectDir, 'lib', 'auth.ts'),
    authConfig
  );
  
  // Add auth provider component
  await fs.writeFile(
    path.join(projectDir, 'components/auth', 'auth-provider.tsx'),
    authProviderContent
  );
  
  // Create signin page
  await fs.ensureDir(path.join(projectDir, 'app/auth/signin'));
  await fs.writeFile(
    path.join(projectDir, 'app/auth/signin', 'page.tsx'),
    signInPage
  );
  
  // Create a client-side auth session provider wrapper
  await fs.writeFile(
    path.join(projectDir, 'app', 'providers.tsx'),
    `"use client"

import { SessionProvider } from "next-auth/react"
import { ThemeProvider } from "@/components/theme-provider"
import { ReactNode } from "react"

export function Providers({ 
  children 
}: { 
  children: ReactNode 
}) {
  return (
    <SessionProvider>
      <ThemeProvider
        attribute="class"
        defaultTheme="system"
        enableSystem
        disableTransitionOnChange
      >
        {children}
      </ThemeProvider>
    </SessionProvider>
  )
}`
  );
  
  // Update layout to use the providers component
  try {
    const layoutPath = path.join(projectDir, 'app/layout.tsx');
    let layoutContent = await fs.readFile(layoutPath, 'utf8');
    
    // Add import for Providers
    if (!layoutContent.includes('import { Providers }')) {
      layoutContent = `import { Providers } from "./providers"\n` + layoutContent;
    }
    
    // Replace ThemeProvider with Providers if it exists
    if (layoutContent.includes('<ThemeProvider')) {
      const themeProviderPattern = /<ThemeProvider[\s\S]*?>([\s\S]*?)<\/ThemeProvider>/;
      const themeProviderMatch = layoutContent.match(themeProviderPattern);
      
      if (themeProviderMatch) {
        const themeProviderContent = themeProviderMatch[1];
        layoutContent = layoutContent.replace(
          themeProviderPattern, 
          `<Providers>${themeProviderContent}</Providers>`
        );
      }
    } else {
      // If no ThemeProvider, add Providers directly inside body
      const bodyPattern = /<body[^>]*>([\s\S]*?)<\/body>/;
      const bodyMatch = layoutContent.match(bodyPattern);
      
      if (bodyMatch) {
        const bodyContent = bodyMatch[1];
        const bodyStart = bodyMatch[0].split('>')[0] + '>';
        
        layoutContent = layoutContent.replace(
          bodyPattern, 
          `${bodyStart}\n      <Providers>${bodyContent}</Providers>\n    </body>`
        );
      }
    }
    
    await fs.writeFile(layoutPath, layoutContent);
  } catch (error) {
    console.log(chalk.yellow(`Warning: Could not update layout with auth provider: ${error.message}`));
  }
  
  // Update package.json to include NextAuth dependencies
  const pkgJsonPath = path.join(projectDir, 'package.json');
  const pkgJson = await fs.readJSON(pkgJsonPath);
  
  pkgJson.dependencies = {
    ...pkgJson.dependencies,
    "next-auth": "^4.24.5",
    "@auth/mongodb-adapter": "^2.0.0",
    "mongodb": "^6.3.0"
  };
  
  await fs.writeJSON(pkgJsonPath, pkgJson, { spaces: 2 });
}

// Function to set up Prisma with MongoDB
async function setupPrisma(projectDir) {
  // Import prisma templates
  const prismaTemplatesModule = await import('./prisma-setup.js');
  const prismaTemplates = prismaTemplatesModule.default || prismaTemplatesModule;
  
  // Create Prisma directory
  await fs.ensureDir(path.join(projectDir, 'prisma'));
  await fs.ensureDir(path.join(projectDir, 'lib'));
  
  // Create Prisma schema with MongoDB configuration
  await fs.writeFile(
    path.join(projectDir, 'prisma', 'schema.prisma'),
    prismaTemplates.prismaSchema
  );
  
  // Create MongoDB connection utility
  await fs.writeFile(
    path.join(projectDir, 'lib', 'mongodb.ts'),
    prismaTemplates.mongodbClientUtil
  );
  
  // Update package.json to include Prisma and MongoDB dependencies
  const pkgJsonPath = path.join(projectDir, 'package.json');
  const pkgJson = await fs.readJSON(pkgJsonPath);
  
  pkgJson.dependencies = {
    ...pkgJson.dependencies,
    "@prisma/client": "latest",
    "mongodb": "latest"
  };
  
  pkgJson.devDependencies = {
    ...pkgJson.devDependencies,
    "prisma": "latest"
  };
  
  await fs.writeJSON(pkgJsonPath, pkgJson, { spaces: 2 });
}

// Function to create environment variables file
async function createEnvFile(projectDir) {
  const envContent = `# Application
NEXT_PUBLIC_APP_URL=http://localhost:3000

# Authentication
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=${Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)}

# Database - MongoDB
DATABASE_URL="mongodb+srv://username:password@cluster.mongodb.net/mydb?retryWrites=true&w=majority"

# Example API keys (replace with your own)
# GITHUB_ID=your_github_client_id
# GITHUB_SECRET=your_github_client_secret
# GOOGLE_CLIENT_ID=your_google_client_id
# GOOGLE_CLIENT_SECRET=your_google_client_secret
`;
  
  await fs.writeFile(path.join(projectDir, '.env'), envContent);
  await fs.writeFile(path.join(projectDir, '.env.example'), envContent);
}

// Add the function to fix TypeScript errors
async function fixTypeScriptErrors(projectDir) {
  // Fix for next-themes type issue
  const themeProviderContent = `"use client"

import * as React from "react"
import { ThemeProvider as NextThemesProvider } from "next-themes"

type ThemeProviderProps = {
  children: React.ReactNode;
  attribute?: string;
  defaultTheme?: string;
  enableSystem?: boolean;
  disableTransitionOnChange?: boolean;
};

export function ThemeProvider({ 
  children,
  attribute = "class",
  defaultTheme = "system",
  enableSystem = true,
  disableTransitionOnChange = false,
}: ThemeProviderProps) {
  return (
    <NextThemesProvider
      attribute={attribute}
      defaultTheme={defaultTheme}
      enableSystem={enableSystem}
      disableTransitionOnChange={disableTransitionOnChange}
    >
      {children}
    </NextThemesProvider>
  );
}`;

  await fs.ensureDir(path.join(projectDir, 'components/theme'));
  await fs.writeFile(path.join(projectDir, 'components/theme/theme-provider.tsx'), themeProviderContent);
  
  // Fix for auth provider type issue
  const authProviderContent = `"use client"

import { SessionProvider } from "next-auth/react"
import { ReactNode } from "react"

type AuthProviderProps = {
  children: ReactNode;
}

export function AuthProvider({ children }: AuthProviderProps) {
  return <SessionProvider>{children}</SessionProvider>
}`;

  await fs.ensureDir(path.join(projectDir, 'components/auth'));
  await fs.writeFile(path.join(projectDir, 'components/auth/auth-provider.tsx'), authProviderContent);
  
  // Update layout.tsx to include providers correctly
  const layoutPath = path.join(projectDir, 'app/layout.tsx');
  let layoutContent = await fs.readFile(layoutPath, 'utf8');
  
  if (!layoutContent.includes('import { ThemeProvider }')) {
    layoutContent = `import { ThemeProvider } from "@/components/theme/theme-provider"\n${layoutContent}`;
  }
  
  if (!layoutContent.includes('import { AuthProvider }')) {
    layoutContent = `import { AuthProvider } from "@/components/auth/auth-provider"\n${layoutContent}`;
  }
  
  // Update the body tag to include providers
  if (!layoutContent.includes('<ThemeProvider') && !layoutContent.includes('<AuthProvider')) {
    const bodyPattern = /<body[^>]*>([\s\S]*?)<\/body>/;
    const bodyMatch = layoutContent.match(bodyPattern);
    
    if (bodyMatch) {
      const bodyTag = bodyMatch[0];
      const bodyContent = bodyMatch[1];
      
      // Extract class names if they exist
      const classNameMatch = bodyTag.match(/className=["']([^"']*)["']/);
      const className = classNameMatch ? classNameMatch[1] : '';
      
      // Create the replacement with providers
      const newBodyTag = `<body${className ? ` className="${className}"` : ''}>
      <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
        <AuthProvider>${bodyContent}</AuthProvider>
      </ThemeProvider>
    </body>`;
      
      // Replace the body tag in the layout content
      layoutContent = layoutContent.replace(bodyPattern, newBodyTag);
    }
  }
  
  await fs.writeFile(layoutPath, layoutContent);
}

run();